void Update()
        {
            if (Physics.RaycastNonAlloc(
                    ray: m_Camera.ScreenPointToRay(Input.mousePosition),
                    results: m_UpdateResult,
                    maxDistance: float.PositiveInfinity,
                    layerMask: m_GroundLayerMask) > 0)
            {
                transform.position = m_UpdateResult[0].point;
            }

            float range     = GameDataSource.Instance.ActionDataByType[m_ActionType].Range;
            bool  isInRange = (m_Origin - transform.position).sqrMagnitude <= range * range;

            m_InRangeVisualization.SetActive(isInRange);
            m_OutOfRangeVisualization.SetActive(!isInRange);

            if (Input.GetMouseButtonUp(0))
            {
                if (isInRange)
                {
                    var data = new ActionRequestData
                    {
                        Position       = transform.position,
                        ActionTypeEnum = m_ActionType,
                        ShouldQueue    = false,
                        TargetIds      = null
                    };
                    m_PlayerOwner.RecvDoActionServerRPC(data);
                }
                Destroy(gameObject);
                return;
            }
        }
Ejemplo n.º 2
0
        public override void NetworkStart()
        {
            if (!IsServer)
            {
                enabled = false;
            }
            else
            {
                NetState = GetComponent <NetworkCharacterState>();
                NetState.DoActionEventServer             += OnActionPlayRequest;
                NetState.ReceivedClientInput             += OnClientMoveRequest;
                NetState.OnStopChargingUpServer          += OnStoppedChargingUp;
                NetState.NetworkLifeState.OnValueChanged += OnLifeStateChanged;


                NetState.ApplyCharacterData();

                if (m_StartingAction != ActionType.None)
                {
                    var startingAction = new ActionRequestData()
                    {
                        ActionTypeEnum = m_StartingAction
                    };
                    PlayAction(ref startingAction);
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Targeted skills should implicitly set the active target of the character, if not already set.
        /// </summary>
        /// <param name="baseIndex">The new index of the base action in m_Queue</param>
        /// <returns></returns>
        private int SynthesizeTargetIfNecessary(int baseIndex)
        {
            Action baseAction = m_Queue[baseIndex];
            var    targets    = baseAction.Data.TargetIds;

            if (targets != null && targets.Length == 1 && targets[0] != m_Parent.NetState.TargetId.Value)
            {
                //if this is a targeted skill (with a single requested target), and it is different from our
                //active target, then we synthesize a TargetAction to change  our target over.

                ActionRequestData data = new ActionRequestData
                {
                    ActionTypeEnum = ActionType.GeneralTarget,
                    TargetIds      = baseAction.Data.TargetIds
                };

                //this shouldn't run redundantly, because the next time the base Action comes up to play, its Target
                //and the active target in our NetState should match.
                Action targetAction = Action.MakeAction(m_Parent, ref data);
                m_Queue.Insert(baseIndex, targetAction);
                return(baseIndex + 1);
            }

            return(baseIndex);
        }
        /// <summary>
        /// Populates the ActionRequestData with additional information. The TargetIds of the action should already be set before calling this.
        /// </summary>
        /// <param name="hitPoint">The point in world space where the click ray hit the target.</param>
        /// <param name="action">The action to perform (will be stamped on the resultData)</param>
        /// <param name="resultData">The ActionRequestData to be filled out with additional information.</param>
        private void PopulateSkillRequest(Vector3 hitPoint, ActionType action, ref ActionRequestData resultData)
        {
            resultData.ActionTypeEnum = action;
            var actionInfo = GameDataSource.Instance.ActionDataByType[action];

            //most skill types should implicitly close distance. The ones that don't are explicitly set to false in the following switch.
            resultData.ShouldClose = true;

            switch (actionInfo.Logic)
            {
            //for projectile logic, infer the direction from the click position.
            case ActionLogic.LaunchProjectile:
                Vector3 offset = hitPoint - transform.position;
                offset.y               = 0;
                resultData.Direction   = offset.normalized;
                resultData.ShouldClose = false;     //why? Because you could be lining up a shot, hoping to hit other people between you and your target. Moving you would be quite invasive.
                return;

            case ActionLogic.Target:
                resultData.ShouldClose = false;
                return;

            case ActionLogic.Emote:
                resultData.CancelMovement = true;
                return;

            case ActionLogic.RangedFXTargeted:
                if (resultData.TargetIds == null)
                {
                    resultData.Position = hitPoint;
                }
                return;
            }
        }
        public static ActionFX MakeActionFX(ref ActionRequestData data, ClientCharacterVisualization parent)
        {
            ActionLogic logic = GameDataSource.Instance.ActionDataByType[data.ActionTypeEnum].Logic;

            switch (logic)
            {
            case ActionLogic.Melee: return(new MeleeActionFX(ref data, parent));

            case ActionLogic.RangedFXTargeted: return(new FXProjectileTargetedActionFX(ref data, parent));

            case ActionLogic.Trample: return(new TrampleActionFX(ref data, parent));

            case ActionLogic.AoE: return(new AoeActionFX(ref data, parent));

            case ActionLogic.Stunned: return(new AnimationOnlyActionFX(ref data, parent));

            case ActionLogic.Target: return(new TargetActionFX(ref data, parent));

            case ActionLogic.ChargedShield:
            case ActionLogic.ChargedLaunchProjectile: return(new ChargedActionFX(ref data, parent));

            case ActionLogic.StealthMode: return(new StealthModeActionFX(ref data, parent));

            default: throw new System.NotImplementedException();
            }
        }
Ejemplo n.º 6
0
        private void OnActionPlayRequest(ActionRequestData data)
        {
            if (!GameDataSource.Instance.ActionDataByType[data.ActionTypeEnum].IsFriendly)
            {
                // notify running actions that we're using a new attack. (e.g. so Stealth can cancel itself)
                RunningActions.OnGameplayActivity(Action.GameplayActivity.UsingAttackAction);
            }

            PlayAction(ref data);
        }
        public override void Update()
        {
            if (!m_Brain.IsAppropriateFoe(m_Foe))
            {
                // time for a new foe!
                m_Foe = ChooseFoe();
                // whatever we used to be doing, stop that. New plan is coming!
                m_ActionPlayer.ClearActions(true);
            }

            // if we're out of foes, stop! IsEligible() will now return false so we'll soon switch to a new state
            if (!m_Foe)
            {
                return;
            }

            // see if we're already chasing or attacking our active foe!
            if (m_ActionPlayer.GetActiveActionInfo(out var info))
            {
                if (info.ActionTypeEnum == ActionType.GeneralChase)
                {
                    if (info.TargetIds != null && info.TargetIds[0] == m_Foe.NetworkObjectId)
                    {
                        // yep we're chasing our foe; all set! (The attack is enqueued after it)
                        return;
                    }
                }
                else if (info.ActionTypeEnum == m_CurAttackAction)
                {
                    if (info.TargetIds != null && info.TargetIds[0] == m_Foe.NetworkObjectId)
                    {
                        // yep we're attacking our foe; all set!
                        return;
                    }
                }
                else if (info.ActionTypeEnum == ActionType.Stun)
                {
                    // we can't do anything right now. We're stunned!
                    return;
                }
            }

            // Choose whether we can attack our foe directly, or if we need to get closer first
            var attackInfo = GetCurrentAttackInfo();
            var attackData = new ActionRequestData
            {
                ActionTypeEnum = attackInfo.ActionTypeEnum,
                TargetIds      = new ulong[] { m_Foe.NetworkObjectId },
                ShouldClose    = true
            };

            m_ActionPlayer.PlayAction(ref attackData);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Play a sequence of actions!
        /// </summary>
        public void PlayAction(ref ActionRequestData action)
        {
            //the character needs to be alive in order to be able to play actions
            if (NetState.NetworkLifeState.Value == LifeState.Alive && !m_Movement.IsPerformingForcedMovement())
            {
                if (action.CancelMovement)
                {
                    m_Movement.CancelMove();
                }

                m_ActionPlayer.PlayAction(ref action);
            }
        }
Ejemplo n.º 9
0
 /// <summary>
 /// If an Action is active, fills out 'data' param and returns true. If no Action is active, returns false.
 /// This only refers to the blocking action! (multiple non-blocking actions can be running in the background, and
 /// this will still return false).
 /// </summary>
 public bool GetActiveActionInfo(out ActionRequestData data)
 {
     if (m_Queue.Count > 0)
     {
         data = m_Queue[0].Data;
         return(true);
     }
     else
     {
         data = new ActionRequestData();
         return(false);
     }
 }
Ejemplo n.º 10
0
 public override bool ChainIntoNewAction(ref ActionRequestData newAction)
 {
     if (m_WasStunned)
     {
         newAction = new ActionRequestData()
         {
             ActionTypeEnum = ActionType.Stun,
             ShouldQueue    = false,
         };
         return(true);
     }
     return(false);
 }
        /// <summary>
        /// Perform a skill in response to some input trigger. This is the common method to which all input-driven skill plays funnel.
        /// </summary>
        /// <param name="actionType">The action you want to play. Note that "Skill1" may be overriden contextually depending on the target.</param>
        /// <param name="triggerStyle">What sort of input triggered this skill?</param>
        /// <param name="targetId">(optional) Pass in a specific networkID to target for this action</param>
        private void PerformSkill(ActionType actionType, SkillTriggerStyle triggerStyle, ulong targetId = 0)
        {
            Transform hitTransform = null;

            if (targetId != 0)
            {
                // if a targetId is given, try to find the object
                NetworkObject targetNetObj;
                if (NetworkSpawnManager.SpawnedObjects.TryGetValue(targetId, out targetNetObj))
                {
                    hitTransform = targetNetObj.transform;
                }
            }
            else
            {
                // otherwise try to find an object under the input position
                int numHits = 0;
                if (triggerStyle == SkillTriggerStyle.MouseClick)
                {
                    var ray = m_MainCamera.ScreenPointToRay(Input.mousePosition);
                    numHits = Physics.RaycastNonAlloc(ray, k_CachedHit, k_MouseInputRaycastDistance, k_ActionLayerMask);
                }

                int networkedHitIndex = -1;
                for (int i = 0; i < numHits; i++)
                {
                    if (k_CachedHit[i].transform.GetComponent <NetworkObject>())
                    {
                        networkedHitIndex = i;
                        break;
                    }
                }

                hitTransform = networkedHitIndex >= 0 ? k_CachedHit[networkedHitIndex].transform : null;
            }

            if (GetActionRequestForTarget(hitTransform, actionType, triggerStyle, out ActionRequestData playerAction))
            {
                //Don't trigger our move logic for another 500ms. This protects us from moving  just because we clicked on them to target them.
                m_LastSentMove = Time.time + k_TargetMoveTimeout;

                m_NetworkCharacter.RecvDoActionServerRPC(playerAction);
            }
            else if (actionType != ActionType.GeneralTarget)
            {
                // clicked on nothing... perform a "miss" attack on the spot they clicked on
                var data = new ActionRequestData();
                PopulateSkillRequest(k_CachedHit[0].point, actionType, ref data);
                m_NetworkCharacter.RecvDoActionServerRPC(data);
            }
        }
        private void Start()
        {
            // get our particle near the right spot!
            transform.position = m_PlayerOwner.transform.position;

            m_StartTime = Time.time;
            // right now we only support "untargeted" charged attacks.
            // Will need more input (e.g. click position) for fancier types of charged attacks!
            var data = new ActionRequestData
            {
                Position       = transform.position,
                ActionTypeEnum = m_ActionType,
                ShouldQueue    = false,
                TargetIds      = null
            };

            m_PlayerOwner.RecvDoActionServerRPC(data);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Synthesizes a Chase Action for the action at the Head of the queue, if necessary (the base action must have a target,
        /// and must have the ShouldClose flag set). This method must not be called when the queue is empty.
        /// </summary>
        /// <returns>The new index of the Action being operated on.</returns>
        private int SynthesizeChaseIfNecessary(int baseIndex)
        {
            Action baseAction = m_Queue[baseIndex];

            if (baseAction.Data.ShouldClose && baseAction.Data.TargetIds != null)
            {
                ActionRequestData data = new ActionRequestData
                {
                    ActionTypeEnum = ActionType.GeneralChase,
                    TargetIds      = baseAction.Data.TargetIds,
                    Amount         = baseAction.Description.Range
                };
                baseAction.Data.ShouldClose = false; //you only get to do this once!
                Action chaseAction = Action.MakeAction(m_Parent, ref data);
                m_Queue.Insert(baseIndex, chaseAction);
                return(baseIndex + 1);
            }
            return(baseIndex);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Perform a sequence of actions.
        /// </summary>
        public void PlayAction(ref ActionRequestData action)
        {
            if (!action.ShouldQueue && m_Queue.Count > 0 && m_Queue[0].Description.ActionInterruptible)
            {
                ClearActions(false);
            }

            if (GetQueueTimeDepth() >= k_MaxQueueTimeDepth)
            {
                //the queue is too big (in execution seconds) to accommodate any more actions, so this action must be discarded.
                return;
            }

            var newAction = Action.MakeAction(m_Parent, ref action);

            m_Queue.Add(newAction);
            if (m_Queue.Count == 1)
            {
                StartAction();
            }
        }
        /// <summary>
        /// Factory method that creates Actions from their request data.
        /// </summary>
        /// <param name="parent">The component that owns the ActionPlayer this action is running on. </param>
        /// <param name="data">the data to instantiate this skill from. </param>
        /// <returns>the newly created action. </returns>
        public static Action MakeAction(ServerCharacter parent, ref ActionRequestData data)
        {
            if (!GameDataSource.Instance.ActionDataByType.TryGetValue(data.ActionTypeEnum, out var actionDesc))
            {
                throw new System.Exception($"Trying to create Action {data.ActionTypeEnum} but it isn't defined on the GameDataSource!");
            }

            var logic = actionDesc.Logic;

            switch (logic)
            {
            case ActionLogic.Melee: return(new MeleeAction(parent, ref data));

            case ActionLogic.AoE: return(new AoeAction(parent, ref data));

            case ActionLogic.Chase: return(new ChaseAction(parent, ref data));

            case ActionLogic.Revive: return(new ReviveAction(parent, ref data));

            case ActionLogic.RangedFXTargeted: return(new FXProjectileTargetedAction(parent, ref data));

            case ActionLogic.LaunchProjectile: return(new LaunchProjectileAction(parent, ref data));

            case ActionLogic.Emote: return(new EmoteAction(parent, ref data));

            case ActionLogic.Trample: return(new TrampleAction(parent, ref data));

            case ActionLogic.ChargedShield: return(new ChargedShieldAction(parent, ref data));

            case ActionLogic.Stunned: return(new StunnedAction(parent, ref data));

            case ActionLogic.Target: return(new TargetAction(parent, ref data));

            case ActionLogic.ChargedLaunchProjectile: return(new ChargedLaunchProjectileAction(parent, ref data));

            case ActionLogic.StealthMode: return(new StealthModeAction(parent, ref data));

            default: throw new System.NotImplementedException();
            }
        }
        /// <summary>
        /// When you right-click on something you will want to do contextually different things. For example you might attack an enemy,
        /// but revive a friend. You might also decide to do nothing (e.g. right-clicking on a friend who hasn't FAINTED).
        /// </summary>
        /// <param name="hit">The Transform of the entity we clicked on, or null if none.</param>
        /// <param name="actionType">The Action to build for</param>
        /// <param name="triggerStyle">How did this skill play get triggered? Mouse, Keyboard, UI etc.</param>
        /// <param name="resultData">Out parameter that will be filled with the resulting action, if any.</param>
        /// <returns>true if we should play an action, false otherwise. </returns>
        private bool GetActionRequestForTarget(Transform hit, ActionType actionType, SkillTriggerStyle triggerStyle, out ActionRequestData resultData)
        {
            resultData = new ActionRequestData();

            var targetNetObj = hit != null?hit.GetComponent <NetworkObject>() : null;

            //if we can't get our target from the submitted hit transform, get it from our stateful target in our NetworkCharacterState.
            if (!targetNetObj && actionType != ActionType.GeneralTarget)
            {
                ulong targetId = m_NetworkCharacter.TargetId.Value;
                NetworkSpawnManager.SpawnedObjects.TryGetValue(targetId, out targetNetObj);
            }

            //sanity check that this is indeed a valid target.
            if (targetNetObj == null || !ActionUtils.IsValidTarget(targetNetObj.NetworkObjectId))
            {
                return(false);
            }

            var targetNetState = targetNetObj.GetComponent <NetworkCharacterState>();

            if (targetNetState != null)
            {
                //Skill1 may be contextually overridden if it was generated from a mouse-click.
                if (actionType == CharacterData.Skill1 && triggerStyle == SkillTriggerStyle.MouseClick)
                {
                    if (!targetNetState.IsNpc && targetNetState.NetworkLifeState.Value == LifeState.Fainted)
                    {
                        //right-clicked on a downed ally--change the skill play to Revive.
                        actionType = ActionType.GeneralRevive;
                    }
                }
            }

            // record our target in case this action uses that info (non-targeted attacks will ignore this)
            resultData.ActionTypeEnum = actionType;
            resultData.TargetIds      = new ulong[] { targetNetObj.NetworkObjectId };
            PopulateSkillRequest(targetNetObj.transform.position, actionType, ref resultData);
            return(true);
        }
Ejemplo n.º 17
0
        public void PlayAction(ref ActionRequestData data)
        {
            ActionDescription actionDesc = GameDataSource.Instance.ActionDataByType[data.ActionTypeEnum];

            //Do Trivial Actions (actions that just require playing a single animation, and don't require any state tracking).
            switch (actionDesc.Logic)
            {
            case ActionLogic.LaunchProjectile:
            case ActionLogic.Revive:
            case ActionLogic.Emote:
                Parent.OurAnimator.SetTrigger(actionDesc.Anim);
                return;
            }

            var actionFX = ActionFX.MakeActionFX(ref data, Parent);

            actionFX.TimeStarted = Time.time;
            if (actionFX.Start())
            {
                m_PlayingActions.Add(actionFX);
            }
        }
Ejemplo n.º 18
0
 public FXProjectileTargetedAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
 public StealthModeActionFX(ref ActionRequestData data, ClientCharacterVisualization parent) : base(ref data, parent)
 {
 }
 public ActionFX(ref ActionRequestData data, ClientCharacterVisualization parent) : base(ref data)
 {
     m_Parent = parent;
 }
 public ChargedLaunchProjectileAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Ejemplo n.º 22
0
 public ChargedActionFX(ref ActionRequestData data, ClientCharacterVisualization parent) : base(ref data, parent)
 {
 }
Ejemplo n.º 23
0
        /// <inheritdoc />
        public override void NetworkStart()
        {
            if (!IsClient || transform.parent == null)
            {
                enabled = false;
                return;
            }

            m_AliveStateTriggerID     = Animator.StringToHash("StandUp");
            m_FaintedStateTriggerID   = Animator.StringToHash("FallDown");
            m_DeadStateTriggerID      = Animator.StringToHash("Dead");
            m_AnticipateMoveTriggerID = Animator.StringToHash("AnticipateMove");
            m_SpeedVariableID         = Animator.StringToHash("Speed");
            m_HitStateTriggerID       = Animator.StringToHash(ActionFX.k_DefaultHitReact);

            m_ActionViz = new ActionVisualization(this);

            Parent = transform.parent;

            m_NetState = Parent.gameObject.GetComponent <NetworkCharacterState>();
            m_NetState.DoActionEventClient             += PerformActionFX;
            m_NetState.CancelAllActionsEventClient     += CancelAllActionFXs;
            m_NetState.CancelActionsByTypeEventClient  += CancelActionFXByType;
            m_NetState.NetworkLifeState.OnValueChanged += OnLifeStateChanged;
            m_NetState.OnPerformHitReaction            += OnPerformHitReaction;
            m_NetState.OnStopChargingUpClient          += OnStoppedChargingUp;
            m_NetState.IsStealthy.OnValueChanged       += OnStealthyChanged;

            //we want to follow our parent on a spring, which means it can't be directly in the transform hierarchy.
            Parent.GetComponent <ClientCharacter>().ChildVizObject = this;
            transform.SetParent(null);

            // sync our visualization position & rotation to the most up to date version received from server
            var parentMovement = Parent.GetComponent <INetMovement>();

            transform.position = parentMovement.NetworkPosition.Value;
            transform.rotation = Quaternion.Euler(0, parentMovement.NetworkRotationY.Value, 0);

            // listen for char-select info to change (in practice, this info doesn't
            // change, but we may not have the values set yet) ...
            m_NetState.CharacterAppearance.OnValueChanged += OnCharacterAppearanceChanged;

            // ...and visualize the current char-select value that we know about
            OnCharacterAppearanceChanged(0, m_NetState.CharacterAppearance.Value);

            // ...and visualize the current char-select value that we know about
            SetAppearanceSwap();

            // sync our animator to the most up to date version received from server
            SyncEntryAnimation(m_NetState.NetworkLifeState.Value);

            if (!m_NetState.IsNpc)
            {
                // track health for heroes
                m_NetState.HealthState.HitPoints.OnValueChanged += OnHealthChanged;

                // find the emote bar to track its buttons
                GameObject partyHUDobj = GameObject.FindGameObjectWithTag("PartyHUD");
                m_PartyHUD = partyHUDobj.GetComponent <Visual.PartyHUD>();

                if (IsLocalPlayer)
                {
                    ActionRequestData data = new ActionRequestData {
                        ActionTypeEnum = ActionType.GeneralTarget
                    };
                    m_ActionViz.PlayAction(ref data);
                    gameObject.AddComponent <CameraController>();
                    m_PartyHUD.SetHeroData(m_NetState);

                    if (Parent.TryGetComponent(out ClientInputSender inputSender))
                    {
                        inputSender.ClientMoveEvent += OnMoveInput;
                    }
                }
                else
                {
                    m_PartyHUD.SetAllyData(m_NetState);

                    // getting our parent's NetworkObjectID for PartyHUD removal on Destroy
                    var parentNetworkObjectID = m_NetState.NetworkObjectId;

                    // once this object is destroyed, remove this ally from the PartyHUD UI
                    // NOTE: architecturally this will be refactored
                    Destroyed += () =>
                    {
                        if (m_PartyHUD != null)
                        {
                            m_PartyHUD.RemoveAlly(parentNetworkObjectID);
                        }
                    };
                }
            }
        }
Ejemplo n.º 24
0
 private void PerformActionFX(ActionRequestData data)
 {
     m_ActionViz.PlayAction(ref data);
 }
 /// <summary>
 /// constructor. The "data" parameter should not be retained after passing in to this method, because we take ownership of its internal memory.
 /// </summary>
 public Action(ServerCharacter parent, ref ActionRequestData data) : base(ref data)
 {
     m_Parent = parent;
     m_Data   = data; //do a shallow copy.
 }
 /// <summary>
 /// Called *AFTER* End(). At this point, the Action has ended, meaning its Update() etc. functions will never be
 /// called again. If the Action wants to immediately segue into a different Action, it can do so here. The new
 /// Action will take effect in the next Update().
 ///
 /// Note that this is not called on prematurely cancelled Actions, only on ones that have their End() called.
 /// </summary>
 /// <param name="newAction">the new Action to immediately transition to</param>
 /// <returns>true if there's a new action, false otherwise</returns>
 public virtual bool ChainIntoNewAction(ref ActionRequestData newAction)
 {
     return(false);
 }
Ejemplo n.º 27
0
 public StunnedAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Ejemplo n.º 28
0
 //cache Physics Cast hits, to minimize allocs.
 public MeleeAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
 public ChargedShieldAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Ejemplo n.º 30
0
 public StealthModeAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }