4 Effects system explained
Andrei Andreev edited this page 2019-01-22 21:38:05 +03:00

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.