Delaying code executions
We have implemented a function called 'Wait' for AngelScript, so executions can be delayed like in Postal3Script, however, you have to be very careful with how you write the logic of your script.
Let's say, you are creating an entity in map called 'A', you called an input called "turnon" on it, and you wish to turn it off, after 7 seconds or so:
class Tut : IPostal3Script
{
void SpawnSmoke(Vector pos)
{
CBaseEntity@ smoke = FindEntByName("reinforcement_smoke");
if (smoke == null)
{
CBaseEntity@ ent = CreateEnt("env_smokestack");
if ( ent != null )
{
SetKeyValue( ent, "angles", "0" );
SetKeyValue( ent, "BaseSpread", "60" );
SetKeyValue( ent, "EndSize", "115" );
SetKeyValue( ent, "InitialState", "1" );
SetKeyValue( ent, "JetLength", "150" );
SetKeyValue( ent, "Rate", "20" );
SetKeyValue( ent, "renderamt", "255" );
SetKeyValue( ent, "rendercolor", "45 45 45" );
SetKeyValue( ent, "roll", "15" );
SetKeyValue( ent, "SmokeMaterial", "particle/SmokeStack.vmt" );
SetKeyValue( ent, "Speed", "50" );
SetKeyValue( ent, "SpreadSpeed", "60" );
SetKeyValue( ent, "StartSize", "100" );
SetKeyValue( ent, "targetname", "reinforcement_smoke" );
SetKeyValue( ent, "twist", "7" );
SetKeyValue( ent, "WindAngle", "0" );
SetKeyValue( ent, "WindSpeed", "0" );
Spawn(ent);
ent.Activate();
SetAbsOrigin( ent, pos );
Wait(7.0);
FireInput(ent, "turnoff");
}
}
else
{
FireInput(smoke, "turnon");
Wait(7.0);
FireInput(smoke, "turnoff");
}
}
}
During those 7 seconds the Player might go through a transit zone, after 7 seconds if AS tries to access the ent
or smoke
object,
AngelScript will crash, because an entity created/or accessed in 'A' map doesn't exists in 'B' map!
Here's one way of solving the crash involved with the above code:
class Tut : IPostal3Script
{
void SpawnSmoke(Vector pos)
{
CBaseEntity@ smoke = FindEntByName("reinforcement_smoke");
if (smoke == null)
{
CBaseEntity@ ent = CreateEnt("env_smokestack");
if ( ent != null )
{
SetKeyValue( ent, "angles", "0" );
SetKeyValue( ent, "BaseSpread", "60" );
SetKeyValue( ent, "EndSize", "115" );
SetKeyValue( ent, "InitialState", "1" );
SetKeyValue( ent, "JetLength", "150" );
SetKeyValue( ent, "Rate", "20" );
SetKeyValue( ent, "renderamt", "255" );
SetKeyValue( ent, "rendercolor", "45 45 45" );
SetKeyValue( ent, "roll", "15" );
SetKeyValue( ent, "SmokeMaterial", "particle/SmokeStack.vmt" );
SetKeyValue( ent, "Speed", "50" );
SetKeyValue( ent, "SpreadSpeed", "60" );
SetKeyValue( ent, "StartSize", "100" );
SetKeyValue( ent, "targetname", "reinforcement_smoke" );
SetKeyValue( ent, "twist", "7" );
SetKeyValue( ent, "WindAngle", "0" );
SetKeyValue( ent, "WindSpeed", "0" );
Spawn(ent);
ent.Activate();
SetAbsOrigin( ent, pos );
// Add a wait handling here.
Wait(7.0, WH_FREEZE_MAPNAME);
// And if we want to be 100% sure, let's remake the handle
@ent = FindEntByName("reinforcement_smoke");
FireInput(ent, "turnoff");
}
}
else
{
FireInput(smoke, "turnon");
// Add a wait handling here.
Wait(7.0, WH_FREEZE_MAPNAME);
// And if we want to be 100% sure, let's remake the handle
@smoke = FindEntByName("reinforcement_smoke");
FireInput(smoke, "turnoff");
}
}
}
The only exception is the Player, it'll always be a valid entity after transition.
There are several ways handling this:
Wait(7.0, WH_ABORT_MAPNAME);
-- This will cause the execution to abort if the map name doesn't match after waiting.Wait(7.0, WH_ABORT_LEVELCHANGE);
-- The WH_ABORT_LEVELCHANGE one will cause the execution to abort on any kind of level change.
Or...
Wait(7.0, WH_FREEZE_MAPNAME);
-- This will cause the execution to freeze until the map name matches it originally executed from.
When you work with entities, just keep this in mind that after a level change some entities in the code execution might become invalid, so it's fine to just simply abort them to avoid any crashes.