/// <summary>
 /// Notify the AIBrain that we should consider this character an enemy.
 /// </summary>
 /// <param name="character"></param>
 public void Hate(ServerCharacter character)
 {
     if (!m_HatedEnemies.Contains(character))
     {
         m_HatedEnemies.Add(character);
     }
 }
 /// <summary>
 /// Called when we received some HP. Positive HP is healing, negative is damage.
 /// </summary>
 /// <param name="inflicter">The person who hurt or healed us. May be null. </param>
 /// <param name="amount">The amount of HP received. Negative is damage. </param>
 public void ReceiveHP(ServerCharacter inflicter, int amount)
 {
     if (inflicter != null && amount < 0)
     {
         Hate(inflicter);
     }
 }
 private void Awake()
 {
     m_NavMeshAgent          = GetComponent <NavMeshAgent>();
     m_NetworkCharacterState = GetComponent <NetworkCharacterState>();
     m_CharLogic             = GetComponent <ServerCharacter>();
     m_Rigidbody             = GetComponent <Rigidbody>();
     m_NavigationSystem      = GameObject.FindGameObjectWithTag(NavigationSystem.NavigationSystemTag).GetComponent <NavigationSystem>();
 }
Пример #4
0
 /// <summary>
 /// Receive a Life State change that brings Fainted characters back to Alive state.
 /// </summary>
 /// <param name="inflicter">Person reviving the character.</param>
 /// <param name="HP">The HP to set to a newly revived character.</param>
 public void Revive(ServerCharacter inflicter, int HP)
 {
     if (NetState.NetworkLifeState.Value == LifeState.Fainted)
     {
         NetState.HitPoints = NetState.CharacterData.BaseHP.Value;
         NetState.NetworkLifeState.Value = LifeState.Alive;
     }
 }
        public void ReceiveHP(ServerCharacter inflicter, int HP)
        {
            if (HP < 0)
            {
                //any damage at all is enough to slay me.
                GetComponent <NetworkBreakableState>().IsBroken.Value = true;

                //don't let us take another blow.
                GetComponent <Collider>().enabled = false;
            }
        }
        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);
        }
        /// <summary>
        /// Returns true if it be appropriate for us to murder this character, starting right now!
        /// </summary>
        public bool IsAppropriateFoe(ServerCharacter potentialFoe)
        {
            if (potentialFoe == null ||
                potentialFoe.IsNpc ||
                potentialFoe.NetState.NetworkLifeState.Value != LifeState.Alive ||
                potentialFoe.NetState.IsStealthy.Value != 0)
            {
                return(false);
            }

            // Also, we could use NavMesh.Raycast() to see if we have line of sight to foe?
            return(true);
        }
        // TODO: David will create interface hookup for receiving hits on non-NPC/PC objects (GOMPS-ID TBD)
        void ReceiveHP(ServerCharacter inflicter, int HP)
        {
            if (!IsServer)
            {
                return;
            }

            m_NetworkHealthState.HitPoints.Value += HP;

            if (m_NetworkHealthState.HitPoints.Value <= 0)
            {
                StopWaveSpawning();
            }
        }
        public AIBrain(ServerCharacter me, ActionPlayer myActionPlayer)
        {
            m_ServerCharacter = me;
            m_ActionPlayer    = myActionPlayer;

            m_Logics = new Dictionary <AIStateType, AIState>
            {
                [AIStateType.IDLE] = new IdleAIState(this),
                //[ AIStateType.WANDER ] = new WanderAIState(this), // not written yet
                [AIStateType.ATTACK] = new AttackAIState(this, m_ActionPlayer),
            };
            m_HatedEnemies = new List <ServerCharacter>();
            m_CurrentState = AIStateType.IDLE;
        }
        protected void DetectFoes()
        {
            float detectionRange = m_Brain.CharacterData.DetectRange;
            // we are doing this check every Update, so we'll use square-magnitude distance to avoid the expensive sqrt (that's implicit in Vector3.magnitude)
            float   detectionRangeSqr = detectionRange * detectionRange;
            Vector3 position          = m_Brain.GetMyServerCharacter().transform.position;

            foreach (ServerCharacter character in ServerCharacter.GetAllActiveServerCharacters())
            {
                if (m_Brain.IsAppropriateFoe(character) && (character.transform.position - position).sqrMagnitude <= detectionRangeSqr)
                {
                    m_Brain.Hate(character);
                }
            }
        }
Пример #11
0
        public override bool Start()
        {
            if (m_Data.TargetIds == null || m_Data.TargetIds.Length == 0 || !NetworkSpawnManager.SpawnedObjects.ContainsKey(m_Data.TargetIds[0]))
            {
                Debug.Log("Failed to start ReviveAction. The target entity  wasn't submitted or doesn't exist anymore");
                return(false);
            }

            var targetNeworkedObj = NetworkSpawnManager.SpawnedObjects[m_Data.TargetIds[0]];

            m_TargetCharacter = targetNeworkedObj.GetComponent <ServerCharacter>();
            m_Parent.NetState.RecvDoActionClientRPC(Data);

            return(true);
        }
