/// <summary> /// Catches the animator event during the execution and may perform the action. Returns true if that is the case. /// </summary> public override bool OnFinishAction() { var count = AIUtil.FindActorsIncludingDead(_targetPosition, Radius); for (int i = 0; i < count; i++) { PlayEffect(AIUtil.Actors[i], AIUtil.Actors[i].transform.position); Perform(AIUtil.Actors[i]); } return(true); }
private void Update() { if (!_actor.IsAlive) { return; } _wait -= Time.deltaTime; if (_wait > float.Epsilon) { _oldVisible.Clear(); _oldVisibleHash.Clear(); for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (actor != null) { _oldVisible.Add(actor); _oldVisibleHash.Add(actor); } } for (int i = 0; i < _oldVisible.Count; i++) { var actor = _oldVisible[i]; if (!actor.IsAlive) { _visible.Remove(actor); _visibleHash.Remove(actor); if (!_seenDeadHash.Contains(actor)) { _seenDeadHash.Add(actor); Message("OnSeeDeath", actor); } } } return; } _wait = Random.Range(UpdateDelay * 0.8f, UpdateDelay * 1.2f); _oldVisible.Clear(); _oldVisibleHash.Clear(); for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (actor != null) { _oldVisible.Add(actor); _oldVisibleHash.Add(actor); } } _visible.Clear(); _visibleHash.Clear(); var count = AIUtil.FindActorsIncludingDead(_actor.TopPosition, Distance, _actor); for (int i = 0; i < count; i++) { var actor = AIUtil.Actors[i]; if (CheckVisibility(actor)) { if (actor.IsAlive) { _visible.Add(actor); _visibleHash.Add(actor); } else { if (!_seenDeadHash.Contains(actor)) { _seenDeadHash.Add(actor); Message("OnSeeDeath", actor); } // A hack, should fix in some other way else if (_brain != null && _brain.Threat == actor) { _brain.OnSeeDeath(actor); } } } } for (int i = 0; i < _oldVisible.Count; i++) { var actor = _oldVisible[i]; if (!_visibleHash.Contains(actor)) { Message("OnUnseeActor", actor); } } for (int i = 0; i < _visible.Count; i++) { var actor = _visible[i]; if (!_oldVisibleHash.Contains(actor)) { Message("OnSeeActor", actor); } } }
private void Update() { if (_autoWait > float.Epsilon) { _autoWait -= Time.deltaTime; } if (_checkWait > float.Epsilon) { _checkWait -= Time.deltaTime; } if (_coverTimer > float.Epsilon) { _coverTimer -= Time.deltaTime; } if (_active != null && (!_active.Update() || (!_active.HasNoTimeout && (Time.timeSinceLevelLoad - _actionStart) > Timeout))) { end(); } if (_actor.Cover == null) { _isInCover = false; _coverTimer = 0; } else if (!_isInCover) { _coverTimer = CoverDelay; _isInCover = true; } if (_active == null && _autoWait <= float.Epsilon && _checkWait <= float.Epsilon && _brain.enabled && _brain.State != FighterState.process && _actor.IsAlive && _coverTimer <= float.Epsilon && (!WaitForCoverActions || !_isTakingCover)) { _checkWait = 0.5f; _nearbyActors.Clear(); { var count = AIUtil.FindActorsIncludingDead(transform.position, AutoDistance); for (int i = 0; i < count; i++) { _nearbyActors.Add(AIUtil.Actors[i]); } } var canUseAttackAction = !AutoAttackOnlyFromCover || _actor.Cover != null; AIAction bestSingleAction = null; Actor bestSingleActionTarget = null; /////////////////////////////////////////////////////// // // CONSIDER MULTIPLE TARGET ACTIONS // /////////////////////////////////////////////////////// AIAction bestMultipleAction = null; int bestMultipleActionTargetCount = 0; for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || action.NeedsSingleTargetActor || action.NeedsTargetLocation || action.NeedsOnlySelf) { continue; } int count = 0; for (int j = 0; j < Actors.Count; j++) { var actor = Actors.Get(j); if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor.Side == _actor.Side) { if (((actor == _actor && action.CanTargetSelf) || action.CanTargetAlly) && action.IsNeededFor(actor)) { count++; } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor) && (!AutoAttackOnlyFromCover || !action.WillMoveForActor(actor))) { count++; } } if (count > bestMultipleActionTargetCount) { bestMultipleAction = action; bestMultipleActionTargetCount = count; } } if (bestMultipleAction != null) { _autoWait = AutoCooldown; Execute(bestMultipleAction); return; } /////////////////////////////////////////////////////// // // CONSIDER SELF ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsOnlySelf) { continue; } if (action.CanTargetSelf && action.IsNeededFor(_actor)) { if (_actor == Priority) { _autoWait = AutoCooldown; Execute(action, _actor); return; } bestSingleAction = action; bestSingleActionTarget = _actor; break; } } /////////////////////////////////////////////////////// // // CONSIDER SINGLE TARGET ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsSingleTargetActor) { continue; } Actor best = null; var bestDistance = 0f; var isBestSelf = false; for (int j = 0; j < _nearbyActors.Count; j++) { var actor = _nearbyActors[j]; if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor == _actor) { if (action.CanTargetSelf && action.IsNeededFor(actor)) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } if (best == null) { best = actor; bestDistance = 0; isBestSelf = true; } } } else if (actor.Side == _actor.Side) { if (action.CanTargetAlly && action.IsNeededFor(actor)) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } var distance = Vector3.Distance(transform.position, actor.transform.position); if (best == null || isBestSelf || distance < bestDistance) { best = actor; bestDistance = distance; isBestSelf = false; } } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor) && (!AutoAttackOnlyFromCover || !action.WillMoveForActor(actor))) { if (actor == Priority) { _autoWait = AutoCooldown; Execute(action, actor); return; } var distance = Vector3.Distance(transform.position, actor.transform.position); if (best == null || isBestSelf || distance < bestDistance) { best = actor; bestDistance = distance; isBestSelf = false; } } } if (best != null) { bestSingleAction = action; bestSingleActionTarget = best; break; } } /////////////////////////////////////////////////////// // // CONSIDER AREA ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || !action.NeedsTargetLocation) { continue; } _possibleActionTargets.Clear(); for (int j = 0; j < _nearbyActors.Count; j++) { var actor = _nearbyActors[j]; if (!actor.IsAlive && action.ShouldIgnoreDead) { continue; } if (actor == _actor) { if (action.CanTargetSelf && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } else if (actor.Side == _actor.Side) { if (action.CanTargetAlly && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } else if (canUseAttackAction && action.CanTargetEnemy && action.IsNeededFor(actor)) { _possibleActionTargets.Add(actor); } } if (_possibleActionTargets.Count == 0 || (_possibleActionTargets.Count == 1 && bestSingleAction != null)) { continue; } if (action.UIRadius > float.Epsilon) { var radius = action.UIRadius; var steps = (int)(2 * AutoDistance / radius); var hasBest = false; var bestPosition = Vector3.zero; var bestDistance = 0f; var bestActorCount = 0; var bestHasPriority = false; for (int x = -steps / 2; x < steps / 2; x++) { for (int y = -steps / 2; y < steps / 2; y++) { var position = transform.position + new Vector3(x * action.UIRadius, 0, y * radius); var distance = Vector3.Distance(position, transform.position); if (distance > AutoDistance) { continue; } if (AutoAttackOnlyFromCover && action.CanTargetEnemy && action.WillMoveForPosition(position)) { continue; } var actorCount = 0; var hasPriority = false; for (int j = 0; j < _possibleActionTargets.Count; j++) { var a = _possibleActionTargets[j]; if (Vector3.Distance(position, a.transform.position) < radius) { actorCount++; if (a == Priority) { hasPriority = true; } } } if (actorCount > 0) { var isBetter = false; if (!hasBest) { isBetter = true; } else if (hasPriority && !bestHasPriority) { isBetter = false; } else if (!bestHasPriority) { if (actorCount > bestActorCount) { isBetter = true; } else if (actorCount == bestActorCount) { isBetter = distance < bestDistance; } } if (isBetter) { hasBest = true; bestPosition = position; bestDistance = distance; bestHasPriority = hasPriority; bestActorCount = actorCount; } } } } if (hasBest && (bestSingleAction == null || bestActorCount > 1)) { _autoWait = AutoCooldown; Execute(action, bestPosition); return; } } } /////////////////////////////////////////////////////// // // EXECUTE BEST SINGLE IF NO AREA POSSIBLE // /////////////////////////////////////////////////////// if (bestSingleAction != null) { _autoWait = AutoCooldown; Execute(bestSingleAction, bestSingleActionTarget); } /////////////////////////////////////////////////////// // // CONSIDER NON-TARGET ACTIONS // /////////////////////////////////////////////////////// for (int i = 0; i < Actions.Length; i++) { var action = Actions[i]; if (action.Wait > float.Epsilon || !action.Auto || action.CanTargetAny || action.CanTargetGround || action.UIRadius > float.Epsilon || action.CanTargetMultiple || action.NeedsOnlySelf) { continue; } _autoWait = AutoCooldown; Execute(action); return; } } }