Couch changes
This commit is contained in:
+147
-15
@@ -2,6 +2,11 @@
|
||||
#include <sdktools>
|
||||
#include <Couch>
|
||||
|
||||
// Optional: without Zombie:Reloaded nobody is tagged as a zombie.
|
||||
#undef REQUIRE_PLUGIN
|
||||
#include <zombiereloaded>
|
||||
#define REQUIRE_PLUGIN
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
@@ -18,6 +23,24 @@
|
||||
// Put in front of the name of anyone whose grenade hit the couch recently.
|
||||
#define HUD_GRENADE_TAG "<G!> "
|
||||
|
||||
// Put in front of the name of anyone who was a zombie when they last hit or pushed the couch.
|
||||
#define HUD_ZOMBIE_TAG "<Z> "
|
||||
|
||||
// First line while the couch is below full HP: "♥ 500/1000". It stays up on its own, so it is resent
|
||||
// this often; that also catches players who joined after the last send. Held for two refreshes so a
|
||||
// late timer tick can't let it lapse.
|
||||
#define HUD_HP_HEART "♥"
|
||||
#define HUD_HP_REFRESH 5.0
|
||||
#define HUD_HP_HOLD (HUD_HP_REFRESH * 2.0)
|
||||
|
||||
// While the couch is below full and not healing, the heart blinks between "♥" and "♥!" on every
|
||||
// tick of the expiry timer, so each state shows for HUD_EXPIRE_INTERVAL. Healing means a regen
|
||||
// tick raised the HP within this long; regen ticks once a second, so a healing couch never lapses
|
||||
// between ticks. A drop ends the healing state at once, so the blink starts on the hit. (The HUD
|
||||
// fonts only cover the WGL4 set, which has ♥ but no empty heart, hence the "!" suffix.)
|
||||
#define HUD_HP_HEART_ALERT "!"
|
||||
#define HUD_HP_HEAL_HOLD 1.5
|
||||
|
||||
// A user message holds 255 bytes; HudMsg's fixed fields take 34 and the string its terminator.
|
||||
// Overflowing drops the whole message.
|
||||
#define HUD_MSG_BYTES 255
|
||||
@@ -35,11 +58,19 @@ float g_fExpire[MAXPLAYERS + 1]; // GetGameTime() at which this client drops of
|
||||
float g_fLastHit[MAXPLAYERS + 1]; // Ordering key: the most recent shooter is listed first.
|
||||
float g_fGrenadeUntil[MAXPLAYERS + 1]; // GetGameTime() at which this client loses the grenade tag.
|
||||
float g_fNextUseSend[MAXPLAYERS + 1]; // GetGameTime() before which this client's +use doesn't resend.
|
||||
bool g_bZombieHit[MAXPLAYERS + 1]; // This client was a zombie at their last hit or push.
|
||||
|
||||
bool g_bZRLoaded;
|
||||
|
||||
float g_fNextExpiry; // Soonest expiry or tag drop among the names currently listed.
|
||||
float g_fLastExpiry; // Latest expiry among them; the text is held until then.
|
||||
bool g_bFlushQueued; // A send is already scheduled for the next frame.
|
||||
bool g_bHudShown; // Clients currently have text up, so an empty list must clear it.
|
||||
bool g_bHpLine; // The text starts with the HP line, so it must stay up even with nobody listed.
|
||||
float g_fNextHpRefresh; // GetGameTime() at which the HP line is resent.
|
||||
int g_iLastHp; // HP as of the last Couch_OnCouchHpChanged, or -1 if none seen.
|
||||
float g_fHealUntil; // GetGameTime() until which the couch counts as healing; the heart is solid until then.
|
||||
bool g_bHeartAlert; // The "♥!" state is the one currently up.
|
||||
|
||||
char g_sHudText[HUD_TEXT_MAX + 1];
|
||||
|
||||
@@ -65,6 +96,17 @@ public void OnPluginStart()
|
||||
AutoExecConfig(true, "CouchHud");
|
||||
}
|
||||
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
g_bZRLoaded = LibraryExists("zombiereloaded");
|
||||
}
|
||||
|
||||
public void OnLibraryAdded(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "zombiereloaded"))
|
||||
g_bZRLoaded = true;
|
||||
}
|
||||
|
||||
public void OnMapStart()
|
||||
{
|
||||
// TIMER_FLAG_NO_MAPCHANGE already killed the timer.
|
||||
@@ -73,6 +115,11 @@ public void OnMapStart()
|
||||
g_bHudShown = false;
|
||||
g_fNextExpiry = 0.0;
|
||||
g_fLastExpiry = 0.0;
|
||||
g_bHpLine = false;
|
||||
g_fNextHpRefresh = 0.0;
|
||||
g_iLastHp = -1;
|
||||
g_fHealUntil = 0.0;
|
||||
g_bHeartAlert = false;
|
||||
|
||||
for (int i = 1; i <= MAXPLAYERS; i++)
|
||||
{
|
||||
@@ -80,6 +127,7 @@ public void OnMapStart()
|
||||
g_fLastHit[i] = 0.0;
|
||||
g_fGrenadeUntil[i] = 0.0;
|
||||
g_fNextUseSend[i] = 0.0;
|
||||
g_bZombieHit[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +137,7 @@ public void OnClientPutInServer(int client)
|
||||
g_fLastHit[client] = 0.0;
|
||||
g_fGrenadeUntil[client] = 0.0;
|
||||
g_fNextUseSend[client] = 0.0;
|
||||
g_bZombieHit[client] = false;
|
||||
}
|
||||
|
||||
public void OnClientDisconnect(int client)
|
||||
@@ -99,6 +148,7 @@ public void OnClientDisconnect(int client)
|
||||
g_fLastHit[client] = 0.0;
|
||||
g_fGrenadeUntil[client] = 0.0;
|
||||
g_fNextUseSend[client] = 0.0;
|
||||
g_bZombieHit[client] = false;
|
||||
|
||||
if (bListed)
|
||||
QueueFlush();
|
||||
@@ -114,6 +164,8 @@ public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
if (StrEqual(name, "Couch"))
|
||||
ResetAll();
|
||||
else if (StrEqual(name, "zombiereloaded"))
|
||||
g_bZRLoaded = false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -129,6 +181,7 @@ public void Couch_OnCouchDamaged(int couch, int attacker, float damage)
|
||||
float fNow = GetGameTime();
|
||||
g_fLastHit[attacker] = fNow;
|
||||
g_fExpire[attacker] = MaxFloat(g_fExpire[attacker], fNow + g_cvTime.FloatValue);
|
||||
g_bZombieHit[attacker] = (g_bZRLoaded && ZR_IsClientZombie(attacker));
|
||||
|
||||
QueueFlush();
|
||||
}
|
||||
@@ -142,6 +195,9 @@ public void Couch_OnCouchInteraction(int couch, int client, CouchMethod method,
|
||||
|
||||
float fNow = GetGameTime();
|
||||
|
||||
// Every method lands here, so this covers +use and grenades, which never go through damage.
|
||||
g_bZombieHit[client] = (g_bZRLoaded && ZR_IsClientZombie(client));
|
||||
|
||||
if (method == CouchMethod_Use)
|
||||
{
|
||||
g_fLastHit[client] = fNow;
|
||||
@@ -174,6 +230,27 @@ public void Couch_OnCouchRemoved()
|
||||
ResetAll();
|
||||
}
|
||||
|
||||
// Regen ticks land here once a second; the flush decides whether the line is still wanted. A rise
|
||||
// means a regen tick (or an admin raising it) and keeps the heart solid; a drop means a zombie hit
|
||||
// (or an admin lowering it) and starts the blink at once.
|
||||
public void Couch_OnCouchHpChanged(int couch, int hp, int maxhp)
|
||||
{
|
||||
if (!g_cvEnabled.BoolValue)
|
||||
return;
|
||||
|
||||
if (g_iLastHp >= 0)
|
||||
{
|
||||
if (hp > g_iLastHp)
|
||||
g_fHealUntil = GetGameTime() + HUD_HP_HEAL_HOLD;
|
||||
else if (hp < g_iLastHp)
|
||||
g_fHealUntil = 0.0;
|
||||
}
|
||||
|
||||
g_iLastHp = hp;
|
||||
|
||||
QueueFlush();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Sending
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -203,7 +280,7 @@ void Flush()
|
||||
return;
|
||||
}
|
||||
|
||||
if (BuildHudText(g_sHudText, sizeof(g_sHudText)) == 0)
|
||||
if (!BuildHudText(g_sHudText, sizeof(g_sHudText)))
|
||||
{
|
||||
ClearAll();
|
||||
return;
|
||||
@@ -215,8 +292,20 @@ void Flush()
|
||||
void Send()
|
||||
{
|
||||
// Held until the last name lapses, so the text leaves on its own if no update ever comes.
|
||||
// Names lapsing sooner are dropped by a resend from the expiry timer.
|
||||
SetHudTextParams(0.8, 0.2, g_fLastExpiry - GetGameTime(), 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
|
||||
// Names lapsing sooner are dropped by a resend from the expiry timer. The HP line outlives
|
||||
// the names and is kept up by the refresh instead.
|
||||
float fNow = GetGameTime();
|
||||
float fHold = g_fLastExpiry - fNow;
|
||||
|
||||
if (g_bHpLine)
|
||||
{
|
||||
if (fHold < HUD_HP_HOLD)
|
||||
fHold = HUD_HP_HOLD;
|
||||
|
||||
g_fNextHpRefresh = fNow + HUD_HP_REFRESH;
|
||||
}
|
||||
|
||||
SetHudTextParams(0.8, 0.2, fHold, 255, 255, 255, 255, 0, 0.0, 0.0, 0.0);
|
||||
|
||||
for (int i = 1; i <= MaxClients; i++)
|
||||
{
|
||||
@@ -231,9 +320,10 @@ void Send()
|
||||
g_hExpireTimer = CreateTimer(HUD_EXPIRE_INTERVAL, Timer_Expire, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
||||
}
|
||||
|
||||
// Most recent shooters first. Returns how many players are listed, shown or not. Names are
|
||||
// packed until the message is full, then come off the bottom until a "+ N" line fits.
|
||||
int BuildHudText(char[] text, int maxlen)
|
||||
// HP line first when the couch is below full, then the most recent shooters. Returns whether there
|
||||
// is anything to show. Names are packed until the message is full, then come off the bottom until
|
||||
// a "+ N" line fits.
|
||||
bool BuildHudText(char[] text, int maxlen)
|
||||
{
|
||||
float fNow = GetGameTime();
|
||||
bool bTaken[MAXPLAYERS + 1];
|
||||
@@ -276,13 +366,26 @@ int BuildHudText(char[] text, int maxlen)
|
||||
g_fLastExpiry = g_fExpire[best];
|
||||
}
|
||||
|
||||
if (total == 0)
|
||||
g_bHpLine = (Couch_IsHpEnabled() && Couch_GetHp() < Couch_GetMaxHp());
|
||||
|
||||
text[0] = '\0';
|
||||
|
||||
if (g_bHpLine)
|
||||
{
|
||||
text[0] = '\0';
|
||||
return 0;
|
||||
// Solid while healing; the timer only flips it otherwise.
|
||||
if (fNow < g_fHealUntil)
|
||||
g_bHeartAlert = false;
|
||||
|
||||
char sHp[16], sMax[16];
|
||||
FormatThousands(Couch_GetHp(), sHp, sizeof(sHp));
|
||||
FormatThousands(Couch_GetMaxHp(), sMax, sizeof(sMax));
|
||||
FormatEx(text, maxlen, HUD_HP_HEART ... "%s %s/%s", g_bHeartAlert ? HUD_HP_HEART_ALERT : "", sHp, sMax);
|
||||
}
|
||||
|
||||
strcopy(text, maxlen, "Shooting couch:");
|
||||
if (total == 0)
|
||||
return g_bHpLine;
|
||||
|
||||
StrCat(text, maxlen, g_bHpLine ? "\nShooting couch:" : "Shooting couch:");
|
||||
|
||||
// rowStart[n] is the text length before row n, so a row can be cut back off.
|
||||
int rowStart[MAXPLAYERS + 1];
|
||||
@@ -294,8 +397,8 @@ int BuildHudText(char[] text, int maxlen)
|
||||
GetClientName(order[rows], sName, sizeof(sName));
|
||||
TruncateUtf8(sName, HUD_NAME_BYTES);
|
||||
|
||||
char sLine[HUD_NAME_BYTES + sizeof(HUD_GRENADE_TAG) + 1];
|
||||
FormatEx(sLine, sizeof(sLine), "\n%s%s", (g_fGrenadeUntil[order[rows]] > fNow) ? HUD_GRENADE_TAG : "", sName);
|
||||
char sLine[HUD_NAME_BYTES + sizeof(HUD_ZOMBIE_TAG) + sizeof(HUD_GRENADE_TAG) + 1];
|
||||
FormatEx(sLine, sizeof(sLine), "\n%s%s%s", g_bZombieHit[order[rows]] ? HUD_ZOMBIE_TAG : "", (g_fGrenadeUntil[order[rows]] > fNow) ? HUD_GRENADE_TAG : "", sName);
|
||||
|
||||
int len = strlen(text);
|
||||
if (len + strlen(sLine) >= maxlen)
|
||||
@@ -321,7 +424,7 @@ int BuildHudText(char[] text, int maxlen)
|
||||
text[rowStart[rows]] = '\0';
|
||||
}
|
||||
|
||||
return total;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cuts to at most maxBytes without leaving a partial multi-byte character on the end.
|
||||
@@ -347,8 +450,30 @@ public Action Timer_Expire(Handle timer)
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
||||
// Only resend when a name has lapsed.
|
||||
if (GetGameTime() >= g_fNextExpiry)
|
||||
// Only resend when a name has lapsed, the HP line is due its refresh, or the heart has to flip.
|
||||
// With nobody listed there is no expiry (0.0), so the guard stops the HP line being resent
|
||||
// every tick.
|
||||
float fNow = GetGameTime();
|
||||
bool bExpiry = (g_fNextExpiry > 0.0 && fNow >= g_fNextExpiry);
|
||||
bool bRefresh = (g_bHpLine && fNow >= g_fNextHpRefresh);
|
||||
bool bHeart = false;
|
||||
|
||||
if (g_bHpLine)
|
||||
{
|
||||
if (fNow >= g_fHealUntil)
|
||||
{
|
||||
// Not healing: alternate every tick.
|
||||
g_bHeartAlert = !g_bHeartAlert;
|
||||
bHeart = true;
|
||||
}
|
||||
else if (g_bHeartAlert)
|
||||
{
|
||||
// Healing started while "♥!" was up; put the solid heart back.
|
||||
bHeart = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bExpiry || bRefresh || bHeart)
|
||||
Flush();
|
||||
|
||||
return Plugin_Continue;
|
||||
@@ -378,7 +503,14 @@ void ResetAll()
|
||||
g_fLastHit[i] = 0.0;
|
||||
g_fGrenadeUntil[i] = 0.0;
|
||||
g_fNextUseSend[i] = 0.0;
|
||||
g_bZombieHit[i] = false;
|
||||
}
|
||||
|
||||
g_bHpLine = false;
|
||||
g_fNextHpRefresh = 0.0;
|
||||
g_iLastHp = -1;
|
||||
g_fHealUntil = 0.0;
|
||||
g_bHeartAlert = false;
|
||||
|
||||
ClearAll();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user