I’m not sure what to call this, but basically, I’m trying to build a system that looks at the same stat values of two different items, and computes a final result based on instructions on how to combine each stat.
For example, say you have:
- Weapon
- Ammo - paired with a weapon
Each of these can contribute to the final stat values when combined. As stripped down example, say those stats are:
-
float damage
-
float accuracy
-
int maxAmmoCount
Now, in the past I was separating the stats into things only the weapon could affect, and things only the ammo could affect. But the thing is:
- There are plenty of thematic ways to justify why each of these could modify any of the stats.
- It’s much more convenient to see the final result on the weapon script, than having to find multiple parts.
So, the weapon script and the ammo script each have the same three stats, with instructions on how to apply the value.
The intent was to have the weapon version inherit the base stat class, and add combo instruction and final value.
So, the base class would have a float or int, and an enum saying the stat is to be used as a flat Value, or a Multiplier.
The derived class has the final value, and an enum to determine how to combine:
- WeaponOnly - Only use the weapon stats
- AmmoOnly - Only use the ammo stats
- WeaponBase - Starts with the baseValue stat of the weapon and either adds or multiplies the baseValue stat of the ammo, based on the ValueType of the ammo stat
- AmmoBase - Starts with the baseValue stat of the ammo and either adds or multiplies the baseValue stat of the weapon, based on the ValueType of theweapon stat
The final result is stored in the derived class as StatItem.value
To compute the final result, I have the weapon able to accept a reference to an ammo script prefab.
The the resulting final values are generated by calling:
public StatBlock weaponStats;
public AmmoClass ammo;
void Awake() {
weaponStats.ComputeStats( ammo.ammoStats );
}
What I came up with, works… but… it feels convoluted, and I thinks there’s got to be a better way.
Problems:
-
I entirely failed to make StatBlock inherit from BaseStatBlock, at least while also having the individual stat items inherit. Duplicating each stat seems wasteful.
-
Needing a seperate class for floats and ints. More duplication.
-
Needing a way to convert between float and int during evaluation, and StatItem_int not inheriting the converter from the base class. Yet more duplication.
Stripped down version:
public enum ComboType { WeaponOnly, AmmoOnly, WeaponBase, AmmoBase }
public enum ValueType { Value, Multiplier }
[System.Serializable]
public class BaseStatBlock {
public BaseStatItem damage;
public BaseStatItem accuracy;
public BaseStatItem_int maxAmmoCount;
}
[System.Serializable]
public class StatBlock {
public StatItem damage;
public StatItem accuracy;
public StatItem_int maxAmmoCount;
public void ComputeStats ( BaseStatBlock ammoBlock ) {
damage.value = ComputeStat( damage, ammoBlock.damage );
accuracy.value = ComputeStat( accuracy, ammoBlock.accuracy );
maxAmmoCount.value = ComputeStat( maxAmmoCount, maxAmmoCount.maxRange );
}
int ComputeStat ( StatItem_int weap, BaseStatItem_int ammo ) {
if ( weap == null || ammo == null ) { return 0; }
return (int)ComputeStat( weap.ToFloat(), ammo.ToFloat() );
}
float ComputeStat ( StatItem weap, BaseStatItem ammo ) {
if ( weap == null || ammo == null ) { return 0f; }
switch ( weap.combo ) {
case ComboType.WeaponOnly:
return weap.baseValue;
case ComboType.AmmoOnly:
return ammo.baseValue;
case ComboType.WeaponBase:
if ( ammo.type == ValueType.Multiplier ) {
return weap.baseValue * ammo.baseValue;
} else {
return weap.baseValue + ammo.baseValue;
}
case ComboType.AmmoBase:
if ( weap.type == ValueType.Multiplier ) {
return ammo.baseValue * weap.baseValue;
} else {
return ammo.baseValue + weap.baseValue;
}
default: return 0f;
}
}
}
[System.Serializable]
public class BaseStatItem {
public float baseValue;
public ValueType type;
}
[System.Serializable]
public class StatItem : BaseStatItem {
public float value;
public ComboType combo;
}
[System.Serializable]
public class BaseStatItem_int {
public int baseValue;
public ValueType type;
public BaseStatItem ToFloat () {
BaseStatItem f = new() {
baseValue = this.baseValue,
type = this.type,
};
return f;
}
}
[System.Serializable]
public class StatItem_int : BaseStatItem_int {
public int value;
public ComboType combo;
public new StatItem ToFloat () {
StatItem f = new() {
baseValue = this.baseValue,
type = this.type,
};
return f;
}
}