using UnityEngine; using SanAndreasUnity.Utilities; using SanAndreasUnity.Importing.Animation; namespace SanAndreasUnity.Behaviours.Peds.States { public class RollState : BaseMovementState { public override AnimId movementAnim { get { return m_rollLeft ? new AnimId ("ped", "Crouch_Roll_L") : new AnimId ("ped", "Crouch_Roll_R"); } } public override AnimId movementWeaponAnim { get { return this.movementAnim; } } private bool m_rollLeft = false; private AnimationState m_animState; const string kRollDirSyncName = "rollDir"; public bool CanRoll() { if( this.IsActiveState ) return false; return true; } public bool Roll(bool left) { if( !this.CanRoll() ) return false; m_rollLeft = left; m_ped.SwitchState(); return true; } protected override void Start() { base.Start(); // subscribe to dictionary event if (m_isClientOnly) m_ped.syncDictionaryStringUint.Callback += this.OnDictChanged; } public override void OnBecameActive () { base.OnBecameActive(); this.PlayAnim(); if (m_isServer) m_ped.syncDictionaryStringUint[kRollDirSyncName] = m_rollLeft ? (uint) 1 : (uint) 0; } public override byte[] GetAdditionalNetworkData() { var writer = new Mirror.NetworkWriter(); writer.Write("roll"); writer.Write(m_rollLeft); return writer.ToArray(); } public override void OnSwitchedStateByServer(byte[] data) { var reader = new Mirror.NetworkReader(data); string magicWord = reader.ReadString(); if (magicWord != "roll") Debug.LogErrorFormat("wrong magic word when switching to roll state: {0}", magicWord); m_rollLeft = reader.ReadBoolean(); m_ped.SwitchState(this.GetType()); } void OnDictChanged(Ped.SyncDictionaryStringUint.Operation op, string key, uint value) { // switch (op) // { // case Ped.SyncDictionaryStringUint.Operation.OP_ADD: // case Ped.SyncDictionaryStringUint.Operation.OP_DIRTY: // case Ped.SyncDictionaryStringUint.Operation.OP_SET: // break; // } //Debug.LogFormat("OnDictChanged() - op: {0}, key: {1}, value: {2}", op, key, value); if (!this.IsActiveState) return; if (key != kRollDirSyncName) return; F.RunExceptionSafe( () => { if (m_ped.syncDictionaryStringUint.ContainsKey(key)) { // roll direction possibly changed m_rollLeft = m_ped.syncDictionaryStringUint[key] == 1; //Debug.LogFormat("roll dir changed - old left: {0}, new left: {1}", oldLeft, m_rollLeft); this.PlayAnim(); m_animState.time = 0f; } }); } protected override void SwitchToMovementState () { // don't switch to any state from here - we'll switch when anim finishes } protected override void SwitchToAimState () { // don't switch to any state from here - we'll switch when anim finishes } protected override void UpdateHeading () { if (m_isServer || m_ped.IsControlledByLocalPlayer) m_ped.Heading = m_ped.transform.forward; } protected override void UpdateRotation () { // don't change rotation } protected override void UpdateMovement () { // adjust movement input before updating movement // because only server performs moving, we'll do this only on server // also, we should preserve movement input - this is needed because, if we exit roll state, // we could quickly switch back to it, because we overrided client's movement input Vector3 originalMovementInput = m_ped.Movement; if (m_isServer) m_ped.Movement = m_rollLeft ? -m_ped.transform.right : m_ped.transform.right; base.UpdateMovement(); m_ped.Movement = originalMovementInput; } void PlayAnim() { m_animState = m_model.PlayAnim( this.movementAnim ); // clients have wrap mode set to 'Loop', because state can be switched very fast between roll and crouchaim, and // server will not update current state syncvar, so client will not start the state again, // and roll state will remain m_animState.wrapMode = WrapMode.Once; m_model.VelocityAxis = 0; // movement will be done along x axis } protected override void UpdateAnims () { // correct local x position of root frame Vector3 pos = m_model.RootFrame.transform.localPosition; pos.x = 0f; m_model.RootFrame.transform.localPosition = pos; } public override void LateUpdateState () { base.LateUpdateState(); if( !this.IsActiveState ) return; if (m_isServer) { // check if anim is finished if( null == m_animState || !m_animState.enabled ) { // anim finished // switch to other state // try to switch to crouch aim state CrouchState.SwitchToAimState(m_ped); if( !this.IsActiveState ) return; // switch to crouch state m_ped.SwitchState(); } } } public override void OnSubmitPressed () { // ignore } public override void OnJumpPressed () { // ignore } public override void OnCrouchButtonPressed () { // ignore } public override void OnFlyButtonPressed () { // ignore } public override void OnFlyThroughButtonPressed () { // ignore } } }