openmohaa/code/cgame/cg_specialfx.cpp
2023-05-07 14:45:23 +02:00

1053 lines
25 KiB
C++

/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// DESCRIPTION:
// Special Effects code
#include "cg_local.h"
#include "cg_commands.h"
#include "surfaceflags.h"
#include "cg_specialfx.h"
extern refEntity_t *current_entity;
extern dtiki_t* current_tiki;
static vec3_t g_vFootstepMins = { -4, -4, 0 };
static vec3_t g_vFootstepMaxs = { 4, 4, 2 };
static vec3_t g_vLadderstepMins;
static vec3_t g_vLadderstepMaxs;
ClientSpecialEffectsManager sfxManager;
Event EV_SFX_EffectDelay
(
"effectdelay",
EV_DEFAULT,
"iivvvvv",
"iEffect iCurrEmitter vPos vAngles vAxisA vAxisB vAxisC",
"Resumes the execution of iEffect effect from its iCurrEmitter emitter."
);
CLASS_DECLARATION(Listener, ClientSpecialEffectsManager, NULL)
{
{ &EV_SFX_EffectDelay, &ClientSpecialEffectsManager::ContinueEffectExecution },
{ NULL, NULL }
};
specialeffectcommand_t::specialeffectcommand_t()
{
emitter = NULL;
fCommandTime = 0.0f;
endfcn = NULL;
}
specialeffect_t::specialeffect_t()
{
m_iCommandCount = 0;
}
specialeffectcommand_t* specialeffect_t::AddNewCommand()
{
if (m_iCommandCount == MAX_SPECIAL_EFFECT_COMMANDS - 1) {
return NULL;
}
m_commands[m_iCommandCount] = new specialeffectcommand_t;
return m_commands[m_iCommandCount++];
}
ClientSpecialEffectsManager::ClientSpecialEffectsManager()
{
m_bEffectsLoaded = 0;
m_iNumPendingEvents = 0;
}
void ClientSpecialEffectsManager::LoadEffects()
{
int i, j, k;
const char* szEffectModel;
float axis[3][3];
specialeffect_t* pEffect;
if (m_bEffectsLoaded) {
return;
}
cgi.DPrintf("Loading Special Effects...\n");
AxisClear(axis);
for (i = 0; i < MAX_SPECIAL_EFFECTS; i++)
{
switch (i)
{
case 0:
szEffectModel = "models/fx/bh_paper_lite.tik";
break;
case 1:
szEffectModel = "models/fx/bh_paper_hard.tik";
break;
case 2:
szEffectModel = "models/fx/bh_wood_lite.tik";
break;
case 3:
szEffectModel = "models/fx/bh_wood_hard.tik";
break;
case 4:
szEffectModel = "models/fx/bh_metal_lite.tik";
break;
case 5:
szEffectModel = "models/fx/bh_metal_hard.tik";
break;
case 6:
szEffectModel = "models/fx/bh_stone_lite.tik";
break;
case 7:
szEffectModel = "models/fx/bh_stone_hard.tik";
break;
case 8:
szEffectModel = "models/fx/bh_dirt_lite.tik";
break;
case 9:
szEffectModel = "models/fx/bh_dirt_hard.tik";
break;
case 10:
szEffectModel = "models/fx/bh_metal_lite.tik";
break;
case 11:
szEffectModel = "models/fx/bh_metal_hard.tik";
break;
case 12:
szEffectModel = "models/fx/bh_grass_lite.tik";
break;
case 13:
szEffectModel = "models/fx/bh_grass_hard.tik";
break;
case 14:
szEffectModel = "models/fx/bh_mud_lite.tik";
break;
case 15:
szEffectModel = "models/fx/bh_mud_hard.tik";
break;
case 16:
szEffectModel = "models/fx/bh_water_lite.tik";
break;
case 17:
szEffectModel = "models/fx/bh_water_hard.tik";
break;
case 18:
szEffectModel = "models/fx/bh_glass_lite.tik";
break;
case 19:
szEffectModel = "models/fx/bh_glass_hard.tik";
break;
case 20:
szEffectModel = "models/fx/bh_stone_lite.tik";
break;
case 21:
szEffectModel = "models/fx/bh_stone_hard.tik";
break;
case 22:
szEffectModel = "models/fx/bh_sand_lite.tik";
break;
case 23:
szEffectModel = "models/fx/bh_sand_hard.tik";
break;
case 24:
szEffectModel = "models/fx/bh_foliage_lite.tik";
break;
case 25:
szEffectModel = "models/fx/bh_foliage_hard.tik";
break;
case 26:
szEffectModel = "models/fx/bh_snow_lite.tik";
break;
case 27:
szEffectModel = "models/fx/bh_snow_hard.tik";
break;
case 28:
szEffectModel = "models/fx/bh_carpet_lite.tik";
break;
case 29:
szEffectModel = "models/fx/bh_carpet_hard.tik";
break;
case 30:
szEffectModel = "models/fx/bh_human_uniform_lite.tik";
break;
case 31:
szEffectModel = "models/fx/bh_human_uniform_hard.tik";
break;
case 32:
szEffectModel = "models/fx/water_trail_bubble.tik";
break;
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
case 58:
case 59:
case 60:
case 61:
case 62:
szEffectModel = "models/fx/bh_stone_hard.tik";
break;
case 63:
szEffectModel = "models/fx/grenexp_base.tik";
break;
case 64:
szEffectModel = "models/fx/bazookaexp_base.tik";
break;
case 65:
szEffectModel = "models/fx/grenexp_paper.tik";
break;
case 66:
szEffectModel = "models/fx/grenexp_wood.tik";
break;
case 67:
szEffectModel = "models/fx/grenexp_metal.tik";
break;
case 68:
szEffectModel = "models/fx/grenexp_stone.tik";
break;
case 69:
szEffectModel = "models/fx/grenexp_dirt.tik";
break;
case 70:
szEffectModel = "models/fx/grenexp_metal.tik";
break;
case 71:
szEffectModel = "models/fx/grenexp_grass.tik";
break;
case 72:
szEffectModel = "models/fx/grenexp_mud.tik";
break;
case 73:
szEffectModel = "models/fx/grenexp_water.tik";
break;
case 74:
case 98:
continue;
case 75:
szEffectModel = "models/fx/grenexp_gravel.tik";
break;
case 76:
szEffectModel = "models/fx/grenexp_sand.tik";
break;
case 77:
szEffectModel = "models/fx/grenexp_foliage.tik";
break;
case 78:
szEffectModel = "models/fx/grenexp_snow.tik";
break;
case 79:
szEffectModel = "models/fx/grenexp_carpet.tik";
break;
case 80:
szEffectModel = "models/fx/water_ripple_still.tik";
break;
case 81:
szEffectModel = "models/fx/water_ripple_moving.tik";
break;
case 82:
szEffectModel = "models/fx/barrel_oil_leak_big.tik";
break;
case 83:
szEffectModel = "models/fx/barrel_oil_leak_medium.tik";
break;
case 84:
szEffectModel = "models/fx/barrel_oil_leak_small.tik";
break;
case 85:
szEffectModel = "models/fx/barrel_oil_leak_splat.tik";
break;
case 86:
szEffectModel = "models/fx/barrel_water_leak_big.tik";
break;
case 87:
szEffectModel = "models/fx/barrel_water_leak_medium.tik";
break;
case 88:
szEffectModel = "models/fx/barrel_water_leak_small.tik";
break;
case 89:
szEffectModel = "models/fx/barrel_water_leak_splat.tik";
break;
case 90:
szEffectModel = "models/fx/fs_light_dust.tik";
break;
case 91:
szEffectModel = "models/fx/fs_heavy_dust.tik";
break;
case 92:
szEffectModel = "models/fx/fs_dirt.tik";
break;
case 93:
szEffectModel = "models/fx/fs_grass.tik";
break;
case 94:
szEffectModel = "models/fx/fs_mud.tik";
break;
case 95:
szEffectModel = "models/fx/fs_puddle.tik";
break;
case 96:
szEffectModel = "models/fx/fs_sand.tik";
break;
case 97:
szEffectModel = "models/fx/fs_snow.tik";
break;
default:
szEffectModel = "models/fx/bh_stone_hard.tik";
}
pEffect = &m_effects[i];
commandManager.SetCurrentSFX(pEffect);
cgi.R_SpawnEffectModel(szEffectModel, vec_zero, axis);
commandManager.ClearCurrentSFX();
for (j = 0; j < pEffect->m_iCommandCount - 1; j++)
{
for (k = 0; k < j; k++)
{
if (pEffect->m_commands[k]->fCommandTime > pEffect->m_commands[k + 1]->fCommandTime)
{
specialeffectcommand_t* pCur = pEffect->m_commands[k];
pEffect->m_commands[k] = pEffect->m_commands[k + 1];
pEffect->m_commands[k + 1] = pCur;
}
}
}
}
m_bEffectsLoaded = qtrue;
}
void CG_InitializeSpecialEffectsManager()
{
sfxManager.LoadEffects();
}
void CG_AddPendingEffects()
{
if (sfxManager.EffectsPending()) {
sfxManager.ProcessPendingEvents();
}
}
void ClientSpecialEffectsManager::ContinueEffectExecution(Event* ev)
{
Vector norm;
float axis[3][3];
norm = ev->GetVector(5);
VectorCopy(norm, axis[0]);
norm = ev->GetVector(6);
VectorCopy(norm, axis[1]);
norm = ev->GetVector(7);
VectorCopy(norm, axis[2]);
ExecuteEffect(
ev->GetInteger(1),
ev->GetInteger(2),
ev->GetVector(3),
ev->GetVector(4),
axis
);
}
void ClientSpecialEffectsManager::ExecuteEffect(int iEffect, int iStartCommand, Vector vPos, Vector vAngles, float axis[3][3])
{
int i;
int iCommandCount;
float fStartCommandTime;
specialeffect_t* pEffect;
specialeffectcommand_t* pCommand;
refEntity_t* old_entity;
dtiki_t* old_tiki;
refEntity_t tmpEntity;
memset(&tmpEntity, 0, sizeof(tmpEntity));
VectorCopy(((const float*)vPos), tmpEntity.origin);
tmpEntity.scale = 1.0;
tmpEntity.renderfx = 0;
tmpEntity.shaderRGBA[3] = -1;
pEffect = &this->m_effects[iEffect];
iCommandCount = pEffect->m_iCommandCount;
if (pEffect->m_iCommandCount)
{
old_entity = current_entity;
old_tiki = current_tiki;
current_entity = NULL;
current_tiki = NULL;
pCommand = pEffect->m_commands[iStartCommand];
fStartCommandTime = pCommand->fCommandTime;
for (i = iStartCommand; i < iCommandCount; i++)
{
pCommand = pEffect->m_commands[i];
if (pCommand->fCommandTime > fStartCommandTime)
{
Event ev1(EV_SFX_EffectDelay);
ev1.AddInteger(iEffect);
ev1.AddInteger(i);
ev1.AddVector(vPos);
ev1.AddVector(vAngles);
ev1.AddVector(axis[0]);
ev1.AddVector(axis[1]);
ev1.AddVector(axis[2]);
PostEvent(ev1, pCommand->fCommandTime - fStartCommandTime);
++m_iNumPendingEvents;
break;
}
if (pCommand->pEvent)
{
current_entity = &tmpEntity;
current_tiki = tmpEntity.tiki;
// give a reference to it so the event doesn't get deleted
commandManager.ProcessEvent(*pCommand->pEvent);
}
else if (pCommand->emitter && pCommand->endfcn)
{
current_entity = NULL;
current_tiki = NULL;
pCommand->emitter->cgd.origin = vPos;
if (pCommand->emitter->cgd.flags & T_ANGLES) {
pCommand->emitter->cgd.angles = vAngles;
}
AxisCopy(axis, pCommand->emitter->axis);
AxisCopy(axis, pCommand->emitter->tag_axis);
pCommand->emitter->cgd.createTime = cg.time;
commandManager.SetSpawnthing(pCommand->emitter);
}
}
current_entity = old_entity;
current_tiki = old_tiki;
}
}
void ClientSpecialEffectsManager::MakeEffect_Normal(int iEffect, Vector vPos, Vector vNormal)
{
Vector vAngles;
float axis[3][3];
vAngles = vNormal.toAngles();
AnglesToAxis(vAngles, axis);
ClientSpecialEffectsManager::ExecuteEffect(iEffect, 0, vPos, vAngles, axis);
}
void ClientSpecialEffectsManager::MakeEffect_Angles(int iEffect, Vector vPos, Vector vAngles)
{
float axis[3][3];
AnglesToAxis((const float*)vAngles, axis);
ClientSpecialEffectsManager::ExecuteEffect(iEffect, 0, vPos, vAngles, axis);
}
void ClientSpecialEffectsManager::MakeEffect_Axis(int iEffect, Vector vPos, float axis[3][3])
{
Vector vAngles;
MatrixToEulerAngles(axis, (float*)vAngles);
ClientSpecialEffectsManager::ExecuteEffect(iEffect, 0, vPos, vAngles, axis);
}
/*
==============================================================
FOOTSTEP CODE
==============================================================
*/
#define GROUND_DISTANCE 8
#define WATER_NO_SPLASH_HEIGHT 16
static void CG_FootstepMain(trace_t* trace, int iRunning, int iEquipment)
{
int contents;
int surftype;
int iEffectNum;
float fVolume;
vec3_t vPos;
vec3_t midlegs;
str sSoundName;
iEffectNum = -1;
VectorCopy(trace->endpos, vPos);
sSoundName = "snd_step_";
contents = CG_PointContents(trace->endpos, -1);
if (contents & MASK_WATER)
{
// take our ground position and trace upwards
VectorCopy(trace->endpos, midlegs);
midlegs[2] += WATER_NO_SPLASH_HEIGHT;
contents = CG_PointContents(midlegs, -1);
if (contents & MASK_WATER)
{
sSoundName += "wade";
}
else
{
sSoundName += "puddle";
iEffectNum = 95;
}
}
else
{
surftype = trace->surfaceFlags & MASK_SURF_TYPE;
switch (surftype)
{
case SURF_FOLIAGE:
sSoundName += "foliage";
iEffectNum = 93;
break;
case SURF_SNOW:
sSoundName += "snow";
iEffectNum = 97;
break;
case SURF_CARPET:
sSoundName += "carpet";
iEffectNum = 90;
break;
case SURF_SAND:
sSoundName += "sand";
iEffectNum = 96;
break;
case SURF_PUDDLE:
sSoundName += "puddle";
iEffectNum = 95;
break;
case SURF_GLASS:
sSoundName += "glass";
iEffectNum = 90;
break;
case SURF_GRAVEL:
sSoundName += "gravel";
iEffectNum = 91;
break;
case SURF_MUD:
sSoundName += "mud";
iEffectNum = 94;
break;
case SURF_DIRT:
sSoundName += "dirt";
iEffectNum = 92;
break;
case SURF_GRILL:
sSoundName += "grill";
iEffectNum = 90;
break;
case SURF_GRASS:
sSoundName += "grass";
iEffectNum = 93;
break;
case SURF_ROCK:
sSoundName += "stone";
iEffectNum = 91;
break;
case SURF_PAPER:
sSoundName += "paper";
iEffectNum = 90;
break;
case SURF_WOOD:
sSoundName += "wood";
iEffectNum = 90;
break;
case SURF_METAL:
sSoundName += "metal";
iEffectNum = 90;
break;
default:
sSoundName += "stone";
iEffectNum = 91;
break;
}
}
if (cg_debugFootsteps->integer) {
cgi.DPrintf("Footstep: %s running = %i effect = %i\n", sSoundName.c_str(), iRunning, surftype);
}
if (iRunning)
{
if (iRunning == -1) {
fVolume = 0.5;
}
else {
fVolume = 1.0;
}
}
else {
fVolume = 0.25;
}
if (!iRunning && cgs.gametype == GT_SINGLE_PLAYER) {
return;
}
commandManager.PlaySound(
sSoundName,
vPos,
-1,
fVolume,
-1,
-1,
1
);
if (iEquipment && random() < 0.3)
{
// also play equipment sound
commandManager.PlaySound(
"snd_step_equipment",
vPos,
-1,
fVolume,
-1,
-1,
1
);
}
}
void CG_Footstep(const char* szTagName, centity_t* ent, refEntity_t* pREnt, int iRunning, int iEquipment)
{
int i;
int iTagNum;
vec3_t vStart, vEnd;
vec3_t midlegs;
vec3_t vMins, vMaxs;
str sSoundName;
trace_t trace;
orientation_t oTag;
// send a trace down from the player to the ground
VectorCopy(ent->lerpOrigin, vStart);
vStart[2] += GROUND_DISTANCE;
if (szTagName)
{
iTagNum = cgi.Tag_NumForName(pREnt->tiki, szTagName);
if (iTagNum != -1)
{
oTag = cgi.TIKI_Orientation(pREnt, iTagNum);
for (i = 0; i < 2; i++) {
VectorMA(vStart, oTag.origin[i], pREnt->axis[i], vStart);
}
}
}
if (iRunning == -1)
{
AngleVectors(ent->lerpAngles, midlegs, NULL, NULL);
VectorMA(vStart, -16, midlegs, vStart);
VectorMA(vStart, 64, midlegs, vEnd);
VectorSet(vMins, -2, -2, -8);
VectorSet(vMaxs, 2, 2, 8);
}
else
{
VectorSet(vMins, -4, -4, 0);
VectorSet(vMaxs, 4, 4, 2);
// add 16 units above feets
vStart[2] += 16.0;
VectorCopy(vStart, vEnd);
vEnd[2] -= 64.0;
}
if (ent->currentState.eType == ET_PLAYER)
{
CG_Trace(
&trace,
vStart,
vMins,
vMaxs,
vEnd,
ent->currentState.number,
MASK_PLAYERSOLID,
qtrue,
qtrue,
"Player Footsteps"
);
}
else
{
CG_Trace(
&trace,
vStart,
vMins,
vMaxs,
vEnd,
ent->currentState.number,
MASK_MONSTERSOLID,
qfalse,
qfalse,
"Monster Footsteps"
);
}
if (trace.fraction == 1.0f)
{
if (cg_debugFootsteps->integer) {
cgi.DPrintf("Footstep: missed floor\n");
}
return;
}
CG_FootstepMain(&trace, iRunning, iEquipment);
}
void CG_MeleeImpact(vec3_t vStart, vec3_t vEnd) {
vec3_t vMins, vMaxs;
trace_t trace;
VectorSet(vMins, -4, -4, 0);
VectorSet(vMaxs, 4, 4, 2);
CG_Trace(&trace, vStart, vMins, vMaxs, vEnd, ENTITYNUM_NONE, MASK_PLAYERSOLID, qtrue, qtrue, "CG_MeleeImpact");
if (trace.fraction != 1.0) {
CG_FootstepMain(&trace, qtrue, qfalse);
}
}
void CG_LandingSound(centity_t* ent, refEntity_t* pREnt, float volume, int iEquipment)
{
int contents;
int surftype;
int iEffectNum;
vec3_t vStart, vEnd;
vec3_t midlegs;
str sSoundName;
trace_t trace;
if (ent->iNextLandTime > cg.time) {
ent->iNextLandTime = cg.time + 200;
return;
}
ent->iNextLandTime = cg.time + 200;
VectorCopy(ent->lerpOrigin, vStart);
vStart[2] += GROUND_DISTANCE;
VectorCopy(vStart, vEnd);
vEnd[2] -= 64.0;
if (ent->currentState.eType == ET_PLAYER)
{
CG_Trace(
&trace,
vStart,
g_vFootstepMins,
g_vFootstepMaxs,
vEnd,
ent->currentState.number,
MASK_PLAYERSOLID,
qtrue,
qtrue,
"Player Footsteps"
);
}
else
{
CG_Trace(
&trace,
vStart,
g_vFootstepMins,
g_vFootstepMaxs,
vEnd,
ent->currentState.number,
MASK_MONSTERSOLID,
qfalse,
qfalse,
"Monster Footsteps"
);
}
if (trace.fraction == 1.0) {
return;
}
sSoundName += "snd_landing_";
contents = CG_PointContents(trace.endpos, -1);
if (contents & MASK_WATER)
{
// take our ground position and trace upwards
VectorCopy(trace.endpos, midlegs);
midlegs[2] += WATER_NO_SPLASH_HEIGHT;
contents = CG_PointContents(midlegs, -1);
if (contents & MASK_WATER)
{
sSoundName += "wade";
}
else
{
sSoundName += "puddle";
iEffectNum = 95;
}
}
else
{
surftype = trace.surfaceFlags & MASK_SURF_TYPE;
switch (surftype)
{
case SURF_FOLIAGE:
sSoundName += "foliage";
iEffectNum = 93;
break;
case SURF_SNOW:
sSoundName += "snow";
iEffectNum = 97;
break;
case SURF_CARPET:
sSoundName += "carpet";
iEffectNum = 90;
break;
case SURF_SAND:
sSoundName += "sand";
iEffectNum = 96;
break;
case SURF_PUDDLE:
sSoundName += "puddle";
iEffectNum = 95;
break;
case SURF_GLASS:
sSoundName += "glass";
iEffectNum = 90;
break;
case SURF_GRAVEL:
sSoundName += "gravel";
iEffectNum = 91;
break;
case SURF_MUD:
sSoundName += "mud";
iEffectNum = 94;
break;
case SURF_DIRT:
sSoundName += "dirt";
iEffectNum = 92;
break;
case SURF_GRILL:
sSoundName += "grill";
iEffectNum = 90;
break;
case SURF_GRASS:
sSoundName += "grass";
iEffectNum = 93;
break;
case SURF_ROCK:
sSoundName += "stone";
iEffectNum = 91;
break;
case SURF_PAPER:
sSoundName += "paper";
iEffectNum = 90;
break;
case SURF_WOOD:
sSoundName += "wood";
iEffectNum = 90;
break;
case SURF_METAL:
sSoundName += "metal";
iEffectNum = 90;
break;
default:
sSoundName += "stone";
iEffectNum = 91;
break;
}
}
if (cg_debugFootsteps->integer) {
cgi.DPrintf("Landing: %s volume: %.2f effect = %i\n", sSoundName.c_str(), volume, contents);
}
commandManager.PlaySound(
sSoundName,
trace.endpos,
-1,
volume,
-1,
-1,
1
);
if (iEquipment && random() < 0.5) {
commandManager.PlaySound(
"snd_step_equipment",
ent->lerpOrigin,
-1,
volume,
-1,
-1,
1
);
}
if (iEffectNum != -1)
{
sfxManager.MakeEffect_Angles(
iEffectNum,
trace.endpos,
Vector(270, 0, 0)
);
}
}
void CG_BodyFallSound(centity_t* ent, refEntity_t* pREnt, float volume)
{
// FIXME: unimplemented
}
/*
===============
CG_Splash
Draw a mark at the water surface
===============
*/
void CG_Splash( centity_t *cent ) {
vec3_t start, end, diff;
trace_t trace;
int contents;
float dist;
float time_required;
spawnthing_t m_ripple;
if ( !cg_shadows->integer ) {
return;
}
VectorSubtract( cent->currentState.origin, cent->nextState.origin, diff );
diff[ 2 ] = 0;
dist = VectorLength( diff );
// See if enough time has passed to add another ripple
if ( dist >= 1 )
time_required = 100 - dist;
else
time_required = 200;
if ( time_required < 10 )
time_required = 10;
if ( cent->splash_last_spawn_time + time_required > cg.time )
return;
// Save the current time
cent->splash_last_spawn_time = cg.time;
// Make sure the entity is moving
if ( dist < 1 )
{
if ( cent->splash_still_count >= 0 )
{
cent->splash_still_count++;
if ( cent->splash_still_count > 2 )
cent->splash_still_count = 0;
else
return;
}
else
return;
}
VectorCopy( cent->lerpOrigin, end );
// if the feet aren't in liquid, don't make a mark
// this won't handle moving water brushes, but they wouldn't draw right anyway...
contents = cgi.CM_PointContents( end, 0 );
if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
return;
}
VectorCopy( cent->lerpOrigin, start );
start[2] += 88;
// if the head isn't out of liquid, don't make a mark
contents = cgi.CM_PointContents( start, 0 );
if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
return;
}
// trace down to find the surface
cgi.CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ), qfalse );
if ( trace.fraction == 1.0 ) {
return;
}
// FIXME
// TODO: Make effect
}
qboolean ClientSpecialEffectsManager::EffectsPending()
{
return sfxManager.m_iNumPendingEvents > 0;
}
specialeffect_t* ClientSpecialEffectsManager::GetTestEffectPointer()
{
return &m_effects[SPECIAL_EFFECT_TEST];
}