From e64e72d15588f366accd04539dca34f96b40a4d3 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Sat, 30 Apr 2022 20:18:56 -0700 Subject: [PATCH] env_bubbles: Implementation. Should be complete. NSIO: Add methods ReadBool(), SaveBool(), and PREDICTED_BOOL macro. --- src/client/entities.qc | 3 + src/gs-entbase/server/ambient_generic.qc | 274 ----------------- src/gs-entbase/shared.src | 1 + src/gs-entbase/shared/NSIO.h | 2 + src/gs-entbase/shared/NSIO.qc | 13 + src/gs-entbase/shared/env_bubbles.qc | 361 +++++++++++++++++++++++ src/shared/defs.h | 1 + src/shared/entities.h | 1 + 8 files changed, 382 insertions(+), 274 deletions(-) delete mode 100644 src/gs-entbase/server/ambient_generic.qc create mode 100644 src/gs-entbase/shared/env_bubbles.qc diff --git a/src/client/entities.qc b/src/client/entities.qc index 76dc057f..8cebe9fb 100644 --- a/src/client/entities.qc +++ b/src/client/entities.qc @@ -132,6 +132,9 @@ Entity_EntityUpdate(float type, float new) case ENT_PROPROPE: prop_rope_readentity(new); break; + case ENT_BUBBLES: + env_bubbles_ReadEntity(new); + break; default: //error(sprintf("Unknown entity type update received. (%d)\n", t)); } diff --git a/src/gs-entbase/server/ambient_generic.qc b/src/gs-entbase/server/ambient_generic.qc deleted file mode 100644 index 44d7622d..00000000 --- a/src/gs-entbase/server/ambient_generic.qc +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2016-2020 Marco Cawthorne - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/*QUAKED ambient_generic (1 1 1) (-8 -8 -8) (8 8 8) AS_ARADIUS AS_SRADIUS AS_MRADIUS AS_LRADIUS AS_SILENT AS_NOTTOGGLED -Plays a sound sample of whatever format the engine is configured to support. - --------- KEYS -------- -"targetname" : Name -"target" : Target when triggered. -"killtarget" : Target to kill when triggered. -"message" : Sound file to play -"volume" : Playback volume from 0.0 to 1.0 -"pitch" : Playback pitch from 0.0 to 2.0 - --------- SPAWNFLAGS -------- -AS_ARADIUS : Plays the sound everywhere. Heard by everyone. -AS_SRADIUS : Small playback radius. -AS_MRADIUS : Medium playback radius. -AS_LRADIUS : Large playback radius. -AS_SILENT : Start silent, trigger to make it play! -AS_NOTTOGGLED : Don't toggle playback. When triggered, only play the sample once. - --------- NOTES -------- -If you want it to loop, you have to give the file itself a loop flag. - --------- TRIVIA -------- -This entity was introduced in Half-Life (1998). -*/ - -enumflags -{ - AS_ARADIUS, - AS_SRADIUS, - AS_MRADIUS, - AS_LRADIUS, - AS_SILENT, - AS_NOTTOGGLED -}; - -enumflags -{ - AG_INFO, - AG_SAMPLE -}; - -class ambient_generic:NSPointTrigger -{ - string m_strActivePath; - string m_strSoundPath; - float m_flVolume; - float m_flRadius; - float m_flPitch; - int m_iToggleSwitch; - int m_iLoop; - - void(void) ambient_generic; - - /* overrides */ - virtual void(float) Save; - virtual void(string, string) Restore; - virtual float(entity, float) SendEntity; - virtual void(string, string) SpawnKey; - virtual void(void) Respawn; - - virtual void(entity, int) UseNormal; - virtual void(entity, int) UseLoop; -}; - -void -ambient_generic::Save(float handle) -{ - SaveString(handle, "activepath", m_strActivePath); - SaveString(handle, "soundpath", m_strSoundPath); - SaveFloat(handle, "volume", m_flVolume); - SaveFloat(handle, "radius", m_flRadius); - SaveFloat(handle, "pitch", m_flPitch); - SaveInt(handle, "toggleswitch", m_iToggleSwitch); - SaveInt(handle, "loop", m_iLoop); - super::Save(handle); -} - -void -ambient_generic::Restore(string strKey, string strValue) -{ - switch (strKey) { - case "loop": - m_iLoop = ReadInt(strValue); - break; - case "toggleswitch": - m_iToggleSwitch = ReadInt(strValue); - break; - case "pitch": - m_flPitch = ReadFloat(strValue); - break; - case "radius": - m_flRadius = ReadFloat(strValue); - break; - case "volume": - m_flVolume = ReadFloat(strValue); - break; - case "soundpath": - m_strSoundPath = ReadString(strValue); - break; - case "activepath": - m_strActivePath = ReadString(strValue); - break; - default: - super::Restore(strKey, strValue); - } -} - -float -ambient_generic::SendEntity(entity ePEnt, float fChanged) -{ - if (clienttype(ePEnt) != CLIENTTYPE_REAL) { - return (0); - } - - /* only override when we're doing the toggle guff */ - if (m_iLoop == FALSE) { - return (0); - } - - WriteByte(MSG_ENTITY, ENT_AMBIENTSOUND); - WriteFloat(MSG_ENTITY, fChanged); - - if (fChanged & AG_INFO) { - WriteCoord(MSG_ENTITY, origin[0]); - WriteCoord(MSG_ENTITY, origin[1]); - WriteCoord(MSG_ENTITY, origin[2]); - WriteFloat(MSG_ENTITY, m_flVolume); - WriteByte(MSG_ENTITY, m_flRadius); - WriteFloat(MSG_ENTITY, m_flPitch); - } - - /* TODO: work with soundindices? */ - if (fChanged & AG_SAMPLE) { - WriteString(MSG_ENTITY, m_strActivePath); - } - - return (1); -} - -void -ambient_generic::UseNormal(entity act, int state) -{ - sound(this, CHAN_VOICE, m_strActivePath, m_flVolume, m_flRadius, m_flPitch); - dprint(sprintf("^2ambient_generic::^3UseNormal^7: %s plays `%s`\n", - target, m_strActivePath)); -} - -void -ambient_generic::UseLoop(entity act, int state) -{ - if (m_iToggleSwitch == TRUE) { - dprint(sprintf("^2ambient_generic::^3UseLoop^7: %s stops `%s`\n", - target, m_strActivePath)); - m_strActivePath = "common/null.wav"; - } else { - m_strActivePath = m_strSoundPath; - dprint(sprintf("^2ambient_generic::^3UseLoop^7: %s plays `%s`\n", - target, m_strActivePath)); - } - - m_iToggleSwitch = 1 - m_iToggleSwitch; - SetSendFlags(AG_SAMPLE); -} - -void -ambient_generic::Respawn(void) -{ - m_strActivePath = m_strSoundPath; - - if (HasSpawnFlags(AS_NOTTOGGLED)) { - Trigger = UseNormal; - m_iLoop = FALSE; - } else { - m_iLoop = TRUE; - - /* set our sample up */ - if (HasSpawnFlags(AS_SILENT)) { - m_iToggleSwitch = FALSE; - m_strActivePath = "common/null.wav"; - } else { - m_iToggleSwitch = TRUE; - m_strActivePath = m_strSoundPath; - } - - Trigger = UseLoop; - SetSendFlags(AG_SAMPLE); - } -} - -void -ambient_generic::SpawnKey(string strKey, string strValue) -{ - switch (strKey) { - case "message": - m_strSoundPath = strValue; - m_strActivePath = m_strSoundPath; - precache_sound(m_strSoundPath); - message = __NULL__; - break; - case "health": - m_flVolume = stof(strValue) * 0.1f; - health = __NULL__; - break; - case "volume": - m_flVolume = stof(strValue); - break; - case "pitch": - m_flPitch = stof(strValue); - break; - /* TODO: currently unimplemented */ - case "preset": - case "volstart": - case "fadein": - case "fadeout": - case "pitchstart": - case "spinup": - case "spindown": - case "lfotype": - case "lforate": - case "lfomodpitch": - case "lfomodvol": - case "cspinup": - break; - default: - super::SpawnKey(strKey, strValue); - break; - } -} - -void -ambient_generic::ambient_generic(void) -{ - super::NSPointTrigger(); - - if (!m_strSoundPath) { - objerror("ambient_generic: No sound file specified!"); - } - - if (!m_flVolume) { - m_flVolume = 1.0f; - } - - // There can be only one - if (HasSpawnFlags(AS_ARADIUS)) { - m_flRadius = ATTN_NONE; - } else if (HasSpawnFlags(AS_SRADIUS)) { - m_flRadius = ATTN_IDLE; - } else if (HasSpawnFlags(AS_MRADIUS)) { - m_flRadius = ATTN_STATIC; - } else if (HasSpawnFlags(AS_LRADIUS)) { - m_flRadius = ATTN_NORM; - } else { - m_flRadius = ATTN_STATIC; - } - - pvsflags = PVSF_USEPHS; -} diff --git a/src/gs-entbase/shared.src b/src/gs-entbase/shared.src index 2f5c7eed..1bf76755 100644 --- a/src/gs-entbase/shared.src +++ b/src/gs-entbase/shared.src @@ -19,6 +19,7 @@ shared/ambient_generic.qc shared/decals.qc shared/spraylogo.qc shared/func_friction.qc +shared/env_bubbles.qc shared/env_projectedtexture.qc shared/env_fog_controller.qc shared/light_dynamic.qc diff --git a/src/gs-entbase/shared/NSIO.h b/src/gs-entbase/shared/NSIO.h index 96e0321d..bf969229 100644 --- a/src/gs-entbase/shared/NSIO.h +++ b/src/gs-entbase/shared/NSIO.h @@ -54,10 +54,12 @@ class NSIO nonvirtual void(float, string, int) SaveInt; nonvirtual void(float, string, string) SaveString; nonvirtual void(float, string, vector) SaveVector; + nonvirtual void(float, string, bool) SaveBool; nonvirtual float(string) ReadFloat; nonvirtual int(string) ReadInt; nonvirtual string(string) ReadString; nonvirtual vector(string) ReadVector; + nonvirtual bool(string) ReadBool; /* Handle incoming entities input messaging */ virtual void(entity, string, string) Input; diff --git a/src/gs-entbase/shared/NSIO.qc b/src/gs-entbase/shared/NSIO.qc index 7daa8545..22f10762 100644 --- a/src/gs-entbase/shared/NSIO.qc +++ b/src/gs-entbase/shared/NSIO.qc @@ -159,6 +159,12 @@ NSIO::Respawn(void) // Respawn code goes here... } +void +NSIO::SaveBool(float handle, string key, bool value) +{ + if (value) + fputs(handle, sprintf("%S \"%f\"\n", key, value)); +} void NSIO::SaveFloat(float handle, string key, float value) { @@ -184,6 +190,13 @@ NSIO::SaveVector(float handle, string key, vector value) fputs(handle, sprintf("%S \"%v\"\n", key, value)); } +float +NSIO::ReadBool(string strValue) +{ + if (strValue && strValue != "") + return stof(strValue); + return __NULL__; +} float NSIO::ReadFloat(string strValue) { diff --git a/src/gs-entbase/shared/env_bubbles.qc b/src/gs-entbase/shared/env_bubbles.qc new file mode 100644 index 00000000..d6d4e53f --- /dev/null +++ b/src/gs-entbase/shared/env_bubbles.qc @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2016-2022 Marco Cawthorne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED env_bubbles (1 0 0) ? BUBFL_STARTOFF +Brush volume that emits rising bubbles. + +-------- KEYS -------- +"targetname" : Name +"density" : Bubble count when it's emitting +"frequency" : Emitting frequency in seconds +"angles" : Direction of water current +"current" : Speed of the water current + +-------- SPAWNFLAGS -------- +BUBFL_STARTOFF : Start disabled. + +-------- INPUTS -------- +"Activate" : Turns the entity on +"Deactive" : Turns the entity off +"Toggle" : Toggles the entity to an on/off state +"SetDensity" : Sets the bubble count when it's emitting +"SetFrequency" : Sets the emitting frequency in seconds +"SetCurrent" : Sets the speed of the water current + +-------- NOTES -------- +The bubbles emit will pick a random place in the volume (although always at the bottom) +and rise up in varying speeds. This means you can only place vertical bubble emitters. + +-------- TRIVIA -------- +This entity was introduced in Half-Life (1998). +*/ + +enumflags +{ + BUBBLES_ORIGIN, + BUBBLES_ANGLES, + BUBBLES_DENSITY, + BUBBLES_FREQUENCY, + BUBBLES_CURRENT, + BUBBLES_ENABLED +}; + +#define BUBFL_STARTOFF 1 + +class +env_bubbles:NSPointTrigger +{ + PREDICTED_INT(m_iDensity); + PREDICTED_FLOAT(m_flFrequency); + PREDICTED_FLOAT(m_flCurrent); + PREDICTED_BOOL(m_bEnabled); + + /* spawn values */ + int m_iSpawnDensity; + float m_flSpawnFrequency; + float m_flSpawnCurrent; + + void(void) env_bubbles; + + /* overrides */ + virtual void(string, string) SpawnKey; + virtual void(void) Respawn; + +#ifdef SERVER + virtual void(float) Save; + virtual void(string, string) Restore; + virtual void(void) EvaluateEntity; + virtual float(entity, float) SendEntity; + virtual void(entity, int) Trigger; + virtual void(entity, string, string) Input; +#else + virtual void(void) EmitBubbles; + virtual void(float, float) ReceiveEntity; +#endif +}; + +#ifdef SERVER +void +env_bubbles::Save(float handle) +{ + SaveInt(handle, "density", m_iDensity); + SaveFloat(handle, "frequency", m_flFrequency); + SaveFloat(handle, "current", m_flCurrent); + SaveBool(handle, "enabled", m_bEnabled); + + SaveInt(handle, "spawn_density", m_iSpawnDensity); + SaveFloat(handle, "spawn_frequency", m_flSpawnFrequency); + SaveFloat(handle, "spawn_current", m_flSpawnCurrent); + + super::Save(handle); +} + +void +env_bubbles::Restore(string strKey, string strValue) +{ + switch (strKey) { + case "density": + m_iDensity = ReadInt(strValue); + break; + case "frequency": + m_flFrequency = ReadFloat(strValue); + break; + case "current": + m_flCurrent = ReadFloat(strValue); + break; + case "enabled": + m_bEnabled = ReadBool(strValue); + break; + case "spawn_density": + m_iSpawnDensity = ReadInt(strValue); + break; + case "spawn_frequency": + m_flSpawnFrequency = ReadFloat(strValue); + break; + case "spawn_current": + m_flSpawnCurrent = ReadFloat(strValue); + break; + default: + super::Restore(strKey, strValue); + } +} + +void +env_bubbles::Input(entity eAct, string strKey, string strData) +{ + switch (strKey) { + case "Activate": + Trigger(eAct, TRIG_ON); + break; + case "Deactivate": + Trigger(eAct, TRIG_OFF); + break; + case "Toggle": + Trigger(eAct, TRIG_TOGGLE); + break; + case "SetDensity": + m_iDensity = stoi(strData); + break; + case "SetFrequency": + m_flSpawnFrequency = stof(strData); + break; + case "SetCurrent": + m_flSpawnCurrent = stof(strData); + break; + default: + super::Input(eAct, strKey, strData); + } +} + +void +env_bubbles::Trigger(entity eAct, int iState) +{ + switch (iState) { + case TRIG_OFF: + m_bEnabled = false; + break; + case TRIG_ON: + m_bEnabled = true; + break; + default: + m_bEnabled = true - m_bEnabled; + } +} +void +env_bubbles::EvaluateEntity(void) +{ + if (ATTR_CHANGED(origin)) + SetSendFlags(BUBBLES_ORIGIN); + if (ATTR_CHANGED(angles)) + SetSendFlags(BUBBLES_ANGLES); + if (ATTR_CHANGED(m_iDensity)) + SetSendFlags(BUBBLES_DENSITY); + if (ATTR_CHANGED(m_flFrequency)) + SetSendFlags(BUBBLES_FREQUENCY); + if (ATTR_CHANGED(m_flCurrent)) + SetSendFlags(BUBBLES_CURRENT); + if (ATTR_CHANGED(m_bEnabled)) + SetSendFlags(BUBBLES_ENABLED); + + SAVE_STATE(origin); + SAVE_STATE(angles); + SAVE_STATE(m_iDensity); + SAVE_STATE(m_flFrequency); + SAVE_STATE(m_flCurrent); + SAVE_STATE(m_bEnabled); +} +float +env_bubbles::SendEntity(entity ePVSent, float flChanged) +{ + WriteByte(MSG_ENTITY, ENT_BUBBLES); + WriteFloat(MSG_ENTITY, flChanged); + + if (flChanged & BUBBLES_ORIGIN) { + WriteCoord(MSG_ENTITY, origin[0]); + WriteCoord(MSG_ENTITY, origin[1]); + WriteCoord(MSG_ENTITY, origin[2]); + WriteCoord(MSG_ENTITY, mins[0]); + WriteCoord(MSG_ENTITY, mins[1]); + WriteCoord(MSG_ENTITY, mins[2]); + WriteCoord(MSG_ENTITY, maxs[0]); + WriteCoord(MSG_ENTITY, maxs[1]); + WriteCoord(MSG_ENTITY, maxs[2]); + } + + if (flChanged & BUBBLES_ANGLES) { + WriteCoord(MSG_ENTITY, angles[0]); + WriteCoord(MSG_ENTITY, angles[1]); + WriteCoord(MSG_ENTITY, angles[2]); + } + + if (flChanged & BUBBLES_DENSITY) + WriteByte(MSG_ENTITY, m_iDensity); + if (flChanged & BUBBLES_FREQUENCY) + WriteFloat(MSG_ENTITY, m_flFrequency); + if (flChanged & BUBBLES_CURRENT) + WriteFloat(MSG_ENTITY, m_flCurrent); + if (flChanged & BUBBLES_ENABLED) + WriteByte(MSG_ENTITY, m_bEnabled); + + return (1); +} +#else +void +env_bubbles::EmitBubbles(void) +{ + vector vecPos; + + //if (m_bEnabled) + for (int i = 0; i < m_iDensity; i++) { + float timer; + vecPos[0] = mins[0] + (random() * (maxs[0] - mins[0])); + vecPos[1] = mins[1] + (random() * (maxs[1] - mins[1])); + vecPos[2] = mins[2]; + + env_sprite eBubble = spawn(env_sprite); + setorigin(eBubble, vecPos); + setmodel(eBubble, "sprites/bubble.spr"); + eBubble.drawmask = MASK_ENGINE; + eBubble.m_vecRenderColor = [1,1,1]; + eBubble.m_iRenderMode = RM_ADDITIVE; + eBubble.m_flRenderAmt = 1.0f; + eBubble.movetype = MOVETYPE_FLY; + eBubble.velocity[2] = 100 + random(0, 50); + + /* apply current */ + if (m_flCurrent > 0) { + makevectors(eBubble.angles); + eBubble.velocity *= v_forward * m_flCurrent; + } + + /* destroy the bubble once it exits out the water */ + timer = (size[2] / eBubble.velocity[2]); + eBubble.think = Util_Destroy; + eBubble.nextthink = time + timer; + } + + nextthink = time + m_flFrequency; +} + +void +env_bubbles::ReceiveEntity(float is_new, float flChanged) +{ + if (flChanged & BUBBLES_ORIGIN) { + origin[0] = readcoord(); + origin[1] = readcoord(); + origin[2] = readcoord(); + mins[0] = readcoord(); + mins[1] = readcoord(); + mins[2] = readcoord(); + maxs[0] = readcoord(); + maxs[1] = readcoord(); + maxs[2] = readcoord(); + setsize(this, mins, maxs); + setorigin(this, origin); + } + + if (flChanged & BUBBLES_ANGLES) { + angles[0] = readcoord(); + angles[1] = readcoord(); + angles[2] = readcoord(); + } + + if (flChanged & BUBBLES_DENSITY) + m_iDensity = readbyte(); + if (flChanged & BUBBLES_FREQUENCY) + m_flFrequency = readfloat(); + if (flChanged & BUBBLES_CURRENT) + m_flCurrent = readfloat(); + if (flChanged & BUBBLES_ENABLED) + m_bEnabled = readbyte(); + + think = EmitBubbles; + nextthink = time + m_flFrequency; +} +#endif + +void +env_bubbles::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "density": + m_iSpawnDensity = stoi(strValue); + break; + case "frequency": + m_flSpawnFrequency = stof(strValue); + break; + case "current": + m_flSpawnCurrent = stof(strValue); + break; + default: + super::SpawnKey(strKey, strValue); + } +} + +void +env_bubbles::Respawn(void) +{ + SetModel(GetSpawnModel()); + SetSolid(SOLID_NOT); + SetOrigin(GetSpawnOrigin()); + SetAngles(GetSpawnAngles()); + m_iDensity = m_iSpawnDensity; + m_flFrequency = m_flSpawnFrequency; + m_flCurrent = m_flSpawnCurrent; + + if (spawnflags & BUBFL_STARTOFF) + m_bEnabled = false; + else + m_bEnabled = true; +} + +void +env_bubbles::env_bubbles(void) +{ + super::NSPointTrigger(); +} + +#ifdef CLIENT +void +env_bubbles_ReadEntity(float new) +{ + env_bubbles me = (env_bubbles)self; + if (new) { + spawnfunc_env_bubbles(); + } + me.ReceiveEntity(new, readfloat()); +} +#endif \ No newline at end of file diff --git a/src/shared/defs.h b/src/shared/defs.h index 7c054f77..6259b273 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -20,6 +20,7 @@ #define PREDICTED_VECTOR(x) vector x; vector x ##_net #define PREDICTED_ENT(x) entity x; entity x ##_net #define PREDICTED_STRING(x) string x; string x ##_net +#define PREDICTED_BOOL(x) bool x; bool x ##_net #define PREDICTED_INT_N(x) int x ##_net #define PREDICTED_FLOAT_N(x) float x ##_net diff --git a/src/shared/entities.h b/src/shared/entities.h index f80e2bc9..a68aaef0 100644 --- a/src/shared/entities.h +++ b/src/shared/entities.h @@ -41,5 +41,6 @@ enum ENT_VEH_TANKMORTAR, ENT_VEH_4WHEEL, ENT_PROPROPE, + ENT_BUBBLES, ENT_SEPARATOR, };