Author Topic: Weapon state system playing animation when it's not supposed to [SOLVED]  (Read 985 times)

I have a weapon with multiple ammo modes controlled by pref, one with infinite ammo ($Pref::Server::TTAmmo = 2) and one with DOOM style ammo ($Pref::Server::TTAmmo = 3), where you can keep shooting until your reserve is empty.

My problem occurs when starting in TTAmmo 3 with no reserve ammo left. The weapon cycles between "Empty", "LoadCheckA", and "LoadCheckB" in an infinite loop for as long as there is no ammo, which is working as intended. But if I change the pref to TTAmmo 2 while the weapon is in that loop, "LoadCheckB" is meant to go to "Ready" since ammo is no longer necessary. It does this, but it also plays the pistol roostering animation (stateSequence = "Fire") and pistolClickSound when it's not supposed to, as it goes straight to "Ready" and not to any states with those animations.

I've done debugging loops to just print out the image's state every 9 ms or so, and it goes from Empty -> LoadCheckA -> LoadCheckB -> Ready
I think it has something to do with the "Empty" transitions, because changing stateTransitionOnAmmo in "Empty" to a state with a different sound/animation such as "Activate" will play that state's sound/animation instead. But that makes no sense, since the image should never be in the "Empty" state with image ammo equal to 1 in the first place.

All states of the weapon:
Code: [Select]
// Initial start up state
stateName[0]                       = "Activate";
stateTimeoutValue[0]            = 0.15;
stateSequence[0]                 = "Activate";
stateTransitionOnTimeout[0]     = "LoadCheckA";
stateSound[0] = weaponSwitchSound;

stateName[1]                    = "Ready";
stateTransitionOnNoAmmo[1] = "AmmoCheck";
stateTransitionOnTriggerDown[1] = "FireCheckA";
stateAllowImageChange[1]        = true;
stateScript[1]                  = "onReady";
stateSequence[1]   = "ready";

stateName[2]                    = "FireCheckA";
stateScript[2]                    = "onFireCheck";
stateTimeoutValue[2]            = 0.01;
stateTransitionOnTimeout[2]     = "FireCheckB";

stateName[3]                    = "FireCheckB";
stateTransitionOnAmmo[3]        = "Fire";
stateTransitionOnNoAmmo[3]      = "Empty";

stateName[4]                    = "Fire";
stateTransitionOnTimeout[4]     = "Smoke";
stateTimeoutValue[4]            = 0.05;
stateFire[4]                    = true;
stateAllowImageChange[4]        = false;
stateEjectShell[4]              = true;
stateScript[4]                  = "onFire";
stateSequence[4] = "Fire";
stateWaitForTimeout[4] = true;
stateEmitter[4] = gunFlashEmitter;
stateEmitterTime[4] = 0.05;
stateEmitterNode[4] = "muzzleNode";
stateSound[4] = PistolfireSound;

stateName[5] = "Smoke";
stateEmitter[5] = gunSmokeEmitter;
stateEmitterTime[5] = 0.2;
stateEmitterNode[5] = "muzzleNode";
stateTransitionOnTriggerUp[5] = "Wait";

stateName[6] = "Wait";
stateTimeoutValue[6] = 0.001;
stateScript[6]                  = "onBounce";
stateTransitionOnTimeout[6] = "LoadCheckA";
stateSound[6] = pistolClickSound;

//Torque switches states instantly if there is an ammo/noammo state, regardless of stateWaitForTimeout
stateName[7] = "LoadCheckA";
stateScript[7] = "onLoadCheck";
stateTimeoutValue[7] = 0.01;
stateTransitionOnTimeout[7] = "LoadCheckB";

stateName[8] = "LoadCheckB";
stateTransitionOnAmmo[8] = "Ready";
stateTransitionOnNoAmmo[8] = "Empty";

stateName[9] = "ReloadWait";
stateTimeoutValue[9] = 0.3;
stateScript[9] = "onReloadWait";
stateTransitionOnTimeout[9] = "ReloadStart";
stateWaitForTimeout[9] = true;

stateName[10] = "ReloadStart";
stateTimeoutValue[10] = 0.3;
stateScript[10] = "onReloadStart";
stateTransitionOnTimeout[10] = "Reloaded";
stateWaitForTimeout[10] = true;

stateName[11] = "Reloaded";
stateTimeoutValue[11] = 0.4;
stateScript[11] = "onReloaded";
stateTransitionOnTimeout[11] = "Ready";
stateSequence[11] = "Fire";
stateSound[11] = pistolClickSound;

stateName[12] = "Empty";
stateScript[12] = "onEmpty";
stateTransitionOnAmmo[12] = "ReloadWait";
stateTimeoutValue[12] = 0.01;
stateTransitionOnTimeout[12] = "LoadCheckA";

stateName[13] = "AmmoCheck";
stateScript[13] = "onAmmoCheck";
stateTransitionOnAmmo[13] = "Ready";
stateTimeoutValue[13] = 0.01;
stateTransitionOnTimeout[13] = "ReloadStart";

Here are the relevant state functions:
Code: [Select]
function PistolImage::onEmpty(%this, %obj, %slot)
{
       // do nothing for now
}

