diff --git a/src/client/entities.qc b/src/client/entities.qc index fc30aa91..57876fc3 100644 --- a/src/client/entities.qc +++ b/src/client/entities.qc @@ -35,6 +35,9 @@ Entity_EntityUpdate(float type, float new) case ENT_BEAM: env_beam_ReadEntity(new); break; + case ENT_LASER: + env_laser_ReadEntity(new); + break; case ENT_PHYSICS: NSPhysicsEntity_ReadEntity(new); break; @@ -118,13 +121,6 @@ Entity_EntityUpdate(float type, float new) case ENT_FOGCONTROLLER: env_fog_controller_readentity(new); break; - case ENT_ENVLASER: - env_laser l = (env_laser)self; - if (new) { - spawnfunc_env_laser(); - } - l.ReceiveEntity(new, readfloat()); - break; case ENT_PARTSYSTEM: info_particle_system ips = (info_particle_system)self; if (new) { diff --git a/src/gs-entbase/client.src b/src/gs-entbase/client.src index 50f907f1..5d80a19f 100644 --- a/src/gs-entbase/client.src +++ b/src/gs-entbase/client.src @@ -11,7 +11,6 @@ client/env_sun.qc client/env_sound.qc client/env_soundscape.qc client/env_particle.qc -client/env_laser.qc client/func_lod.qc client/func_dustmotes.qc client/func_dustcloud.qc diff --git a/src/gs-entbase/client/env_laser.qc b/src/gs-entbase/client/env_laser.qc deleted file mode 100644 index 8bffe6d3..00000000 --- a/src/gs-entbase/client/env_laser.qc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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. -*/ - -void(float radius, vector texcoordbias) R_EndPolygonRibbon = #0; - -enumflags -{ - ENVLASER_CHANGED_ORIGIN, - ENVLASER_CHANGED_ANGLES, - ENVLASER_CHANGED_TEXTURE, - ENVLASER_CHANGED_ENDTEXTURE, - ENVLASER_CHANGED_STATE -}; - -class env_laser -{ -public: - void env_laser(void); - - virtual void ReceiveEntity(float,float); - virtual float predraw(void); - -private: - int m_iState; - string m_strBeamTex; - string m_strEndTex; -}; - -float env_laser::predraw(void) -{ - if (!m_iState) - return (PREDRAW_NEXT); - - R_BeginPolygon(m_strBeamTex, 0, 0); - R_PolygonVertex(angles, [0,0], [1,1,1], 1.0f); - R_PolygonVertex(origin, [0,1], [1,1,1], 1.0f); - R_EndPolygonRibbon(2, [1,0]); - - return (PREDRAW_NEXT); -} - -void -env_laser::ReceiveEntity(float flNew, float flChanged) -{ - if (flChanged & ENVLASER_CHANGED_ORIGIN) { - origin[0] = readcoord(); - origin[1] = readcoord(); - origin[2] = readcoord(); - } - if (flChanged & ENVLASER_CHANGED_ANGLES) { - angles[0] = readcoord(); - angles[1] = readcoord(); - angles[2] = readcoord(); - } - if (flChanged & ENVLASER_CHANGED_TEXTURE) - m_strBeamTex = sprintf("%s_0.tga", readstring()); - if (flChanged & ENVLASER_CHANGED_ENDTEXTURE) - m_strEndTex = readstring(); - if (flChanged & ENVLASER_CHANGED_STATE) - m_iState = readbyte(); - - setorigin(this, origin); -} - -void -env_laser::env_laser(void) -{ -} diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index 3429b5e0..766594b6 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -82,7 +82,6 @@ server/trigger_relay.qc server/env_shooter.qc server/gibshooter.qc server/env_beverage.qc -server/env_laser.qc server/item_food.qc server/speaker.qc server/multi_manager.qc diff --git a/src/gs-entbase/server/env_laser.qc b/src/gs-entbase/server/env_laser.qc deleted file mode 100644 index 23c6f1d6..00000000 --- a/src/gs-entbase/server/env_laser.qc +++ /dev/null @@ -1,260 +0,0 @@ -/* - * 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_laser (1 0 0) (-8 -8 -8) (8 8 8) ENVLAZ_STARTON x x x ENVLAZ_STARTSPARK ENVLAZ_ENDSPARK ENVLAZ_DECALEND -Shoots a frickin lazer. - --------- KEYS -------- -"targetname" : Name -"target" : Target when triggered. -"killtarget" : Target to kill when triggered. -"texture" : Sprite to use as a texture. -"EndSprite" : Sprite to draw at the end of the laser beam impact. -"LaserTarget" : Which entity determines the end position. -"damage" : Damage-per-second when something passes through. - --------- NOTES -------- -Use an info_notnull for a LaserTarget. - --------- TRIVIA -------- -This entity was introduced in Half-Life (1998). -*/ - -enumflags -{ - ENVLASER_CHANGED_ORIGIN, - ENVLASER_CHANGED_ANGLES, - ENVLASER_CHANGED_TEXTURE, - ENVLASER_CHANGED_ENDTEXTURE, - ENVLASER_CHANGED_STATE -}; - -enumflags -{ - ENVLAZ_STARTON, - ENVLAZ_2, - ENVLAZ_4, - ENVLAZ_8, - ENVLAZ_STARTSPARK, - ENVLAZ_ENDSPARK, - ENVLAZ_DECALEND -}; - -class -env_laser:NSPointTrigger -{ - int m_iState; - int m_iStateOld; - float m_flDPS; - string m_strLaserDest; - string m_strBeamTex; - string m_strEndTex; - -public: - void env_laser(void); - - virtual void Save(float); - virtual void Restore(string,string); - virtual void SpawnKey(string,string); - virtual void Respawn(void); - virtual void LaserThink(void); - virtual void Trigger(entity,int); - virtual void EvaluateEntity(void); - virtual float SendEntity(entity,float); - -}; - -void -env_laser::env_laser(void) -{ - pvsflags = PVSF_IGNOREPVS; - m_iState = 0i; - m_iStateOld = 0i; - m_flDPS = 0.0f; - m_strLaserDest = __NULL__; - m_strBeamTex = __NULL__; - m_strEndTex = __NULL__; -} - -void -env_laser::Save(float handle) -{ - super::Save(handle); - SaveInt(handle, "m_iState", m_iState); - SaveInt(handle, "m_iStateOld", m_iStateOld); - SaveFloat(handle, "m_flDPS", m_flDPS); - SaveString(handle, "m_strLaserDest", m_strLaserDest); - SaveString(handle, "m_strBeamTex", m_strBeamTex); - SaveString(handle, "m_strEndTex", m_strEndTex); -} - -void -env_laser::Restore(string strKey, string strValue) -{ - switch (strKey) { - case "m_iState": - m_iState = ReadInt(strValue); - break; - case "m_iStateOld": - m_iStateOld = ReadInt(strValue); - break; - case "m_flDPS": - m_flDPS = ReadFloat(strValue); - break; - case "m_strLaserDest": - m_strLaserDest = ReadString(strValue); - break; - case "m_strBeamTex": - m_strBeamTex = ReadString(strValue); - break; - case "m_strEndTex": - m_strEndTex = ReadString(strValue); - break; - default: - super::Restore(strKey, strValue); - } -} - -void -env_laser::SpawnKey(string strKey, string strValue) -{ - switch (strKey) { - case "texture": - m_strBeamTex = strValue; - precache_model(m_strBeamTex); - break; - case "EndSprite": - m_strEndTex = strValue; - precache_model(m_strEndTex); - break; - case "LaserTarget": - m_strLaserDest = strValue; - break; - case "damage": - m_flDPS = stof(strValue); - break; - default: - super::SpawnKey(strKey, strValue); - } -} - -void -env_laser::Respawn(void) -{ - if (HasSpawnFlags(ENVLAZ_STARTON)) { - m_iState = 1; - ScheduleThink(LaserThink, 0.0f); - } -} - -void -env_laser::LaserThink(void) -{ - entity t; - - if (!m_iState) { - return; - } else { - ScheduleThink(LaserThink, 0.1f); - } - - t = find(world, ::targetname, m_strLaserDest); - SetAngles(t.origin); - - if (!t) { - print(sprintf("^1env_laser::^3LaserThink^7: %s has no valid target. Aborting\n", targetname)); - return; - } - - traceline(origin, t.origin, FALSE, this); - - if (trace_ent.takedamage) { - Damage_Apply(trace_ent, this, rint(m_flDPS), 0, DMG_GENERIC); - } -} - -void -env_laser::Trigger(entity act, int state) -{ - switch (state) { - case TRIG_OFF: - m_iState = 0; - break; - case TRIG_ON: - m_iState = 1; - break; - default: - m_iState = 1 - m_iState; - } - - if (m_iState) { - ScheduleThink(LaserThink, 0.0f); - } else { - ReleaseThink(); - } -} - -float -env_laser::SendEntity(entity ePEnt, float fChanged) -{ - if (clienttype(ePEnt) != CLIENTTYPE_REAL) { - return (0); - } - - WriteByte(MSG_ENTITY, ENT_ENVLASER); - WriteFloat(MSG_ENTITY, fChanged); - - if (fChanged & ENVLASER_CHANGED_ORIGIN) { - WriteCoord(MSG_ENTITY, origin[0]); - WriteCoord(MSG_ENTITY, origin[1]); - WriteCoord(MSG_ENTITY, origin[2]); - } - if (fChanged & ENVLASER_CHANGED_ANGLES) { - WriteCoord(MSG_ENTITY, angles[0]); - WriteCoord(MSG_ENTITY, angles[1]); - WriteCoord(MSG_ENTITY, angles[2]); - } - if (fChanged & ENVLASER_CHANGED_TEXTURE) { - WriteString(MSG_ENTITY, m_strBeamTex); - } - if (fChanged & ENVLASER_CHANGED_ENDTEXTURE) { - WriteString(MSG_ENTITY, m_strEndTex); - } - if (fChanged & ENVLASER_CHANGED_STATE) { - WriteByte(MSG_ENTITY, m_iState); - } - - return (1); -} - -void -env_laser::EvaluateEntity(void) -{ - /* FIXME: Check our fields for networking */ - if (ATTR_CHANGED(angles)) { - SetSendFlags(ENVLASER_CHANGED_ORIGIN); - } - if (ATTR_CHANGED(origin)) { - SetSendFlags(ENVLASER_CHANGED_ANGLES); - } - if (m_iState != m_iStateOld) { - SetSendFlags(ENVLASER_CHANGED_STATE); - m_iStateOld = m_iState; - } - - SAVE_STATE(origin); - SAVE_STATE(angles); -} diff --git a/src/gs-entbase/shared.src b/src/gs-entbase/shared.src index 90ab460c..2ec08989 100644 --- a/src/gs-entbase/shared.src +++ b/src/gs-entbase/shared.src @@ -9,6 +9,7 @@ shared/func_friction.qc shared/env_beam.qc shared/env_sprite.qc shared/env_bubbles.qc +shared/env_laser.qc shared/env_projectedtexture.qc shared/env_fog_controller.qc shared/light_dynamic.qc diff --git a/src/gs-entbase/shared/env_beam.qc b/src/gs-entbase/shared/env_beam.qc index 65eed80a..274e0763 100644 --- a/src/gs-entbase/shared/env_beam.qc +++ b/src/gs-entbase/shared/env_beam.qc @@ -15,7 +15,7 @@ */ /*QUAKED env_beam (1 0 0) (-8 -8 -8) (8 8 8) BEAM_STARTON BEAM_TOGGLE BEAM_RANDOMSTRIKE BEAM_RING BEAM_STARTSPARKS BEAM_ENDSPARKS BEAM_DECAL BEAM_SHADESTART BEAM_SHADEEND -This entity is incomplete. Purely stub. +Controllable beam effect, akin to lightning. Also known as env_lightning. -------- KEYS -------- "targetname" : Name @@ -26,17 +26,20 @@ This entity is incomplete. Purely stub. "life" : Lifetime of the beam in seconds. "StrikeTime" : Time in seconds before the beam reactivates. "damage" : Damage per second that's dealt when touching the inner beam. +"texture" : Path to the sprite to use in place of a texture. +"BoltWidth" : Thickness multiplier. 0-255 range. +"NoiseAmplitude" : Amplitude multiplier. 0-255 range. -------- SPAWNFLAGS -------- BEAM_STARTON : Activate the beam at map start. BEAM_TOGGLE : Beam can now be toggled off, else StrikeTime + life keys take over. BEAM_RANDOMSTRIKE : Use variations in StrikeTime + life keys when set. -BEAM_RING : TODO: Instead of a beam, two points will connect into a ring. +BEAM_RING : Instead of a beam, two points will connect into a ring. BEAM_STARTSPARKS : TODO: Start of the beam will spark when set. BEAM_ENDSPARKS : TODO: End of the beam will spark when set. BEAM_DECAL : TODO: Presumably leaves decals when sparks hit a surface. -BEAM_SHADESTART : TODO: Beam will fade towards the start point when set. -BEAM_SHADEEND : TODO: Beam will fade towards the end point when set. +BEAM_SHADESTART : Beam will fade towards the start point when set. +BEAM_SHADEEND : Beam will fade towards the end point when set. -------- TRIVIA -------- This entity was introduced in Half-Life (1998). @@ -44,14 +47,18 @@ This entity was introduced in Half-Life (1998). enumflags { - BEAM_CHANGED_ORIGIN, + BEAM_CHANGED_SPRITE, BEAM_CHANGED_STARTPOS_X, BEAM_CHANGED_STARTPOS_Y, BEAM_CHANGED_STARTPOS_Z, BEAM_CHANGED_ENDPOS_X, BEAM_CHANGED_ENDPOS_Y, BEAM_CHANGED_ENDPOS_Z, - BEAM_CHANGED_ACTIVE + BEAM_CHANGED_ACTIVE, + BEAM_CHANGED_FLAGS, + BEAM_CHANGED_COLOR, + BEAM_CHANGED_WIDTH, + BEAM_CHANGED_AMPLITUDE }; enumflags @@ -68,7 +75,7 @@ enumflags }; class -env_beam:NSEntity +env_beam:NSRenderableEntity { public: void env_beam(void); @@ -92,6 +99,13 @@ private: PREDICTED_VECTOR(m_vecStartPos) PREDICTED_VECTOR(m_vecEndPos) PREDICTED_INT(m_iActive) + PREDICTED_INT(m_iBeamFlags) + PREDICTED_FLOAT(m_flBeamWidth) + PREDICTED_FLOAT(m_flAmplitude) + + /* visual fluff */ + string m_strTexture; + PREDICTED_INT(m_iSpriteID) #ifdef SERVER string m_strStartEnt; @@ -113,6 +127,10 @@ env_beam::env_beam(void) m_flLifeTime = 0.0f; m_flStrikeTime = 0.0f; m_iDamage = 0i; + m_iBeamFlags = 0i; + + m_strTexture = __NULL__; + m_flBeamWidth = 0.0f; #endif } @@ -124,8 +142,14 @@ env_beam::Respawn(void) SetOrigin(GetSpawnOrigin()); m_iValue = 0; + /* force us to precache the sprite model... and get a modelindex back */ + m_iSpriteID = getmodelindex(m_strTexture, false); + if (HasSpawnFlags(BEAM_STARTON)) Trigger(this, TRIG_ON); + + /* keep it simple */ + m_iBeamFlags = spawnflags | BEAM_SHADEEND; } void @@ -150,6 +174,18 @@ env_beam::SpawnKey(string strKey, string strValue) case "damage": m_iDamage = ReadInt(strValue); break; + case "texture": + m_strTexture = ReadString(strValue); + break; + case "BoltWidth": + m_flBeamWidth = ReadFloat(strValue); + break; + case "NoiseAmplitude": + m_flAmplitude = ReadFloat(strValue); + break; + case "rendercolor": + m_vecRenderColor = ReadVector(strValue); + break; default: super::SpawnKey(strValue, strKey); } @@ -185,16 +221,13 @@ env_beam::LaunchBeam(void) if (HasSpawnFlags(BEAM_TOGGLE)) return; - /* if we have a specific life time set */ - if (m_flLifeTime) - lifetime = m_flLifeTime; - else if (m_flStrikeTime) /* else, we'll just have to take the strike time */ - lifetime = m_flStrikeTime; - /* if lifetime is less or equal to 0, it's an infinite beam */ - if (lifetime <= 0.0f) + if (m_flLifeTime <= 0.0f) return; + /* if we have a specific life time set */ + lifetime = m_flLifeTime; + if (HasSpawnFlags(BEAM_RANDOMSTRIKE)) lifetime *= random(); @@ -210,14 +243,6 @@ env_beam::EndBeam(void) m_iActive = 0i; /* beam is now active */ striketime = m_flStrikeTime; - /* if the strike time is less or equal to 0, don't replay it */ - if (striketime <= 0.0f) - return; - - /* odd behaviour: don't re-strike if we have no life-time set? */ - if (m_flLifeTime <= 0.0f) - return; - if (HasSpawnFlags(BEAM_RANDOMSTRIKE)) striketime *= random(); @@ -236,7 +261,7 @@ void env_beam::Trigger(entity act, int state) { /* if toggle isn't enabled, it can only ever get activated */ - if (HasSpawnFlags(BEAM_TOGGLE) == false) { + if (m_flLifeTime > 0 && HasSpawnFlags(BEAM_TOGGLE) == false) { m_iValue = 1; } else { switch (state) { @@ -294,15 +319,20 @@ env_beam::EvaluateEntity(void) } } + EVALUATE_FIELD(m_iSpriteID, BEAM_CHANGED_SPRITE) EVALUATE_VECTOR(m_vecStartPos, 0, BEAM_CHANGED_STARTPOS_X) EVALUATE_VECTOR(m_vecStartPos, 1, BEAM_CHANGED_STARTPOS_Y) EVALUATE_VECTOR(m_vecStartPos, 2, BEAM_CHANGED_STARTPOS_Z) - EVALUATE_VECTOR(m_vecEndPos, 0, BEAM_CHANGED_ENDPOS_X) EVALUATE_VECTOR(m_vecEndPos, 1, BEAM_CHANGED_ENDPOS_Y) EVALUATE_VECTOR(m_vecEndPos, 2, BEAM_CHANGED_ENDPOS_Z) - EVALUATE_FIELD(m_iActive, BEAM_CHANGED_ACTIVE) + EVALUATE_FIELD(m_iBeamFlags, BEAM_CHANGED_FLAGS) + EVALUATE_VECTOR(m_vecRenderColor, 0, BEAM_CHANGED_COLOR) + EVALUATE_VECTOR(m_vecRenderColor, 1, BEAM_CHANGED_COLOR) + EVALUATE_VECTOR(m_vecRenderColor, 2, BEAM_CHANGED_COLOR) + EVALUATE_FIELD(m_flBeamWidth, BEAM_CHANGED_WIDTH) + EVALUATE_FIELD(m_flAmplitude, BEAM_CHANGED_AMPLITUDE) } float @@ -311,6 +341,7 @@ env_beam::SendEntity(entity ePEnt, float flChanged) WriteByte(MSG_ENTITY, ENT_BEAM); WriteFloat(MSG_ENTITY, flChanged); + SENDENTITY_INT(m_iSpriteID, BEAM_CHANGED_SPRITE) SENDENTITY_COORD(m_vecStartPos[0], BEAM_CHANGED_STARTPOS_X) SENDENTITY_COORD(m_vecStartPos[1], BEAM_CHANGED_STARTPOS_Y) SENDENTITY_COORD(m_vecStartPos[2], BEAM_CHANGED_STARTPOS_Z) @@ -318,6 +349,12 @@ env_beam::SendEntity(entity ePEnt, float flChanged) SENDENTITY_COORD(m_vecEndPos[1], BEAM_CHANGED_ENDPOS_Y) SENDENTITY_COORD(m_vecEndPos[2], BEAM_CHANGED_ENDPOS_Z) SENDENTITY_BYTE(m_iActive, BEAM_CHANGED_ACTIVE) + SENDENTITY_BYTE(m_iBeamFlags, BEAM_CHANGED_FLAGS) + SENDENTITY_BYTE(m_vecRenderColor[0], BEAM_CHANGED_COLOR) + SENDENTITY_BYTE(m_vecRenderColor[1], BEAM_CHANGED_COLOR) + SENDENTITY_BYTE(m_vecRenderColor[2], BEAM_CHANGED_COLOR) + SENDENTITY_BYTE(m_flBeamWidth, BEAM_CHANGED_WIDTH) + SENDENTITY_BYTE(m_flAmplitude, BEAM_CHANGED_AMPLITUDE) //print(sprintf("S (%x): %v %v %i\n", flChanged, m_vecStartPos, m_vecEndPos, m_iActive)); @@ -327,6 +364,7 @@ env_beam::SendEntity(entity ePEnt, float flChanged) void env_beam::ReceiveEntity(float flNew, float flChanged) { + READENTITY_INT(m_iSpriteID, BEAM_CHANGED_SPRITE) READENTITY_COORD(m_vecStartPos[0], BEAM_CHANGED_STARTPOS_X) READENTITY_COORD(m_vecStartPos[1], BEAM_CHANGED_STARTPOS_Y) READENTITY_COORD(m_vecStartPos[2], BEAM_CHANGED_STARTPOS_Z) @@ -334,14 +372,58 @@ env_beam::ReceiveEntity(float flNew, float flChanged) READENTITY_COORD(m_vecEndPos[1], BEAM_CHANGED_ENDPOS_Y) READENTITY_COORD(m_vecEndPos[2], BEAM_CHANGED_ENDPOS_Z) READENTITY_BYTE(m_iActive, BEAM_CHANGED_ACTIVE) + READENTITY_BYTE(m_iBeamFlags, BEAM_CHANGED_FLAGS) + READENTITY_BYTE(m_vecRenderColor[0], BEAM_CHANGED_COLOR) + READENTITY_BYTE(m_vecRenderColor[1], BEAM_CHANGED_COLOR) + READENTITY_BYTE(m_vecRenderColor[2], BEAM_CHANGED_COLOR) + READENTITY_BYTE(m_flBeamWidth, BEAM_CHANGED_WIDTH) + READENTITY_BYTE(m_flAmplitude, BEAM_CHANGED_AMPLITUDE) //print(sprintf("R (%x): %v %v %i\n", flChanged, m_vecStartPos, m_vecEndPos, m_iActive)); drawmask = MASK_ENGINE; setsize(this, [0,0,0], [0,0,0]); setorigin(this, m_vecStartPos); + + /* the sprite has changed, we need to query a new texture */ + if (flChanged & BEAM_CHANGED_SPRITE) { + m_strTexture = spriteframe(modelnameforindex(m_iSpriteID), 0, 0.0f); + } } +#define BEAM_COUNT 16 +float +pseudorand(float input) +{ + float seed = (float)input % 5.0f; + seed += (float)input % 8.0f; + seed += (float)input % 4.0f; + seed += (float)input % 13.0f; + seed += (float)input % 70.0f; + + /* like the engine its random(), never return 0, never return 1 */ + return bound(0.01, (seed) / 100.0f, 0.99f); +} + +static float env_beam_jitlut[BEAM_COUNT] = { + 0.000000, + 0.195090, + 0.382683, + 0.555570, + 0.707106, + 0.831469, + 0.923879, + 0.980785, + 1.000000, + 0.980786, + 0.923880, + 0.831471, + 0.707108, + 0.555572, + 0.382685, + 0.000000, +}; + float env_beam::predraw(void) { @@ -349,11 +431,77 @@ env_beam::predraw(void) if (!m_iActive) return; + if (autocvar(r_skipBeams, 0)) + return; + /* primitive representation */ +#if 0 R_BeginPolygon("", 0, 0); R_PolygonVertex(m_vecStartPos, [0,1], [0,1,0], 1.0f); R_PolygonVertex(m_vecEndPos, [1,1], [0,1,0], 1.0f); R_EndPolygon(); +#endif + + if (m_strTexture) { + float last_progression = 0.0f; + makevectors(g_view.GetCameraAngle()); + setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin); + R_BeginPolygon(m_strTexture, DRAWFLAG_ADDITIVE, 0); + + for (float i = 0; i < BEAM_COUNT; i++) { + float progression = (i / (BEAM_COUNT-1)); + vector point; + vector jitter; + float strength; + float a = 1.0f; + + /* our steps from a - b */ + if (m_iBeamFlags & BEAM_RING) { + vector center = m_vecStartPos - (m_vecStartPos - m_vecEndPos) * 0.5f; + float length = vlen(m_vecStartPos - m_vecEndPos) * 0.5; + + float theta = 2.0f * M_PI * (i / (BEAM_COUNT-1)); + vector p = [sin(theta), cos(theta)] * length; + + point = center + p; + } else { + point[0] = Math_Lerp(m_vecStartPos[0], m_vecEndPos[0], progression); + point[1] = Math_Lerp(m_vecStartPos[1], m_vecEndPos[1], progression); + point[2] = Math_Lerp(m_vecStartPos[2], m_vecEndPos[2], progression); + } + + /* get the direction the beam is 'looking' */ + makevectors(vectoangles(m_vecEndPos - m_vecStartPos)); + + /* nudge it a lil bit up/down left/right from its trajectory */ + /* these are all randomly chosen constants */ + jitter = v_right * (pseudorand((4 * time) + i + entnum) - 0.5); + jitter += v_up * (pseudorand((4 * time) + i + 64.12 + entnum) - 0.5); + jitter += v_right * (pseudorand(100 + (8 * time) + i + entnum) - 0.5); + jitter += v_up * (pseudorand(100 + (8 * time) + i + 25.4 + entnum) - 0.5); + jitter *= m_flAmplitude; + + /* start/end points get less jittery the closer we get*/ + jitter *= env_beam_jitlut[i]; + + /* apply jitter */ + point += jitter; + + /* fading flag stuff */ + if ((i == 0 && m_iBeamFlags & BEAM_SHADESTART) || + (i == BEAM_COUNT && m_iBeamFlags & BEAM_SHADEEND)) + a = 0.0f; + + R_PolygonVertex(point, [1, 0], m_vecRenderColor / 255, a); + + if (autocvar(cl_showoff, 0)) + dynamiclight_add(point, 150, m_vecRenderColor/255); + + last_progression = progression; + } + + R_EndPolygonRibbon(m_flBeamWidth/8, [-1,0]); + } return (PREDRAW_NEXT); } diff --git a/src/gs-entbase/shared/env_laser.qc b/src/gs-entbase/shared/env_laser.qc new file mode 100644 index 00000000..f1402de2 --- /dev/null +++ b/src/gs-entbase/shared/env_laser.qc @@ -0,0 +1,413 @@ +/* + * 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_laser (1 0 0) (-8 -8 -8) (8 8 8) LASER_STARTON x x x LASER_STARTSPARKS LASER_ENDSPARKS LASER_DECAL +Controllable laser effect. More or less a stripped-down version of env_beam. + +-------- KEYS -------- +"targetname" : Name +"LaserTarget" : Targetname of the entity that acts as end point for the laser. +"damage" : Damage per second that's dealt when touching the inner laser. +"texture" : Path to the sprite to use in place of a texture. +"BoltWidth" : Thickness multiplier. 0-255 range. +"NoiseAmplitude" : Amplitude multiplier. 0-255 range. + +-------- SPAWNFLAGS -------- +LASER_STARTON : Activate the laser at map start. +LASER_STARTSPARKS :Start of the laser will spark when set. +LASER_ENDSPARKS : End of the laser will spark when set. +LASER_DECAL : TODO: Presumably leaves decals when sparks hit a surface. + +-------- TRIVIA -------- +This entity was introduced in Half-Life (1998). +*/ + +enumflags +{ + LASER_CHANGED_SPRITE, + LASER_CHANGED_STARTPOS_X, + LASER_CHANGED_STARTPOS_Y, + LASER_CHANGED_STARTPOS_Z, + LASER_CHANGED_ENDPOS_X, + LASER_CHANGED_ENDPOS_Y, + LASER_CHANGED_ENDPOS_Z, + LASER_CHANGED_ACTIVE, + LASER_CHANGED_FLAGS, + LASER_CHANGED_COLOR, + LASER_CHANGED_WIDTH, + LASER_CHANGED_AMPLITUDE +}; + +enumflags +{ + LASER_STARTON, + LASER_UNUSED1, + LASER_UNUSED2, + LASER_UNUSED3, + LASER_STARTSPARKS, + LASER_ENDSPARKS, + LASER_DECAL +}; + +class +env_laser:NSRenderableEntity +{ +public: + void env_laser(void); + +#ifdef SERVER + virtual void Respawn(void); + virtual void SpawnKey(string,string); + virtual void EvaluateEntity(void); + virtual float SendEntity(entity,float); + virtual void Trigger(entity,int); + nonvirtual void CastLaser(void); +#else + virtual float predraw(void); + virtual void ReceiveEntity(float,float); +#endif + +private: + PREDICTED_VECTOR(m_vecEndPos) + PREDICTED_INT(m_iActive) + PREDICTED_INT(m_iBeamFlags) + PREDICTED_FLOAT(m_flBeamWidth) + PREDICTED_FLOAT(m_flAmplitude) + + /* visual fluff */ + string m_strTexture; + PREDICTED_INT(m_iSpriteID) + +#ifdef SERVER + string m_strEndEnt; + float m_iDamage; + float m_flTraceTime; +#else + float m_flSparkTime; +#endif +}; + +void +env_laser::env_laser(void) +{ +#ifdef SERVER + m_strEndEnt = __NULL__; + m_iDamage = 0i; + m_iBeamFlags = 0i; + + m_strTexture = __NULL__; + m_flBeamWidth = 4.0f; +#else + m_flSparkTime = 0.0f; +#endif +} + +#ifdef SERVER +void +env_laser::Respawn(void) +{ + SetSize([0,0,0], [0,0,0]); + SetOrigin(GetSpawnOrigin()); + m_iValue = 0; + + /* force us to precache the sprite model... and get a modelindex back */ + m_iSpriteID = getmodelindex(m_strTexture, false); + + if (HasSpawnFlags(LASER_STARTON)) + Trigger(this, TRIG_ON); + + /* keep it simple */ + m_iBeamFlags = spawnflags; +} + +void +env_laser::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "LaserTarget": + m_strEndEnt = ReadString(strValue); + break; + case "damage": + m_iDamage = ReadInt(strValue); + break; + case "texture": + m_strTexture = ReadString(strValue); + break; + case "BoltWidth": + m_flBeamWidth = ReadFloat(strValue); + break; + case "NoiseAmplitude": + m_flAmplitude = ReadFloat(strValue); + break; + case "rendercolor": + m_vecRenderColor = ReadVector(strValue); + break; + default: + super::SpawnKey(strValue, strKey); + } +} + +void +env_laser::CastLaser(void) +{ + traceline(origin, m_vecEndPos, MOVE_NORMAL, this); + + if (trace_fraction >= 1.0) + return; + + if (trace_ent.takedamage == DAMAGE_NO) + return; + + Damage_Apply(trace_ent, this, m_iDamage, 0, DMG_ELECTRO); + m_flTraceTime = time + 1.0f; +} + +void +env_laser::Trigger(entity act, int state) +{ + switch (state) { + case TRIG_OFF: + m_iValue = 0; + break; + case TRIG_ON: + m_iValue = 1; + break; + default: + m_iValue = 1 - m_iValue; + } + + m_iActive = m_iValue; +} + +void +env_laser::EvaluateEntity(void) +{ + entity eFind; + + /* only bother updating our start/end pos if we're running */ + if (m_iActive) { + m_vecEndPos = origin; + + if (m_strEndEnt) { + eFind = find(world, ::targetname, m_strEndEnt); + + if (eFind) { + m_vecEndPos = eFind.origin; + } + } + } + + if (m_flTraceTime < time) { + CastLaser(); + } + + traceline(origin, m_vecEndPos, MOVE_NORMAL, this); + m_vecEndPos = trace_endpos; + + EVALUATE_FIELD(m_iSpriteID, LASER_CHANGED_SPRITE) + EVALUATE_VECTOR(origin, 0, LASER_CHANGED_STARTPOS_X) + EVALUATE_VECTOR(origin, 1, LASER_CHANGED_STARTPOS_Y) + EVALUATE_VECTOR(origin, 2, LASER_CHANGED_STARTPOS_Z) + EVALUATE_VECTOR(m_vecEndPos, 0, LASER_CHANGED_ENDPOS_X) + EVALUATE_VECTOR(m_vecEndPos, 1, LASER_CHANGED_ENDPOS_Y) + EVALUATE_VECTOR(m_vecEndPos, 2, LASER_CHANGED_ENDPOS_Z) + EVALUATE_FIELD(m_iActive, LASER_CHANGED_ACTIVE) + EVALUATE_FIELD(m_iBeamFlags, LASER_CHANGED_FLAGS) + EVALUATE_VECTOR(m_vecRenderColor, 0, LASER_CHANGED_COLOR) + EVALUATE_VECTOR(m_vecRenderColor, 1, LASER_CHANGED_COLOR) + EVALUATE_VECTOR(m_vecRenderColor, 2, LASER_CHANGED_COLOR) + EVALUATE_FIELD(m_flBeamWidth, LASER_CHANGED_WIDTH) + EVALUATE_FIELD(m_flAmplitude, LASER_CHANGED_AMPLITUDE) +} + +float +env_laser::SendEntity(entity ePEnt, float flChanged) +{ + WriteByte(MSG_ENTITY, ENT_LASER); + WriteFloat(MSG_ENTITY, flChanged); + + SENDENTITY_INT(m_iSpriteID, LASER_CHANGED_SPRITE) + SENDENTITY_COORD(origin[0], LASER_CHANGED_STARTPOS_X) + SENDENTITY_COORD(origin[1], LASER_CHANGED_STARTPOS_Y) + SENDENTITY_COORD(origin[2], LASER_CHANGED_STARTPOS_Z) + SENDENTITY_COORD(m_vecEndPos[0], LASER_CHANGED_ENDPOS_X) + SENDENTITY_COORD(m_vecEndPos[1], LASER_CHANGED_ENDPOS_Y) + SENDENTITY_COORD(m_vecEndPos[2], LASER_CHANGED_ENDPOS_Z) + SENDENTITY_BYTE(m_iActive, LASER_CHANGED_ACTIVE) + SENDENTITY_BYTE(m_iBeamFlags, LASER_CHANGED_FLAGS) + SENDENTITY_BYTE(m_vecRenderColor[0], LASER_CHANGED_COLOR) + SENDENTITY_BYTE(m_vecRenderColor[1], LASER_CHANGED_COLOR) + SENDENTITY_BYTE(m_vecRenderColor[2], LASER_CHANGED_COLOR) + SENDENTITY_BYTE(m_flBeamWidth, LASER_CHANGED_WIDTH) + SENDENTITY_BYTE(m_flAmplitude, LASER_CHANGED_AMPLITUDE) + + //print(sprintf("S (%x): %v %v %i\n", flChanged, origin, m_vecEndPos, m_iActive)); + + return (1); +} +#else +void +env_laser::ReceiveEntity(float flNew, float flChanged) +{ + READENTITY_INT(m_iSpriteID, LASER_CHANGED_SPRITE) + READENTITY_COORD(origin[0], LASER_CHANGED_STARTPOS_X) + READENTITY_COORD(origin[1], LASER_CHANGED_STARTPOS_Y) + READENTITY_COORD(origin[2], LASER_CHANGED_STARTPOS_Z) + READENTITY_COORD(m_vecEndPos[0], LASER_CHANGED_ENDPOS_X) + READENTITY_COORD(m_vecEndPos[1], LASER_CHANGED_ENDPOS_Y) + READENTITY_COORD(m_vecEndPos[2], LASER_CHANGED_ENDPOS_Z) + READENTITY_BYTE(m_iActive, LASER_CHANGED_ACTIVE) + READENTITY_BYTE(m_iBeamFlags, LASER_CHANGED_FLAGS) + READENTITY_BYTE(m_vecRenderColor[0], LASER_CHANGED_COLOR) + READENTITY_BYTE(m_vecRenderColor[1], LASER_CHANGED_COLOR) + READENTITY_BYTE(m_vecRenderColor[2], LASER_CHANGED_COLOR) + READENTITY_BYTE(m_flBeamWidth, LASER_CHANGED_WIDTH) + READENTITY_BYTE(m_flAmplitude, LASER_CHANGED_AMPLITUDE) + + //print(sprintf("R (%x): %v %v %i\n", flChanged, origin, m_vecEndPos, m_iActive)); + + drawmask = MASK_ENGINE; + setsize(this, [0,0,0], [0,0,0]); + setorigin(this, origin); + + /* the sprite has changed, we need to query a new texture */ + if (flChanged & LASER_CHANGED_SPRITE) { + m_strTexture = spriteframe(modelnameforindex(m_iSpriteID), 0, 0.0f); + } +} + +#define LASER_COUNT 16 + +void FX_Spark(vector pos, vector ang); + +static float env_laser_jitlut[LASER_COUNT] = { + 0.000000, + 0.195090, + 0.382683, + 0.555570, + 0.707106, + 0.831469, + 0.923879, + 0.980785, + 1.000000, + 0.980786, + 0.923880, + 0.831471, + 0.707108, + 0.555572, + 0.382685, + 0.000000, +}; + +float +env_laser::predraw(void) +{ + /* only draw when active. */ + if (!m_iActive) + return; + + if (autocvar(r_skipBeams, 0)) + return; + + /* primitive representation */ +#if 0 + R_BeginPolygon("", 0, 0); + R_PolygonVertex(origin, [0,1], [0,1,0], 1.0f); + R_PolygonVertex(m_vecEndPos, [1,1], [0,1,0], 1.0f); + R_EndPolygon(); +#endif + + if (m_strTexture) { + float last_progression = 0.0f; + makevectors(g_view.GetCameraAngle()); + setproperty(VF_ORIGIN, pSeat->m_vecPredictedOrigin); + R_BeginPolygon(m_strTexture, DRAWFLAG_ADDITIVE, 0); + + for (float i = 0; i < LASER_COUNT; i++) { + float progression = (i / (LASER_COUNT-1)); + vector point; + vector jitter; + float strength; + float a = 1.0f; + + /* our steps from a - b */ + point[0] = Math_Lerp(origin[0], m_vecEndPos[0], progression); + point[1] = Math_Lerp(origin[1], m_vecEndPos[1], progression); + point[2] = Math_Lerp(origin[2], m_vecEndPos[2], progression); + + /* get the direction the laser is 'looking' */ + makevectors(vectoangles(m_vecEndPos - origin)); + + /* nudge it a lil bit up/down left/right from its trajectory */ + /* these are all randomly chosen constants */ + jitter = v_right * (pseudorand((4 * time) + i + entnum) - 0.5); + jitter += v_up * (pseudorand((4 * time) + i + 64.12 + entnum) - 0.5); + jitter += v_right * (pseudorand(100 + (8 * time) + i + entnum) - 0.5); + jitter += v_up * (pseudorand(100 + (8 * time) + i + 25.4 + entnum) - 0.5); + jitter *= m_flAmplitude; + + /* start/end points get less jittery the closer we get*/ + jitter *= env_laser_jitlut[i]; + + /* apply jitter */ + point += jitter; + + R_PolygonVertex(point, [1, 0], m_vecRenderColor / 255, a); + + if (autocvar(cl_showoff, 0)) + dynamiclight_add(point, 150, m_vecRenderColor/255); + + last_progression = progression; + } + + R_EndPolygonRibbon(m_flBeamWidth/8, [-1,0]); + } + + if (m_flSparkTime > time) + return (PREDRAW_NEXT); + + if (m_iBeamFlags & (LASER_STARTSPARKS)) { + vector dir = vectoangles(origin - m_vecEndPos); + makevectors(dir); + FX_Spark(origin, -v_forward); + } + + if (m_iBeamFlags & (LASER_ENDSPARKS)) { + vector dir2 = vectoangles(m_vecEndPos - origin); + + makevectors(dir2); + FX_Spark(m_vecEndPos, -v_forward); + } + + m_flSparkTime = time + 0.25f; + + return (PREDRAW_NEXT); +} +#endif + +#ifdef CLIENT +void +env_laser_ReadEntity(float isnew) +{ + env_laser laser = (env_laser)self; + float changedflags = readfloat(); + + if (isnew) + spawnfunc_env_laser(); + + laser.ReceiveEntity(isnew, changedflags); +} +#endif \ No newline at end of file diff --git a/src/shared/entities.h b/src/shared/entities.h index 6cbdd157..f1381e53 100644 --- a/src/shared/entities.h +++ b/src/shared/entities.h @@ -31,7 +31,7 @@ enum ENT_DLIGHT, ENT_PROJECTEDTEXTURE, ENT_FOGCONTROLLER, - ENT_ENVLASER, + ENT_LASER, ENT_PARTSYSTEM, ENT_SPRITE, ENT_SPRAY,