Enemy database list has duplicate damage values.

So, for a sort of JRPG-inspired game I’m making, I’m designing an enemy class. I have several scripts which I’ve adapated from my Inventory script. The Inventory script works the way I want it to (Grouping items, duplicates doing the same thing) but I don’t know how to adequately change it for the sake of my enemy databse so that when I spawn monsters from the class to a list, they’re all unique, so that if I damage them the damage isn’t shared.

So if I load four slime monsters into my scene, and trigger damage, all of them get damaged instead of the one I’ve specified, despite them all being separate entries in my list.

Below are my scripts. Cobbled together via google-fu and probably some very dumb ideas of mine since I’m not really much of a coder.

Base enemy class script;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]

public class Enemy
{
    public int id;
    public int enemyNumberInBattle;
    public int hp;
    public string name;
    public string description;
    public GameObject enemyPrefab;
    public bool isAlive;
    public Dictionary<string, int> stats = new Dictionary<string, int>();
    public Dictionary<string, int> attacks = new Dictionary<string, int>();
    //======================================DEFINITION======================================\\
    public Enemy(int id, int enemyNumberInBattle, string name, int hp, string description, bool isAlive, GameObject enemyPrefab,
        Dictionary<string, int> stats, Dictionary<string, int> attacks)
    {
        this.id = id;
        this.enemyNumberInBattle = enemyNumberInBattle;
        this.name = name;
        this.hp = hp;
        this.description = description;
        this.isAlive = isAlive;
        this.enemyPrefab = Resources.Load<GameObject>("Enemies/" + id);
        this.stats = stats;
        this.attacks = attacks;
    }
    public Enemy(Enemy enemy)
    {
        this.id = enemy.id;
        this.enemyNumberInBattle = enemy.enemyNumberInBattle;
        this.name = name;
        this.hp = enemy.hp;
        this.description = enemy.description;
        this.isAlive = enemy.isAlive;
        this.enemyPrefab = Resources.Load<GameObject>("Enemies/" + id);
        this.stats = enemy.stats;
        this.attacks = enemy.attacks;

    }
}

Enemy Database script;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyDatabase : MonoBehaviour
{


    public List<Enemy> enemies = new List<Enemy>();
 

    private void Awake()
    {
        BuildDatabase();
    }
    public Enemy GetEnemy(int id)
    {
        return enemies.Find(enemy => enemy.id == id);
    }
    public Enemy GetEnemy(string enemyName)
    {
        return enemies.Find(enemy => enemy.name == enemyName);
    }
    void BuildDatabase()
    {
        enemies = new List<Enemy>()
        {
            // ORDER: "ID" (Can't be 000 or 010, must be 0 or 10), "ID IN BATTLE (For selecting")"NAME", "HP", "CURRENT HP" "DESCRIPTION", "IsAlive", "Prefab (ADD ID AGAIN AFTER THE + because for some reason defining it like that in the other script isn't working. figure this out later, future me.)" "stats[]" "attacks[]"


            //===================== SLIME ===================== //

            new Enemy(0, 0, "Slime", 500, "A simple blue slime. It wiggles.", true, Resources.Load<GameObject>("Enemies/" + 0),

            //STATS
            new Dictionary<string, int>
            {
                {"ATK" , 25},
                {"DEF" , 25},
                {"Max HP" , 500},
            },

            //ATTACKS ("Name", "ID")
            new Dictionary<string, int>
            {
                {"Attack" , 001},
                {"Wiggle" , 002},
                {"Consume" , 003},

            }),

        };
    }



}

