/* * Copyright (c) 2016-2022 Vera Visions LLC. * * 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 env_bubbles(void); /* overrides */ virtual void SpawnKey(string,string); virtual void Respawn(void); virtual void Spawned(void); #ifdef SERVER virtual void Save(float); virtual void Restore(string,string); virtual void EvaluateEntity(void); virtual float SendEntity(entity,float); virtual void Trigger(entity,int); virtual void Input(entity,string,string); #else virtual void EmitBubbles(void); virtual void ReceiveEntity(float,float); #endif }; void env_bubbles::env_bubbles(void) { } #ifdef SERVER void env_bubbles::Save(float handle) { super::Save(handle); SaveInt(handle, "m_iDensity", m_iDensity); SaveFloat(handle, "m_flFrequency", m_flFrequency); SaveFloat(handle, "m_flCurrent", m_flCurrent); SaveBool(handle, "m_bEnabled", m_bEnabled); SaveInt(handle, "m_iSpawnDensity", m_iSpawnDensity); SaveFloat(handle, "m_flSpawnFrequency", m_flSpawnFrequency); SaveFloat(handle, "m_flSpawnCurrent", m_flSpawnCurrent); } void env_bubbles::Restore(string strKey, string strValue) { switch (strKey) { case "m_iDensity": m_iDensity = ReadInt(strValue); break; case "m_flFrequency": m_flFrequency = ReadFloat(strValue); break; case "m_flCurrent": m_flCurrent = ReadFloat(strValue); break; case "m_bEnabled": m_bEnabled = ReadBool(strValue); break; case "m_iSpawnDensity": m_iSpawnDensity = ReadInt(strValue); break; case "m_flSpawnFrequency": m_flSpawnFrequency = ReadFloat(strValue); break; case "m_flSpawnCurrent": 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::Spawned(void) { super::Spawned(); precache_model("sprites/bubble.spr"); } #ifdef CLIENT void env_bubbles_ReadEntity(float new) { env_bubbles me = (env_bubbles)self; if (new) { spawnfunc_env_bubbles(); } me.ReceiveEntity(new, readfloat()); } #endif