Almost every upgrade/TS/etc is generalized as an Effect
, which consists of two parts: effectValue
and canBeApplied
.
effectValue
is an actual value of the effect. It always returns the effect value, even if it cannot be applied (for ex. if an upgrade is not purchased)canBeApplied
is pretty obvious, idk if I should explain it
Basic application
To apply an effect you need to do the following:
if (effect.canBeApplied) {
multiplier *= effect.effectValue
}
To reduce the boilerplate, there is a function applyEffect
which checks canBeApplied
automatically, so this code can be rewritten as:
effect.applyEffect(v => multiplier *= v);
Applying multiple effects
Things look cool and dandy until we need multiple effects applied subsequently
effect1.applyEffect(v => multiplier *= v);
effect2.applyEffect(v => multiplier *= v);
effect3.applyEffect(v => multiplier *= v);
effect4.applyEffect(v => multiplier *= v);
effect5.applyEffect(v => multiplier *= v);
And here we go with boilerplate again. To battle this one, we have a set of effect application functions:
Effects.sum
- returns a Number sum of Number effects passed into it. If none of passed effects is applicable, defaults to 0.
multiplier += Effects.sum(
effect1,
effect2
);
Effects.product
- returns a Number product of Number effects passed into it. If none of passed effects is applicable, defaults to 1.
multiplier *= Effects.product(
effect1,
effect2
);
Decimal.prototype.plusEffectsOf
- adds a Decimal sum of Decimal or Number effects passed into it to the Decimal called on.
multiplier = multiplier.plusEffectsOf(
effect1,
effect2
);
Decimal.prototype.timesEffectsOf
- adds a Decimal prduct of Decimal or Number effects passed into it to the Decimal called on.
multiplier = multiplier.timesEffectsOf(
effect1,
effect2
);
Conditional effect application
Let's say, you are calculating ND multiplier, and you need to apply Achievement 23 to ND 8 only, and Achievement 34 to ND < 8 only. The obvious way would be
if (tier === 8) {
multiplier = multiplier.timesEffectsOf(Achievement(23));
}
if (tier !== 8) {
multiplier = multiplier.timesEffectsOf(Achievement(34));
}
Looks meh, isn't it? Good thing that Effects system ignores null
and undefined
values passed in effect chains, so this code can be simplified:
multiplier = multiplier.timesEffectsOf(
tier === 8 ? Achievement(23) : null,
tier !== 8 ? Achievement(34) : null
)
Selecting one effect of many
Let's say, you need to find the biggest possible amount of starting antimatter. You have Perk(51)
, Achievement(21)
and other effects which offer different starting AM amount. And you want to default to 10 if none of the effects are applicable. In this case there is a Effects.max
function available which does just that:
player.money = Effects.max(
10,
Perk(51),
Achievement(21),
...
);
Apart from Effects.max
, there are two more functions: Effects.min
and Effects.last
. All these functions are working with Number (not Decimal) only, and they accept first argument as a default value - it must be of a Number type.
Effects.last
serves more of an optimization if min
or max
do too excessive calculations, and it returns the last applicable effect in the chain.