And here’s the active script in the scene that I use to spawn, damage, destroy and list enemies;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BattleScene : MonoBehaviour
{
    public bool SpawnSlime = false;
    public bool HurtEnemy1By100hp = false;
    public bool HurtEnemy2By100hp = false;
    public EnemyDatabase enemyDatabase;
    public List<Enemy> battleEnemies = new List<Enemy>();
    public GameObject[] EnemyPrefabs;
    public GameObject[] SpawnLocations;
    private GameObject SpawnPoint1;
    private GameObject SpawnPoint2;
    private GameObject SpawnPoint3;
    private GameObject SpawnPoint4;
    private GameObject SpawnPoint5;
    public GameObject EnemyParent;
    public GameObject Enemy01;
    public GameObject Enemy02;
    public GameObject Enemy03;
    public GameObject Enemy04;
    public GameObject Enemy05;
    // Start is called before the first frame update
    void Start()
    {
        if (SpawnLocations[0] != null)
        {
            SpawnPoint1 = SpawnLocations[0];
        }
        if (SpawnLocations[1] != null)
        {
            SpawnPoint2 = SpawnLocations[1];
        }
        if (SpawnLocations[2] != null)
        {
            SpawnPoint3 = SpawnLocations[2];
        }
        if (SpawnLocations[3] != null)
        {
            SpawnPoint4 = SpawnLocations[3];
        }
        if (SpawnLocations[4] != null)
        {
            SpawnPoint5 = SpawnLocations[4];
        }
    }
    // Update is called once per frame
    void Update()
    {
        if (SpawnSlime == true)
        {
            SpawnEnemy(000);
            SpawnSlime = false;
            return;
        }
        if (HurtEnemy1By100hp == true)
        {
            battleEnemies[0].hp = battleEnemies[0].hp - 100;
            HurtEnemy1By100hp = false;
            return;
        }
        if (HurtEnemy2By100hp == true)
        {
            battleEnemies[1].hp = battleEnemies[1].hp - 100;
            HurtEnemy2By100hp = false;
            return;
        }
        for (int i = 0; i < battleEnemies.Count; i++)
        {
            if (battleEnemies[i].hp < 0 )
            {
                Debug.Log("i is " + i);
                battleEnemies[i].isAlive = false;
                //Destroy(battleEnemies[i].GameObject);
                RemoveEnemy(i);
                //battleEnemies.RemoveAt(i);  // Remove it. Our list resized, so...
                i--;    // step back. Next loop iteration will check correct member of array.
                Debug.Log("Enemy dies!");

            }
            else if (battleEnemies[i].hp == 0)
            {
                Debug.Log("i is " + i);
                battleEnemies[i].isAlive = false;
                RemoveEnemy(i);
                //battleEnemies.RemoveAt(i);  // Remove it. Our list resized, so...
                i--;    // step back. Next loop iteration will check correct member of array. (Thanks person on google for these handy notes.)
                Debug.Log("Enemy dies!");
            }
        }
        }
    public void SpawnEnemy(string enemyName)
    {
        Enemy enemyToAdd = enemyDatabase.GetEnemy(enemyName);
        battleEnemies.Add(enemyToAdd);
        var NewEnemy = Instantiate(enemyToAdd.enemyPrefab);

        NewEnemy.transform.SetParent(EnemyParent.transform);

        if (battleEnemies.Count == 1)
        {
            NewEnemy.transform.position = SpawnPoint1.transform.position;
            NewEnemy.tag = "Enemy01";
            enemyToAdd.enemyNumberInBattle = 1;
            Enemy01 = NewEnemy;
      
        }
        if (battleEnemies.Count == 2)
        {
            NewEnemy.transform.position = SpawnPoint2.transform.position;
            NewEnemy.tag = "Enemy02";
            enemyToAdd.enemyNumberInBattle = 2;
            Enemy02 = NewEnemy;
      
        }
        if (battleEnemies.Count == 3)
        {
            NewEnemy.transform.position = SpawnPoint3.transform.position;
            NewEnemy.tag = "Enemy03";
            enemyToAdd.enemyNumberInBattle = 3;
            Enemy03 = NewEnemy;
      
        }
        if (battleEnemies.Count == 4)
        {
            NewEnemy.transform.position = SpawnPoint4.transform.position;
            NewEnemy.tag = "Enemy04";
            enemyToAdd.enemyNumberInBattle = 4;
            Enemy04 = NewEnemy;
        }
        if (battleEnemies.Count == 5)
        {
            NewEnemy.transform.position = SpawnPoint5.transform.position;
            NewEnemy.tag = "Enemy05";
            enemyToAdd.enemyNumberInBattle = 5;
            Enemy05 = NewEnemy;
        }
        return;
    }
    public void SpawnEnemy(int enemyId)
    {
        Enemy enemyToAdd = enemyDatabase.GetEnemy(enemyId);
        battleEnemies.Add(enemyToAdd);


        var NewEnemy = Instantiate(enemyToAdd.enemyPrefab);
            NewEnemy.transform.SetParent(EnemyParent.transform);

        if (battleEnemies.Count == 1)
        {
            NewEnemy.transform.position = SpawnPoint1.transform.position;
            NewEnemy.tag = "Enemy01";
            enemyToAdd.enemyNumberInBattle = 1;
            Enemy01 = NewEnemy;
      
        }
        if (battleEnemies.Count == 2)
        {
            NewEnemy.transform.position = SpawnPoint2.transform.position;
            NewEnemy.tag = "Enemy02";
            enemyToAdd.enemyNumberInBattle = 2;
            Enemy02 = NewEnemy;
      
        }
        if (battleEnemies.Count == 3)
        {
            NewEnemy.transform.position = SpawnPoint3.transform.position;
            NewEnemy.tag = "Enemy03";
            enemyToAdd.enemyNumberInBattle = 3;
            Enemy03 = NewEnemy;
      
        }
        if (battleEnemies.Count == 4)
        {
            NewEnemy.transform.position = SpawnPoint4.transform.position;
            NewEnemy.tag = "Enemy04";
            enemyToAdd.enemyNumberInBattle = 4;
            Enemy04 = NewEnemy;
      
        }
        if (battleEnemies.Count == 5)
        {
            NewEnemy.transform.position = SpawnPoint5.transform.position;
            NewEnemy.tag = "Enemy05";
            enemyToAdd.enemyNumberInBattle = 5;
            Enemy05 = NewEnemy;
      
        }

        return;
    }
    public void RemoveEnemy(int id)
    {
        Enemy enemy = CheckForEnemy(id);
        if (enemy == null)
        {
            Debug.Log("You're trying to remove something that does not exist! (Would you prefer a NullRef error?)");
            return;
        }
        if (enemy != null)
        {
            battleEnemies.Remove(enemy);
            Debug.Log("Enemy Removed: " + enemy.name);
        }
        else
        {
            Debug.Log("Failed to remove " + enemy.name + "! What the crap?! Item is appearing as " + enemy + "! Fix that!");
        }
    }
    public Enemy CheckForEnemy(int id)
    {
        Debug.Log("Searching for enemy...");
        return battleEnemies.Find(enemy => enemy.id == id);
  
    }
}

