mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
315 lines
9.8 KiB
C++
315 lines
9.8 KiB
C++
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
// mdl: Only steam this often to keep performance hits when hitting several pipes
|
|
#define STEAM_DELAY 1000.0f
|
|
|
|
|
|
hhWoundManagerRenderEntity::hhWoundManagerRenderEntity( const idEntity* self ) {
|
|
this->self = self;
|
|
}
|
|
|
|
hhWoundManagerRenderEntity::~hhWoundManagerRenderEntity() {
|
|
}
|
|
|
|
void hhWoundManagerRenderEntity::DetermineWoundInfo( const trace_t& trace, const idVec3 velocity, jointHandle_t& jointNum, idVec3& origin, idVec3& normal, idVec3& dir ) {
|
|
jointNum = CLIPMODEL_ID_TO_JOINT_HANDLE( trace.c.id );
|
|
|
|
origin = trace.c.point;
|
|
normal = trace.c.normal;
|
|
dir = velocity;
|
|
dir.Normalize();
|
|
}
|
|
|
|
void hhWoundManagerRenderEntity::AddWounds( const idDeclEntityDef *def, surfTypes_t matterType, jointHandle_t jointNum, const idVec3& origin, const idVec3& normal, const idVec3& dir ) {
|
|
PROFILE_SCOPE("AddWounds", PROFMASK_COMBAT);
|
|
if( !def ) {
|
|
return;
|
|
}
|
|
|
|
// Do a check so we don't spawn too many steam effects at once
|
|
if( matterType == SURFTYPE_PIPE ) {
|
|
if ( gameLocal.time > gameLocal.GetSteamTime() ) {
|
|
// Mark the current steam time
|
|
gameLocal.SetSteamTime( gameLocal.time + STEAM_DELAY );
|
|
} else {
|
|
// Don't steam on this hit
|
|
matterType = SURFTYPE_METAL;
|
|
}
|
|
}
|
|
|
|
ApplyWound( def->dict, gameLocal.MatterTypeToMatterKey("fx_wound", matterType), jointNum, origin, normal, dir ); //smoke_wound conflicted with smoke
|
|
|
|
const char* impactMarkKey = gameLocal.MatterTypeToMatterKey("mtr", matterType);
|
|
ApplyMark( def->dict, impactMarkKey, origin, normal, dir );
|
|
ApplySplatExit( def->dict, impactMarkKey, origin, normal, dir );
|
|
}
|
|
|
|
void hhWoundManagerRenderEntity::ApplyMark( const idDict& damageDict, const char* impactMarkKey, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir ) {
|
|
PROFILE_SCOPE("ApplyMark", PROFMASK_COMBAT);
|
|
idList<idStr> strList;
|
|
|
|
const idDict* decalDict = gameLocal.FindEntityDefDict( damageDict.GetString("def_entranceMark_decal"), false );
|
|
if( !decalDict || !self->spawnArgs.GetBool("accepts_decals", "1") ) {
|
|
return;
|
|
}
|
|
|
|
if( !impactMarkKey || !impactMarkKey[0] ) {
|
|
return;
|
|
}
|
|
|
|
idStr impactMark = decalDict->RandomPrefix( impactMarkKey, gameLocal.random );
|
|
if( !impactMark.Length() ) {
|
|
return;
|
|
}
|
|
|
|
if( self->IsType(idBrittleFracture::Type) ) {
|
|
static_cast<idBrittleFracture *>( self.GetEntity() )->ProjectDecal( origin, normal, gameLocal.GetTime(), NULL );
|
|
} else {
|
|
hhUtils::SplitString( impactMark, strList );
|
|
|
|
//bjk: uses normal now
|
|
float depth = 10.0f;
|
|
|
|
for( int ix = strList.Num() - 1; ix >= 0; --ix ) {
|
|
gameLocal.ProjectDecal( origin, -normal, depth, true, hhMath::Lerp(decalDict->GetVec2("mark_size"), gameLocal.random.RandomFloat()), strList[ix] ); //HUMANHEAD bjk: using normal
|
|
}
|
|
}
|
|
|
|
//rww - if we were running this code on the server, we could do this:
|
|
/*
|
|
idBitMsg msg;
|
|
byte msgBuf[MAX_EVENT_PARAM_SIZE];
|
|
idVec2 markSize;
|
|
|
|
msg.Init(msgBuf, sizeof(msgBuf));
|
|
msg.BeginWriting();
|
|
msg.WriteFloat(origin[0]);
|
|
msg.WriteFloat(origin[1]);
|
|
msg.WriteFloat(origin[2]);
|
|
msg.WriteDir(normal, 24);
|
|
msg.WriteDir(dir, 24);
|
|
|
|
//AGH. rwwFIXME: configstring type system?
|
|
msg.WriteString(impactMark);
|
|
|
|
if (self->IsType(idBrittleFracture::Type))
|
|
{
|
|
msg.WriteBool(true);
|
|
msg.WriteShort(self.GetEntity()->entityNumber);
|
|
}
|
|
else
|
|
{
|
|
msg.WriteBool(false);
|
|
markSize = damageDict->GetVec2("mark_size");
|
|
msg.WriteFloat(markSize[0]);
|
|
msg.WriteFloat(markSize[1]);
|
|
}
|
|
|
|
self.GetEntity()->BroadcastEventReroute(idEntity::EVENT_PROJECT_DECAL, &msg);
|
|
*/
|
|
}
|
|
|
|
void hhWoundManagerRenderEntity::ApplyWound( const idDict& damageDict, const char* woundKey, jointHandle_t jointNum, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir ) {
|
|
PROFILE_SCOPE("ApplyWound", PROFMASK_COMBAT);
|
|
|
|
if ( !g_bloodEffects.GetBool() ) {
|
|
return;
|
|
}
|
|
|
|
if (! (self->fl.acceptsWounds && woundKey && *woundKey)) {
|
|
return;
|
|
}
|
|
|
|
const idDict* woundDict = gameLocal.FindEntityDefDict( damageDict.GetString("def_entranceWound"), false );
|
|
if( !woundDict ) {
|
|
return;
|
|
}
|
|
|
|
const char* woundName = woundDict->RandomPrefix( woundKey, gameLocal.random );
|
|
if( !woundName || !woundName[0] ) {
|
|
return;
|
|
}
|
|
|
|
// for steam sound
|
|
idStr sndKey = "snd_";
|
|
sndKey += woundKey;
|
|
const char *sndName = woundDict->GetString(sndKey.c_str());
|
|
bool bHasSound = idStr::Length(sndName) > 0;
|
|
|
|
/* // Optimization: works but it could cause problems in some of our non-euclidean spaces if
|
|
// a portal destination is less than 1024 units away
|
|
if (!bHasSound && !gameLocal.isMultiplayer && gameLocal.GetLocalPlayer()) {
|
|
// Don't spawn purely visual wounds when behind player
|
|
idVec3 playerOrigin = gameLocal.GetLocalPlayer()->GetOrigin();
|
|
idMat3 playerView = gameLocal.GetLocalPlayer()->GetAxis();
|
|
idVec3 toSpot = origin - playerOrigin;
|
|
float dist = toSpot.Normalize();
|
|
|
|
// Only do this optimization when within some distance to avoid problems with the effects being on the
|
|
// other side of a portal
|
|
if (dist < 1024.0f) {
|
|
float dot = toSpot * playerView[0];
|
|
|
|
// If spot is behind me and not right on the border, throw it out
|
|
if (dot < 0.0f && dist > 64.0f) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
hhFxInfo fxInfo;
|
|
fxInfo.SetNormal( normal );
|
|
if( !self.GetEntity()->IsType(idStaticEntity::Type) ) {
|
|
fxInfo.SetEntity( self.GetEntity() );
|
|
}
|
|
fxInfo.RemoveWhenDone( true );
|
|
//correct for server: self->BroadcastFxInfo( woundName, origin, dir.ToMat3(), &fxInfo );
|
|
//however this function should -only- be called on the client.
|
|
if (gameLocal.isServer && !gameLocal.GetLocalPlayer()) {
|
|
gameLocal.Error("hhWoundManagerRenderEntity::ApplyWound called on non-listen server!");
|
|
}
|
|
hhEntityFx *fx = self.GetEntity()->SpawnFxLocal( woundName, origin, normal.ToMat3(), &fxInfo, true );
|
|
if (fx) {
|
|
fx->fl.neverDormant = true;
|
|
|
|
if (bHasSound) {
|
|
const idSoundShader *shader = declManager->FindSound( sndName );
|
|
fx->StartSoundShader(shader, SND_CHANNEL_ANY, 0, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhWoundManagerRenderEntity::ApplySplatExit( const idDict& damageDict, const char* impactMarkKey, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir ) {
|
|
PROFILE_SCOPE("ApplySplatExit", PROFMASK_COMBAT);
|
|
|
|
if ( !g_bloodEffects.GetBool() ) {
|
|
return;
|
|
}
|
|
|
|
if( gameLocal.random.RandomFloat() > 0.8f ) {
|
|
return;
|
|
}
|
|
|
|
const idDict *splatDict = gameLocal.FindEntityDefDict( damageDict.GetString("def_exitSplat"), false );
|
|
if( !splatDict || !self->spawnArgs.GetBool("produces_splats") ) {
|
|
return;
|
|
}
|
|
|
|
if( !impactMarkKey || !impactMarkKey[0] ) {
|
|
return;
|
|
}
|
|
|
|
const char* decal = splatDict->RandomPrefix( impactMarkKey, gameLocal.random );
|
|
if( !decal || !decal[0] ) {
|
|
return;
|
|
}
|
|
|
|
trace_t trace;
|
|
if( gameLocal.clip.TracePoint(trace, origin, origin+dir*200.0f, MASK_SHOT_RENDERMODEL, self.GetEntity() ) ) {
|
|
|
|
surfTypes_t matterType = gameLocal.GetMatterType( self.GetEntity(), trace.c.material, "ApplySplatExit" );
|
|
|
|
gameLocal.ProjectDecal( trace.endpos,
|
|
-trace.c.normal,
|
|
splatDict->GetFloat("splat_dist", "10"),
|
|
true,
|
|
hhMath::Lerp(splatDict->GetVec2("mark_size"),
|
|
gameLocal.random.RandomFloat()), decal );
|
|
}
|
|
}
|
|
|
|
hhWoundManagerAnimatedEntity::hhWoundManagerAnimatedEntity( const idEntity* self ) : hhWoundManagerRenderEntity(self) {
|
|
}
|
|
|
|
hhWoundManagerAnimatedEntity::~hhWoundManagerAnimatedEntity() {
|
|
}
|
|
|
|
void hhWoundManagerAnimatedEntity::ApplyMark( const idDict& damageDict, const char* impactMarkKey, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir ) {
|
|
idList<idStr> strList;
|
|
|
|
const idDict* overlayDict = gameLocal.FindEntityDefDict( damageDict.GetString("def_entranceMark_overlay"), false );
|
|
if( !overlayDict || !self->spawnArgs.GetBool("accepts_decals", "1") ) {
|
|
return;
|
|
}
|
|
|
|
if( !impactMarkKey && !impactMarkKey[0] ) {
|
|
return;
|
|
}
|
|
|
|
idStr impactMark = overlayDict->RandomPrefix( impactMarkKey, gameLocal.random );
|
|
if( !impactMark.Length() ) {
|
|
return;
|
|
}
|
|
|
|
hhUtils::SplitString( impactMark, strList );
|
|
for( int ix = strList.Num() - 1; ix >= 0; --ix ) {
|
|
self->ProjectOverlay( origin, dir, overlayDict->GetFloat("mark_size"), strList[ix] );
|
|
}
|
|
}
|
|
|
|
void hhWoundManagerAnimatedEntity::ApplyWound( const idDict& damageDict, const char* woundKey, jointHandle_t jointNum, const idVec3 &origin, const idVec3 &normal, const idVec3 &dir ) {
|
|
|
|
if (! (self->fl.acceptsWounds && woundKey && *woundKey && jointNum != INVALID_JOINT)) {
|
|
return;
|
|
}
|
|
|
|
const idDict* woundDict = gameLocal.FindEntityDefDict( damageDict.GetString("def_entranceWound"), false );
|
|
if( !woundDict ) {
|
|
return;
|
|
}
|
|
|
|
const char* woundName = woundDict->RandomPrefix( woundKey, gameLocal.random );
|
|
if( !woundName || !woundName[0] ) {
|
|
return;
|
|
}
|
|
|
|
#if !GOLD
|
|
//correct for server: self->BroadcastFxInfo( woundName, origin, dir.ToMat3(), &fxInfo );
|
|
//however this function should -only- be called on the client.
|
|
if (gameLocal.isServer && !gameLocal.GetLocalPlayer()) {
|
|
gameLocal.Error("hhWoundManagerRenderEntity::ApplyWound called on non-listen server!");
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
hhFxInfo fxInfo;
|
|
fxInfo.SetNormal( normal );
|
|
fxInfo.SetEntity( self.GetEntity() );
|
|
fxInfo.RemoveWhenDone( true );
|
|
|
|
hhEntityFx* fx = self.GetEntity()->SpawnFxLocal( woundName, origin, normal.ToMat3(), &fxInfo, true );
|
|
if (fx) {
|
|
fx->fl.neverDormant = true;
|
|
fx->BindToJoint( self.GetEntity(), jointNum, true);
|
|
}
|
|
#else //rww - do this in some way that is less hideous and slow (specialcased here, cause this function gets a lot of use)
|
|
//(avoids binding twice and some other needless logic)
|
|
if ( !g_skipFX.GetBool() ) {
|
|
idDict fxArgs;
|
|
|
|
fxArgs.Set( "fx", woundName );
|
|
fxArgs.SetBool( "start", true );
|
|
fxArgs.SetBool( "removeWhenDone", true );
|
|
fxArgs.SetVector( "origin", origin );
|
|
fxArgs.SetMatrix( "rotation", normal.ToMat3() );
|
|
|
|
hhEntityFx *fx = (hhEntityFx *)gameLocal.SpawnClientObject( "func_fx", &fxArgs );
|
|
if (fx) {
|
|
hhFxInfo fxInfo;
|
|
fxInfo.SetNormal( normal );
|
|
fxInfo.SetEntity( self.GetEntity() );
|
|
fxInfo.RemoveWhenDone( true );
|
|
fx->SetFxInfo( fxInfo );
|
|
|
|
fx->fl.neverDormant = true;
|
|
fx->BindToJoint( self.GetEntity(), jointNum, true );
|
|
fx->Show();
|
|
}
|
|
}
|
|
#endif
|
|
}
|