I feel like something similar may have been posted before or something, but here's a thing I set up for myself.
As opposed to using CreateTimer() to run code in the future, there are various scenarios where you could just use a single float to do things such as, preventing code from executing until a certain amount of time has passed.
For example, all of the "m_flNext..." variables Source uses work this way. TF2 has m_flNextPrimaryAttack. This is set to a time in seconds, which is compared to the GameTime, which can be retrieved in SM via GetGameTime().
m_flNextPrimaryAttack prevents a player from attacking with +attack until the GameTime has reached or passed whatever time is stored in m_flNextPrimaryAttack.
If m_flNextPrimaryAttack is 60.0, the GameTime must reach 60 seconds before the player can attack. If at this 60 second mark you then set m_fl to 95.0, the player has to wait another 35 seconds before attacking again.
One might use that netprop by setting its value to GetGameTime()+flTime.
It's a fairly simple comparison, and it has a lot of applications. So I made a simple enum to easily track these.
For our version, we'll use GetEngineTime(). GetGameTime() resets to 0.0 when the map changes, whereas the Engine Time will keep increasing forever from the point the server started up. (Seriously, the previous sentence isn't documented anywhere where you'll find it soon).
Stocks:
Spoiler
PHP Code:
#define TF_MAX_PLAYERS 34 // Replace this with MAXPLAYERS+1 if not TF2
// These two enums have to be placed before all code that uses them. #define USE_NEXTTIME enum e_flNext // These are single stand-alone "timers" { e_flNextVoice = 0, e_flNextBossTaunt, e_flNextAllowBossSuicide, e_flNextAllowOtherSpawnTele, e_flNextTrample, e_flNextMissile }
#define USE_NEXTTIME2 enum e_flNext2 // Give an individual timer for every player { e_flNextHeavySound = 0, e_flNextFlashTrigger, e_flNextBrassShieldSfx, e_flNextExplosiveShotgunSfx }
#if defined USE_NEXTTIME static Float:g_flNext[e_flNext]; #endif
#if defined USE_NEXTTIME2 static Float:g_flNext2[e_flNext2][TF_MAX_PLAYERS]; #endif
const Float:UseNextTime_Disabled = -1.0; const Float:UseNextTime_MinimumTime = 0.0; // Instant, and only happens when the server first boots up, but valid.
/* If next time occurs, we also add time on for when it is next allowed. */ stock bool:DoNextTime(iIndex, Float:flThenAdd) { if (IsNextTime(iIndex)) { SetNextTime(iIndex, flThenAdd); return true; } return false; }
/* Similar to DoNextTime(), but disables the timer after running instead of setting it to run again in the future. */ stock bool:EndNextTime(iIndex) { if (CanNextTime(iIndex) && IsNextTime(iIndex)) // Removing the IsNextTime() check essentially turns this into | if (bool) { bool = false; } { StopNextTime(iIndex); return true; } return false; }
/* Can function like DoNextTime(), but can also be canceled externally. */ stock bool:TryNextTime(iIndex, Float:flThenAdd = 0.0) { if (CanNextTime(iIndex) && IsNextTime(iIndex)) { SetNextTime(iIndex, flThenAdd); return true; } return false; }
/* If next time occurs, we also add time on for when it is next allowed. */ stock bool:DoNextTime2(iClient, iIndex, Float:flThenAdd) { if (IsNextTime2(iClient, iIndex)) { SetNextTime2(iClient, iIndex, flThenAdd); return true; } return false; }
/* Similar to DoNextTime2(), but disables the timer after running instead of setting it to run again in the future. */ stock bool:EndNextTime2(iClient, iIndex) { if (CanNextTime2(iClient, iIndex) && IsNextTime2(iClient, iIndex)) { StopNextTime2(iClient, iIndex); return true; } return false; }
/* Can function like DoNextTime2(), but can also be canceled externally. */ stock bool:TryNextTime2(iClient, iIndex, Float:flThenAdd = 0.0) { if (CanNextTime2(iClient, iIndex) && IsNextTime2(iClient, iIndex)) { SetNextTime2(iClient, iIndex, flThenAdd); return true; } return false; }
And here's an example of where I use it.
Note: This code is actually from Advanced Weaponiser which I imported to another plugin.
Spoiler
PHP Code:
public Action:Attribute_1079_OnTakeDamage(iVictim, &iAttacker, iSlot, &Float:fDamage, &iDamageType, Float:fForce[3], Float:fForcePos[3]) { if (IfDoNextTime2(iAttacker, m_flNextExplosiveShotgunSfx, 0.2)) // This sound can only play every 0.2 seconds, even if I use a minigun and this function is called faster than that. { EmitSoundFromOrigin(g_szSoundExplosionBox[GetRandomInt(0, sizeof(g_szSoundExplosionBox)-1)], fForcePos); }
iDamageType |= DMG_BLAST;
NormalizeVector(fForce, fForce); if (fForce[2] < 0.2) fForce[2] = 0.2;
new Float:flResist = (iSlot == 0) ? 1.0+(1.0/g_flKnockbackResist) : g_flKnockbackResist;
new Float:fScale = Float:clamp(flTrueDamage*flResist, 100.0, 600.0); ScaleVector(fForce, fScale); if (fForce[2] < 320.0 && fDamage >= 10.0) fForce[2] = 320.0;
DoFlash(iClient, Float:flMinNextTime, Float:flMaxNextTime) { if (IsNextTime2(iClient, m_flNextFlashTrigger)) // I didn't use IfDoNextTime2, because it's slightly more efficient to only call GetRandomFloat when we need to, instead of every time we do this check. { Client_ScreenFade(iClient, 400, (FFADE_PURGE|FFADE_IN), 150, 255, 255, 255, 192);
SetNextTime2(iClient, m_flNextFlashTrigger, GetRandomFloat(flMinNextTime, flMaxNextTime)); // The next time this function can be fired for the client is set here, you could set it from 0.0 to 30.0 seconds to 99999.0 if you wish. } }
And lastly:
Spoiler
PHP Code:
public Action:Timer_Afkcheck(Handle:hTimer, any:UserId) { new iClient = GetClientOfUserId(UserId); if (iClient && IsClientInGame(iClient)) { if (g_bAfk[iClient]) { OnIdle(); } else { OnActivelyParticipate(); } } }
OnActivelyParticipate() { if (IsNextTime2(iClient, m_flNextShortSongStart)) { ClientMusicStart(iClient); // Every 8-16 seconds, a new song plays
OnIdle() { if (IsNextTime2(iClient, m_flPlayShortSong, 2.0)) // This checks whatever time is here, plus an extra 2 seconds. { // For example, if NextTime is 8.0 seconds, then this can fire in 10.0 seconds ClientMusicStop(iClient); // And basically this would fade out the music 2 seconds after the time a new song should've appeared, if the person is idling. //SetNextTime2(iClient, m_flPlayShortSong, 0.0, true); // You don't need to do this. If the if() statement above already passed, then it surely will again until a new time is set anyway. } }
All you need to do to add a new / separate "timer" is add another entry into one of the enums above. Simple.
The m_flNext naming convention doesn't matter and isn't meant to be meaningful.
I just did that cause there's no other place I'm going to name variables like that and it gives a similar feel to the netprops in Source.
You can also use this in things like OnGameFrame() or OnThink to have times faster than 100 milliseconds (0.1 seconds), depending on what you want to do. Of course then it's still restricted to however fast OnGameFrame/OnThinkHooks fire.
As opposed to using CreateTimer() to run code in the future, there are various scenarios where you could just use a single float to do things such as, preventing code from executing until a certain amount of time has passed.
For example, all of the "m_flNext..." variables Source uses work this way. TF2 has m_flNextPrimaryAttack. This is set to a time in seconds, which is compared to the GameTime, which can be retrieved in SM via GetGameTime().
This seems like a lot of work for what is essentially OnGameFrame on the game server side.
__________________
Not currently working on SourceMod plugin development.
As for what Powerlord said... wot? I don't know how you're getting OnGameFrame out of that. You only need to set the next attack once, rather than multiple frames, depending on what you're doing.
For example, VSH sets your next attack to 2 seconds after you get a backstab, instead of the normal 0.6s fire rate of the knife.
And when you climb walls as a sniper it does more or less the same thing... but the plugin doesn't use OnGameFrame at all. (It does use a think hook for something else now though).
As for what Powerlord said... wot? I don't know how you're getting OnGameFrame out of that. You only need to set the next attack once, rather than multiple frames, depending on what you're doing.
For example, VSH sets your next attack to 2 seconds after you get a backstab, instead of the normal 0.6s fire rate of the knife.
And when you climb walls as a sniper it does more or less the same thing... but the plugin doesn't use OnGameFrame at all. (It does use a think hook for something else now though).
Comparatively a couple of bools shouldn't be bad. Unless you have like hundreds of bools. I try to use switch cases... according to Psychonic or Asherkin they don't calculate every comparison for every switch or something, just jump to whichever case.
Comparatively a couple of bools shouldn't be bad. Unless you have like hundreds of bools. I try to use switch cases... according to Psychonic or Asherkin they don't calculate every comparison for every switch or something, just jump to whichever case.
It's pretty common for a compiler to generate a jump table for a switch instead of treating it like an if / else if / else block.
__________________
Not currently working on SourceMod plugin development.