Apologies for the overly complicated script just above. I’m trying to figure out how to PROPERLY TIE each and every spawned prefab to the assigned monster in the list so that I can adequately have the player select between them in combat (And that when the list shrinks, all the animation triggers still go to the right place…). This is such a headache. I’ve taken a few steps to try and diagnose-- the “HurtEnemy2” bool, for instance, functions the same as “HurtEnemy1” despite having a different target in the script.

It’s probably better if I refactor everything so that the prefab itself has all the stats, isn’t it? … Sighhhhhhhhhhhhhhh. If there’s a way I don’t have to result to that, I’m all ears. Thanks in advance!

-Elaine

All your Enemy01, etc fields are getting a reference to the same instantiated prefab. Remember, classes are reference type values, so a reference to a class is more so a pointer to that instance. The way you’ve coded things causes them to all be pointing to the same reference, hence why damaging one damages them all. You will need to instantiate a new instance per enemy you want to add.

That aside, your code is pretty clunky. Your Enemy01, etc, should be collections (array or List), and your SpawnEnemy method could be compressed down to mere single loop.

I would also look into making your enemies into scriptable objects, so they can live as assets in your project files and not tied to a monobehaviour.

1 Like

Thanks for the advice! I’ve actually been spending the last couple of hours refactoring the code to point to a generic stat script in the enemy prefab, so each prefab has it’s own instance of the stat script when spawned and thus sidesteps my error.

Yeah, my code’s real clunky-- I generally have no idea what i’m doing or what questions to even ask because this is my first coding project ever really and I’ve mostly been getting by with excessive google search tabs. I’m quite literally learning as I go, lmao. Probably picking up a lot of bad habits. I’ll have to look at scriptable object enemies as another solution later.

I also just figured out today how to trigger Void Action()'s via a string reference so that massively helped my workflow with all these attacks and actions.

For those interested, here’s how I’ve refactored my code;

