From 9dfdf89f75d6b127011dc2f5efc0488e32167d54 Mon Sep 17 00:00:00 2001
From: eheimburg <74330250+eheimburg@users.noreply.github.com>
Date: Tue, 20 Apr 2021 07:36:18 -0400
Subject: [PATCH 1/5] Feature/Spawner Improvements
Adds new settings to the ServerWaveSpawner, so they can be tuned in the final "balance pass".
- The max # of simultaneous spawns is now determined based on the number of players
- Spawner can now override the spawned entity's detection range
- Added a total of 10 spawn-positions to the prefab (to allow a max of 10 spawned imps without any overlapping each other)
- Removed the "unlimited spawns" option because it's very dangerous (and could easily overwhelm the host)
---
.../BossRoom/Prefabs/Game/EnemySpawner.prefab | 166 +++++++++++++++++-
.../Server/Game/AIState/IdleAIState.cs | 2 +-
.../Server/Game/Character/ServerCharacter.cs | 33 ++++
.../Server/Game/Entity/ServerWaveSpawner.cs | 67 +++++--
4 files changed, 254 insertions(+), 14 deletions(-)
diff --git a/Assets/BossRoom/Prefabs/Game/EnemySpawner.prefab b/Assets/BossRoom/Prefabs/Game/EnemySpawner.prefab
index 5892f68a6..3399460bf 100644
--- a/Assets/BossRoom/Prefabs/Game/EnemySpawner.prefab
+++ b/Assets/BossRoom/Prefabs/Game/EnemySpawner.prefab
@@ -60,6 +60,36 @@ Transform:
m_Father: {fileID: 8727022540156222958}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
+--- !u!1 &1898120149362029613
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 894520739242503044}
+ m_Layer: 0
+ m_Name: SpawnPoint (5)
+ m_TagString: Untagged
+ m_Icon: {fileID: 7148428337604731935, guid: 0000000000000000d000000000000000, type: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &894520739242503044
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1898120149362029613}
+ m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -0.87499994, y: 0, z: 1.73}
+ m_LocalScale: {x: 1.3539857, y: 1.1857388, z: 1.2418212}
+ m_Children: []
+ m_Father: {fileID: 8727022540156222958}
+ m_RootOrder: 10
+ m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
--- !u!1 &2091269911110589480
GameObject:
m_ObjectHideFlags: 0
@@ -120,6 +150,36 @@ Transform:
m_Father: {fileID: 8727022540156222958}
m_RootOrder: 9
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
+--- !u!1 &3496698763770893301
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5949595974590154644}
+ m_Layer: 0
+ m_Name: SpawnPoint (9)
+ m_TagString: Untagged
+ m_Icon: {fileID: 7148428337604731935, guid: 0000000000000000d000000000000000, type: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5949595974590154644
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3496698763770893301}
+ m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -1.722057, y: 0, z: 2.298}
+ m_LocalScale: {x: 1.3539857, y: 1.1857388, z: 1.2418212}
+ m_Children: []
+ m_Father: {fileID: 8727022540156222958}
+ m_RootOrder: 14
+ m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
--- !u!1 &3529278057740772152
GameObject:
m_ObjectHideFlags: 0
@@ -302,6 +362,11 @@ Transform:
- {fileID: 7959506842896624360}
- {fileID: 5974522673422337960}
- {fileID: 1274569494783434218}
+ - {fileID: 894520739242503044}
+ - {fileID: 7124730538960977854}
+ - {fileID: 2077514793330709529}
+ - {fileID: 3127061543440710862}
+ - {fileID: 5949595974590154644}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -341,17 +406,26 @@ MonoBehaviour:
- {fileID: 7959506842896624360}
- {fileID: 5974522673422337960}
- {fileID: 1274569494783434218}
+ - {fileID: 894520739242503044}
+ - {fileID: 7124730538960977854}
+ - {fileID: 2077514793330709529}
+ - {fileID: 3127061543440710862}
+ - {fileID: 5949595974590154644}
m_BlockingMask:
serializedVersion: 2
m_Bits: 1
m_PlayerProximityValidationTimestep: 2
+ m_SpawnedEntityDetectDistance: 30
+ m_DetectStealthyPlayers: 1
m_NumberOfWaves: 2
m_SpawnsPerWave: 2
m_TimeBetweenSpawns: 0.5
m_TimeBetweenWaves: 5
m_RestartDelay: 10
m_ProximityDistance: 30
- m_MaxActiveSpawns: 5
+ m_MinSpawnCap: 2
+ m_MaxSpawnCap: 10
+ m_SpawnCapIncreasePerPlayer: 1
--- !u!114 &2847004539442057774
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -495,6 +569,96 @@ MeshCollider:
m_Convex: 0
m_CookingOptions: 30
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &6585344981094142531
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7124730538960977854}
+ m_Layer: 0
+ m_Name: SpawnPoint (6)
+ m_TagString: Untagged
+ m_Icon: {fileID: 7148428337604731935, guid: 0000000000000000d000000000000000, type: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7124730538960977854
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6585344981094142531}
+ m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -0.875, y: 0, z: 3.167}
+ m_LocalScale: {x: 1.3539857, y: 1.1857388, z: 1.2418212}
+ m_Children: []
+ m_Father: {fileID: 8727022540156222958}
+ m_RootOrder: 11
+ m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
+--- !u!1 &8845616318696809149
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2077514793330709529}
+ m_Layer: 0
+ m_Name: SpawnPoint (7)
+ m_TagString: Untagged
+ m_Icon: {fileID: 7148428337604731935, guid: 0000000000000000d000000000000000, type: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2077514793330709529
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8845616318696809149}
+ m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -0.8760569, y: 0, z: 4.495}
+ m_LocalScale: {x: 1.3539857, y: 1.1857388, z: 1.2418212}
+ m_Children: []
+ m_Father: {fileID: 8727022540156222958}
+ m_RootOrder: 12
+ m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
+--- !u!1 &9017715703642230324
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3127061543440710862}
+ m_Layer: 0
+ m_Name: SpawnPoint (8)
+ m_TagString: Untagged
+ m_Icon: {fileID: 7148428337604731935, guid: 0000000000000000d000000000000000, type: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &3127061543440710862
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 9017715703642230324}
+ m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
+ m_LocalPosition: {x: -1.722057, y: 0, z: 3.961}
+ m_LocalScale: {x: 1.3539857, y: 1.1857388, z: 1.2418212}
+ m_Children: []
+ m_Father: {fileID: 8727022540156222958}
+ m_RootOrder: 13
+ m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
--- !u!1001 &1263729836617029720
PrefabInstance:
m_ObjectHideFlags: 0
diff --git a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
index e5846b1de..a27dde72c 100644
--- a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
@@ -29,7 +29,7 @@ public override void Update()
protected void DetectFoes()
{
- float detectionRange = m_Brain.CharacterData.DetectRange;
+ float detectionRange = m_Brain.GetMyServerCharacter().GetDetectRadius();
// we are doing this check every Update, so we'll use square-magnitude distance to avoid the expensive sqrt (that's implicit in Vector3.magnitude)
float detectionRangeSqr = detectionRange * detectionRange;
Vector3 position = m_Brain.GetMyServerCharacter().transform.position;
diff --git a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
index 76d3c129b..53027c816 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
@@ -42,6 +42,12 @@ public bool IsNpc
// Cached component reference
private ServerCharacterMovement m_Movement;
+ ///
+ /// If we are created by a spawner, the spawner might override our detection radius
+ /// -1 is a sentinel value meaning "no override"
+ ///
+ private float m_DetectRadiusOverride = -1;
+
private void Awake()
{
m_Movement = GetComponent();
@@ -214,6 +220,33 @@ public float GetBuffedValue(Action.BuffableValue buffType)
return m_ActionPlayer.GetBuffedValue(buffType);
}
+ ///
+ /// Returns the range at which this character can detect enemies. Only applicable to NPCs.
+ /// This is usually the same value as is indicated by our game data, but it can be
+ /// dynamically overridden.
+ ///
+ /// our entity detection range (in meters)
+ public float GetDetectRadius()
+ {
+ if (m_DetectRadiusOverride == -1)
+ {
+ return GameDataSource.Instance.CharacterDataByType[NetState.CharacterType].DetectRange;
+ }
+ else
+ {
+ return m_DetectRadiusOverride;
+ }
+ }
+
+ ///
+ /// Changes this creature's detection radius. Only meaningful for NPCs.
+ ///
+ /// new detection radius for this entity (in meters)
+ public void SetDetectRadius(float radius)
+ {
+ m_DetectRadiusOverride = radius;
+ }
+
///
/// Receive a Life State change that brings Fainted characters back to Alive state.
///
diff --git a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
index 69d113c77..a2e5ad808 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
@@ -43,6 +43,10 @@ public class ServerWaveSpawner : NetworkBehaviour
[SerializeField]
float m_PlayerProximityValidationTimestep;
+ [SerializeField]
+ [Tooltip("The detection range of spawned entities. Only meaningful for NPCs (not breakables). -1 = \"use default for this NPC\"")]
+ float m_SpawnedEntityDetectDistance = -1;
+
[Header("Wave parameters")]
[Tooltip("Total number of waves.")]
[SerializeField]
@@ -63,8 +67,19 @@ public class ServerWaveSpawner : NetworkBehaviour
[SerializeField]
float m_ProximityDistance;
[SerializeField]
- [Tooltip("The spawner won't create more than this many entities at a time. 0 = don't track spawn count")]
- int m_MaxActiveSpawns;
+ [Tooltip("When looking for players within proximity distance, should we count players in stealth mode?")]
+ bool m_DetectStealthyPlayers;
+
+ [Header("Spawn Cap (i.e. number of simultaneously spawned entities)")]
+ [SerializeField]
+ [Tooltip("The minimum number of entities this spawner will try to maintain (regardless of player count)")]
+ int m_MinSpawnCap;
+ [SerializeField]
+ [Tooltip("The maximum number of entities this spawner will try to maintain (regardless of player count)")]
+ int m_MaxSpawnCap;
+ [SerializeField]
+ [Tooltip("For each player in the game, the Spawn Cap is raised above the minimum by this amount. (Rounds up to nearest whole number.)")]
+ float m_SpawnCapIncreasePerPlayer;
// indicates whether NetworkStart() has been called on us yet
bool m_IsStarted;
@@ -189,10 +204,7 @@ IEnumerator SpawnWave()
if (IsRoomAvailableForAnotherSpawn())
{
var newSpawn = SpawnPrefab();
- if (m_MaxActiveSpawns > 0) // 0 = no limit on spawns, so we don't bother tracking 'em
- {
- m_ActiveSpawns.Add(newSpawn);
- }
+ m_ActiveSpawns.Add(newSpawn);
}
yield return new WaitForSeconds(m_TimeBetweenSpawns);
@@ -217,20 +229,45 @@ NetworkObject SpawnPrefab()
{
clone.Spawn();
}
+
+ if (m_SpawnedEntityDetectDistance > -1)
+ {
+ // need to override the spawned creature's detection range (if they even have a detection range!)
+ var serverChar = clone.GetComponent();
+ if (serverChar)
+ {
+ serverChar.SetDetectRadius(m_SpawnedEntityDetectDistance);
+ }
+ }
+
return clone;
}
bool IsRoomAvailableForAnotherSpawn()
{
- if (m_MaxActiveSpawns <= 0)
- {
- // no max-spawn limit
- return true;
- }
// references to spawned components that no longer exist will become null,
// so clear those out. Then we know how many we have left
m_ActiveSpawns.RemoveAll(spawnedNetworkObject => { return spawnedNetworkObject == null; });
- return m_ActiveSpawns.Count < m_MaxActiveSpawns;
+ return m_ActiveSpawns.Count < GetCurrentSpawnCap();
+ }
+
+ ///
+ /// Returns the current max number of entities we should try to maintain.
+ /// This can change based on the current number of living players; if the cap goes below
+ /// our current number of active spawns, we don't spawn anything new until we're below the cap.
+ ///
+ int GetCurrentSpawnCap()
+ {
+ int numPlayers = 0;
+ foreach (var serverCharacter in PlayerServerCharacter.GetPlayerServerCharacters())
+ {
+ if (serverCharacter.NetState.NetworkLifeState.Value == LifeState.Alive)
+ {
+ ++numPlayers;
+ }
+ }
+
+ return Mathf.CeilToInt(Mathf.Min(m_MinSpawnCap + (numPlayers * m_SpawnCapIncreasePerPlayer), m_MaxSpawnCap));
}
///
@@ -250,6 +287,12 @@ bool IsAnyPlayerNearbyAndVisible()
// and is not occluded by a blocking collider.
foreach (var serverCharacter in PlayerServerCharacter.GetPlayerServerCharacters())
{
+ if (!m_DetectStealthyPlayers && serverCharacter.NetState.IsStealthy.Value)
+ {
+ // we don't detect stealthy players
+ continue;
+ }
+
var playerPosition = serverCharacter.transform.position;
var direction = playerPosition - spawnerPosition;
From 910f2c3741a7360f816869bac66767e9ebbd788b Mon Sep 17 00:00:00 2001
From: eheimburg <74330250+eheimburg@users.noreply.github.com>
Date: Tue, 20 Apr 2021 09:13:32 -0400
Subject: [PATCH 2/5] Give defaults to serialized values
---
.../Server/Game/Entity/ServerWaveSpawner.cs | 52 +++++++++----------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
index a2e5ad808..29ec3b212 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
@@ -20,28 +20,13 @@ public class ServerWaveSpawner : NetworkBehaviour
[Tooltip("Each spawned enemy appears at one of the points in this list")]
List m_SpawnPositions;
- // cache reference to our own transform
- Transform m_Transform;
-
- // track wave index and reset once all waves are complete
- int m_WaveIndex;
-
- // keep reference to our current watch-for-players coroutine
- Coroutine m_WatchForPlayers;
-
- // keep reference to our wave spawning coroutine
- Coroutine m_WaveSpawning;
-
- // cache array of RaycastHit as it will be reused for player visibility
- RaycastHit[] m_Hit;
-
[Tooltip("Select which layers will block visibility.")]
[SerializeField]
LayerMask m_BlockingMask;
[Tooltip("Time between player distance & visibility scans, in seconds.")]
[SerializeField]
- float m_PlayerProximityValidationTimestep;
+ float m_PlayerProximityValidationTimestep = 2;
[SerializeField]
[Tooltip("The detection range of spawned entities. Only meaningful for NPCs (not breakables). -1 = \"use default for this NPC\"")]
@@ -50,36 +35,51 @@ public class ServerWaveSpawner : NetworkBehaviour
[Header("Wave parameters")]
[Tooltip("Total number of waves.")]
[SerializeField]
- int m_NumberOfWaves;
+ int m_NumberOfWaves = 2;
[Tooltip("Number of spawns per wave.")]
[SerializeField]
- int m_SpawnsPerWave;
+ int m_SpawnsPerWave = 2;
[Tooltip("Time between individual spawns, in seconds.")]
[SerializeField]
- float m_TimeBetweenSpawns;
+ float m_TimeBetweenSpawns = 0.5f;
[Tooltip("Time between waves, in seconds.")]
[SerializeField]
- float m_TimeBetweenWaves;
+ float m_TimeBetweenWaves = 5;
[Tooltip("Once last wave is spawned, the spawner waits this long to restart wave spawns, in seconds.")]
[SerializeField]
- float m_RestartDelay;
+ float m_RestartDelay = 10;
[Tooltip("A player must be within this distance to commence first wave spawn.")]
[SerializeField]
- float m_ProximityDistance;
+ float m_ProximityDistance = 30;
[SerializeField]
[Tooltip("When looking for players within proximity distance, should we count players in stealth mode?")]
- bool m_DetectStealthyPlayers;
+ bool m_DetectStealthyPlayers = true;
[Header("Spawn Cap (i.e. number of simultaneously spawned entities)")]
[SerializeField]
[Tooltip("The minimum number of entities this spawner will try to maintain (regardless of player count)")]
- int m_MinSpawnCap;
+ int m_MinSpawnCap = 2;
[SerializeField]
[Tooltip("The maximum number of entities this spawner will try to maintain (regardless of player count)")]
- int m_MaxSpawnCap;
+ int m_MaxSpawnCap = 10;
[SerializeField]
[Tooltip("For each player in the game, the Spawn Cap is raised above the minimum by this amount. (Rounds up to nearest whole number.)")]
- float m_SpawnCapIncreasePerPlayer;
+ float m_SpawnCapIncreasePerPlayer = 1;
+
+ // cache reference to our own transform
+ Transform m_Transform;
+
+ // track wave index and reset once all waves are complete
+ int m_WaveIndex;
+
+ // keep reference to our current watch-for-players coroutine
+ Coroutine m_WatchForPlayers;
+
+ // keep reference to our wave spawning coroutine
+ Coroutine m_WaveSpawning;
+
+ // cache array of RaycastHit as it will be reused for player visibility
+ RaycastHit[] m_Hit;
// indicates whether NetworkStart() has been called on us yet
bool m_IsStarted;
From 240fbdaf01135f82576280d305652ab69ca6287a Mon Sep 17 00:00:00 2001
From: eheimburg <74330250+eheimburg@users.noreply.github.com>
Date: Wed, 21 Apr 2021 03:59:09 -0400
Subject: [PATCH 3/5] move detect range into AIBrain
---
.../Server/Game/AIState/IdleAIState.cs | 3 +-
.../Scripts/Server/Game/Character/AIBrain.cs | 24 ++++++++++++
.../Server/Game/Character/ServerCharacter.cs | 38 +++----------------
.../Server/Game/Entity/ServerWaveSpawner.cs | 4 +-
4 files changed, 32 insertions(+), 37 deletions(-)
diff --git a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
index a27dde72c..91a0fd10e 100644
--- a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
@@ -29,9 +29,8 @@ public override void Update()
protected void DetectFoes()
{
- float detectionRange = m_Brain.GetMyServerCharacter().GetDetectRadius();
// we are doing this check every Update, so we'll use square-magnitude distance to avoid the expensive sqrt (that's implicit in Vector3.magnitude)
- float detectionRangeSqr = detectionRange * detectionRange;
+ float detectionRangeSqr = m_Brain.DetectRange * m_Brain.DetectRange;
Vector3 position = m_Brain.GetMyServerCharacter().transform.position;
// in this game, NPCs only attack players (and never other NPCs), so we can just iterate over the players to see if any are nearby
diff --git a/Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs b/Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs
index 67b85da54..d84137be4 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Character/AIBrain.cs
@@ -25,6 +25,12 @@ private enum AIStateType
private Dictionary m_Logics;
private List m_HatedEnemies;
+ ///
+ /// If we are created by a spawner, the spawner might override our detection radius
+ /// -1 is a sentinel value meaning "no override"
+ ///
+ private float m_DetectRangeOverride = -1;
+
public AIBrain(ServerCharacter me, ActionPlayer myActionPlayer)
{
m_ServerCharacter = me;
@@ -148,5 +154,23 @@ public CharacterClass CharacterData
}
}
+ ///
+ /// The range at which this character can detect enemies, in meters.
+ /// This is usually the same value as is indicated by our game data, but it
+ /// can be dynamically overridden.
+ ///
+ public float DetectRange
+ {
+ get
+ {
+ return (m_DetectRangeOverride == -1) ? CharacterData.DetectRange : m_DetectRangeOverride;
+ }
+
+ set
+ {
+ m_DetectRangeOverride = value;
+ }
+ }
+
}
}
diff --git a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
index 53027c816..97809fa8e 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
@@ -42,12 +42,6 @@ public bool IsNpc
// Cached component reference
private ServerCharacterMovement m_Movement;
- ///
- /// If we are created by a spawner, the spawner might override our detection radius
- /// -1 is a sentinel value meaning "no override"
- ///
- private float m_DetectRadiusOverride = -1;
-
private void Awake()
{
m_Movement = GetComponent();
@@ -220,33 +214,6 @@ public float GetBuffedValue(Action.BuffableValue buffType)
return m_ActionPlayer.GetBuffedValue(buffType);
}
- ///
- /// Returns the range at which this character can detect enemies. Only applicable to NPCs.
- /// This is usually the same value as is indicated by our game data, but it can be
- /// dynamically overridden.
- ///
- /// our entity detection range (in meters)
- public float GetDetectRadius()
- {
- if (m_DetectRadiusOverride == -1)
- {
- return GameDataSource.Instance.CharacterDataByType[NetState.CharacterType].DetectRange;
- }
- else
- {
- return m_DetectRadiusOverride;
- }
- }
-
- ///
- /// Changes this creature's detection radius. Only meaningful for NPCs.
- ///
- /// new detection radius for this entity (in meters)
- public void SetDetectRadius(float radius)
- {
- m_DetectRadiusOverride = radius;
- }
-
///
/// Receive a Life State change that brings Fainted characters back to Alive state.
///
@@ -287,5 +254,10 @@ public IDamageable.SpecialDamageFlags GetSpecialDamageFlags()
{
return IDamageable.SpecialDamageFlags.None;
}
+
+ ///
+ /// This character's AIBrain. Will be null if this is not an NPC, or if this NPC has no "brain".
+ ///
+ public AIBrain AIBrain { get { return m_AIBrain; } }
}
}
diff --git a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
index 29ec3b212..eb99ec380 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Entity/ServerWaveSpawner.cs
@@ -234,9 +234,9 @@ NetworkObject SpawnPrefab()
{
// need to override the spawned creature's detection range (if they even have a detection range!)
var serverChar = clone.GetComponent();
- if (serverChar)
+ if (serverChar && serverChar.AIBrain != null)
{
- serverChar.SetDetectRadius(m_SpawnedEntityDetectDistance);
+ serverChar.AIBrain.DetectRange = m_SpawnedEntityDetectDistance;
}
}
From a23433ddfe939a0e89db796ac5037468722ad056 Mon Sep 17 00:00:00 2001
From: eheimburg <74330250+eheimburg@users.noreply.github.com>
Date: Wed, 21 Apr 2021 04:00:45 -0400
Subject: [PATCH 4/5] Update ServerCharacter.cs
---
.../BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
index 97809fa8e..1c9e783e6 100644
--- a/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/Character/ServerCharacter.cs
@@ -256,7 +256,7 @@ public IDamageable.SpecialDamageFlags GetSpecialDamageFlags()
}
///
- /// This character's AIBrain. Will be null if this is not an NPC, or if this NPC has no "brain".
+ /// This character's AIBrain. Will be null if this is not an NPC.
///
public AIBrain AIBrain { get { return m_AIBrain; } }
}
From 0b46165420ebbf30fd940bb951bbccdc6b93bce0 Mon Sep 17 00:00:00 2001
From: eheimburg <74330250+eheimburg@users.noreply.github.com>
Date: Wed, 21 Apr 2021 04:05:30 -0400
Subject: [PATCH 5/5] Update IdleAIState.cs
---
Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
index 91a0fd10e..8d66e8701 100644
--- a/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
+++ b/Assets/BossRoom/Scripts/Server/Game/AIState/IdleAIState.cs
@@ -29,8 +29,9 @@ public override void Update()
protected void DetectFoes()
{
+ float detectionRange = m_Brain.DetectRange;
// we are doing this check every Update, so we'll use square-magnitude distance to avoid the expensive sqrt (that's implicit in Vector3.magnitude)
- float detectionRangeSqr = m_Brain.DetectRange * m_Brain.DetectRange;
+ float detectionRangeSqr = detectionRange * detectionRange;
Vector3 position = m_Brain.GetMyServerCharacter().transform.position;
// in this game, NPCs only attack players (and never other NPCs), so we can just iterate over the players to see if any are nearby