function PistolImage::onLoadCheck(%this,%obj,%slot)
{
if(($Pref::Server::TTAmmo != 2 && $Pref::Server::TTAmmo != 3 && %obj.toolAmmo[%obj.currTool] <= 0 && %this.item.maxAmmo > 0
|| $Pref::Server::TTAmmo == 3 && %obj.client.quantity[%this.item.TTammoType] <= 0) && %obj.getState() !$= "Dead")
{
%obj.setImageAmmo(%slot,0);
}
else
{
%obj.setImageAmmo(%slot,1);
}
}
« Last Edit: June 11, 2018, 07:10:19 PM by TheViewer »

I still don't exactly understand why the state system was acting the way it was, but I have found that using setImageLoaded to trigger the desired state changes somehow fixed the problem. Here is my revised code:
Code: [Select]
// Initial start up state
stateName[0]                    = "Activate";
stateTimeoutValue[0]            = 0.15;
stateSequence[0]                = "Activate";
stateTransitionOnTimeout[0]     = "LoadCheckA";
stateSound[0]                   = weaponSwitchSound;

stateName[1]                    = "Ready";
stateTransitionOnNoAmmo[1]      = "ReloadStart";
stateTransitionOnTriggerDown[1] = "FireCheckA";
stateAllowImageChange[1]        = true;
stateScript[1]                  = "onReady";
stateSequence[1]                = "ready";

stateName[2]                    = "FireCheckA";
stateScript[2]                  = "onFireCheck";
stateTimeoutValue[2]            = 0.01;
stateTransitionOnTimeout[2]     = "FireCheckB";

stateName[3]                    = "FireCheckB";
stateTransitionOnNotLoaded[3]   = "Fire";
stateTransitionOnLoaded[3]      = "Empty";

stateName[4]                    = "Fire";
stateTransitionOnTimeout[4]     = "Smoke";
stateTimeoutValue[4]            = 0.05;
stateFire[4]                    = true;
stateAllowImageChange[4]        = false;
stateEjectShell[4]              = true;
stateScript[4]                  = "onFire";
stateSequence[4]                = "Fire";
stateWaitForTimeout[4]          = true;
stateEmitter[4]                 = gunFlashEmitter;
stateEmitterTime[4]             = 0.05;
stateEmitterNode[4]             = "muzzleNode";
stateSound[4]                   = PistolfireSound;

stateName[5]                    = "Smoke";
stateEmitter[5]                 = gunSmokeEmitter;
stateEmitterTime[5]             = 0.2;
stateEmitterNode[5]             = "muzzleNode";
stateTransitionOnTriggerUp[5]   = "Wait";

stateName[6]                    = "Wait";
stateTimeoutValue[6]            = 0.001;
stateScript[6]                  = "onBounce";
stateTransitionOnTimeout[6]     = "LoadCheckA";
stateSound[6]                   = pistolClickSound;

//Torque switches states instantly if there is an ammo/noammo state, regardless of stateWaitForTimeout
stateName[7]                    = "LoadCheckA";
stateScript[7]                  = "onLoadCheck";
stateTimeoutValue[7]            = 0.01;
stateTransitionOnTimeout[7]     = "LoadCheckB";

stateName[8]                    = "LoadCheckB";
stateTransitionOnAmmo[8]        = "Ready";
stateTransitionOnNoAmmo[8]      = "Empty";

stateName[9]                    = "ReloadWait";
stateTimeoutValue[9]            = 0.3;
stateScript[9]                  = "onReloadWait";
stateTransitionOnTimeout[9]     = "ReloadStart";
stateWaitForTimeout[9]          = true;

stateName[10]                   = "ReloadStart";
stateTimeoutValue[10]           = 0.3;
stateScript[10]                 = "onReloadStart";
stateTransitionOnTimeout[10]    = "Reloaded";
stateWaitForTimeout[10]         = true;

stateName[11]                   = "Reloaded";
stateTimeoutValue[11]           = 0.4;
stateScript[11]                 = "onReloaded";
stateTransitionOnTimeout[11]    = "Ready";
stateSequence[11]               = "Fire";
stateSound[11]                  = pistolClickSound;

stateName[12]                   = "Empty";
stateTransitionOnNotLoaded[12]  = "ReloadWait";
stateTransitionOnLoaded[12]     = "LoadCheckA";
Code: [Select]
function PistolImage::onLoadCheck(%this,%obj,%slot)
{
   TT_displayAmmo(%this, %obj, 4);
if(($Pref::Server::TTAmmo != 2 && $Pref::Server::TTAmmo != 3 && %obj.toolAmmo[%obj.currTool] <= 0 && %this.item.maxAmmo > 0
|| $Pref::Server::TTAmmo == 3 && %obj.client.quantity[%this.item.TTammoType] <= 0) && %obj.getState() !$= "Dead")
{
%obj.setImageAmmo(%slot,0);
}
else
{
%obj.setImageAmmo(%slot,1);
}

if(%obj.client.quantity[%this.item.TTammoType] <= 0 && $Pref::Server::TTAmmo != 1)
{
%obj.setImageLoaded(%slot, 1);
}
else
{
%obj.setImageLoaded(%slot, 0);
}
}