Sign in to follow this  
AaronWizardstar

Effects that depend on runtime information in composition-based spell system

Recommended Posts

I'm designing a spell system. While I don't have a full ECS system available, I want to use a composition-based approach as much as possible. For instance a spell's targeting and area of effect would be defined by Range components with modifier components like TargetEnemyActors or TargetNeedsLineOfSight.
 
What a spell does would be defined by Effect components, which determine both graphics and game state changes. So Effects would include animating a projectile from a start location to an ending location, or damaging a certain actor. Effects would likely have a time or delay value for animation, or be able to be paired in parent-to-child chains to make effects follow one another. I'm wondering though about things certain Effects could only know at runtime. At runtime a spell would know its caster, target, and area of effect, but I'm wondering if that's enough for all Effects without making the Effects themselves more complex.
 
Take a fireball spell. It'd have an effect for launching a fireball sprite from the caster to the target (AnimateProjectileFromCasterToTarget...?) followed by an effect for damaging the target actor (DamageTargetActor). Though if I wanted fireball to be an area of effect spell, DamageTargetActor would need to be modified to damage all actors in the spell's area of effect (...DamageActorsInAreaOfEffect?). And maybe I'd want fireball to be able to damage the caster's allies, but have another area of effect spell that only damages enemies, so now DamageTargetActor needs an extra flag.
 
Now imagine a Ball Lightning spell, where the caster launches a bolt at an enemy and after it hits more bolts are launched from the target enemy to other enemies. The spell would need to determine at runtime what enemies are near the target enemy. Would this be inside some other kind of Effect, that would dynamically generate extra Effects to represent the new bolts following the initial bolt? Is this even a good path to go on for my spell system?

Share this post


Link to post
Share on other sites

Well first off, your ECS like system is your own as there is no global standard.  I personally more closely follow T-Machine's usage idioms myself, it's not exactly like his either.  I want the simplest components I can get.  Where I cannot break it down any further, then build up entities from there.

To me your Effect sounds way to complex, unless that is the Entity portion of the ECS system?  You were not very explicit about that.  I was actually designing a spell system for our ECS on Friday, so at least how I want it done is still fresh in my head.  I only laid out a fireball spell as I did not have a lot of time.  Your component names are way to complex for me, DamageActorsInAreaOfEffect, instead, RadiusDamage

Entity is composed of the following components:

    - Position

    - Direction

    - Velocity -- It spawns moving at it's maximum velocity

    - Max Distance -- it flames out when it reaches this, or it can be set to zero and goes until it collides

    - Animate -- in this case it has a list of sprites to loop through, each update it changes to the next

    - Damage -- It has a radius variable, if it's > 0.0 it's area damage, if it's 0.0 all damage is applied to what it collided with, along with a fall off variable.  Currently I also have a damage type variable for fire, ice, blunt, etc.  I might split then up into a radius damage and damage component though.

My goal is to have as few components as possible and use them in the most ways I can to simplify my system code.  It's meant to give designers, non-programmers and easier time in making new things without dragging a coder away from their task.  But if you have a billion components you've just defeated the purpose I believe.

Share this post


Link to post
Share on other sites

Your component names are way to complex for me, DamageActorsInAreaOfEffect, instead, RadiusDamage

I say "area of effect" instead of "radius" since the area might not be a circle, like with a ray spell that hits multiple enemies in a line (my game is tile-based).

 

Entity is composed of the following components

<snip>

Does Entity represent a whole spell? I'm not sure how well I can apply this system to myself since your example seems primarily geared for projectile spells or basic damage spells. I'm trying to come up with a system that can handle several kinds of spells like teleporting spells, spells that alter terrain, ally buff spells, and so on.

 

Well first off, your ECS like system is your own as there is no global standard.  I personally more closely follow T-Machine's usage idioms myself, it's not exactly like his either.  I want the simplest components I can get.  Where I cannot break it down any further, then build up entities from there.

