public static Vector3 GetClosestHitIgnoreSide(Vector3 origin, Vector3 target, float minDistance, int side, out Vector3 normal) { var vector = (target - origin).normalized; var maxDistance = Vector3.Distance(origin, target); var closestHit = target; var count = Physics.RaycastNonAlloc(origin, vector, Hits); normal = Vector3.zero; for (int i = 0; i < count; i++) { var hit = Hits[i]; if (!hit.collider.isTrigger) { if (hit.distance > minDistance && hit.distance < maxDistance) { var actor = Actors.Get(hit.collider.gameObject); if (actor == null || actor.Side != side) { maxDistance = hit.distance; closestHit = hit.point; normal = hit.normal; } } } } return(closestHit); }
public static bool IsFree(GameObject gameObject, Vector3 origin, Vector3 direction, float distance, bool coverMeansFree, bool actorMeansFree) { var count = Physics.RaycastNonAlloc(origin, direction, Hits, distance); var isFree = true; for (int i = 0; i < count; i++) { if (coverMeansFree && Hits[i].collider.isTrigger && Hits[i].collider.GetComponent <Cover>() != null) { return(true); } if (actorMeansFree && !Hits[i].collider.isTrigger && Actors.Get(Hits[i].collider.gameObject) != null) { return(true); } if (!Hits[i].collider.isTrigger && !InHiearchyOf(Hits[i].collider.gameObject, gameObject)) { isFree = false; } } return(isFree); }
public static Vector3 GetClosestNonActorHit(Vector3 origin, Vector3 target, float minDistance, GameObject ignore = null) { var vector = (target - origin).normalized; var maxDistance = Vector3.Distance(origin, target); var closestHit = target; var count = Physics.RaycastNonAlloc(origin, vector, Hits); for (int i = 0; i < count; i++) { var hit = Hits[i]; if (hit.collider.gameObject != ignore && !hit.collider.isTrigger) { if (hit.distance > minDistance && hit.distance < maxDistance) { if (Actors.Get(hit.collider.gameObject) == null) { maxDistance = hit.distance; closestHit = hit.point; } } } } return(closestHit); }
/// <summary> /// Notified of a successful melee hit. /// </summary> public void OnSuccessfulHit(Hit hit) { if (!hit.IsMelee) { return; } var target = Actors.Get(hit.Target); if (target == null) { return; } if (target.Side == _actor.Side) { return; } _hits++; if (_hits >= MeleeHits) { ToStopAssault(); } }
public void Regroup() { _friends.Clear(); _takenPositions.Clear(); var count = Physics.OverlapSphereNonAlloc(_actor.transform.position, CallDistance, _colliders, 0x1, QueryTriggerInteraction.Ignore); var limit = Limit; for (int i = 0; i < count; i++) { var friend = Actors.Get(_colliders[i].gameObject); if (friend != null && friend != _actor && friend.IsAlive && friend.Side == _actor.Side) { var distance = Vector3.Distance(_actor.transform.position, friend.transform.position); if (distance < CallDistance) { if (limit <= 0) { break; } else { limit--; } friend.SendMessage("ToLeaveCover"); _friends.Add(friend); } } } foreach (var friend in _friends) { friend.SendMessage("ToRegroupAround", this); } }
/// <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() { for (int i = 0; i < Actors.Count; i++) { var actor = Actors.Get(i); var isAlly = actor.Side == _actor.Side; var isSelf = actor == _actor; if (isSelf && !CanTargetSelf) { continue; } if (actor.isActiveAndEnabled && ((CanTargetAlly && isAlly) || (CanTargetEnemy && !isAlly) || (CanTargetSelf && isSelf))) { PlayEffect(actor, actor.transform.position); Perform(actor); } } return(true); }
private void Update() { if (!_actor.IsAlive) { return; } _wait -= Time.deltaTime; if (DebugFriends) { foreach (var friend in _friends) { if (friend != null) { Debug.DrawLine(friend.transform.position, transform.position, Color.yellow); } } } if (_wait > float.Epsilon) { return; } _wait = Random.Range(UpdateDelay * 0.8f, UpdateDelay * 1.2f); _oldFriends.Clear(); foreach (var friend in _friends) { if (friend != null) { _oldFriends.Add(friend); } } _friends.Clear(); foreach (var friend in _oldFriends) { var distance = Vector3.Distance(_actor.transform.position, friend.transform.position); if (distance < Distance && friend.IsAlive) { _friends.Add(friend); } else { Message("OnLostFriend", friend); var comm = get(friend); if (comm != null) { if (comm._friends.Contains(_actor)) { comm._friends.Remove(_actor); comm.Message("OnLostFriend", _actor); } } } } var count = Physics.OverlapSphereNonAlloc(_actor.transform.position, Distance, Util.Colliders, Layers.Character, QueryTriggerInteraction.Ignore); for (int i = 0; i < count; i++) { var friend = Actors.Get(Util.Colliders[i].gameObject); if (friend != null && friend != _actor && friend.IsAlive && friend.Side == _actor.Side && !_oldFriends.Contains(friend)) { var distance = Vector3.Distance(_actor.transform.position, friend.transform.position); if (distance < Distance) { var comm = get(friend); if (comm != null) { Message("OnFoundFriend", friend); _friends.Add(friend); comm._stayFriends.Add(_actor); if (!comm._friends.Contains(_actor)) { comm._friends.Add(_actor); comm.Message("OnFoundFriend", _actor); } } } } } _stayFriends.Clear(); }
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; } } }
/// <summary> /// Broadcasts an alert. /// </summary> public void OnHit(Hit hit) { Alerts.Broadcast(hit.Position, Range, true, Actors.Get(hit.Attacker), false); }
private void Update() { if (!_actor.IsAlive || !_canCall) { return; } if (_firstTriggerWait < FirstCheckDelay) { _firstTriggerWait += Time.deltaTime; return; } if (_triggerSpacing >= float.Epsilon) { _triggerSpacing -= Time.deltaTime; return; } var count = _nearbyFriends.Count; foreach (var friend in _visibleFriends) { if (!_nearbyFriends.Contains(friend)) { count++; } } if (count <= FriendCount) { var levelCount = 0; for (int i = 0; i < Actors.Count; i++) { var actor = Actors.Get(i); if (actor.IsAlive && actor.Side == _actor.Side && actor.IsAggressive) { levelCount++; } } if (levelCount < MaxLevelCount) { if (_triggerWait >= CheckDuration) { if (!OnlyCallInCover || _motor.IsInCover) { _triggerWait = 0; _triggerSpacing = CheckSpacing; _isWaitingForCall = true; Message("ToMakeCall"); } } else { _triggerWait += Time.deltaTime; } } else { _triggerWait = 0; } } else { _triggerWait = 0; } }
private void Update() { if (!_actor.IsAlive) { return; } _wait -= Time.deltaTime; if (_wait > float.Epsilon) { return; } _wait = Random.Range(UpdateDelay * 0.8f, UpdateDelay * 1.2f); _oldVisible.Clear(); foreach (var actor in _visible) { _oldVisible.Add(actor); } _visible.Clear(); foreach (var actor in _oldVisible) { if (actor != null && actor.IsAlive && AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView)) { _visible.Add(actor); } else { Message("OnUnseeActor", actor); if (!actor.IsAlive) { _knownAlive.Remove(actor); Message("OnSeeDeath", actor); } } } var count = Physics.OverlapSphereNonAlloc(_actor.TopPosition, Distance, _colliders, 0x1, QueryTriggerInteraction.Ignore); for (int i = 0; i < count; i++) { var actor = Actors.Get(_colliders[i].gameObject); if (actor != null && actor != _actor && actor.IsAlive && !_oldVisible.Contains(actor)) { if (AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView)) { _visible.Add(actor); Message("OnSeeActor", actor); } } } _oldKnownAlive.Clear(); foreach (var known in _knownAlive) { _oldKnownAlive.Add(known); } _knownAlive.Clear(); foreach (var actor in _oldKnownAlive) { if (_visible.Contains(actor)) { _knownAlive.Add(actor); } else if (AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView)) { Message("OnSeeDeath", actor); } else { _knownAlive.Add(actor); } } }
/// <summary> /// Finds an object and a hit position a bullet would hit if fired. Checks if it is a friend. /// </summary> public RaycastHit Raycast(Vector3 origin, Vector3 direction, out bool isFriend, bool friendCheck) { RaycastHit closestHit = new RaycastHit(); float closestDistance = Distance * 10; var minDistance = 0f; if (_isUsingCustomRaycast) { minDistance = Vector3.Distance(Origin, RaycastOrigin); } if (minDistance > 0.5f) { minDistance -= 0.5f; } isFriend = false; var count = Physics.RaycastNonAlloc(origin, direction, _hits, Distance); for (int i = 0; i < count; i++) { var hit = _hits[i]; if (Character != null && Util.InHiearchyOf(hit.collider.gameObject, Character.gameObject)) { continue; } if (hit.distance < closestDistance && hit.distance > minDistance) { var isOk = true; var isShield = false; if (hit.collider.isTrigger) { if (BodyPartHealth.Contains(hit.collider.gameObject)) { isOk = true; } else { var shield = BulletShield.Get(hit.collider.gameObject); if (shield != null) { if (Vector3.Dot(shield.transform.forward, hit.normal) >= -0.2f) { isOk = true; isShield = true; } else { isOk = false; } } else { isOk = false; } } } else { var health = CharacterHealth.Get(hit.collider.gameObject); if (health != null) { isOk = health.IsRegisteringHits; } } if (isOk) { if (!isShield && (_isIgnoringSelf || _hasFireCondition) && friendCheck) { var root = getHealthTarget(hit.collider.gameObject); if (root != null) { if (_isIgnoringSelf && Character != null && root == Character.gameObject) { isFriend = true; } else if (_hasFireCondition) { var actor = Actors.Get(root); if (actor != null) { isFriend = actor.Side == _fireConditionSide; } else { isFriend = false; } } else { isFriend = false; } } else { isFriend = false; } } closestHit = hit; closestDistance = hit.distance; } } } return(closestHit); }