Here is a simple solution to the problem that builds upon our code. Assuming we’re not late in development and using the modifier system all around our codebase, I’ve decided to replace the method add_modifier()
with two specialized ones: add_value_modifier()
and add_rate_modifier()
. They both call the now pseudo-private _add_modifier()
.
All the code below is an update on BattlerStats.gd
.
# Initializes keys in the modifiers dict, ensuring they all exist. func _init() -> void: for stat in UPGRADABLE_STATS: _modifiers[stat] = { # Each stat now have two dictionaries, `value` and `rate`, to later make it easy # to calculate final stats. value = {}, rate = {} } # Adds a value-based modifier. The value gets added to the stat `stat_name` after applying # rate-based modifiers. func add_value_modifier(stat_name: String, value: float) -> void: # I miss some features from languages like Python here, that would allow for a more explicit # syntax. _add_modifier(stat_name, value, 0.0) # Adds a rate-based modifier. A value of `0.2` represents an increase in 20% of the stat `stat_name`. func add_rate_modifier(stat_name: String, rate: float) -> void: _add_modifier(stat_name, 0.0, rate) # Adds either a value-based or a rate-based modifier. Notice I'm using a third argument with a # default value of `0.0`. func _add_modifier(stat_name: String, value: float, rate := 0.0) -> int: assert(stat_name in UPGRADABLE_STATS, "Trying to add a modifier to a nonexistent stat.") var id := -1 # If the argument `value` is not `0.0`, we register a value-based modifier. if not is_equal_approx(value, 0.0): # Generates a new unique id for the "value" key. id = _generate_unique_id(stat_name, true) _modifiers[stat_name]["value"][id] = value # If the argument `value` is not `0.0`, we register a rate-based modifier. if not is_equal_approx(rate, 0.0): # Generates a new unique id for the "rate" key. id = _generate_unique_id(stat_name, false) _modifiers[stat_name]["rate"][id] = rate _recalculate_and_update(stat_name) return id func _recalculate_and_update(stat: String) -> void: var value: float = get("base_" + stat) # We first get and sum all rate-based multipliers. var modifiers_multiplier: Array = _modifiers[stat]["rate"].values() var multiplier := 1.0 for modifier in modifiers_multiplier: multiplier += modifier # Then, we multiply the base stat's value, if necessary. if not is_equal_approx(multiplier, 1.0): value *= multiplier # And we add all value-based modifiers. var modifiers_value: Array = _modifiers[stat]["value"].values() for modifier in modifiers_value: value += modifier value = round(max(value, 0.0)) set(stat, value) func _generate_unique_id(stat_name: String, is_value_modifier: bool) -> int: # We now use a boolean to pick the right key and generate a corresponding id. var type := "value" if is_value_modifier else "rate" var keys: Array = _modifiers[stat_name][type].keys() if keys.empty(): return 0 else: return keys.back() + 1
That’s for a solution that builds upon our code. A more object-oriented solution would be to scrap the modifiers variable and to use an array of stat modifier objects to represent modifiers, each object applying a change itself. You can do so using the command pattern we’ll see in the next lesson.
I prefer to avoid objects when simple functions can do the job, though, as in the above. That is, considering this is the extent of our stat upgrade system.