I brought up ECS and my lack of such really because the engine I'm using is more like a scene tree. A Spell would be a node and effects would be child nodes. So it's not like an ECS, but composition is still present.

Share this post


Link to post
Share on other sites

My system can handle teleporting spells, the fireball example was the entity created when firing a fireball only.  Any other spell would compose some of the same or completely different components.  You can even make Damage (for direct damage to one thing only), DamageRadius and DamageSquare, etc.  We're doing a grid based game so it's slightly different, radius is enough for us.  I might make a DamageGrids component so we can do so many grids in each direction or something, if we come up with a use case for it.

With components you have to think differently about the composition of something.  Then break it down to the most reusable parts possible.  I have a general rule that my components won't have more than x variables, so far that rule is < 6.  I'm willing to break it under very specific situations of course, I don't believe in absolutes for this sort of thing.  An entity will be made up of any number of components mind you.  I don't care.

But I can see having DamageTeleport (when it hits an enemy it teleports them else where).  Or even Teleport as a form of movement.  Either re-appearing regularly then disappearing, moving and reappearing.  All should be possible with minimal work depending on the engine; never really looked at godot to be honest.  Then altering terrain would be DamageTerrain, or AlterTerrain if you prefer that nomenclature, where maybe no damage is given to things in the area but it deforms the ground around them.  You can attach a ModifyStatsOnCollision component if you want.  Then have a variable for Friend/Foe and which stat (just one) and how much to alter it by.  If you want to have 4 stats changed on your allies, attach 4 of these components is how I would do it.

 

Again, everyone is different.  Having used something like ECS long before it was even a name but I still find issue at times breaking things down.  I often write it once, use it for a bit then sub-divide it.

Share this post


Link to post
Share on other sites
Sorry, I'm still having trouble understanding how your system works and how I'd adapt it to my game.
 
I'm worried I put too much emphasis on the "ECS" notion. I'm not really using ECS, but since my engine represents everything as nested nodes it implies some kind of composition approach.
 
If it helps, my game is turn based as well as tile based.
 
You talked about an Entity a lot. This is the whole spell right? Or the visible part of the spell? A magic bolt basically, that applies its effect when it reaches the target? Hence you said that different effects like teleport, terrain altering, and buffs would be represented by different "damage" components?
 
And I was confused by your teleport spell examples. So you had an idea that the "teleport self" spell could be represented by a form of movement, though I'm unsure how such a spell would be composed while fitting in with how you seem to be doing the rest of your spells.
 
For instance, I was imagining a teleport spell which moves the caster to the target tile, and a wind spell which moves a target actor away from the caster. I guess the wind spell is where you'd use a "DamageTeleport" component in the spell (though, how would you set it up to make it move the target away from the caster specifically)? Maybe the teleport spell would use some kind of "DamageTeleportSelf" component?

Share this post


Link to post
Share on other sites

Not sure how much use this is if you want a hardcore 'component'-ized spell system, but in Idle Raiders (and its successor Second Run, you can look it up on Kongregate if you want to play it) we don't solve this generally, instead there's a whole bunch of (re-usable) hard coding which is working fairly well. I think we have close to a hundred different spells in the actual game now (playable by people, and more added on a regular basis) and we haven't really encountered major issues.

