This looks pretty sweet. Are the modifiers able to stack right out of the box? If I take Binding of Isaac as an example - if you have burning shots and leave a trail of tears after yourself when you move, the trail of tears burn as well. Those combinations are kind of a mess to program and has left me with so much spaghetti code that I could open up an Italian restaurant.
Would this tool be able to assist with that kind of stuff? Thanks!
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")
);
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..
Wow, thank you for such an in-depth answer! Your way of coding is quite different from mine, so it's very interesting to see how you'd solve issues like this. I'm just rounding up a small project at the moment, but I'll make sure to purchase this when I jump back to my main game where something like this is desperately needed.
← Return to tool
Comments
Log in with itch.io to leave a comment.
This looks pretty sweet. Are the modifiers able to stack right out of the box? If I take Binding of Isaac as an example - if you have burning shots and leave a trail of tears after yourself when you move, the trail of tears burn as well. Those combinations are kind of a mess to program and has left me with so much spaghetti code that I could open up an Italian restaurant.
Would this tool be able to assist with that kind of stuff?
Thanks!
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:
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..
Wow, thank you for such an in-depth answer! Your way of coding is quite different from mine, so it's very interesting to see how you'd solve issues like this. I'm just rounding up a small project at the moment, but I'll make sure to purchase this when I jump back to my main game where something like this is desperately needed.