Refactored BattleScene.cs;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BattleScene : MonoBehaviour
{
    public bool SpawnSlime = false;
    public bool HurtEnemy1By100hp = false;
    public bool HurtEnemy2By100hp = false;
    public EnemyDatabase enemyDatabase;
    public List<Enemy> battleEnemies = new List<Enemy>();
    public GameObject[] EnemyPrefabs;
    public GameObject[] SpawnLocations;
    private GameObject SpawnPoint1;
    private GameObject SpawnPoint2;
    private GameObject SpawnPoint3;
    private GameObject SpawnPoint4;
    private GameObject SpawnPoint5;
    public GameObject EnemyParent;
    public GameObject Enemy01;
    public GameObject Enemy02;
    public GameObject Enemy03;
    public GameObject Enemy04;
    public GameObject Enemy05;
    // Start is called before the first frame update
    void Start()
    {
        if (SpawnLocations[0] != null)
        {
            SpawnPoint1 = SpawnLocations[0];
        }
        if (SpawnLocations[1] != null)
        {
            SpawnPoint2 = SpawnLocations[1];
        }
        if (SpawnLocations[2] != null)
        {
            SpawnPoint3 = SpawnLocations[2];
        }
        if (SpawnLocations[3] != null)
        {
            SpawnPoint4 = SpawnLocations[3];
        }
        if (SpawnLocations[4] != null)
        {
            SpawnPoint5 = SpawnLocations[4];
        }
    }
    // Update is called once per frame
    void Update()
    {
        if (SpawnSlime == true)
        {
            SpawnEnemy(000);
            SpawnSlime = false;
            return;
        }
        if (HurtEnemy1By100hp == true)
        {

            if (Enemy01 != null)
                {
                Enemy01.GetComponent<GenericEnemyStats>().HP = Enemy01.GetComponent<GenericEnemyStats>().HP - 100;
                HurtEnemy1By100hp = false;
                return;
            }

        }
        if (HurtEnemy2By100hp == true)
        {
            if (Enemy02 != null)
            {
                Enemy02.GetComponent<GenericEnemyStats>().HP = Enemy02.GetComponent<GenericEnemyStats>().HP - 100;
                HurtEnemy2By100hp = false;
                return;
            }

        }
        //for (int i = 0; i < battleEnemies.Count; i++)
        //{
        //    if (battleEnemies[i].hp < 0 )
        //    {
        //        Debug.Log("i is " + i);
        //        //Destroy(battleEnemies[i].GameObject);
        //        RemoveEnemy(i);
        //        //battleEnemies.RemoveAt(i);  // Remove it. Our list resized, so...
        //        i--;    // step back. Next loop iteration will check correct member of array.
        //        Debug.Log("Enemy dies!");
        //
        //    }
        //    else if (battleEnemies[i].hp == 0)
        //    {
        //        Debug.Log("i is " + i);
        //        RemoveEnemy(i);
        //        //battleEnemies.RemoveAt(i);  // Remove it. Our list resized, so...
        //        i--;    // step back. Next loop iteration will check correct member of array. (Thanks person on google for these handy notes.)
        //        Debug.Log("Enemy dies!");
        //    }
        //}
        }
    public void SpawnEnemy(string enemyName)
    {
        Enemy enemyToAdd = enemyDatabase.GetEnemy(enemyName);
        battleEnemies.Add(enemyToAdd);
        var NewEnemy = Instantiate(enemyToAdd.enemyPrefab);

        NewEnemy.transform.SetParent(EnemyParent.transform);

        if (battleEnemies.Count == 1)
        {
            NewEnemy.transform.position = SpawnPoint1.transform.position;
            NewEnemy.tag = "Enemy01";
            Enemy01 = NewEnemy;
         
        }
        if (battleEnemies.Count == 2)
        {
            NewEnemy.transform.position = SpawnPoint2.transform.position;
            NewEnemy.tag = "Enemy02";
            Enemy02 = NewEnemy;
         
        }
        if (battleEnemies.Count == 3)
        {
            NewEnemy.transform.position = SpawnPoint3.transform.position;
            NewEnemy.tag = "Enemy03";
            Enemy03 = NewEnemy;
         
        }
        if (battleEnemies.Count == 4)
        {
            NewEnemy.transform.position = SpawnPoint4.transform.position;
            NewEnemy.tag = "Enemy04";
            Enemy04 = NewEnemy;
        }
        if (battleEnemies.Count == 5)
        {
            NewEnemy.transform.position = SpawnPoint5.transform.position;
            NewEnemy.tag = "Enemy05";
            Enemy05 = NewEnemy;
        }
        return;
    }
    public void SpawnEnemy(int enemyId)
    {
        Enemy enemyToAdd = enemyDatabase.GetEnemy(enemyId);
        battleEnemies.Add(enemyToAdd);


        var NewEnemy = Instantiate(enemyToAdd.enemyPrefab);
            NewEnemy.transform.SetParent(EnemyParent.transform);

        if (battleEnemies.Count == 1)
        {
            NewEnemy.transform.position = SpawnPoint1.transform.position;
            NewEnemy.tag = "Enemy01";
            Enemy01 = NewEnemy;

        }
        if (battleEnemies.Count == 2)
        {
            NewEnemy.transform.position = SpawnPoint2.transform.position;
            NewEnemy.tag = "Enemy02";
            Enemy02 = NewEnemy;

        }
        if (battleEnemies.Count == 3)
        {
            NewEnemy.transform.position = SpawnPoint3.transform.position;
            NewEnemy.tag = "Enemy03";
            Enemy03 = NewEnemy;

        }
        if (battleEnemies.Count == 4)
        {
            NewEnemy.transform.position = SpawnPoint4.transform.position;
            NewEnemy.tag = "Enemy04";
            Enemy04 = NewEnemy;
        }
        if (battleEnemies.Count == 5)
        {
            NewEnemy.transform.position = SpawnPoint5.transform.position;
            NewEnemy.tag = "Enemy05";
            Enemy05 = NewEnemy;
        }


        return;
    }
    public void RemoveEnemy(int id)
    {
        Enemy enemy = CheckForEnemy(id);
        if (enemy == null)
        {
            Debug.Log("You're trying to remove something that does not exist! (Would you prefer a NullRef error?)");
            return;
        }
        if (enemy != null)
        {
            battleEnemies.Remove(enemy);
            Debug.Log("Enemy Removed: " + enemy.name);
        }
        else
        {
            Debug.Log("Failed to remove " + enemy.name + "! What the crap?! Item is appearing as " + enemy + "! Fix that!");
        }
    }
    public Enemy CheckForEnemy(int id)
    {
        Debug.Log("Searching for enemy...");
        return battleEnemies.Find(enemy => enemy.id == id);
     
    }
}

