Jamryl
Jamryl

Reputation: 23

How to update one element of a list without updating all elements in C#?

I'm struggling with one piece of code and I was wondering if anyone could help me.

Here's my problem:

I'm working on a game and I created an inventory with a Dictionary. My Dictionary TKey is the item name and the TValue is a List. Each items has a boolean "isEquipped".

Dictionary<string, List<Item>> itemsInventory;

When equipping an item I want to check if the player has enough of this item and if this item can be equipped. I do that with this code:

public void EquipWeapon(bool tog, Item item)
{
    if (tog == true)
    {
        if (player.weapon == false)
        {
            player.GetStatByName(item.StatAffectedName).AddModifier(item.StatAffectedValue, StatModifier.ModifierOrigins.Equipment, StatModifier.ModifierType.Flat, item.AssetName);
            GameManagerSingleton.Instance.PlayerData.itemsInventory.FirstOrDefault(x => x.Key == item.AssetName && x.Value.Any(x => x.isEquiped == false)).Value.FirstOrDefault().isEquiped = true;
        }
    }
    else
    {
        if (player.weapon == true)
        {
            player.GetStatByName(item.StatAffectedName).RemoveModifier(item.AssetName);
            GameManagerSingleton.Instance.PlayerData.itemsInventory.FirstOrDefault(x => x.Key == item.AssetName && x.Value.Any(x => x.isEquiped == true)).Value.FirstOrDefault().isEquiped = false;
        }
        player.weapon = false;
    }
    player.GetStatByName(item.StatAffectedName).GetValue();
}

Problem with my code is that it changes all the items of the list and not just one. Anyone could help me please?

Upvotes: 0

Views: 254

Answers (5)

Jamryl
Jamryl

Reputation: 23

My code looked like this :

public void AddItemToInventory(Item? item, Ressource? ressource, int number = 1)
    {
        if (item != null)
        {
            int totalPrice = (item.Price * number);
            ressources.First(x => x.currency == item.Currency).amount -= totalPrice;
            DailyReportHandler.GetAndFillTodayReport().moneyForItems += totalPrice;
            if (itemsInventory.ContainsKey(item.AssetName))
            {
                for (int i = 0; i < number; i++)
                {
                    itemsInventory.FirstOrDefault(x => x.Key == item.AssetName).Value.Add(item);
                }
            }
            else
            {
                itemsInventory.Add(item.AssetName, new List<Item>());
                for (int i = 0; i < number; i++)
                {
                    itemsInventory.FirstOrDefault(x => x.Key == item.AssetName).Value.Add(item);
                }
            }
        }
        if (ressource != null)
        {
            int totalPrice = (ressource.price * number);
            ressources.First(x => x.currency == Currency.credit).amount -= totalPrice;
            DailyReportHandler.GetAndFillTodayReport().moneyForItems += totalPrice;
            GameManagerSingleton.Instance.PlayerData.AddResource(ressource.currency, number);
        }
    }

and I change it to this:

public void AddItemToInventory(Item? item, Ressource? ressource, int number = 1)
    {
        if (item != null)
        {
            int totalPrice = (item.Price * number);
            ressources.First(x => x.currency == item.Currency).amount -= totalPrice;
            DailyReportHandler.GetAndFillTodayReport().moneyForItems += totalPrice;
            if (itemsInventory.ContainsKey(item.AssetName))
            {
                for (int i = 0; i < number; i++)
                {
                    itemsInventory.FirstOrDefault(x => x.Key == item.AssetName).Value.Add(GenerateNewItem(item));
                }
            }
            else
            {
                itemsInventory.Add(item.AssetName, new List<Item>());
                for (int i = 0; i < number; i++)
                {
                    itemsInventory.FirstOrDefault(x => x.Key == item.AssetName).Value.Add(GenerateNewItem(item));
                }
            }
        }
        if (ressource != null)
        {
            int totalPrice = (ressource.price * number);
            ressources.First(x => x.currency == Currency.credit).amount -= totalPrice;
            DailyReportHandler.GetAndFillTodayReport().moneyForItems += totalPrice;
            GameManagerSingleton.Instance.PlayerData.AddResource(ressource.currency, number);
        }
    }

With GenerateNewItem(item) like this:

public Item GenerateNewItem(Item item)
    {
        var newItem = new Item
        {
            AssetName = item.AssetName,
            Currency = item.Currency,
            DescriptionLocalizationKey = item.AssetName,
            isEquiped = false,
            itemType = item.itemType,
            NameLocalizationKey = item.NameLocalizationKey,
            Price = item.Price,
            ShortDescriptionLocalizationKey = item.ShortDescriptionLocalizationKey,
            StatAffectedName = item.StatAffectedName,
            StatAffectedValue = item.StatAffectedValue
        };
        return newItem;
    }

Upvotes: 0

Jamryl
Jamryl

Reputation: 23

When I create the player, I create the Dictionary like this:

itemsInventory = new Dictionary<string, List<Item>>(),

And when I create the list it's like this:

Instance.PlayerData.itemsInventory.Add("weapon", new List<Item>());
Instance.PlayerData.itemsInventory.Add("armour", new List<Item>());
Instance.PlayerData.itemsInventory.Add("shield", new List<Item>());

Upvotes: 0

tmaj
tmaj

Reputation: 35105

From the comment you made "If I update in the debugger it change all items as well." it seems that the values in the list are the same object. This strongly suggests that Item is a class (not a struct).

You need to clone the objects before adding them to the list if they represent different instances.

From Reference types (C# Reference):

With reference types, two variables can reference the same object; therefore, operations on one variable can affect the object referenced by the other variable.

For completeness, you could use a value type (maybe something like record struct available from C#10):

With value types, each variable has its own copy of the data, and it is not possible for operations on one variable to affect the other (except in the case of in, ref and out parameter variables; see in, ref and out parameter modifier).

Upvotes: 2

Jamryl
Jamryl

Reputation: 23

List View

List's item detail

After I changed from false to true not only the item at index 0 but all objects changes.

Upvotes: 0

zaakm
zaakm

Reputation: 151

Use linq to query out the list you want to update and then update the item of the list individually. Use this as an example: How to update a single item of liist of objects using LINQ in C#

Upvotes: 0

Related Questions