public void EnterState(ActorAiState newState) { if (LockAiState) { return; } LastAiState = CurrentAiState; ExitState(CurrentAiState); //good place or no? TimeInState = 0; switch (newState) { case ActorAiState.Idle: Target = null; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Idle); MovementComponent.AbortMove(); break; case ActorAiState.Dead: if (CurrentAiState == ActorAiState.Dead) //fix for glitchy looking behaviour { break; } MovementComponent.AbortMove(); MovementComponent.HandleDeath(); if (DieImmediately) { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Dead); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Dying); } AudioComponent.Ref()?.StopLivingSounds(); AudioComponent.Ref()?.PlayDeathSound(); if (InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = false; } if (DestroyOnDeath) { this.gameObject.SetActive(false); //actually destroying the object breaks saving } if (OnDeathSpecial != null) { OnDeathSpecial.Execute(new ActionInvokerData { Activator = this }); } if (Target != null && Target.GetComponent <PlayerController>() && GrantXpOnDeath > 0) //what the f**k { GameState.Instance.PlayerRpgState.GrantXPScaled(GrantXpOnDeath); } break; case ActorAiState.Wandering: Target = null; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); //set initial destination Vector2 newpos = VectorUtils.GetRandomVector2(InitialPosition.GetFlatVector(), WanderRadius); MovementComponent.SetDestination(newpos.GetSpaceVector()); AudioComponent.Ref()?.StartWalkSound(); break; case ActorAiState.Chasing: if (RunOnChase) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } { //set target if (Target == null) { GetSwizzledTarget(); //fix for loading saves } var d = Target.position; //FIXME what if Target is null? MovementComponent.SetDestination(d); } break; case ActorAiState.ScriptedMoveTo: if (RunOnChase) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } MovementComponent.SetDestination(MovementComponent.MovementTarget); break; case ActorAiState.Attacking: if (AttackComponent == null) { Debug.LogError($"{name} tried to attack, but has no attack component!"); EnterState(ActorAiState.Idle); return; } if (Target == null) { GetSwizzledTarget(); //fix for loading saves } //set animation, fire projectile, set timer AttackComponent.BeginAttack(); break; case ActorAiState.Covering: break; case ActorAiState.Hurting: AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Hurting); AudioComponent.Ref()?.PlayPainSound(); break; case ActorAiState.Fleeing: if (RunOnFlee) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } { //set target var d = transform.position + ((Target.position - transform.position).normalized * -(1 + Mathf.Abs(MovementComponent.TargetThreshold))); MovementComponent.SetDestination(d); } break; default: break; } CurrentAiState = newState; }
private void UpdateState() { //forced death check if (CurrentAiState != ActorAiState.Dead) { if (Health <= 0) { EnterState(ActorAiState.Dead); } } //hack to retrieve swizzled target after a load GetSwizzledTarget(); switch (CurrentAiState) { case ActorAiState.Idle: if (Aggressive) { //search for targets, select target SelectTarget(); if (Target != null) { EnterState(ActorAiState.Chasing); } } break; case ActorAiState.Wandering: //TODO aggression if (MovementComponent.DistToTarget <= WanderThreshold || TimeInState >= WanderTimeout) { Vector2 newpos = VectorUtils.GetRandomVector2(InitialPosition.GetFlatVector(), WanderRadius); MovementComponent.SetDestination(newpos.GetSpaceVector()); TimeInState = 0; } if (Aggressive) { //search for targets, select target SelectTarget(); if (Target != null) { EnterState(ActorAiState.Chasing); AudioComponent.Ref()?.PlayAlertSound(); } } break; case ActorAiState.Chasing: if (!RpgWorldUtils.TargetIsAlive(Target)) { EnterState(BaseAiState); break; } if ((MetaState.Instance.SessionFlags.Contains("NoTarget") || GameState.Instance.PlayerFlags.Contains(PlayerFlags.NoTarget)) && Target.GetComponent <PlayerController>()) { EnterState(BaseAiState); break; } if (AttackComponent != null && AttackComponent.ReadyToAttack && AttackComponent.TargetInRange) { EnterState(ActorAiState.Attacking); return; } else { //set target var d = Target.position; MovementComponent.SetDestination(d); } if (!Relentless) { //break off if we are too far away or too badly hurt if (Health <= (MaxHealth * 0.2f)) { EnterState(ActorAiState.Fleeing); } else if ((Target.position - transform.position).magnitude > SearchRadius) { EnterState(BaseAiState); Target = null; } } break; case ActorAiState.ScriptedMoveTo: if (MovementComponent.AtTarget) //we made it! { EnterState(ActorAiState.Idle); //don't wander off if you were sent there! } break; case ActorAiState.Attacking: //wait... if (!AttackComponent.DidAttack && AttackComponent.WarmupIsDone) { AttackComponent.DoAttack(); //waaaaay too complicated to cram here } if (AttackComponent.AttackIsDone) { //just return if (!RpgWorldUtils.TargetIsAlive(Target)) { EnterState(BaseAiState); } else { EnterState(ActorAiState.Chasing); } } break; case ActorAiState.Hurting: if (TimeInState >= PainWaitTime) { if (BeenHit && Target != null) { EnterState(ActorAiState.Chasing); } else { EnterState(LastAiState); } } break; case ActorAiState.Fleeing: //stop running if far enough away, or target is gone if (!RpgWorldUtils.TargetIsAlive(Target) || (Target.position - transform.position).magnitude > SearchRadius) { EnterState(BaseAiState); Target = null; break; } { //set target var d = transform.position + ((Target.position - transform.position).normalized * -(1 + Mathf.Abs(MovementComponent.TargetThreshold))); MovementComponent.SetDestination(d); } break; } }
public void EnterState(ActorAiState newState) { if (LockAiState) { return; } if (newState != CurrentAiState) { LastAiState = CurrentAiState; } ExitState(CurrentAiState); //good place or no? TimeInState = 0; switch (newState) { case ActorAiState.Idle: Target = null; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Idle); MovementComponent.AbortMove(); break; case ActorAiState.Dead: { if (CurrentAiState == ActorAiState.Dead) //fix for glitchy looking behaviour { break; } MovementComponent.AbortMove(); MovementComponent.HandleDeath(); var deathStateArgs = new DeathStateActorAnimationArgs() { DamageEffector = LastHit?.DamageEffector ?? 0, DamageType = LastHit?.DamageType ?? 0, ExtremeDeath = WasExtremeDeath, HitLocation = LastHit?.HitLocation ?? 0, HitMaterial = LastHit?.HitMaterial ?? 0 }; if (DieImmediately) { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Dead, deathStateArgs); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Dying, deathStateArgs); } AudioComponent.Ref()?.StopLivingSounds(); if (WasExtremeDeath) { AudioComponent.Ref()?.PlayExtremeDeathSound(); } else { AudioComponent.Ref()?.PlayDeathSound(); } if (InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = false; } if (DestroyOnDeath) { this.gameObject.SetActive(false); //actually destroying the object breaks saving } if (OnDeathSpecial != null) { OnDeathSpecial.Execute(new ActionInvokerData { Activator = this }); } if (DisableHitboxesOnDeath) { var hitboxComponents = GetComponentsInChildren <IHitboxComponent>(true); foreach (var hitboxComponent in hitboxComponents) { if (hitboxComponent is MonoBehaviour mb) //IHitboxComponent does not actually imply MonoBehaviour { mb.gameObject.SetActive(false); } } } if (DisableCollidersOnDeath) { var colliders = GetComponentsInChildren <Collider>(true); foreach (var collider in colliders) { collider.enabled = false; } } if ( ((LastHit != null && LastHit.Value.Originator != null && LastHit.Value.Originator is PlayerController) || (Target != null && Target.GetComponent <PlayerController>()) ) && GrantXpOnDeath > 0 ) { GameState.Instance.PlayerRpgState.GrantXPScaled(GrantXpOnDeath); } } break; case ActorAiState.Wandering: Target = null; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); //set initial destination Vector2 newpos = VectorUtils.GetRandomVector2(InitialPosition.GetFlatVector(), WanderRadius); MovementComponent.SetDestination(newpos.GetSpaceVector()); AudioComponent.Ref()?.StartWalkSound(); break; case ActorAiState.Chasing: if (RunOnChase) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } { //set target if (Target == null) { GetSwizzledTarget(); //fix for loading saves } SetChaseDestination(); } break; case ActorAiState.ScriptedMoveTo: if (RunOnChase) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } MovementComponent.SetDestination(MovementComponent.MovementTarget); break; case ActorAiState.Attacking: if (AttackComponent == null) { Debug.LogError($"{name} tried to attack, but has no attack component!"); EnterState(ActorAiState.Idle); return; } if (Target == null) { GetSwizzledTarget(); //fix for loading saves } //set animation, fire projectile, set timer AttackComponent.BeginAttack(); break; case ActorAiState.Covering: break; case ActorAiState.Hurting: AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Hurting); AudioComponent.Ref()?.PlayPainSound(); break; case ActorAiState.Fleeing: if (RunOnFlee) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); AudioComponent.Ref()?.StartRunSound(); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); AudioComponent.Ref()?.StartWalkSound(); } { //set target var d = transform.position + ((Target.position - transform.position).normalized * -(1 + Mathf.Abs(MovementComponent.TargetThreshold))); MovementComponent.SetDestination(d); } break; case ActorAiState.ScriptedAction: //nop break; default: break; } CurrentAiState = newState; }