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