Our spells are lists of actions (basically, functions that get called one after the other, with delays between them). We have a fireball spell in our game, and that has the actions "projectileFX" (for the graphical effect of launching the projectile, that also computes how long it's going to take), "projectileSound" (for sound effect), followed by the action "fireDamage" (for dealing damage). 

When spells are constructed they are being given generic options (string-value pairs), in this case stuff like the file name for the projectile, the damage modifier, speed of the projectile, etc... We also have an "Ice Shard" spell, that could just be implemented as the Fireball spell but with different options (for various gameplay reasons, it's an entirely separate spell in our system though).

If we wanted to add an AOE component at the end of your spell, there are two ways we could do it. We could again hard-code it (maybe leave it out by disabling it with an option when the spell is constructed), and just add an "AOEFireDamage" action in addition or instead of the single target fireDamage action. The second way would be via our 'passive ability' system. All abilities can trigger other abilities using various gameplay rules. We could just create a generic "aoedamage" spell that is triggered by the fireball spell. As a practical example there's an "Ignite" passive skill that triggers a (burning) damage over time effect on the target every time a Fireball crits. Oh yeah, there's also an actual AOEDamage ability that can be used by warriors to have a chance to "cleave" their melee attacks, that works like this.

This works in a data-driven approach, too. All these 'actions' are created at runtime anyways (it's Javascript so it's easier to do this there, but in C++ they would just be function pointers that always have the same type, or if you want to get fancy, instances of classes derived from a SpellAction base class), so it's not a problem to cobble together an editor that assembles new spells from these basic actions. 

There's one major upgrade we could and would like to make to this system, which is to have skills be an action-tree instead of an action list. With action trees (so a single action can branch out into multiple follow-up actions, or multiple branches can join back into one), it would be easier to have effects where you need to track multiple instaces of something that has previously been started by a different action.

For example, a random spell could say spawn three different projectiles that travel around for a bit, which would mean the action tree branches out into three paths, and at the end there would be a connecting action node that waits for all three projectiles to arrive at their target before doing something. Or the spell launches a random amount of projectiles, and each of them does something different (random?) when it reaches the target. That kind of stuff would be a lot easier to handle in terms of code if the "random things" that happen at the end can refer to a parent chain of action nodes, instead of having to find whatever they need within the linear list of actions that is there now.

We haven't done that yet mostly because we haven't encountered any serious use cases where we couldn't just (again) hard code around the problem. It sounds dirty but complicating the system needs to be a productivity win (less time spent creating the same things for the game), which we don't see at the moment.

Edited by agleed

Share this post


Link to post
Share on other sites

Our spells are lists of actions (basically, functions that get called one after the other, with delays between them). We have a fireball spell in our game, and that has the actions "projectileFX" (for the graphical effect of launching the projectile, that also computes how long it's going to take), "projectileSound" (for sound effect), followed by the action "fireDamage" (for dealing damage).

Yeah, this was what I had in mind when I was talking about "effect components" earlier.

My main question is like this: You have a projectileFX action for showing a sprite moving from one location to another. It's simple enough to set a projectileFX action's sprite and speed when designing a spell that uses it. When the spell is cast the projectileFX action also needs to know the start and end points of its sprite. Do projectileFX actions assume the start is always the spell caster while the end is always the spell target? If not, how do you give the action the correct points when the spell is running?

Also, your passive ability system (basically, an action in a spell can be set to trigger another spell?) and your ideas for action trees are similar to ideas I've had for my own system, like the ball lightning spell that starts as one lightning bolt then spawns other lightning bolts. Though this gets back to my earlier question of how actions access necessary runtime information, like setting up the start and end points of the secondary lightning bolts. Also, I'm not sure my engine would be able to handle joining actions in an action tree so that'll be something I'll miss out on.

Share this post


Link to post
Share on other sites

The projectileFX action internally calls a function in our graphical effects system. That function is hardcoded to take raw game-world positions, and what the projectileFX action does is take the casting entity and the target, get the positions of those and hands it over to that FX function. If we wanted to shoot the projectile not at the target, but somewhere else, we'd add a different kind of action (or just an option to projectileFX) that doesn't take the target entity's position, but whatever else you need. 

I should add that all over the code, we have cases where 'variables' are really treated as 'this is either a variable or a function' (inspired by functional programming languages), so we can do stuff like changing the target position given to that FX function at runtime (for example, if we want a projectile to track a target, instead of flying to the same place when the target moves), or change the 'arrival time' variable which the spells use to know when to trigger damage.

The lightning bolt thing wouldn't require a tree or anything like that. In our linear action list system, I would just give the thing [n] iterations of a [lightningFX, lightningSFX, damage] action triple, where n is the number of times you want it to jump. Every time the lightningFX is used, it writes the location of the target to some internal variable of the spell that later executions of lightningFX reference. We have a bunch of tracking stuff like that in our spells, too. We have a spell that spawns rotating orbs around a caster, who shoot particles regularly. Since the positions of those always change and they are spawned at runtime, we just add those particle effect objects into a 'spawnedParticleEffects' array that every spell has, and every time that spell shoots a projectile, it picks a random entry out of that array as a source location.

Share this post


Link to post
Share on other sites

 

Sorry, I'm still having trouble understanding how your system works and how I'd adapt it to my game.
 
I'm worried I put too much emphasis on the "ECS" notion. I'm not really using ECS, but since my engine represents everything as nested nodes it implies some kind of composition approach.
 
If it helps, my game is turn based as well as tile based.
 
You talked about an Entity a lot. This is the whole spell right? Or the visible part of the spell? A magic bolt basically, that applies its effect when it reaches the target? Hence you said that different effects like teleport, terrain altering, and buffs would be represented by different "damage" components?
 
And I was confused by your teleport spell examples. So you had an idea that the "teleport self" spell could be represented by a form of movement, though I'm unsure how such a spell would be composed while fitting in with how you seem to be doing the rest of your spells.
 
For instance, I was imagining a teleport spell which moves the caster to the target tile, and a wind spell which moves a target actor away from the caster. I guess the wind spell is where you'd use a "DamageTeleport" component in the spell (though, how would you set it up to make it move the target away from the caster specifically)? Maybe the teleport spell would use some kind of "DamageTeleportSelf" component?

 

 

As an aside I did not read agleed's responses or yours to him.  Don't have time at this moment.

Yeah, I figured my teleport spell would be a little confusing that night, but I as already in bed :)  I envisioned it as a "thing" (say a fireball) that cloaks while it moves then reappears.  I'm working on a semi-turn based (after x seconds a turn auto happens) and it's tile/grid based.  I'm not the designer, just a mere peasant programmer so I don't come up with the spell ideas, clearly.  Also, yes the entity is the whole spell.  In my case it's a struct with a std::bitset that says what components it has.  It can have multiple components also, the system that holds all of that type of component (some systems have 2-3 very small components that are similar) will figure out if it has multiple and handle that automatically.  So you just attach component after component without worry.

For your idea of a spell that moves the spell caster them self to a new location, with my system would not be all that difficult (though far harder than a fireball or lightning effect).  You clearly have an animation period after a turn executes (or whatever you call it).  I would spawn an entity for the spell with all the requisite components.  Mine is VanishComponent, DestinationComponent (moves you so much per turn), SpritesComponent (not Sprite as it has multiple images, it handles timing and changing frequency etc, this can give it a shimmer effect as it moves for instance, or nothing if you don't want that).  Upon arrival at it's destination the movement system will automatically raise an event announcing it to any listeners.  I have an EventListenerComponent (I use compile time hashed names to speed things right up and broadcast hashed names) which causes the ReappearComponent (think that's it's name, but it is what it does.  It undoes what Vanish did).

As "simple" as all that is, there is your spell via my system(s).  My ECS system is getting an addition of entities being attached to entities (linked list with children).  Thankfully it's just two pointer to get things started that I need to store per Entity.  I also don't have a ton of them; I envision < 2000 per level.

Share this post


Link to post
Share on other sites

The projectileFX action internally calls a function in our graphical effects system. That function is hardcoded to take raw game-world positions, and what the projectileFX action does is take the casting entity and the target, get the positions of those and hands it over to that FX function.

 OK, so projectileFX does assume the start is the caster and the end is the target. By extension all actions are implemented such that they are aware of all the runtime data they need themselves, like a damage action would always damage the target unit or the units in the spell's area of effect?

If we wanted to shoot the projectile not at the target, but somewhere else, we'd add a different kind of action (or just an option to projectileFX) that doesn't take the target entity's position, but whatever else you need.

This position parameter would have to be relative to either the caster's position or the target's position though, right? You wouldn't give the action just a constant position?

The lightning bolt thing wouldn't require a tree or anything like that. In our linear action list system, I would just give the thing [n] iterations of a [lightningFX, lightningSFX, damage] action triple, where n is the number of times you want it to jump. Every time the lightningFX is used, it writes the location of the target to some internal variable of the spell that later executions of lightningFX reference. We have a bunch of tracking stuff like that in our spells, too. We have a spell that spawns rotating orbs around a caster, who shoot particles regularly. Since the positions of those always change and they are spawned at runtime, we just add those particle effect objects into a 'spawnedParticleEffects' array that every spell has, and every time that spell shoots a projectile, it picks a random entry out of that array as a source location.

So in addition to a set of actions (and the locations of the caster and target, and the area of effect) spells have semi-generic storage properties the actions can write to and read from?

Though I'm still wondering about the ball lightning spell. You said you'd have the set of actions that represent a single bolt repeated for the number of secondary bolts you'd want to appear. By using spell's storage properties (I assume) you'd be able to keep track of the target of one bolt to be used as the start of the next bolt. Now after the first bolt I need the secondary bolts to launch at enemies within a certain distance to the original target. I'm guessing before starting a secondary bolt I could have an action that works out the next enemy, storing its location in one of the spell's storage properties to use as the target for the next bolt?

For myself, assuming I'm getting your spell storage properties idea right, I'd use a single dictionary in the spell. And if actions store values in that dictionary I could expose properties in them to control what key strings they use.

For your idea of a spell that moves the spell caster them self to a new location, with my system would not be all that difficult (though far harder than a fireball or lightning effect).  You clearly have an animation period after a turn executes (or whatever you call it).  I would spawn an entity for the spell with all the requisite components.  Mine is VanishComponent, DestinationComponent (moves you so much per turn), SpritesComponent (not Sprite as it has multiple images, it handles timing and changing frequency etc, this can give it a shimmer effect as it moves for instance, or nothing if you don't want that).  Upon arrival at it's destination the movement system will automatically raise an event announcing it to any listeners.  I have an EventListenerComponent (I use compile time hashed names to speed things right up and broadcast hashed names) which causes the ReappearComponent (think that's it's name, but it is what it does.  It undoes what Vanish did).

OK. Similar to my earlier question to agleed, are VanishComponent, DestinationComponent, and ReappearComponent implemented to always be affecting the caster, or are they made aware of which actor they're affecting in some other way?

Share this post


Link to post
Share on other sites

All my components that can effect a character reference an ID which just says what entity it's effecting aka character.  So it can be the caster or target.  Though as they stand now, only just the one target.  Just makes life easier and the designer less likely to harass me ;-)

Share this post


Link to post
Share on other sites

OK, so your components don't reference a character directly, but the character's ID. Nonetheless it sounds like effects are hardcoded to manipulate either the spell's current caster or the spell's current target (maybe both? or the ability to choose whether to manipulate the caster or the target?). In other words there isn't something else telling the effect what character to work on while the spell is running.

Share this post


Link to post
Share on other sites

No, it can only take one ID, at this time but adding another ID in a struct is not a big deal.  So it can only effect that entity (entities for me are ID's only uint32_t currently but I do a using EntityID = std::uint32_t so I can change at anytime and it just works).  Spell systems have no idea who they're working on, just the ID.  They have access to it's health as input if needed etc.  I somewhat based my ECS off of the DoomRL (DRL now) code that is open sourced.

For me a Component System only works on as few components as possible.  The vast majority of mine work on one alone as it should be in my opinion, and without checking them all out they might only be a 1:1 right now.  Spells are a bit different in that they need to tie several unrelated components together but they only hold reference ID's to the components and timing info on when to trigger their use.  So say when the vanish component triggers first, making our caster vanish.  when it finishes and there is a listener it generates an event and our spell system being the listener gets it.  It then knows, okay step 1 is done, activate the lightning bolt component, and a destination component.  When the destination system see's it has a listener for this event (upon arrival) it fires off the event and the spell then removes the bolt/destination components and activates the reappear component and we're done so it kills itself off too.

Also when an entity "dies", in this case say an enemy, I don't destroy it (death animations start though).  Say it dies on frame 20, all my entities have an (bool) is_alive variable which would be set to false when they're killed.  Then on frame 21 any unresolved projectiles do not "hit" something that is gone.  At the beginning of frame 22 though I cleanup all dead entities from frame 20.  By that time all systems what were targeting that enemy have figured out it's is_alive is false and do whatever they need to do to handle that case.

Apparently it doesn't want to embed an image from dropbox... https://www.dropbox.com/s/dxem3elsautghve/Entity.png?dl=0

So that is my entity struct, simple.  The handle is made up of two parts, the ID itself taking up 24 or 28 bits, I forget now and the second is the version number of this handle.  That way if something old is holding onto an old handle it won't have the correct version number and the newer Entity won't be referenced by whatever system has an expired handle.

I'm tired, hopefully that made sense :)

Edited by Mike2343

Share this post


Link to post
Share on other sites
I'm still working out how to handle my spells, so I decided to make mockups of the actual spells I'm planning and break down how they work.

For context, my game looks like this.

n2zmW9p.png

Realistically my game's actual animation will be limited and consist of shuffling actor icon sprites around.

Attack

kD88Yr1.gif

"Spells" in my game are really any kind of combat ability, so I'd be counting regular attacks as a spell. Still, beyond just applying damage to the target I wanted some animation, primitive as it is.
  • Let Y be the point halfway between the caster sprite and the target sprite
  • Over X seconds move the caster sprite to Y
    • This doesn't change the caster actor's true tile position
  • Apply damage to the target
    • This should be a reusable effect, handling what happens when the actor dies and such. It also takes a vector to handle the recoil bounce, in this case taking the vector from the caster to the target.
  • Over X seconds move the caster sprite back to its original position
Explosive Bolt

IazAfed.gif

Everyone likes area of effect explosion spells right? This spell targets any tile within range of the caster and has an area of effect that damages all enemies within it.
  • Show a projectile sprite moving from the caster to the target tile at a speed of X units/second
  • For each "layer" of the area of effect (tiles grouped by their distance from the target)
    • Show a spell field sprite over each layer tile
    • For each enemy actor on a tile in the current layer
      • Apply damage to the actor
    • Proceed to the next layer in X seconds
I decided to get fancy by having the explosion spread out from the target instead of hitting every enemy inside the AOE at the same time. So when the spell is running I'll need to work out what tiles to hit at what time interval.

Throw

a9fRKKv.gif

I was going to give warriors a "push" ability but decided that was boring. Now warriors will get to pick up adjacent enemies and throw them. Also, the warrior here is throwing the skeleton against a wall for extra damage...
Throw actually takes two targets. One for the adjacent enemy and a second for the throw target. But lets assume I already know how to handle multiple targets for a spell and work out how the spell runs.
  • Let A be the point halfway between the caster sprite and the target 1 actor sprite
  • Let B be the point Bi units above the caster sprite along the Y axis. This is where the target 1 actor sprite will be after it's been picked up.
  • Let C be the point Ci units above the caster sprite along the Y axis. This is where the caster sprite will be when it throws the target 1 actor.
  • Let D be the point Ci units above B along the Y axis. This is where the target 1 actor sprite will be at the start of the throw.
  • Over E seconds move the caster sprite to A
  • Simultaneously (the caster is picking up the target 1 actor):
    • Over F seconds (less than E) rotate the target 1 actor sprite to 90 degrees
    • Over E seconds move the caster sprite to its original position
    • Over E seconds move the target 1 actor sprite to B
  • Simultaneously (the caster is "jumping" to make the throw):
    • Over G seconds move the caster sprite to C
    • Over G seconds move the target 1 actor sprite to D
  • Simultaneously (the caster finally throws the target 1 actor):
    • Over G seconds move the caster sprite to its original position
    • Rotate the target 1 actor sprite to the angle along the vector from D to the target 2 tile
    • Start moving the target 1 sprite to the target 2 tile with a speed of H units/second
  • When the target 1 actor sprite reaches the target 2 tile
    • Apply damage to the target 1 actor
    • If the target 1 actor is still alive
      • If the target 2 tile is empty
        • Set the target 1 actor's tile to the target 2 tile
      • Else
        • Determine the true landing tile and make it Z
        • Rotate the target 1 sprite to 180 degrees (upside down)
        • Over J seconds move the target 1 sprite to tile Z
        • Apply damage to the target 1 actor (again)
        • Set the target 1 actor's tile to Z
      • Reset the target 1 actor sprite's rotation
How to implement all these

I was thinking of composing my spells out of hopefully reusable "effects" because the spells themselves seem to be built from common base actions like moving an actor's sprite between two points or displaying a hit sprite. But for a lot of spells these "base" actions require information that seems like they'd have to be worked out at run time. I mean, in Throw the target enemy actor's sprite gets moved between up to four points. This makes breaking spells up into interchangeable "effects" difficult, especially if I'd want to compose spells in a visual editor.

The suggestions in this thread so far has boiled down the idea that a spell's effects being able to access the spell's caster and target(s) should be enough (I'd add effects being able to access the spell's area of effect too). agleed also added a suggestion that sounded like effects could store temporary values in the main spell to be used later.

With that in mind, this is how I imagine spells made from composable effects would work in my game.
  • Spells have a caster, one or more targets, and an area of effect for each target.
  • Spells have a list of effects. When started, a spell runs its effects sequentially.
  • Effects themselves may have child effects. When an effect is finished it runs its children sequentially.
  • Most effects have a time component, and may also have a speed component like for projectiles.
  • Spells have a dictionary. Effects may read from and write to this dictionary.
  • That means there may be special "calculator" effects for writing data needed by other effects to the spell's dictionary.
  • Effects will typically have some kind of enum property that sets whether it manipulates or is relative to the caster or one of the targets.
Here are some common effects and their settings
  • MoveActorSprite
    • Moves an actor's sprite between two positions that are relative to any combination of the caster's position and the target positions. So the start position could be set to be relative to the caster while the end position could be set to be relative to target 1, or vise versa.
    • The actor to move is affect either the caster or one of the target actors.
  • ShowSingleSprite
    • Shows a single sprite over a position relative to the position of either the caster or one of the targets.
  • ShowProjectileSprite
    • Shows a sprite moving between two positions that are relative to any combination of the caster's position and the target positions.
  • DamageActor
    • Damages an actor. Set to affect one of the targets (or maybe even the caster).
Then there are some more specialized effects.
  • StoreEmptyAdjacentTile
    • Given either the caster or one of the targets, gets an adjacent empty tile and stores it in the spell's dictionary under "StoredTile" (key can be customized).
  • SetActorTileToStoredTile
    • A companion to StoreEmptyAdjacentTile. Set to affect either the caster or one of the targets, set's the actor's tile position to the value under "StoredTile" (key can be customized) in the spell's dictionary.
So, the Throw spell would start something like MoveActorSprite{ actor = caster; start = [caster, (0,0)]; end = [target 1, (0, -16)] }. The step where the target 1 actor is thrown would be represented by MoveActorSprite{ actor = target(0), start = [caster, (0, 16)], end = [target(1), (0, 0)] }. If the thrown actor hits a wall, the effects StoreEmptyAdjacentTile{ tile = target(1); storekey = "StoredTile" } and SetActorTileToStoredTile{ actor = target(0); storekey = "StoredTile" } are used.

Though I'm still not sure how to handle the branching logic, like checking whether the thrown actor hit a wall in the first place. Nor am I sure about how to handle the explosion spread effect in the Explosive Blast spell.

I'm actually starting to wonder if I'd be better off abandoning my effect composition idea and just writing each spell in code individually.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this