Categories
alchementalist alchementalist devlog personal blog

Alchementalog #2: A Song of Stats and Boosts (Part 2)


All Alchementalogs


In A Song of Stats and Boosts (Part 1) we covered the stats part of the system, both in terms of the player and the spells. In Part 2, we’ll be going over Spell Boosts. What are they? How are they implemented? Etc, etc.

As I said previously, Spell Boosts were a kind of nebulous concept in my head to begin with. I wanted to be able to craft “increases” to spells, but I didn’t have a properly detailed idea of what that was in my head. So I started toying around with ideas. Perhaps some boosts would heal the player on spell cast? Perhaps some boosts would make the spell create something when it’s destroyed? Maybe a boost will increase the “penetration” of a spell and make it able to hit multiple targets? Determining this stuff would give me a proper framework to start implementing the boosts with.

So I started thinking about triggers. When does a spell boost’s effects get applied? The most obvious one is when you add a boost to a spell. If we have a boost that gives the player +10 hp, that should be triggered when the boost is added to the spell. So I set up an enum and added that as the first one: ON_ADD. Then I spent a little more time thinking of situations I might want a spell boost to have an effect and ended up with this list of enums:

enum e_boost_triggers {
	ON_ADD = 100,
	ON_REMOVE = 200,
	ON_CAST = 300,
	ON_TRAVEL = 350,
	ON_HIT = 400,
	ON_RESIST = 500,
	ON_DESTROY = 600,
	ON_ENEMY_DAMAGE = 700,
	ON_ALLY_DAMAGE = 800,
	ON_ENEMY_HEAL = 900,
	ON_ALLY_HEAL = 1000
}

Why am I adding 100 to each enum instead of simply letting it auto-increment by 1? Well, you can actually see an example of why. When I initially made the list, I didn’t think of an ON_TRAVEL enum. Later on, when I did think of it, I could easily add it in-between ON_CAST and ON_HIT by simply making it equal 350. This isn’t necessary right now, but once we start saving this data (if, indeed, Alchementalist will have saving), then letting it auto-increment by 1 will mean that if I added a new enum in the middle, it would mess with all the save data and make it so that the enum values saved in the player data no longer correspond to the enum value of the game data. By using much larger increments, I can squeeze new values in between without having everything become out of sync.

So I’ve got some triggers setup. Whenever a certain thing happens (such as an enemy taking damage), it’ll check through the boosts involved with both entities (as, at least right now, I’m trying to make it so that the player and the enemies both use the exact same spell/boost setup), find any that apply to that trigger and apply them.

This takes us straight to the next section, what does running a boost entail? Well, each boost has a method called Activate which takes three arguments: caster, projectile and target. This gives each boost access to all the three entities it might want to interact with.

Perhaps there’s a boost effect that lowers the target’s speed by half for 2 seconds if the spell hitting it crits. In this case, we’d have the trigger set to ON_HIT, then whenever a spell projectile (or AOE damage, or whatever) hits, it’ll search through the boosts, find the ON_HIT one and send it the ID’s of the caster, the projectile itself and the enemy. The projectile will be holding information about whether it has crit or not, so the function attached to the Activate() method for this “target speed lowering on crit” boost would look something like this:

function (_caster,_projectile,_target) {
	if (_projectile.crit == true) {
		with (_caster) {
			stats.ModifyMoveSpeed(0.5,e_stat_mod_type.PERCENTADD,game_get_speed(gamespeed_fps)*2,other);
		}
	}
}

ModifyMoveSpeed() here is basically just a setter that creates a new StatModifier struct and adds it to the stat_modifiers array of the move_speed struct.

By being able to create custom functions for the spell boosts to run when they activate, I can relatively easily add any sort effect I want.

However, there’s one thing in all the above that I’ve kind of skipped over. So far I’ve only been talking about spell boosts with a single effect. However, I definitely want some spell boosts to have multiple effects. So the system I’ve been describing is actually the system I’ve put in place for creating spell boost effects. They, in themselves, are not actual spell boosts. When I create a new spell boost, they have no effects. Instead, I have a function inside the spell boost which I can call to add an effect to the spell boost. So there are really two constructors involved in creating a fully functioning spell boost. One constructs the spell boost itself, and the other constructs any effects I want to add to the spell boost. The actual spell boost constructor really just creates a struct that holds a list of the effects attached to it (with a few other bookkeeping variables alongside it).

By doing it this way, I can have a single spell boost that has multiple effects. It also means that it’s easy to implement a crafting option to combine spell boosts, as I just need to grab the spell boost effect from one and add it to the effects list of the other spell boost. Or, inversely, I can remove a spell boost effect from a spell boost. This means that I could potentially have a “reroll shrine” of some sort that would remove a random spell boost effect from a spell boost and replace it with a new one.

An array holding the data for a placeholder spell boost effect, and the generator code for generating a randomised version of that effect.

Overall, this whole spell boosts system is what had really been at the heart of the problems I’ve been running into. Every time I implemented something, I would run into problems later on that invalidated what I’d done before; nothing was flexible enough.

However, I believe the solution I’ve described above is robust enough to handle all the possible spell boosts I want to add to the game (believe me, I have quite a few planned). And this system, combined with the assortment of actual spells the player can craft that I have planned, should yield a pretty huge number of potential spells effects, bringing with it a bunch of synergistic and non-synergistic combinations for the players (and indeed myself) to work out. I hope to be able to balance the game in such a way that any spell boost can be added to any spell in the game and it will function as the player would expect.

Having said that, I’m sure there’ll still be refactoring in the future for this system, but at least it shouldn’t be a complete overhaul (as I’ve done at least 3 times so far), it will only be tweaks and modifications.

Well, this has been another pretty large and relatively technically detailed devlog, so hope you’re someone who enjoys diving into code, hahaha.

The next devlog will be about why I’ve been absent from posting stuff about Alchementalist for a while, so it’ll be much less technical. Until then, stay frosty!

By RefresherTowel

I'm a solo indie game developer, based in Innisfail, Australia.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s