Refactored EnemyDatabase;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyDatabase : MonoBehaviour
{


    public List<Enemy> enemies = new List<Enemy>();
 

    private void Awake()
    {
        BuildDatabase();
    }
    public Enemy GetEnemy(int id)
    {
        return enemies.Find(enemy => enemy.id == id);
    }
    public Enemy GetEnemy(string enemyName)
    {
        return enemies.Find(enemy => enemy.name == enemyName);
    }
    void BuildDatabase()
    {
        enemies = new List<Enemy>()
        {
            // ORDER: "ID" (Can't be 000 or 010, must be 0 or 10),"Prefab (ADD ID AGAIN AFTER THE +)"


            //===================== SLIME ===================== //

            new Enemy(0, "Slime", Resources.Load<GameObject>("Enemies/" + 0))
        };
    }



}

Refactored Enemy Stats (Now on the prefab that gets Instantated)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GenericEnemyStats : MonoBehaviour
{
    public int EnemyID;
    public int HP;
    public int MaxHP;
    public int MP;
    public int MaxMP;
    public bool Attack1Trigger = false;
    public bool Attack2Trigger = false;
    public bool Attack3Trigger = false;
    public string[] AttackNames;
    private GameObject CombatManager;
    private GameObject BattleActions;
    private GameObject partyManager;
    public int PotentialTargets;
    //private GameObject ThisGameObject;

    // Start is called before the first frame update
    void Start()
    {
     
    }

    void Awake()
    {
        CombatManager = GameObject.FindWithTag("EnemyDatabase");
        BattleActions = GameObject.FindWithTag("BattleActions");
        partyManager = GameObject.FindWithTag("PartyManager");
        PotentialTargets = partyManager.GetComponent<PartyManager>().CurrentPartyMembers.Count;

        //ThisGameObject = this;
    }

    // Update is called once per frame
    void Update()
    {
        {
         
            if (HP <= 0)
            {
                CombatManager.GetComponent<BattleScene>().RemoveEnemy(EnemyID);
                Destroy(this.gameObject);
            }

            if (Attack1Trigger == true)
            {
                Attack1();
                Attack1Trigger = false;
            }
            if (Attack2Trigger == true)
            {
                Attack2();
                Attack2Trigger = false;
            }
            if (Attack3Trigger == true)
            {
                Attack3();
                Attack3Trigger = false;
            }



        }
    }

    void Attack1()
    {
        BattleActions.GetComponent<BattleActions>().Actor = this.gameObject;
        BattleActions.GetComponent<BattleActions>().Invoke(AttackNames[0], 0.0f);
    }

    void Attack2()
    {
        BattleActions.GetComponent<BattleActions>().Actor = this.gameObject;
        BattleActions.GetComponent<BattleActions>().Invoke(AttackNames[1], 0.0f);
    }
    void Attack3()
    {
        BattleActions.GetComponent<BattleActions>().Actor = this.gameObject;
        BattleActions.GetComponent<BattleActions>().Invoke(AttackNames[2], 0.0f);
    }

}

(A lot of those bools are there so I can test the functions in Unity’s inspector to see if they even work. They won’t be how the functions are used in actual play.)

How would I go about compressing my SpawnEnemies loop? This kind of thinking still breaks my brain a little.