Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(+1)

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!

(1 edit) (+1)

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..

(+1)

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.