mirror of
https://github.com/ReactionQuake3/reaction.git
synced 2024-12-01 16:42:42 +00:00
1480 lines
41 KiB
C
1480 lines
41 KiB
C
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Log$
|
|
// Revision 1.50 2007/02/03 15:02:21 jbravo
|
|
// Renamed RQ3 to Reaction, Dropped loading of various baseq3 media, disabled the follow command, fixed grenades killing teammates and some cleanups
|
|
//
|
|
// Revision 1.49 2005/02/15 16:33:38 makro
|
|
// Tons of updates (entity tree attachment system, UI vectors)
|
|
//
|
|
// Revision 1.48 2004/01/26 21:26:08 makro
|
|
// no message
|
|
//
|
|
// Revision 1.47 2003/09/10 21:40:35 makro
|
|
// Cooler breath puffs. Locked r_fastSky on maps with global fog.
|
|
// Some other things I can't remember.
|
|
//
|
|
// Revision 1.46 2003/09/07 19:51:39 makro
|
|
// no message
|
|
//
|
|
// Revision 1.45 2003/08/26 19:28:37 makro
|
|
// target_speakers
|
|
//
|
|
// Revision 1.44 2003/08/10 20:13:26 makro
|
|
// no message
|
|
//
|
|
// Revision 1.43 2003/04/19 15:27:30 jbravo
|
|
// Backing out of most of unlagged. Only optimized prediction and smooth clients
|
|
// remains.
|
|
//
|
|
// Revision 1.42 2003/04/02 17:58:03 jbravo
|
|
// Ammo skin only replacements now work.
|
|
//
|
|
// Revision 1.41 2003/03/28 22:25:10 makro
|
|
// no message
|
|
//
|
|
// Revision 1.40 2003/03/10 07:07:58 jbravo
|
|
// Small unlagged fixes and voting delay added.
|
|
//
|
|
// Revision 1.39 2003/03/09 21:30:38 jbravo
|
|
// Adding unlagged. Still needs work.
|
|
//
|
|
// Revision 1.38 2003/03/08 09:58:08 niceass
|
|
// Changes to make my "position on tag with offset" work correctly with orientation matrices crap for CTB tag_weapon2
|
|
//
|
|
// Revision 1.37 2003/02/27 20:51:14 makro
|
|
// no message
|
|
//
|
|
// Revision 1.36 2003/02/27 19:52:34 makro
|
|
// dlights
|
|
//
|
|
// Revision 1.35 2003/02/01 02:15:31 jbravo
|
|
// Replacement models and items
|
|
//
|
|
// Revision 1.34 2003/01/08 04:46:25 jbravo
|
|
// Wrote a new hackish model replacement system
|
|
//
|
|
// Revision 1.33 2002/09/01 21:14:36 makro
|
|
// Sky portal tweaks
|
|
//
|
|
// Revision 1.32 2002/08/29 23:58:27 makro
|
|
// Sky portals
|
|
//
|
|
// Revision 1.31 2002/08/27 05:10:42 niceass
|
|
// new ctb marker shader names
|
|
//
|
|
// Revision 1.30 2002/07/19 04:33:52 niceass
|
|
// laser fog fix and ctb markers
|
|
//
|
|
// Revision 1.29 2002/06/21 21:06:20 niceass
|
|
// laserfog stuff
|
|
//
|
|
// Revision 1.28 2002/06/21 04:11:17 niceass
|
|
// fog laser
|
|
//
|
|
// Revision 1.27 2002/06/16 20:06:13 jbravo
|
|
// Reindented all the source files with "indent -kr -ut -i8 -l120 -lc120 -sob -bad -bap"
|
|
//
|
|
// Revision 1.26 2002/06/16 19:12:52 jbravo
|
|
// Removed the MISSIONPACK ifdefs and missionpack only code.
|
|
//
|
|
// Revision 1.25 2002/04/23 06:09:18 niceass
|
|
// pressure stuff
|
|
//
|
|
// Revision 1.24 2002/04/09 18:52:13 makro
|
|
// Target_speakers can now be toggled on/off
|
|
//
|
|
// Revision 1.23 2002/04/03 03:13:48 blaze
|
|
// NEW BREAKABLE CODE - will break all old breakables(wont appear in maps)
|
|
//
|
|
// Revision 1.22 2002/03/31 13:52:48 jbravo
|
|
// More cleanups
|
|
//
|
|
// Revision 1.21 2002/03/31 03:31:24 jbravo
|
|
// Compiler warning cleanups
|
|
//
|
|
// Revision 1.20 2002/03/31 02:03:35 niceass
|
|
// new testing tag command
|
|
//
|
|
// Revision 1.19 2002/03/16 21:50:49 niceass
|
|
// I tabbed a function properly. It was pissing me off
|
|
//
|
|
// Revision 1.18 2002/01/14 01:19:23 niceass
|
|
// No more default 800 gravity on items - NiceAss
|
|
//
|
|
// Revision 1.17 2002/01/11 19:48:29 jbravo
|
|
// Formatted the source in non DOS format.
|
|
//
|
|
// Revision 1.16 2001/12/31 16:28:41 jbravo
|
|
// I made a Booboo with the Log tag.
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
// cg_ents.c -- present snapshot entities, happens every single frame
|
|
|
|
#include "cg_local.h"
|
|
|
|
static void CG_LaserSight(centity_t * cent);
|
|
static void CG_Dlight(centity_t * cent);
|
|
static void CG_FakeShadow(centity_t * cent);
|
|
static void CG_Corona(centity_t * cent);
|
|
extern char rq3_breakables[RQ3_MAX_BREAKABLES][80];
|
|
extern void trap_R_AddAdditiveLightToScene(const vec3_t org, float intensity, float r, float g, float b);
|
|
|
|
|
|
/*
|
|
* ================
|
|
* RotatePoint
|
|
* ================
|
|
* */
|
|
void RotatePoint(vec3_t point, /*const*/ vec3_t matrix[3]) { // FIXME
|
|
vec3_t tvec;
|
|
|
|
VectorCopy(point, tvec);
|
|
point[0] = DotProduct(matrix[0], tvec);
|
|
point[1] = DotProduct(matrix[1], tvec);
|
|
point[2] = DotProduct(matrix[2], tvec);
|
|
}
|
|
|
|
/*
|
|
* ================
|
|
* TransposeMatrix
|
|
* ================
|
|
* */
|
|
void TransposeMatrix(/*const*/ vec3_t matrix[3], vec3_t transpose[3]) { // FIXME
|
|
int i, j;
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 3; j++) {
|
|
transpose[i][j] = matrix[j][i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ================
|
|
* CreateRotationMatrix
|
|
* ================
|
|
* */
|
|
void CreateRotationMatrix(const vec3_t angles, vec3_t matrix[3]) {
|
|
AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
|
|
VectorInverse(matrix[1]);
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_PositionEntityOnTag
|
|
|
|
Modifies the entities position and axis by the given
|
|
tag location
|
|
======================
|
|
*/
|
|
void CG_PositionEntityOnTag(refEntity_t * entity, const refEntity_t * parent, qhandle_t parentModel, char *tagName)
|
|
{
|
|
int i;
|
|
orientation_t lerped;
|
|
|
|
// lerp the tag
|
|
trap_R_LerpTag(&lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName);
|
|
|
|
// FIXME: allow origin offsets along tag?
|
|
VectorCopy(parent->origin, entity->origin);
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(entity->origin, lerped.origin[i], parent->axis[i], entity->origin);
|
|
}
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply(lerped.axis, ((refEntity_t *) parent)->axis, entity->axis);
|
|
entity->backlerp = parent->backlerp;
|
|
}
|
|
|
|
/* [QUARANTINE] - CG_PositionWeaponOnTag
|
|
======================
|
|
CG_PositionWeaponOnTag
|
|
|
|
Changed from CG_PositionEntityOnTag function to prevent backlerp change in animations
|
|
======================
|
|
*/
|
|
void CG_PositionWeaponOnTag(refEntity_t * entity, const refEntity_t * parent, qhandle_t parentModel, char *tagName)
|
|
{
|
|
int i;
|
|
orientation_t lerped;
|
|
|
|
// lerp the tag
|
|
trap_R_LerpTag(&lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName);
|
|
|
|
// FIXME: allow origin offsets along tag?
|
|
VectorCopy(parent->origin, entity->origin);
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(entity->origin, lerped.origin[i], parent->axis[i], entity->origin);
|
|
}
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply(lerped.axis, ((refEntity_t *) parent)->axis, entity->axis);
|
|
// entity->backlerp = parent->backlerp;
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_PositionRotatedEntityOnTag
|
|
|
|
Modifies the entities position and axis by the given
|
|
tag location
|
|
======================
|
|
*/
|
|
void CG_PositionRotatedEntityOnTag(refEntity_t * entity, const refEntity_t * parent,
|
|
qhandle_t parentModel, char *tagName)
|
|
{
|
|
int i;
|
|
orientation_t lerped;
|
|
vec3_t tempAxis[3];
|
|
|
|
//AxisClear( entity->axis );
|
|
// lerp the tag
|
|
trap_R_LerpTag(&lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName);
|
|
|
|
// FIXME: allow origin offsets along tag?
|
|
VectorCopy(parent->origin, entity->origin);
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(entity->origin, lerped.origin[i], parent->axis[i], entity->origin);
|
|
}
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply(entity->axis, lerped.axis, tempAxis);
|
|
MatrixMultiply(tempAxis, ((refEntity_t *) parent)->axis, entity->axis);
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_PositionRotatedEntityOnTag
|
|
|
|
Modifies the entities position and axis by the given
|
|
tag location
|
|
======================
|
|
*/
|
|
void CG_PositionRotatedOffsetEntityOnTag(refEntity_t * entity, const refEntity_t * parent,
|
|
qhandle_t parentModel, char *tagName, vec3_t Offset)
|
|
{
|
|
int i;
|
|
orientation_t lerped;
|
|
vec3_t tempAxis[3]; //, tmp;
|
|
|
|
// lerp the tag
|
|
trap_R_LerpTag(&lerped, parentModel, parent->oldframe, parent->frame, 1.0 - parent->backlerp, tagName);
|
|
|
|
VectorCopy(parent->origin, entity->origin);
|
|
for (i = 0; i < 3; i++)
|
|
VectorMA(entity->origin, lerped.origin[i], parent->axis[i], entity->origin);
|
|
|
|
// had to cast away the const to avoid compiler problems...
|
|
MatrixMultiply(entity->axis, lerped.axis, tempAxis);
|
|
MatrixMultiply(tempAxis, ((refEntity_t *) parent)->axis, entity->axis);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
VectorMA(entity->origin, Offset[i], entity->axis[i], entity->origin);
|
|
}
|
|
|
|
/*
|
|
==========================================================================
|
|
|
|
FUNCTIONS CALLED EACH FRAME
|
|
|
|
==========================================================================
|
|
*/
|
|
|
|
/*
|
|
======================
|
|
CG_SetEntitySoundPosition
|
|
|
|
Also called by event processing code
|
|
======================
|
|
*/
|
|
void CG_SetEntitySoundPosition(centity_t * cent)
|
|
{
|
|
if (cent->currentState.solid == SOLID_BMODEL)
|
|
{
|
|
vec3_t origin;
|
|
|
|
VectorCopy(cgs.inlineModelMidpoints[cent->currentState.modelindex], origin);
|
|
//Makro - rotate if needed
|
|
if (cent->lerpAngles[YAW] || cent->lerpAngles[PITCH] || cent->lerpAngles[ROLL])
|
|
{
|
|
vec3_t axis[3];
|
|
AnglesToAxis(cent->lerpAngles, axis);
|
|
ChangeRefSystem(origin, NULL, axis, origin);
|
|
}
|
|
VectorAdd(cent->lerpOrigin, origin, origin);
|
|
trap_S_UpdateEntityPosition(cent->currentState.number, origin);
|
|
} else {
|
|
trap_S_UpdateEntityPosition(cent->currentState.number, cent->lerpOrigin);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_EntityEffects
|
|
|
|
Add continuous entity effects, like local entity emission and lighting
|
|
==================
|
|
*/
|
|
static void CG_EntityEffects(centity_t * cent)
|
|
{
|
|
|
|
// update sound origins
|
|
CG_SetEntitySoundPosition(cent);
|
|
|
|
// add loop sound
|
|
if (cent->currentState.loopSound) {
|
|
//Makro - for speakers, s.weapon == 1 => no PVS check
|
|
if (cent->currentState.eType != ET_SPEAKER || cent->currentState.weapon == 0) {
|
|
trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
cgs.gameSounds[cent->currentState.loopSound]);
|
|
} else {
|
|
//Makro - note: doesn't take into account PVS info
|
|
trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
|
|
cgs.gameSounds[ cent->currentState.loopSound ] );
|
|
}
|
|
}
|
|
|
|
// constant light glow
|
|
if (cent->currentState.constantLight && cent->currentState.eType != ET_DLIGHT) {
|
|
int cl;
|
|
float i, r, g, b;
|
|
|
|
cl = cent->currentState.constantLight;
|
|
r = (float) (cl & 0xFF) / 255.0;
|
|
g = (float) ((cl >> 8) & 0xFF) / 255.0;
|
|
b = (float) ((cl >> 16) & 0xFF) / 255.0;
|
|
i = (float) ((cl >> 24) & 0xFF) * 4.0;
|
|
trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_General
|
|
==================
|
|
*/
|
|
static void CG_General(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex) {
|
|
return;
|
|
}
|
|
|
|
memset(&ent, 0, sizeof(ent));
|
|
|
|
// set frame
|
|
|
|
ent.frame = s1->frame;
|
|
ent.oldframe = ent.frame;
|
|
ent.backlerp = 0;
|
|
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
|
|
ent.hModel = cgs.gameModels[s1->modelindex];
|
|
|
|
// player model
|
|
if (s1->number == cg.snap->ps.clientNum) {
|
|
ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
|
|
}
|
|
// convert angles to axis
|
|
AnglesToAxis(cent->lerpAngles, ent.axis);
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Speaker
|
|
|
|
Speaker entities can automatically play sounds
|
|
==================
|
|
*/
|
|
static void CG_Speaker(centity_t * cent)
|
|
{
|
|
if (!cent->currentState.clientNum) { // FIXME: use something other than clientNum...
|
|
return; // not auto triggering
|
|
}
|
|
|
|
if (cg.time < cent->miscTime) {
|
|
return;
|
|
}
|
|
|
|
trap_S_StartSound(NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm]);
|
|
|
|
// ent->s.frame = ent->wait * 10;
|
|
// ent->s.clientNum = ent->random * 10;
|
|
cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Item
|
|
==================
|
|
*/
|
|
static void CG_Item(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *es;
|
|
gitem_t *item;
|
|
int msec;
|
|
float frac, scale;
|
|
weaponInfo_t *wi;
|
|
itemInfo_t *itemInfo;
|
|
|
|
int boundIndex = 0;
|
|
float offsetMul = 1.f;
|
|
int boundAxis = 2;
|
|
|
|
es = ¢->currentState;
|
|
if (es->modelindex >= bg_numItems) {
|
|
CG_Error("Bad item index %i on entity", es->modelindex);
|
|
}
|
|
// if set to invisible, skip
|
|
if (!es->modelindex || (es->eFlags & EF_NODRAW)) {
|
|
return;
|
|
}
|
|
|
|
item = &bg_itemlist[es->modelindex];
|
|
if (cg_simpleItems.integer && item->giType != IT_TEAM) {
|
|
memset(&ent, 0, sizeof(ent));
|
|
ent.reType = RT_SPRITE;
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
// Elder: lower them slightly
|
|
ent.origin[2] -= 6;
|
|
// Elder: make auto-sprites smaller, especially grenades and knifes
|
|
if (item->giType == IT_WEAPON) {
|
|
if (item->giTag == WP_GRENADE || item->giTag == WP_KNIFE)
|
|
ent.radius = 4;
|
|
else
|
|
ent.radius = 14;
|
|
} else if (item->giType == IT_HOLDABLE)
|
|
ent.radius = 10;
|
|
else
|
|
ent.radius = 6;
|
|
//ent.radius = 14;
|
|
ent.customShader = cg_items[es->modelindex].icon;
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = 255;
|
|
ent.shaderRGBA[2] = 255;
|
|
ent.shaderRGBA[3] = 255;
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
return;
|
|
}
|
|
|
|
memset(&ent, 0, sizeof(ent));
|
|
|
|
//Elder: knife rotation code so it's always "in" the wall
|
|
if (item->giTag == WP_KNIFE && ((es->eFlags & FL_THROWN_KNIFE) == FL_THROWN_KNIFE)) {
|
|
VectorCopy(es->angles, cent->lerpAngles);
|
|
//Elder: nudges out a bit so it's more visible
|
|
VectorCopy(es->origin, cent->lerpOrigin);
|
|
AnglesToAxis(cent->lerpAngles, ent.axis);
|
|
} else {
|
|
//Elder: default item orientation
|
|
VectorCopy(cg.autoAngles, cent->lerpAngles);
|
|
AxisCopy(cg.autoAxis, ent.axis);
|
|
}
|
|
|
|
if (item->giType == IT_WEAPON && item->giTag != WP_KNIFE &&
|
|
(es->pos.trDelta[0] != 0 || es->pos.trDelta[1] != 0 || es->pos.trDelta[2] != 0)) {
|
|
vec3_t myvec;
|
|
|
|
//Elder: old fashioned AQ2 weapon drop rotating-style code :)
|
|
//It's out here because the wi calculations mess up the lerpOrigin
|
|
VectorCopy(cg.autoAnglesFast, cent->lerpAngles);
|
|
AxisCopy(cg.autoAxisFast, ent.axis);
|
|
|
|
VectorCopy(ent.axis[1], myvec);
|
|
VectorNegate(ent.axis[2], ent.axis[1]);
|
|
VectorCopy(myvec, ent.axis[2]);
|
|
} else if (item->giType == IT_HOLDABLE) {
|
|
//Elder: rotate item if moving
|
|
if (es->pos.trDelta[0] != 0 || es->pos.trDelta[1] != 0 || es->pos.trDelta[2] != 0) {
|
|
VectorCopy(cg.autoAnglesFast, cent->lerpAngles);
|
|
AxisCopy(cg.autoAxisFast, ent.axis);
|
|
} else
|
|
AnglesToAxis(es->angles, ent.axis);
|
|
}
|
|
|
|
wi = NULL;
|
|
// the weapons have their origin where they attatch to player
|
|
// models, so we need to offset them or they will rotate
|
|
// eccentricly
|
|
//Elder: added knife conditional
|
|
if (item->giType == IT_WEAPON &&
|
|
!(item->giTag == WP_KNIFE && ((es->eFlags & FL_THROWN_KNIFE) == FL_THROWN_KNIFE))) {
|
|
|
|
vec3_t myvec;
|
|
|
|
// Elder: bad hack -- but oh well.
|
|
if (es->pos.trDelta[0] == 0 && es->pos.trDelta[1] == 0 && es->pos.trDelta[2] == 0)
|
|
AnglesToAxis(es->angles, ent.axis);
|
|
|
|
//CG_Printf("Should not be in here if it's a thrown knife\n");
|
|
wi = &cg_weapons[item->giTag];
|
|
cent->lerpOrigin[0] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][0] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][0] + wi->weaponMidpoint[2] * ent.axis[2][0];
|
|
cent->lerpOrigin[1] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][1] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][1] + wi->weaponMidpoint[2] * ent.axis[2][1];
|
|
// Makro - did we really need this?
|
|
/*
|
|
cent->lerpOrigin[2] -=
|
|
wi->weaponMidpoint[0] * ent.axis[0][2] +
|
|
wi->weaponMidpoint[1] * ent.axis[1][2] + wi->weaponMidpoint[2] * ent.axis[2][2];
|
|
*/
|
|
|
|
if (es->pos.trDelta[0] == 0 && es->pos.trDelta[1] == 0 && es->pos.trDelta[2] == 0) {
|
|
// Blaze: rotate the gun by 90 degrees to place it on the ground
|
|
VectorCopy(ent.axis[1], myvec);
|
|
VectorNegate(ent.axis[2], ent.axis[1]);
|
|
VectorCopy(myvec, ent.axis[2]);
|
|
boundIndex = 1;
|
|
boundAxis = 1;
|
|
offsetMul = -1.f;
|
|
}
|
|
}
|
|
// JBravo: world skins
|
|
if (item->giType == IT_WEAPON) {
|
|
wi = &cg_weapons[item->giTag];
|
|
if (wi->customSkin)
|
|
ent.customSkin = wi->customSkin;
|
|
}
|
|
if (item->giType == IT_HOLDABLE || item->giType == IT_AMMO) {
|
|
itemInfo = &cg_items[item - bg_itemlist];
|
|
if (itemInfo->customSkin)
|
|
ent.customSkin = itemInfo->customSkin;
|
|
}
|
|
|
|
ent.hModel = cg_items[es->modelindex].models[0];
|
|
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
|
|
ent.nonNormalizedAxes = qfalse;
|
|
|
|
// if just respawned, slowly scale up
|
|
msec = cg.time - cent->miscTime;
|
|
if (msec >= 0 && msec < ITEM_SCALEUP_TIME) {
|
|
frac = (float) msec / ITEM_SCALEUP_TIME;
|
|
VectorScale(ent.axis[0], frac, ent.axis[0]);
|
|
VectorScale(ent.axis[1], frac, ent.axis[1]);
|
|
VectorScale(ent.axis[2], frac, ent.axis[2]);
|
|
ent.nonNormalizedAxes = qtrue;
|
|
} else {
|
|
frac = 1.0;
|
|
}
|
|
|
|
// Elder: special items and ammo should have minimum light too
|
|
// items without glow textures need to keep a minimum light value
|
|
// so they are always visible
|
|
if ((item->giType == IT_WEAPON) ||
|
|
(item->giType == IT_ARMOR) || (item->giType == IT_AMMO) || (item->giType == IT_HOLDABLE)) {
|
|
ent.renderfx |= RF_MINLIGHT;
|
|
}
|
|
// increase the size of the weapons when they are presented as items
|
|
|
|
// Elder: only for knives, which are hard to spot
|
|
// NiceAss: Scale code modified for weapons too.
|
|
if (item->giTag == WP_KNIFE)
|
|
scale = WEAPON_KNIFE_SCALE;
|
|
else if (item->giType == IT_WEAPON && item->giTag != WP_KNIFE)
|
|
scale = (float) WEAPON_GUN_SCALE;
|
|
else
|
|
scale = WEAPON_OTHER_SCALE;
|
|
|
|
VectorScale(ent.axis[0], scale, ent.axis[0]);
|
|
VectorScale(ent.axis[1], scale, ent.axis[1]);
|
|
VectorScale(ent.axis[2], scale, ent.axis[2]);
|
|
|
|
// Makro - offset based on bounding box, not magic numbers
|
|
{
|
|
vec3_t bound[2];
|
|
float offset;
|
|
|
|
trap_R_ModelBounds(ent.hModel, bound[0], bound[1]);
|
|
offset = -ITEM_RADIUS - bound[boundIndex][boundAxis] * offsetMul * scale;
|
|
|
|
// Makro - nudge them up a bit
|
|
// the strobe effect looks a lot better if it isn't clipped by the ground
|
|
// Note - magic number :)
|
|
offset += 0.5f;
|
|
|
|
ent.origin[2] += offset;
|
|
ent.oldorigin[2] += offset;
|
|
}
|
|
|
|
// add strobe effect -- should make this toggle?
|
|
// NiceAss: Temp Cvar usage for strobe shader.
|
|
if ((item->giType == IT_WEAPON ||
|
|
item->giType == IT_ARMOR ||
|
|
item->giType == IT_AMMO || item->giType == IT_HOLDABLE) && cg_RQ3_strobe.integer) {
|
|
const float MAX_DIST = 512.f;
|
|
vec3_t delta;
|
|
float dist;
|
|
|
|
VectorSubtract(cg.refdef.vieworg, ent.origin, delta);
|
|
dist = VectorLengthSquared(delta);
|
|
|
|
if (dist <= MAX_DIST * MAX_DIST) {
|
|
refEntity_t glow = ent;
|
|
float frac = 1.f - sqrt(dist) * (1.f / MAX_DIST);
|
|
const float freq = 1.f;
|
|
vec4_t color;
|
|
|
|
frac *= frac;
|
|
|
|
VectorCopy(g_color_table[cg_RQ3_strobeColor.integer % ARRAY_LEN(g_color_table)], color);
|
|
|
|
// Makro - constant alpha for values >= 2, otherwise pulse
|
|
if (cg_RQ3_strobe.integer > 1)
|
|
color[3] = Com_Clamp(0.f, 1.f, frac);
|
|
else
|
|
color[3] = Com_Clamp(0.f, 1.f, frac * (sin(freq * 2.f * M_PI * cg.time / 1000.f) * 0.5f + 0.5f));
|
|
|
|
glow.shaderRGBA[0] = (int) (color[0] * 255.f + 0.5f);
|
|
glow.shaderRGBA[1] = (int) (color[1] * 255.f + 0.5f);
|
|
glow.shaderRGBA[2] = (int) (color[2] * 255.f + 0.5f);
|
|
glow.shaderRGBA[3] = (int) (color[3] * 255.f + 0.5f);
|
|
glow.customShader = cgs.media.itemStrobeShader;
|
|
trap_R_AddRefEntityToScene(&glow);
|
|
}
|
|
}
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
// accompanying rings / spheres for powerups
|
|
if (!cg_simpleItems.integer) {
|
|
vec3_t spinAngles;
|
|
|
|
VectorClear(spinAngles);
|
|
|
|
if (item->giType == IT_HEALTH || item->giType == IT_POWERUP) {
|
|
if ((ent.hModel = cg_items[es->modelindex].models[1]) != 0) {
|
|
if (item->giType == IT_POWERUP) {
|
|
ent.origin[2] += 12;
|
|
spinAngles[1] = (cg.time & 1023) * 360 / -1024.0f;
|
|
}
|
|
AnglesToAxis(spinAngles, ent.axis);
|
|
|
|
// scale up if respawning
|
|
if (frac != 1.0) {
|
|
VectorScale(ent.axis[0], frac, ent.axis[0]);
|
|
VectorScale(ent.axis[1], frac, ent.axis[1]);
|
|
VectorScale(ent.axis[2], frac, ent.axis[2]);
|
|
ent.nonNormalizedAxes = qtrue;
|
|
}
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
===============
|
|
CG_Missile
|
|
===============
|
|
*/
|
|
static void CG_Missile(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
const weaponInfo_t *weapon;
|
|
|
|
s1 = ¢->currentState;
|
|
if (s1->weapon >= WP_NUM_WEAPONS) {
|
|
s1->weapon = 0;
|
|
}
|
|
weapon = &cg_weapons[s1->weapon];
|
|
|
|
// calculate the axis
|
|
VectorCopy(s1->angles, cent->lerpAngles);
|
|
|
|
// add trails
|
|
if (weapon->missileTrailFunc) {
|
|
weapon->missileTrailFunc(cent, weapon);
|
|
}
|
|
// add dynamic light
|
|
if (weapon->missileDlight) {
|
|
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
|
|
weapon->missileDlightColor[0], weapon->missileDlightColor[1],
|
|
weapon->missileDlightColor[2]);
|
|
}
|
|
// add missile sound
|
|
if (weapon->missileSound) {
|
|
vec3_t velocity;
|
|
|
|
CG_EvaluateTrajectoryDelta(¢->currentState.pos, cg.time, velocity);
|
|
|
|
trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound);
|
|
}
|
|
// create the render entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
|
|
// flicker between two skins
|
|
ent.skinNum = cg.clientFrame & 1;
|
|
ent.hModel = weapon->missileModel;
|
|
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
|
|
|
|
// convert direction of travel into axis
|
|
if (VectorNormalize2(s1->pos.trDelta, ent.axis[0]) == 0) {
|
|
ent.axis[0][2] = 1;
|
|
}
|
|
// spin as it moves
|
|
if (s1->pos.trType != TR_STATIONARY) {
|
|
//Spin knife like a wheel
|
|
if (s1->weapon == WP_KNIFE) {
|
|
vec3_t knifeVelocity;
|
|
|
|
CG_EvaluateTrajectoryDelta(&s1->pos, cg.time, knifeVelocity);
|
|
vectoangles(knifeVelocity, cent->lerpAngles);
|
|
cent->lerpAngles[0] += cg.time; // was / 6
|
|
|
|
AnglesToAxis(cent->lerpAngles, ent.axis);
|
|
|
|
// NiceAss: Added for scaling of the knife in flight and not just when sticking in a wall.
|
|
VectorScale(ent.axis[0], WEAPON_KNIFE_SCALE, ent.axis[0]);
|
|
VectorScale(ent.axis[1], WEAPON_KNIFE_SCALE, ent.axis[1]);
|
|
VectorScale(ent.axis[2], WEAPON_KNIFE_SCALE, ent.axis[2]);
|
|
} else
|
|
RotateAroundDirection(ent.axis, cg.time / 4);
|
|
} else {
|
|
RotateAroundDirection(ent.axis, s1->time);
|
|
}
|
|
|
|
// add to refresh list, possibly with quad glow
|
|
CG_AddRefEntityWithPowerups(&ent, s1, TEAM_FREE);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Grapple
|
|
|
|
This is called when the grapple is sitting up against the wall
|
|
===============
|
|
*/
|
|
static void CG_Grapple(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
const weaponInfo_t *weapon;
|
|
|
|
s1 = ¢->currentState;
|
|
if (s1->weapon >= WP_NUM_WEAPONS) {
|
|
s1->weapon = 0;
|
|
}
|
|
weapon = &cg_weapons[s1->weapon];
|
|
|
|
// calculate the axis
|
|
VectorCopy(s1->angles, cent->lerpAngles);
|
|
|
|
#if 0 // FIXME add grapple pull sound here..?
|
|
// add missile sound
|
|
if (weapon->missileSound) {
|
|
trap_S_AddLoopingSound(cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound);
|
|
}
|
|
#endif
|
|
|
|
// Will draw cable if needed
|
|
//Blaze: Not needed
|
|
// CG_GrappleTrail ( cent, weapon );
|
|
|
|
// create the render entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
|
|
// flicker between two skins
|
|
ent.skinNum = cg.clientFrame & 1;
|
|
ent.hModel = weapon->missileModel;
|
|
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
|
|
|
|
// convert direction of travel into axis
|
|
if (VectorNormalize2(s1->pos.trDelta, ent.axis[0]) == 0) {
|
|
ent.axis[0][2] = 1;
|
|
}
|
|
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Mover
|
|
===============
|
|
*/
|
|
static void CG_Mover(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
AnglesToAxis(cent->lerpAngles, ent.axis);
|
|
|
|
ent.renderfx = RF_NOSHADOW;
|
|
|
|
// flicker between two skins (FIXME?)
|
|
ent.skinNum = (cg.time >> 6) & 1;
|
|
|
|
// get the model, either as a bmodel or a modelindex
|
|
if (s1->solid == SOLID_BMODEL) {
|
|
ent.hModel = cgs.inlineDrawModel[s1->modelindex];
|
|
} else {
|
|
ent.hModel = cgs.gameModels[s1->modelindex];
|
|
}
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
|
|
// add the secondary model
|
|
if (s1->modelindex2) {
|
|
ent.skinNum = 0;
|
|
ent.hModel = cgs.gameModels[s1->modelindex2];
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Beam
|
|
|
|
Also called as an event
|
|
===============
|
|
*/
|
|
void CG_Beam(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(s1->pos.trBase, ent.origin);
|
|
VectorCopy(s1->origin2, ent.oldorigin);
|
|
AxisClear(ent.axis);
|
|
ent.reType = RT_BEAM;
|
|
|
|
ent.renderfx = RF_NOSHADOW;
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Portal
|
|
===============
|
|
*/
|
|
static void CG_Portal(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(s1->origin2, ent.oldorigin);
|
|
ByteToDir(s1->eventParm, ent.axis[0]);
|
|
PerpendicularVector(ent.axis[1], ent.axis[0]);
|
|
|
|
// negating this tends to get the directions like they want
|
|
// we really should have a camera roll value
|
|
VectorSubtract(vec3_origin, ent.axis[1], ent.axis[1]);
|
|
|
|
CrossProduct(ent.axis[0], ent.axis[1], ent.axis[2]);
|
|
ent.reType = RT_PORTALSURFACE;
|
|
ent.oldframe = s1->powerups;
|
|
ent.frame = s1->frame; // rotation speed
|
|
ent.skinNum = s1->clientNum / 256.0 * 360; // roll offset
|
|
|
|
// add to refresh list
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|
|
|
|
/*
|
|
=========================
|
|
CG_AdjustPositionForMover
|
|
|
|
Also called by client movement prediction code
|
|
=========================
|
|
*/
|
|
|
|
//Makro - made it so that angles get adjusted, too
|
|
void CG_AdjustPositionForMover(const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out, vec3_t angleOut)
|
|
{
|
|
centity_t *cent;
|
|
vec3_t oldOrigin, origin, deltaOrigin;
|
|
vec3_t oldAngles, angles, deltaAngles;
|
|
|
|
if (moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL) {
|
|
VectorCopy(in, out);
|
|
return;
|
|
}
|
|
|
|
cent = &cg_entities[moverNum];
|
|
//if (cent->currentState.eType != ET_MOVER) {
|
|
//Makro - adjust for movers and attached entities
|
|
if ((cent->currentState.eType != ET_PLAYER && ((cent->currentState.eFlags & EF_ATTACHED) == 0)) //if not attached
|
|
&& cent->currentState.eType != ET_MOVER) //and not a mover, either
|
|
{
|
|
VectorCopy(in, out);
|
|
return;
|
|
}
|
|
|
|
//Makro - if mover is blocked, don't do anything
|
|
if (cent->currentState.eFlags & EF_MOVER_BLOCKED)
|
|
{
|
|
VectorCopy(in, out);
|
|
return;
|
|
}
|
|
|
|
//CG_EvaluateTrajectory(¢->currentState.pos, fromTime, oldOrigin);
|
|
//CG_EvaluateTrajectory(¢->currentState.apos, fromTime, oldAngles);
|
|
CG_EvaluateTrajectoryEx(cent, fromTime, oldOrigin, oldAngles);
|
|
|
|
//CG_EvaluateTrajectory(¢->currentState.pos, toTime, origin);
|
|
//CG_EvaluateTrajectory(¢->currentState.apos, toTime, angles)
|
|
CG_EvaluateTrajectoryEx(cent, toTime, origin, angles);
|
|
|
|
VectorSubtract(origin, oldOrigin, deltaOrigin);
|
|
//VectorSubtract(angles, oldAngles, deltaAngles);
|
|
|
|
VectorAdd(in, deltaOrigin, out);
|
|
|
|
|
|
// FIXME: origin change when on a rotating object
|
|
//Makro - okay
|
|
if (angleOut)
|
|
VectorAdd(angleOut, deltaAngles, angleOut);
|
|
|
|
if (deltaAngles[0] || deltaAngles[1] || deltaAngles[2])
|
|
{
|
|
vec3_t matrix[3], transpose[3];
|
|
VectorSubtract(in, oldOrigin, oldOrigin);
|
|
VectorCopy(oldOrigin, deltaOrigin);
|
|
CreateRotationMatrix(deltaAngles, transpose);
|
|
TransposeMatrix(transpose, matrix);
|
|
RotatePoint(deltaOrigin, matrix);
|
|
VectorSubtract(deltaOrigin, oldOrigin, deltaOrigin);
|
|
VectorAdd(out, deltaOrigin, out);
|
|
/*
|
|
float norm;
|
|
VectorSubtract(in, origin, deltaOrigin);
|
|
norm = VectorLength(deltaOrigin);
|
|
vectoangles(deltaOrigin, angles);
|
|
VectorAdd(angles, deltaAngles, angles);
|
|
AngleVectors(angles, origin, NULL, NULL);
|
|
VectorScale(origin, norm, origin);
|
|
VectorSubtract(origin, deltaOrigin, deltaOrigin);
|
|
VectorAdd(out, deltaOrigin, out);
|
|
*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============================
|
|
CG_InterpolateEntityPosition
|
|
=============================
|
|
*/
|
|
static void CG_InterpolateEntityPosition(centity_t * cent)
|
|
{
|
|
vec3_t current, next;
|
|
float f;
|
|
|
|
// it would be an internal error to find an entity that interpolates without
|
|
// a snapshot ahead of the current one
|
|
if (cg.nextSnap == NULL) {
|
|
CG_Error("CG_InterpolateEntityPosition: cg.nextSnap == NULL");
|
|
}
|
|
|
|
f = cg.frameInterpolation;
|
|
|
|
// this will linearize a sine or parabolic curve, but it is important
|
|
// to not extrapolate player positions if more recent data is available
|
|
CG_EvaluateTrajectory(¢->currentState.pos, cg.snap->serverTime, current);
|
|
CG_EvaluateTrajectory(¢->nextState.pos, cg.nextSnap->serverTime, next);
|
|
|
|
cent->lerpOrigin[0] = current[0] + f * (next[0] - current[0]);
|
|
cent->lerpOrigin[1] = current[1] + f * (next[1] - current[1]);
|
|
cent->lerpOrigin[2] = current[2] + f * (next[2] - current[2]);
|
|
|
|
CG_EvaluateTrajectory(¢->currentState.apos, cg.snap->serverTime, current);
|
|
CG_EvaluateTrajectory(¢->nextState.apos, cg.nextSnap->serverTime, next);
|
|
|
|
cent->lerpAngles[0] = LerpAngle(current[0], next[0], f);
|
|
cent->lerpAngles[1] = LerpAngle(current[1], next[1], f);
|
|
cent->lerpAngles[2] = LerpAngle(current[2], next[2], f);
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_CalcEntityLerpPositions
|
|
|
|
===============
|
|
*/
|
|
static void CG_CalcEntityLerpPositions(centity_t * cent)
|
|
{
|
|
// this is done server-side now - cg_smoothClients is undefined
|
|
// players will always be TR_INTERPOLATE
|
|
// if this player does not want to see extrapolated players
|
|
// JBravo: done serverside now via unlagged
|
|
/* if (!cg_smoothClients.integer) {
|
|
// make sure the clients use TR_INTERPOLATE
|
|
if (cent->currentState.number < MAX_CLIENTS) {
|
|
cent->currentState.pos.trType = TR_INTERPOLATE;
|
|
cent->nextState.pos.trType = TR_INTERPOLATE;
|
|
}
|
|
}
|
|
*/
|
|
if (cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE) {
|
|
CG_InterpolateEntityPosition(cent);
|
|
return;
|
|
}
|
|
// first see if we can interpolate between two snaps for
|
|
// linear extrapolated clients
|
|
if (cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
|
|
cent->currentState.number < MAX_CLIENTS) {
|
|
CG_InterpolateEntityPosition(cent);
|
|
return;
|
|
}
|
|
// JBravo: unlagged
|
|
if (cent->currentState.number < MAX_CLIENTS &&
|
|
cent->currentState.clientNum != cg.predictedPlayerState.clientNum) {
|
|
cent->currentState.pos.trType = TR_LINEAR_STOP;
|
|
cent->currentState.pos.trTime = cg.snap->serverTime;
|
|
cent->currentState.pos.trDuration = 1000 / sv_fps.integer;
|
|
}
|
|
|
|
// just use the current frame and evaluate as best we can
|
|
//Makro - if this is a mover, it might be blocked
|
|
if (cent->currentState.eType == ET_MOVER && (cent->currentState.eFlags & EF_MOVER_BLOCKED) != 0)
|
|
{
|
|
//no prediction in this case
|
|
//CG_EvaluateTrajectory(¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin);
|
|
//CG_EvaluateTrajectory(¢->currentState.apos, cg.snap->serverTime, cent->lerpAngles);
|
|
CG_EvaluateTrajectoryEx(cent, cg.snap->serverTime, cent->lerpOrigin, cent->lerpAngles);
|
|
} else {
|
|
//CG_EvaluateTrajectory(¢->currentState.pos, cg.time, cent->lerpOrigin);
|
|
//CG_EvaluateTrajectory(¢->currentState.apos, cg.time, cent->lerpAngles);
|
|
CG_EvaluateTrajectoryEx(cent, cg.time, cent->lerpOrigin, cent->lerpAngles);
|
|
}
|
|
|
|
// adjust for riding a mover if it wasn't rolled into the predicted
|
|
// player state
|
|
if (cent != &cg.predictedPlayerEntity) {
|
|
CG_AdjustPositionForMover(cent->lerpOrigin, cent->currentState.groundEntityNum,
|
|
cg.snap->serverTime, cg.time, cent->lerpOrigin, cent->lerpAngles);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_TeamBase
|
|
===============
|
|
*/
|
|
static void CG_TeamBase(centity_t * cent)
|
|
{
|
|
refEntity_t model;
|
|
|
|
if (cgs.gametype == GT_CTF) {
|
|
// show the flag base
|
|
memset(&model, 0, sizeof(model));
|
|
model.reType = RT_MODEL;
|
|
VectorCopy(cent->lerpOrigin, model.lightingOrigin);
|
|
VectorCopy(cent->lerpOrigin, model.origin);
|
|
AnglesToAxis(cent->currentState.angles, model.axis);
|
|
if (cent->currentState.modelindex == TEAM_RED) {
|
|
model.hModel = cgs.media.redFlagBaseModel;
|
|
} else if (cent->currentState.modelindex == TEAM_BLUE) {
|
|
model.hModel = cgs.media.blueFlagBaseModel;
|
|
} else {
|
|
model.hModel = cgs.media.neutralFlagBaseModel;
|
|
}
|
|
trap_R_AddRefEntityToScene(&model);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_DrawDecal By NiceAss
|
|
===============
|
|
*/
|
|
static void CG_DrawDecal(centity_t * cent)
|
|
{
|
|
trace_t trace;
|
|
vec3_t end;
|
|
//float alpha, radius;
|
|
float radius;
|
|
|
|
if (cgs.gametype == GT_CTF) {
|
|
//cent->currentState.modelindex
|
|
|
|
VectorCopy(cent->lerpOrigin, end);
|
|
end[2] -= 8192;
|
|
|
|
// alpha = sin(cg.time / 160.0f) * 0.25 + 0.75f;
|
|
radius = 48 + (cos(cg.time / 320.0f) * 4.0f);
|
|
|
|
// Project downward to the ground
|
|
CG_Trace(&trace, cent->lerpOrigin, NULL, NULL, end, 0, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);
|
|
|
|
if (cent->currentState.modelindex == TEAM_RED)
|
|
CG_ImpactMark(cgs.media.ctbXMarkSilver, trace.endpos, trace.plane.normal, 45.0f,
|
|
1, 1, 1, 1, qfalse, radius, qtrue);
|
|
else
|
|
CG_ImpactMark(cgs.media.ctbOMarkBlack, trace.endpos, trace.plane.normal, 45.0f,
|
|
1, 1, 1, 1, qfalse, radius, qtrue);
|
|
}
|
|
}
|
|
/*
|
|
===============
|
|
CG_AddCEntity
|
|
|
|
===============
|
|
*/
|
|
static void CG_AddCEntity(centity_t * cent)
|
|
{
|
|
// event-only entities will have been dealt with already
|
|
if (cent->currentState.eType >= ET_EVENTS) {
|
|
return;
|
|
}
|
|
// calculate the current origin
|
|
CG_CalcEntityLerpPositions(cent);
|
|
|
|
// add automatic effects
|
|
CG_EntityEffects(cent);
|
|
|
|
switch (cent->currentState.eType) {
|
|
default:
|
|
CG_Error("Bad entity type: %i", cent->currentState.eType);
|
|
break;
|
|
case ET_INVISIBLE:
|
|
case ET_PUSH_TRIGGER:
|
|
case ET_TELEPORT_TRIGGER:
|
|
break;
|
|
case ET_GENERAL:
|
|
CG_General(cent);
|
|
break;
|
|
case ET_PLAYER:
|
|
CG_Player(cent);
|
|
break;
|
|
case ET_ITEM:
|
|
CG_Item(cent);
|
|
break;
|
|
case ET_MISSILE:
|
|
CG_Missile(cent);
|
|
break;
|
|
case ET_MOVER:
|
|
CG_Mover(cent);
|
|
break;
|
|
case ET_BREAKABLE:
|
|
CG_Mover(cent);
|
|
break;
|
|
case ET_PRESSURE:
|
|
CG_Mover(cent);
|
|
break;
|
|
case ET_BEAM:
|
|
CG_Beam(cent);
|
|
break;
|
|
case ET_PORTAL:
|
|
CG_Portal(cent);
|
|
break;
|
|
case ET_SPEAKER:
|
|
CG_Speaker(cent);
|
|
break;
|
|
case ET_GRAPPLE:
|
|
CG_Grapple(cent);
|
|
break;
|
|
case ET_TEAM:
|
|
CG_TeamBase(cent);
|
|
break;
|
|
case ET_DECAL:
|
|
CG_DrawDecal(cent);
|
|
break;
|
|
case ET_LASER:
|
|
//Elder: the local laser call is checked in playerstate unless it is disabled
|
|
//if (!cg_RQ3_laserAssist.integer || cent->currentState.clientNum != cg.snap->ps.clientNum)
|
|
CG_LaserSight(cent);
|
|
break;
|
|
case ET_DLIGHT:
|
|
CG_Dlight(cent);
|
|
break;
|
|
case ET_CORONA:
|
|
CG_Corona(cent);
|
|
break;
|
|
case ET_SHADOW:
|
|
CG_FakeShadow(cent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddPacketEntities
|
|
|
|
Makro - added skyportal param
|
|
===============
|
|
*/
|
|
|
|
//Makro - added
|
|
int cmpSnapEntities(const void *a, const void *b)
|
|
{
|
|
return cg_moveParentRanks[((entityState_t*)b)->number] - cg_moveParentRanks[((entityState_t*)a)->number];
|
|
}
|
|
|
|
void CG_AddPacketEntities(int mode)
|
|
{
|
|
centity_t *cent;
|
|
int num;
|
|
|
|
//Makro - if we're rendering the entities in a sky portal, we don't need this stuff
|
|
if (mode != ADDENTS_SKYPORTAL) {
|
|
playerState_t *ps;
|
|
|
|
// set cg.frameInterpolation
|
|
if (cg.nextSnap) {
|
|
int delta;
|
|
|
|
delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
|
|
if (delta == 0) {
|
|
cg.frameInterpolation = 0;
|
|
} else {
|
|
cg.frameInterpolation = (float) (cg.time - cg.snap->serverTime) / delta;
|
|
}
|
|
} else {
|
|
cg.frameInterpolation = 0; // actually, it should never be used, because
|
|
// no entities should be marked as interpolating
|
|
}
|
|
|
|
// the auto-rotating items will all have the same axis
|
|
cg.autoAngles[0] = 0;
|
|
cg.autoAngles[1] = 0; //Blaze: changed this ( cg.time & 2047 ) * 360 / 2048.0; to 0;
|
|
cg.autoAngles[2] = 0;
|
|
|
|
cg.autoAnglesFast[0] = 0;
|
|
//Elder: restored for weapon rotation
|
|
cg.autoAnglesFast[1] = (cg.time & 1023) * 360 / 1024.0f;
|
|
cg.autoAnglesFast[2] = 0;
|
|
|
|
AnglesToAxis(cg.autoAngles, cg.autoAxis);
|
|
AnglesToAxis(cg.autoAnglesFast, cg.autoAxisFast);
|
|
|
|
// generate and add the entity from the playerstate
|
|
ps = &cg.predictedPlayerState;
|
|
BG_PlayerStateToEntityState(ps, &cg.predictedPlayerEntity.currentState, qfalse);
|
|
CG_AddCEntity(&cg.predictedPlayerEntity);
|
|
|
|
// lerp the non-predicted value for lightning gun origins
|
|
CG_CalcEntityLerpPositions(&cg_entities[cg.snap->ps.clientNum]);
|
|
// JBravo: unlagged
|
|
if (cg.nextSnap) {
|
|
for (num = 0 ; num < cg.nextSnap->numEntities ; num++) {
|
|
//Makro - use pre-determined order so that attached entities are added before their "parents"
|
|
cent = &cg_entities[cg.nextSnap->entities[cg_nextSnapEntityOrder[num]].number];
|
|
if (cent->nextState.eType == ET_MISSILE || cent->nextState.eType == ET_GENERAL) {
|
|
CG_TransitionEntity(cent);
|
|
cent->interpolate = qtrue;
|
|
CG_AddCEntity(cent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Makro - if we have a sky portal
|
|
if (mode != ADDENTS_NOSKYPORTAL) {
|
|
// add each entity sent over by the server
|
|
for (num = 0; num < cg.snap->numEntities; num++) {
|
|
//Makro - use pre-determined order so that attached entities are added before their "parents"
|
|
cent = &cg_entities[cg.snap->entities[cg_snapEntityOrder[num]].number];
|
|
//if we're adding sky portal entities
|
|
if (mode == ADDENTS_SKYPORTAL) {
|
|
if (cent->currentState.eFlags & EF_HEADLESS) {
|
|
CG_AddCEntity(cent);
|
|
}
|
|
//if not, we're adding normal entities
|
|
} else {
|
|
if (!(cent->currentState.eFlags & EF_HEADLESS) || cent->currentState.eType == ET_PLAYER) {
|
|
CG_AddCEntity(cent);
|
|
}
|
|
}
|
|
}
|
|
//no sky portal defined, just add all the entities without checking if they're
|
|
//sky portal entities or not (faster)
|
|
} else {
|
|
for (num = 0; num < cg.snap->numEntities; num++) {
|
|
//Makro - use pre-determined order so that attached entities are added before their "parents"
|
|
cent = &cg_entities[cg.snap->entities[cg_snapEntityOrder[num]].number];
|
|
if (!cg.nextSnap || (cent->nextState.eType != ET_MISSILE && cent->nextState.eType != ET_GENERAL)) {
|
|
CG_AddCEntity(cent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_LaserSight
|
|
|
|
Creates the laser dot
|
|
Elder's Note: Client does not use this if the dot is his/her own -- see CG_LocalLaser
|
|
NiceAss's Note: Lies?! It seems to for me =P I don't even think CG_LocalLaser gets called...
|
|
==================
|
|
*/
|
|
|
|
static void CG_LaserSight(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
|
|
// create the reference entity
|
|
memset(&ent, 0, sizeof(ent));
|
|
|
|
VectorCopy(cent->lerpOrigin, ent.origin);
|
|
VectorCopy(cent->lerpOrigin, ent.oldorigin);
|
|
|
|
if (cent->currentState.eventParm == 1) {
|
|
ent.reType = RT_SPRITE;
|
|
ent.radius = 3;
|
|
ent.rotation = 0;
|
|
ent.customShader = cgs.media.laserShader;
|
|
|
|
// NiceAss: If it's your laser dot, draw it
|
|
// or if not in the fog and not your laser dot draw it
|
|
if ( ( cent->currentState.clientNum == cg.clientNum ||
|
|
(!(trap_CM_PointContents(cent->lerpOrigin, 0) & CONTENTS_FOG) &&
|
|
cent->currentState.clientNum != cg.clientNum) ) &&
|
|
cg_enableLaserFog.integer )
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
} else {
|
|
trap_R_AddLightToScene(ent.origin, 200, 1, 1, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_Dlight
|
|
Added by Elder.
|
|
|
|
Use sparingly.
|
|
=================
|
|
*/
|
|
//Makro - dlight styles
|
|
char dlightStyles[MAX_DLIGHT_STYLES][MAX_DLIGHT_STLE_LEN];
|
|
int dlightStyleCount;
|
|
|
|
#define DLIGHT_FRAMETIME 50
|
|
|
|
float Dlight_IntensityForChar(char c)
|
|
{
|
|
if (c>= 'a' && c<='z')
|
|
return ((float)(c-'a'))/((float)('z'-'a'));
|
|
else if (c>= 'A' && c<='Z')
|
|
return ((float)(c-'A'))/((float)('Z'-'A'));
|
|
else if (c>= '0' && c<='9')
|
|
return ((float)(c-'0'))/((float)('9'-'0'));
|
|
else
|
|
return 1.0f;
|
|
}
|
|
|
|
static void CG_Dlight(centity_t * cent)
|
|
{
|
|
//Makro - kinda hackish, but oh well...
|
|
//allows us to trigger them on off; SVF_NOCLIENT should've done this already, though
|
|
if (!(cent->currentState.eFlags & EF_NODRAW)) {
|
|
int cl, dls;
|
|
float i, r, g, b, i2;
|
|
|
|
dls = cent->currentState.eventParm & DLIGHT_CUSTOMSTYLE;
|
|
cl = cent->currentState.constantLight;
|
|
r = (cl & 255) / 255.0f;
|
|
g = ((cl >> 8) & 255) / 255.0f;
|
|
b = ((cl >> 16) & 255) / 255.0f;
|
|
i = ((cl >> 24) & 255) * 8;
|
|
i2 = cent->currentState.weapon;
|
|
|
|
if (cent->currentState.eventParm & DLIGHT_FLICKER)
|
|
i += random() * (i2-i);
|
|
|
|
if (cent->currentState.eventParm & DLIGHT_PULSE)
|
|
{
|
|
float frequency = cent->currentState.powerups / 1000.0f;
|
|
float phase = 2 * M_PI * (frequency * cg.time / 1000.0f + cent->currentState.otherEntityNum2 / 1000.0f);
|
|
//CG_Printf("cgame: (%f %f %f) %f -> %f = ", r, g, b, i, i2);
|
|
i += (sin(phase) + 1) * (i2-i) / 2.0f;
|
|
//CG_Printf("%f\n", i);
|
|
}
|
|
|
|
if (dls>0)
|
|
{
|
|
int slen = strlen(dlightStyles[dls-1]);
|
|
if (slen)
|
|
{
|
|
int index = (cg.time / DLIGHT_FRAMETIME) % slen, nindex = index+1 % slen;
|
|
int dtime = cg.time % DLIGHT_FRAMETIME;
|
|
float f1 = Dlight_IntensityForChar(dlightStyles[dls-1][index]);
|
|
float f2 = Dlight_IntensityForChar(dlightStyles[dls-1][nindex]);
|
|
float frac = (f2 * dtime + f1 * (DLIGHT_FRAMETIME-dtime)) / DLIGHT_FRAMETIME;
|
|
r *= frac;
|
|
g *= frac;
|
|
b *= frac;
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.eventParm & DLIGHT_ADDITIVE)
|
|
trap_R_AddAdditiveLightToScene(cent->lerpOrigin, i, r, g, b);
|
|
else
|
|
trap_R_AddLightToScene(cent->lerpOrigin, i, r, g, b);
|
|
}
|
|
}
|
|
|
|
static void CG_FakeShadow(centity_t * cent)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void CG_Corona(centity_t * cent)
|
|
{
|
|
refEntity_t ent;
|
|
|
|
memset(&ent, 0, sizeof(ent));
|
|
VectorCopy(cent->currentState.origin, ent.origin);
|
|
ent.reType = RT_SPRITE;
|
|
ent.customShader = cgs.media.coronaShader;
|
|
ent.radius = 32;
|
|
if (0)
|
|
ent.renderfx = RF_DEPTHHACK;
|
|
ent.shaderRGBA[0] = 255;
|
|
ent.shaderRGBA[1] = ent.shaderRGBA[0];
|
|
ent.shaderRGBA[2] = ent.shaderRGBA[0];
|
|
ent.shaderRGBA[3] = ent.shaderRGBA[0];
|
|
trap_R_AddRefEntityToScene(&ent);
|
|
}
|