private void OnActorAiState(SocketAsyncEventArgs args, byte[] bytes) { ActorAiState input = ActorAiState.Parser.ParseFrom(bytes); if (input.RoomId != RoomId) { return; // 不是自己房间的消息,略过 } if (input.AiCellIndexFrom == 0) { string msg = $"Actor position is lost!"; ServerRoomManager.Instance.Log("RoomLogic OnActorAiState Error - " + msg); ActorAiStateReply output2 = new ActorAiStateReply() { RoomId = input.RoomId, OwnerId = input.OwnerId, ActorId = input.ActorId, Ret = false, ErrMsg = msg, }; ServerRoomManager.Instance.SendMsg(args, ROOM_REPLY.ActorAiStateReply, output2.ToByteArray()); return; } // 更新单元Ai信息,在服务器的ActorBehaviour里保存一份 var ab = ActorManager.GetActor(input.ActorId); if (ab != null) { ab.AiState = input.AiState; ab.CellIndex = input.AiCellIndexFrom; ab.AiCellIndexTo = input.AiCellIndexTo; ab.AiTargetId = input.AiTargetId; ab.Orientation = input.Orientation; ab.AiDurationTime = input.AiDurationTime; ab.AiTotalTime = input.AiTotalTime; ab.AiStartTime = DateTime.Now; // 记录得到当前状态的时间, 在存盘的时候再记录一下时间, 把剩余时间保存起来 } // 注意: 有一种情况, 可能是ab并不存在,但是这条消息还是有用的,因为ActorRemoveReply消息里, 服务器已经把本单元删掉了, // 但是服务器仍然需要转发一条将单位状态设置为StateEnum.DIE的消息 ActorAiStateReply output = new ActorAiStateReply() { RoomId = input.RoomId, OwnerId = input.OwnerId, ActorId = input.ActorId, AiState = input.AiState, AiCellIndexFrom = input.AiCellIndexFrom, AiCellIndexTo = input.AiCellIndexTo, AiTargetId = input.AiTargetId, Orientation = input.Orientation, AiDurationTime = input.AiDurationTime, AiTotalTime = input.AiTotalTime, Ret = true, }; BroadcastMsg(ROOM_REPLY.ActorAiStateReply, output.ToByteArray()); }
public void ForceEnterState(ActorAiState newState) { //disgusting hack, need to rework how this is handled bool oldLockAiState = LockAiState; LockAiState = false; EnterState(newState); LockAiState = oldLockAiState; }
public override void RestoreEntityData(Dictionary <string, object> data) { base.RestoreEntityData(data); if (data.ContainsKey("Actor")) { ActorExtraData actorData = data["Actor"] as ActorExtraData; if (actorData != null) { //restore! CurrentAiState = actorData.CurrentAiState; LastAiState = actorData.LastAiState; LockAiState = actorData.LockAiState; if (AnimationComponent != null) { AnimationComponent.CurrentAnimState = actorData.CurrentAnimState; AnimationComponent.LockAnimState = actorData.LockAnimState; } SavedTarget = actorData.SavedTarget; TimeInState = actorData.TimeInState; Health = actorData.Health; BeenHit = actorData.BeenHit; LastHit = actorData.LastHit; LastHitDamage = actorData.LastHitDamage; WasExtremeDeath = actorData.WasExtremeDeath; MovementComponent.BeforeRestore(data); MovementComponent.MovementTarget = actorData.AltTarget; MovementComponent.IsRunning = actorData.IsRunning; if (CurrentAiState == ActorAiState.Dead) { MovementComponent.HandleDeath(); } if (InteractionComponent != null) { InteractionComponent.InteractionDisabledByHit = actorData.InteractionForceDisabled; if (data.ContainsKey("Container")) { InteractionComponent.CorpseContainer = SerializableContainerModel.MakeContainerModel((SerializableContainerModel)data["Container"]); } } } else { CDebug.LogEx(string.Format("Invalid actor data for {0} found on restore!", this.name), LogLevel.Error, this); } } else { CDebug.LogEx(string.Format("No actor data for {0} found on restore!", this.name), LogLevel.Error, this); } }
public void TriggerTransition(StateEnum newState, int cellIndex = 0, long actorId = 0, float durationTime = 0, float totalTime = 0) { if (IsValidTransition(newState)) { SetTarget(cellIndex, actorId); var ab = _actorBehaviour; ActorAiState output = new ActorAiState() { RoomId = ab.RoomId, OwnerId = ab.OwnerId, ActorId = ab.ActorId, AiState = (int)newState, AiCellIndexFrom = ab.CellIndex, AiCellIndexTo = TargetCellIndex, AiTargetId = TargetActorId, Orientation = ab.Orientation, AiDurationTime = durationTime, AiTotalTime = totalTime, }; GameRoomManager.Instance.SendMsg(ROOM.ActorAiState, output.ToByteArray()); var oldState = CurrentAiState; CurrentAiState = newState; TransitionTo(newState); _startTime = Time.time; // 必须放在后面,因为这个数值大多在Tick()和Exit()里进行判定,而在Enter()里是没用的 _durationTime = durationTime; _totalTime = totalTime; if (logChanges) { Debug.Log( $"ActorStateMachine: <{ab.ActorId}> State changed from<{oldState}> to<{newState}> - TargetPosition:<{TargetPosX},{TargetPosZ}> DurationTime:<{output.AiDurationTime}> -Time:{DateTime.Now.ToLongTimeString()}"); } } else { Debug.LogErrorFormat("ActorStateMachine: Invalid transition from {0} to {1} detected.", CurrentAiState, newState); } }
private void ExitState(ActorAiState oldState) { if (LockAiState) { return; } //TODO we may need this at some point switch (oldState) { case ActorAiState.Idle: break; case ActorAiState.Dead: break; case ActorAiState.Wandering: MovementComponent.AbortMove(); AudioComponent.Ref()?.StopMoveSound(); break; case ActorAiState.Chasing: MovementComponent.AbortMove(); AudioComponent.Ref()?.StopMoveSound(); break; case ActorAiState.Attacking: AttackComponent.Ref()?.EndAttack(); break; case ActorAiState.Covering: break; case ActorAiState.Fleeing: MovementComponent.AbortMove(); AudioComponent.Ref()?.StopMoveSound(); break; default: break; } }
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); } 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) { 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()); break; case ActorAiState.Chasing: if (RunOnChase) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); } { //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); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); } 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); break; case ActorAiState.Fleeing: if (RunOnFlee) { MovementComponent.IsRunning = true; AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Running); } else { AnimationComponent.Ref()?.SetAnimation(ActorAnimState.Walking); } { //set target var d = transform.position + ((Target.position - transform.position).normalized * -1); MovementComponent.SetDestination(d); } break; default: break; } CurrentAiState = newState; }
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; }