Пример #12
0
        /// <summary>
        /// We've crashed into a victim! This function determines what happens to them... and to us!
        /// It's possible for us to be stunned by our victim if they have a special power that allows that.
        /// This function checks for that special case; if we become stunned, the victim is entirely unharmed,
        /// and further collisions with other victims will also have no effect.
        /// </summary>
        /// <param name="victim">The character we've collided with</param>
        private void CollideWithVictim(ServerCharacter victim)
        {
            if (victim == m_Parent)
            {
                // can't collide with ourselves!
                return;
            }

            if (m_WasStunned)
            {
                // someone already stunned us, so no further damage can happen
                return;
            }

            // if we collide with allies, we don't want to hurt them (but we do knock them back, see below)
            if (m_Parent.IsNpc != victim.IsNpc)
            {
                // first see if this victim has the special ability to stun us!
                float chanceToStun = victim.GetBuffedValue(BuffableValue.ChanceToStunTramplers);
                if (chanceToStun > 0 && Random.Range(0, 1) < chanceToStun)
                {
                    // we're stunned! No collision behavior for the victim. Stun ourselves and abort.
                    m_WasStunned = true;
                    m_Movement.CancelMove();
                    m_Parent.NetState.RecvCancelAllActionsClientRpc();
                    return;
                }

                // We deal a certain amount of damage to our "initial" target and a different amount to all other victims.
                int damage;
                if (m_Data.TargetIds != null && m_Data.TargetIds.Length > 0 && m_Data.TargetIds[0] == victim.NetworkObjectId)
                {
                    damage = Description.Amount;
                }
                else
                {
                    damage = Description.SplashDamage;
                }
                victim.NetState.RecvPerformHitReactionClientRPC();
                victim.ReceiveHP(this.m_Parent, -damage);
            }

            var victimMovement = victim.GetComponent <ServerCharacterMovement>();

            victimMovement.StartKnockback(m_Parent.transform.position, Description.KnockbackSpeed, Description.KnockbackDuration);
        }
Пример #13
0
        /// <summary>
        /// Receive an HP change from somewhere. Could be healing or damage.
        /// </summary>
        /// <param name="inflicter">Person dishing out this damage/healing. Can be null. </param>
        /// <param name="HP">The HP to receive. Positive value is healing. Negative is damage.  </param>
        public void ReceiveHP(ServerCharacter inflicter, int HP)
        {
            //to our own effects, and modify the damage or healing as appropriate. But in this game, we just take it straight.
            if (HP > 0)
            {
                m_ActionPlayer.OnGameplayActivity(Action.GameplayActivity.Healed);
                float healingMod = m_ActionPlayer.GetBuffedValue(Action.BuffableValue.PercentHealingReceived);
                HP = (int)(HP * healingMod);
            }
            else
            {
                m_ActionPlayer.OnGameplayActivity(Action.GameplayActivity.AttackedByEnemy);
                float damageMod = m_ActionPlayer.GetBuffedValue(Action.BuffableValue.PercentDamageReceived);
                HP = (int)(HP * damageMod);
            }

            NetState.HitPoints = Mathf.Min(NetState.CharacterData.BaseHP.Value, NetState.HitPoints + HP);

            if (m_AIBrain != null)
            {
                //let the brain know about the modified amount of damage we received.
                m_AIBrain.ReceiveHP(inflicter, HP);
            }

            //we can't currently heal a dead character back to Alive state.
            //that's handled by a separate function.
            if (NetState.HitPoints <= 0)
            {
                ClearActions(false);

                if (IsNpc)
                {
                    if (m_KilledDestroyDelaySeconds >= 0.0f && NetState.NetworkLifeState.Value != LifeState.Dead)
                    {
                        StartCoroutine(KilledDestroyProcess());
                    }

                    NetState.NetworkLifeState.Value = LifeState.Dead;
                }
                else
                {
                    NetState.NetworkLifeState.Value = LifeState.Fainted;
                }
            }
        }
        /// <summary>
        /// Picks the most appropriate foe for us to attack right now, or null if none are appropriate
        /// (Currently just chooses the foe closest to us in distance)
        /// </summary>
        /// <returns></returns>
        private ServerCharacter ChooseFoe()
        {
            Vector3 myPosition = m_Brain.GetMyServerCharacter().transform.position;

            float           closestDistanceSqr = int.MaxValue;
            ServerCharacter closestFoe         = null;

            foreach (var foe in m_Brain.GetHatedEnemies())
            {
                float distanceSqr = (myPosition - foe.transform.position).sqrMagnitude;
                if (distanceSqr < closestDistanceSqr)
                {
                    closestDistanceSqr = distanceSqr;
                    closestFoe         = foe;
                }
            }
            return(closestFoe);
        }
        /// <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();
            }
        }
        public override void Initialize()
        {
            m_AttackActions = new List <ActionType>();
            if (m_Brain.CharacterData.Skill1 != ActionType.None)
            {
                m_AttackActions.Add(m_Brain.CharacterData.Skill1);
            }
            if (m_Brain.CharacterData.Skill2 != ActionType.None)
            {
                m_AttackActions.Add(m_Brain.CharacterData.Skill2);
            }
            if (m_Brain.CharacterData.Skill3 != ActionType.None)
            {
                m_AttackActions.Add(m_Brain.CharacterData.Skill3);
            }

            // pick a starting attack action from the possible
            m_CurAttackAction = m_AttackActions[Random.Range(0, m_AttackActions.Count)];

            // clear any old foe info; we'll choose a new one in Update()
            m_Foe = null;
        }
 public ChargedLaunchProjectileAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Пример #18
0
 public ActionPlayer(ServerCharacter parent)
 {
     m_Parent             = parent;
     m_Queue              = new List <Action>();
     m_NonBlockingActions = new List <Action>();
 }
Пример #19
0
 public FXProjectileTargetedAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Пример #20
0
 public StunnedAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Пример #21
0
 //cache Physics Cast hits, to minimize allocs.
 public MeleeAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, 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.
 }
 public ChargedShieldAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }
Пример #24
0
 public StealthModeAction(ServerCharacter parent, ref ActionRequestData data) : base(parent, ref data)
 {
 }