!!! Mods/games should now remove modelevent.qc and view.qc from their csprogs !!!

env_muzzleflash: Initial implementation of this Half-Life 2 entity
Client: model events are for viewmodels are now handled by NSRenderableEntity.
This commit is contained in:
Marco Cawthorne 2023-10-12 13:58:32 -07:00
parent 05ed789f9a
commit 900cf43bd0
Signed by: eukara
GPG key ID: CE2032F0A2882A22
9 changed files with 241 additions and 183 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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 */

View file

@ -56,6 +56,7 @@ enum
EV_DECALGROUP,
EV_BREAKMODEL,
EV_BEAMCYLINDER,
EV_MUZZLEFLASH,
EV_TRACEDEBUG,
EV_SEPARATOR
};