diff --git a/base/src/client/view.qc b/base/src/client/view.qc deleted file mode 100644 index c71eb90d..00000000 --- a/base/src/client/view.qc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2016-2022 Vera Visions LLC. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -void -View_UpdateWeapon(player pl, entity vm, entity mflash) -{ - /* only bother upon change */ - if (pSeat->m_iLastWeapon == pl.activeweapon) { - return; - } - pSeat->m_iOldWeapon = pSeat->m_iLastWeapon; - pSeat->m_iLastWeapon = pl.activeweapon; - - if (!pl.activeweapon) { - return; - } - - /* hack, we changed the wep, move this into Game_Input/PMove */ - Weapons_Draw(pl); - - /* we forced a weapon call outside the prediction, - * thus we need to update all the net variables to - * make sure these updates are recognized. this is - * vile but it'll have to do for now */ - SAVE_STATE(pl.w_attack_next) - SAVE_STATE(pl.w_idle_next) - SAVE_STATE(pl.viewzoom) - SAVE_STATE(pl.weapontime) - - /* figure out when the attachments start. in FTE attachments for - * HLMDL are treated as bones. they start at numbones + 1 */ - skel_delete(mflash.skeletonindex); - mflash.skeletonindex = skel_create(vm.modelindex); - pSeat->m_iVMBones = skel_get_numbones(mflash.skeletonindex) + 1; -} diff --git a/src/client/defs.h b/src/client/defs.h index 82dc6f7f..9c17665f 100644 --- a/src/client/defs.h +++ b/src/client/defs.h @@ -171,8 +171,6 @@ void Event_Callback(float mtime, __inout float btime); void View_AddEvent(void(void) pCallback, float flTime); void View_PlayAnimation(int); void View_PlayAnimation(int); -void Event_ProcessModel(float, int, string); -void ClientGame_ModelEvent(float, int, string); void View_EnableViewmodel(void); void View_DisableViewmodel(void); diff --git a/src/client/modelevent.qc b/src/client/modelevent.qc index 3385131b..f6c94a7f 100644 --- a/src/client/modelevent.qc +++ b/src/client/modelevent.qc @@ -14,78 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -string -Event_DataToMuzzleflash(string dataString) -{ - float id = stof(substring(dataString, 0, 1)) - 1; - - if (id) - return strcat("sprites/muzzleflash", ftos(id), ".spr"); - else - return "sprites/muzzleflash.spr"; -} - -/* -==================== -Event_ProcessModel - -Called by the engine whenever a model tries to play an event and isn't handled -by ClientGame_ModelEvent. This gives a mod the chance to override builtin -events - hook into them and all that. -==================== -*/ -void -Event_ProcessModel(float flTimeStamp, int iCode, string strData) -{ - switch(iCode) { - case 1004: - if (substring(strData, 0, 1) == "*") - sound(self, CHAN_BODY, substring(strData, 1, -1), 1.0f, ATTN_NORM); - else - sound(self, CHAN_BODY, strData, 1.0f, ATTN_NORM); - break; - case 1005: - NSTalkMonster targ = (NSTalkMonster)self; - targ.Sentence(strData); - break; - case 1008: - if (substring(strData, 0, 1) == "*") - sound(self, CHAN_VOICE, substring(strData, 1, -1), 1.0f, ATTN_NORM); - else - sound(self, CHAN_VOICE, strData, 1.0f, ATTN_NORM); - break; - case 5004: /* view model sound */ - localsound(strData, CHAN_AUTO, 1.0); - break; - case 5001: /* muzzle flash on attachment 0 */ - pSeat->m_eMuzzleflash.alpha = 1.0f; - pSeat->m_eMuzzleflash.scale = 0.35; - pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones; - setmodel(pSeat->m_eMuzzleflash, Event_DataToMuzzleflash(strData)); - break; - case 5011: /* muzzle flash on attachment 1 */ - pSeat->m_eMuzzleflash.alpha = 1.0f; - pSeat->m_eMuzzleflash.scale = 0.35; - pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 1; - setmodel(pSeat->m_eMuzzleflash, Event_DataToMuzzleflash(strData)); - break; - case 5021: /* muzzle flash on attachment 2 */ - pSeat->m_eMuzzleflash.alpha = 1.0f; - pSeat->m_eMuzzleflash.scale = 0.35; - pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 2; - setmodel(pSeat->m_eMuzzleflash, Event_DataToMuzzleflash(strData)); - break; - case 5031: /* muzzle flash on attachment 3 */ - pSeat->m_eMuzzleflash.alpha = 1.0f; - pSeat->m_eMuzzleflash.scale = 0.35; - pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 3; - setmodel(pSeat->m_eMuzzleflash, Event_DataToMuzzleflash(strData)); - break; - default: - print(sprintf("Unknown model event: %f %i %S\n", flTimeStamp, iCode, strData)); - } -} - /* ==================== Event_Callback diff --git a/src/client/view.qc b/src/client/view.qc index fca0b9bd..c2caf334 100644 --- a/src/client/view.qc +++ b/src/client/view.qc @@ -15,7 +15,6 @@ */ var bool autocvar_cg_viewmodelLag = 0; -var bool autocvar_cg_muzzleDLight = 1; void View_Init(void) @@ -33,12 +32,6 @@ View_Init(void) setsize(pSeat->m_eViewModel, [0,0,0], [0,0,0]); - pSeat->m_eMuzzleflash = spawn(); - pSeat->m_eMuzzleflash.classname = "mflash"; - pSeat->m_eMuzzleflash.renderflags = RF_ADDITIVE | RF_FIRSTPERSON; - pSeat->m_eMuzzleflash.effects |= EF_NOSHADOW; - setsize(pSeat->m_eMuzzleflash, [0,0,0], [0,0,0]); - /* left side */ pSeat->m_eViewModelL = spawn(NSRenderableEntity); pSeat->m_eViewModelL.classname = "vm"; @@ -47,12 +40,6 @@ View_Init(void) pSeat->m_eViewModelL.alpha = 1.0f; pSeat->m_eViewModelL.drawmask = 0; setsize(pSeat->m_eViewModelL, [0,0,0], [0,0,0]); - - pSeat->m_eMuzzleflashL = spawn(); - pSeat->m_eMuzzleflashL.classname = "mflash"; - pSeat->m_eMuzzleflashL.renderflags = RF_ADDITIVE | RF_FIRSTPERSON; - pSeat->m_eMuzzleflashL.effects |= EF_NOSHADOW; - setsize(pSeat->m_eMuzzleflashL, [0,0,0], [0,0,0]); } } } @@ -60,7 +47,10 @@ View_Init(void) void View_SetMuzzleflash(int index) { - pSeat->m_eMuzzleflash.modelindex = (float)index; + NSRenderableEntity viewModel = (NSRenderableEntity)pSeat->m_eViewModelL; + viewModel.m_iMuzzleModel = (float)index; + viewModel = (NSRenderableEntity)pSeat->m_eViewModel; + viewModel.m_iMuzzleModel = (float)index; } void @@ -85,12 +75,6 @@ View_ClearEvents(void) pSeat->m_eViewModelL.frame1time = 0.0f; pSeat->m_eViewModel.frame1time = 0.0f; - pSeat->m_eMuzzleflash.modelindex = - pSeat->m_eMuzzleflashL.modelindex = - pSeat->m_eMuzzleflash.alpha = - pSeat->m_eMuzzleflashL.alpha = - pSeat->m_eMuzzleflash.frame1time = - pSeat->m_eMuzzleflashL.frame1time = 0; } void @@ -139,9 +123,7 @@ void View_DrawViewModel(void) { NSRenderableEntity m_eViewModel = (NSRenderableEntity)pSeat->m_eViewModel; - entity m_eMuzzleflash = pSeat->m_eMuzzleflash; NSRenderableEntity m_eViewModelL = (NSRenderableEntity)pSeat->m_eViewModelL; - entity m_eMuzzleflashL = pSeat->m_eMuzzleflashL; player pl = __NULL__; NSClient cl = (NSClient)pSeat->m_ePlayer; @@ -185,7 +167,38 @@ View_DrawViewModel(void) m_eViewModelL.SetRenderColor(pl.GetRenderColor()); m_eViewModelL.SetRenderAmt(pl.GetRenderAmt()); - View_UpdateWeapon(pl, m_eViewModel, m_eMuzzleflash); + /* used to be View_UpdateWeapon */ + /* only bother upon change */ + if (pSeat->m_iLastWeapon != pl.activeweapon) { + pSeat->m_iOldWeapon = pSeat->m_iLastWeapon; + pSeat->m_iLastWeapon = pl.activeweapon; + + if (pl.activeweapon) { + /* we forced a weapon call outside the prediction, + * thus we need to update all the net variables to + * make sure these updates are recognized. this is + * vile but it'll have to do for now */ + SAVE_STATE(pl.w_attack_next) + SAVE_STATE(pl.w_idle_next) + SAVE_STATE(pl.viewzoom) + SAVE_STATE(pl.weapontime) + SAVE_STATE(pl.weaponframe) + + /* hack, we changed the wep, move this into Game_Input/PMove */ + Weapons_Draw(pl); + + /* we forced a weapon call outside the prediction, + * thus we need to update all the net variables to + * make sure these updates are recognized. this is + * vile but it'll have to do for now */ + ROLL_BACK(pl.w_attack_next) + ROLL_BACK(pl.w_idle_next) + ROLL_BACK(pl.viewzoom) + ROLL_BACK(pl.weapontime) + ROLL_BACK(pl.weaponframe) + } + } + float fBaseTime2 = m_eViewModel.frame1time; float fBaseTime = m_eViewModel.frame1time; m_eViewModelL.frame = m_eViewModel.frame = pl.weaponframe; @@ -194,8 +207,12 @@ View_DrawViewModel(void) m_eViewModel.frame2time = m_eViewModel.frame1time = pl.weapontime; Event_Callback(m_eViewModel.frame1time, fBaseTime2); + + entity oldSelf = self; + self = m_eViewModel; processmodelevents(m_eViewModel.modelindex, m_eViewModel.frame, fBaseTime, - m_eViewModel.frame1time, ClientGame_ModelEvent); + m_eViewModel.frame1time, NSRenderableEntity::HandleAnimEvent); + self = oldSelf; makevectors(view_angles); @@ -265,30 +282,6 @@ View_DrawViewModel(void) /* only draw the model when it's 'enabled'... */ if (m_eViewModel.alpha != 0.0f) { - if (m_eMuzzleflash.alpha > 0.0f) { - makevectors(getproperty(VF_ANGLES)); - m_eMuzzleflash.origin = gettaginfo(m_eViewModel, m_eMuzzleflash.skin); - m_eMuzzleflash.angles = m_eViewModel.angles; - m_eMuzzleflash.angles[2] += (random() * 10) - 5; - - if (autocvar_cg_muzzleDLight == true) - dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 256, [1,0.45,0]); - - setorigin(m_eMuzzleflash, m_eMuzzleflash.origin); - addentity(m_eMuzzleflash); - } - if (m_eMuzzleflashL.alpha > 0.0f) { - makevectors(getproperty(VF_ANGLES)); - m_eMuzzleflashL.origin = gettaginfo(m_eViewModelL, m_eMuzzleflashL.skin); - m_eMuzzleflashL.angles = m_eViewModelL.angles; - m_eMuzzleflashL.angles[2] += (random() * 10) - 5; - - if (autocvar_cg_muzzleDLight == true) - dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 256, [1,0.45,0]); - - setorigin(m_eMuzzleflashL, m_eMuzzleflashL.origin); - addentity(m_eMuzzleflashL); - } setorigin(m_eViewModel, m_eViewModel.origin); setorigin(m_eViewModelL, m_eViewModel.origin); m_eViewModel.RenderFXPass(); @@ -313,14 +306,6 @@ View_PreDraw(void) void View_PostDraw(void) { - // Take away alpha once it has drawn fully at least once - if (pSeat->m_eMuzzleflash.alpha > 0.0f) { - pSeat->m_eMuzzleflash.alpha -= (clframetime * 16); - } - if (pSeat->m_eMuzzleflashL.alpha > 0.0f) { - pSeat->m_eMuzzleflashL.alpha -= (clframetime * 16); - } - ClientGame_PostDraw(); } diff --git a/src/gs-entbase/shared.src b/src/gs-entbase/shared.src index c71ab385..c4ab3455 100644 --- a/src/gs-entbase/shared.src +++ b/src/gs-entbase/shared.src @@ -14,6 +14,7 @@ shared/env_glow.qc shared/env_projectedtexture.qc shared/env_fog.qc shared/env_fog_controller.qc +shared/env_muzzleflash.qc //shared/env_fire.qc shared/env_steam.qc shared/env_shockwave.qc diff --git a/src/gs-entbase/shared/env_muzzleflash.qc b/src/gs-entbase/shared/env_muzzleflash.qc new file mode 100644 index 00000000..4dfeff0e --- /dev/null +++ b/src/gs-entbase/shared/env_muzzleflash.qc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/*!QUAKED env_muzzleflash (1 .5 0) (-8 -8 -8) (8 8 8) +# OVERVIEW +Creates a muzzleflash effect when triggered. + +# KEYS +- "targetname" : Name +- "parentname" : Entity it's attached to. +- "parentattachment" : Name of the attachment to parent to. +- "scale" : Scale multiplier of the muzzleflash. + +# INPUTS +- "Fire" : Triggers the effect. + +# TRIVIA +This entity was introduced in Half-Life 2 (2004) +*/ +class +env_muzzleflash:NSEntity +{ +public: + void env_muzzleflash(void); + +#ifdef SERVER + virtual void SpawnKey(string, string); + virtual void Input(entity, string, string); + virtual void Input(entity, string, string); + virtual void Trigger(entity, triggermode_t); +#endif + +#ifdef CLIENT + virtual float predraw(void); +#endif + +private: + string m_strAttachmentName; + int m_iAttachment; + NSRenderableEntity m_eOwner; + string m_strModel; + string m_strParticle; + entity m_eMuzzler; /* lol */ +}; + +void +env_muzzleflash::env_muzzleflash(void) +{ + m_iAttachment = 0i; + m_eOwner = __NULL__; + m_strModel = __NULL__; + m_strParticle = __NULL__; + scale = 1.0f; +} + +#ifdef SERVER +void +env_muzzleflash::SpawnKey(string keyName, string setValue) +{ + switch (keyName) { + case "parentname": + m_parent = ReadString(setValue); + break; + case "parentattachment": + m_strAttachmentName = ReadString(setValue); + break; + case "attachment": + m_iAttachment = ReadInt(setValue); + break; + case "scale": + scale = ReadFloat(setValue); + break; + case "model": + model = ReadString(setValue); + break; + default: + super::SpawnKey(keyName, setValue); + break; + } +} + +void +env_muzzleflash::Input(entity eAct, string strKey, string strValue) +{ + switch (strKey) { + case "Fire": + Trigger(eAct, TRIG_TOGGLE); + break; + default: + super::Input(eAct, strKey, strValue); + } +} + +void +env_muzzleflash::Trigger(entity theActivator, triggermode_t triggerState) +{ + NSEntity targetEnt; + vector targetPosition = GetOrigin(); + + if (m_parent) { + targetEnt = (NSEntity)find(world, ::targetname, m_parent); + } + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_MUZZLEFLASH); + WriteEntity(MSG_MULTICAST, targetEnt); + WriteByte(MSG_MULTICAST, m_iAttachment); + WriteFloat(MSG_MULTICAST, scale); + WriteShort(MSG_MULTICAST, getmodelindex(model)); + + msg_entity = this; + multicast(origin, MULTICAST_PVS); +} +#endif + +#ifdef CLIENT +var bool autocvar_cg_muzzleDLight = true; +float +env_muzzleflash::predraw(void) +{ + vector muzzlePos = gettaginfo(m_eOwner, (float)m_iAttachment); + + if (autocvar_cg_muzzleDLight == true) + dynamiclight_add(muzzlePos, 256, [1,0.45,0]); + + setorigin(this, muzzlePos); + effects = EF_ADDITIVE; + angles = m_eOwner.angles; + addentity(this); + alpha -= frametime * 16.0f; + + if (alpha <= 0.0) { + Destroy(); + } + + return (PREDRAW_NEXT); +} + +/* basically re-implementing TE_BEAMCYLINDER*/ +void +EV_MuzzleFlash_Parse(void) +{ + env_muzzleflash tempMuzzle = spawn(env_muzzleflash); + + tempMuzzle.m_eOwner = findfloat(world, entnum, readentitynum()); + tempMuzzle.m_iAttachment = readbyte(); + tempMuzzle.scale = readfloat(); + tempMuzzle.modelindex = readshort(); + tempMuzzle.alpha = 1.0f; + + setorigin(tempMuzzle, tempMuzzle.m_eOwner.origin); + setsize(tempMuzzle, [0,0,0], [0,0,0]); + tempMuzzle.drawmask = MASK_ENGINE; +} + +void +EV_MuzzleFlash_Create(entity muzzleOwner, int attachmentID, float muzzleScale, int muzzleModel) +{ + env_muzzleflash tempMuzzle = spawn(env_muzzleflash); + + tempMuzzle.m_eOwner = muzzleOwner; + tempMuzzle.m_iAttachment = attachmentID; + tempMuzzle.scale = muzzleScale; + tempMuzzle.alpha = 1.0f; + tempMuzzle.modelindex = muzzleModel; + setmodel(tempMuzzle, modelnameforindex(muzzleModel)); + + setorigin(tempMuzzle, tempMuzzle.m_eOwner.origin); + setsize(tempMuzzle, [0,0,0], [0,0,0]); + tempMuzzle.drawmask = MASK_ENGINE; +} +#endif \ No newline at end of file diff --git a/src/shared/NSRenderableEntity.h b/src/shared/NSRenderableEntity.h index 053ddddb..d13b4850 100644 --- a/src/shared/NSRenderableEntity.h +++ b/src/shared/NSRenderableEntity.h @@ -108,6 +108,11 @@ private: PREDICTED_VECTOR(m_vecRenderColor) PREDICTED_VECTOR(m_vecAxialScale) + /* either a sprite model or a particle */ + PREDICTED_INT(m_iMuzzleModel) + PREDICTED_INT(m_iMuzzlePart) + PREDICTED_FLOAT(m_flMuzzleScale) + /* model events */ float m_flBaseTime; diff --git a/src/shared/NSRenderableEntity.qc b/src/shared/NSRenderableEntity.qc index 1e9d407c..61902c64 100644 --- a/src/shared/NSRenderableEntity.qc +++ b/src/shared/NSRenderableEntity.qc @@ -35,6 +35,9 @@ NSRenderableEntity::NSRenderableEntity(void) effects |= EF_NOSHADOW; + m_iMuzzleModel = 0i; + m_flMuzzleScale = 0.25f; + #ifdef CLIENT drawmask = MASK_ENGINE; #endif @@ -971,19 +974,19 @@ NSRenderableEntity::HandleAnimEvent(float flTimeStamp, int iCode, string strData break; #ifdef CLIENT case 5004: /* view model sound? */ - sound(this, CHAN_AUTO, strData, 1.0, ATTN_NORM); + sound(this, CHAN_AUTO, strData, 1.0, ATTN_NORM, 100, SOUNDFLAG_FOLLOW | SOUNDFLAG_NOSPACIALISE); break; case 5001: /* muzzle flash on attachment 0 */ - vector muzzleOrg = gettaginfo(this, m_iNumBones); + EV_MuzzleFlash_Create(this, m_iNumBones, m_flMuzzleScale, m_iMuzzleModel); break; case 5011: /* muzzle flash on attachment 1 */ - vector muzzleOrg = gettaginfo(this, m_iNumBones + 1); + EV_MuzzleFlash_Create(this, m_iNumBones + 1, m_flMuzzleScale, m_iMuzzleModel); break; case 5021: /* muzzle flash on attachment 2 */ - vector muzzleOrg = gettaginfo(this, m_iNumBones + 2); + EV_MuzzleFlash_Create(this, m_iNumBones + 2, m_flMuzzleScale, m_iMuzzleModel); break; case 5031: /* muzzle flash on attachment 3 */ - vector muzzleOrg = gettaginfo(this, m_iNumBones + 3); + EV_MuzzleFlash_Create(this, m_iNumBones + 3, m_flMuzzleScale, m_iMuzzleModel); break; #else case 1003: /* trigger SSQC entity with specific targetname */ diff --git a/src/shared/events.h b/src/shared/events.h index e766ff17..17ad06d9 100644 --- a/src/shared/events.h +++ b/src/shared/events.h @@ -56,6 +56,7 @@ enum EV_DECALGROUP, EV_BREAKMODEL, EV_BEAMCYLINDER, + EV_MUZZLEFLASH, EV_TRACEDEBUG, EV_SEPARATOR };