Hey, so yeah, Catalyst is designed with the intent of killing spaghetti of a roughly similar kind, but it's not EXACTLY targeted at the kind of trail of tears / burning shots example you've laid out (at least, as far as I understand how that interaction works, I haven't played much binding of isaac). Catalyst specifically deals with stats and modifiers, whereas the example looks like a combination of stats + mods and an effects system. If I were to program it with catalyst, I'd do something like this (this is a very rough pseudo-code toy example which won't work if copy & pasted but should at least give you an idea of the rough shape of the system):
First create an effect (burn in this example), which is where the modifiable catalyst stats live (maybe have a base Effects() constructor that BurnEffects() can inherit from, but I'll leave that out here):
function BurnEffect() constructor {
chance = new CatalystStatistic(1, 0, 1)
.SetClamped();
damage = new CatalystStatistic(3);
duration = 120;
apply = function(_context) {
_context.target.ApplyBurn(
damage.GetValue(_context),
duration
);
}
}
Then you'd have an effects collection, something like this:
function AttackEffectSet() constructor {
effects = {};
static Provide = function(_effect_id, _source, _effect) {
var _entry = effects[$ _effect_id];
if (is_undefined(_entry)) {
_entry = {
effect : _effect,
providers : []
};
effects[$ _effect_id] = _entry;
}
array_push(_entry.providers, _source);
return _entry.effect;
}
static Resolve = function(_context) {
var _names = variable_struct_get_names(effects);
for (var _i = 0; _i < array_length(_names); _i++) {
var _effect = effects[$ _names[_i]].effect;
if (random(1) < _effect.chance.GetValue(_context)) {
_effect.apply(_context);
}
}
}
}
Then you'd have the actual burn being created through the collection constructor:
burn = attack_effects.Provide( "burn", fire_mind_item, new BurnEffect() );
If you wanted to modify the burn later (maybe a buff or whatever), it'd be something like:
burn.damage.AddModifier(
new CatalystModifier(2, eCatMathOps.ADD)
.SetSourceLabel("Hotter Flames")
);
Or:
burn.damage.AddModifier(
new CatalystModifier(0.5, eCatMathOps.MULTIPLY)
.SetSourceLabel("Concentrated Tears")
.SetCondition(function(_stat, _context) {
return _context.source == eAttackSource.TEAR;
})
);
And then the tears would resolve the effect when they hit the enemy:
player.attack_effects.Resolve({
owner : player,
target : enemy,
source : eAttackSource.TEAR
});
And the trail would resolve probably on some per-enemy cooldown based on whether they are still standing on it or not, something like this:
if (CanDamageTarget(_enemy)) {
DealCreepDamage(_enemy);
player.attack_effects.Resolve({
owner : player,
target : _enemy,
source : eAttackSource.TRAIL
});
StartTargetHitCooldown(_enemy);
}
And THEN on the enemy side, you'd need some sort of status/tick handler dealing the actual damage:
function BurnStatus(_target, _damage, _duration, _tick_rate) constructor {
target = _target;
damage = _damage;
duration = _duration;
tick_rate = _tick_rate;
time_until_tick = _tick_rate;
static Update = function() {
duration--;
time_until_tick--;
if (time_until_tick <= 0) {
target.TakeDamage(damage);
time_until_tick = tick_rate;
}
return duration > 0;
}
}
Something like that. An effects system like that is much more game-specific than catalyst is trying to be, which is much more targeted towards "I have a number and I want to modify it in various ways" compared to stuff like "I want a burn system applied to an enemy". But catalyst naturally fits into the larger effect/status style system very cleanly and makes altering the burns (like "Hotter Flames" or "Concentrated Tears" example) much easier to manage.
That being said, thinking about this example gave me a few ideas for some extension points to Catalyst that I might add into the library, lol..