- Joined
- May 26, 2009
- Messages
- 1,829
Introduction
I've been asked many times before how I go about making large spells and where to start when doing so. So I've written this Tutorial to explain my method of doing it that I've used for years:
Creating large abilities with many parts can be daunting and difficult to wrap your head around. This tutorial explores a simplistic way to break the process down into more easily managed components that allow you to build up your spell at your own pace. This is not a tutorial for those who've just started out spell making but for those who wish to expand the size of their works but struggle to manage all the parts to their spells or simply don't know where to start. As such it assumes you know how to make an approvable spell
The Concept
This tutorial explores the "StageID" Methodology of creating large spells focusing on using distinct Parts and behaviours to your spell and putting them in separate "Stages" and filtering the main loop function of your spell to run these behaviours simultaneously:
The method is an implementation of Switch/Enum
Implementing the method
Basic Structure
The key to this method is a StageID Variable/Value, it can be utilised by any indexing method as well as hash tables so you can use your preferred method. Apply the StageID the moment you create a part to your spell.
In this Tutorial we'll be using the following example: we want an ability which summons a turret, this turret then fires projectiles which slow units that they hit. This gives us three clear Stages:
We'll also be assuming our indexing method is the dynamic indexing method in this example but it should be simple enough to translate into the other methods
So in our loop we can immediately set up this layout as we know we want to filter based on the stages set out above
Stage IDs can use any datatype you like so long as each one is distinct; for example instead of using "1, 2, 3" we could use "Turret, Projectile, Unit"
The GUI example can be cleared up by nesting the If statements in the Else section of the previous statement and the last one removed (taking the place of the else section of the second) but it's clearer in this format and can be moved later, in the case of JASS you can also replace the numbers with constant functions to make it even easier! In this case I'd recommend ST_TurretStageID(), ST_ProjectileStageID() and ST_UnitStageID()
Setting up the stages
Examples of Spells that use StageIDs
Wrap Up
If you still find it hard to understand, feel free to PM me any time.
Thank you for reading.
~Tank-Commander
I've been asked many times before how I go about making large spells and where to start when doing so. So I've written this Tutorial to explain my method of doing it that I've used for years:
Creating large abilities with many parts can be daunting and difficult to wrap your head around. This tutorial explores a simplistic way to break the process down into more easily managed components that allow you to build up your spell at your own pace. This is not a tutorial for those who've just started out spell making but for those who wish to expand the size of their works but struggle to manage all the parts to their spells or simply don't know where to start. As such it assumes you know how to make an approvable spell
This tutorial explores the "StageID" Methodology of creating large spells focusing on using distinct Parts and behaviours to your spell and putting them in separate "Stages" and filtering the main loop function of your spell to run these behaviours simultaneously:
- Think about the spell you want to create and all the components - if you've used Object Oriented Languages you'll be familiar with this process - think about what differences define these parts and what (if any) things they do in common, these form the basis of your Stages
- Some complex behaviours may lead to needing additional stages, perhaps an object or entity changes behaviour completely halfway through?
- With this method you do not need to worry about variable overlap when recycling so long as you follow the structure correctly which can make the process of spell creation faster and less hassle when debugging
- The method allows you to use a single dynamic index/linked list/hashtable more easily when creating complex spells
- Some complex behaviours may lead to needing additional stages, perhaps an object or entity changes behaviour completely halfway through?
- With this method you do not need to worry about variable overlap when recycling so long as you follow the structure correctly which can make the process of spell creation faster and less hassle when debugging
- The method allows you to use a single dynamic index/linked list/hashtable more easily when creating complex spells
The method is an implementation of Switch/Enum
Basic Structure
The key to this method is a StageID Variable/Value, it can be utilised by any indexing method as well as hash tables so you can use your preferred method. Apply the StageID the moment you create a part to your spell.
In this Tutorial we'll be using the following example: we want an ability which summons a turret, this turret then fires projectiles which slow units that they hit. This gives us three clear Stages:
- Turrets
- Projectiles
- Slowed Units
- Projectiles
- Slowed Units
We'll also be assuming our indexing method is the dynamic indexing method in this example but it should be simple enough to translate into the other methods
So in our loop we can immediately set up this layout as we know we want to filter based on the stages set out above
GUI
JASS
-
ST Slowing Turret Loop
-
Events
-
Time - Every 0.03 seconds of game time
-
-
Conditions
-
Actions
-
For each (Integer Index) from 1 to MaxIndex, do (Actions)
-
Loop - Actions
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 1
-
-
Then - Actions
-
-------- Turret Actions --------
-
-
Else - Actions
-
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 2
-
-
Then - Actions
-
-------- Projectile Actions --------
-
-
Else - Actions
-
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 3
-
-
Then - Actions
-
-------- Slowed Unit Actions --------
-
-
Else - Actions
-
-
-
-
-
JASS:
function ST_Slowing_Turret_Loop takes nothing returns nothing
local integer i = 0
loop
exitwhen i > MaxIndex
if StageID[i] == 1 then
//Turret Actions
elseif StageID[i] == 2 then
//Projectile Actions
else
//Slowed unit Actions
endif
i = i + 1
endloop
endfunction
Stage IDs can use any datatype you like so long as each one is distinct; for example instead of using "1, 2, 3" we could use "Turret, Projectile, Unit"
The GUI example can be cleared up by nesting the If statements in the Else section of the previous statement and the last one removed (taking the place of the else section of the second) but it's clearer in this format and can be moved later, in the case of JASS you can also replace the numbers with constant functions to make it even easier! In this case I'd recommend ST_TurretStageID(), ST_ProjectileStageID() and ST_UnitStageID()
So now we've got our general structure in place but, how does this help us?
Well we now have clear "IF" blocks to go to when we're working on a particular part of the spell so there's that
But more importantly there's never going to be any overlap between the stages so long as we index them correctly
So how do we index things correctly? Well Simple:
1) Do what you normally do for your chosen indexing method
2) Set the StageID for that index to match the relevent StageID in the if structure
3) Done
Connecting the StagesWell we now have clear "IF" blocks to go to when we're working on a particular part of the spell so there's that
But more importantly there's never going to be any overlap between the stages so long as we index them correctly
So how do we index things correctly? Well Simple:
1) Do what you normally do for your chosen indexing method
2) Set the StageID for that index to match the relevent StageID in the if structure
3) Done
GUI
JASS
-
Set MaxIndex = (MaxIndex + 1)
-
Set StageID[MaxIndex] = 1
-
-------- Set up everything else --------
JASS:
set MaxIndex = MaxIndex + 1
set StageID[MaxIndex] = ST_TurretStageID()
//Set up everything else
in complex spells however, these Stages tend to overlap - such as in our example where our turret shoots projectiles which slow units: They each lead on to the next stage!
No problem: We just do as we did before.
Congratulations, if you follow this structure you now no longer have to worry about variable overlapping when it comes to your recycling and you have clear, distinct sections to work on each part of your spell.
If you want to change the behaviour of a specific entity (projectile lands and becomes a turret) you simply change it's own StageID at the appropriate time and you don't need to create a new index
If you have common behaviours between StageIDs then you can separate based on that common factor before separating by the StageID (check if the StageID is either of those two, then later check which one it is specifically)
When you recycle things you only need to swap the data specific to that StageID with the recycled one - to recycle minimal data you should check what stageID the one you're swapping it with is in to know what data needs to be swapped, otherwise you would still need to swap all data
No problem: We just do as we did before.
GUI
JASS
-
ST Slowing Turret Loop
-
Events
-
Time - Every 0.03 seconds of game time
-
-
Conditions
-
Actions
-
For each (Integer Index) from 1 to MaxIndex, do (Actions)
-
Loop - Actions
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 1
-
-
Then - Actions
-
-------- Turret Actions --------
-
-------- (...) --------
-
-------- Turret fires a projectile! --------
-
Set MaxIndex = (MaxIndex + 1)
-
Set StageID[MaxIndex] = 2
-
-------- Set up projectile data --------
-
-
Else - Actions
-
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 2
-
-
Then - Actions
-
-------- Projectile Actions --------
-
-------- (...) --------
-
-------- Projectile Hits a unit! --------
-
Set MaxIndex = (MaxIndex + 1)
-
Set StageID[MaxIndex] = 3
-
-------- Set up unit data --------
-
-
Else - Actions
-
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
StageID[Index] Equal to 3
-
-
Then - Actions
-
-------- Slowed Unit Actions --------
-
-
Else - Actions
-
-
-
-
-
JASS:
function ST_Slowing_Turret_Loop takes nothing returns nothing
local integer i = 0
loop
exitwhen i > MaxIndex
if StageID[i] == ST_TurretStageID() then
//Turret Actions
// (...)
//Turret fires a projectile!
set MaxIndex = MaxIndex + 1
set StageID[MaxIndex] = ST_ProjectileStageID()
//Set up projectile data
elseif StageID[i] == ST_ProjectileStageID() then
//Projectile Actions
// (...)
//Projectile hits a unit!
set MaxIndex = MaxIndex + 1
set StageID[MaxIndex] = ST_UnitStageID()
//Set up slowed unit Data
else
//Slowed unit Actions
endif
i = i + 1
endloop
endfunction
Congratulations, if you follow this structure you now no longer have to worry about variable overlapping when it comes to your recycling and you have clear, distinct sections to work on each part of your spell.
If you want to change the behaviour of a specific entity (projectile lands and becomes a turret) you simply change it's own StageID at the appropriate time and you don't need to create a new index
If you have common behaviours between StageIDs then you can separate based on that common factor before separating by the StageID (check if the StageID is either of those two, then later check which one it is specifically)
When you recycle things you only need to swap the data specific to that StageID with the recycled one - to recycle minimal data you should check what stageID the one you're swapping it with is in to know what data needs to be swapped, otherwise you would still need to swap all data
Examples of Spells that use StageIDs
Wrap Up
If you still find it hard to understand, feel free to PM me any time.
Thank you for reading.
~Tank-Commander
Last edited: