- Home /
C# Inventory System: Modify a targeted character's stats
So I'm pretty new to coding but I've been working on a simple inventory and I decided to redo my item classes after some thinking.
One of the classes (which inherits from the item class) is called StatItem. Which is just an item class for items which modify stats on a character.
It only has 2 additional variables, the int BuffAmount and the Attribute TargetAttribute (the attribute class has 3 values: name, basevalue and buffvalue)
public class StatItem : Item {
public int buffAmount;
public Attribute targetAttribute;
public StatItem (...(the item class constructors)..., int amountToBuff, Attribute attributeBuffed)
{
buffAmount = amountToBuff;
targetAttribute = attributeBuffed;
}
public static void UseStatItem (StatItem itemUsed){
itemUsed.targetAttribute._buffValue += itemUsed.buffAmount;
Debug.Log ("function " + itemUsed.targetAttribute._name + itemUsed.targetAttribute._buffValue);
}
So with my current method I can define in the ItemDatabase which Attribute is targeted, which for the moment is:
GameObject.FindGameObjectWithTag("Player").GetComponent().playerStats.strength;
.
However!! I'd like to be able to define the target character in the UseStatItem method but still have which attribute on that character (ie strength, agility...) defined on the database.
So my initial plan was on the database to have target.strength as the Attribute (target being a CharacterAttributes variable that I made a class of) and then define the target in the method. However for some reason (and my theory is, is that it's because the database is created OnStart and then not updated) the target CharacterAttributes is updated but the targetAttribute is not.
I hope I've explained okay :s
So basically what catastrophically dumb thing am I doing, is there a way of doing what I want? Am I making it all too complicated?
Answer by equus_ligneus · Apr 11, 2015 at 12:51 AM
Am I right to assume:
You want StatItem to store what kind of attribute (e.g. Strength, Agility) in general it is targetting
You want your StatItem to get a reference to the attribute of matching kind in a given player's stats and modify it (in UseStatItem(StatItem itemUsed))
Considering the fact that you propably won't change your list of attributes much (like adding new attributes called "proficience in solving theoretical problems" at runtime) I think creating an enum that holds all of your attribute-types, and then writing a getter-method for attributes should suffice:
public enum EAttributeTypes
{
Strength,
Agility,
...
}
// StatItem class
public class StatItem : Item {
public int buffAmount;
public EAttributeType targetAttribute;
public StatItem (...(the item class constructors)..., int amountToBuff, EAttributeType attributeBuffed)
: base(...(the item class constructors)...)
{
buffAmount = amountToBuff;
targetAttribute = attributeBuffed;
}
public static void UseStatItem (StatItem itemUsed, CharacterStats target){
target.GetAttribute(itemUsed.targetAttribute)._buffValue += itemUsed.buffAmount;
}
And inside your CharacterStats (or wherever you store your character's attributes) have a method that looks roughly like this:
public Attribute GetAttribute(EAttributeType type)
{
switch(type)
{
case EAttributeType.Strength:
return strength;
case EAttributeType.Agility:
return agility;
//...
}
}
You may want to rename your Attribute class. There's already a class called Attribute in namespace System so if you use this namespace in any class that also has references to one of your attributes your code will produce errors (your compiler does not guess which Attribute it is looking at, therefore you have to tell it explicitly)
This is awesome, thankyou so so very much for answering! you've made my day :D I've gone through it several times and I think I understand now what you're doing. I was just wondering in which class do I put the EAttributeType enum? :) I originally put it in StatItem class but then the method in the CharacterAttributes class doesn't recognize the EAttributeType
Oh, right, had not thought about that pitfall :). Put the enum itself outside of any class (basically in the StatItem.cs-file, but outside the class' braces).
If you put an enum/a class/a struct inside another class/struct it becomes a nested enum/class/struct inside the scope of the class/struct.
So you have to:
a) Put the enum outside of your StatItem-class or
b) Tell the class you want to use the enum in where the enum resides (ClassName.EnumName ...) e.g.:
StatItem.EAttributeType attType = StatItem.EAttributeType.Strength;
I put the enum outside the statitem class that way if I need to do this for some other part of the ui it's nice and easy to access :D
Sorry for bothering again I hope I'm not doing anything dumb but I'm not sure how to fix this error I'm getting with the method you suggested "not all code paths return a value" because from what I can see all the returns are correct and of StatAttribute, and I still get the error even when the rest is commented out except for this:
public StatAttribute GetAttribute(EAttributeType type)
{
switch(type)
{
case EAttributeType.Constitution:
return constitution;
}
Again thanks for all the help so far!
Well, "not all code paths return a value" means that in some way, a method that should return something (e.g. a StatAttribute) can be executed so that it does not return anything (Which is not allowed. You have to at least return some instance of the return type). This mostly happens if you forgot to handle all cases in an if or switch statement.
In your case, the method will only return something if type == EAttributeType.Constitution. You have to make sure your method returns some StatAttribute, whatever the conditions are when it is executed.
You could add a case for every item in your EAttributeType-enum to solve this. They'd all have to return a StatAttribute.
To circumvent this, the switch statement has a case that is used if no other case's conditions are met: the default case. It is to switch what else is to if.
switch(type)
{
case EAttributeType.Constitution:
return constitution;
default:
return null; // if StatItem is a struct, return default(StatItem) ins$$anonymous$$d (structs usually cannot be null)
}
Since you could return null, make sure to check whether you got null as a return value where you called the method. Change
target.GetAttribute(itemUsed.targetAttribute)._buffValue += itemUsed.buffAmount;
to
StatAttribute attribute = target.GetAttribute(itemUsed.targetAttribute);
if(attribute != null)
{
attribute._buffValue += itemUsed.buffAmount;
}
else
{
Debug.LogWarning("Could not get " + itemUsed.targetAttribute);
}
The else part is entirely optional and serves as a re$$anonymous$$der to handle all cases.
IT WOR$$anonymous$$SSSSSSSSSSSSSSSSSSSSSS!! :D I'm so happy right now, this has been hounding me all week xD thankyou thankyou thankyou!!!! (and yeah I'm sorry managed to figure out I just had to put in a default for it to work). Thankyou so very much, you have helped not only this project but my future ones too!! Thankyou!