private void SelectTarget() { var gameplayConfig = ConfigState.Instance.GetGameplayConfig(); if (TotalTickCount % SearchInterval * (1f / gameplayConfig.Difficulty.ActorAggression) != 0) { return; } var detectionDifficultyFactor = 1f / gameplayConfig.Difficulty.ActorPerception; //check player first since it's (relatively) cheap if (FactionModel.GetRelation(Faction, "Player") == FactionRelationStatus.Hostile && !MetaState.Instance.SessionFlags.Contains("NoTarget") && !GameState.Instance.PlayerFlags.Contains(PlayerFlags.NoTarget)) { var playerObj = WorldUtils.GetPlayerObject(); if (playerObj != null && RpgWorldUtils.TargetIsAlive(playerObj.transform)) { PlayerController pc = playerObj.GetComponent <PlayerController>(); if ((playerObj.transform.position - transform.position).magnitude <= SearchRadius && UnityEngine.Random.Range(0f, 1f * detectionDifficultyFactor) <= RpgValues.DetectionChance(GameState.Instance.PlayerRpgState, pc.MovementComponent.IsCrouching, pc.MovementComponent.IsRunning)) { if (UseLineOfSight) { //additional check RaycastHit hitinfo; if (Physics.Raycast(transform.position + new Vector3(0, 1.0f, 0), (playerObj.transform.position - transform.position), out hitinfo)) { if (hitinfo.collider.gameObject == playerObj) { Target = playerObj.transform; return; } } } else { //otherwise, close enough Target = playerObj.transform; return; } } } } //if(TargetNpc) { //var sw = System.Diagnostics.Stopwatch.StartNew(); //new code should be faster if n is large but it may not bear out in practice, and it probably allocs more dedotated wam var colliders = Physics.OverlapSphere(transform.position, SearchRadius, LayerMask.GetMask("Default", "ActorHitbox")); HashSet <ActorController> potentialTargets = new HashSet <ActorController>(); foreach (var collider in colliders) { var actorController = collider.GetComponent <ActorController>(); if (actorController != null) { potentialTargets.Add(actorController); break; } var hitboxComponent = collider.GetComponent <IHitboxComponent>(); if (hitboxComponent != null && hitboxComponent.ParentController is ActorController hitboxAC) { potentialTargets.Add(hitboxAC); break; } } //old stupid code: should work well enough as long as n is small and your computer is fast enough //IEnumerable<ActorController> potentialTargets = transform.root.GetComponentsInChildren<ActorController>(); foreach (var potentialTarget in potentialTargets) { if (RpgWorldUtils.TargetIsAlive(potentialTarget.transform) && (potentialTarget.transform.position - transform.position).magnitude <= SearchRadius && FactionModel.GetRelation(Faction, potentialTarget.Faction) == FactionRelationStatus.Hostile && !(potentialTarget == this)) { //roll some dice if (potentialTarget.Detectability < 1 && UnityEngine.Random.Range(0f, 1f * detectionDifficultyFactor) > potentialTarget.Detectability) //probably correct { continue; } if (UseLineOfSight) { //additional check RaycastHit hitinfo; if (Physics.Raycast(transform.position + new Vector3(0, 1.0f, 0), (potentialTarget.transform.position - transform.position), out hitinfo)) { if (hitinfo.collider.gameObject == potentialTarget.gameObject) { Target = potentialTarget.transform; break; } } } else { //otherwise, close enough Target = potentialTarget.transform; break; } } } //sw.Stop(); //Debug.Log($"Target lookup: {sw.Elapsed.TotalMilliseconds:F4} ms"); } if (!RpgWorldUtils.TargetIsAlive(Target)) { Target = null; } }