can you post a small example of the struct, it seems confusing trying to picture it this way... How would a var that increments++ or -- would always start at zero when the strut begins
So, if the timer is just belonging to the state itself, then you would make the variable be part of the struct for that state. And the constructor function would simply set it to zero. If you need to access that value elsewhere for something, you can instead make it a variable of the object/instance.
I will post a couple of the states of my structs from my Jam entry. Anything in ALL CAPS is a #macro I defined elsewhere. Also, there are some function calls here that are part of my input and audio assets(fhAudio/fhInput) so don't try to find docs on those in the manual. Those don't matter anyway for the purpose of this example.
Code:
function bStateIdle(owner) constructor
{
myOwner = owner;
with(myOwner)
{
myState = BSTATE_IDLE;
hspeed = 0;
vspeed = 0;
}
static run = function()
{
//do nothing
}
static leave = function()
{
}
}
Code:
function bStateDrop(owner) constructor
{
myOwner = owner;
with(myOwner)
{
myState = BSTATE_DROPPING;
vspeed = BOX_SPEED_DROP;
}
static run = function()
{
with(myOwner)
{
var spd = BOX_SPEED_DROP;
if(fhInputActionCheckDown(FHINPUTACTION_Down))
spd = 32;
vspeed = spd;
var inst = instance_place(x, y+spd, objSolid);
if(inst == noone)
inst = instance_place(x, y+spd, objSpikes);
if(inst != noone)
{
if(inst.object_index == objPlatform || inst.object_index == objSpikes)
{
pushAgainstDown();
other.leave();
other.myStateCode = new bStateStill(id);
break;
}
//if adding a spring, add a case for it here to destroy the box
if(inst.object_index == objBox)
{
var otherType = inst.myType;
var stop = false;
switch(myType)
{
case BOX_CARDBOARD:
stop = true;
break;
case BOX_WOOD:
if((otherType == BOX_WOOD) || (otherType == BOX_METAL))
stop = true;
break;
case BOX_METAL:
if(otherType == BOX_METAL)
stop = true;
break;
}
if(stop)
{
pushAgainstDown();
other.leave();
other.myStateCode = new bStateStill(id);
break;
}
else
{
instance_destroy(inst);
}
break;
}
}
}
}
static leave = function()
{
}
static playLandSound = function()
{
with(myOwner)
{
if(myType == BOX_CARDBOARD)
PlaySoundExt(SND_BOXLANDCARDBOARD);
if(myType == BOX_METAL)
PlaySoundExt(SND_BOXLANDMETAL);
if(myType == BOXWOOD)
PlaySoundExt(SND_BOXLANDWOOD);
}
}
}
So the "idle" state you see there simply sets the box speed to 0. Note that the constructor function takes the owner instance as the only argument, and it stores that for future usage. The idle state doesn't actually use it though. Note that the idle state also defines a run and a leave() function, even though they don't do anything. Final note on the idle state, the reason the run() function doesn't do anything is because the box in this state is actually being controlled by another object(a crane that drops the box). Therefore, the crane will actually handle making the box go to the dropping state.
Now, the code in the dropping state...when you first start that(by creating the struct with the constructor), note that it also stores the owner in one of the struct variables. Then, the run() function will actually use that owner value to know what box it belongs to, so that it drops that specific box, and check collisions using that specific box. It also does a couple other things. For example, when it is time for the box to stop dropping, it leaves the state(by calling its own leave() function), and it creates the next state on the box(the owner). Finally, it sets the variable on the owner to that new state as well. So once this code block is done, the next step, the run() function will be on the other state, even though it was in the same variable.
Now, I'll show you the create event of the box object, followed by the step event.
Code:
//create
myType = BOX_CARDBOARD;
myState = BSTATE_IDLE;
myStateCode = new bStateIdle(id);
//step
myStateCode.run();
if(myState == BSTATE_STILL && y < 120)
{
instance_destroy();
}
See how simple those are by comparison? The create event simply sets a variable for the box type(I have 3 different boxes though they use the same object), and sets the state, both the variable with the macro and the actual struct. See how I add the 'id' variable to the struct creation? That gets stored in the owner variable of the struct. Then, see how the step even only needs a single line to call the run() function of the state. Note that the state could be ANY state I have defined for the box. Since the states themselves handle changing the 'myStateCode' variable to whatever state the box is in, the step event only has to call the run() function and doesn't actually care what it is doing. The only other code in that step event is checking for a specific case of the box location and state so it will be destroyed if needed.
**************
So, in your case...if you simply want the state to change based on the decrementing variable, you could make that just another variable in the struct for the state. Then, the run() function of that state would decrement it and once it is the right value, it would simply change state to the other struct. That other struct would create its own decrementing variable, which would be a different starting value from the first state going by your example. Finally, the object would just call run() on the variable that you are storing the state struct in, and the object itself has no need to directly know anything about the decrementing variables that are in the struct.
That's the beauty of using structs for this in fact. Each state stores only the values it needs to keep for its own tracking. The states CAN easily affect variables on the object as well in order to make it do whatever it needs, like falling, running, jumping, checking input, etc... The other cool thing is that each state is its own thing. If for example you have a state for a platformer character that is a STANDING state, and another that is for CROUCHING. Each state can check the jump button. The standing state would make it jump, but the crouching state could instead make it go down through the platform(seen in the Contra games for example).
You could also use a similar idea for different weapons. Each weapon can be a state of the actual gun object. They all check the shoot button for example, but one makes a spread shot when the button is hit, while another can check if the button is held down to do a charge shot. And the code for the two different things doesn't affect the others despite them both checking the same button.
If a character is falling for example(and you don't do space/double jump), then maybe you don't even check if the jump button is being hit, since the character can't jump while its falling.