Example #1
0
        /// <summary>
        /// Initialise the movement with the given movement data.
        /// </summary>
        /// <param name="enemy">The enemy.</param>
        /// <param name="movementData">Movement data.</param>
        override public EnemyMovement Init(Enemy enemy)
        {
            base.Init(enemy);
            int anyMoveCount = 0;

            // Initialise and check each movement
            for (int i = 0; i < statesToMovements.Length; i++)
            {
                if (statesToMovements[i].state == EnemyState.DEFAULT)
                {
                    anyMoveCount++;
                }

                if (statesToMovements[i].movement == null)
                {
                    Debug.LogError("The state " + statesToMovements[i].state + " does not have a movement assigned.");
                }
                else
                {
                    statesToMovements[i].movement.Init(enemy);
                    if (statesToMovements[i].state == EnemyState.DEFAULT)
                    {
                        defaultMovement = statesToMovements[i].movement;
                    }
                }
            }

            // Report any move errors
            if (anyMoveCount == 0)
            {
                Debug.LogError("You must have a move assigned to the DEFAULT enemy state");
            }
            else if (anyMoveCount > 1)
            {
                Debug.LogWarning("You have more than one move assigned to the ANY state, only the first will be used.");
            }

            currentMovement = defaultMovement;

            uniqueMovements = statesToMovements.Select(s => s.movement).Distinct().ToArray();

            return(this);
        }
        /// <summary>
        /// Init this enemy AI.
        /// </summary>
        override public void Init(Enemy enemy)
        {
            base.Init(enemy);
            // Listen to damage events and if we get them hide
            enemy.Damaged  += EnemyDamaged;
            enemy.Collided += EnemyDamaged;

            // If we need it try and find a charge movement
            if (useChargeState)
            {
                EnemyMovement_Distributor distributor = enemy.GetComponentInChildren <EnemyMovement_Distributor>();
                if (distributor != null)
                {
                    foreach (EnemyStateToMovement estm in distributor.statesToMovements)
                    {
                        if (estm.state == EnemyState.CHARGING)
                        {
                            chargeMovement = estm.movement;
                            break;
                        }
                    }
                }
                else
                {
                    chargeMovement = enemy.GetComponentInChildren <EnemyMovement_Charge>();
                }
            }
            // Try to find a cahracter hurt box so we can collect coins and damage other enemies
            characterHitBox = GetComponentInChildren <CharacterHitBox>();
            if (characterHitBox != null)
            {
                // Don't hit ourselves
                EnemyHurtBox myHurtBox = enemy.GetComponentInChildren <EnemyHurtBox>();
                if (myHurtBox != null)
                {
                    Physics2D.IgnoreCollision(characterHitBox.GetComponent <Collider2D>(), myHurtBox.GetComponent <Collider2D>());
                }
                // Init hit box
                characterHitBox.Init(new DamageInfo(1, DamageType.PHYSICAL, Vector2.zero));
            }
        }
Example #3
0
        virtual protected void DrawStateInfo(SequenceDrivenEnemy enemy, EnemyPhase phase, EnemyStateInfo info, int phaseIndex, int index)
        {
            GUI.color = (info.gotoState >= 0) ? Color.yellow : (info.assignedMovement == null) ? Color.red : Color.green;
            GUILayout.BeginVertical(EditorStyles.textArea);
            GUI.color = Color.white;

            if (phase == null)
            {
                gotoFoldOutStates [index] = EditorGUILayout.Foldout(gotoFoldOutStates [index], new GUIContent(info.stateName));
            }
            else
            {
                foldOutState [index] = EditorGUILayout.Foldout(foldOutState [index], new GUIContent(info.stateName));
            }

            if ((phase == null && gotoFoldOutStates[index]) || (phase != null && foldOutState[index]))
            {
                string newStateName = EditorGUILayout.TextField(new GUIContent("Name", "State name."), info.stateName);
                if (newStateName != info.stateName)
                {
                    info.stateName = newStateName;
                }

                if (info.gotoState >= 0)
                {
                    int gotoPhase = EditorGUILayout.IntField(new GUIContent("Go To Phase", "State we go back to until exit is reached."), info.gotoPhase);
                    if (gotoPhase >= 0 && gotoPhase < enemy.phaseInfo.Count)
                    {
                        if (gotoPhase != info.gotoPhase)
                        {
                            info.gotoPhase = gotoPhase;
                        }
                    }
                    int gotoState = EditorGUILayout.IntField(new GUIContent("Go To State", "State we go back to until exit is reached."), info.gotoState);
                    if (gotoState >= 0 && (info.gotoPhase != phaseIndex || gotoState != index) && gotoState < enemy.phaseInfo[info.gotoPhase].stateInfo.Count)
                    {
                        if (gotoState != info.gotoState)
                        {
                            info.gotoState = gotoState;
                        }
                    }

                    EnemyStateExitType exitType = (EnemyStateExitType)EditorGUILayout.EnumPopup(new GUIContent("Go To when...", "What condition causes us to run this Go To."), info.exitType);
                    if (exitType != info.exitType)
                    {
                        info.exitType = exitType;
                    }
                    if (info.exitType == EnemyStateExitType.NONE)
                    {
                        EditorGUILayout.HelpBox("The type NONE means this goto will never execute", MessageType.Warning);
                    }
                }
                else
                {
                    EnemyMovement newAssignedMovement = (EnemyMovement)EditorGUILayout.ObjectField(new GUIContent("Movement", "Assocaited enemy movement."), info.assignedMovement, typeof(EnemyMovement), true);
                    if (newAssignedMovement != info.assignedMovement)
                    {
                        info.assignedMovement = newAssignedMovement;
                    }
                    EditorGUILayout.HelpBox(info.assignedMovement == null ? "No movement set" : info.assignedMovement.GetType().Name, MessageType.None);

                    EnemyStateExitType exitType = (EnemyStateExitType)EditorGUILayout.EnumPopup(new GUIContent("Exit Type", "How we decide to exit this state."), info.exitType);
                    if (exitType != info.exitType)
                    {
                        info.exitType = exitType;
                    }
                    if (info.exitType == EnemyStateExitType.ALWAYS)
                    {
                        EditorGUILayout.HelpBox("The type ALWAYS is mainly meant for GOTO states. In this case it means this state will always exit instantly.", MessageType.Warning);
                    }
                }

                float exitSupportingData;
                float exitSupportingDataAlt;
                switch (info.exitType)
                {
                case EnemyStateExitType.TIMER:
                    exitSupportingData = EditorGUILayout.FloatField(new GUIContent("Exit Time", "Time to spend in state before exiting."), info.exitSupportingData);
                    if (exitSupportingData != info.exitSupportingData)
                    {
                        info.exitSupportingData = exitSupportingData;
                    }
                    break;

                case EnemyStateExitType.TIMER_PLUS_RANDOM:
                    exitSupportingData = EditorGUILayout.FloatField(new GUIContent("Exit Time", "Time to spend in state before exiting."), info.exitSupportingData);
                    if (exitSupportingData != info.exitSupportingData)
                    {
                        info.exitSupportingData = exitSupportingData;
                    }
                    int altAsInt = (int)info.exitSupportingDataAlt;
                    altAsInt = EditorGUILayout.IntSlider(new GUIContent("Random Chance", "Chance that we will exit state after timer is expired."), altAsInt, 0, 100);
                    exitSupportingDataAlt = (float)altAsInt;
                    if (exitSupportingDataAlt != info.exitSupportingDataAlt)
                    {
                        info.exitSupportingDataAlt = exitSupportingDataAlt;
                    }
                    break;

                case EnemyStateExitType.HEALTH_PERCENTAGE:
                    int healthAsInt = (int)(info.exitSupportingData * 100.0f);
                    healthAsInt        = EditorGUILayout.IntSlider(new GUIContent("Percentage", "Health percentage, state will exit when health is below this."), healthAsInt, 0, 100);
                    exitSupportingData = ((float)healthAsInt) / 100.0f;
                    if (exitSupportingData != info.exitSupportingData)
                    {
                        info.exitSupportingData = exitSupportingData;
                    }
                    break;

                case EnemyStateExitType.TARGET_WITHIN_RANGE:
                    exitSupportingData = EditorGUILayout.FloatField(new GUIContent("Range", "Range target must be within."), info.exitSupportingData);
                    if (exitSupportingData != info.exitSupportingData)
                    {
                        info.exitSupportingData = exitSupportingData;
                    }
                    exitSupportingDataAlt = (EditorGUILayout.Toggle(new GUIContent("Must Be Visible", "If true enemy must have a clear line of sight to the target."), info.exitSupportingData == 1.0f) ? 1.0f : 0.0f);
                    if (exitSupportingDataAlt != info.exitSupportingDataAlt)
                    {
                        info.exitSupportingDataAlt = exitSupportingDataAlt;
                    }
                    break;

                case EnemyStateExitType.NUMBER_OF_HITS:
                    exitSupportingData = (float)EditorGUILayout.IntField(new GUIContent("Number of Hits", "Number of hits before exiting to the next state."), (int)info.exitSupportingData);
                    if (exitSupportingData != info.exitSupportingData)
                    {
                        info.exitSupportingData = exitSupportingData;
                    }
                    DamageType exitSupportingDamageType = (DamageType)EditorGUILayout.EnumPopup(new GUIContent("Damage Type", "If not set to NONE, only damage of this type will count towards number of hits."), info.exitSupportingDamageType);
                    if (exitSupportingDamageType != info.exitSupportingDamageType)
                    {
                        info.exitSupportingDamageType = exitSupportingDamageType;
                    }
                    break;
                }

                GUILayout.Space(4);

                if (phase != null)
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    if (index == 0)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Move Up", "Move this state up."), EditorStyles.miniButton))
                    {
                        phase.stateInfo[index]     = phase.stateInfo[index - 1];
                        phase.stateInfo[index - 1] = info;
                    }
                    GUI.enabled = true;
                    if (index >= phase.stateInfo.Count - 1)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Move Down", "Move this state down."), EditorStyles.miniButton))
                    {
                        phase.stateInfo[index]     = phase.stateInfo[index + 1];
                        phase.stateInfo[index + 1] = info;
                    }
                    GUI.enabled = true;
                    if (phase.stateInfo.Count <= 1)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Delete", "Delete this state."), EditorStyles.miniButton))
                    {
                        phase.stateInfo.Remove(info);
                    }
                    GUI.enabled = true;
                    GUILayout.EndHorizontal();
                }
                else
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    if (index == 0)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Move Up", "Move this state up."), EditorStyles.miniButton))
                    {
                        enemy.globalGotos[index]     = enemy.globalGotos[index - 1];
                        enemy.globalGotos[index - 1] = info;
                    }
                    GUI.enabled = true;
                    if (index >= enemy.globalGotos.Count - 1)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Move Down", "Move this state down."), EditorStyles.miniButton))
                    {
                        enemy.globalGotos[index]     = enemy.globalGotos[index + 1];
                        enemy.globalGotos[index + 1] = info;
                    }
                    GUI.enabled = true;
                    if (enemy.globalGotos.Count <= 1)
                    {
                        GUI.enabled = false;
                    }
                    if (GUILayout.Button(new GUIContent("Delete", "Delete this state."), EditorStyles.miniButton))
                    {
                        enemy.globalGotos.Remove(info);
                    }
                    GUI.enabled = true;
                    GUILayout.EndHorizontal();
                }
            }

            GUILayout.EndVertical();

            GUILayout.Space(4);
        }
        /// <summary>
        /// Initialise the movement with the given movement data.
        /// </summary>
        /// <param name="enemy">The enemy.</param>
        /// <param name="movementData">Movement data.</param>
        public override EnemyMovement Init(Enemy enemy)
        {
            base.Init (enemy);
            int anyMoveCount = 0;
            // Initialise and check each movement
            for (int i = 0; i < statesToMovements.Length; i++)
            {
                if (statesToMovements[i].state == EnemyState.DEFAULT) anyMoveCount++;

                if (statesToMovements[i].movement == null)
                {
                    Debug.LogError ("The state " + statesToMovements[i].state + " does not have a movement assigned.");
                }
                else
                {
                    statesToMovements[i].movement.Init (enemy);
                    if (statesToMovements[i].state == EnemyState.DEFAULT) defaultMovement = statesToMovements[i].movement;
                }
            }

            // Report any move errors
            if (anyMoveCount == 0)  Debug.LogError ("You must have a move assigned to the DEFAULT enemy state");
            else if (anyMoveCount > 1) Debug.LogWarning ("You have more than one move assigned to the ANY state, only the first will be used.");

            currentMovement = defaultMovement;

            uniqueMovements = statesToMovements.Select (s => s.movement).Distinct().ToArray();

            return this;
        }
 /// <summary>
 /// Called when the enemy hits the character.
 /// </summary>
 /// <param name="character">Character.</param>
 /// <param name="info">Damage info.</param>
 public override void HitCharacter(Character character, DamageInfo info)
 {
     for (int i = 0; i < statesToMovements.Length; i++)
     {
         if (statesToMovements[i].state == EnemyState.HITTING) currentMovement = statesToMovements[i].movement;
     }
     currentMovement.HitCharacter(character, info);
 }
 /// <summary>
 /// Moves the character.
 /// </summary>
 public override bool DoMove()
 {
     EnemyMovement previousMovement = currentMovement;
     for (int i = 0; i < statesToMovements.Length; i++)
     {
         if (statesToMovements[i].state == enemy.State)
         {
             if (statesToMovements[i].movement != previousMovement)
             {
                 if (previousMovement.LosingControl())
                 {
                     // The previous movement wants to hold on to control, don't update it.
                     return true;
                 }
                 else
                 {
                     statesToMovements[i].movement.GainingControl();
                     currentMovement = statesToMovements[i].movement;
                 }
             }
             if (currentMovement.DoMove ()) return true;
         }
     }
     if (currentMovement == null) {
         currentMovement = defaultMovement;
         return currentMovement.DoMove();
     }
     return false;
 }
 /// <summary>
 /// Do the death movement
 /// </summary>
 public override void DoDeath(DamageInfo info)
 {
     for (int i = 0; i < statesToMovements.Length; i++)
     {
         if (statesToMovements[i].state == EnemyState.DEAD) currentMovement = statesToMovements[i].movement;
     }
     currentMovement.DoDeath(info);
 }