diff --git a/cl_dll/cl_dll.h b/cl_dll/cl_dll.h index dc6b11a21..b742540e1 100644 --- a/cl_dll/cl_dll.h +++ b/cl_dll/cl_dll.h @@ -30,7 +30,6 @@ #define CL_DLL_H #include "build.h" typedef unsigned char byte; -typedef unsigned short word; // redefine //typedef int ( *pfnUserMsgHook )( const char *pszName, int iSize, void *pbuf ); diff --git a/cl_dll/cl_msg_fx.cpp b/cl_dll/cl_msg_fx.cpp index 36f9d5fc2..6e08a35a9 100644 --- a/cl_dll/cl_msg_fx.cpp +++ b/cl_dll/cl_msg_fx.cpp @@ -261,7 +261,72 @@ int __MsgFunc_CustomBeam( const char* pszName, int iSize, void *pbuf ) return 1; } -void FX_Sprite_Trail( Vector start, Vector end, int modelIndex, int count, float life, float size, float amp, int renderamt, float speed, int r = 0, int g = 0, int b = 0, float extraLifeMax = 0.0f ) +void FX_TempSprite(Vector pos, int modelIndex, float scale, int rendermode, color24 color, int a, int renderfx, float framerate, float life) +{ + model_t *pmodel; + if(( pmodel = gEngfuncs.pfnGetModelByIndex( modelIndex )) == NULL ) + return; + + TEMPENTITY *pTemp = gEngfuncs.pEfxAPI->CL_TempEntAlloc( pos, pmodel ); + if( !pTemp ) return; + + const float clientTime = gEngfuncs.GetClientTime(); + + pTemp->frameMax = pmodel->numframes - 1; + + pTemp->entity.curstate.rendermode = rendermode; + pTemp->entity.baseline.renderamt = pTemp->entity.curstate.renderamt = a; + pTemp->entity.curstate.renderfx = renderfx; + pTemp->entity.curstate.rendercolor = color; + pTemp->entity.curstate.scale = scale; + pTemp->entity.curstate.framerate = framerate; + + if (framerate > 0 && pmodel->numframes > 1) + { + pTemp->flags |= FTENT_SPRANIMATE; + } + + if (life) + { + if (pTemp->flags & FTENT_SPRANIMATE) + pTemp->flags |= FTENT_SPRANIMATELOOP; + pTemp->die = clientTime + life; + } + else if (framerate > 0) + { + pTemp->die = clientTime + (pTemp->frameMax / framerate); + } + else + { + pTemp->die = clientTime + 0.1f; + } +} + +int __MsgFunc_Sprite( const char* pszName, int iSize, void *pbuf ) +{ + BEGIN_READ( pbuf, iSize ); + + Vector pos; + + pos[0] = READ_COORD(); + pos[1] = READ_COORD(); + pos[2] = READ_COORD(); + int modelIndex = READ_SHORT(); + float scale = READ_BYTE() * 0.1f; + int rendermode = READ_BYTE(); + color24 color = READ_COLOR(); + int a = READ_BYTE(); + int renderfx = READ_BYTE(); + float framerate = READ_SHORT() * 0.1f; + float life = READ_BYTE() * 0.1f; + + FX_TempSprite(pos, modelIndex, scale, rendermode, color, a, renderfx, framerate, life); + + return 1; +} + +void FX_Sprite_Trail( Vector start, Vector end, int modelIndex, int count, float life, float scale, float amp, + float speed, int rendermode, color24 color, int renderamt, int renderfx = kRenderFxNone, float extraLifeMax = 0.0f ) { Vector delta, dir; model_t *pmodel; @@ -289,7 +354,7 @@ void FX_Sprite_Trail( Vector start, Vector end, int modelIndex, int count, float if( !pTemp ) return; pTemp->flags = (FTENT_COLLIDEWORLD|FTENT_FADEOUT|FTENT_SLOWGRAVITY); - pTemp->frameMax = pmodel->numframes; + pTemp->frameMax = pmodel->numframes - 1; if (pmodel->numframes > 1) pTemp->flags |= FTENT_SPRCYCLE; @@ -300,16 +365,14 @@ void FX_Sprite_Trail( Vector start, Vector end, int modelIndex, int count, float VectorCopy( vel, pTemp->entity.baseline.origin ); VectorCopy( pos, pTemp->entity.origin ); - pTemp->entity.curstate.scale = size; - pTemp->entity.curstate.rendermode = kRenderGlow; - pTemp->entity.curstate.renderfx = kRenderFxNoDissipation; + pTemp->entity.curstate.scale = scale; + pTemp->entity.curstate.rendermode = rendermode; + pTemp->entity.curstate.renderfx = renderfx; pTemp->entity.curstate.renderamt = pTemp->entity.baseline.renderamt = renderamt; - pTemp->entity.curstate.rendercolor.r = r; - pTemp->entity.curstate.rendercolor.g = g; - pTemp->entity.curstate.rendercolor.b = b; + pTemp->entity.curstate.rendercolor = color; if (pmodel->numframes > 1) - pTemp->entity.curstate.frame = Com_RandomLong( 0, pmodel->numframes-1 ); + pTemp->entity.curstate.frame = Com_RandomLong( 0, pmodel->numframes - 1 ); pTemp->die = clientTime + life; if (extraLifeMax) pTemp->die += Com_RandomFloat( 0.0f, extraLifeMax ); @@ -338,12 +401,105 @@ int __MsgFunc_SpriteTrail( const char* pszName, int iSize, void *pbuf ) scale *= 0.1f; float vel = (float)READ_BYTE() * 10; float random = (float)READ_BYTE() * 10; - int r = READ_BYTE(); - int g = READ_BYTE(); - int b = READ_BYTE(); + int rendermode = READ_BYTE(); + color24 color = READ_COLOR(); int a = READ_BYTE(); + int renderfx = READ_BYTE(); float extraLifeMax = READ_BYTE() * 0.1f; - FX_Sprite_Trail( pos, pos2, modelIndex, count, life, scale, random, a, vel, r, g, b, extraLifeMax ); + FX_Sprite_Trail( pos, pos2, modelIndex, count, life, scale, random, vel, rendermode, color, a, renderfx, extraLifeMax ); + + return 1; +} + +void FX_Spray( Vector pos, Vector dir, int modelIndex, int count, int speed, int spread, int rendermode, color24 color, int renderamt, int renderfx, float scale, float framerate, int flags ) +{ + TEMPENTITY *pTemp; + model_t *pmodel; + int i; + + if(( pmodel = gEngfuncs.pfnGetModelByIndex( modelIndex )) == NULL ) + return; + + float noise = (float)spread / 100.0f; + float znoise = Q_min( 1.0f, noise * 1.5f ); + + const float clientTime = gEngfuncs.GetClientTime(); + + for( i = 0; i < count; i++ ) + { + pTemp = gEngfuncs.pEfxAPI->CL_TempEntAlloc( pos, pmodel ); + if( !pTemp ) return; + + pTemp->frameMax = pmodel->numframes - 1; + if (pmodel->numframes > 1) + pTemp->flags |= FTENT_SPRCYCLE; + + pTemp->entity.curstate.scale = scale; + pTemp->entity.curstate.rendermode = rendermode; + pTemp->entity.curstate.rendercolor = color; + pTemp->entity.baseline.renderamt = pTemp->entity.curstate.renderamt = renderamt; + pTemp->entity.curstate.renderfx = renderfx; + pTemp->entity.curstate.framerate = framerate; + + pTemp->flags |= FTENT_SLOWGRAVITY; + if (flags & SPRAY_FLAG_COLLIDEWORLD) + pTemp->flags |= FTENT_COLLIDEWORLD; + if (flags & SPRAY_FLAG_ANIMATE) + pTemp->flags |= FTENT_SPRANIMATE; + if (flags & SPRAY_FLAG_FADEOUT) + { + pTemp->flags |= FTENT_FADEOUT; + pTemp->fadeSpeed = 2.0f; + } + + if(pmodel->numframes > 1 && (flags & SPRAY_FLAG_ANIMATE)) + { + pTemp->die = clientTime + (pTemp->frameMax / framerate); + } + else + pTemp->die = clientTime + 0.35f; + + if (pmodel->numframes > 1 && !(flags & SPRAY_FLAG_ANIMATE)) + { + pTemp->entity.curstate.frame = Com_RandomLong( 0, pmodel->numframes - 1 ); + } + + pTemp->entity.baseline.origin[0] = dir[0] + Com_RandomFloat( -noise, noise ); + pTemp->entity.baseline.origin[1] = dir[1] + Com_RandomFloat( -noise, noise ); + pTemp->entity.baseline.origin[2] = dir[2] + Com_RandomFloat( 0, znoise ); + VectorScale( pTemp->entity.baseline.origin, Com_RandomFloat(( speed * 0.8f ), ( speed * 1.2f )), pTemp->entity.baseline.origin ); + } +} + +int __MsgFunc_Spray( const char* pszName, int iSize, void *pbuf ) +{ + BEGIN_READ( pbuf, iSize ); + + Vector pos, dir; + + pos[0] = READ_COORD(); + pos[1] = READ_COORD(); + pos[2] = READ_COORD(); + dir[0] = READ_COORD(); + dir[1] = READ_COORD(); + dir[2] = READ_COORD(); + int modelIndex = READ_SHORT(); + int count = READ_BYTE(); + int speed = READ_BYTE(); + int spread = READ_BYTE(); + int rendermode = READ_BYTE(); + color24 color = READ_COLOR(); + int a = READ_BYTE(); + int renderfx = READ_BYTE(); + float scale = (float)READ_BYTE(); + if( !scale ) + scale = 1.0f; + else + scale *= 0.1f; + float framerate = READ_SHORT() * 0.1f; + int flags = READ_BYTE(); + + FX_Spray(pos, dir, modelIndex, count, speed, spread, rendermode, color, a, renderfx, scale, framerate, flags); return 1; } @@ -405,14 +561,12 @@ void ExpandCallback(TEMPENTITY *ent, float frametime, float currenttime) } } -void FX_Smoke(TEMPENTITY* pTemp, float scale, float speed, float zOffset, int rendermode, int renderamt, int r, int g, int b ) +void FX_Smoke(TEMPENTITY* pTemp, float scale, float speed, float zOffset, int rendermode, int renderamt, color24 color) { pTemp->entity.curstate.rendermode = rendermode; pTemp->entity.curstate.renderfx = kRenderFxNone; pTemp->entity.baseline.origin[2] = speed; - pTemp->entity.curstate.rendercolor.r = r; - pTemp->entity.curstate.rendercolor.g = g; - pTemp->entity.curstate.rendercolor.b = b; + pTemp->entity.curstate.rendercolor = color; pTemp->entity.curstate.renderamt = renderamt; pTemp->entity.origin[2] += zOffset; pTemp->entity.curstate.scale = scale; @@ -443,9 +597,7 @@ int __MsgFunc_Smoke( const char* pszName, int iSize, void *pbuf ) zOffset = READ_SHORT(); rendermode = READ_BYTE(); renderamt = READ_BYTE(); - r = READ_BYTE(); - g = READ_BYTE(); - b = READ_BYTE(); + color24 color = READ_COLOR(); scaleSpeed = READ_SHORT() / 10.0f; if (directed) @@ -465,14 +617,14 @@ int __MsgFunc_Smoke( const char* pszName, int iSize, void *pbuf ) renderamt = 255; if (rendermode == 0) rendermode = kRenderTransAlpha; - if (r + g + b == 0) - r = g = b = Com_RandomLong( 20, 35 ); + if (color.r + color.g + color.b == 0) + color.r = color.g = color.b = Com_RandomLong( 20, 35 ); TEMPENTITY* pTemp = gEngfuncs.pEfxAPI->R_DefaultSprite( pos, modelIndex, frameRate ); if (pTemp) { - FX_Smoke(pTemp, scale, speed, zOffset, rendermode, renderamt, r, g, b); + FX_Smoke(pTemp, scale, speed, zOffset, rendermode, renderamt, color); if (directed) { pTemp->entity.baseline.origin = dir * speed; @@ -590,7 +742,9 @@ void HookFXMessages() HOOK_MESSAGE( RandomGibs ); HOOK_MESSAGE( MuzzleLight ); HOOK_MESSAGE( CustomBeam ); + HOOK_MESSAGE( Sprite ); HOOK_MESSAGE( SpriteTrail ); + HOOK_MESSAGE( Spray ); HOOK_MESSAGE( Streaks ); HOOK_MESSAGE( Smoke ); HOOK_MESSAGE( SparkShower ); diff --git a/cl_dll/parsemsg.cpp b/cl_dll/parsemsg.cpp index 4e58a161f..8acee9a7b 100644 --- a/cl_dll/parsemsg.cpp +++ b/cl_dll/parsemsg.cpp @@ -16,8 +16,7 @@ // parsemsg.cpp // -typedef unsigned char byte; -#define true 1 +#include "parsemsg.h" static byte *gpBuf; static int giSize; @@ -38,7 +37,7 @@ int READ_CHAR( void ) if( giRead + 1 > giSize ) { - giBadRead = true; + giBadRead = 1; return -1; } @@ -54,7 +53,7 @@ int READ_BYTE( void ) if( giRead + 1 > giSize ) { - giBadRead = true; + giBadRead = 1; return -1; } @@ -70,7 +69,7 @@ int READ_SHORT( void ) if( giRead + 2 > giSize ) { - giBadRead = true; + giBadRead = 1; return -1; } @@ -92,7 +91,7 @@ int READ_LONG( void ) if( giRead + 4 > giSize ) { - giBadRead = true; + giBadRead = 1; return -1; } @@ -162,3 +161,12 @@ float READ_HIRESANGLE( void ) { return (float)( READ_SHORT() * ( 360.0 / 65536 ) ); } + +color24 READ_COLOR( void ) +{ + color24 color; + color.r = READ_BYTE(); + color.g = READ_BYTE(); + color.b = READ_BYTE(); + return color; +} diff --git a/cl_dll/parsemsg.h b/cl_dll/parsemsg.h index a37a100ae..ef2382780 100644 --- a/cl_dll/parsemsg.h +++ b/cl_dll/parsemsg.h @@ -21,6 +21,8 @@ #define ASSERT( x ) +#include "common_types.h" + void BEGIN_READ( void *buf, int size ); int READ_CHAR( void ); int READ_BYTE( void ); @@ -33,6 +35,8 @@ float READ_COORD( void ); float READ_ANGLE( void ); float READ_HIRESANGLE( void ); +color24 READ_COLOR( void ); + #endif // PARSEMSG_H diff --git a/common/common_types.h b/common/common_types.h new file mode 100644 index 000000000..fba84b896 --- /dev/null +++ b/common/common_types.h @@ -0,0 +1,12 @@ +#pragma once +#ifndef COMMON_TYPES_H +#define COMMON_TYPES_H + +typedef unsigned char byte; + +typedef struct +{ + byte r, g, b; +} color24; + +#endif diff --git a/common/const.h b/common/const.h index 9c92f0999..acd496890 100644 --- a/common/const.h +++ b/common/const.h @@ -15,6 +15,9 @@ #pragma once #if !defined(CONST_H) #define CONST_H + +#include "common_types.h" + // // Constants shared by the engine and dlls // This header file included by engine files and DLL files. @@ -741,9 +744,6 @@ enum typedef unsigned int func_t; typedef int string_t; -typedef unsigned char byte; -typedef unsigned short word; - #undef true #undef false @@ -753,11 +753,6 @@ typedef enum { false, true } qboolean; typedef int qboolean; #endif -typedef struct -{ - byte r, g, b; -} color24; - typedef struct { unsigned r, g, b, a; diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index db9e20299..5e8df5f92 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -184,6 +184,8 @@ set (SVDLL_SOURCES uzi.cpp util.cpp vehicle.cpp + visuals.cpp + visuals_utils.cpp voltigore.cpp warpball.cpp weapons.cpp diff --git a/dlls/agrunt.cpp b/dlls/agrunt.cpp index e0910e8ca..4ac6f4622 100644 --- a/dlls/agrunt.cpp +++ b/dlls/agrunt.cpp @@ -27,6 +27,7 @@ #include "hornet.h" #include "scripted.h" #include "common_soundscripts.h" +#include "visuals_utils.h" //========================================================= // monster-specific schedule types @@ -45,7 +46,7 @@ enum TASK_AGRUNT_SETUP_HIDE_ATTACK = LAST_FOLLOWINGMONSTER_TASK + 1, }; -int iAgruntMuzzleFlash; +extern int gmsgSprite; //========================================================= // Monster's Anim Events Go Here @@ -136,6 +137,8 @@ class CAGrunt : public CFollowingMonster float m_flNextSpeakTime; float m_flNextWordTime; int m_iLastWord; + + static const NamedVisual muzzleFlashVisual; }; LINK_ENTITY_TO_CLASS( monster_alien_grunt, CAGrunt ) @@ -202,6 +205,12 @@ const NamedSoundScript CAGrunt::fireSoundScript = { "AlienGrunt.Fire" }; +const NamedVisual CAGrunt::muzzleFlashVisual = BuildVisual("AlienGrunt.MuzzleFlash") + .Model("sprites/muz4.spr") + .RenderMode(kRenderTransAdd) + .Scale(0.6f) + .Alpha(128); + //========================================================= // IRelationship - overridden because Human Grunts are // Alien Grunt's nemesis. @@ -250,13 +259,8 @@ static void AgruntTraceAttack( CBaseMonster* self, entvars_t *pevInflictor, entv MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, ptr->vecEndPos ); WRITE_BYTE( TE_TRACER ); - WRITE_COORD( ptr->vecEndPos.x ); - WRITE_COORD( ptr->vecEndPos.y ); - WRITE_COORD( ptr->vecEndPos.z ); - - WRITE_COORD( vecTracerDir.x ); - WRITE_COORD( vecTracerDir.y ); - WRITE_COORD( vecTracerDir.z ); + WRITE_VECTOR( ptr->vecEndPos ); + WRITE_VECTOR( vecTracerDir ); MESSAGE_END(); } @@ -490,15 +494,15 @@ void CAGrunt::HandleAnimEvent( MonsterEvent_t *pEvent ) GetAttachment( 0, vecArmPos, vecArmDir ); vecArmPos = vecArmPos + vecDirToEnemy * 32.0f; - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecArmPos ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( vecArmPos.x ); // pos - WRITE_COORD( vecArmPos.y ); - WRITE_COORD( vecArmPos.z ); - WRITE_SHORT( iAgruntMuzzleFlash ); // model - WRITE_BYTE( 6 ); // size * 10 - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); + + const Visual* visual = GetVisual(muzzleFlashVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_PVS, gmsgSprite, vecArmPos ); + WRITE_VECTOR( vecArmPos ); // pos + WriteSpriteVisual(visual); + MESSAGE_END(); + } CBaseEntity *pHornet = CBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict(), m_soundList ); UTIL_MakeVectors( pHornet->pev->angles ); @@ -636,7 +640,7 @@ void CAGrunt::Precache() RegisterAndPrecacheSoundScript(useSoundScript, idleSoundScript); RegisterAndPrecacheSoundScript(unuseSoundScript, alertSoundScript); - iAgruntMuzzleFlash = PRECACHE_MODEL( "sprites/muz4.spr" ); + RegisterVisual(muzzleFlashVisual); EntityOverrides entityOverrides; entityOverrides.soundList = m_soundList; diff --git a/dlls/apache.cpp b/dlls/apache.cpp index 1ce4838cd..10871b83f 100644 --- a/dlls/apache.cpp +++ b/dlls/apache.cpp @@ -25,6 +25,7 @@ #include "mod_features.h" #include "game.h" #include "common_soundscripts.h" +#include "visuals_utils.h" extern DLL_GLOBAL int g_iSkillLevel; @@ -322,9 +323,7 @@ void CApache::DyingThink( void ) WRITE_BYTE( TE_BREAKMODEL ); // position - WRITE_COORD( vecSpot.x ); - WRITE_COORD( vecSpot.y ); - WRITE_COORD( vecSpot.z ); + WRITE_VECTOR( vecSpot ); // size WRITE_COORD( 400 ); @@ -332,9 +331,7 @@ void CApache::DyingThink( void ) WRITE_COORD( 132 ); // velocity - WRITE_COORD( pev->velocity.x ); - WRITE_COORD( pev->velocity.y ); - WRITE_COORD( pev->velocity.z ); + WRITE_VECTOR( pev->velocity ); // randomization WRITE_BYTE( 50 ); @@ -854,9 +851,7 @@ void CApache::FireRocket( void ) { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); WRITE_BYTE( TE_SMOKE ); - WRITE_COORD( vecSrc.x ); - WRITE_COORD( vecSrc.y ); - WRITE_COORD( vecSrc.z ); + WRITE_VECTOR( vecSrc ); WRITE_SHORT( g_sModelIndexSmoke ); WRITE_BYTE( 20 ); // scale * 10 WRITE_BYTE( 12 ); // framerate @@ -1049,10 +1044,11 @@ class CApacheHVR : public CGrenade int Restore( CRestore &restore ); static TYPEDESCRIPTION m_SaveData[]; - int m_iTrail; Vector m_vecForward; static const NamedSoundScript rpgSoundScript; + + static const NamedVisual trailVisual; }; LINK_ENTITY_TO_CLASS( hvr_rocket, CApacheHVR ) @@ -1073,6 +1069,13 @@ const NamedSoundScript CApacheHVR::rpgSoundScript = { "Apache.RPG" }; +const NamedVisual CApacheHVR::trailVisual = BuildVisual("Apache.RocketTrail") + .Model("sprites/smoke.spr") + .Life(1.5f) + .BeamWidth(5) + .RenderColor(224, 224, 255) + .Alpha(255); + void CApacheHVR::Spawn( void ) { Precache(); @@ -1100,7 +1103,7 @@ void CApacheHVR::Precache( void ) { PrecacheBaseGrenadeSounds(); PRECACHE_MODEL( "models/HVR.mdl" ); - m_iTrail = PRECACHE_MODEL( "sprites/smoke.spr" ); + RegisterVisual(trailVisual); RegisterAndPrecacheSoundScript(rpgSoundScript); } @@ -1115,17 +1118,15 @@ void CApacheHVR::IgniteThink( void ) EmitSoundScript(rpgSoundScript); // rocket trail - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT( entindex() ); // entity - WRITE_SHORT( m_iTrail ); // model - WRITE_BYTE( 15 ); // life - WRITE_BYTE( 5 ); // width - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + const Visual* visual = GetVisual(trailVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT( entindex() ); // entity + WriteBeamFollowVisual(visual); + MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + } // set to accelerate SetThink( &CApacheHVR::AccelerateThink ); diff --git a/dlls/bigmomma.cpp b/dlls/bigmomma.cpp index 86a3d437c..c00359ba7 100644 --- a/dlls/bigmomma.cpp +++ b/dlls/bigmomma.cpp @@ -26,6 +26,8 @@ #include "combat.h" #include "game.h" #include "common_soundscripts.h" +#include "visuals_utils.h" +#include "fx_flags.h" #define SF_INFOBM_RUN 0x0001 #define SF_INFOBM_WAIT 0x0002 @@ -33,6 +35,8 @@ #define SF_BIGMOM_NOBABYCRABS SF_MONSTER_DONT_DROP_GUN #define SF_MONSTERCLIP_BABYCRABS SF_MONSTER_SPECIAL_FLAG +extern int gmsgSpray; + // AI Nodes for Big Momma class CInfoBM : public CPointEntity { @@ -121,7 +125,9 @@ class CBMortar : public CBaseEntity static constexpr const char* spitTouchSoundScript = "BigMomma.SpitTouch"; static constexpr const char* spitHitSoundScript = "BigMomma.SpitHit"; - int m_SpitSprite; + static const NamedVisual mortarVisual; + static const NamedVisual mortarSprayVisual; + int m_SpitDebrisSprite; }; @@ -134,6 +140,16 @@ TYPEDESCRIPTION CBMortar::m_SaveData[] = IMPLEMENT_SAVERESTORE( CBMortar, CBaseEntity ) +const NamedVisual CBMortar::mortarVisual = BuildVisual("BigMomma.Mortar") + .Model("sprites/mommaspit.spr") + .RenderMode(kRenderTransAlpha) + .Alpha(255) + .Scale(2.5f) + .Framerate(10.0f); + +const NamedVisual CBMortar::mortarSprayVisual = BuildVisual::Spray("BigMomma.MortarSpray") + .Model("sprites/mommaspout.spr"); + //========================================================= // Monster's Anim Events Go Here //========================================================= @@ -170,7 +186,7 @@ IMPLEMENT_SAVERESTORE( CBMortar, CBaseEntity ) #define bits_MEMORY_FIRED_NODE ( bits_MEMORY_CUSTOM4 ) Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot2, float maxHeight ); -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ); +void MortarSpray( const Vector &position, const Vector &direction, const Visual* visual, int count ); // UNDONE: // @@ -700,7 +716,7 @@ void CBigMomma::LaunchMortar( void ) EmitSoundScript(launchMortarSoundScript); CBMortar *pBomb = CBMortar::Shoot( edict(), startPos, vecLaunch, m_soundList ); pBomb->pev->gravity = 1.0f; - MortarSpray( startPos, Vector( 0.0f, 0.0f, 1.0f ), m_SpitSprite, 24 ); + MortarSpray( startPos, Vector( 0.0f, 0.0f, 1.0f ), GetVisual(CBMortar::mortarSprayVisual), 24 ); } //========================================================= @@ -750,7 +766,7 @@ void CBigMomma::Precache() UTIL_PrecacheOther(BIG_CHILDCLASS, entityOverrides); UTIL_PrecacheOther("bmortar", entityOverrides); - m_SpitSprite = PRECACHE_MODEL( "sprites/mommaspout.spr" );// client side spittle. + RegisterVisual(CBMortar::mortarSprayVisual);// client side spittle. } void CBigMomma::Activate( void ) @@ -1177,20 +1193,22 @@ Vector VecCheckSplatToss( entvars_t *pev, const Vector &vecSpot1, Vector vecSpot // Mortar // // --------------------------------- -void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count ) +void MortarSpray( const Vector &position, const Vector &direction, const Visual* visual, int count ) { - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, position ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( position.x ); // pos - WRITE_COORD( position.y ); - WRITE_COORD( position.z ); - WRITE_COORD( direction.x ); // dir - WRITE_COORD( direction.y ); - WRITE_COORD( direction.z ); - WRITE_SHORT( spriteModel ); // model + MESSAGE_BEGIN( MSG_PVS, gmsgSpray, position ); + WRITE_VECTOR( position ); // pos + WRITE_VECTOR( direction ); // dir + WRITE_SHORT( visual->modelIndex ); // model WRITE_BYTE ( count ); // count WRITE_BYTE ( 130 ); // speed WRITE_BYTE ( 80 ); // noise ( client will divide by 100 ) + WRITE_BYTE( visual->rendermode ); + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( visual->renderamt ); + WRITE_BYTE( visual->renderfx ); + WRITE_BYTE( (int)(visual->scale * 10) ); + WRITE_SHORT( (int)(visual->framerate * 10) ); + WRITE_BYTE( SPRAY_FLAG_FADEOUT ); MESSAGE_END(); } @@ -1202,12 +1220,12 @@ void CBMortar::Spawn( void ) pev->classname = MAKE_STRING( "bmortar" ); pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - SET_MODEL( ENT( pev ), "sprites/mommaspit.spr" ); + const Visual* visual = GetVisual(mortarVisual); + + ApplyVisualToEntity(this, visual); + pev->frame = 0; - pev->scale = 0.5f; UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); @@ -1217,8 +1235,8 @@ void CBMortar::Spawn( void ) void CBMortar::Precache() { - PRECACHE_MODEL( "sprites/mommaspit.spr" );// spit projectile. - m_SpitSprite = PRECACHE_MODEL( "sprites/mommaspout.spr" );// client side spittle. + RegisterVisual( mortarVisual );// spit projectile. + RegisterVisual( mortarSprayVisual );// client side spittle. m_SpitDebrisSprite = PRECACHE_MODEL( "sprites/mommablob.spr" ); // TODO: not used? RegisterAndPrecacheSoundScript(spitTouchSoundScript, NPC::spitTouchSoundScript); @@ -1232,15 +1250,9 @@ void CBMortar::Animate( void ) if( gpGlobals->time > pev->dmgtime ) { pev->dmgtime = gpGlobals->time + 0.2f; - MortarSpray( pev->origin, -pev->velocity.Normalize(), m_SpitSprite, 3 ); - } - if( pev->frame++ ) - { - if( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } + MortarSpray( pev->origin, -pev->velocity.Normalize(), GetVisual(mortarSprayVisual), 3 ); } + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate); } CBMortar *CBMortar::Shoot(edict_t *pOwner, Vector vecStart, Vector vecVelocity , string_t soundList) @@ -1252,7 +1264,6 @@ CBMortar *CBMortar::Shoot(edict_t *pOwner, Vector vecStart, Vector vecVelocity , UTIL_SetOrigin( pSpit->pev, vecStart ); pSpit->pev->velocity = vecVelocity; pSpit->pev->owner = pOwner; - pSpit->pev->scale = 2.5f; pSpit->SetThink( &CBMortar::Animate ); pSpit->pev->nextthink = gpGlobals->time + 0.1f; @@ -1280,7 +1291,7 @@ void CBMortar::Touch( CBaseEntity *pOther ) } // make some flecks - MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, m_SpitSprite, 24 ); + MortarSpray( tr.vecEndPos, tr.vecPlaneNormal, GetVisual(mortarSprayVisual), 24 ); entvars_t *pevOwner = NULL; if( pev->owner ) diff --git a/dlls/bullsquid.cpp b/dlls/bullsquid.cpp index 97c052369..cb4043015 100644 --- a/dlls/bullsquid.cpp +++ b/dlls/bullsquid.cpp @@ -29,14 +29,19 @@ #include "game.h" #include "bullsquid.h" #include "common_soundscripts.h" +#include "visuals_utils.h" +#include "fx_flags.h" + +extern int gmsgSpray; + +const NamedVisual sharedTinySpitVisual = BuildVisual("Bullsquid.TinySpitBase") + .Model("sprites/tinyspit.spr"); // Slow big poisonous ball as alternative range attack for bullsquid #define FEATURE_BULLSQUID_TOXICSPIT 1 #define SQUID_SPRINT_DIST 256.0f // how close the squid has to get before starting to sprint and refusing to swerve -int iSquidSpitSprite; - //========================================================= // monster-specific schedule types //========================================================= @@ -71,37 +76,36 @@ TYPEDESCRIPTION CSquidSpit::m_SaveData[] = IMPLEMENT_SAVERESTORE( CSquidSpit, CBaseEntity ) + +const NamedVisual CSquidSpit::spitVisual = BuildVisual::Animated("Bullsquid.Spit") + .Model("sprites/bigspit.spr") + .RenderMode(kRenderTransAlpha) + .Scale(0.5f); + +const NamedVisual CSquidSpit::fleckVisual = BuildVisual::Spray("Bullsquid.Fleck").Mixin(&sharedTinySpitVisual); + void CSquidSpit::Spawn( void ) { - SpawnHelper("squidspit"); + SpawnHelper("squidspit", spitVisual); } void CSquidSpit::Precache() { - PRECACHE_MODEL( "sprites/bigspit.spr" ); - PrecacheSounds(); - iSquidSpitSprite = PRECACHE_MODEL( "sprites/tinyspit.spr" );// client side spittle. -} - -void CSquidSpit::PrecacheSounds() -{ + RegisterVisual(spitVisual); RegisterAndPrecacheSoundScript(spitTouchSoundScript, NPC::spitTouchSoundScript); RegisterAndPrecacheSoundScript(spitHitSoundScript, NPC::spitHitSoundScript); + RegisterVisual(fleckVisual);// client side spittle. } -void CSquidSpit::SpawnHelper(const char *className) +void CSquidSpit::SpawnHelper(const char *className, const char* spitVisualName) { Precache(); pev->movetype = MOVETYPE_FLY; pev->classname = MAKE_STRING( className ); - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAlpha; - pev->renderamt = 255; - SET_MODEL( ENT( pev ), "sprites/bigspit.spr" ); + ApplyVisualToEntity(this, GetVisual(spitVisualName)); pev->frame = 0; - pev->scale = 0.5f; UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); @@ -111,14 +115,7 @@ void CSquidSpit::SpawnHelper(const char *className) void CSquidSpit::Animate( void ) { pev->nextthink = gpGlobals->time + 0.1f; - - if( pev->frame++ ) - { - if( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate); } void CSquidSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, string_t soundList ) @@ -148,20 +145,26 @@ void CSquidSpit::Touch( CBaseEntity *pOther ) UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); UTIL_DecalTrace( &tr, DECAL_SPIT1 + RANDOM_LONG( 0, 1 ) ); - // make some flecks - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, tr.vecEndPos ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( tr.vecEndPos.x ); // pos - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_COORD( tr.vecPlaneNormal.x ); // dir - WRITE_COORD( tr.vecPlaneNormal.y ); - WRITE_COORD( tr.vecPlaneNormal.z ); - WRITE_SHORT( iSquidSpitSprite ); // model - WRITE_BYTE( 5 ); // count - WRITE_BYTE( 30 ); // speed - WRITE_BYTE( 80 ); // noise ( client will divide by 100 ) - MESSAGE_END(); + const Visual* visual = GetVisual(fleckVisual); + if (visual->modelIndex) + { + // make some flecks + MESSAGE_BEGIN( MSG_PVS, gmsgSpray, tr.vecEndPos ); + WRITE_VECTOR( tr.vecEndPos ); // pos + WRITE_VECTOR( tr.vecPlaneNormal ); // dir + WRITE_SHORT( visual->modelIndex ); // model + WRITE_BYTE( 5 ); // count + WRITE_BYTE( 30 ); // speed + WRITE_BYTE( 80 );// noise ( client will divide by 100 ) + WRITE_BYTE( visual->rendermode ); + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( visual->renderamt ); + WRITE_BYTE( visual->renderfx ); + WRITE_BYTE( (int)(visual->scale * 10) ); + WRITE_SHORT( (int)(visual->framerate * 10) ); + WRITE_BYTE( SPRAY_FLAG_FADEOUT ); + MESSAGE_END(); + } } else { @@ -199,22 +202,28 @@ const NamedSoundScript CSquidToxicSpit::spithitSoundScript = { "Bullsquid.ToxicSpitHit" }; +const NamedVisual CSquidToxicSpit::toxicSpitVisual = BuildVisual::Animated("Bullsquid.ToxicSpit") + .Model("sprites/cnt1.spr") + .RenderProps(kRenderTransAdd, Color(110, 120, 0), 228) + .Scale(0.8f); + +const NamedVisual CSquidToxicSpit::fleckVisual = BuildVisual::Spray("Bullsquid.ToxicFleck").Mixin(&sharedTinySpitVisual); + +const NamedVisual CSquidToxicSpit::particleVisual = BuildVisual("Bullsquid.ToxicParticle") + .Model("sprites/glow01.spr") + .RenderProps(kRenderGlow, Color(80, 160, 0), 255, kRenderFxNoDissipation) + .Scale(0.3f) + .Life(0.1f); + void CSquidToxicSpit::Spawn( void ) { Precache(); pev->movetype = MOVETYPE_FLY; pev->classname = MAKE_STRING( "bigsquidspit" ); - pev->solid = SOLID_BBOX; - pev->rendermode = kRenderTransAdd; - pev->renderamt = 228; - pev->rendercolor.x = 110; - pev->rendercolor.y = 120; - pev->rendercolor.z = 0; - SET_MODEL( ENT( pev ), "sprites/cnt1.spr" ); + ApplyVisualToEntity(this, GetVisual(toxicSpitVisual)); pev->frame = 0; - pev->scale = 0.8; UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); @@ -223,13 +232,13 @@ void CSquidToxicSpit::Spawn( void ) void CSquidToxicSpit::Precache() { - PRECACHE_MODEL( "sprites/cnt1.spr" ); + RegisterVisual(toxicSpitVisual); RegisterAndPrecacheSoundScript(acidSoundScript); RegisterAndPrecacheSoundScript(spithitSoundScript); - m_iImpactSprite = PRECACHE_MODEL( "sprites/tinyspit.spr" ); - m_iFleckSprite = PRECACHE_MODEL( "sprites/glow01.spr" ); + RegisterVisual(fleckVisual); + RegisterVisual(particleVisual); } extern int gmsgSpriteTrail; @@ -248,41 +257,34 @@ void CSquidToxicSpit::Animate( void ) if (pev->dmgtime < gpGlobals->time) { - const Vector end = pev->origin + pev->velocity.Normalize() * 16.0f; - - // make some flecks - MESSAGE_BEGIN( MSG_PVS, gmsgSpriteTrail, pev->origin ); - WRITE_COORD( pev->origin.x ); // start - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( end.x ); // end - WRITE_COORD( end.y ); - WRITE_COORD( end.z + 16.0f ); - WRITE_SHORT( m_iFleckSprite ); // model - WRITE_BYTE( 3 ); // count - WRITE_BYTE( 1 ); // life in 0.1s - WRITE_BYTE( 3 ); // scale in 0.1 - WRITE_BYTE( 20 ); // velocity along vector in 10's - WRITE_BYTE( 20 ); // randomness of velocity in 10's - WRITE_BYTE( 80 ); - WRITE_BYTE( 160 ); - WRITE_BYTE( 0 ); - WRITE_BYTE( 255 ); - WRITE_BYTE( 10 ); // random extra life in 0.1s - MESSAGE_END(); + Vector end = pev->origin + pev->velocity.Normalize() * 16.0f; + end.z += 16.0f; + + const Visual* visual = GetVisual(particleVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_PVS, gmsgSpriteTrail, pev->origin ); + WRITE_VECTOR( pev->origin ); // start + WRITE_VECTOR( end ); // end + WRITE_SHORT( visual->modelIndex ); // model + WRITE_BYTE( 3 ); // count + WRITE_BYTE( RandomizeNumberFromRange(visual->life)*10 ); // life in 0.1s + WRITE_BYTE( (int)(visual->scale * 10) ); // scale in 0.1 + WRITE_BYTE( 20 ); // velocity along vector in 10's + WRITE_BYTE( 20 ); // randomness of velocity in 10's + WRITE_BYTE( visual->rendermode ); + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( visual->renderamt ); + WRITE_BYTE( visual->renderfx ); + WRITE_BYTE( 10 ); // random extra life in 0.1s + MESSAGE_END(); + } pev->dmgtime = gpGlobals->time + 0.2f; } pev->nextthink = gpGlobals->time + 0.1; - - if( pev->frame++ ) - { - if( pev->frame > m_maxFrame ) - { - pev->frame = 0; - } - } + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate); } void CSquidToxicSpit::Shoot( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, string_t soundList ) @@ -312,20 +314,27 @@ void CSquidToxicSpit::Touch( CBaseEntity *pOther ) UTIL_TraceLine( pev->origin, pev->origin + pev->velocity * 10, dont_ignore_monsters, ENT( pev ), &tr ); UTIL_DecalTrace( &tr, DECAL_SPIT1 + RANDOM_LONG( 0, 1 ) ); - // make some flecks - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, tr.vecEndPos ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( tr.vecEndPos.x ); // pos - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_COORD( tr.vecPlaneNormal.x ); // dir - WRITE_COORD( tr.vecPlaneNormal.y ); - WRITE_COORD( tr.vecPlaneNormal.z ); - WRITE_SHORT( m_iImpactSprite ); // model - WRITE_BYTE( 8 ); // count - WRITE_BYTE( 15 ); // speed - WRITE_BYTE( 100 ); // noise ( client will divide by 100 ) - MESSAGE_END(); + const Visual* visual = GetVisual(fleckVisual); + + if (visual->modelIndex) + { + // make some flecks + MESSAGE_BEGIN( MSG_PVS, gmsgSpray, tr.vecEndPos ); + WRITE_VECTOR( tr.vecEndPos ); // pos + WRITE_VECTOR( tr.vecPlaneNormal ); // dir + WRITE_SHORT( visual->modelIndex ); // model + WRITE_BYTE( 8 ); // count + WRITE_BYTE( 15 ); // speed + WRITE_BYTE( 100 );// noise ( client will divide by 100 ) + WRITE_BYTE( visual->rendermode ); + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( visual->renderamt ); + WRITE_BYTE( visual->renderfx ); + WRITE_BYTE( (int)(visual->scale * 10) ); + WRITE_SHORT( (int)(visual->framerate * 10) ); + WRITE_BYTE( SPRAY_FLAG_FADEOUT ); + MESSAGE_END(); + } } else if (pev->owner == pOther->edict()) { @@ -419,6 +428,9 @@ class CBullsquid : public CBaseMonster static const NamedSoundScript attackSoundScript; static const NamedSoundScript attackToxicSoundScript; static const NamedSoundScript biteSoundScript; + + static const NamedVisual tinySpitVisual; + static const NamedVisual toxicTinySpitVisual; }; LINK_ENTITY_TO_CLASS( monster_bullchicken, CBullsquid ) @@ -488,6 +500,10 @@ const NamedSoundScript CBullsquid::biteSoundScript = { "Bullsquid.Bite" }; +const NamedVisual CBullsquid::tinySpitVisual = BuildVisual::Spray("Bullsquid.TinySpit").Mixin(&sharedTinySpitVisual); + +const NamedVisual CBullsquid::toxicTinySpitVisual = BuildVisual::Spray("Bullsquid.ToxicTinySpit").Mixin(&sharedTinySpitVisual); + //========================================================= // IgnoreConditions //========================================================= @@ -773,20 +789,26 @@ void CBullsquid::HandleAnimEvent( MonsterEvent_t *pEvent ) // do stuff for this event. AttackSound(toxicSpit); - // spew the spittle temporary ents. - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSpitOrigin ); - WRITE_BYTE( TE_SPRITE_SPRAY ); - WRITE_COORD( vecSpitOrigin.x ); // pos - WRITE_COORD( vecSpitOrigin.y ); - WRITE_COORD( vecSpitOrigin.z ); - WRITE_COORD( vecSpitDir.x ); // dir - WRITE_COORD( vecSpitDir.y ); - WRITE_COORD( vecSpitDir.z ); - WRITE_SHORT( iSquidSpitSprite ); // model - WRITE_BYTE( 15 ); // count - WRITE_BYTE( 210 ); // speed - WRITE_BYTE( 25 ); // noise ( client will divide by 100 ) - MESSAGE_END(); + const Visual* visual = toxicSpit ? GetVisual(toxicTinySpitVisual) : GetVisual(tinySpitVisual); + if (visual->modelIndex) + { + // spew the spittle temporary ents. + MESSAGE_BEGIN( MSG_PVS, gmsgSpray, vecSpitOrigin ); + WRITE_VECTOR( vecSpitOrigin ); // pos + WRITE_VECTOR( vecSpitDir ); // dir + WRITE_SHORT( visual->modelIndex ); // model + WRITE_BYTE( 15 ); // count + WRITE_BYTE( 210 ); // speed + WRITE_BYTE( 25 );// noise ( client will divide by 100 ) + WRITE_BYTE( visual->rendermode ); + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( visual->renderamt ); + WRITE_BYTE( visual->renderfx ); + WRITE_BYTE( (int)(visual->scale * 10) ); + WRITE_SHORT( (int)(visual->framerate * 10) ); + WRITE_BYTE( SPRAY_FLAG_FADEOUT ); + MESSAGE_END(); + } if (toxicSpit) { CSquidToxicSpit::Shoot(pev, vecSpitOrigin, vecSpitDir * CSquidToxicSpit::SpitSpeed(), m_soundList); @@ -918,6 +940,9 @@ void CBullsquid::Precache() UTIL_PrecacheOther("squidtoxicspit", entityOverrides); // toxic spit projectile #endif + RegisterVisual(tinySpitVisual); + RegisterVisual(toxicTinySpitVisual); + RegisterAndPrecacheSoundScript(NPC::swishSoundScript);// because we use the basemonster SWIPE animation event RegisterAndPrecacheSoundScript(idleSoundScript); diff --git a/dlls/bullsquid.h b/dlls/bullsquid.h index 0ecee8a8e..8bf8da1e3 100644 --- a/dlls/bullsquid.h +++ b/dlls/bullsquid.h @@ -16,6 +16,7 @@ // bullsquid - big, spotty tentacle-mouthed meanie. //========================================================= +#pragma once #ifndef BULLSQUID_H #define BULLSQUID_H @@ -29,7 +30,6 @@ class CSquidSpit : public CBaseEntity public: void Spawn(void); void Precache(); - virtual void PrecacheSounds(); static void Shoot(entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, string_t soundList = iStringNull); static float SpitSpeed() { return 900.0f; } @@ -44,8 +44,11 @@ class CSquidSpit : public CBaseEntity static constexpr const char* spitTouchSoundScript = "Bullsquid.SpitTouch"; static constexpr const char* spitHitSoundScript = "Bullsquid.SpitHit"; + + static const NamedVisual spitVisual; + static const NamedVisual fleckVisual; protected: - void SpawnHelper(const char* className); + void SpawnHelper(const char* className, const char* spitVisualName); }; class CSquidToxicSpit : public CBaseEntity @@ -66,11 +69,12 @@ class CSquidToxicSpit : public CBaseEntity int m_maxFrame; - int m_iImpactSprite; - int m_iFleckSprite; - static const NamedSoundScript acidSoundScript; static const NamedSoundScript spithitSoundScript; + + static const NamedVisual toxicSpitVisual; + static const NamedVisual fleckVisual; + static const NamedVisual particleVisual; }; #endif // BULLSQUID_H diff --git a/dlls/cbase.cpp b/dlls/cbase.cpp index 5734cd5fd..3c9e86875 100644 --- a/dlls/cbase.cpp +++ b/dlls/cbase.cpp @@ -863,6 +863,19 @@ void CBaseEntity::RegisterAndPrecacheSoundScript(const char* derivative, const RegisterAndPrecacheSoundScript(derivative, defaultSoundScript.name, defaultSoundScript, paramsOverride); } +const Visual* CBaseEntity::RegisterVisual(const NamedVisual &defaultVisual, bool precache) +{ + if (defaultVisual.mixin) + { + const Visual* visual = RegisterVisual(*defaultVisual.mixin, false); + Visual changedVisual = defaultVisual; + changedVisual.CompleteFrom(*visual); + return ProvideDefaultVisual(defaultVisual.name, changedVisual, precache); + } + else + return ProvideDefaultVisual(defaultVisual.name, defaultVisual, precache); +} + int CBaseEntity::Save( CSave &save ) { if( save.WriteEntVars( "ENTVARS", pev ) ) diff --git a/dlls/cbase.h b/dlls/cbase.h index 781cdbe21..98a5b29b7 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -19,6 +19,7 @@ #include "extdll.h" #include "util.h" #include "soundscripts.h" +#include "visuals.h" /* Class Hierachy @@ -304,6 +305,7 @@ class CBaseEntity void RegisterAndPrecacheSoundScript(const NamedSoundScript& defaultSoundScript); void RegisterAndPrecacheSoundScript(const char* derivative, const char* base, const SoundScript& defaultSoundScript, const SoundScriptParamOverride paramsOverride = SoundScriptParamOverride()); void RegisterAndPrecacheSoundScript(const char* derivative, const NamedSoundScript& defaultSoundScript, const SoundScriptParamOverride paramsOverride = SoundScriptParamOverride()); + const Visual* RegisterVisual(const NamedVisual& defaultVisual, bool precache = true); // allow engine to allocate instance data void *operator new( size_t stAllocateBlock, entvars_t *pev ) diff --git a/dlls/controller.cpp b/dlls/controller.cpp index 397db5086..2134e4aa9 100644 --- a/dlls/controller.cpp +++ b/dlls/controller.cpp @@ -26,6 +26,7 @@ #include "squadmonster.h" #include "scripted.h" #include "global_models.h" +#include "visuals_utils.h" //========================================================= // Monster's Anim Events Go Here @@ -38,6 +39,11 @@ #define CONTROLLER_FLINCH_DELAY 2 // at most one flinch every n secs +const NamedVisual sharedEnergyBallVisual = BuildVisual("Controller.EnergyBallBase") + .Model("sprites/xspark4.spr") + .RenderColor(255, 255, 255) + .Alpha(255); + class CController : public CSquadMonster { public: @@ -47,6 +53,7 @@ class CController : public CSquadMonster void Spawn( void ); void Precache( void ); + void ClearBalls(); void UpdateOnRemove(); void SetYawSpeed( void ); int DefaultClassify( void ); @@ -88,6 +95,13 @@ class CController : public CSquadMonster static const NamedSoundScript dieSoundScript; static const NamedSoundScript attackSoundScript; + static const NamedVisual sharedBallLightVisual; + + static const NamedVisual energyBallVisual; + static const NamedVisual headOpenLightVisual; + static const NamedVisual headShootLightVisual; + static const NamedVisual energyBallLightVisual; + int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ); void OnDying(); void GibMonster( void ); @@ -160,6 +174,30 @@ const NamedSoundScript CController::attackSoundScript = { "Controller.Attack" }; +const NamedVisual CController::energyBallVisual = BuildVisual("Controller.EnergyBall") + .RenderMode(kRenderGlow) + .RenderFx(kRenderFxNoDissipation) + .Scale(1.0f) + .Mixin(&sharedEnergyBallVisual); + +const NamedVisual CController::sharedBallLightVisual = BuildVisual("Controller.EnergyBallLightBase") + .RenderColor(255, 192, 64); + +const NamedVisual CController::headOpenLightVisual = BuildVisual("Controller.HeadOpenLight") + .Radius(1) + .Life(2.0f) + .Mixin(&CController::sharedBallLightVisual); + +const NamedVisual CController::headShootLightVisual = BuildVisual("Controller.HeadShootLight") + .Radius(32) + .Life(1.0f) + .Mixin(&CController::sharedBallLightVisual); + +const NamedVisual CController::energyBallLightVisual = BuildVisual("Controller.EnergyBallLight") + .Radius(8) + .Life(0.5f) + .Mixin(&CController::sharedBallLightVisual); + //========================================================= // Classify - indicates this monster's place in the // relationship table. @@ -212,17 +250,7 @@ void CController::OnDying() void CController::GibMonster( void ) { - // delete balls - if( m_pBall[0] ) - { - UTIL_Remove( m_pBall[0] ); - m_pBall[0] = NULL; - } - if( m_pBall[1] ) - { - UTIL_Remove( m_pBall[1] ); - m_pBall[1] = NULL; - } + ClearBalls(); CSquadMonster::GibMonster(); } @@ -266,17 +294,12 @@ void CController::HandleAnimEvent( MonsterEvent_t *pEvent ) GetAttachment( 0, vecStart, angleGun ); + const Visual* visual = GetVisual(headOpenLightVisual); MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex() + 0x1000 ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( 1 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 20 ); // life * 10 + WRITE_VECTOR( vecStart ); // origin + WriteEntLightVisual( visual ); WRITE_COORD( -32 ); // decay MESSAGE_END(); @@ -292,17 +315,12 @@ void CController::HandleAnimEvent( MonsterEvent_t *pEvent ) GetAttachment( 0, vecStart, angleGun ); + const Visual* visual = GetVisual(headShootLightVisual); MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex() + 0x1000 ); // entity, attachment - WRITE_COORD( 0 ); // origin - WRITE_COORD( 0 ); - WRITE_COORD( 0 ); - WRITE_COORD( 32 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 10 ); // life * 10 + WRITE_VECTOR( g_vecZero ); // origin + WriteEntLightVisual( visual ); WRITE_COORD( 32 ); // decay MESSAGE_END(); @@ -386,25 +404,26 @@ void CController::Precache() RegisterAndPrecacheSoundScript(dieSoundScript); RegisterAndPrecacheSoundScript(attackSoundScript); - PRECACHE_MODEL( "sprites/xspark4.spr" ); + RegisterVisual(energyBallVisual); + RegisterVisual(headOpenLightVisual); + RegisterVisual(headShootLightVisual); + RegisterVisual(energyBallLightVisual); UTIL_PrecacheOther( "controller_energy_ball" ); UTIL_PrecacheOther( "controller_head_ball" ); } -void CController::UpdateOnRemove() +void CController::ClearBalls() { - if( m_pBall[0] ) - { - UTIL_Remove( m_pBall[0] ); - m_pBall[0] = 0; - } + UTIL_Remove( m_pBall[0] ); + m_pBall[0] = 0; + UTIL_Remove( m_pBall[1] ); + m_pBall[1] = 0; +} - if( m_pBall[1] ) - { - UTIL_Remove( m_pBall[1] ); - m_pBall[1] = 0; - } +void CController::UpdateOnRemove() +{ + ClearBalls(); CSquadMonster::UpdateOnRemove(); } @@ -850,10 +869,9 @@ void CController::RunAI( void ) { if( m_pBall[i] == NULL ) { - m_pBall[i] = CSprite::SpriteCreate( "sprites/xspark4.spr", pev->origin, TRUE ); - m_pBall[i]->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); + + m_pBall[i] = CreateSpriteFromVisual(GetVisual(energyBallVisual), pev->origin); m_pBall[i]->SetAttachment( edict(), ( i + 3 ) ); - m_pBall[i]->SetScale( 1.0f ); } float t = m_iBallTime[i] - gpGlobals->time; @@ -869,17 +887,14 @@ void CController::RunAI( void ) GetAttachment( i + 2, vecStart, angleGun ); UTIL_SetOrigin( m_pBall[i]->pev, vecStart ); + const Visual* visual = GetVisual(energyBallLightVisual); MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex() + 0x1000 * ( i + 3 ) ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); + WRITE_VECTOR( vecStart ); // origin WRITE_COORD( m_iBallCurrent[i] / 8 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 192 ); // G - WRITE_BYTE( 64 ); // B - WRITE_BYTE( 5 ); // life * 10 + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( RandomizeNumberFromRange(visual->life)*10 ); // life * 10 WRITE_COORD( 0 ); // decay MESSAGE_END(); } @@ -1157,15 +1172,40 @@ class CControllerHeadBall : public CBaseMonster void EXPORT BounceTouch( CBaseEntity *pOther ); void MovetoTarget( Vector vecTarget ); void Crawl( void ); + void MakeTraceBeam(const Vector& vecSrc); + int m_flNextAttack; Vector m_vecIdeal; EHANDLE m_hOwner; + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + static const NamedSoundScript electroSoundScript; + static const NamedVisual headBallVisual; + static const NamedVisual headBallBeamVisual; + static const NamedVisual headBallLightVisual; + +private: + int m_maxFrame; + void SetMaxFrame() { + if (pev->modelindex) + { + m_maxFrame = MODEL_FRAMES(pev->modelindex) - 1; + } + } }; LINK_ENTITY_TO_CLASS( controller_head_ball, CControllerHeadBall ) +TYPEDESCRIPTION CControllerHeadBall::m_SaveData[] = +{ + DEFINE_FIELD( CControllerHeadBall, m_hOwner, FIELD_EHANDLE ), +}; + +IMPLEMENT_SAVERESTORE( CControllerHeadBall, CBaseMonster ) + const NamedSoundScript CControllerHeadBall::electroSoundScript = { CHAN_STATIC, {"weapons/electro4.wav"}, @@ -1175,6 +1215,24 @@ const NamedSoundScript CControllerHeadBall::electroSoundScript = { "Controller.HeadElectro" }; +const NamedVisual CControllerHeadBall::headBallVisual = BuildVisual("Controller.HeadBall") + .RenderMode(kRenderTransAdd) + .Scale(2.0f) + .Mixin(&sharedEnergyBallVisual); + +const NamedVisual CControllerHeadBall::headBallBeamVisual = BuildVisual("Controller.HeadBallBeam") + .Model(g_pModelNameLaser) + .RenderColor(255, 255, 255) + .Alpha(255) + .BeamParams(20, 0, 10) + .Framerate(10.f) + .Life(0.3f); + +const NamedVisual CControllerHeadBall::headBallLightVisual = BuildVisual("Controller.HeadBallLight") + .RenderColor(255, 255, 255) + .Radius(16) + .Life(0.2f); + void CControllerHeadBall::Spawn( void ) { Precache(); @@ -1182,13 +1240,7 @@ void CControllerHeadBall::Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; - SET_MODEL(ENT( pev ), "sprites/xspark4.spr" ); - pev->rendermode = kRenderTransAdd; - pev->rendercolor.x = 255; - pev->rendercolor.y = 255; - pev->rendercolor.z = 255; - pev->renderamt = 255; - pev->scale = 2.0f; + ApplyVisualToEntity(this, GetVisual(headBallVisual)); UTIL_SetSize(pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); UTIL_SetOrigin( pev, pev->origin ); @@ -1202,33 +1254,26 @@ void CControllerHeadBall::Spawn( void ) m_hOwner = Instance( pev->owner ); pev->dmgtime = gpGlobals->time; + + SetMaxFrame(); } void CControllerHeadBall::Precache( void ) { - PRECACHE_MODEL( "sprites/xspark4.spr" ); + RegisterVisual(headBallVisual); + RegisterVisual(headBallBeamVisual); + RegisterVisual(headBallLightVisual); RegisterAndPrecacheSoundScript(electroSoundScript); + SetMaxFrame(); } void CControllerHeadBall::HuntThink( void ) { pev->nextthink = gpGlobals->time + 0.1f; - pev->renderamt -= 5; + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate); - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_ELIGHT ); - WRITE_SHORT( entindex() ); // entity, attachment - WRITE_COORD( pev->origin.x ); // origin - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( pev->renderamt / 16 ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B - WRITE_BYTE( 2 ); // life * 10 - WRITE_COORD( 0 ); // decay - MESSAGE_END(); + pev->renderamt -= 5; // check world boundaries if( gpGlobals->time - pev->dmgtime > 5 || pev->renderamt < 64 || m_hEnemy == 0 || m_hOwner == 0 || pev->origin.x < -4096 || pev->origin.x > 4096 || pev->origin.y < -4096 || pev->origin.y > 4096 || pev->origin.z < -4096 || pev->origin.z > 4096 ) @@ -1238,6 +1283,17 @@ void CControllerHeadBall::HuntThink( void ) return; } + const Visual* visual = GetVisual(headBallLightVisual); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_ELIGHT ); + WRITE_SHORT( entindex() ); // entity, attachment + WRITE_VECTOR( pev->origin ); // origin + WRITE_COORD( pev->renderamt / 16 ); // radius + WRITE_COLOR( visual->rendercolor ); + WRITE_BYTE( RandomizeNumberFromRange(visual->life)*10 ); // life * 10 + WRITE_COORD( 0 ); // decay + MESSAGE_END(); + MovetoTarget( m_hEnemy->Center() ); if( ( m_hEnemy->Center() - pev->origin ).Length() < 64 ) @@ -1252,25 +1308,7 @@ void CControllerHeadBall::HuntThink( void ) pEntity->ApplyTraceAttack(pev, m_hOwner->pev, gSkillData.controllerDmgZap, pev->velocity, &tr, DMG_SHOCK); } - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( entindex() ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_SHORT( g_sModelIndexLaser ); - WRITE_BYTE( 0 ); // frame start - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 3 ); // life - WRITE_BYTE( 20 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); - + MakeTraceBeam(tr.vecEndPos); EmitSoundScriptAmbient(tr.vecEndPos, electroSoundScript); m_flNextAttack = gpGlobals->time + 3.0f; @@ -1310,24 +1348,21 @@ void CControllerHeadBall::Crawl( void ) Vector vecAim = Vector( RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ), RANDOM_FLOAT( -1.0f, 1.0f ) ).Normalize(); Vector vecPnt = pev->origin + pev->velocity * 0.3f + vecAim * 64.0f; - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( entindex() ); - WRITE_COORD( vecPnt.x ); - WRITE_COORD( vecPnt.y ); - WRITE_COORD( vecPnt.z ); - WRITE_SHORT( g_sModelIndexLaser ); - WRITE_BYTE( 0 ); // frame start - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 3 ); // life - WRITE_BYTE( 20 ); // width - WRITE_BYTE( 0 ); // noise - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); + MakeTraceBeam(vecPnt); +} + +void CControllerHeadBall::MakeTraceBeam(const Vector &vecSrc) +{ + const Visual* visual = GetVisual(headBallBeamVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMENTPOINT ); + WRITE_SHORT( entindex() ); + WRITE_VECTOR( vecSrc ); + WriteBeamVisual(visual); + MESSAGE_END(); + } } void CControllerHeadBall::BounceTouch( CBaseEntity *pOther ) @@ -1357,6 +1392,16 @@ class CControllerZapBall : public CBaseMonster static TYPEDESCRIPTION m_SaveData[]; static const NamedSoundScript electroSoundScript; + static const NamedVisual zapBallVisual; + +private: + int m_maxFrame; // don't save + void SetMaxFrame() { + if (pev->modelindex) + { + m_maxFrame = MODEL_FRAMES(pev->modelindex) - 1; + } + } }; LINK_ENTITY_TO_CLASS( controller_energy_ball, CControllerZapBall ) @@ -1377,6 +1422,11 @@ const NamedSoundScript CControllerZapBall::electroSoundScript = { "Controller.ZapElectro" }; +const NamedVisual CControllerZapBall::zapBallVisual = BuildVisual::Animated("Controller.ZapBall") + .RenderMode(kRenderTransAdd) + .Scale(0.5f) + .Mixin(&sharedEnergyBallVisual); + void CControllerZapBall::Spawn( void ) { Precache(); @@ -1384,13 +1434,7 @@ void CControllerZapBall::Spawn( void ) pev->movetype = MOVETYPE_FLY; pev->solid = SOLID_BBOX; - SET_MODEL( ENT( pev ), "sprites/xspark4.spr" ); - pev->rendermode = kRenderTransAdd; - pev->rendercolor.x = 255; - pev->rendercolor.y = 255; - pev->rendercolor.z = 255; - pev->renderamt = 255; - pev->scale = 0.5f; + ApplyVisualToEntity(this, GetVisual(zapBallVisual)); UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); UTIL_SetOrigin( pev, pev->origin ); @@ -1401,19 +1445,22 @@ void CControllerZapBall::Spawn( void ) m_hOwner = Instance( pev->owner ); pev->dmgtime = gpGlobals->time; // keep track of when ball spawned pev->nextthink = gpGlobals->time + 0.1f; + + SetMaxFrame(); } void CControllerZapBall::Precache( void ) { - PRECACHE_MODEL( "sprites/xspark4.spr" ); + RegisterVisual(zapBallVisual); RegisterAndPrecacheSoundScript(electroSoundScript); + SetMaxFrame(); } void CControllerZapBall::AnimateThink( void ) { pev->nextthink = gpGlobals->time + 0.1f; - pev->frame = ( (int)pev->frame + 1 ) % 11; + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate); if( gpGlobals->time - pev->dmgtime > 5 || pev->velocity.Length() < 10.0f ) { @@ -1509,10 +1556,37 @@ class CZapBallTrap : public CBaseEntity float RespawnTime() const { return pev->dmg_take > 0 ? pev->dmg_take : gSkillData.zaptrapRespawnTime; } + + virtual int Save( CSave &save ); + virtual int Restore( CRestore &restore ); + static TYPEDESCRIPTION m_SaveData[]; + + static const NamedVisual zapBallVisual; + +private: + int m_maxFrame; + float m_lastTime; + void SetMaxFrame() { + if (pev->modelindex) + { + m_maxFrame = MODEL_FRAMES(pev->modelindex) - 1; + } + } }; LINK_ENTITY_TO_CLASS( env_energy_ball_trap, CZapBallTrap ) +TYPEDESCRIPTION CZapBallTrap::m_SaveData[] = +{ + DEFINE_FIELD( CZapBallTrap, m_lastTime, FIELD_TIME ), +}; + +IMPLEMENT_SAVERESTORE( CZapBallTrap, CBaseEntity ) + +const NamedVisual CZapBallTrap::zapBallVisual = BuildVisual::Animated("ZapTrap.EnergyBall") + .RenderMode(kRenderTransAdd) + .Mixin(&sharedEnergyBallVisual); + void CZapBallTrap::KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "wait")) @@ -1534,6 +1608,8 @@ void CZapBallTrap::Precache() UTIL_PrecacheOther("controller_head_ball"); PRECACHE_SOUND(ZAPBALLTRAP_DETECT_SOUND); PRECACHE_SOUND(ZAPBALLTRAP_LAUNCH_SOUND); + + SetMaxFrame(); } void CZapBallTrap::Spawn() @@ -1543,11 +1619,7 @@ void CZapBallTrap::Spawn() pev->movetype = MOVETYPE_NONE; pev->solid = SOLID_NOT; - SET_MODEL(ENT( pev ), "sprites/xspark4.spr" ); - pev->rendermode = kRenderTransAdd; - pev->rendercolor.x = 255; - pev->rendercolor.y = 255; - pev->rendercolor.z = 255; + ApplyVisualToEntity(this, zapBallVisual); UTIL_SetSize(pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); UTIL_SetOrigin( pev, pev->origin ); @@ -1561,6 +1633,8 @@ void CZapBallTrap::Spawn() pev->effects |= EF_NODRAW; SetUse(&CZapBallTrap::EnableUse); } + + SetMaxFrame(); } void CZapBallTrap::EnableUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) @@ -1574,7 +1648,7 @@ void CZapBallTrap::EnableUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_ void CZapBallTrap::Animate() { - pev->frame = ( (int)pev->frame + 1 ) % 11; + pev->frame = AnimateWithFramerate(pev->frame, m_maxFrame, pev->framerate, &m_lastTime); } void CZapBallTrap::DetectThink() @@ -1633,6 +1707,7 @@ void CZapBallTrap::Materialize() pev->frame = 0; SetThink( &CZapBallTrap::DetectThink ); pev->nextthink = gpGlobals->time + 0.1f; + m_lastTime = gpGlobals->time; } bool CZapBallTrap::IncreaseAwareness(CBaseEntity *pTarget, int value) diff --git a/dlls/game.cpp b/dlls/game.cpp index ad26206cb..d1c34c081 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -24,6 +24,7 @@ #include "ammo_amounts.h" #include "inventory.h" #include "soundscripts.h" +#include "visuals.h" #include "followers.h" #include "savetitles.h" #include "vcs_info.h" @@ -1408,6 +1409,7 @@ void GameDLLInit( void ) LoadWarpballTemplates(); ReadInventorySpec(); ReadSoundScripts(); + ReadVisuals(); ReadFollowersDescription(); ReadSaveTitles(); @@ -2038,6 +2040,7 @@ void GameDLLInit( void ) g_engfuncs.pfnAddServerCommand("dump_precached_sounds", ReportPrecachedSounds); g_engfuncs.pfnAddServerCommand("dump_sound_replacements", ReportSoundReplacements); g_engfuncs.pfnAddServerCommand("dump_soundscripts", DumpSoundScripts); + g_engfuncs.pfnAddServerCommand("dump_visuals", DumpVisuals); } bool ItemsPickableByTouch() diff --git a/dlls/gargantua.cpp b/dlls/gargantua.cpp index 6038102f8..2b743e73a 100644 --- a/dlls/gargantua.cpp +++ b/dlls/gargantua.cpp @@ -36,6 +36,7 @@ #include "fx_flags.h" #include "locus.h" #include "common_soundscripts.h" +#include "visuals_utils.h" //========================================================= // Gargantua Monster @@ -87,9 +88,7 @@ struct StompParams Vector origin; Vector end; float speed; - Vector color; float damage; - float scale; edict_t* owner = nullptr; float soundAttenuation = 0.0f; }; @@ -108,13 +107,14 @@ class CStomp : public CBaseEntity virtual const char* StompSoundScript() { return stompSoundScript; } - virtual string_t StompSprite() { - return MAKE_STRING(GARG_STOMP_SPRITE_NAME); - } virtual string_t MyClassname() { return MAKE_STRING("garg_stomp"); } + static const NamedVisual stompVisual; + + const Visual* m_stompVisual; + private: // UNDONE: re-use this sprite list instead of creating new ones all the time // CSprite *m_pSprites[STOMP_SPRITE_COUNT]; @@ -122,6 +122,21 @@ class CStomp : public CBaseEntity LINK_ENTITY_TO_CLASS( garg_stomp, CStomp ) +const NamedSoundScript CStomp::stompSoundScript = { + CHAN_BODY, + {GARG_STOMP_BUZZ_SOUND}, + 55, + "Garg.Stomp", +}; + +const NamedVisual CStomp::stompVisual = BuildVisual("Garg.Stomp") + .Model(GARG_STOMP_SPRITE_NAME) + .Scale(1.0f) + .RenderColor(255, 255, 255) + .Alpha(255) + .RenderMode(kRenderTransAdd) + .RenderFx(kRenderFxFadeFast); + #define BABYGARG_STOMP_SPRITE_NAME "sprites/flare3.spr" class CBabyStomp : public CStomp @@ -135,22 +150,22 @@ class CBabyStomp : public CStomp const char* StompSoundScript() { return stompSoundScript; } - virtual string_t StompSprite() { - return MAKE_STRING(BABYGARG_STOMP_SPRITE_NAME); - } virtual string_t MyClassname() { return MAKE_STRING("babygarg_stomp"); } + + static const NamedVisual stompVisual; }; LINK_ENTITY_TO_CLASS( babygarg_stomp, CBabyStomp ) -const NamedSoundScript CStomp::stompSoundScript = { - CHAN_BODY, - {GARG_STOMP_BUZZ_SOUND}, - 55, - "Garg.Stomp", -}; +const NamedVisual CBabyStomp::stompVisual = BuildVisual("BabyGarg.Stomp") + .Model(BABYGARG_STOMP_SPRITE_NAME) + .Scale(0.5f) + .RenderColor(225, 170, 80) + .Alpha(255) + .RenderMode(kRenderTransAdd) + .RenderFx(kRenderFxFadeFast); #define SF_STOMPSHOOTER_FIRE_ONCE 1 @@ -223,9 +238,7 @@ void CStompShooter::Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE stompParams.origin = vecStart; stompParams.end = vecEnd; stompParams.speed = pev->speed; - stompParams.color = Vector(255, 255, 255); stompParams.damage = gSkillData.gargantuaDmgStomp; - stompParams.scale = pev->scale > 0 ? pev->scale : 1.0f; stompParams.owner = pOwner ? pOwner->edict() : NULL; stompParams.soundAttenuation = pev->armortype; @@ -253,9 +266,7 @@ void CStomp::SetStompParams(CStomp* pStomp, const StompParams &stompParams) pStomp->pev->scale = dir.Length(); pStomp->pev->movedir = dir.Normalize(); pStomp->pev->speed = stompParams.speed; - pStomp->pev->rendercolor = stompParams.color; pStomp->pev->dmg = stompParams.damage; - pStomp->pev->frags = stompParams.scale; pStomp->pev->owner = stompParams.owner; pStomp->pev->armortype = stompParams.soundAttenuation; } @@ -263,7 +274,6 @@ void CStomp::SetStompParams(CStomp* pStomp, const StompParams &stompParams) void CStomp::Spawn( void ) { Precache(); - pev->model = StompSprite(); pev->nextthink = gpGlobals->time; pev->classname = MyClassname(); pev->dmgtime = gpGlobals->time; @@ -283,7 +293,7 @@ void CStomp::Spawn( void ) void CStomp::Precache() { - PRECACHE_MODEL(GARG_STOMP_SPRITE_NAME); + m_stompVisual = RegisterVisual(stompVisual); RegisterAndPrecacheSoundScript(stompSoundScript); } @@ -321,7 +331,7 @@ void CStomp::Think( void ) float stompInterval = STOMP_INTERVAL; int numOfSprites = 2; int maxNumOfSprites = 8; - float spriteScale = pev->frags; + float spriteScale = m_stompVisual->scale; if (g_pGameRules->IsMultiplayer()) { stompInterval = STOMP_INTERVAL*2; @@ -339,7 +349,7 @@ void CStomp::Think( void ) for( int i = 0; i < numOfSprites && maxNumOfSprites > 0; i++ ) { maxNumOfSprites--; - CSprite *pSprite = CSprite::SpriteCreate( STRING(pev->model), pev->origin, TRUE ); + CSprite *pSprite = CreateSpriteFromVisual(m_stompVisual, pev->origin); if( pSprite ) { UTIL_TraceLine( pev->origin, pev->origin - Vector( 0, 0, 500 ), ignore_monsters, edict(), &tr ); @@ -348,7 +358,6 @@ void CStomp::Think( void ) // pSprite->AnimateAndDie( RANDOM_FLOAT( 8.0f, 12.0f ) ); pSprite->pev->nextthink = gpGlobals->time + 0.3f; pSprite->SetThink( &CBaseEntity::SUB_Remove ); - pSprite->SetTransparency( kRenderTransAdd, pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z, 255, kRenderFxFadeFast ); pSprite->SetScale(spriteScale); } } @@ -367,7 +376,7 @@ void CStomp::Think( void ) void CBabyStomp::Precache() { - PRECACHE_MODEL(BABYGARG_STOMP_SPRITE_NAME); + m_stompVisual = RegisterVisual(stompVisual); SoundScriptParamOverride paramOverride; paramOverride.OverridePitchAbsolute(65); @@ -386,12 +395,8 @@ void StreakSplash( const Vector &origin, const Vector &direction, int color, int { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, origin ); WRITE_BYTE( TE_STREAK_SPLASH ); - WRITE_COORD( origin.x ); // origin - WRITE_COORD( origin.y ); - WRITE_COORD( origin.z ); - WRITE_COORD( direction.x ); // direction - WRITE_COORD( direction.y ); - WRITE_COORD( direction.z ); + WRITE_VECTOR( origin ); // origin + WRITE_VECTOR( direction ); // direction WRITE_BYTE( color ); // Streak color 6 WRITE_SHORT( count ); // count WRITE_SHORT( speed ); @@ -404,7 +409,6 @@ class CGargantua : public CFollowingMonster public: void Spawn( void ); void Precache( void ); - void PrecacheImpl(); void UpdateOnRemove(); void SetYawSpeed( void ); int DefaultClassify( void ); @@ -475,17 +479,12 @@ class CGargantua : public CFollowingMonster virtual float FireAttackDamage(); virtual float StompAttackDamage(); virtual const char* DefaultModel(); - virtual const char* EyeSprite(); - virtual float EyeScale(); - virtual Vector EyeColor(); - virtual int MaxEyeBrightness(); virtual void FootEffect(); virtual void MakeStomp(const StompParams& stompParams); virtual void StompEffect(); virtual float FlameLength(); - virtual int BigFlameScale(); - virtual int SmallFlameScale(); - virtual void PrecacheSounds(); + virtual const Visual* GetWideFlameVisual(); + virtual const Visual* GetNarrowFlameVisual(); virtual void BreatheSound(); virtual void AttackSound(); virtual float FlameTimeDivider(); @@ -508,6 +507,12 @@ class CGargantua : public CFollowingMonster static const char *pRicSounds[]; + static const NamedVisual eyeVisual; + static const NamedVisual flameVisual; + static const NamedVisual bigFlameVisual; + static const NamedVisual smallFlameVisual; + static const NamedVisual flameLightVisual; + virtual void PlayAttackHitSound() { EmitSoundScript(attackHitSoundScript); } @@ -537,6 +542,9 @@ class CGargantua : public CFollowingMonster float m_flameX; // Flame thrower aim float m_flameY; float m_breatheTime; + + const Visual* m_eyeVisual; // this is accessed quite often so cache it + const Visual* m_flameVisual; // this is accessed quite often so cache it }; LINK_ENTITY_TO_CLASS( monster_gargantua, CGargantua ) @@ -637,6 +645,35 @@ const char *CGargantua::pRicSounds[] = "weapons/ric5.wav", }; +const NamedVisual CGargantua::eyeVisual = BuildVisual("Garg.Eye") + .Model(GARG_EYE_SPRITE_NAME) + .Scale(1.0f) + .RenderColor(255, 255, 255) + .Alpha(200) + .RenderMode(kRenderGlow) + .RenderFx(kRenderFxNoDissipation); + +const NamedVisual CGargantua::flameVisual = BuildVisual("Garg.FlameBase") + .Alpha(190) + .BeamScrollRate(20); + +const NamedVisual CGargantua::bigFlameVisual = BuildVisual("Garg.FlameWide") + .Model(GARG_BEAM_SPRITE_NAME) + .RenderColor(255, 130, 90) + .BeamWidth(240) + .Mixin(&CGargantua::flameVisual); + +const NamedVisual CGargantua::smallFlameVisual = BuildVisual("Garg.FlameNarrow") + .Model(GARG_BEAM_SPRITE2) + .RenderColor(0, 120, 255) + .BeamWidth(140) + .Mixin(&CGargantua::flameVisual); + +const NamedVisual CGargantua::flameLightVisual = BuildVisual("Garg.FlameLight") + .RenderColor(255, 255, 255) + .Life(0.2f) + .Radius(IntRange(32, 48)); + //========================================================= // AI Schedules Specific to this monster //========================================================= @@ -731,7 +768,7 @@ void CGargantua::EyeUpdate( void ) { if( m_pEyeGlow ) { - m_pEyeGlow->pev->renderamt = UTIL_Approach( m_eyeBrightness, m_pEyeGlow->pev->renderamt, MaxEyeBrightness()/8+1 ); + m_pEyeGlow->pev->renderamt = UTIL_Approach( m_eyeBrightness, m_pEyeGlow->pev->renderamt, m_eyeVisual->renderamt/8+1 ); if( m_pEyeGlow->pev->renderamt == 0 ) m_pEyeGlow->pev->effects |= EF_NODRAW; else @@ -755,9 +792,7 @@ void CGargantua::StompAttack( void ) stompParams.origin = vecStart; stompParams.end = trace.vecEndPos; stompParams.speed = 0; - stompParams.color = EyeColor(); stompParams.damage = StompAttackDamage(); - stompParams.scale = EyeScale(); stompParams.owner = edict(); MakeStomp(stompParams); @@ -776,12 +811,15 @@ void CGargantua::FlameCreate( void ) UTIL_MakeVectors( pev->angles ); + const Visual* bigFlameVisual = GetWideFlameVisual(); + const Visual* smallFlameVisual = GetNarrowFlameVisual(); + for( i = 0; i < 4; i++ ) { if( i < 2 ) - m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE_NAME, BigFlameScale() ); + m_pFlame[i] = CreateBeamFromVisual(bigFlameVisual); else - m_pFlame[i] = CBeam::BeamCreate( GARG_BEAM_SPRITE2, SmallFlameScale() ); + m_pFlame[i] = CreateBeamFromVisual(smallFlameVisual); if( m_pFlame[i] ) { int attach = i%2; @@ -792,13 +830,7 @@ void CGargantua::FlameCreate( void ) UTIL_TraceLine( posGun, vecEnd, dont_ignore_monsters, edict(), &trace ); m_pFlame[i]->PointEntInit( trace.vecEndPos, entindex() ); - if( i < 2 ) - m_pFlame[i]->SetColor( 255, 130, 90 ); - else - m_pFlame[i]->SetColor( 0, 120, 255 ); - m_pFlame[i]->SetBrightness( 190 ); m_pFlame[i]->SetFlags( BEAM_FSHADEIN ); - m_pFlame[i]->SetScrollRate( 20 ); // attachment is 1 based in SetEndAttachment m_pFlame[i]->SetEndAttachment( attach + 2 ); CSoundEnt::InsertSound( bits_SOUND_COMBAT, posGun, 384, 0.3 ); @@ -864,14 +896,8 @@ void CGargantua::FlameUpdate( void ) MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex() + 0x1000 * ( i + 2 ) ); // entity, attachment - WRITE_COORD( vecStart.x ); // origin - WRITE_COORD( vecStart.y ); - WRITE_COORD( vecStart.z ); - WRITE_COORD( RANDOM_FLOAT( 32.0f, 48.0f ) ); // radius - WRITE_BYTE( 255 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B - WRITE_BYTE( 2 ); // life * 10 + WRITE_VECTOR( vecStart ); // origin + WriteEntLightVisual(m_flameVisual); WRITE_COORD( 0 ); // decay MESSAGE_END(); } @@ -968,7 +994,7 @@ void CGargantua::PrescheduleThink( void ) EyeOff(); } else - EyeOn( MaxEyeBrightness() ); + EyeOn( m_eyeVisual->renderamt ); EyeUpdate(); CFollowingMonster::PrescheduleThink(); @@ -1032,11 +1058,9 @@ void CGargantua::Spawn() FollowingMonsterInit(); - m_pEyeGlow = CSprite::SpriteCreate( EyeSprite(), pev->origin, FALSE ); - const Vector eyeColor = EyeColor(); - m_pEyeGlow->SetTransparency( kRenderGlow, eyeColor.x, eyeColor.y, eyeColor.z, 0, kRenderFxNoDissipation ); + m_pEyeGlow = CreateSpriteFromVisual(m_eyeVisual, pev->origin); m_pEyeGlow->SetAttachment( edict(), 1 ); - m_pEyeGlow->SetScale( EyeScale() ); + m_pEyeGlow->SetBrightness(0); // start with eye off EyeOff(); m_seeTime = gpGlobals->time + 5; m_flameTime = gpGlobals->time + 2; @@ -1046,21 +1070,33 @@ void CGargantua::Spawn() // Precache - precaches all resources this monster needs //========================================================= void CGargantua::Precache() -{ - PrecacheImpl(); - UTIL_PrecacheOther("garg_stomp"); - m_GargGibModel = PRECACHE_MODEL( GibModel() ); -} - -void CGargantua::PrecacheImpl() { PrecacheMyModel( DefaultModel() ); - PRECACHE_MODEL( EyeSprite() ); - PRECACHE_MODEL( GARG_BEAM_SPRITE_NAME ); - PRECACHE_MODEL( GARG_BEAM_SPRITE2 ); + SoundScriptParamOverride paramOverride; + paramOverride.OverridePitchAbsolute(IntRange(50, 65)); + RegisterAndPrecacheSoundScript(attackHitSoundScript, NPC::attackHitSoundScript, paramOverride); + RegisterAndPrecacheSoundScript(attackMissSoundScript, NPC::attackMissSoundScript, paramOverride); + RegisterAndPrecacheSoundScript(flameOnSoundScript); + RegisterAndPrecacheSoundScript(flameRunSoundScript); + RegisterAndPrecacheSoundScript(flameOffSoundScript); + RegisterAndPrecacheSoundScript(footSoundScript); + RegisterAndPrecacheSoundScript(idleSoundScript); + RegisterAndPrecacheSoundScript(alertSoundScript); + RegisterAndPrecacheSoundScript(painSoundScript); + RegisterAndPrecacheSoundScript(attackSoundScript); + RegisterAndPrecacheSoundScript(stompSoundScript); + RegisterAndPrecacheSoundScript(breathSoundScript); + + PRECACHE_SOUND_ARRAY( pRicSounds ); + + m_eyeVisual = RegisterVisual(eyeVisual); + RegisterVisual(bigFlameVisual); + RegisterVisual(smallFlameVisual); + m_flameVisual = RegisterVisual(flameLightVisual); - PrecacheSounds(); + UTIL_PrecacheOther("garg_stomp"); + m_GargGibModel = PRECACHE_MODEL( GibModel() ); } void CGargantua::UpdateOnRemove() @@ -1573,26 +1609,6 @@ const char* CGargantua::DefaultModel() return "models/garg.mdl"; } -const char* CGargantua::EyeSprite() -{ - return GARG_EYE_SPRITE_NAME; -} - -float CGargantua::EyeScale() -{ - return 1.0f; -} - -Vector CGargantua::EyeColor() -{ - return Vector(255, 255, 255); -} - -int CGargantua::MaxEyeBrightness() -{ - return 200; -} - void CGargantua::FootEffect() { UTIL_ScreenShake( pev->origin, 4.0, 3.0, 1.0, 750 ); @@ -1615,34 +1631,14 @@ float CGargantua::FlameLength() return GARG_FLAME_LENGTH; } -int CGargantua::BigFlameScale() +const Visual* CGargantua::GetWideFlameVisual() { - return 240; + return GetVisual(bigFlameVisual); } -int CGargantua::SmallFlameScale() +const Visual* CGargantua::GetNarrowFlameVisual() { - return 140; -} - -void CGargantua::PrecacheSounds() -{ - SoundScriptParamOverride paramOverride; - paramOverride.OverridePitchAbsolute(IntRange(50, 65)); - RegisterAndPrecacheSoundScript(attackHitSoundScript, NPC::attackHitSoundScript, paramOverride); - RegisterAndPrecacheSoundScript(attackMissSoundScript, NPC::attackMissSoundScript, paramOverride); - RegisterAndPrecacheSoundScript(flameOnSoundScript); - RegisterAndPrecacheSoundScript(flameRunSoundScript); - RegisterAndPrecacheSoundScript(flameOffSoundScript); - RegisterAndPrecacheSoundScript(footSoundScript); - RegisterAndPrecacheSoundScript(idleSoundScript); - RegisterAndPrecacheSoundScript(alertSoundScript); - RegisterAndPrecacheSoundScript(painSoundScript); - RegisterAndPrecacheSoundScript(attackSoundScript); - RegisterAndPrecacheSoundScript(stompSoundScript); - RegisterAndPrecacheSoundScript(breathSoundScript); - - PRECACHE_SOUND_ARRAY( pRicSounds ); + return GetVisual(smallFlameVisual); } void CGargantua::BreatheSound() @@ -1884,9 +1880,7 @@ void CSmoker::Think( void ) WRITE_SHORT( pev->frags ); WRITE_BYTE( pev->rendermode ); WRITE_BYTE( pev->renderamt ); - WRITE_BYTE( pev->rendercolor.x ); - WRITE_BYTE( pev->rendercolor.y ); - WRITE_BYTE( pev->rendercolor.z ); + WRITE_COLOR( pev->rendercolor ); WRITE_SHORT( pev->armorvalue * 10 ); if (directed) { @@ -2006,11 +2000,7 @@ void SpawnExplosion( Vector center, float randomRange, float time, int magnitude class CBabyGargantua : public CGargantua { public: - void Precache() - { - PrecacheImpl(); - UTIL_PrecacheOther("babygarg_stomp"); - } + void Precache(); bool IsEnabledInMod() { return g_modFeatures.IsMonsterEnabled("babygarg"); } void SetYawSpeed( void ); const char* ReverseRelationshipModel() { return "models/babygargf.mdl"; } @@ -2033,21 +2023,16 @@ class CBabyGargantua : public CGargantua Vector DefaultMaxHullSize() { return Vector( 32.0f, 32.0f, 64.0f ); } protected: - void PrecacheSounds(); float DefaultHealth(); float FireAttackDamage(); float StompAttackDamage(); const char* DefaultModel(); - const char* EyeSprite(); - float EyeScale(); - Vector EyeColor(); - int MaxEyeBrightness(); void FootEffect(); void MakeStomp(const StompParams& stompParams); void StompEffect(); float FlameLength(); - int BigFlameScale(); - int SmallFlameScale(); + const Visual* GetWideFlameVisual(); + const Visual* GetNarrowFlameVisual(); void BreatheSound(); void AttackSound(); float FlameTimeDivider(); @@ -2067,6 +2052,11 @@ class CBabyGargantua : public CGargantua static const NamedSoundScript stompSoundScript; static const NamedSoundScript breathSoundScript; + static const NamedVisual eyeVisual; + static const NamedVisual bigFlameVisual; + static const NamedVisual smallFlameVisual; + static const NamedVisual flameLightVisual; + virtual void PlayAttackHitSound() { EmitSoundScript(attackHitSoundScript); } @@ -2147,8 +2137,29 @@ const NamedSoundScript CBabyGargantua::breathSoundScript = { "BabyGarg.Breath" }; -void CBabyGargantua::PrecacheSounds() +const NamedVisual CBabyGargantua::eyeVisual = BuildVisual("BabyGarg.Eye") + .Model("sprites/flare3.spr") + .Scale(0.5f) + .RenderColor(225, 170, 80) + .Alpha(255) + .RenderMode(kRenderGlow) + .RenderFx(kRenderFxNoDissipation); + +const NamedVisual CBabyGargantua::bigFlameVisual = BuildVisual("BabyGarg.FlameWide") + .BeamWidth(120) + .Mixin(&CGargantua::bigFlameVisual); + +const NamedVisual CBabyGargantua::smallFlameVisual = BuildVisual("BabyGarg.FlameNarrow") + .BeamWidth(70) + .Mixin(&CGargantua::smallFlameVisual); + +const NamedVisual CBabyGargantua::flameLightVisual = BuildVisual("BabyGarg.FlameLight") + .Mixin(&CGargantua::flameLightVisual); + +void CBabyGargantua::Precache() { + PrecacheMyModel( DefaultModel() ); + SoundScriptParamOverride paramOverride; paramOverride.OverridePitchAbsolute(IntRange(60, 75)); RegisterAndPrecacheSoundScript(attackHitSoundScript, NPC::attackHitSoundScript, paramOverride); @@ -2166,6 +2177,13 @@ void CBabyGargantua::PrecacheSounds() RegisterAndPrecacheSoundScript(attackSoundScript); RegisterAndPrecacheSoundScript(stompSoundScript); RegisterAndPrecacheSoundScript(breathSoundScript); + + m_eyeVisual = RegisterVisual(eyeVisual); + RegisterVisual(bigFlameVisual); + RegisterVisual(smallFlameVisual); + m_flameVisual = RegisterVisual(flameLightVisual); + + UTIL_PrecacheOther("babygarg_stomp"); } void CBabyGargantua::StartTask(Task_t *pTask) @@ -2246,26 +2264,6 @@ const char* CBabyGargantua::DefaultModel() return "models/babygarg.mdl"; } -const char* CBabyGargantua::EyeSprite() -{ - return "sprites/flare3.spr"; -} - -float CBabyGargantua::EyeScale() -{ - return 0.5; -} - -Vector CBabyGargantua::EyeColor() -{ - return Vector(225, 170, 80); -} - -int CBabyGargantua::MaxEyeBrightness() -{ - return 255; -} - int CBabyGargantua::TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType) { return CFollowingMonster::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType); @@ -2310,14 +2308,14 @@ float CBabyGargantua::FlameLength() return GARG_FLAME_LENGTH / 2; } -int CBabyGargantua::BigFlameScale() +const Visual* CBabyGargantua::GetWideFlameVisual() { - return 120; + return GetVisual(bigFlameVisual); } -int CBabyGargantua::SmallFlameScale() +const Visual* CBabyGargantua::GetNarrowFlameVisual() { - return 70; + return GetVisual(smallFlameVisual); } void CBabyGargantua::BreatheSound() diff --git a/dlls/gonome.cpp b/dlls/gonome.cpp index 04e80b632..d2ce657d9 100644 --- a/dlls/gonome.cpp +++ b/dlls/gonome.cpp @@ -63,24 +63,31 @@ class CGonomeGuts : public CSquidSpit { public: - void Spawn(void); - void PrecacheSounds(); + void Spawn(); + void Precache(); void Touch(CBaseEntity *pOther); static constexpr const char* spitTouchSoundScript = "Gonome.SpitTouch"; static constexpr const char* spitHitSoundScript = "Gonome.SpitHit"; + + static const NamedVisual gutsVisual; }; LINK_ENTITY_TO_CLASS( gonomeguts, CGonomeGuts ) +const NamedVisual CGonomeGuts::gutsVisual = BuildVisual::Animated("Gonome.Guts") + .Model("sprites/bigspit.spr") + .RenderProps(kRenderTransAlpha, Color(255, 0, 0)) + .Scale(0.5f); + void CGonomeGuts::Spawn() { - SpawnHelper("gonomeguts"); - pev->rendercolor.x = 255; + SpawnHelper("gonomeguts", gutsVisual); } -void CGonomeGuts::PrecacheSounds() +void CGonomeGuts::Precache() { + RegisterVisual(gutsVisual); RegisterAndPrecacheSoundScript(spitTouchSoundScript, NPC::spitTouchSoundScript); RegisterAndPrecacheSoundScript(spitHitSoundScript, NPC::spitHitSoundScript); } diff --git a/dlls/hornet.cpp b/dlls/hornet.cpp index fd1fb935f..8e3c44f17 100644 --- a/dlls/hornet.cpp +++ b/dlls/hornet.cpp @@ -23,9 +23,9 @@ #include "soundent.h" #include "hornet.h" #include "gamerules.h" +#include "visuals_utils.h" -int iHornetTrail; -int iHornetPuff; +extern int gmsgSprite; LINK_ENTITY_TO_CLASS( hornet, CHornet ) @@ -57,6 +57,26 @@ const NamedSoundScript CHornet::dieSoundScript = { "Hornet.Die" }; +const NamedVisual CHornet::sharedHornetTrailVisual = BuildVisual("Hornet.TrailBase") + .Model("sprites/laserbeam.spr") + .Alpha(128) + .BeamParams(2, 0) + .Life(1.0f); + +const NamedVisual CHornet::hornetTrailVisual = BuildVisual("Hornet.Trail") + .RenderColor(179, 39, 14) + .Mixin(&CHornet::sharedHornetTrailVisual); + +const NamedVisual CHornet::hornetTrailAltVisual = BuildVisual("Hornet.TrailAlt") + .RenderColor(255, 128, 0) + .Mixin(&CHornet::sharedHornetTrailVisual); + +const NamedVisual CHornet::hornetPuffVisual = BuildVisual("Hornet.Puff") + .Model("sprites/muz1.spr") + .RenderMode(kRenderTransAdd) + .Scale(0.2f) + .Alpha(128); + //========================================================= // don't let hornets gib, ever. //========================================================= @@ -135,8 +155,9 @@ void CHornet::Precache() RegisterAndPrecacheSoundScript(buzzSoundScript); RegisterAndPrecacheSoundScript(dieSoundScript); - iHornetPuff = PRECACHE_MODEL( "sprites/muz1.spr" ); - iHornetTrail = PRECACHE_MODEL( "sprites/laserbeam.spr" ); + RegisterVisual(hornetPuffVisual); + RegisterVisual(hornetTrailVisual); + RegisterVisual(hornetTrailAltVisual); } //========================================================= @@ -232,29 +253,23 @@ old colors */ // trail - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT( entindex() ); // entity - WRITE_SHORT( iHornetTrail ); // model - WRITE_BYTE( 10 ); // life - WRITE_BYTE( 2 ); // width - - switch( m_iHornetType ) - { - case HORNET_TYPE_RED: - WRITE_BYTE( 179 ); // r, g, b - WRITE_BYTE( 39 ); // r, g, b - WRITE_BYTE( 14 ); // r, g, b - break; - case HORNET_TYPE_ORANGE: - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 128 ); // r, g, b - WRITE_BYTE( 0 ); // r, g, b - break; - } - - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); + const Visual* visual; + if (m_iHornetType == HORNET_TYPE_RED) + { + visual = GetVisual(hornetTrailVisual); + } + else + { + visual = GetVisual(hornetTrailAltVisual); + } + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT( entindex() ); // entity + WriteBeamFollowVisual( visual ); + MESSAGE_END(); + } } //========================================================= @@ -347,16 +362,14 @@ void CHornet::TrackTarget( void ) { if( flDelta >= 0.4f && ( pev->origin - m_vecEnemyLKP ).Length() <= 300 ) { - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); - WRITE_BYTE( TE_SPRITE ); - WRITE_COORD( pev->origin.x ); // pos - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_SHORT( iHornetPuff ); // model - // WRITE_BYTE( 0 ); // life * 10 - WRITE_BYTE( 2 ); // size * 10 - WRITE_BYTE( 128 ); // brightness - MESSAGE_END(); + const Visual* visual = GetVisual(hornetPuffVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_PVS, gmsgSprite, pev->origin ); + WRITE_VECTOR( pev->origin ); // pos + WriteSpriteVisual(visual); + MESSAGE_END(); + } EmitSoundScript(buzzSoundScript); pev->velocity = pev->velocity * 2.0f; diff --git a/dlls/hornet.h b/dlls/hornet.h index 61f48804a..a4058a1f1 100644 --- a/dlls/hornet.h +++ b/dlls/hornet.h @@ -30,8 +30,6 @@ #define HORNET_ORANGE_SPEED (float)800 #define HORNET_BUZZ_VOLUME (float)0.8 -extern int iHornetPuff; - //========================================================= // Hornet - this is the projectile that the Alien Grunt fires. //========================================================= @@ -63,5 +61,10 @@ class CHornet : public CBaseMonster static const NamedSoundScript buzzSoundScript; static const NamedSoundScript dieSoundScript; + + static const NamedVisual sharedHornetTrailVisual; + static const NamedVisual hornetTrailVisual; + static const NamedVisual hornetTrailAltVisual; + static const NamedVisual hornetPuffVisual; }; #endif // HORNET_H diff --git a/dlls/houndeye.cpp b/dlls/houndeye.cpp index e6c5d95fe..5c3fef426 100644 --- a/dlls/houndeye.cpp +++ b/dlls/houndeye.cpp @@ -26,6 +26,7 @@ #include "squadmonster.h" #include "soundent.h" #include "game.h" +#include "visuals_utils.h" // houndeye does 20 points of damage spread over a sphere 384 units in diameter, and each additional // squad member increases the BASE damage by 110%, per the spec. @@ -120,7 +121,7 @@ class CHoundeye : public CSquadMonster void Activate(); int LookupActivity(int activity); void SetActivity( Activity NewActivity ); - void WriteBeamColor( void ); + const Visual* GetWaveVisual(); BOOL CheckRangeAttack1( float flDot, float flDist ); BOOL FValidateHintType( short sHint ); BOOL FCanActiveIdle( void ); @@ -144,7 +145,6 @@ class CHoundeye : public CSquadMonster Vector DefaultMinHullSize() { return Vector( -16.0f, -16.0f, 0.0f ); } Vector DefaultMaxHullSize() { return Vector( 16.0f, 16.0f, 36.0f ); } - int m_iSpriteTexture; short m_iAsleep;// some houndeyes sleep in idle mode if this is set, the houndeye is lying down short m_iBlink; Vector m_vecPackCenter; // the center of the pack. The leader maintains this by averaging the origins of all pack members. @@ -159,6 +159,12 @@ class CHoundeye : public CSquadMonster static const NamedSoundScript anger1SoundScript; static const NamedSoundScript anger2SoundScript; + + static const NamedVisual waveVisual; + static const NamedVisual wave1Visual; + static const NamedVisual wave2Visual; + static const NamedVisual wave3Visual; + static const NamedVisual wave4Visual; }; LINK_ENTITY_TO_CLASS( monster_houndeye, CHoundeye ) @@ -229,6 +235,28 @@ const NamedSoundScript CHoundeye::anger2SoundScript = { "HoundEye.Anger2" }; +const NamedVisual CHoundeye::waveVisual = BuildVisual("Houndeye.WaveBase") + .Model("sprites/shockwave.spr") + .Life(0.2f) + .BeamParams(16, 0) + .Alpha(255); + +const NamedVisual CHoundeye::wave1Visual = BuildVisual("Houndeye.Wave1") + .RenderColor(188, 220, 255) + .Mixin(&CHoundeye::waveVisual); + +const NamedVisual CHoundeye::wave2Visual = BuildVisual("Houndeye.Wave2") + .RenderColor(101, 133, 221) + .Mixin(&CHoundeye::waveVisual); + +const NamedVisual CHoundeye::wave3Visual = BuildVisual("Houndeye.Wave3") + .RenderColor(67, 85, 255) + .Mixin(&CHoundeye::waveVisual); + +const NamedVisual CHoundeye::wave4Visual = BuildVisual("Houndeye.Wave4") + .RenderColor(62, 33, 211) + .Mixin(&CHoundeye::waveVisual); + //========================================================= // Classify - indicates this monster's place in the // relationship table. @@ -444,7 +472,11 @@ void CHoundeye::Precache() RegisterAndPrecacheSoundScript(anger1SoundScript); RegisterAndPrecacheSoundScript(anger2SoundScript); - m_iSpriteTexture = PRECACHE_MODEL( "sprites/shockwave.spr" ); + RegisterVisual(waveVisual); + RegisterVisual(wave1Visual); + RegisterVisual(wave2Visual); + RegisterVisual(wave3Visual); + RegisterVisual(wave4Visual); } //========================================================= @@ -504,10 +536,8 @@ void CHoundeye::PainSound( void ) // WriteBeamColor - writes a color vector to the network // based on the size of the group. //========================================================= -void CHoundeye::WriteBeamColor( void ) +const Visual* CHoundeye::GetWaveVisual() { - BYTE bRed, bGreen, bBlue; - const int squadSize = SquadCount(); switch( squadSize ) { @@ -516,32 +546,15 @@ void CHoundeye::WriteBeamColor( void ) case 0: case 1: // solo houndeye - weakest beam - bRed = 188; - bGreen = 220; - bBlue = 255; - break; + return GetVisual(wave1Visual); case 2: - // no case for 0 or 1, cause those are impossible for monsters in Squads. - bRed = 101; - bGreen = 133; - bBlue = 221; - break; + return GetVisual(wave2Visual); case 3: - bRed = 67; - bGreen = 85; - bBlue = 255; - break; + return GetVisual(wave3Visual); case 4: case 5: - bRed = 62; - bGreen = 33; - bBlue = 211; - break; + return GetVisual(wave4Visual); } - - WRITE_BYTE( bRed ); - WRITE_BYTE( bGreen ); - WRITE_BYTE( bBlue ); } //========================================================= @@ -554,6 +567,8 @@ void CHoundeye::SonicAttack( void ) EmitSoundScript(blastSoundScript); + const Visual* visual = GetWaveVisual(); + // blast circles MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); @@ -563,17 +578,7 @@ void CHoundeye::SonicAttack( void ) WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z + 16.0f + HOUNDEYE_MAX_ATTACK_RADIUS / 0.2f ); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed + WriteBeamVisual(visual); MESSAGE_END(); MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); @@ -584,17 +589,7 @@ void CHoundeye::SonicAttack( void ) WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z + 16.0f + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2.0f ) / 0.2f ); // reach damage radius over .3 seconds - WRITE_SHORT( m_iSpriteTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 0 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 16 ); // width - WRITE_BYTE( 0 ); // noise - - WriteBeamColor(); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed + WriteBeamVisual(visual); MESSAGE_END(); CBaseEntity *pEntity = NULL; diff --git a/dlls/islave.cpp b/dlls/islave.cpp index ee3dfff1e..56ff170b1 100644 --- a/dlls/islave.cpp +++ b/dlls/islave.cpp @@ -29,6 +29,7 @@ #include "mod_features.h" #include "game.h" #include "common_soundscripts.h" +#include "visuals_utils.h" #define bits_MEMORY_ISLAVE_PROVOKED bits_MEMORY_CUSTOM1 #define bits_MEMORY_ISLAVE_REVIVED bits_MEMORY_CUSTOM2 @@ -79,21 +80,13 @@ enum #define ISLAVE_HAND_SPRITE_NAME "sprites/glow02.spr" -#define ISLAVE_ZAP_RED 180 -#define ISLAVE_ZAP_GREEN 255 -#define ISLAVE_ZAP_BLUE 96 +constexpr Color VortigauntZapBeamColor = Color(180, 255, 96); +constexpr Color VortigauntZapBeamLeaderColor = Color(150, 255, 120); -#define ISLAVE_LEADER_ZAP_RED 150 -#define ISLAVE_LEADER_ZAP_GREEN 255 -#define ISLAVE_LEADER_ZAP_BLUE 120 +constexpr Color VortigauntArmBeamColor = Color(96, 128, 16); +constexpr Color VortigauntArmBeamLeaderColor = Color(72, 180, 72); -#define ISLAVE_ARMBEAM_RED 96 -#define ISLAVE_ARMBEAM_GREEN 128 -#define ISLAVE_ARMBEAM_BLUE 16 - -#define ISLAVE_LEADER_ARMBEAM_RED 72 -#define ISLAVE_LEADER_ARMBEAM_GREEN 180 -#define ISLAVE_LEADER_ARMBEAM_BLUE 72 +constexpr Color VortigauntBeamLightColor = Color(255, 180, 96); #define ISLAVE_LIGHT_RED 255 #define ISLAVE_LIGHT_GREEN 180 @@ -180,6 +173,9 @@ class CChargeToken : public CBaseEntity EHANDLE m_hTarget; static const NamedSoundScript suitOnSoundScript; + + static const NamedVisual tokenVisual; + static const NamedVisual tokenLightVisual; }; LINK_ENTITY_TO_CLASS( charge_token, CChargeToken ) @@ -199,13 +195,20 @@ const NamedSoundScript CChargeToken::suitOnSoundScript = { "Vortigaunt.SuitOn" }; +const NamedVisual CChargeToken::tokenVisual = BuildVisual::Animated("Vortigaunt.ChargeToken") + .Model("sprites/xspark1.spr") + .RenderProps(kRenderTransAdd, Color(255, 255, 255), 225); + +const NamedVisual CChargeToken::tokenLightVisual = BuildVisual("Vortigaunt.ChargeTokenLight") + .RenderColor(0, 255, 255) + .Radius(30); + void CChargeToken::Spawn() { pev->classname = MAKE_STRING("charge_token"); Precache(); - SET_MODEL( ENT(pev), "sprites/xspark1.spr" ); - pev->rendermode = kRenderTransAdd; - pev->renderamt = 225; + + ApplyVisualToEntity(this, GetVisual(tokenVisual)); pev->movetype = MOVETYPE_NONE; pev->solid = SOLID_NOT; @@ -222,7 +225,8 @@ void CChargeToken::Spawn() void CChargeToken::Precache() { - PRECACHE_MODEL("sprites/xspark1.spr"); + RegisterVisual(tokenVisual); + RegisterVisual(tokenLightVisual); RegisterAndPrecacheSoundScript(suitOnSoundScript); } @@ -253,13 +257,7 @@ void CChargeToken::AnimateThink() void CChargeToken::Animate() { - if( pev->frame++ ) - { - if( pev->frame > pev->frags ) - { - pev->frame = 0; - } - } + pev->frame = AnimateWithFramerate(pev->frame, pev->frags, pev->framerate); } void CChargeToken::HuntThink() @@ -339,16 +337,14 @@ void CChargeToken::Launch(CBaseEntity* pTarget, const Vector& pos) void CChargeToken::MakeEntLight(int timeDs) { + const Visual* visual = GetVisual(tokenLightVisual); + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); WRITE_BYTE( TE_ELIGHT ); WRITE_SHORT( entindex() ); // entity, attachment - WRITE_COORD( pev->origin.x ); // origin - WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z ); - WRITE_COORD( pev->renderamt / 8 ); // radius - WRITE_BYTE( 0 ); // R - WRITE_BYTE( 255 ); // G - WRITE_BYTE( 255 ); // B + WRITE_VECTOR( pev->origin ); // origin + WRITE_COORD( RandomizeNumberFromRange(visual->radius) * pev->renderamt / 225 ); // radius + WRITE_COLOR( visual->rendercolor ); WRITE_BYTE( timeDs ); // life * 10 WRITE_COORD( 0 ); // decay MESSAGE_END(); @@ -428,7 +424,7 @@ class CISlave : public CFollowingMonster void RemoveHandGlows(); void RemoveChargeToken(); void CoilBeam(); - void MakeDynamicLight(const Vector& vecSrc, int radius, int t); + void MakeDynamicLight(const Vector& vecSrc, const char* visualName, int t); Vector HandPosition(int side); @@ -448,9 +444,6 @@ class CISlave : public CFollowingMonster int HealOther(CBaseEntity* pEntity); bool CanSpawnFamiliar(); - Vector GetZapColor(); - Vector GetArmBeamColor(int& brightness); - inline int AttachmentFromSide(int side) { return side < 0 ? 2 : 1; } @@ -525,9 +518,6 @@ class CISlave : public CFollowingMonster CChargeToken* m_chargeToken; - int m_iLightningTexture; - int m_iTrailTexture; - static const NamedSoundScript painSoundScript; static const NamedSoundScript dieSoundScript; static constexpr const char* attackHitSoundScript = "Vortigaunt.AttackHit"; @@ -539,6 +529,25 @@ class CISlave : public CFollowingMonster static const NamedSoundScript idleZapSoundScript; static const NamedSoundScript summonStartSoundScript; static const NamedSoundScript summonEndSoundScript; + + static const NamedVisual zapBeamColorVisual; + static const NamedVisual armBeamColorVisual; + static const NamedVisual beamLightColorVisual; + + static const NamedVisual zapBeamVisual; + static const NamedVisual powerupBeamVisual; + static const NamedVisual revivalBeamVisual; + static const NamedVisual summonBeamVisual; + static const NamedVisual idleBeamVisual; + static const NamedVisual coilBeamVisual; + static const NamedVisual trailBeamVisual; + + static const NamedVisual summonSpriteVisual; + static const NamedVisual handGlowVisual; + + static const NamedVisual powerupLightVisual; + static const NamedVisual idleLightVisual; + static const NamedVisual summonLightVisual; }; LINK_ENTITY_TO_CLASS( monster_alien_slave, CISlave ) @@ -639,6 +648,87 @@ const NamedSoundScript CISlave::summonEndSoundScript = { "Vortigaunt.SummonEnd", }; +const NamedVisual CISlave::zapBeamColorVisual = BuildVisual("Vortigaunt.ZapBeamColor") + .RenderColor(VortigauntZapBeamColor); + +const NamedVisual CISlave::armBeamColorVisual = BuildVisual("Vortigaunt.ArmBeamColor") + .RenderColor(VortigauntArmBeamColor); + +const NamedVisual CISlave::beamLightColorVisual = BuildVisual("Vortigaunt.BeamLightColor") + .RenderColor(VortigauntBeamLightColor); + +const NamedVisual CISlave::zapBeamVisual = BuildVisual("Vortigaunt.ZapBeam") + .Model("sprites/lgtning.spr") + .Alpha(255) + .BeamParams(50, 20) + .Mixin(&CISlave::zapBeamColorVisual); + +const NamedVisual CISlave::powerupBeamVisual = BuildVisual("Vortigaunt.PowerupBeam") + .Model("sprites/lgtning.spr") + .Alpha(64) + .BeamParams(30, 80) + .Mixin(&CISlave::armBeamColorVisual); + +const NamedVisual CISlave::revivalBeamVisual = BuildVisual("Vortigaunt.RevivalBeam") + .Model("sprites/lgtning.spr") + .Alpha(255) + .BeamParams(30, 80) + .Mixin(&CISlave::zapBeamColorVisual); + +const NamedVisual CISlave::summonBeamVisual = BuildVisual("Vortigaunt.SummonBeam") + .Model("sprites/lgtning.spr") + .Alpha(192) + .BeamParams(30, 80) + .Mixin(&CISlave::zapBeamColorVisual); + +const NamedVisual CISlave::idleBeamVisual = BuildVisual("Vortigaunt.IdleBeam") + .Model("sprites/lgtning.spr") + .Alpha(64) + .Framerate(10.0f) + .BeamParams(30, 80, 10) + .Life(FloatRange(0.8f, 1.5f)) + .Mixin(&CISlave::armBeamColorVisual); + +const NamedVisual CISlave::coilBeamVisual = BuildVisual("Vortigaunt.CoilBeam") + .Model("sprites/lgtning.spr") + .Alpha(255) + .Framerate(10.0f) + .BeamParams(128, 20) + .Life(0.2f) + .Mixin(&CISlave::zapBeamColorVisual); + +const NamedVisual CISlave::trailBeamVisual = BuildVisual("Vortigaunt.MeleeTrailBeam") + .Model("sprites/plasma.spr") + .Alpha(128) + .BeamParams(3, 0) + .Life(0.5f) + .Mixin(&CISlave::armBeamColorVisual); + +const NamedVisual CISlave::summonSpriteVisual = BuildVisual("Vortigaunt.SummonSprite") + .Model("sprites/bexplo.spr") + .RenderProps(kRenderTransAdd, VortigauntArmBeamColor, 255, kRenderFxNoDissipation) + .Framerate(20.0f); + +const NamedVisual CISlave::handGlowVisual = BuildVisual("Vortigaunt.HandGlow") + .Model("sprites/glow02.spr") + .RenderMode(kRenderTransAdd) + .Alpha(224) + .RenderFx(kRenderFxNoDissipation) + .Scale(0.25f) + .Mixin(&CISlave::zapBeamColorVisual); + +const NamedVisual CISlave::powerupLightVisual = BuildVisual("Vortigaunt.PowerupLight") + .Radius(120) + .Mixin(&CISlave::beamLightColorVisual); + +const NamedVisual CISlave::idleLightVisual = BuildVisual("Vortigaunt.IdleLight") + .Radius(80) + .Mixin(&CISlave::beamLightColorVisual); + +const NamedVisual CISlave::summonLightVisual = BuildVisual("Vortigaunt.SummonLight") + .Radius(100) + .Mixin(&CISlave::beamLightColorVisual); + //========================================================= // Classify - indicates this monster's place in the // relationship table. @@ -714,7 +804,7 @@ void CISlave::IdleSound( void ) UTIL_MakeAimVectors( pev->angles ); Vector vecSrc = pev->origin + gpGlobals->v_right * 2 * side; - MakeDynamicLight(vecSrc, 8, 10); + MakeDynamicLight(vecSrc, idleLightVisual, 10); EmitSoundScript(idleZapSoundScript); } @@ -864,7 +954,7 @@ void CISlave::HandleAnimEvent( MonsterEvent_t *pEvent ) if( m_iBeams == 0 ) { Vector vecSrc = pev->origin + gpGlobals->v_forward * 2; - MakeDynamicLight(vecSrc, 12, (int)(20/pev->framerate)); + MakeDynamicLight(vecSrc, powerupLightVisual, (int)(20/pev->framerate)); } if( CanRevive() ) { @@ -1135,7 +1225,7 @@ void CISlave::StartTask( Task_t *pTask ) EmitSoundScript(summonStartSoundScript); UTIL_MakeAimVectors( pev->angles ); Vector vecSrc = pev->origin + gpGlobals->v_forward * 8; - MakeDynamicLight(vecSrc, 10, 15); + MakeDynamicLight(vecSrc, summonLightVisual, 15); HandsGlowOn(); CreateSummonBeams(); } @@ -1272,12 +1362,7 @@ void CISlave::SpawnFamiliar(const char *entityName, const Vector &origin, int hu CBaseMonster *pNewMonster = pNew->MyMonsterPointer( ); Remember(bits_MEMORY_ISLAVE_FAMILIAR_IS_ALIVE); - CSprite *pSpr = CSprite::SpriteCreate( ISLAVE_SPAWNFAMILIAR_SPRITE, origin, TRUE, SF_SPRITE_ONCE_AND_REMOVE ); - if (pSpr) - { - pSpr->pev->framerate = 20.0f; - pSpr->SetTransparency( kRenderTransAdd, ISLAVE_ARMBEAM_RED, ISLAVE_ARMBEAM_GREEN, ISLAVE_ARMBEAM_BLUE, 255, kRenderFxNoDissipation ); - } + CreateSpriteFromVisual(GetVisual(summonSpriteVisual), origin, SF_SPRITE_ONCE_AND_REMOVE); EmitSoundScript(summonEndSoundScript); SetBits( pNew->pev->spawnflags, SF_MONSTER_FALL_TO_GROUND ); @@ -1297,9 +1382,8 @@ void CISlave::SpawnFamiliar(const char *entityName, const Vector &origin, int hu CSprite* CISlave::CreateHandGlow(int attachment) { - CSprite* handSprite = CSprite::SpriteCreate( ISLAVE_HAND_SPRITE_NAME, pev->origin, FALSE ); + CSprite* handSprite = CreateSpriteFromVisual(GetVisual(handGlowVisual), pev->origin); handSprite->SetAttachment( edict(), attachment ); - handSprite->SetScale(0.25); return handSprite; } @@ -1350,8 +1434,18 @@ void CISlave::Spawn() //========================================================= void CISlave::Precache() { - m_iLightningTexture = PRECACHE_MODEL( "sprites/lgtning.spr" ); - m_iTrailTexture = PRECACHE_MODEL( "sprites/plasma.spr" ); + RegisterVisual(zapBeamVisual); + RegisterVisual(powerupBeamVisual); + RegisterVisual(revivalBeamVisual); + RegisterVisual(summonBeamVisual); + + RegisterVisual(idleBeamVisual); + RegisterVisual(coilBeamVisual); + RegisterVisual(trailBeamVisual); + + RegisterVisual(powerupLightVisual); + RegisterVisual(idleLightVisual); + RegisterVisual(summonLightVisual); PrecacheMyModel( "models/islave.mdl" ); @@ -1375,12 +1469,12 @@ void CISlave::Precache() UTIL_PrecacheOther( "test_effect" ); #if FEATURE_ISLAVE_HANDGLOW - PRECACHE_MODEL( ISLAVE_HAND_SPRITE_NAME ); + RegisterVisual(handGlowVisual); if (g_modFeatures.vortigaunt_arm_boost) RegisterAndPrecacheSoundScript(glowAlarmSoundScript); #endif #if FEATURE_ISLAVE_FAMILIAR - PRECACHE_MODEL( ISLAVE_SPAWNFAMILIAR_SPRITE ); + RegisterVisual(summonSpriteVisual); UTIL_PrecacheOther( "monster_snark" ); UTIL_PrecacheOther( "monster_headcrab" ); #endif @@ -1745,18 +1839,12 @@ void CISlave::ArmBeam( int side ) DecalGunshot( &tr, BULLET_PLAYER_CROWBAR ); - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + m_pBeam[m_iBeams] = CreateBeamFromVisual(GetVisual(powerupBeamVisual)); if( !m_pBeam[m_iBeams] ) return; - int brightness; - const Vector armBeamColor = GetArmBeamColor(brightness); m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex() ); m_pBeam[m_iBeams]->SetEndAttachment( AttachmentFromSide(side) ); - // m_pBeam[m_iBeams]->SetColor( 180, 255, 96 ); - m_pBeam[m_iBeams]->SetColor( armBeamColor.x, armBeamColor.y, armBeamColor.z ); - m_pBeam[m_iBeams]->SetBrightness( brightness ); - m_pBeam[m_iBeams]->SetNoise( 80 ); m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; } @@ -1785,26 +1873,16 @@ void CISlave::ArmBeamMessage( int side ) if( flDist == 1.0 ) return; - int brightness; - const Vector armBeamColor = GetArmBeamColor(brightness); - MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); - WRITE_BYTE( TE_BEAMENTPOINT ); - WRITE_SHORT( entindex() + 0x1000 * (AttachmentFromSide(side)) ); - WRITE_COORD( tr.vecEndPos.x ); - WRITE_COORD( tr.vecEndPos.y ); - WRITE_COORD( tr.vecEndPos.z ); - WRITE_SHORT( m_iLightningTexture ); - WRITE_BYTE( 0 ); // framestart - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( (int)(10*RANDOM_FLOAT( 0.8, 1.5 )) ); // life - WRITE_BYTE( 30 ); // width - WRITE_BYTE( 80 ); // noise - WRITE_BYTE( armBeamColor.x ); // r, g, b - WRITE_BYTE( armBeamColor.y ); // r, g, b - WRITE_BYTE( armBeamColor.z ); // r, g, b - WRITE_BYTE( brightness ); // brightness - WRITE_BYTE( 10 ); // speed - MESSAGE_END(); + const Visual* visual = GetVisual(idleBeamVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); + WRITE_BYTE( TE_BEAMENTPOINT ); + WRITE_SHORT( entindex() + 0x1000 * (AttachmentFromSide(side)) ); + WRITE_VECTOR( tr.vecEndPos ); + WriteBeamVisual(visual); + MESSAGE_END(); + } } //========================================================= @@ -1841,16 +1919,12 @@ void CISlave::WackBeam( int side, CBaseEntity *pEntity ) if( pEntity == NULL ) return; - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + m_pBeam[m_iBeams] = CreateBeamFromVisual(GetVisual(revivalBeamVisual)); if( !m_pBeam[m_iBeams] ) return; - Vector zapColor = GetZapColor(); m_pBeam[m_iBeams]->PointEntInit( pEntity->Center(), entindex() ); m_pBeam[m_iBeams]->SetEndAttachment( AttachmentFromSide(side) ); - m_pBeam[m_iBeams]->SetColor( zapColor.x, zapColor.y, zapColor.z ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 80 ); m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; } @@ -1882,16 +1956,12 @@ CBaseEntity *CISlave::ZapBeam( int side ) vecAim = vecAim + side * gpGlobals->v_right * RANDOM_FLOAT( 0, deflection ) + gpGlobals->v_up * RANDOM_FLOAT( -deflection, deflection ); UTIL_TraceLine( vecSrc, vecSrc + vecAim * 1024, dont_ignore_monsters, ENT( pev ), &tr ); - m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.spr", 50 ); + m_pBeam[m_iBeams] = CreateBeamFromVisual(GetVisual(zapBeamVisual)); if( !m_pBeam[m_iBeams] ) return NULL; - const Vector zapColor = GetZapColor(); m_pBeam[m_iBeams]->PointEntInit( tr.vecEndPos, entindex() ); m_pBeam[m_iBeams]->SetEndAttachment( AttachmentFromSide(side) ); - m_pBeam[m_iBeams]->SetColor( zapColor.x, zapColor.y, zapColor.z ); - m_pBeam[m_iBeams]->SetBrightness( 255 ); - m_pBeam[m_iBeams]->SetNoise( 20 ); m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition m_iBeams++; @@ -1953,8 +2023,11 @@ void CISlave::ClearBeams() void CISlave::CoilBeam() { - Vector zapColor = GetZapColor(); - + const Visual* visual = GetVisual(coilBeamVisual); + + if (!visual->modelIndex) + return; + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x ); @@ -1962,22 +2035,10 @@ void CISlave::CoilBeam() WRITE_COORD( pev->origin.z + 16 ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z + 16 + ISLAVE_COIL_ATTACK_RADIUS*5 ); - WRITE_SHORT( m_iLightningTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 128 ); // width - WRITE_BYTE( 20 ); // noise - - WRITE_BYTE( zapColor.x ); - WRITE_BYTE( zapColor.y ); - WRITE_BYTE( zapColor.z ); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed + WRITE_COORD( pev->origin.z + 16 + ISLAVE_COIL_ATTACK_RADIUS*5 ); + WriteBeamVisual(visual); MESSAGE_END(); - + MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x ); @@ -1985,34 +2046,20 @@ void CISlave::CoilBeam() WRITE_COORD( pev->origin.z + 48 ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); - WRITE_COORD( pev->origin.z + 48 + ISLAVE_COIL_ATTACK_RADIUS*2 ); - WRITE_SHORT( m_iLightningTexture ); - WRITE_BYTE( 0 ); // startframe - WRITE_BYTE( 10 ); // framerate - WRITE_BYTE( 2 ); // life - WRITE_BYTE( 128 ); // width - WRITE_BYTE( 25 ); // noise - - WRITE_BYTE( zapColor.x ); - WRITE_BYTE( zapColor.y ); - WRITE_BYTE( zapColor.z ); - - WRITE_BYTE( 255 ); //brightness - WRITE_BYTE( 0 ); // speed + WRITE_COORD( pev->origin.z + 48 + ISLAVE_COIL_ATTACK_RADIUS*2 ); + WriteBeamVisual(visual); MESSAGE_END(); } -void CISlave::MakeDynamicLight(const Vector &vecSrc, int radius, int t) +void CISlave::MakeDynamicLight(const Vector &vecSrc, const char* visualName, int t) { + const Visual* visual = GetVisual(visualName); + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); WRITE_BYTE( TE_DLIGHT ); - WRITE_COORD( vecSrc.x ); // X - WRITE_COORD( vecSrc.y ); // Y - WRITE_COORD( vecSrc.z ); // Z - WRITE_BYTE( radius ); // radius * 0.1 - WRITE_BYTE( ISLAVE_LIGHT_RED ); // r - WRITE_BYTE( ISLAVE_LIGHT_GREEN); // g - WRITE_BYTE( ISLAVE_LIGHT_BLUE ); // b + WRITE_VECTOR( vecSrc ); + WRITE_BYTE( RandomizeNumberFromRange(visual->radius) * 0.1f ); // radius * 0.1 + WRITE_COLOR( visual->rendercolor ); WRITE_BYTE( t ); // time * 10 WRITE_BYTE( 0 ); // decay * 0.1 MESSAGE_END(); @@ -2040,8 +2087,7 @@ void CISlave::HandsGlowOn(int brightness) void CISlave::HandGlowOn(CSprite *handGlow, int brightness) { if (handGlow) { - Vector zapColor = GetZapColor(); - handGlow->SetTransparency( kRenderTransAdd, zapColor.x, zapColor.y, zapColor.z, brightness, kRenderFxNoDissipation ); + handGlow->SetBrightness(brightness); UTIL_SetOrigin(handGlow->pev, pev->origin); handGlow->SetScale(brightness / (float)255 * 0.3); handGlow->pev->effects &= ~EF_NODRAW; @@ -2052,18 +2098,15 @@ void CISlave::StartMeleeAttackGlow(int side) { CSprite* handGlow = side == ISLAVE_LEFT_ARM ? m_handGlow2 : m_handGlow1; HandGlowOn(handGlow); - int brightness; - const Vector armBeamColor = GetArmBeamColor(brightness); + + const Visual* visual = GetVisual(trailBeamVisual); + if (!visual->modelIndex) + return; + MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMFOLLOW ); WRITE_SHORT( entindex() + 0x1000 * (AttachmentFromSide(side)) ); - WRITE_SHORT( m_iTrailTexture ); - WRITE_BYTE( 5 ); // life - WRITE_BYTE( 3 ); // width - WRITE_BYTE( armBeamColor.x ); // r, g, b - WRITE_BYTE( armBeamColor.y ); // r, g, b - WRITE_BYTE( armBeamColor.z ); // r, g, b - WRITE_BYTE( 128 ); // brightness + WriteBeamFollowVisual( visual ); MESSAGE_END(); } @@ -2092,16 +2135,12 @@ void CISlave::CreateSummonBeams() CBeam* CISlave::CreateSummonBeam(const Vector& vecEnd, int attachment) { - CBeam* beam = CBeam::BeamCreate( "sprites/lgtning.spr", 30 ); + CBeam* beam = CreateBeamFromVisual(GetVisual(summonBeamVisual)); if( !beam ) return beam; - Vector zapColor = GetZapColor(); beam->PointEntInit(vecEnd, entindex()); beam->SetEndAttachment(attachment); - beam->SetColor( zapColor.x, zapColor.y, zapColor.z ); - beam->SetBrightness( 192 ); - beam->SetNoise( 80 ); return beam; } @@ -2174,28 +2213,6 @@ bool CISlave::CanSpawnFamiliar() return false; } -Vector CISlave::GetZapColor() -{ -#if FEATURE_ISLAVE_LEADER_COLOR - if (pev->spawnflags & SF_SQUADMONSTER_LEADER) { - return Vector(ISLAVE_LEADER_ZAP_RED, ISLAVE_LEADER_ZAP_GREEN, ISLAVE_LEADER_ZAP_BLUE); - } -#endif - return Vector(ISLAVE_ZAP_RED, ISLAVE_ZAP_GREEN, ISLAVE_ZAP_BLUE); -} - -Vector CISlave::GetArmBeamColor(int &brightness) -{ -#if FEATURE_ISLAVE_LEADER_COLOR - if (pev->spawnflags & SF_SQUADMONSTER_LEADER) { - brightness = 128; - return Vector(ISLAVE_LEADER_ARMBEAM_RED, ISLAVE_LEADER_ARMBEAM_GREEN, ISLAVE_LEADER_ARMBEAM_BLUE); - } -#endif - brightness = 64; - return Vector(ISLAVE_ARMBEAM_RED, ISLAVE_ARMBEAM_GREEN, ISLAVE_ARMBEAM_BLUE); -} - void CISlave::PlayUseSentence() { SENTENCEG_PlayRndSz( ENT( pev ), "SLV_IDLE", 0.85, ATTN_NORM, 0, m_voicePitch ); diff --git a/dlls/player.cpp b/dlls/player.cpp index 1baac36cf..7e0355a1e 100644 --- a/dlls/player.cpp +++ b/dlls/player.cpp @@ -221,7 +221,9 @@ int gmsgStatusIcon = 0; int gmsgRandomGibs = 0; int gmsgMuzzleLight = 0; int gmsgCustomBeam = 0; +int gmsgSprite = 0; int gmsgSpriteTrail = 0; +int gmsgSpray = 0; int gmsgStreaks = 0; int gmsgSmoke = 0; int gmsgSparkShower = 0; @@ -319,7 +321,9 @@ void LinkUserMessages( void ) gmsgRandomGibs = REG_USER_MSG( "RandomGibs", 27 ); gmsgMuzzleLight = REG_USER_MSG( "MuzzleLight", 6 ); gmsgCustomBeam = REG_USER_MSG( "CustomBeam", -1 ); - gmsgSpriteTrail = REG_USER_MSG( "SpriteTrail", 24 ); + gmsgSprite = REG_USER_MSG( "Sprite", 18 ); + gmsgSpriteTrail = REG_USER_MSG( "SpriteTrail", 26 ); + gmsgSpray = REG_USER_MSG( "Spray", 27 ); gmsgStreaks = REG_USER_MSG( "Streaks", 23 ); gmsgSmoke = REG_USER_MSG( "Smoke", -1 ); gmsgSparkShower = REG_USER_MSG( "SparkShower", 20 ); diff --git a/dlls/rpg.cpp b/dlls/rpg.cpp index ccbf99da0..3fa87d30f 100644 --- a/dlls/rpg.cpp +++ b/dlls/rpg.cpp @@ -25,6 +25,7 @@ LINK_ENTITY_TO_CLASS( weapon_rpg, CRpg ) #if !CLIENT_DLL #include "gamerules.h" #include "rpgrocket.h" +#include "visuals_utils.h" LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot ) @@ -117,6 +118,13 @@ const NamedSoundScript CRpgRocket::rocketIgniteSoundScript = { "RPG.RocketIgnite" }; +const NamedVisual CRpgRocket::trailVisual = BuildVisual("RPG.Trail") + .Model("sprites/smoke.spr") + .Life(4) + .BeamWidth(5) + .RenderColor(224, 224, 255) + .Alpha(255); + //========================================================= //========================================================= CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher ) @@ -204,7 +212,7 @@ void CRpgRocket::Precache( void ) { PrecacheBaseGrenadeSounds(); PRECACHE_MODEL( "models/rpgrocket.mdl" ); - m_iTrail = PRECACHE_MODEL( "sprites/smoke.spr" ); + RegisterVisual(trailVisual); RegisterAndPrecacheSoundScript(rocketIgniteSoundScript); } @@ -219,17 +227,15 @@ void CRpgRocket::IgniteThink( void ) EmitSoundScript(rocketIgniteSoundScript); // rocket trail - MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); - WRITE_BYTE( TE_BEAMFOLLOW ); - WRITE_SHORT( entindex() ); // entity - WRITE_SHORT( m_iTrail ); // model - WRITE_BYTE( 40 ); // life - WRITE_BYTE( 5 ); // width - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 224 ); // r, g, b - WRITE_BYTE( 255 ); // r, g, b - WRITE_BYTE( 255 ); // brightness - MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + const Visual* visual = GetVisual(trailVisual); + if (visual->modelIndex) + { + MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY ); + WRITE_BYTE( TE_BEAMFOLLOW ); + WRITE_SHORT( entindex() ); // entity + WriteBeamFollowVisual( visual ); + MESSAGE_END(); // move PHS/PVS data sending into here (SEND_ALL, SEND_PVS, SEND_PHS) + } m_flIgniteTime = gpGlobals->time; diff --git a/dlls/rpgrocket.h b/dlls/rpgrocket.h index 3445d32d6..fcac3cafa 100644 --- a/dlls/rpgrocket.h +++ b/dlls/rpgrocket.h @@ -20,11 +20,12 @@ class CRpgRocket : public CGrenade void Explode( TraceResult *pTrace, int bitsDamageType ); inline CRpg *GetLauncher( void ); - int m_iTrail; float m_flIgniteTime; EHANDLE m_hLauncher; // handle back to the launcher that fired me. static const NamedSoundScript rocketIgniteSoundScript; + + static const NamedVisual trailVisual; }; #endif diff --git a/dlls/tripmine.cpp b/dlls/tripmine.cpp index 837fe3edc..c7284bda7 100644 --- a/dlls/tripmine.cpp +++ b/dlls/tripmine.cpp @@ -20,6 +20,7 @@ #include "weapons.h" #include "player.h" #include "effects.h" +#include "visuals_utils.h" #define TRIPMINE_PRIMARY_VOLUME 450 @@ -63,6 +64,8 @@ class CTripmineGrenade : public CGrenade static const NamedSoundScript deploySoundScript; static const NamedSoundScript activateSoundScript; static const NamedSoundScript chargeSoundScript; + + static const NamedVisual beamVisual; }; LINK_ENTITY_TO_CLASS( monster_tripmine, CTripmineGrenade ) @@ -106,6 +109,13 @@ const NamedSoundScript CTripmineGrenade::chargeSoundScript = { "TripmineGrenade.Charge" }; +const NamedVisual CTripmineGrenade::beamVisual = BuildVisual("Tripmine.Beam") + .Model(g_pModelNameLaser) + .RenderColor(0, 214, 198) + .Alpha(64) + .BeamWidth(10) + .BeamScrollRate(255); + void CTripmineGrenade::Spawn( void ) { Precache(); @@ -173,6 +183,7 @@ void CTripmineGrenade::Precache( void ) RegisterAndPrecacheSoundScript(deploySoundScript); RegisterAndPrecacheSoundScript(activateSoundScript); RegisterAndPrecacheSoundScript(chargeSoundScript); + RegisterVisual(beamVisual); } void CTripmineGrenade::UpdateOnRemove() @@ -281,13 +292,12 @@ void CTripmineGrenade::MakeBeam( void ) Vector vecTmpEnd = pev->origin + m_vecDir * 2048.0f * m_flBeamLength; - m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 10 ); + const Visual* visual = GetVisual(beamVisual); + + m_pBeam = CreateBeamFromVisual(visual); //Mark as temporary so the beam will be recreated on save game load and level transitions. m_pBeam->pev->spawnflags |= SF_BEAM_TEMPORARY; m_pBeam->PointEntInit( vecTmpEnd, entindex() ); - m_pBeam->SetColor( 0, 214, 198 ); - m_pBeam->SetScrollRate( 255 ); - m_pBeam->SetBrightness( 64 ); } void CTripmineGrenade::BeamBreakThink( void ) diff --git a/dlls/turret.cpp b/dlls/turret.cpp index 8cfc1e638..8b0985da6 100644 --- a/dlls/turret.cpp +++ b/dlls/turret.cpp @@ -27,6 +27,7 @@ #include "effects.h" #include "game.h" #include "common_soundscripts.h" +#include "visuals_utils.h" #define TURRET_SHOTS 2 #define TURRET_RANGE (100 * 12) @@ -87,6 +88,7 @@ class CBaseTurret : public CBaseMonster virtual void Ping( void ); virtual void EyeOn( void ); virtual void EyeOff( void ); + virtual int MaxEyeBrightness() { return 255; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); @@ -226,6 +228,7 @@ class CTurret : public CBaseTurret const char* DefaultDisplayName() { return "Turret"; } void SpinUpCall( void ); void SpinDownCall( void ); + virtual int MaxEyeBrightness(); virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); @@ -238,6 +241,8 @@ class CTurret : public CBaseTurret static const NamedSoundScript shootSoundScript; static const NamedSoundScript spinupCallSoundScript; static const NamedSoundScript spindownCallSoundScript; + + static const NamedVisual glowVisual; private: int m_iStartSpin; }; @@ -266,6 +271,13 @@ const NamedSoundScript CTurret::spindownCallSoundScript = { "Turret.SpinDownCall" }; +const NamedVisual CTurret::glowVisual = BuildVisual("Turret.Glow") + .Model("sprites/flare3.spr") + .RenderMode(kRenderGlow) + .RenderColor(255, 0, 0) + .Alpha(255) + .RenderFx(kRenderFxNoDissipation); + TYPEDESCRIPTION CTurret::m_SaveData[] = { DEFINE_FIELD( CTurret, m_iStartSpin, FIELD_INTEGER ), @@ -368,8 +380,6 @@ void CBaseTurret::UpdateOnRemove() CBaseMonster::UpdateOnRemove(); } -#define TURRET_GLOW_SPRITE "sprites/flare3.spr" - void CTurret::Spawn() { Precache(); @@ -388,8 +398,9 @@ void CTurret::Spawn() SetThink( &CBaseTurret::Initialize ); - m_pEyeGlow = CSprite::SpriteCreate( TURRET_GLOW_SPRITE, pev->origin, FALSE ); - m_pEyeGlow->SetTransparency( kRenderGlow, 255, 0, 0, 0, kRenderFxNoDissipation ); + const Visual* visual = GetVisual(glowVisual); + m_pEyeGlow = CreateSpriteFromVisual(visual, pev->origin); + m_pEyeGlow->SetBrightness(0); m_pEyeGlow->SetAttachment( edict(), 2 ); m_eyeBrightness = 0; @@ -400,7 +411,7 @@ void CTurret::Precache() { CBaseTurret::Precache(); PrecacheMyModel( "models/turret.mdl" ); - PRECACHE_MODEL( TURRET_GLOW_SPRITE ); + RegisterVisual(glowVisual); RegisterAndPrecacheSoundScript(shootSoundScript); RegisterAndPrecacheSoundScript(spinupCallSoundScript); RegisterAndPrecacheSoundScript(spindownCallSoundScript); @@ -518,9 +529,10 @@ void CBaseTurret::EyeOn() { if( m_pEyeGlow ) { - if( m_eyeBrightness != 255 ) + const int maxBrightness = MaxEyeBrightness(); + if( m_eyeBrightness != maxBrightness ) { - m_eyeBrightness = 255; + m_eyeBrightness = maxBrightness; } m_pEyeGlow->SetBrightness( m_eyeBrightness ); } @@ -852,6 +864,12 @@ void CTurret::SpinDownCall( void ) } } +int CTurret::MaxEyeBrightness() +{ + const Visual* visual = GetVisual(glowVisual); + return visual->renderamt; +} + void CBaseTurret::SetTurretAnim( TURRET_ANIM anim ) { if( pev->sequence != anim ) diff --git a/dlls/util.cpp b/dlls/util.cpp index d5519d549..3742417bd 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -171,6 +171,27 @@ void AddMapBSPAsPrecachedModel() g_precachedModels.insert(buf); } +void WRITE_COLOR(const Color& color) +{ + WRITE_BYTE( color.r ); + WRITE_BYTE( color.g ); + WRITE_BYTE( color.b ); +} + +void WRITE_COLOR(const Vector& color) +{ + WRITE_BYTE( color.x ); + WRITE_BYTE( color.y ); + WRITE_BYTE( color.z ); +} + +void WRITE_VECTOR(const Vector& vecSrc) +{ + WRITE_COORD( vecSrc.x ); + WRITE_COORD( vecSrc.y ); + WRITE_COORD( vecSrc.z ); +} + void UTIL_DynamicLight( const Vector &vecSrc, float flRadius, byte r, byte g, byte b, float flTime, float flDecay ) { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecSrc ); diff --git a/dlls/util.h b/dlls/util.h index ac110cef6..11f108c3e 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -64,6 +64,10 @@ extern void ReportPrecachedModels(); extern void ReportPrecachedSounds(); extern void AddMapBSPAsPrecachedModel(); +extern void WRITE_COLOR(const Color& color); +extern void WRITE_COLOR(const Vector& color); +extern void WRITE_VECTOR(const Vector& vecSrc); + inline edict_t *FIND_ENTITY_BY_CLASSNAME(edict_t *entStart, const char *pszName) { return FIND_ENTITY_BY_STRING(entStart, "classname", pszName); diff --git a/dlls/visuals.cpp b/dlls/visuals.cpp new file mode 100644 index 000000000..3d772bc97 --- /dev/null +++ b/dlls/visuals.cpp @@ -0,0 +1,550 @@ +#include "extdll.h" +#include "enginecallback.h" +#include "visuals.h" +#include "util.h" + +#include +#include +#include + +#include "json_utils.h" + +using namespace rapidjson; + +const char* visualsSchema = R"( +{ + "definitions": { + "visual": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "sprite": { + "type": "string" + }, + "rendermode": { + "type": "string", + "pattern": "^Normal|normal|Color|color|Texture|texture|Glow|glow|Solid|solid|Additive|additive$" + }, + "color": { + "$ref": "definitions.json#/color" + }, + "alpha": { + "$ref": "definitions.json#/alpha" + }, + "renderfx": { + "type": ["integer", "string"], + "pattern": "^Normal|normal|Constant Glow|constant glow$", + "minimum": 0, + "maximum": 20 + }, + "scale": { + "type": "number", + "minimum": 0.0 + }, + "framerate": { + "type": "number", + "minimum": 0.0 + }, + "width": { + "type": "integer", + "minimum": 1 + }, + "noise": { + "type": "integer" + }, + "scrollrate": { + "type": "integer" + }, + "life": { + "$ref": "definitions.json#/range" + }, + "radius": { + "$ref": "definitions.json#/range_int" + } + } + } + }, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/visual" + } +} +)"; + +void Visual::DoPrecache() +{ + if (HasModel()) + { + modelIndex = PRECACHE_MODEL(model); + } +} + +void Visual::CompleteFrom(const Visual &visual) +{ + if (ShouldCompleteFrom(visual, MODEL_DEFINED)) + { + SetModel(visual.model); + } + if (ShouldCompleteFrom(visual, RENDERMODE_DEFINED)) + { + SetRenderMode(visual.rendermode); + } + if (ShouldCompleteFrom(visual, COLOR_DEFINED)) + { + SetColor(visual.rendercolor); + } + if (ShouldCompleteFrom(visual, ALPHA_DEFINED)) + { + SetAlpha(visual.renderamt); + } + if (ShouldCompleteFrom(visual, RENDERFX_DEFINED)) + { + SetRenderFx(visual.renderfx); + } + if (ShouldCompleteFrom(visual, SCALE_DEFINED)) + { + SetScale(visual.scale); + } + if (ShouldCompleteFrom(visual, FRAMERATE_DEFINED)) + { + SetFramerate(visual.framerate); + } + if (ShouldCompleteFrom(visual, BEAMWIDTH_DEFINED)) + { + SetBeamWidth(visual.beamWidth); + } + if (ShouldCompleteFrom(visual, BEAMNOISE_DEFINED)) + { + SetBeamNoise(visual.beamNoise); + } + if (ShouldCompleteFrom(visual, BEAMSCROLLRATE_DEFINED)) + { + SetBeamScrollRate(visual.beamScrollRate); + } + if (ShouldCompleteFrom(visual, LIFE_DEFINED)) + { + SetLife(visual.life); + } + if (ShouldCompleteFrom(visual, RADIUS_DEFINED)) + { + SetRadius(visual.radius); + } +} + +struct CaseInsensitiveCompare +{ + bool operator()(const std::string& lhs, const std::string& rhs) const noexcept + { + return stricmp(lhs.c_str(), rhs.c_str()) < 0; + } +}; + +class VisualSystem +{ +public: + bool ReadFromFile(const char* fileName); + const Visual* GetVisual(const char* name); + const Visual* ProvideDefaultVisual(const char* name, const Visual& visual, bool doPrecache); + const Visual* ProvideDefaultVisual(const char* name, const Visual& visual, const char* mixinName, const Visual& mixinVisual); + void DumpVisuals(); + void DumpVisual(const char* name); +private: + void DumpVisualImpl(const char* name, const Visual& visual); + + std::map _visuals; + std::set _modelStringSet; + std::string _temp; +}; + +static bool ParseRenderMode(const char* str, int& rendermode) +{ + constexpr std::pair modes[] = { + {"normal", kRenderNormal}, + {"color", kRenderTransColor}, + {"texture", kRenderTransTexture}, + {"glow", kRenderGlow}, + {"solid", kRenderTransAlpha}, + {"additive", kRenderTransAdd}, + }; + + for (auto& p : modes) + { + if (stricmp(str, p.first) == 0) + { + rendermode = p.second; + return true; + } + } + return false; +} + +static const char* RenderModeToString(int rendermode) +{ + switch (rendermode) { + case kRenderNormal: + return "Normal"; + case kRenderTransColor: + return "Color"; + case kRenderTransTexture: + return "Texture"; + case kRenderGlow: + return "Glow"; + case kRenderTransAlpha: + return "Solid"; + case kRenderTransAdd: + return "Additive"; + default: + return "Unknown"; + } +} + +static bool ParseRenderFx(const char* str, int& renderfx) +{ + constexpr std::pair modes[] = { + {"normal", kRenderFxNone}, + {"constant glow", kRenderFxNoDissipation}, + }; + + for (auto& p : modes) + { + if (stricmp(str, p.first) == 0) + { + renderfx = p.second; + return true; + } + } + return false; +} + +static const char* RenderFxToString(int renderfx) +{ + switch (renderfx) { + case kRenderFxNone: return "Normal"; + case kRenderFxPulseSlow: return "Slow Pulse"; + case kRenderFxPulseFast: return "Fast Pulse"; + case kRenderFxPulseSlowWide:return "Slow Wide Pulse"; + case kRenderFxFadeSlow: return "Slow Fade Away"; + case kRenderFxFadeFast: return "Fast Fade Away"; + case kRenderFxSolidSlow: return "Slow Become Solid"; + case kRenderFxSolidFast: return "Fast Become Solid"; + case kRenderFxStrobeSlow: return "Slow Strobe"; + case kRenderFxStrobeFast: return "Fast Strobe"; + case kRenderFxStrobeFaster: return "Faster Strobe"; + case kRenderFxFlickerSlow: return "Slow Flicker"; + case kRenderFxFlickerFast: return "Fast Flicker"; + case kRenderFxNoDissipation:return "Constant Glow"; + case kRenderFxDistort: return "Distort"; + case kRenderFxHologram: return "Hologram"; + case kRenderFxGlowShell: return "Glow Shell"; + default: return "Unknown"; + } +} + +bool VisualSystem::ReadFromFile(const char *fileName) +{ + int fileSize; + char *pMemFile = (char*)g_engfuncs.pfnLoadFileForMe( fileName, &fileSize ); + if (!pMemFile) + return false; + + ALERT(at_console, "Parsing %s\n", fileName); + + Document document; + bool success = ReadJsonDocumentWithSchema(document, pMemFile, fileSize, visualsSchema, fileName); + g_engfuncs.pfnFreeFile(pMemFile); + + if (!success) + return false; + + for (auto scriptIt = document.MemberBegin(); scriptIt != document.MemberEnd(); ++scriptIt) + { + const char* name = scriptIt->name.GetString(); + + Visual visual; + + Value& value = scriptIt->value; + + { + auto it = value.FindMember("model"); + if (it != value.MemberEnd()) + { + std::string str = it->value.GetString(); + auto strIt = _modelStringSet.find(str); + if (strIt == _modelStringSet.end()) + { + auto p = _modelStringSet.insert(str); + strIt = p.first; + } + visual.SetModel(strIt->c_str()); + } + } + + { + auto it = value.FindMember("sprite"); + if (it != value.MemberEnd()) + { + if (visual.HasDefined(Visual::MODEL_DEFINED)) + { + ALERT(at_warning, "Visual \"s\" has both 'model' and 'sprite' properties defined!\n", name); + } + else + { + std::string str = it->value.GetString(); + auto strIt = _modelStringSet.find(str); + if (strIt == _modelStringSet.end()) + { + auto p = _modelStringSet.insert(str); + strIt = p.first; + } + visual.SetModel(strIt->c_str()); + } + } + } + + { + auto it = value.FindMember("rendermode"); + if (it != value.MemberEnd()) + { + Value& rendermodeValue = it->value; + if (rendermodeValue.IsString()) + { + int rendermode; + if (ParseRenderMode(rendermodeValue.GetString(), rendermode)) + { + visual.SetRenderMode(rendermode); + } + } + else if (rendermodeValue.IsInt()) + { + visual.SetRenderMode(rendermodeValue.GetInt()); + } + } + } + + Color color; + if (UpdatePropertyFromJson(color, value, "color")) + { + visual.SetColor(color); + } + + int renderamt; + if (UpdatePropertyFromJson(renderamt, value, "alpha")) + { + visual.SetAlpha(renderamt); + } + + { + auto it = value.FindMember("renderfx"); + if (it != value.MemberEnd()) + { + Value& renderfxValue = it->value; + if (renderfxValue.IsString()) + { + int renderfx; + if (ParseRenderFx(renderfxValue.GetString(), renderfx)) + { + visual.SetRenderFx(renderfx); + } + } + else if (renderfxValue.IsInt()) + { + visual.SetRenderFx(renderfxValue.GetInt()); + } + } + } + + float scale; + if (UpdatePropertyFromJson(scale, value, "scale")) + { + visual.SetScale(scale); + } + + float framerate; + if (UpdatePropertyFromJson(framerate, value, "framerate")) + { + visual.SetFramerate(framerate); + } + + int beamWidth, beamNoise, beamScrollRate; + if (UpdatePropertyFromJson(beamWidth, value, "width")) + { + visual.SetBeamWidth(beamWidth); + } + if (UpdatePropertyFromJson(beamNoise, value, "noise")) + { + visual.SetBeamNoise(beamNoise); + } + if (UpdatePropertyFromJson(beamScrollRate, value, "scrollrate")) + { + visual.SetBeamScrollRate(beamScrollRate); + } + + FloatRange life; + if (UpdatePropertyFromJson(life, value, "life")) + { + visual.SetLife(life); + } + + IntRange radius; + if (UpdatePropertyFromJson(radius, value, "radius")) + { + visual.SetRadius(radius); + } + + _visuals[name] = visual; + } + + return true; +} + +const Visual* VisualSystem::GetVisual(const char *name) +{ + _temp = name; // reuse the same std::string for search to avoid reallocation + auto it = _visuals.find(_temp); + if (it != _visuals.end()) + return &it->second; + return nullptr; +} + +const Visual* VisualSystem::ProvideDefaultVisual(const char *name, const Visual &visual, bool doPrecache) +{ + _temp = name; + auto it = _visuals.find(_temp); + if (it != _visuals.end()) + { + Visual& existing = it->second; + existing.CompleteFrom(visual); + + if (doPrecache) + existing.DoPrecache(); + + return &existing; + } + else + { + auto inserted = _visuals.insert(std::make_pair(_temp, visual)); + if (inserted.second) + { + Visual* insertedVisual = &inserted.first->second; + if (doPrecache) + insertedVisual->DoPrecache(); + return insertedVisual; + } + // Should never get here: if it already existed it should have used the first if branch. + return nullptr; + } +} + +void VisualSystem::DumpVisualImpl(const char *name, const Visual &visual) +{ + ALERT(at_console, "%s:\n", name); + + ALERT(at_console, "Model/Sprite: \"%s\"\n", visual.model ? visual.model : ""); + + ALERT(at_console, "Rendermode: %s. Color: (%d, %d, %d). Alpha: %d. Renderfx: %s. ", + RenderModeToString(visual.rendermode), + visual.rendercolor.r, visual.rendercolor.g, visual.rendercolor.b, + visual.renderamt, + RenderFxToString(visual.renderfx)); + + ALERT(at_console, "Scale: %g. Framerate: %g. ", visual.scale, visual.framerate); + + if (visual.HasDefined(Visual::BEAMWIDTH_DEFINED)) + { + ALERT(at_console, "Beam width: %d. Beam noise: %d. Beam scoll rate: %d. ", visual.beamWidth, visual.beamNoise, visual.beamScrollRate); + } + + if (visual.HasDefined(Visual::LIFE_DEFINED)) + { + if (visual.life.max <= visual.life.min) + { + ALERT(at_console, "Life: %g. ", visual.life.min); + } + else + { + ALERT(at_console, "Life: %g-%g. ", visual.life.min, visual.life.max); + } + } + + if (visual.HasDefined(Visual::RADIUS_DEFINED)) + { + if (visual.radius.max <= visual.radius.min) + { + ALERT(at_console, "Radius: %d. ", visual.radius.min); + } + else + { + ALERT(at_console, "Radius: %d-%d. ", visual.radius.min, visual.radius.max); + } + } + + ALERT(at_console, "\n\n"); +} + +void VisualSystem::DumpVisuals() +{ + for (const auto& p : _visuals) + { + DumpVisualImpl(p.first.c_str(), p.second); + } +} + +void VisualSystem::DumpVisual(const char *name) +{ + _temp = name; + if (_temp[_temp.size()-1] == '.') + { + bool foundSomething = false; + for (const auto& p : _visuals) + { + if (strnicmp(p.first.c_str(), _temp.c_str(), _temp.size()) == 0) + { + foundSomething = true; + DumpVisualImpl(p.first.c_str(), p.second); + } + } + if (foundSomething) + return; + } + else + { + auto it = _visuals.find(_temp); + if (it != _visuals.end()) + { + DumpVisualImpl(name, it->second); + return; + } + } + ALERT(at_console, "Couldn't find a visual for %s\n", name); +} + +VisualSystem g_VisualSystem; + +void ReadVisuals() +{ + g_VisualSystem.ReadFromFile("templates/visuals.json"); +} + +const Visual* ProvideDefaultVisual(const char* name, const Visual& visual, bool precache) +{ + return g_VisualSystem.ProvideDefaultVisual(name, visual, precache); +} + +const Visual* GetVisual(const char* name) +{ + return g_VisualSystem.GetVisual(name); +} + +void DumpVisuals() +{ + int argc = CMD_ARGC(); + if (argc > 1) + { + for (int i=1; imodel = model; + MarkAsDefined(MODEL_DEFINED); + } + inline void SetRenderMode(int rendermode) + { + this->rendermode = rendermode; + MarkAsDefined(RENDERMODE_DEFINED); + } + inline void SetColor(Color rendercolor) + { + this->rendercolor = rendercolor; + MarkAsDefined(COLOR_DEFINED); + } + inline void SetAlpha(int alpha) + { + this->renderamt = alpha; + MarkAsDefined(ALPHA_DEFINED); + } + inline void SetRenderFx(int renderfx) + { + this->renderfx = renderfx; + MarkAsDefined(RENDERFX_DEFINED); + } + inline void SetScale(float scale) + { + this->scale = scale; + MarkAsDefined(SCALE_DEFINED); + } + inline void SetFramerate(float framerate) + { + this->framerate = framerate; + MarkAsDefined(FRAMERATE_DEFINED); + } + inline void SetBeamWidth(int width) + { + this->beamWidth = width; + MarkAsDefined(BEAMWIDTH_DEFINED); + } + inline void SetBeamNoise(int noise) + { + this->beamNoise = noise; + MarkAsDefined(BEAMNOISE_DEFINED); + } + inline void SetBeamScrollRate(int scrollRate) + { + this->beamScrollRate = scrollRate; + MarkAsDefined(BEAMSCROLLRATE_DEFINED); + } + inline void SetLife(FloatRange life) + { + this->life = life; + MarkAsDefined(LIFE_DEFINED); + } + inline void SetRadius(IntRange radius) + { + this->radius = radius; + MarkAsDefined(RADIUS_DEFINED); + } + + inline bool HasModel() const { + return model && *model; + } + inline bool HasDefined(int param) const { + return (defined & param) != 0; + } + inline void MarkAsDefined(int param) { + defined |= param; + } + void DoPrecache(); + void CompleteFrom(const Visual& visual); + +private: + inline bool ShouldCompleteFrom(const Visual& visual, int param) const { + return visual.HasDefined(param) && !HasDefined(param); + } +}; + +struct NamedVisual : public Visual +{ + constexpr NamedVisual(const char* vName): name(vName) {} + const char* name; + operator const char* () const { + return name; + } + const NamedVisual* mixin = nullptr; +}; + +struct BuildVisual +{ + BuildVisual(const char* name): visual(name) {} + + static BuildVisual Animated(const char* name) + { + return BuildVisual(name).Framerate(10.f); + } + static BuildVisual Spray(const char* name) + { + return BuildVisual(name).RenderMode(kRenderTransAlpha).Alpha(255).Framerate(0.5f); + } + + NamedVisual Result() const { + return visual; + } + operator NamedVisual() const { + return visual; + } + + inline BuildVisual& Model(const char* model) { + visual.SetModel(model); + return *this; + } + inline BuildVisual& RenderMode(int rendermode) { + visual.SetRenderMode(rendermode); + return *this; + } + inline BuildVisual& RenderColor(Color rendercolor) { + visual.SetColor(rendercolor); + return *this; + } + inline BuildVisual& RenderColor(int r, int g, int b) { + return RenderColor(Color(r, g, b)); + } + inline BuildVisual& Alpha(int renderamt) { + visual.SetAlpha(renderamt); + return *this; + } + inline BuildVisual& RenderFx(int renderfx) { + visual.SetRenderFx(renderfx); + return *this; + } + inline BuildVisual& RenderProps(int rendermode, Color rendercolor, int renderamt, int renderfx) + { + return RenderMode(rendermode).RenderColor(rendercolor).Alpha(renderamt).RenderFx(renderfx); + } + inline BuildVisual& RenderProps(int rendermode, Color rendercolor, int renderamt) + { + return RenderMode(rendermode).RenderColor(rendercolor).Alpha(renderamt); + } + inline BuildVisual& RenderProps(int rendermode, Color rendercolor) + { + return RenderMode(rendermode).RenderColor(rendercolor); + } + inline BuildVisual& Scale(float scale) + { + visual.SetScale(scale); + return *this; + } + inline BuildVisual& Framerate(float framerate) + { + visual.SetFramerate(framerate); + return *this; + } + inline BuildVisual& BeamParams(int width, int noise, int scrollrate = 0) + { + return BeamWidth(width).BeamNoise(noise).BeamScrollRate(scrollrate); + } + inline BuildVisual& BeamWidth(int width) + { + visual.SetBeamWidth(width); + return *this; + } + inline BuildVisual& BeamNoise(int noise) + { + visual.SetBeamNoise(noise); + return *this; + } + inline BuildVisual& BeamScrollRate(int scrollrate) + { + visual.SetBeamScrollRate(scrollrate); + return *this; + } + inline BuildVisual& Life(FloatRange life) + { + visual.SetLife(life); + return *this; + } + inline BuildVisual& Radius(IntRange radius) { + visual.SetRadius(radius); + return *this; + } + inline BuildVisual& Mixin(const NamedVisual* mixin) + { + visual.mixin = mixin; + return *this; + } +private: + NamedVisual visual; +}; + +void ReadVisuals(); + +const Visual* ProvideDefaultVisual(const char* name, const Visual& visual, bool precache); +const Visual* ProvideDefaultVisual(const char* name, const Visual& visual, const char* mixinName, const Visual& mixinVisual); + +const Visual* GetVisual(const char* name); + +void DumpVisuals(); + +#endif diff --git a/dlls/visuals_utils.cpp b/dlls/visuals_utils.cpp new file mode 100644 index 000000000..209307032 --- /dev/null +++ b/dlls/visuals_utils.cpp @@ -0,0 +1,150 @@ +#include "visuals_utils.h" + +CSprite* CreateSpriteFromVisual(const Visual& visual, const Vector& origin, int spawnFlags) +{ + CSprite *sprite = CSprite::SpriteCreate( visual.model, origin, visual.framerate > 0.0f, spawnFlags ); + if (sprite) + { + sprite->pev->framerate = visual.framerate; + sprite->SetTransparency(visual.rendermode, visual.rendercolor.r, visual.rendercolor.g, visual.rendercolor.b, visual.renderamt, visual.renderfx); + sprite->SetScale(visual.scale); + } + return sprite; +} + +CSprite* CreateSpriteFromVisual(const Visual* visual, const Vector& origin, int spawnFlags) +{ + return CreateSpriteFromVisual(*visual, origin, spawnFlags); +} + +CBeam* CreateBeamFromVisual(const Visual& visual) +{ + CBeam* beam = CBeam::BeamCreate(visual.model, visual.beamWidth); + if (beam) + { + beam->SetColor(visual.rendercolor.r, visual.rendercolor.g, visual.rendercolor.b); + beam->SetBrightness(visual.renderamt); + beam->SetWidth(visual.beamWidth); + beam->SetNoise(visual.beamNoise); + if (visual.beamScrollRate) + beam->SetScrollRate(visual.beamScrollRate); + } + return beam; +} + +CBeam* CreateBeamFromVisual(const Visual* visual) +{ + if (visual) + return CreateBeamFromVisual(*visual); + return nullptr; +} + +void WriteBeamVisual(const Visual &visual) +{ + WRITE_SHORT( visual.modelIndex ); + WRITE_BYTE( 0 ); // framestart + WRITE_BYTE( visual.framerate ); // framerate + WRITE_BYTE( (int)(10*RandomizeNumberFromRange(visual.life)) ); // life + WRITE_BYTE( visual.beamWidth ); // width + WRITE_BYTE( visual.beamNoise ); // noise + WRITE_COLOR( visual.rendercolor ); + WRITE_BYTE( visual.renderamt ); // brightness + WRITE_BYTE( visual.beamScrollRate ); // speed +} + +void WriteBeamVisual(const Visual *visual) +{ + WriteBeamVisual(*visual); +} + +void WriteBeamFollowVisual(const Visual &visual) +{ + WRITE_SHORT( visual.modelIndex ); + WRITE_BYTE( (int)(10*RandomizeNumberFromRange(visual.life)) ); // life + WRITE_BYTE( visual.beamWidth ); // width + WRITE_COLOR( visual.rendercolor ); // r, g, b + WRITE_BYTE( visual.renderamt ); // brightness +} + +void WriteBeamFollowVisual(const Visual *visual) +{ + WriteBeamFollowVisual(*visual); +} + +void WriteSpriteVisual(const Visual &visual) +{ + WRITE_SHORT( visual.modelIndex ); // model + WRITE_BYTE( visual.scale * 10 ); // size * 10 + WRITE_BYTE( visual.rendermode ); + WRITE_COLOR( visual.rendercolor ); + WRITE_BYTE( visual.renderamt ); // brightness + WRITE_BYTE( visual.renderfx ); + WRITE_SHORT( visual.framerate * 10 ); + WRITE_BYTE( RandomizeNumberFromRange(visual.life)*10 ); +} + +void WriteSpriteVisual(const Visual *visual) +{ + WriteSpriteVisual(*visual); +} + +void WriteDynLightVisual(const Visual &visual) +{ + WRITE_BYTE( RandomizeNumberFromRange(visual.radius) * 0.1f ); // radius * 0.1 + WRITE_COLOR( visual.rendercolor ); + WRITE_BYTE( RandomizeNumberFromRange(visual.life)*10 ); // time * 10 +} + +void WriteDynLightVisual(const Visual *visual) +{ + WriteDynLightVisual(*visual); +} + +void WriteEntLightVisual(const Visual &visual) +{ + WRITE_COORD( RandomizeNumberFromRange(visual.radius) ); // radius + WRITE_COLOR( visual.rendercolor ); + WRITE_BYTE( RandomizeNumberFromRange(visual.life)*10 ); // life * 10 +} + +void WriteEntLightVisual(const Visual *visual) +{ + WriteEntLightVisual(*visual); +} + +void ApplyVisualToEntity(CBaseEntity* pEntity, const Visual& visual) +{ + if (visual.model && *visual.model) + { + SET_MODEL(pEntity->edict(), visual.model); + } + pEntity->pev->rendermode = visual.rendermode; + pEntity->pev->rendercolor = VectorFromColor(visual.rendercolor); + pEntity->pev->renderamt = visual.renderamt; + pEntity->pev->renderfx = visual.renderfx; + pEntity->pev->scale = visual.scale; + pEntity->pev->framerate = visual.framerate; +} + +void ApplyVisualToEntity(CBaseEntity* pEntity, const Visual* visual) +{ + ApplyVisualToEntity(pEntity, *visual); +} + +float AnimateWithFramerate(float frame, float maxFrame, float framerate, float* pLastTime) +{ + if (maxFrame == 0 || framerate == 0.0f) + return frame; + + const float timeBetween = pLastTime ? (gpGlobals->time - *pLastTime) : 0.1f; + const float frames = framerate * timeBetween; + + frame += frames; + if (maxFrame > 0) + frame = fmod(frame, maxFrame); + + if (pLastTime) + *pLastTime = gpGlobals->time; + + return frame; +} diff --git a/dlls/visuals_utils.h b/dlls/visuals_utils.h new file mode 100644 index 000000000..abdef2836 --- /dev/null +++ b/dlls/visuals_utils.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef VISUALS_UTILS_H +#define VISUALS_UTILS_H + +#include "extdll.h" +#include "util.h" +#include "effects.h" +#include "visuals.h" + +CSprite* CreateSpriteFromVisual(const Visual& visual, const Vector& origin, int spawnFlags = 0); +CSprite* CreateSpriteFromVisual(const Visual* visual, const Vector& origin, int spawnFlags = 0); + +CBeam* CreateBeamFromVisual(const Visual& visual); +CBeam* CreateBeamFromVisual(const Visual* visual); + +void WriteBeamVisual(const Visual& visual); +void WriteBeamVisual(const Visual* visual); + +void WriteBeamFollowVisual(const Visual& visual); +void WriteBeamFollowVisual(const Visual* visual); + +void WriteSpriteVisual(const Visual& visual); +void WriteSpriteVisual(const Visual* visual); + +void WriteDynLightVisual(const Visual& visual); +void WriteDynLightVisual(const Visual* visual); + +void WriteEntLightVisual(const Visual& visual); +void WriteEntLightVisual(const Visual* visual); + +void ApplyVisualToEntity(CBaseEntity* pEntity, const Visual& visual); +void ApplyVisualToEntity(CBaseEntity* pEntity, const Visual* visual); + +float AnimateWithFramerate(float frame, float maxFrame, float framerate, float* pLastTime = nullptr); + +inline Vector VectorFromColor(const Color& color) { + return Vector(color.r, color.g, color.b); +} + +#endif diff --git a/game_shared/fx_flags.h b/game_shared/fx_flags.h index cf083f278..9d915ccff 100644 --- a/game_shared/fx_flags.h +++ b/game_shared/fx_flags.h @@ -47,4 +47,8 @@ enum PARTICLE_LIGHT_INTENSITY, }; +#define SPRAY_FLAG_COLLIDEWORLD (1 << 0) +#define SPRAY_FLAG_ANIMATE (1 << 1) +#define SPRAY_FLAG_FADEOUT (1 << 2) + #endif diff --git a/game_shared/template_property_types.h b/game_shared/template_property_types.h index a459684fc..afd981bb3 100644 --- a/game_shared/template_property_types.h +++ b/game_shared/template_property_types.h @@ -4,8 +4,8 @@ struct Color { - Color(): r(0), g(0), b(0) {} - Color(int red, int green, int blue): r(red), g(green), b(blue) {} + constexpr Color(): r(0), g(0), b(0) {} + constexpr Color(int red, int green, int blue): r(red), g(green), b(blue) {} int r; int g; int b;