Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

105
code/cgame/FX_ATSTMain.cpp Normal file
View File

@@ -0,0 +1,105 @@
// Bowcaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_ATSTMainProjectileThink
---------------------------
*/
void FX_ATSTMainProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 30 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 30.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "atst/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ATSTMainHitWall
---------------------------
*/
void FX_ATSTMainHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "atst/wall_impact", origin, normal );
}
/*
---------------------------
FX_ATSTMainHitPlayer
---------------------------
*/
void FX_ATSTMainHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
if ( humanoid )
{
theFxScheduler.PlayEffect( "atst/flesh_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "atst/droid_impact", origin, normal );
}
}
/*
---------------------------
FX_ATSTSideAltProjectileThink
---------------------------
*/
void FX_ATSTSideAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "atst/side_alt_shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ATSTSideMainProjectileThink
---------------------------
*/
void FX_ATSTSideMainProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "atst/side_main_shot", cent->lerpOrigin, forward );
}

95
code/cgame/FX_Blaster.cpp Normal file
View File

@@ -0,0 +1,95 @@
// Blaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_BlasterProjectileThink
-------------------------
*/
void FX_BlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if (cent->currentState.eFlags & EF_USE_ANGLEDELTA)
{
AngleVectors(cent->currentState.angles, forward, 0, 0);
}
else
{
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 )
{
theFxScheduler.PlayEffect( "blaster/NPCshot", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( cgs.effects.blasterShotEffect, cent->lerpOrigin, forward );
}
}
/*
-------------------------
FX_BlasterAltFireThink
-------------------------
*/
void FX_BlasterAltFireThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
FX_BlasterProjectileThink( cent, weapon );
}
/*
-------------------------
FX_BlasterWeaponHitWall
-------------------------
*/
void FX_BlasterWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.blasterWallImpactEffect, origin, normal );
}
/*
-------------------------
FX_BlasterWeaponHitPlayer
-------------------------
*/
void FX_BlasterWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
theFxScheduler.PlayEffect( cgs.effects.blasterFleshImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,66 @@
// Bowcaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_BowcasterProjectileThink
---------------------------
*/
void FX_BowcasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( cgs.effects.bowcasterShotEffect, cent->lerpOrigin, forward );
}
/*
---------------------------
FX_BowcasterHitWall
---------------------------
*/
void FX_BowcasterHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.bowcasterImpactEffect, origin, normal );
}
/*
---------------------------
FX_BowcasterHitPlayer
---------------------------
*/
void FX_BowcasterHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bowcasterImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,156 @@
// Bryar Pistol Weapon Effects
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
MAIN FIRE
-------------------------
FX_BryarProjectileThink
-------------------------
*/
void FX_BryarProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 )
{
theFxScheduler.PlayEffect( "bryar/NPCshot", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( cgs.effects.bryarShotEffect, cent->lerpOrigin, forward );
}
}
/*
-------------------------
FX_BryarHitWall
-------------------------
*/
void FX_BryarHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect, origin, normal );
}
/*
-------------------------
FX_BryarHitPlayer
-------------------------
*/
void FX_BryarHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bryarFleshImpactEffect, origin, normal );
}
/*
-------------------------
ALT FIRE
-------------------------
FX_BryarAltProjectileThink
-------------------------
*/
void FX_BryarAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
// see if we have some sort of extra charge going on
for ( int t = 1; t < cent->gent->count; t++ )
{
// just add ourselves over, and over, and over when we are charged
theFxScheduler.PlayEffect( cgs.effects.bryarPowerupShotEffect, cent->lerpOrigin, forward );
}
theFxScheduler.PlayEffect( cgs.effects.bryarShotEffect, cent->lerpOrigin, forward );
}
/*
-------------------------
FX_BryarAltHitWall
-------------------------
*/
void FX_BryarAltHitWall( vec3_t origin, vec3_t normal, int power )
{
switch( power )
{
case 4:
case 5:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect3, origin, normal );
break;
case 2:
case 3:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect2, origin, normal );
break;
default:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect, origin, normal );
break;
}
}
/*
-------------------------
FX_BryarAltHitPlayer
-------------------------
*/
void FX_BryarAltHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bryarFleshImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,98 @@
// Concussion Rifle Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_ConcProjectileThink
---------------------------
*/
void FX_ConcProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "concussion/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ConcHitWall
---------------------------
*/
void FX_ConcHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "concussion/explosion", origin, normal );
}
/*
---------------------------
FX_ConcHitPlayer
---------------------------
*/
void FX_ConcHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "concussion/explosion", origin, normal );
}
/*
---------------------------
FX_ConcAltShot
---------------------------
*/
static vec3_t WHITE ={1.0f,1.0f,1.0f};
void FX_ConcAltShot( vec3_t start, vec3_t end )
{
//"concussion/beam"
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/effects/blueLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
vec3_t BRIGHT={0.75f,0.5f,1.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
BRIGHT, BRIGHT, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
/*
---------------------------
FX_ConcAltMiss
---------------------------
*/
void FX_ConcAltMiss( vec3_t origin, vec3_t normal )
{
vec3_t pos, c1, c2;
VectorMA( origin, 4.0f, normal, c1 );
VectorCopy( c1, c2 );
c1[2] += 4;
c2[2] += 12;
VectorAdd( origin, normal, pos );
pos[2] += 28;
FX_AddBezier( origin, pos, c1, vec3_origin, c2, vec3_origin, 6.0f, 6.0f, 0.0f, 0.0f, 0.2f, 0.5f, WHITE, WHITE, 0.0f, 4000, cgi_R_RegisterShader( "gfx/effects/smokeTrail" ), FX_ALPHA_WAVE );
theFxScheduler.PlayEffect( "concussion/alt_miss", origin, normal );
}

92
code/cgame/FX_DEMP2.cpp Normal file
View File

@@ -0,0 +1,92 @@
// DEMP2 Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "FxUtil.h"
/*
---------------------------
FX_DEMP2_ProjectileThink
---------------------------
*/
void FX_DEMP2_ProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
// theFxScheduler.PlayEffect( "demp2/shot", cent->lerpOrigin, forward );
// theFxScheduler.PlayEffect( "demp2/shot2", cent->lerpOrigin, forward );
theFxScheduler.PlayEffect( "demp2/projectile", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_DEMP2_HitWall
---------------------------
*/
void FX_DEMP2_HitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "demp2/wall_impact", origin, normal );
}
/*
---------------------------
FX_DEMP2_HitPlayer
---------------------------
*/
void FX_DEMP2_HitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "demp2/flesh_impact", origin, normal );
}
/*
---------------------------
FX_DEMP2_AltProjectileThink
---------------------------
*/
void FX_DEMP2_AltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "demp2/projectile", cent->lerpOrigin, forward );
}
//---------------------------------------------
void FX_DEMP2_AltDetonate( vec3_t org, float size )
{
localEntity_t *ex;
ex = CG_AllocLocalEntity();
ex->leType = LE_FADE_SCALE_MODEL;
memset( &ex->refEntity, 0, sizeof( refEntity_t ));
ex->refEntity.renderfx |= RF_VOLUMETRIC;
ex->startTime = cg.time;
ex->endTime = ex->startTime + 1300;
ex->radius = size;
ex->refEntity.customShader = cgi_R_RegisterShader( "gfx/effects/demp2shell" );
ex->refEntity.hModel = cgi_R_RegisterModel( "models/items/sphere.md3" );
VectorCopy( org, ex->refEntity.origin );
ex->color[0] = ex->color[1] = ex->color[2] = 255.0f;
}

View File

@@ -0,0 +1,98 @@
// Disruptor Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_DisruptorMainShot
---------------------------
*/
static vec3_t WHITE ={1.0f,1.0f,1.0f};
void FX_DisruptorMainShot( vec3_t start, vec3_t end )
{
FX_AddLine( -1, start, end, 0.1f, 4.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
120, cgi_R_RegisterShader( "gfx/effects/redLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
/*
---------------------------
FX_DisruptorAltShot
---------------------------
*/
void FX_DisruptorAltShot( vec3_t start, vec3_t end, qboolean fullCharge )
{
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/effects/redLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
if ( fullCharge )
{
vec3_t YELLER={0.8f,0.7f,0.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
YELLER, YELLER, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
}
/*
---------------------------
FX_DisruptorAltMiss
---------------------------
*/
void FX_DisruptorAltMiss( vec3_t origin, vec3_t normal )
{
vec3_t pos, c1, c2;
VectorMA( origin, 4.0f, normal, c1 );
VectorCopy( c1, c2 );
c1[2] += 4;
c2[2] += 12;
VectorAdd( origin, normal, pos );
pos[2] += 28;
FX_AddBezier( origin, pos, c1, vec3_origin, c2, vec3_origin, 6.0f, 6.0f, 0.0f, 0.0f, 0.2f, 0.5f, WHITE, WHITE, 0.0f, 4000, cgi_R_RegisterShader( "gfx/effects/smokeTrail" ), FX_ALPHA_WAVE );
theFxScheduler.PlayEffect( "disruptor/alt_miss", origin, normal );
}
/*
---------------------------
FX_KothosBeam
---------------------------
*/
void FX_KothosBeam( vec3_t start, vec3_t end )
{
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/misc/dr1" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
vec3_t YELLER={0.8f,0.7f,0.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
YELLER, YELLER, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}

146
code/cgame/FX_Emplaced.cpp Normal file
View File

@@ -0,0 +1,146 @@
// Emplaced Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_EmplacedProjectileThink
---------------------------
*/
void FX_EmplacedProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
// If tie-fighter missle use green shot.
if ( cent->currentState.weapon == WP_TIE_FIGHTER )
{
theFxScheduler.PlayEffect( "ships/imp_blastershot", cent->lerpOrigin, forward );
}
else
{
if ( cent->gent && cent->gent->owner && cent->gent->owner->activator && cent->gent->owner->activator->s.number > 0 )
{
// NPC's do short shot
if ( cent->gent->alt_fire )
{
theFxScheduler.PlayEffect( "eweb/shotNPC", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( "emplaced/shotNPC", cent->lerpOrigin, forward );
}
}
else
{
// players do long shot
if ( cent->gent && cent->gent->alt_fire )
{
theFxScheduler.PlayEffect( "eweb/shotNPC", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( "emplaced/shot", cent->lerpOrigin, forward );
}
}
}
}
/*
---------------------------
FX_EmplacedHitWall
---------------------------
*/
void FX_EmplacedHitWall( vec3_t origin, vec3_t normal, qboolean eweb )
{
if ( eweb )
{
theFxScheduler.PlayEffect( "eweb/wall_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "emplaced/wall_impact", origin, normal );
}
}
/*
---------------------------
FX_EmplacedHitPlayer
---------------------------
*/
void FX_EmplacedHitPlayer( vec3_t origin, vec3_t normal, qboolean eweb )
{
if ( eweb )
{
theFxScheduler.PlayEffect( "eweb/flesh_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "emplaced/wall_impact", origin, normal );
}
}
/*
---------------------------
FX_TurretProjectileThink
---------------------------
*/
void FX_TurretProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "turret/shot", cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,73 @@
// Golan Arms Flechette Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_FlechetteProjectileThink
-------------------------
*/
void FX_FlechetteProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
EvaluateTrajectoryDelta( &cent->gent->s.pos, cg.time, forward );
if ( VectorNormalize( forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( cgs.effects.flechetteShotEffect, cent->lerpOrigin, forward );
}
/*
-------------------------
FX_FlechetteWeaponHitWall
-------------------------
*/
void FX_FlechetteWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.flechetteShotDeathEffect, origin, normal );
}
/*
-------------------------
FX_BlasterWeaponHitPlayer
-------------------------
*/
void FX_FlechetteWeaponHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
// if ( humanoid )
// {
theFxScheduler.PlayEffect( cgs.effects.flechetteFleshImpactEffect, origin, normal );
// }
// else
// {
// theFxScheduler.PlayEffect( "blaster/droid_impact", origin, normal );
// }
}
/*
-------------------------
FX_FlechetteProjectileThink
-------------------------
*/
void FX_FlechetteAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( cgs.effects.flechetteAltShotEffect, cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,92 @@
// Heavy Repeater Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_RepeaterProjectileThink
---------------------------
*/
void FX_RepeaterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "repeater/projectile", cent->lerpOrigin, forward );
}
/*
------------------------
FX_RepeaterHitWall
------------------------
*/
void FX_RepeaterHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "repeater/wall_impact", origin, normal );
}
/*
------------------------
FX_RepeaterHitPlayer
------------------------
*/
void FX_RepeaterHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "repeater/wall_impact", origin, normal );
// theFxScheduler.PlayEffect( "repeater/flesh_impact", origin, normal );
}
/*
------------------------------
FX_RepeaterAltProjectileThink
-----------------------------
*/
void FX_RepeaterAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "repeater/alt_projectile", cent->lerpOrigin, forward );
// theFxScheduler.PlayEffect( "repeater/alt_projectile", cent->lerpOrigin, forward );
}
/*
------------------------
FX_RepeaterAltHitWall
------------------------
*/
void FX_RepeaterAltHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "repeater/concussion", origin, normal );
// theFxScheduler.PlayEffect( "repeater/alt_wall_impact2", origin, normal );
}
/*
------------------------
FX_RepeaterAltHitPlayer
------------------------
*/
void FX_RepeaterAltHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "repeater/concussion", origin );
// theFxScheduler.PlayEffect( "repeater/alt_wall_impact2", origin, normal );
}

View File

@@ -0,0 +1,72 @@
// Noghri Rifle
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_NoghriShotProjectileThink
-------------------------
*/
void FX_NoghriShotProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "noghri_stick/shot", cent->lerpOrigin, forward );
}
/*
-------------------------
FX_NoghriShotWeaponHitWall
-------------------------
*/
void FX_NoghriShotWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "noghri_stick/flesh_impact", origin, normal );//no "noghri/wall_impact"?
}
/*
-------------------------
FX_NoghriShotWeaponHitPlayer
-------------------------
*/
void FX_NoghriShotWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
/*
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
*/
theFxScheduler.PlayEffect( "noghri_stick/flesh_impact", origin, normal );
}

View File

@@ -0,0 +1,66 @@
// Rocket Launcher Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_RocketProjectileThink
---------------------------
*/
void FX_RocketProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "rocket/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_RocketHitWall
---------------------------
*/
void FX_RocketHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "rocket/explosion", origin, normal );
}
/*
---------------------------
FX_RocketHitPlayer
---------------------------
*/
void FX_RocketHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "rocket/explosion", origin, normal );
}
/*
---------------------------
FX_RocketAltProjectileThink
---------------------------
*/
void FX_RocketAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "rocket/shot", cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,70 @@
// Tusken Rifle
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_TuskenShotProjectileThink
-------------------------
*/
void FX_TuskenShotProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "tusken/shot", cent->lerpOrigin, forward );
}
/*
-------------------------
FX_TuskenShotWeaponHitWall
-------------------------
*/
void FX_TuskenShotWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "tusken/hitwall", origin, normal );
}
/*
-------------------------
FX_TuskenShotWeaponHitPlayer
-------------------------
*/
void FX_TuskenShotWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
theFxScheduler.PlayEffect( "tusken/hit", origin, normal );
}

5
code/cgame/FxParsing.cpp Normal file
View File

@@ -0,0 +1,5 @@
// this include must remain at the top of every FXxxxx.CPP file
#include "common_headers.h"

6
code/cgame/FxParsing.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#if !defined(FX_PARSING_H_INC)
#define FX_PARSING_H_INC
#endif // FX_PARSING_H

2301
code/cgame/FxPrimitives.cpp Normal file

File diff suppressed because it is too large Load Diff

572
code/cgame/FxPrimitives.h Normal file
View File

@@ -0,0 +1,572 @@
#if !defined(FX_SYSTEM_H_INC)
#include "FxSystem.h"
#endif
#ifndef FX_PRIMITIVES_H_INC
#define FX_PRIMITIVES_H_INC
#define MAX_EFFECTS 1200
// Generic group flags, used by parser, then get converted to the appropriate specific flags
#define FX_PARM_MASK 0xC // use this to mask off any transition types that use a parm
#define FX_GENERIC_MASK 0xF
#define FX_LINEAR 0x1
#define FX_RAND 0x2
#define FX_NONLINEAR 0x4
#define FX_WAVE 0x8
#define FX_CLAMP 0xC
// Group flags
#define FX_ALPHA_SHIFT 0
#define FX_ALPHA_PARM_MASK 0x0000000C
#define FX_ALPHA_LINEAR 0x00000001
#define FX_ALPHA_RAND 0x00000002
#define FX_ALPHA_NONLINEAR 0x00000004
#define FX_ALPHA_WAVE 0x00000008
#define FX_ALPHA_CLAMP 0x0000000C
#define FX_RGB_SHIFT 4
#define FX_RGB_PARM_MASK 0x000000C0
#define FX_RGB_LINEAR 0x00000010
#define FX_RGB_RAND 0x00000020
#define FX_RGB_NONLINEAR 0x00000040
#define FX_RGB_WAVE 0x00000080
#define FX_RGB_CLAMP 0x000000C0
#define FX_SIZE_SHIFT 8
#define FX_SIZE_PARM_MASK 0x00000C00
#define FX_SIZE_LINEAR 0x00000100
#define FX_SIZE_RAND 0x00000200
#define FX_SIZE_NONLINEAR 0x00000400
#define FX_SIZE_WAVE 0x00000800
#define FX_SIZE_CLAMP 0x00000C00
#define FX_LENGTH_SHIFT 12
#define FX_LENGTH_PARM_MASK 0x0000C000
#define FX_LENGTH_LINEAR 0x00001000
#define FX_LENGTH_RAND 0x00002000
#define FX_LENGTH_NONLINEAR 0x00004000
#define FX_LENGTH_WAVE 0x00008000
#define FX_LENGTH_CLAMP 0x0000C000
#define FX_SIZE2_SHIFT 16
#define FX_SIZE2_PARM_MASK 0x000C0000
#define FX_SIZE2_LINEAR 0x00010000
#define FX_SIZE2_RAND 0x00020000
#define FX_SIZE2_NONLINEAR 0x00040000
#define FX_SIZE2_WAVE 0x00080000
#define FX_SIZE2_CLAMP 0x000C0000
// Feature flags
#define FX_DEPTH_HACK 0x00100000
#define FX_RELATIVE 0x00200000
#define FX_SET_SHADER_TIME 0x00400000 // by having the effects system set the shader time, we can make animating textures start at the correct time
#define FX_EXPENSIVE_PHYSICS 0x00800000
//rww - g2-related flags (these can slow things down significantly, use sparingly)
//These should be used only with particles/decals as they steal flags used by cylinders.
#define FX_GHOUL2_TRACE 0x00020000 //use in conjunction with particles - actually do full ghoul2 traces for physics collision against entities with a ghoul2 instance
//shared FX_SIZE2_RAND (used only with cylinders)
#define FX_GHOUL2_DECALS 0x00040000 //use in conjunction with decals - can project decal as a ghoul2 gore skin object onto ghoul2 models
//shared FX_SIZE2_NONLINEAR (used only with cylinders)
#define FX_ATTACHED_MODEL 0x01000000
#define FX_APPLY_PHYSICS 0x02000000
#define FX_USE_BBOX 0x04000000 // can make physics more accurate at the expense of speed
#define FX_USE_ALPHA 0x08000000 // the FX system actually uses RGB to do fades, but this will override that
// and cause it to fill in the alpha.
#define FX_EMIT_FX 0x10000000 // emitters technically don't have to emit stuff, but when they do
// this flag needs to be set
#define FX_DEATH_RUNS_FX 0x20000000 // Normal death triggers effect, but not kill_on_impact
#define FX_KILL_ON_IMPACT 0x40000000 // works just like it says, but only when physics are on.
#define FX_IMPACT_RUNS_FX 0x80000000 // an effect can call another effect when it hits something.
// Lightning flags, duplicates of existing flags, but lightning doesn't use those flags in that context...and nothing will ever use these in this context..so we are safe.
#define FX_TAPER 0x01000000 // tapers as it moves towards its endpoint
#define FX_BRANCH 0x02000000 // enables lightning branching
#define FX_GROW 0x04000000 // lightning grows from start point to end point over the course of its life
//------------------------------
class CEffect
{
protected:
vec3_t mOrigin1;
int mTimeStart;
int mTimeEnd;
unsigned int mFlags;
// Size of our object, useful for things that have physics
vec3_t mMin;
vec3_t mMax;
int mImpactFxID; // if we have an impact event, we may have to call an effect
int mDeathFxID; // if we have a death event, we may have to call an effect
refEntity_t mRefEnt;
public:
CEffect() { memset( &mRefEnt, 0, sizeof( refEntity_t )); }
virtual ~CEffect() {}
virtual void Die() {}
virtual bool Update()
{ // Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime ) {
return false;
}
return true;
}
inline void SetSTScale(float s,float t) { mRefEnt.shaderTexCoord[0]=s;mRefEnt.shaderTexCoord[1]=t;}
inline void SetMin( const vec3_t min ) { if(min){VectorCopy(min,mMin);}else{VectorClear(mMin);} }
inline void SetMax( const vec3_t max ) { if(max){VectorCopy(max,mMax);}else{VectorClear(mMax);} }
inline void SetFlags( int flags ) { mFlags = flags; }
inline void AddFlags( int flags ) { mFlags |= flags; }
inline void ClearFlags( int flags ) { mFlags &= ~flags; }
inline void SetOrigin1( const vec3_t org ) { if(org){VectorCopy(org,mOrigin1);}else{VectorClear(mOrigin1);} }
inline void SetTimeStart( int time ) { mTimeStart = time; if (mFlags&FX_SET_SHADER_TIME) { mRefEnt.shaderTime = cg.time * 0.001f; }}
inline void SetTimeEnd( int time ) { mTimeEnd = time; }
inline void SetImpactFxID( int id ) { mImpactFxID = id; }
inline void SetDeathFxID( int id ) { mDeathFxID = id; }
};
//---------------------------------------------------
// This class is kind of an exception to the "rule".
// For now it exists only for allowing an easy way
// to get the saber slash trails rendered.
//---------------------------------------------------
class CTrail : public CEffect
{
// This is such a specific case thing, just grant public access to the goods.
protected:
void Draw();
public:
typedef struct
{
vec3_t origin;
// very specifc case, we can modulate the color and the alpha
vec3_t rgb;
vec3_t destrgb;
vec3_t curRGB;
float alpha;
float destAlpha;
float curAlpha;
// this is a very specific case thing...allow interpolating the st coords so we can map the texture
// properly as this segement progresses through it's life
float ST[2];
float destST[2];
float curST[2];
} TVert;
TVert mVerts[4];
qhandle_t mShader;
CTrail() {};
virtual ~CTrail() {};
virtual bool Update();
};
//------------------------------
class CLight : public CEffect
{
protected:
float mSizeStart;
float mSizeEnd;
float mSizeParm;
vec3_t mRGBStart;
vec3_t mRGBEnd;
float mRGBParm;
void UpdateSize();
void UpdateRGB();
void Draw()
{
theFxHelper.AddLightToScene( mOrigin1, mRefEnt.radius,
mRefEnt.lightingOrigin[0], mRefEnt.lightingOrigin[1], mRefEnt.lightingOrigin[2] );
}
public:
CLight() {}
virtual ~CLight() {}
virtual bool Update();
inline void SetSizeStart( float sz ) { mSizeStart = sz; }
inline void SetSizeEnd( float sz ) { mSizeEnd = sz; }
inline void SetSizeParm( float parm ) { mSizeParm = parm; }
inline void SetRGBStart( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} }
inline void SetRGBEnd( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} }
inline void SetRGBParm( float parm ) { mRGBParm = parm; }
};
//------------------------------
class CFlash : public CLight
{
protected:
void Draw();
public:
CFlash() {}
virtual ~CFlash() {}
virtual bool Update();
inline void SetShader( qhandle_t sh )
{ assert(sh);
mRefEnt.customShader = sh;
}
void Init( void );
};
//------------------------------
class CParticle : public CEffect
{
protected:
vec3_t mOrgOffset;
vec3_t mVel;
vec3_t mAccel;
float mGravity;
float mSizeStart;
float mSizeEnd;
float mSizeParm;
vec3_t mRGBStart;
vec3_t mRGBEnd;
float mRGBParm;
float mAlphaStart;
float mAlphaEnd;
float mAlphaParm;
float mRotationDelta;
float mElasticity;
short mClientID;
char mModelNum;
char mBoltNum;
bool UpdateOrigin();
void UpdateVelocity() {VectorMA( mVel, theFxHelper.mFloatFrameTime, mAccel, mVel ); }
void UpdateSize();
void UpdateRGB();
void UpdateAlpha();
void UpdateRotation() { mRefEnt.rotation += theFxHelper.mFrameTime * 0.01f * mRotationDelta; }
bool Cull();
void Draw();
public:
inline CParticle() { mRefEnt.reType = RT_SPRITE; mClientID = -1; mModelNum = -1; mBoltNum = -1; }
virtual ~CParticle() {}
virtual void Die();
virtual bool Update();
inline void SetShader( qhandle_t sh ) { mRefEnt.customShader = sh;}
inline void SetOrgOffset( const vec3_t o ) { if(o){VectorCopy(o,mOrgOffset);}else{VectorClear(mOrgOffset);}}
inline void SetVel( const vec3_t vel ) { if(vel){VectorCopy(vel,mVel);}else{VectorClear(mVel);} }
inline void SetAccel( const vec3_t ac ) { if(ac){VectorCopy(ac,mAccel);}else{VectorClear(mAccel);} }
inline void SetGravity( float grav ) { mGravity = grav; }
inline void SetSizeStart( float sz ) { mSizeStart = sz; }
inline void SetSizeEnd( float sz ) { mSizeEnd = sz; }
inline void SetSizeParm( float parm ) { mSizeParm = parm; }
inline void SetRGBStart( const vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} }
inline void SetRGBEnd( const vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} }
inline void SetRGBParm( float parm ) { mRGBParm = parm; }
inline void SetAlphaStart( float al ) { mAlphaStart = al; }
inline void SetAlphaEnd( float al ) { mAlphaEnd = al; }
inline void SetAlphaParm( float parm ) { mAlphaParm = parm; }
inline void SetRotation( float rot ) { mRefEnt.rotation = rot; }
inline void SetRotationDelta( float rot ) { mRotationDelta = rot; }
inline void SetElasticity( float el ) { mElasticity = el; }
inline void SetClient( int clientID, int modelNum = -1, int boltNum = -1 ) {mClientID = clientID; mModelNum = modelNum; mBoltNum = boltNum; }
};
//------------------------------
class CLine : public CParticle
{
protected:
vec3_t mOrigin2;
void Draw();
public:
CLine() { mRefEnt.reType = RT_LINE;}
virtual ~CLine() {}
virtual void Die() {}
virtual bool Update();
inline void SetOrigin2( const vec3_t org2 ) { VectorCopy( org2, mOrigin2 ); }
};
//------------------------------
class CBezier : public CLine
{
protected:
vec3_t mControl1;
vec3_t mControl1Vel;
vec3_t mControl2;
vec3_t mControl2Vel;
bool mInit;
void Draw();
public:
CBezier(){ mInit = false; }
virtual ~CBezier() {}
virtual void Die() {}
virtual bool Update();
void DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 );
inline void SetControlPoints( const vec3_t ctrl1, const vec3_t ctrl2 ) { VectorCopy( ctrl1, mControl1 ); VectorCopy( ctrl2, mControl2 ); }
inline void SetControlVel( const vec3_t ctrl1v, const vec3_t ctrl2v ) { VectorCopy( ctrl1v, mControl1Vel ); VectorCopy( ctrl2v, mControl2Vel ); }
};
//------------------------------
class CElectricity : public CLine
{
protected:
float mChaos;
void Draw();
public:
CElectricity() { mRefEnt.reType = RT_ELECTRICITY; }
virtual ~CElectricity() {}
virtual void Die() {}
virtual bool Update();
void Initialize();
inline void SetChaos( float chaos ) { mChaos = chaos; }
};
// Oriented quad
//------------------------------
class COrientedParticle : public CParticle
{
protected:
vec3_t mNormal;
vec3_t mNormalOffset;
bool Cull();
void Draw();
public:
COrientedParticle() { mRefEnt.reType = RT_ORIENTED_QUAD; }
virtual ~COrientedParticle() {}
virtual bool Update();
inline void SetNormal( const vec3_t norm ) { VectorCopy( norm, mNormal ); }
inline void SetNormalOffset( const vec3_t norm ) { VectorCopy( norm, mNormalOffset ); }
};
//------------------------------
class CTail : public CParticle
{
protected:
vec3_t mOldOrigin;
float mLengthStart;
float mLengthEnd;
float mLengthParm;
float mLength;
void UpdateLength();
void CalcNewEndpoint();
void Draw();
bool Cull();
public:
CTail() { mRefEnt.reType = RT_LINE; }
virtual ~CTail() {}
virtual bool Update();
inline void SetLengthStart( float len ) { mLengthStart = len; }
inline void SetLengthEnd( float len ) { mLengthEnd = len; }
inline void SetLengthParm( float len ) { mLengthParm = len; }
};
//------------------------------
class CCylinder : public CTail
{
protected:
float mSize2Start;
float mSize2End;
float mSize2Parm;
void UpdateSize2();
void Draw();
public:
CCylinder() { mRefEnt.reType = RT_CYLINDER; }
virtual ~CCylinder() {}
virtual bool Update();
inline void SetSize2Start( float sz ) { mSize2Start = sz; }
inline void SetSize2End( float sz ) { mSize2End = sz; }
inline void SetSize2Parm( float parm ) { mSize2Parm = parm; }
inline void SetNormal( const vec3_t norm ) { VectorCopy( norm, mRefEnt.axis[0] ); }
};
//------------------------------
// Emitters are derived from particles because, although they don't draw, any effect called
// from them can borrow an initial or ending value from the emitters current alpha, rgb, etc..
class CEmitter : public CParticle
{
protected:
vec3_t mOldOrigin; // we use these to do some nice
vec3_t mLastOrigin; // tricks...
vec3_t mOldVelocity; //
int mOldTime;
vec3_t mAngles; // for a rotating thing, using a delta
vec3_t mAngleDelta; // as opposed to an end angle is probably much easier
int mEmitterFxID; // if we have emitter fx, this is our id
float mDensity; // controls how often emitter chucks an effect
float mVariance; // density sloppiness
void UpdateAngles();
void Draw();
public:
CEmitter() {
// There may or may not be a model, but if there isn't one,
// we just won't bother adding the refEnt in our Draw func
mRefEnt.reType = RT_MODEL;
}
virtual ~CEmitter() {}
virtual bool Update();
inline void SetModel( qhandle_t model ) { mRefEnt.hModel = model; }
inline void SetAngles( const vec3_t ang ) { if(ang){VectorCopy(ang,mAngles);}else{VectorClear(mAngles);} }
inline void SetAngleDelta( const vec3_t ang){ if(ang){VectorCopy(ang,mAngleDelta);}else{VectorClear(mAngleDelta);} }
inline void SetEmitterFxID( int id ) { mEmitterFxID = id; }
inline void SetDensity( float density ) { mDensity = density; }
inline void SetVariance( float var ) { mVariance = var; }
inline void SetOldTime( int time ) { mOldTime = time; }
inline void SetLastOrg( const vec3_t org ) { if(org){VectorCopy(org,mLastOrigin);}else{VectorClear(mLastOrigin);} }
inline void SetLastVel( const vec3_t vel ) { if(vel){VectorCopy(vel,mOldVelocity);}else{VectorClear(mOldVelocity);}}
};
// We're getting pretty low level here, not the kind of thing to abuse considering how much overhead this
// adds to a SINGLE triangle or quad....
// The editor doesn't need to see or do anything with this
//------------------------------
#define MAX_CPOLY_VERTS 5
class CPoly : public CParticle
{
protected:
int mCount;
vec3_t mRotDelta;
int mTimeStamp;
bool Cull();
void Draw();
public:
vec3_t mOrg[MAX_CPOLY_VERTS];
vec2_t mST[MAX_CPOLY_VERTS];
float mRot[3][3];
int mLastFrameTime;
CPoly() {}
virtual ~CPoly() {}
virtual bool Update();
void PolyInit();
void CalcRotateMatrix();
void Rotate();
inline void SetNumVerts( int c ) { mCount = c; }
inline void SetRot( vec3_t r ) { if(r){VectorCopy(r,mRotDelta);}else{VectorClear(mRotDelta);}}
inline void SetMotionTimeStamp( int t ) { mTimeStamp = theFxHelper.mTime + t; }
inline int GetMotionTimeStamp() { return mTimeStamp; }
};
#endif //FX_PRIMITIVES_H_INC

2049
code/cgame/FxScheduler.cpp Normal file

File diff suppressed because it is too large Load Diff

497
code/cgame/FxScheduler.h Normal file
View File

@@ -0,0 +1,497 @@
#if !defined(FX_UTIL_H_INC)
#include "FxUtil.h"
#endif
#include "../qcommon/sstring.h"
typedef sstring_t fxString_t;
#if !defined(FX_PARSING_H_INC)
#include "FxParsing.h"
#endif
#ifndef FX_SCHEDULER_H_INC
#define FX_SCHEDULER_H_INC
using namespace std;
#define FX_FILE_PATH "effects"
#define FX_MAX_TRACE_DIST WORLD_SIZE
#define FX_MAX_EFFECTS 150 // how many effects the system can store
#define FX_MAX_EFFECT_COMPONENTS 24 // how many primitives an effect can hold, this should be plenty
#define FX_MAX_PRIM_NAME 32
//-----------------------------------------------
// These are spawn flags for primitiveTemplates
//-----------------------------------------------
#define FX_ORG_ON_SPHERE 0x00001 // Pretty dang expensive, calculates a point on a sphere/ellipsoid
#define FX_AXIS_FROM_SPHERE 0x00002 // Can be used in conjunction with org_on_sphere to cause particles to move out
// from the center of the sphere
#define FX_ORG_ON_CYLINDER 0x00004 // calculate point on cylinder/disk
#define FX_ORG2_FROM_TRACE 0x00010
#define FX_TRACE_IMPACT_FX 0x00020 // if trace impacts, we should play one of the specified impact fx files
#define FX_ORG2_IS_OFFSET 0x00040 // template specified org2 should be the offset from a trace endpos or
// passed in org2. You might use this to lend a random flair to the endpos.
// Note: this is done pre-trace, so you may have to specify large numbers for this
#define FX_CHEAP_ORG_CALC 0x00100 // Origin is calculated relative to passed in axis unless this is on.
#define FX_CHEAP_ORG2_CALC 0x00200 // Origin2 is calculated relative to passed in axis unless this is on.
#define FX_VEL_IS_ABSOLUTE 0x00400 // Velocity isn't relative to passed in axis with this flag on.
#define FX_ACCEL_IS_ABSOLUTE 0x00800 // Acceleration isn't relative to passed in axis with this flag on.
#define FX_RAND_ROT_AROUND_FWD 0x01000 // Randomly rotates up and right around forward vector
#define FX_EVEN_DISTRIBUTION 0x02000 // When you have a delay, it normally picks a random time to play. When
// this flag is on, it generates an even time distribution
#define FX_RGB_COMPONENT_INTERP 0x04000 // Picks a color on the line defined by RGB min & max, default is to pick color in cube defined by min & max
#define FX_AFFECTED_BY_WIND 0x10000 // we take into account our wind vector when we spawn in
#define FX_SND_LESS_ATTENUATION 0x20000 // attenuate sounds less
//-----------------------------------------------------------------
//
// CMediaHandles
//
// Primitive templates might want to use a list of sounds, shaders
// or models to get a bit more variation in their effects.
//
//-----------------------------------------------------------------
class CMediaHandles
{
private:
vector<int> mMediaList;
public:
void AddHandle( int item ) { mMediaList.push_back( item ); }
int GetHandle() { if (mMediaList.size()==0) {return 0;}
else {return mMediaList[irand(0,mMediaList.size()-1)];} }
void operator=(const CMediaHandles &that );
};
//-----------------------------------------------------------------
//
// CFxRange
//
// Primitive templates typically use this class to define each of
// its members. This is done to make it easier to create effects
// with a desired range of characteristics.
//
//-----------------------------------------------------------------
class CFxRange
{
private:
float mMin;
float mMax;
public:
CFxRange() {mMin=0; mMax=0;}
inline void SetRange(float min,float max) {mMin=min; mMax=max;}
inline void SetMin(float min) {mMin=min;}
inline void SetMax(float max) {mMax=max;}
inline float GetMax() const {return mMax;}
inline float GetMin() const {return mMin;}
inline float GetVal(float percent) const {if(mMin == mMax){return mMin;}
return (mMin + (mMax - mMin) * percent);}
inline float GetVal() const {if(mMin == mMax){return mMin;}
return flrand(mMin, mMax);}
inline int GetRoundedVal() const {if(mMin == mMax){return mMin;}
return (int)(flrand(mMin, mMax) + 0.5f);}
inline void ForceRange(float min,float max) {if(mMin < min){mMin=min;} if(mMin > max){mMin=max;}
if(mMax < min){mMax=min;} if(mMax > max){mMax=max;}}
inline void Sort() {if(mMin > mMax){float temp = mMin; mMin=mMax;mMax=temp;}}
void operator=(const CFxRange &that) {mMin=that.mMin; mMax=that.mMax;}
bool operator==(const CFxRange &rhs) const { return ((mMin == rhs.mMin) &&
(mMax == rhs.mMax)); }
};
//----------------------------
// Supported primitive types
//----------------------------
enum EPrimType
{
None = 0,
Particle, // sprite
Line,
Tail, // comet-like tail thing
Cylinder,
Emitter, // emits effects as it moves, can also attach a chunk
Sound,
#ifdef _IMMERSION
Force,
#endif // _IMMERSION
Decal, // projected onto architecture
OrientedParticle,
Electricity,
FxRunner,
Light,
CameraShake,
ScreenFlash
};
//-----------------------------------------------------------------
//
// CPrimitiveTemplate
//
// The primitive template is used to spawn 1 or more fx primitives
// with the range of characteristics defined by the template.
//
// As such, I just made this one huge shared class knowing that
// there won't be many of them in memory at once, and we won't
// be dynamically creating and deleting them mid-game. Also,
// note that not every primitive type will use all of these fields.
//
//-----------------------------------------------------------------
class CPrimitiveTemplate
{
public:
// These kinds of things should not even be allowed to be accessed publicly
bool mCopy;
int mRefCount; // For a copy of a primitive...when we figure out how many items we want to spawn,
// we'll store that here and then decrement us for each we actually spawn. When we
// hit zero, we are no longer used and so we can just free ourselves
char mName[FX_MAX_PRIM_NAME];
EPrimType mType;
CFxRange mSpawnDelay;
CFxRange mSpawnCount;
CFxRange mLife;
int mCullRange;
CMediaHandles mMediaHandles;
CMediaHandles mImpactFxHandles;
CMediaHandles mDeathFxHandles;
CMediaHandles mEmitterFxHandles;
CMediaHandles mPlayFxHandles;
int mFlags; // These need to get passed on to the primitive
int mSpawnFlags; // These are only used to control spawning, but never get passed to prims.
vec3_t mMin;
vec3_t mMax;
CFxRange mOrigin1X;
CFxRange mOrigin1Y;
CFxRange mOrigin1Z;
CFxRange mOrigin2X;
CFxRange mOrigin2Y;
CFxRange mOrigin2Z;
CFxRange mRadius; // spawn on sphere/ellipse/disk stuff.
CFxRange mHeight;
CFxRange mRotation;
CFxRange mRotationDelta;
CFxRange mAngle1;
CFxRange mAngle2;
CFxRange mAngle3;
CFxRange mAngle1Delta;
CFxRange mAngle2Delta;
CFxRange mAngle3Delta;
CFxRange mVelX;
CFxRange mVelY;
CFxRange mVelZ;
CFxRange mAccelX;
CFxRange mAccelY;
CFxRange mAccelZ;
CFxRange mGravity;
CFxRange mDensity;
CFxRange mVariance;
CFxRange mRedStart;
CFxRange mGreenStart;
CFxRange mBlueStart;
CFxRange mRedEnd;
CFxRange mGreenEnd;
CFxRange mBlueEnd;
CFxRange mRGBParm;
CFxRange mAlphaStart;
CFxRange mAlphaEnd;
CFxRange mAlphaParm;
CFxRange mSizeStart;
CFxRange mSizeEnd;
CFxRange mSizeParm;
CFxRange mSize2Start;
CFxRange mSize2End;
CFxRange mSize2Parm;
CFxRange mLengthStart;
CFxRange mLengthEnd;
CFxRange mLengthParm;
CFxRange mTexCoordS;
CFxRange mTexCoordT;
CFxRange mElasticity;
// Lower level parsing utilities
bool ParseVector( const char *val, vec3_t min, vec3_t max );
bool ParseFloat( const char *val, float *min, float *max );
bool ParseGroupFlags( const char *val, int *flags );
// Base key processing
// Note that these all have their own parse functions in case it becomes important to do certain kinds
// of validation specific to that type.
bool ParseMin( const char *val );
bool ParseMax( const char *val );
bool ParseDelay( const char *val );
bool ParseCount( const char *val );
bool ParseLife( const char *val );
bool ParseElasticity( const char *val );
bool ParseFlags( const char *val );
bool ParseSpawnFlags( const char *val );
bool ParseOrigin1( const char *val );
bool ParseOrigin2( const char *val );
bool ParseRadius( const char *val );
bool ParseHeight( const char *val );
bool ParseRotation( const char *val );
bool ParseRotationDelta( const char *val );
bool ParseAngle( const char *val );
bool ParseAngleDelta( const char *val );
bool ParseVelocity( const char *val );
bool ParseAcceleration( const char *val );
bool ParseGravity( const char *val );
bool ParseDensity( const char *val );
bool ParseVariance( const char *val );
// Group type processing
bool ParseRGB( CGPGroup *grp );
bool ParseAlpha( CGPGroup *grp );
bool ParseSize( CGPGroup *grp );
bool ParseSize2( CGPGroup *grp );
bool ParseLength( CGPGroup *grp );
bool ParseModels( CGPValue *grp );
bool ParseShaders( CGPValue *grp );
bool ParseSounds( CGPValue *grp );
#ifdef _IMMERSION
bool ParseForces( CGPValue *grp );
#endif // _IMMERSION
bool ParseImpactFxStrings( CGPValue *grp );
bool ParseDeathFxStrings( CGPValue *grp );
bool ParseEmitterFxStrings( CGPValue *grp );
bool ParsePlayFxStrings( CGPValue *grp );
// Group keys
bool ParseRGBStart( const char *val );
bool ParseRGBEnd( const char *val );
bool ParseRGBParm( const char *val );
bool ParseRGBFlags( const char *val );
bool ParseAlphaStart( const char *val );
bool ParseAlphaEnd( const char *val );
bool ParseAlphaParm( const char *val );
bool ParseAlphaFlags( const char *val );
bool ParseSizeStart( const char *val );
bool ParseSizeEnd( const char *val );
bool ParseSizeParm( const char *val );
bool ParseSizeFlags( const char *val );
bool ParseSize2Start( const char *val );
bool ParseSize2End( const char *val );
bool ParseSize2Parm( const char *val );
bool ParseSize2Flags( const char *val );
bool ParseLengthStart( const char *val );
bool ParseLengthEnd( const char *val );
bool ParseLengthParm( const char *val );
bool ParseLengthFlags( const char *val );
public:
CPrimitiveTemplate();
~CPrimitiveTemplate() {};
bool ParsePrimitive( CGPGroup *grp );
void operator=(const CPrimitiveTemplate &that);
};
// forward declaration
struct SEffectTemplate;
// Effects are built of one or more primitives
struct SEffectTemplate
{
bool mInUse;
bool mCopy;
char mEffectName[MAX_QPATH]; // is this extraneous??
int mPrimitiveCount;
int mRepeatDelay;
CPrimitiveTemplate *mPrimitives[FX_MAX_EFFECT_COMPONENTS];
bool operator == (const char * name) const
{
return !stricmp( mEffectName, name );
}
void operator=(const SEffectTemplate &that);
};
//-----------------------------------------------------------------
//
// CFxScheduler
//
// The scheduler not only handles requests to play an effect, it
// tracks the request throughout its life if necessary, creating
// any of the delayed components as needed.
//
//-----------------------------------------------------------------
// needs to be in global space now (loadsave stuff)
#define MAX_LOOPED_FX 32
// We hold a looped effect here
struct SLoopedEffect
{
int mId; // effect id
int mBoltInfo; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
int mNextTime; //time to render again
int mLoopStopTime; //time to die
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
bool mIsRelative; // bolt this puppy on keep it updated
};
class CFxScheduler
{
private:
// We hold a scheduled effect here
struct SScheduledEffect
{
CPrimitiveTemplate *mpTemplate; // primitive template
int mStartTime;
char mModelNum; // uset to determine which ghoul2 model we want to bolt this effect to
char mBoltNum; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
short mEntNum; // used to determine which entity this ghoul model is attached to.
short mClientID; // FIXME: redundant. this is used for muzzle bolts, merge into normal bolting
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
bool mIsRelative; // bolt this puppy on keep it updated
vec3_t mOrigin;
vec3_t mAxis[3];
bool operator <= (const int time) const
{
return mStartTime <= time;
}
};
/* Looped Effects get stored and reschedule at mRepeatRate */
// must be in sync with gLoopedEffectArray[MAX_LOOPED_FX]!
//
SLoopedEffect mLoopedEffectArray[MAX_LOOPED_FX];
int ScheduleLoopedEffect( int id, int boltInfo, bool isPortal, int iLoopTime, bool isRelative );
void AddLoopedEffects( );
// this makes looking up the index based on the string name much easier
typedef map<fxString_t, int> TEffectID;
typedef list<SScheduledEffect*> TScheduledEffect;
// Effects
SEffectTemplate mEffectTemplates[FX_MAX_EFFECTS];
TEffectID mEffectIDs; // if you only have the unique effect name, you'll have to use this to get the ID.
// List of scheduled effects that will need to be created at the correct time.
TScheduledEffect mFxSchedule;
// Private function prototypes
SEffectTemplate *GetNewEffectTemplate( int *id, const char *file );
void AddPrimitiveToEffect( SEffectTemplate *fx, CPrimitiveTemplate *prim );
int ParseEffect( const char *file, CGPGroup *base );
void CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, vec3_t axis[3], int lateTime, int clientID = -1, int modelNum = -1, int boltNum = -1 );
void CreateEffect( CPrimitiveTemplate *fx, int clientID, int lateTime );
public:
CFxScheduler();
void LoadSave_Read();
void LoadSave_Write();
void FX_CopeWithAnyLoadedSaveGames();
int RegisterEffect( const char *file, bool bHasCorrectPath = false ); // handles pre-caching
// Nasty overloaded madness
void PlayEffect( int id, vec3_t org, bool isPortal = false ); // uses a default up axis
void PlayEffect( int id, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
void PlayEffect( int id, vec3_t origin, vec3_t axis[3], const int boltInfo=-1, const int entNum=-1, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
void PlayEffect( const char *file, vec3_t org, bool isPortal = false ); // uses a default up axis
void PlayEffect( const char *file, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
void PlayEffect( const char *file, vec3_t origin, vec3_t axis[3], const int boltInfo, const int entNum, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
//for muzzle
void PlayEffect( const char *file, int clientID, bool isPortal = false );
#ifdef _IMMERSION // for ff-system
void PlayEffect( int id, int clientNum, vec3_t org, vec3_t fwd, bool isPortal = false );
void PlayEffect( const char *file, int clientNum, vec3_t origin, vec3_t forward, bool isPortal = false );
#endif // _IMMERSION
void StopEffect( const char *file, const int boltInfo, bool isPortal = false ); //find a scheduled Looping effect with these parms and kill it
void AddScheduledEffects( bool portal ); // call once per CGame frame [rww ammendment - twice now actually, but first only renders portal effects]
int NumScheduledFx() { return mFxSchedule.size(); }
void Clean(bool bRemoveTemplates = true, int idToPreserve = 0); // clean out the system
// FX Override functions
SEffectTemplate *GetEffectCopy( int fxHandle, int *newHandle );
SEffectTemplate *GetEffectCopy( const char *file, int *newHandle );
CPrimitiveTemplate *GetPrimitiveCopy( SEffectTemplate *effectCopy, const char *componentName );
};
//-------------------
// The one and only
//-------------------
extern CFxScheduler theFxScheduler;
#endif // FX_SCHEDULER_H_INC

215
code/cgame/FxSystem.cpp Normal file
View File

@@ -0,0 +1,215 @@
// this include must remain at the top of every FXxxxx.CPP file
#include "common_headers.h"
#if !defined(FX_SCHEDULER_H_INC)
#include "FxScheduler.h"
#endif
#include "cg_media.h" //for cgs.model_draw for G2
extern vmCvar_t fx_debug;
extern vmCvar_t fx_freeze;
extern void CG_ExplosionEffects( vec3_t origin, float intensity, int radius, int time );
// Stuff for the FxHelper
//------------------------------------------------------
void SFxHelper::Init()
{
mTime = 0;
}
//------------------------------------------------------
void SFxHelper::Print( const char *msg, ... )
{
#ifndef FINAL_BUILD
va_list argptr;
char text[1024];
va_start( argptr, msg );
vsprintf( text, msg, argptr );
va_end( argptr );
gi.Printf( text );
#endif
}
//------------------------------------------------------
void SFxHelper::AdjustTime( int frameTime )
{
if ( fx_freeze.integer || ( frameTime <= 0 ))
{
// Allow no time progression when we are paused.
mFrameTime = 0;
mFloatFrameTime = 0.0f;
}
else
{
if ( !cg_paused.integer )
{
if ( frameTime > 300 ) // hack for returning from paused and time bursts
{
frameTime = 300;
}
mFrameTime = frameTime;
mFloatFrameTime = mFrameTime * 0.001f;
mTime += mFrameTime;
}
}
}
//------------------------------------------------------
int SFxHelper::OpenFile( const char *file, fileHandle_t *fh, int mode )
{
// char path[256];
// sprintf( path, "%s/%s", FX_FILE_PATH, file );
return cgi_FS_FOpenFile( file, fh, FS_READ );
}
//------------------------------------------------------
int SFxHelper::ReadFile( void *data, int len, fileHandle_t fh )
{
return cgi_FS_Read( data, len, fh );
}
//------------------------------------------------------
void SFxHelper::CloseFile( fileHandle_t fh )
{
cgi_FS_FCloseFile( fh );
}
//------------------------------------------------------
void SFxHelper::PlaySound( const vec3_t org, int entityNum, int entchannel, int sfxHandle )
{
cgi_S_StartSound( org, entityNum, entchannel, sfxHandle );
}
//------------------------------------------------------
void SFxHelper::PlayLocalSound( int sfxHandle, int channelNum )
{
cgi_S_StartLocalSound(sfxHandle, channelNum);
}
//------------------------------------------------------
void SFxHelper::Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max,
vec3_t end, int skipEntNum, int flags )
{
CG_Trace( tr, start, min, max, end, skipEntNum, flags );
}
void SFxHelper::G2Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max,
vec3_t end, int skipEntNum, int flags )
{
//CG_Trace( tr, start, min, max, end, skipEntNum, flags, G2_COLLIDE );
gi.trace(tr, start, NULL, NULL, end, skipEntNum, flags, G2_COLLIDE);
}
//------------------------------------------------------
void SFxHelper::AddFxToScene( refEntity_t *ent )
{
cgi_R_AddRefEntityToScene( ent );
}
//------------------------------------------------------
int SFxHelper::RegisterShader( const char *shader )
{
return cgi_R_RegisterShader( shader );
}
//------------------------------------------------------
int SFxHelper::RegisterSound( const char *sound )
{
return cgi_S_RegisterSound( sound );
}
//------------------------------------------------------
int SFxHelper::RegisterModel( const char *model )
{
return cgi_R_RegisterModel( model );
}
//------------------------------------------------------
void SFxHelper::AddLightToScene( vec3_t org, float radius, float red, float green, float blue )
{
cgi_R_AddLightToScene( org, radius, red, green, blue );
}
//------------------------------------------------------
void SFxHelper::AddPolyToScene( int shader, int count, polyVert_t *verts )
{
cgi_R_AddPolyToScene( shader, count, verts );
}
//------------------------------------------------------
void SFxHelper::CameraShake( vec3_t origin, float intensity, int radius, int time )
{
CG_ExplosionEffects( origin, intensity, radius, time );
}
//------------------------------------------------------
int SFxHelper::GetOriginAxisFromBolt(const centity_t &cent, int modelNum, int boltNum, vec3_t /*out*/origin, vec3_t /*out*/axis[3])
{
if ((cg.time-cent.snapShotTime) > 200)
{ //you were added more than 200ms ago, so I say you are no longer valid/in our snapshot.
return 0;
}
int doesBoltExist;
mdxaBone_t boltMatrix;
vec3_t G2Angles = {cent.lerpAngles[0] , cent.lerpAngles[1], cent.lerpAngles[2]};
if ( cent.currentState.eType == ET_PLAYER )
{//players use cent.renderAngles
VectorCopy( cent.renderAngles, G2Angles );
if ( cent.gent //has a game entity
&& cent.gent->s.m_iVehicleNum != 0 //in a vehicle
&& cent.gent->m_pVehicle //have a valid vehicle pointer
&& cent.gent->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER //it's not a fighter
&& cent.gent->m_pVehicle->m_pVehicleInfo->type != VH_SPEEDER //not a speeder
)
{
G2Angles[PITCH]=0;
G2Angles[ROLL] =0;
}
}
// go away and get me the bolt position for this frame please
doesBoltExist = gi.G2API_GetBoltMatrix(cent.gent->ghoul2, modelNum,
boltNum, &boltMatrix, G2Angles,
cent.lerpOrigin, cg.time, cgs.model_draw,
cent.currentState.modelScale);
// set up the axis and origin we need for the actual effect spawning
origin[0] = boltMatrix.matrix[0][3];
origin[1] = boltMatrix.matrix[1][3];
origin[2] = boltMatrix.matrix[2][3];
axis[1][0] = boltMatrix.matrix[0][0];
axis[1][1] = boltMatrix.matrix[1][0];
axis[1][2] = boltMatrix.matrix[2][0];
axis[0][0] = boltMatrix.matrix[0][1];
axis[0][1] = boltMatrix.matrix[1][1];
axis[0][2] = boltMatrix.matrix[2][1];
axis[2][0] = boltMatrix.matrix[0][2];
axis[2][1] = boltMatrix.matrix[1][2];
axis[2][2] = boltMatrix.matrix[2][2];
return doesBoltExist;
}
#ifdef _IMMERSION
//------------------------------------------------------
ffHandle_t SFxHelper::RegisterForce( const char *force, int channel )
{
return cgi_FF_Register( force, channel );
}
//------------------------------------------------------
void SFxHelper::PlayForce( int entityNum, ffHandle_t ff )
{
cgi_FF_Start( ff, entityNum );
}
#endif // _IMMERSION

84
code/cgame/FxSystem.h Normal file
View File

@@ -0,0 +1,84 @@
#if !defined(CG_LOCAL_H_INC)
#include "cg_local.h"
#endif
#ifndef FX_SYSTEM_H_INC
#define FX_SYSTEM_H_INC
#define irand Q_irand
#define flrand Q_flrand
extern vmCvar_t fx_debug;
extern vmCvar_t fx_freeze;
inline void Vector2Clear(vec2_t a)
{
a[0] = 0.0f;
a[1] = 0.0f;
}
inline void Vector2Set(vec2_t a,float b,float c)
{
a[0] = b;
a[1] = c;
}
inline void Vector2Copy(vec2_t src,vec2_t dst)
{
dst[0] = src[0];
dst[1] = src[1];
}
extern void CG_CalcEntityLerpPositions( centity_t * );
struct SFxHelper
{
int mTime;
int mFrameTime;
float mFloatFrameTime;
void Init();
void AdjustTime( int time );
// These functions are wrapped and used by the fx system in case it makes things a bit more portable
void Print( const char *msg, ... );
// File handling
int OpenFile( const char *path, fileHandle_t *fh, int mode );
int ReadFile( void *data, int len, fileHandle_t fh );
void CloseFile( fileHandle_t fh );
// Sound
void PlaySound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx );
void PlayLocalSound( sfxHandle_t sfx, int channelNum );
int RegisterSound( const char *sound );
#ifdef _IMMERSION
void PlayForce( int entityNum, ffHandle_t ff );
ffHandle_t RegisterForce( const char *force, int channel );
#endif // _IMMERSION
//G2
int GetOriginAxisFromBolt(const centity_t &cent, int modelNum, int boltNum, vec3_t /*out*/origin, vec3_t /*out*/*axis);
// Physics/collision
void Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max, vec3_t end, int skipEntNum, int flags );
void G2Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max, vec3_t end, int skipEntNum, int flags );
void AddFxToScene( refEntity_t *ent );
void AddLightToScene( vec3_t org, float radius, float red, float green, float blue );
int RegisterShader( const char *shader );
int RegisterModel( const char *model );
void AddPolyToScene( int shader, int count, polyVert_t *verts );
void CameraShake( vec3_t origin, float intensity, int radius, int time );
};
extern SFxHelper theFxHelper;
#endif // FX_SYSTEM_H_INC

2370
code/cgame/FxTemplate.cpp Normal file

File diff suppressed because it is too large Load Diff

1400
code/cgame/FxUtil.cpp Normal file

File diff suppressed because it is too large Load Diff

130
code/cgame/FxUtil.h Normal file
View File

@@ -0,0 +1,130 @@
#if !defined(FX_PRIMITIVES_H_INC)
#include "FxPrimitives.h"
#endif
#ifndef FX_UTIL_H_INC
#define FX_UTIL_H_INC
bool FX_Free( void ); // ditches all active effects;
int FX_Init( void ); // called in CG_Init to purge the fx system.
void FX_Add( bool portal ); // called every cgame frame to add all fx into the scene.
void FX_Stop( void ); // ditches all active effects without touching the templates.
bool FX_ActiveFx(void); // returns whether there are any active or scheduled effects
CParticle *FX_AddParticle( int clientID, const vec3_t org, const vec3_t vel, const vec3_t accel, float gravity,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
const vec3_t rgb1, const vec3_t rgb2, float rgbParm,
float rotation, float rotationDelta,
const vec3_t min, const vec3_t max, float elasticity,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CLine *FX_AddLine( int clientID, vec3_t start, vec3_t end,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, qhandle_t shader, int impactFX_id, int flags, int modelNum = -1, int boltNum = -1 );
CElectricity *FX_AddElectricity( int clientID, vec3_t start, vec3_t end, float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t sRGB, vec3_t eRGB, float rgbParm,
float chaos, int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CTail *FX_AddTail( int clientID, vec3_t org, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float length1, float length2, float lengthParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t min, vec3_t max, float elasticity,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CCylinder *FX_AddCylinder( int clientID, vec3_t start, vec3_t normal,
float size1s, float size1e, float size1Parm,
float size2s, float size2e, float size2Parm,
float length1, float length2, float lengthParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CEmitter *FX_AddEmitter( vec3_t org, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t angs, vec3_t deltaAngs,
vec3_t min, vec3_t max, float elasticity,
int deathID, int impactID, int emitterID,
float density, float variance,
int killTime, qhandle_t model, int flags );
CLight *FX_AddLight( vec3_t org, float size1, float size2, float sizeParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, int flags );
COrientedParticle *FX_AddOrientedParticle( int clientID, vec3_t org, vec3_t norm, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
float rotation, float rotationDelta,
vec3_t min, vec3_t max, float bounce,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CPoly *FX_AddPoly( vec3_t *verts, vec2_t *st, int numVerts,
vec3_t vel, vec3_t accel,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t rotationDelta, float bounce, int motionDelay,
int killTime, qhandle_t shader, int flags );
CFlash *FX_AddFlash( vec3_t origin, vec3_t sRGB, vec3_t eRGB, float rgbParm,
int life, qhandle_t shader, int flags );
// Included for backwards compatibility with CHC and for doing quick programmatic effects.
void FX_AddSprite( vec3_t origin, vec3_t vel, vec3_t accel,
float scale, float dscale,
float sAlpha, float eAlpha,
float rotation, float bounce,
int life, qhandle_t shader, int flags = 0 );
void FX_AddSprite( vec3_t origin, vec3_t vel, vec3_t accel,
float scale, float dscale,
float sAlpha, float eAlpha,
vec3_t sRGB, vec3_t eRGB,
float rotation, float bounce,
int life, qhandle_t shader, int flags = 0 );
void FX_AddLine( vec3_t start, vec3_t end, float stScale,
float width, float dwidth,
float sAlpha, float eAlpha,
int life, qhandle_t shader, int flags = 0 );
void FX_AddLine( vec3_t start, vec3_t end, float stScale,
float width, float dwidth,
float sAlpha, float eAlpha,
vec3_t sRGB, vec3_t eRGB,
int life, qhandle_t shader, int flags = 0 );
void FX_AddQuad( vec3_t origin, vec3_t normal,
vec3_t vel, vec3_t accel,
float sradius, float eradius,
float salpha, float ealpha,
vec3_t sRGB, vec3_t eRGB,
float rotation, int life, qhandle_t shader, int flags = 0 );
CBezier *FX_AddBezier( const vec3_t start, const vec3_t end,
const vec3_t control1, const vec3_t control1Vel,
const vec3_t control2, const vec3_t control2Vel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
const vec3_t sRGB, const vec3_t eRGB, const float rgbParm,
int killTime, qhandle_t shader, int flags = 0 );
#endif //FX_UTIL_H_INC

1792
code/cgame/animtable.h Normal file

File diff suppressed because it is too large Load Diff

2003
code/cgame/cg_camera.cpp Normal file

File diff suppressed because it is too large Load Diff

165
code/cgame/cg_camera.h Normal file
View File

@@ -0,0 +1,165 @@
#ifndef __CG_CAMERA__
#define __CG_CAMERA__
//
// cg_camera.cpp
#define MAX_CAMERA_GROUP_SUBJECTS 16
#define MAX_ACCEL_PER_FRAME 10.0f
#define MAX_SHAKE_INTENSITY 16.0f
#define CAMERA_DEFAULT_FOV 90.0f
#define CAMERA_WIDESCREEN_FOV 120.0f
#define BAR_DURATION 1000.0f
#define BAR_RATIO 48.0f
#define CAMERA_MOVING 0x00000001
#define CAMERA_PANNING 0x00000002
#define CAMERA_ZOOMING 0x00000004
#define CAMERA_BAR_FADING 0x00000008
#define CAMERA_FADING 0x00000010
#define CAMERA_FOLLOWING 0x00000020
#define CAMERA_TRACKING 0x00000040
#define CAMERA_ROFFING 0x00000080
#define CAMERA_SMOOTHING 0x00000100
#define CAMERA_CUT 0x00000200
#define CAMERA_ACCEL 0x00000400
// NOTE!! This structure is now saved out as part of the load/save game, so tell me if you put any pointers or
// other goofy crap in... -Ste
//
typedef struct camera_s
{
//Position / Facing information
vec3_t origin;
vec3_t angles;
vec3_t origin2;
vec3_t angles2;
//Movement information
float move_duration;
float move_time;
int move_type; //CMOVE_LINEAR, CMOVE_BEZIER
//FOV information
float FOV;
float FOV2;
float FOV_duration;
float FOV_time;
float FOV_vel;
float FOV_acc;
//Pan information
float pan_time;
float pan_duration;
//Following information
char cameraGroup[MAX_QPATH];
float cameraGroupZOfs;
char cameraGroupTag[MAX_QPATH];
vec3_t subjectPos;
float subjectSpeed;
float followSpeed;
qboolean followInitLerp;
float distance;
qboolean distanceInitLerp;
//int aimEntNum;//FIXME: remove
//Tracking information
int trackEntNum;
vec3_t trackToOrg;
vec3_t moveDir;
float speed;
float initSpeed;
float trackInitLerp;
int nextTrackEntUpdateTime;
//Cine-bar information
float bar_alpha;
float bar_alpha_source;
float bar_alpha_dest;
float bar_time;
float bar_height_source;
float bar_height_dest;
float bar_height;
vec4_t fade_color;
vec4_t fade_source;
vec4_t fade_dest;
float fade_time;
float fade_duration;
//State information
int info_state;
//Shake information
float shake_intensity;
int shake_duration;
int shake_start;
//Smooth information
float smooth_intensity;
int smooth_duration;
int smooth_start;
vec3_t smooth_origin;
bool smooth_active; // means smooth_origin and angles are valid
// ROFF information
char sRoff[MAX_QPATH]; // name of a cached roff
int roff_frame; // current frame in the roff data
int next_roff_time; // time when it's ok to apply the next roff frame
#ifdef _XBOX
qboolean widescreen;
#endif
} camera_t;
extern bool in_camera;
extern camera_t client_camera;
void CGCam_Init( void );
void CGCam_Enable( void );
void CGCam_Disable( void );
#ifdef _XBOX
void CGCam_SetWidescreen( qboolean widescreen );
#endif
void CGCam_SetPosition( vec3_t org );
void CGCam_SetAngles( vec3_t ang );
void CGCam_SetFOV( float FOV );
#ifdef _XBOX
void CGCam_SetFOV2( float FOV2 );
#endif
void CGCam_Zoom( float FOV, float duration );
//void CGCam_Pan( vec3_t dest, float duration );
void CGCam_Pan( vec3_t dest, vec3_t panDirection, float duration );
void CGCam_Move( vec3_t dest, float duration );
void CGCam_Fade( vec4_t source, vec4_t dest, float duration );
void CGCam_UpdateFade( void );
void CGCam_Update( void );
void CGCam_RenderScene( void );
void CGCam_DrawWideScreen( void );
void CGCam_Shake( float intensity, int duration );
void CGCam_Shake( float intensity, int duration, bool rumble );
void CGCam_UpdateShake( vec3_t origin, vec3_t angles );
void CGCam_Follow( const char *cameraGroup, float speed, float initLerp );
void CGCam_Track( const char *trackName, float speed, float initLerp );
void CGCam_Distance( float distance, float initLerp );
void CGCam_Roll( float dest, float duration );
void CGCam_StartRoff( char *roff );
void CGCam_Smooth( float intensity, int duration );
void CGCam_UpdateSmooth( vec3_t origin, vec3_t angles );
#endif //__CG_CAMERA__

View File

@@ -0,0 +1,324 @@
// cg_consolecmds.c -- text commands typed in at the local console, or
// executed by a key binding
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h" //just for cgs....
void CG_TargetCommand_f( void );
extern qboolean player_locked;
extern void CMD_CGCam_Disable( void );
void CG_NextInventory_f( void );
void CG_PrevInventory_f( void );
void CG_NextForcePower_f( void );
void CG_PrevForcePower_f( void );
void CG_LoadHud_f( void );
/*
====================
CG_ColorFromString
====================
*/
/*
static void CG_SetColor_f( void) {
if (cgi_Argc()==4)
{
g_entities[0].client->renderInfo.customRGBA[0] = atoi( CG_Argv(1) );
g_entities[0].client->renderInfo.customRGBA[1] = atoi( CG_Argv(2) );
g_entities[0].client->renderInfo.customRGBA[2] = atoi( CG_Argv(3) );
}
if (cgi_Argc()==2)
{
int val = atoi( CG_Argv(1) );
if ( val < 1 || val > 7 ) {
g_entities[0].client->renderInfo.customRGBA[0] = 255;
g_entities[0].client->renderInfo.customRGBA[1] = 255;
g_entities[0].client->renderInfo.customRGBA[2] = 255;
return;
}
g_entities[0].client->renderInfo.customRGBA[0]=0;
g_entities[0].client->renderInfo.customRGBA[1]=0;
g_entities[0].client->renderInfo.customRGBA[2]=0;
if ( val & 1 ) {
g_entities[0].client->renderInfo.customRGBA[2] = 255;
}
if ( val & 2 ) {
g_entities[0].client->renderInfo.customRGBA[1] = 255;
}
if ( val & 4 ) {
g_entities[0].client->renderInfo.customRGBA[0] = 255;
}
}
}
*/
/*
=============
CG_Viewpos_f
Debugging command to print the current position
=============
*/
static void CG_Viewpos_f (void) {
CG_Printf ("%s (%i %i %i) : %i\n", cgs.mapname, (int)cg.refdef.vieworg[0],
(int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2],
(int)cg.refdefViewAngles[YAW]);
}
void CG_WriteCam_f (void)
{
char text[1024];
char *targetname;
static int numCams;
numCams++;
targetname = (char *)CG_Argv(1);
if( !targetname || !targetname[0] )
{
targetname = "nameme!";
}
CG_Printf( "Camera #%d ('%s') written to: ", numCams, targetname );
sprintf( text, "//entity %d\n{\n\"classname\" \"ref_tag\"\n\"targetname\" \"%s\"\n\"origin\" \"%i %i %i\"\n\"angles\" \"%i %i %i\"\n\"fov\" \"%i\"\n}\n", numCams, targetname, (int)cg.refdef.vieworg[0], (int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2], (int)cg.refdefViewAngles[0], (int)cg.refdefViewAngles[1], (int)cg.refdefViewAngles[2], cg_fov.integer );
gi.WriteCam( text );
}
void Lock_Disable ( void )
{
player_locked = qfalse;
}
extern float cg_zoomFov; //from cg_view.cpp
void CG_ToggleBinoculars( void )
{
if ( in_camera || !cg.snap )
{
return;
}
if ( cg.zoomMode == 0 || cg.zoomMode >= 2 ) // not zoomed or currently zoomed with the disruptor or LA goggles
{
if ( (cg.snap->ps.saber[0].Active() && cg.snap->ps.saberInFlight) || cg.snap->ps.stats[STAT_HEALTH] <= 0)
{//can't select binoculars when throwing saber
//FIXME: indicate this to the player
return;
}
if ( cg.snap->ps.viewEntity || ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & ( EF_LOCKED_TO_WEAPON | EF_IN_ATST )))
{
// can't zoom when you have a viewEntity or driving an atst or in an emplaced gun
return;
}
cg.zoomMode = 1;
cg.zoomLocked = qfalse;
if ( cg.snap->ps.batteryCharge )
{
// when you have batteries, you can actually zoom in
cg_zoomFov = 40.0f;
}
else if ( cg.overrides.active & CG_OVERRIDE_FOV )
{
cg_zoomFov = cg.overrides.fov;
}
else
{
cg_zoomFov = cg_fov.value;
}
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomStart );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomStartForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
else
{
cg.zoomMode = 0;
cg.zoomTime = cg.time;
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomEndForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
}
void CG_ToggleLAGoggles( void )
{
if ( in_camera || !cg.snap)
{
return;
}
if ( cg.zoomMode == 0 || cg.zoomMode < 3 ) // not zoomed or currently zoomed with the disruptor or regular binoculars
{
if ( (cg.snap->ps.saber[0].Active() && cg.snap->ps.saberInFlight) || cg.snap->ps.stats[STAT_HEALTH] <= 0 )
{//can't select binoculars when throwing saber
//FIXME: indicate this to the player
return;
}
if ( cg.snap->ps.viewEntity || ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & ( EF_LOCKED_TO_WEAPON | EF_IN_ATST )))
{
// can't zoom when you have a viewEntity or driving an atst or in an emplaced gun
return;
}
cg.zoomMode = 3;
cg.zoomLocked = qfalse;
if ( cg.overrides.active & CG_OVERRIDE_FOV )
{
cg_zoomFov = cg.overrides.fov;
}
else
{
cg_zoomFov = cg_fov.value; // does not zoom!!
}
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomStart );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomStartForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
else
{
cg.zoomMode = 0;
cg.zoomTime = cg.time;
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomEndForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
}
static void CG_InfoDown_f( void ) {
// cg.showInformation = qtrue;
}
static void CG_InfoUp_f( void )
{
// cg.showInformation = qfalse;
}
typedef struct {
char *cmd;
void (*function)(void);
} consoleCommand_t;
static consoleCommand_t commands[] = {
{ "testmodel", CG_TestModel_f },
{ "nextframe", CG_TestModelNextFrame_f },
{ "prevframe", CG_TestModelPrevFrame_f },
{ "nextskin", CG_TestModelNextSkin_f },
{ "prevskin", CG_TestModelPrevSkin_f },
/*
Ghoul2 Insert Start
*/
{ "testG2Model", CG_TestG2Model_f},
{ "testsurface", CG_TestModelSurfaceOnOff_f },
{ "testanglespre", CG_TestModelSetAnglespre_f},
{ "testanglespost", CG_TestModelSetAnglespost_f},
{ "testanimate", CG_TestModelAnimate_f},
{ "testlistbones", CG_ListModelBones_f},
{ "testlistsurfaces", CG_ListModelSurfaces_f},
/*
Ghoul2 Insert End
*/
{ "viewpos", CG_Viewpos_f },
{ "writecam", CG_WriteCam_f },
{ "+info", CG_InfoDown_f },
{ "-info", CG_InfoUp_f },
{ "weapnext", CG_NextWeapon_f },
{ "weapprev", CG_PrevWeapon_f },
{ "weapon", CG_Weapon_f },
{ "tcmd", CG_TargetCommand_f },
{ "cam_disable", CMD_CGCam_Disable }, //gets out of camera mode for debuggin
{ "cam_enable", CGCam_Enable }, //gets into camera mode for precise camera placement
{ "lock_disable", Lock_Disable }, //player can move now
{ "zoom", CG_ToggleBinoculars },
{ "la_zoom", CG_ToggleLAGoggles },
{ "invnext", CG_NextInventory_f },
{ "invprev", CG_PrevInventory_f },
{ "forcenext", CG_NextForcePower_f },
{ "forceprev", CG_PrevForcePower_f },
{ "loadhud", CG_LoadHud_f },
{ "dpweapnext", CG_DPNextWeapon_f },
{ "dpweapprev", CG_DPPrevWeapon_f },
{ "dpinvnext", CG_DPNextInventory_f },
{ "dpinvprev", CG_DPPrevInventory_f },
{ "dpforcenext", CG_DPNextForcePower_f },
{ "dpforceprev", CG_DPPrevForcePower_f },
// { "color", CG_SetColor_f },
};
//extern menuDef_t *menuScoreboard;
void Menu_Reset();
void CG_LoadHud_f( void)
{
const char *hudSet;
// cgi_UI_String_Init();
// cgi_UI_Menu_Reset();
hudSet = cg_hudFiles.string;
if (hudSet[0] == '\0')
{
hudSet = "ui/jahud.txt";
}
CG_LoadMenus(hudSet);
// menuScoreboard = NULL;
}
/*
=================
CG_ConsoleCommand
The string has been tokenized and can be retrieved with
Cmd_Argc() / Cmd_Argv()
=================
*/
qboolean CG_ConsoleCommand( void ) {
const char *cmd;
int i;
cmd = CG_Argv(0);
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
if ( !Q_stricmp( cmd, commands[i].cmd ) ) {
commands[i].function();
return qtrue;
}
}
return qfalse;
}
/*
=================
CG_InitConsoleCommands
Let the client system know about all of our commands
so it can perform tab completion
=================
*/
void CG_InitConsoleCommands( void ) {
int i;
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
cgi_AddCommand( commands[i].cmd );
}
}

654
code/cgame/cg_credits.cpp Normal file
View File

@@ -0,0 +1,654 @@
// Filename:- cg_credits.cpp
//
// module for end credits code
// this line must stay at top so the whole PCH thing works...
//
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define fCARD_FADESECONDS 1.0f // fade up time, also fade down time
#define fCARD_SUSTAINSECONDS 2.0f // hold time before fade down
#define fLINE_SECONDTOSCROLLUP 15.0f // how long one line takes to scroll up the screen
#define MAX_LINE_BYTES 2048
qhandle_t ghFontHandle = 0;
float gfFontScale = 1.00f;
vec4_t gv4Color = {0};
struct StringAndSize_t
{
int iStrLenPixels;
string str;
StringAndSize_t()
{
iStrLenPixels = -1;
str = "";
}
StringAndSize_t(const char *psString)
{
iStrLenPixels = -1;
str = psString;
}
StringAndSize_t & operator = (const char *psString)
{
iStrLenPixels = -1;
str = psString;
return *this;
}
const char *c_str(void)
{
return str.c_str();
}
int GetPixelLength(void)
{
if (iStrLenPixels == -1)
{
iStrLenPixels = cgi_R_Font_StrLenPixels(str.c_str(), ghFontHandle, gfFontScale);
}
return iStrLenPixels;
}
bool IsEmpty(void)
{
return str.empty();
}
};
struct CreditCard_t
{
int iTime;
StringAndSize_t strTitle;
vector<StringAndSize_t> vstrText;
CreditCard_t()
{
iTime = -1; // flag "not set yet"
}
};
struct CreditLine_t
{
int iLine;
StringAndSize_t strText;
vector<StringAndSize_t> vstrText;
bool bDotted;
};
typedef list <CreditLine_t> CreditLines_t;
typedef list <CreditCard_t> CreditCards_t;
struct CreditData_t
{
int iStartTime;
CreditCards_t CreditCards;
CreditLines_t CreditLines;
bool Running(void)
{
return !!( CreditCards.size() || CreditLines.size() );
}
};
CreditData_t CreditData;
static LPCSTR Capitalize(LPCSTR psTest)
{
static char sTemp[MAX_LINE_BYTES];
Q_strncpyz(sTemp, psTest, sizeof(sTemp));
// if (!cgi_Language_IsAsian()) // we don't have asian credits, so this is ok to do now
{
strupr(sTemp); // capitalise titles (if not asian!!!!)
}
return sTemp;
}
// cope with hyphenated names and initials (awkward gits)...
//
static bool CountsAsWhiteSpaceForCaps( unsigned /* avoid euro-char sign-extend assert within isspace()*/char c )
{
return !!(isspace(c) || c == '-' || c == '.' || c == '(' || c == ')' || c=='\'');
}
static LPCSTR UpperCaseFirstLettersOnly(LPCSTR psTest)
{
static char sTemp[MAX_LINE_BYTES];
Q_strncpyz(sTemp, psTest, sizeof(sTemp));
// if (!cgi_Language_IsAsian()) // we don't have asian credits, so this is ok to do now
{
strlwr(sTemp);
char *p = sTemp;
while (*p)
{
while (*p && CountsAsWhiteSpaceForCaps(*p)) p++;
if (*p)
{
*p = toupper(*p);
while (*p && !CountsAsWhiteSpaceForCaps(*p)) p++;
}
}
}
// now restore any weird stuff...
//
char *p = strstr(sTemp," Mc"); // eg "Mcfarrell" should be "McFarrell"
if (p && isalpha(p[3]))
{
p[3] = toupper(p[3]);
}
p = strstr(sTemp," O'"); // eg "O'flaherty" should be "O'Flaherty" (this is probably done automatically now, but wtf.
if (p && isalpha(p[3]))
{
p[3] = toupper(p[3]);
}
p = strstr(sTemp,"Lucasarts");
if (p)
{
p[5] = 'A'; // capitalise the 'A' in LucasArts (jeez...)
}
return sTemp;
}
static const char *GetSubString(string &strResult)
{
static char sTemp[MAX_LINE_BYTES];
if (!strlen(strResult.c_str()))
return NULL;
strncpy(sTemp,strResult.c_str(),sizeof(sTemp)-1);
sTemp[sizeof(sTemp)-1]='\0';
char *psSemiColon = strchr(sTemp,';');
if ( psSemiColon)
{
*psSemiColon = '\0';
strResult.erase(0,(psSemiColon-sTemp)+1);
}
else
{
// no semicolon found, probably last entry? (though i think even those have them on, oh well)
//
strResult.erase();
}
return sTemp;
}
// sort entries by their last name (starts at back of string and moves forward until start or just before whitespace)
// ...
static int SortBySurname(const void *elem1, const void *elem2)
{
StringAndSize_t *p1 = (StringAndSize_t *) elem1;
StringAndSize_t *p2 = (StringAndSize_t *) elem2;
LPCSTR psSurName1 = p1->c_str() + (strlen(p1->c_str())-1);
LPCSTR psSurName2 = p2->c_str() + (strlen(p2->c_str())-1);
while (psSurName1 > p1->c_str() && !isspace(*psSurName1)) psSurName1--;
while (psSurName2 > p2->c_str() && !isspace(*psSurName2)) psSurName2--;
if (isspace(*psSurName1)) psSurName1++;
if (isspace(*psSurName2)) psSurName2++;
return stricmp(psSurName1, psSurName2);
}
void CG_Credits_Init( LPCSTR psStripReference, vec4_t *pv4Color)
{
// Play the light side end credits music.
if ( g_entities[0].client->sess.mission_objectives[0].status != 2 )
{
cgi_S_StartBackgroundTrack( "music/endcredits.mp3", NULL, false );
}
// Play the dark side end credits music.
else
{
cgi_S_StartBackgroundTrack( "music/vjun3/vjun3_explore.mp3", NULL, false );
}
// could make these into parameters later, but for now...
//
ghFontHandle = cgs.media.qhFontMedium;
gfFontScale = 1.0f;
memcpy(gv4Color,pv4Color,sizeof(gv4Color)); // memcpy so we can poke into alpha channel
// first, ask the strlen of the final string...
//
int iStrLen = cgi_SP_GetStringTextString( psStripReference, NULL, 0 );
if (!iStrLen)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_Credits_Init(): invalid text key :'%s'\n", psStripReference);
#endif
return;
}
//
// malloc space to hold it...
//
char *psMallocText = (char *) cgi_Z_Malloc( iStrLen+1, TAG_TEMP_WORKSPACE );
//
// now get the string...
//
iStrLen = cgi_SP_GetStringTextString( psStripReference, psMallocText, iStrLen+1 );
//ensure we found a match
if (!iStrLen)
{
assert(0); // should never get here now, but wtf?
cgi_Z_Free(psMallocText);
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_Credits_Init(): invalid text key :'%s'\n", psStripReference);
#endif
return;
}
// read whole string in and process as cards, lines etc...
//
typedef enum
{
eNothing = 0,
eLine,
eDotEntry,
eTitle,
eCard,
eFinished,
} Mode_e;
Mode_e eMode = eNothing;
qboolean bCardsFinished = qfalse;
int iLineNumber = 0;
const char *psTextParse = psMallocText;
while (*psTextParse != NULL)
{
// read a line...
//
char sLine[MAX_LINE_BYTES];
sLine[0]='\0';
qboolean bWasCommand = qtrue;
while (1)
{
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psTextParse, &iAdvanceCount, &bIsTrailingPunctuation);
psTextParse += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && sLine[0] == '\0')
{
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter == '\n' || uiLetter == '\0' )
{
// have we got a command word?...
//
if (!strnicmp(sLine,"(#",2))
{
// yep...
//
if (!stricmp(sLine, "(#CARD)"))
{
if (!bCardsFinished)
{
eMode = eCard;
}
else
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "CG_Credits_Init(): No current support for cards after scroll!\n" );
#endif
eMode = eNothing;
}
break;
}
else
if (!stricmp(sLine, "(#TITLE)"))
{
eMode = eTitle;
bCardsFinished = qtrue;
break;
}
else
if (!stricmp(sLine, "(#LINE)"))
{
eMode = eLine;
bCardsFinished = qtrue;
break;
}
else
if (!stricmp(sLine, "(#DOTENTRY)"))
{
eMode = eDotEntry;
bCardsFinished = qtrue;
break;
}
else
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "CG_Credits_Init(): bad keyword \"%s\"!\n", sLine );
#endif
eMode = eNothing;
}
}
else
{
// I guess not...
//
bWasCommand = qfalse;
break;
}
}
else
{
// must be a letter...
//
if (uiLetter > 255)
{
assert(0); // this means we're attempting to display asian credits, and we don't
// support these now because the auto-capitalisation rules etc would have to
// be inhibited.
Q_strcat(sLine, sizeof(sLine), va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(sLine, sizeof(sLine), va("%c",uiLetter & 0xFF));
}
}
}
// command?...
//
if (bWasCommand)
{
// this'll just be a mode change, so ignore...
//
}
else
{
// else we've got some text to display...
//
switch (eMode)
{
case eNothing: break;
case eLine:
{
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber++;
CreditLine.strText = sLine;
CreditData.CreditLines.push_back( CreditLine );
}
break;
case eDotEntry:
{
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber;
CreditLine.bDotted = true;
string strResult(sLine);
const char *p;
while ((p=GetSubString(strResult)) != NULL)
{
if (CreditLine.strText.IsEmpty())
{
CreditLine.strText = p;
}
else
{
CreditLine.vstrText.push_back( UpperCaseFirstLettersOnly(p) );
}
}
if (!CreditLine.strText.IsEmpty() && CreditLine.vstrText.size())
{
// sort entries RHS dotted entries by alpha...
//
qsort(&CreditLine.vstrText[0], CreditLine.vstrText.size(), sizeof(CreditLine.vstrText[0]), SortBySurname);
CreditData.CreditLines.push_back( CreditLine );
iLineNumber += CreditLine.vstrText.size();
}
}
break;
case eTitle:
{
iLineNumber++; // leading blank line
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber++;
CreditLine.strText = Capitalize(sLine);
CreditData.CreditLines.push_back( CreditLine );
iLineNumber++; // trailing blank line
break;
}
case eCard:
{
CreditCard_t CreditCard;
string strResult(sLine);
const char *p;
while ((p=GetSubString(strResult)) != NULL)
{
if (CreditCard.strTitle.IsEmpty())
{
CreditCard.strTitle = Capitalize( p );
}
else
{
CreditCard.vstrText.push_back( UpperCaseFirstLettersOnly( p ) );
}
}
if (!CreditCard.strTitle.IsEmpty())
{
// sort entries by alpha...
//
qsort(&CreditCard.vstrText[0], CreditCard.vstrText.size(), sizeof(CreditCard.vstrText[0]), SortBySurname);
CreditData.CreditCards.push_back(CreditCard);
}
}
break;
}
}
}
cgi_Z_Free(psMallocText);
CreditData.iStartTime = cg.time;
}
qboolean CG_Credits_Running( void )
{
return CreditData.Running();
}
// returns qtrue if still drawing...
//
qboolean CG_Credits_Draw( void )
{
if ( CG_Credits_Running() )
{
const int iFontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(ghFontHandle, gfFontScale)); // taiwanese & japanese need 1.5 fontheight spacing
// cgi_R_SetColor( *gpv4Color );
// display cards first...
//
if (CreditData.CreditCards.size())
{
// grab first card off the list (we know there's at least one here, so...)
//
CreditCard_t &CreditCard = (*CreditData.CreditCards.begin());
if (CreditCard.iTime == -1)
{
// onceonly time init...
//
CreditCard.iTime = cg.time;
}
// play with the alpha channel for fade up/down...
//
const float fMilliSecondsElapsed = cg.time - CreditCard.iTime;
const float fSecondsElapsed = fMilliSecondsElapsed / 1000.0f;
if (fSecondsElapsed < fCARD_FADESECONDS)
{
// fading up...
//
gv4Color[3] = fSecondsElapsed / fCARD_FADESECONDS;
// OutputDebugString(va("fade up: %f\n",gv4Color[3]));
}
else
if (fSecondsElapsed > fCARD_FADESECONDS + fCARD_SUSTAINSECONDS)
{
// fading down...
//
const float fFadeDownSeconds = fSecondsElapsed - (fCARD_FADESECONDS + fCARD_SUSTAINSECONDS);
gv4Color[3] = 1.0f - (fFadeDownSeconds / fCARD_FADESECONDS);
// OutputDebugString(va("fade dw: %f\n",gv4Color[3]));
}
else
{
gv4Color[3] = 1.0f;
// OutputDebugString(va("normal: %f\n",gv4Color[3]));
}
if (gv4Color[3] < 0.0f)
gv4Color[3] = 0.0f; // ... otherwise numbers that have dipped slightly -ve flash up fullbright after fade down
//
// how many lines is it?
//
int iLines = CreditCard.vstrText.size() + 2; // +2 for title itself & one seperator line
//
int iYpos = (SCREEN_HEIGHT - (iLines * iFontHeight))/2;
//
// draw it, title first...
//
int iWidth = CreditCard.strTitle.GetPixelLength();
int iXpos = (SCREEN_WIDTH - iWidth)/2;
cgi_R_Font_DrawString(iXpos, iYpos, CreditCard.strTitle.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
//
iYpos += iFontHeight*2; // skip blank line then move to main pos
//
for (int i=0; i<CreditCard.vstrText.size(); i++)
{
StringAndSize_t &StringAndSize = CreditCard.vstrText[i];
iWidth = StringAndSize.GetPixelLength();
iXpos = (SCREEN_WIDTH - iWidth)/2;
cgi_R_Font_DrawString(iXpos, iYpos, StringAndSize.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
iYpos += iFontHeight;
}
// next card?...
//
if (fSecondsElapsed > fCARD_FADESECONDS + fCARD_SUSTAINSECONDS + fCARD_FADESECONDS)
{
// yep, so erase the first entry (which will trigger the next one to be initialised on re-entry)...
//
CreditData.CreditCards.erase( CreditData.CreditCards.begin() );
if (!CreditData.CreditCards.size())
{
// all cards gone, so re-init timer for lines...
//
CreditData.iStartTime = cg.time;
}
}
//
return qtrue;
}
else
{
// doing scroll text...
//
if (CreditData.CreditLines.size())
{
// process all lines...
//
const float fMilliSecondsElapsed = cg.time - CreditData.iStartTime;
const float fSecondsElapsed = fMilliSecondsElapsed / 1000.0f;
bool bEraseOccured = false;
for (CreditLines_t::iterator it = CreditData.CreditLines.begin(); it != CreditData.CreditLines.end(); bEraseOccured ? it : ++it)
{
CreditLine_t &CreditLine = (*it);
bEraseOccured = false;
static const float fPixelsPerSecond = ((float)SCREEN_HEIGHT / fLINE_SECONDTOSCROLLUP);
int iYpos = SCREEN_HEIGHT + (CreditLine.iLine * iFontHeight);
iYpos-= (int) (fPixelsPerSecond * fSecondsElapsed);
int iTextLinesThisItem = max(CreditLine.vstrText.size(),1);
if (iYpos + (iTextLinesThisItem * iFontHeight) < 0)
{
// scrolled off top of screen, so erase it...
//
it = CreditData.CreditLines.erase( it );
bEraseOccured = true;
}
else
if (iYpos < SCREEN_HEIGHT)
{
// onscreen, so print it...
//
bool bIsDotted = !!CreditLine.vstrText.size(); // eg "STUNTS ...................... MR ED"
int iWidth = CreditLine.strText.GetPixelLength();
int iXpos = bIsDotted ? 54 : (SCREEN_WIDTH - iWidth)/2;
gv4Color[3] = 1.0f;
cgi_R_Font_DrawString(iXpos, iYpos, CreditLine.strText.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
// now print any dotted members...
//
for (int i=0; i<CreditLine.vstrText.size(); i++)
{
StringAndSize_t &StringAndSize = CreditLine.vstrText[i];
iWidth = StringAndSize.GetPixelLength();
iXpos = (SCREEN_WIDTH-4 - iWidth) - 50;
cgi_R_Font_DrawString(iXpos, iYpos, StringAndSize.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
iYpos += iFontHeight;
}
}
}
return qtrue;
}
}
}
return qfalse;
}
////////////////////// eof /////////////////////

4418
code/cgame/cg_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

491
code/cgame/cg_drawtools.cpp Normal file
View File

@@ -0,0 +1,491 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
================
CG_DrawSides
Coords are virtual 640x480
================
*/
void CG_DrawSides(float x, float y, float w, float h, float size) {
//size *= cgs.screenXScale;
cgi_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
cgi_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
}
void CG_DrawTopBottom(float x, float y, float w, float h, float size) {
//size *= cgs.screenYScale;
cgi_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
cgi_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
}
/*
================
CG_DrawRect
Coordinates are 640*480 virtual values
=================
*/
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
cgi_R_SetColor( color );
CG_DrawTopBottom(x, y, width, height, size);
CG_DrawSides(x, y, width, height, size);
cgi_R_SetColor( NULL );
}
/*
================
CG_FillRect
Coordinates are 640*480 virtual values
=================
*/
void CG_FillRect( float x, float y, float width, float height, const float *color ) {
cgi_R_SetColor( color );
cgi_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader);
cgi_R_SetColor( NULL );
}
/*
================
CG_Scissor
Coordinates are 640*480 virtual values
=================
*/
void CG_Scissor( float x, float y, float width, float height)
{
cgi_R_Scissor( x, y, width, height);
}
/*
================
CG_DrawPic
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
=================
*/
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
cgi_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
/*
================
CG_DrawPic2
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
Can also specify the exact texture coordinates
=================
*/
void CG_DrawPic2( float x, float y, float width, float height, float s1, float t1, float s2, float t2, qhandle_t hShader )
{
cgi_R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, hShader );
}
/*
================
CG_DrawRotatePic
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
rotates around the upper right corner of the passed in point
=================
*/
void CG_DrawRotatePic( float x, float y, float width, float height,float angle, qhandle_t hShader ) {
cgi_R_DrawRotatePic( x, y, width, height, 0, 0, 1, 1, angle, hShader );
}
/*
================
CG_DrawRotatePic2
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
Actually rotates around the center point of the passed in coordinates
=================
*/
void CG_DrawRotatePic2( float x, float y, float width, float height,float angle, qhandle_t hShader ) {
cgi_R_DrawRotatePic2( x, y, width, height, 0, 0, 1, 1, angle, hShader );
}
/*
===============
CG_DrawChar
Coordinates and size in 640*480 virtual screen size
===============
*/
void CG_DrawChar( int x, int y, int width, int height, int ch ) {
int row, col;
float frow, fcol;
float size;
float ax, ay, aw, ah;
ch &= 255;
if ( ch == ' ' ) {
return;
}
ax = x;
ay = y;
aw = width;
ah = height;
row = ch>>4;
col = ch&15;
/*
frow = row*0.0625;
fcol = col*0.0625;
size = 0.0625;
cgi_R_DrawStretchPic( ax, ay, aw, ah,
fcol, frow,
fcol + size, frow + size,
cgs.media.charsetShader );
*/
float size2;
frow = row*0.0625;
fcol = col*0.0625;
size = 0.03125;
size2 = 0.0625;
cgi_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size2,
cgs.media.charsetShader );
}
/*
==================
CG_DrawStringExt
Draws a multi-colored string with a drop shadow, optionally forcing
to a fixed color.
Coordinates are at 640 by 480 virtual resolution
==================
*/
void CG_DrawStringExt( int x, int y, const char *string, const float *setColor,
qboolean forceColor, qboolean shadow, int charWidth, int charHeight ) {
vec4_t color;
const char *s;
int xx;
// draw the drop shadow
if (shadow) {
color[0] = color[1] = color[2] = 0;
color[3] = setColor[3];
cgi_R_SetColor( color );
s = string;
xx = x;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
continue;
}
CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s );
xx += charWidth;
s++;
}
}
// draw the colored text
s = string;
xx = x;
cgi_R_SetColor( setColor );
while ( *s ) {
if ( Q_IsColorString( s ) ) {
if ( !forceColor ) {
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
color[3] = setColor[3];
cgi_R_SetColor( color );
}
s += 2;
continue;
}
CG_DrawChar( xx, y, charWidth, charHeight, *s );
xx += charWidth;
s++;
}
cgi_R_SetColor( NULL );
}
void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) {
CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT );
}
/*
=================
CG_DrawStrlen
Returns character count, skiping color escape codes
=================
*/
int CG_DrawStrlen( const char *str ) {
const char *s = str;
int count = 0;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
} else {
count++;
s++;
}
}
return count;
}
/*
=============
CG_TileClearBox
This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) {
float s1, t1, s2, t2;
s1 = x/64.0;
t1 = y/64.0;
s2 = (x+w)/64.0;
t2 = (y+h)/64.0;
cgi_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader );
}
/*
==============
CG_TileClear
Clear around a sized down screen
==============
*/
void CG_TileClear( void ) {
int top, bottom, left, right;
int w, h;
w = cgs.glconfig.vidWidth;
h = cgs.glconfig.vidHeight;
if ( cg.refdef.x == 0 && cg.refdef.y == 0 &&
cg.refdef.width == w && cg.refdef.height == h ) {
return; // full screen rendering
}
top = cg.refdef.y;
bottom = top + cg.refdef.height-1;
left = cg.refdef.x;
right = left + cg.refdef.width-1;
// clear above view screen
CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader );
// clear below view screen
CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader );
// clear left of view screen
CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader );
// clear right of view screen
CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader );
}
/*
================
CG_FadeColor
================
*/
float *CG_FadeColor( int startMsec, int totalMsec ) {
static vec4_t color;
int t;
if ( startMsec == 0 ) {
return NULL;
}
t = cg.time - startMsec;
if ( t >= totalMsec ) {
return NULL;
}
// fade out
if ( totalMsec - t < FADE_TIME ) {
color[3] = ( totalMsec - t ) * 1.0/FADE_TIME;
} else {
color[3] = 1.0;
}
color[0] = color[1] = color[2] = 1;
return color;
}
/*
==============
CG_DrawNumField
Take x,y positions as if 640 x 480 and scales them to the proper resolution
==============
*/
void CG_DrawNumField (int x, int y, int width, int value,int charWidth,int charHeight,int style,qboolean zeroFill)
{
char num[16], *ptr;
int l;
int frame;
int xWidth;
if (width < 1) {
return;
}
// draw number string
if (width > 5) {
width = 5;
}
switch ( width ) {
case 1:
value = value > 9 ? 9 : value;
value = value < 0 ? 0 : value;
break;
case 2:
value = value > 99 ? 99 : value;
value = value < -9 ? -9 : value;
break;
case 3:
value = value > 999 ? 999 : value;
value = value < -99 ? -99 : value;
break;
case 4:
value = value > 9999 ? 9999 : value;
value = value < -999 ? -999 : value;
break;
}
Com_sprintf (num, sizeof(num), "%i", value);
l = strlen(num);
if (l > width)
l = width;
// FIXME: Might need to do something different for the chunky font??
switch(style)
{
default:
case NUM_FONT_SMALL:
xWidth = charWidth;
break;
/*
case NUM_FONT_CHUNKY:
xWidth = (charWidth/1.2f) + 2;
break;
case NUM_FONT_BIG:
xWidth = (charWidth/2) + 7;//(charWidth/6);
break;
*/
}
#ifndef _XBOX
if ( zeroFill )
{
for (int i = 0; i < (width - l); i++ )
{
switch(style)
{
default:
case NUM_FONT_SMALL:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.smallnumberShaders[0] );
break;
/*
case NUM_FONT_CHUNKY:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.chunkyNumberShaders[0] );
break;
case NUM_FONT_BIG:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[0] );
break;
*/
}
x += 2 + (xWidth);
}
}
else
#endif
{
x += 2 + (xWidth)*(width - l);
}
ptr = num;
while (*ptr && l)
{
if (*ptr == '-')
frame = STAT_MINUS;
else
frame = *ptr -'0';
switch(style)
{
default:
case NUM_FONT_SMALL:
if(zeroFill)
{
CG_DrawPic( x-2,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x+2,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x,y-2, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x,y+2, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
x++; // For a one line gap
}
else {
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
x++; // For a one line gap
}
break;
/*
case NUM_FONT_CHUNKY:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.chunkyNumberShaders[frame] );
break;
case NUM_FONT_BIG:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[frame] );
break;
*/
}
x += (xWidth);
ptr++;
l--;
}
}
/*
=================
CG_DrawProportionalString
=================
*/
void CG_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color )
{
//assert(!style);//call this directly if you need style (OR it into the font handle)
cgi_R_Font_DrawString (x, y, str, color, cgs.media.qhFontMedium, -1, 1.0f);
}

1087
code/cgame/cg_effects.cpp Normal file

File diff suppressed because it is too large Load Diff

2673
code/cgame/cg_ents.cpp Normal file

File diff suppressed because it is too large Load Diff

1281
code/cgame/cg_event.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"

20
code/cgame/cg_headers.h Normal file
View File

@@ -0,0 +1,20 @@
// Precompiled header file for the client game
#include "cg_local.h"
// No PCH at all on Xbox build, we just include everything. Does this slow
// down builds? Somewhat. But then again, if I do change headers, I have to
// tolerate VS.net's piss poor dependency system that requires my to manually
// delete the .pch for the PC version to work at all. So, I'll live.
#ifdef _XBOX
#include "../game/g_local.h"
#include "../game/g_functions.h"
#include "../game/b_local.h"
#endif
//#include "CGEntity.h"
//#include "../game/SpawnSystem.h"
//#include "../game/EntitySystem.h"
//#include "../game/CScheduleSystem.h"
// end

648
code/cgame/cg_info.cpp Normal file
View File

@@ -0,0 +1,648 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\objectives.h"
// For printing objectives
#ifdef _XBOX
static const short objectiveStartingYpos = 100; // Y starting position for objective text
static const short objectiveStartingXpos = 130; // X starting position for objective text
static const int objectiveTextBoxWidth = 400; // Width (in pixels) of text box
static const int objectiveTextBoxHeight = 310; // Height (in pixels) of text box
#else
static const short objectiveStartingYpos = 75; // Y starting position for objective text
static const short objectiveStartingXpos = 60; // X starting position for objective text
static const int objectiveTextBoxWidth = 500; // Width (in pixels) of text box
static const int objectiveTextBoxHeight = 300; // Height (in pixels) of text box
#endif // _XBOX
const char *showLoadPowersName[] =
{
"SP_INGAME_HEAL2",
"SP_INGAME_JUMP2",
"SP_INGAME_SPEED2",
"SP_INGAME_PUSH2",
"SP_INGAME_PULL2",
"SP_INGAME_MINDTRICK2",
"SP_INGAME_GRIP2",
"SP_INGAME_LIGHTNING2",
"SP_INGAME_SABER_THROW2",
"SP_INGAME_SABER_OFFENSE2",
"SP_INGAME_SABER_DEFENSE2",
NULL,
};
#define MAX_OBJ_GRAPHICS 4
#define OBJ_GRAPHIC_SIZE 240
int obj_graphics[MAX_OBJ_GRAPHICS];
qboolean CG_ForcePower_Valid(int forceKnownBits, int index);
/*
====================
ObjectivePrint_Line
Print a single mission objective
====================
*/
static void ObjectivePrint_Line(const int color, const int objectIndex, int &missionYcnt)
{
char *str,*strBegin;
int y,pixelLen,charLen,i;
const int maxHoldText = 1024;
char holdText[maxHoldText];
char finalText[2048];
qhandle_t graphic;
int iYPixelsPerLine = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
cgi_SP_GetStringTextString( va("OBJECTIVES_%s",objectiveTable[objectIndex].name) , finalText, sizeof(finalText) );
// A hack to be able to count prisoners
if (objectIndex==T2_RANCOR_OBJ5)
{
char value[64];
int currTotal, minTotal;
gi.Cvar_VariableStringBuffer("ui_prisonerobj_currtotal",value,sizeof(value));
currTotal = atoi(value);
gi.Cvar_VariableStringBuffer("ui_prisonerobj_maxtotal",value,sizeof(value));
minTotal = atoi(value);
sprintf(finalText,va(finalText,currTotal,minTotal));
}
pixelLen = cgi_R_Font_StrLenPixels(finalText, cgs.media.qhFontMedium, 1.0f);
str = finalText;
if (cgi_Language_IsAsian())
{
// this is execrable, and should NOT have had to've been done now, but...
//
extern const char *CG_DisplayBoxedText( int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
const char *psText, int iFontHandle, float fScale,
const vec4_t v4Color);
extern int giLinesOutput;
extern float gfAdvanceHack;
gfAdvanceHack = 1.0f; // override internal vertical advance
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
// Advance line if a graphic has printed
for (i=0;i<MAX_OBJ_GRAPHICS;i++)
{
if (obj_graphics[i])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
}
CG_DisplayBoxedText(
objectiveStartingXpos,
y,
objectiveTextBoxWidth,
objectiveTextBoxHeight,
finalText, // int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight, const char *psText
cgs.media.qhFontMedium, // int iFontHandle,
1.0f, // float fScale,
colorTable[color] // const vec4_t v4Color
);
gfAdvanceHack = 0.0f; // restore
missionYcnt += giLinesOutput;
}
else
{
// western...
//
if (pixelLen < objectiveTextBoxWidth) // One shot - small enough to print entirely on one line
{
y =objectiveStartingYpos + (iYPixelsPerLine * (missionYcnt));
cgi_R_Font_DrawString (
objectiveStartingXpos,
y,
str,
colorTable[color],
cgs.media.qhFontMedium,
-1,
1.0f);
++missionYcnt;
}
// Text is too long, break into lines.
else
{
char holdText2[2];
pixelLen = 0;
charLen = 0;
holdText2[1] = NULL;
strBegin = str;
while( *str )
{
holdText2[0] = *str;
pixelLen += cgi_R_Font_StrLenPixels(holdText2, cgs.media.qhFontMedium, 1.0f);
pixelLen += 2; // For kerning
++charLen;
if (pixelLen > objectiveTextBoxWidth )
{ //Reached max length of this line
//step back until we find a space
while ((charLen>10) && (*str != ' ' ))
{
--str;
--charLen;
}
if (*str==' ')
{
++str; // To get past space
}
assert( charLen<maxHoldText ); // Too big?
Q_strncpyz( holdText, strBegin, charLen);
holdText[charLen] = NULL;
strBegin = str;
pixelLen = 0;
charLen = 1;
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
CG_DrawProportionalString(
objectiveStartingXpos,
y,
holdText,
CG_SMALLFONT,
colorTable[color] );
++missionYcnt;
}
else if (*(str+1) == NULL)
{
++charLen;
assert( charLen<maxHoldText ); // Too big?
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
Q_strncpyz( holdText, strBegin, charLen);
CG_DrawProportionalString(
objectiveStartingXpos,
y, holdText,
CG_SMALLFONT,
colorTable[color] );
++missionYcnt;
break;
}
++str;
}
}
}
if (objectIndex == T3_BOUNTY_OBJ1)
{
y =objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
if (obj_graphics[1])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
if (obj_graphics[2])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
graphic = cgi_R_RegisterShaderNoMip("textures/system/viewscreen1");
CG_DrawPic( 105, 155, OBJ_GRAPHIC_SIZE * 0.90, OBJ_GRAPHIC_SIZE * 0.90, graphic );
obj_graphics[3] = qtrue;
}
}
/*
====================
CG_DrawDataPadObjectives
Draw routine for the objective info screen of the data pad.
====================
*/
void CG_DrawDataPadObjectives(const centity_t *cent )
{
int i,totalY;
int iYPixelsPerLine = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
const short titleXPos = objectiveStartingXpos - 22; // X starting position for title text
const short titleYPos = objectiveStartingYpos - 23; // Y starting position for title text
const short graphic_size = 16; // Size (width and height) of graphic used to show status of objective
const short graphicXpos = objectiveStartingXpos - graphic_size - 8; // Amount of X to backup from text starting position
const short graphicYOffset = (iYPixelsPerLine - graphic_size)/2; // Amount of Y to raise graphic so it's in the center of the text line
missionInfo_Updated = qfalse; // This will stop the text from flashing
cg.missionInfoFlashTime = 0;
// zero out objective graphics
for (i=0;i<MAX_OBJ_GRAPHICS;i++)
{
obj_graphics[i] = qfalse;
}
// Title Text at the top
char text[1024]={0};
cgi_SP_GetStringTextString( "SP_INGAME_OBJECTIVES", text, sizeof(text) );
cgi_R_Font_DrawString (titleXPos, titleYPos, text, colorTable[CT_TITLE], cgs.media.qhFontMedium, -1, 1.0f);
int missionYcnt = 0;
// Print all active objectives
for (i=0;i<MAX_OBJECTIVES;i++)
{
// Is there an objective to see?
if (cent->gent->client->sess.mission_objectives[i].display)
{
// Calculate the Y position
totalY = objectiveStartingYpos + (iYPixelsPerLine * (missionYcnt))+(iYPixelsPerLine/2);
// Draw graphics that show if mission has been accomplished or not
cgi_R_SetColor(colorTable[CT_BLUE3]);
CG_DrawPic( (graphicXpos), (totalY-graphicYOffset), graphic_size, graphic_size, cgs.media.messageObjCircle); // Circle in front
if (cent->gent->client->sess.mission_objectives[i].status == OBJECTIVE_STAT_SUCCEEDED)
{
CG_DrawPic( (graphicXpos), (totalY-graphicYOffset), graphic_size, graphic_size, cgs.media.messageLitOn); // Center Dot
}
// Print current objective text
ObjectivePrint_Line(CT_WHITE, i, missionYcnt );
}
}
// No mission text?
if (!missionYcnt)
{
// Set the message a quarter of the way down and in the center of the text box
int messageYPosition = objectiveStartingYpos + (objectiveTextBoxHeight / 4);
cgi_SP_GetStringTextString( "SP_INGAME_OBJNONE", text, sizeof(text) );
int messageXPosition = objectiveStartingXpos + (objectiveTextBoxWidth/2) - (cgi_R_Font_StrLenPixels(text, cgs.media.qhFontMedium, 1.0f) /2);
cgi_R_Font_DrawString (
messageXPosition,
messageYPosition,
text,
colorTable[CT_WHITE],
cgs.media.qhFontMedium,
-1,
1.0f);
}
}
/*
//-------------------------------------------------------
static void CG_DrawForceCount( const int force, int x, float *y, const int pad,qboolean *hasForcePowers )
{
char s[MAX_STRING_CHARS];
int val, textColor;
char text[1024]={0};
gi.Cvar_VariableStringBuffer( va("playerfplvl%d", force ),s, sizeof(s) );
sscanf( s, "%d",&val );
if ((val<1) || (val> NUM_FORCE_POWERS))
{
return;
}
textColor = CT_ICON_BLUE;
// Draw title
cgi_SP_GetStringTextString( showLoadPowersName[force], text, sizeof(text) );
CG_DrawProportionalString( x, *y, text, CG_BIGFONT, colorTable[textColor] );
// Draw icons
cgi_R_SetColor( colorTable[CT_WHITE]);
const int iconSize = 30;
if ( val >= 0 )
{
x -= 10; // Back up from title a little
for ( int i = 0; i < val; i++ )
{
CG_DrawPic( x - iconSize - i * (iconSize + 10) , *y, iconSize, iconSize, force_icons[force] );
}
}
*y += pad;
*hasForcePowers = qtrue;
}
/*
====================
CG_LoadScreen_PersonalInfo
====================
*/
/*
static void CG_LoadScreen_PersonalInfo(void)
{
float x, y;
int pad = 25;
char text[1024]={0};
qboolean hasForcePowers;
y = 65 + 30;
pad = 28;
x = 300;
hasForcePowers=qfalse;
CG_DrawForceCount( FP_HEAL, x, &y, pad,&hasForcePowers);
CG_DrawForceCount( FP_LEVITATION, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SPEED, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_PUSH, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_PULL, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_TELEPATHY, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_GRIP, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_LIGHTNING, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABERTHROW, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABER_OFFENSE, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABER_DEFENSE, x, &y, pad,&hasForcePowers );
if (hasForcePowers)
{
cgi_SP_GetStringTextString( "SP_INGAME_CURRENTFORCEPOWERS", text, sizeof(text) );
CG_DrawProportionalString( 200, 65, text, CG_CENTER | CG_BIGFONT, colorTable[CT_WHITE] );
}
else
{ //you are only totally empty on the very first map?
// cgi_SP_GetStringTextString( "SP_INGAME_NONE", text, sizeof(text) );
// CG_DrawProportionalString( 320, y+30, text, CG_CENTER | CG_BIGFONT, colorTable[CT_ICON_BLUE] );
cgi_SP_GetStringTextString( "SP_INGAME_ALONGTIME", text, sizeof(text) );
int w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontMedium, 1.5f);
cgi_R_Font_DrawString((320)-(w/2), y+40, text, colorTable[CT_ICON_BLUE], cgs.media.qhFontMedium, -1, 1.5f);
}
}
*/
static void CG_LoadBar(void)
{
cgi_R_SetColor( colorTable[CT_WHITE]);
int glowHeight = (cg.loadLCARSStage / 9.0f) * 147;
int glowTop = (280 + 147) - glowHeight;
// Draw glow:
CG_DrawPic(280, glowTop, 73, glowHeight, cgs.media.loadTick);
// Draw saber:
CG_DrawPic(280, 265, 73, 147, cgs.media.levelLoad);
}
int CG_WeaponCheck( int weaponIndex );
int loadForcePowerLevel[NUM_FORCE_POWERS];
/*
===============
ForcePowerDataPad_Valid
===============
*/
qboolean CG_ForcePower_Valid(int forceKnownBits, int index)
{
if ((forceKnownBits & (1 << showPowers[index])) &&
loadForcePowerLevel[showPowers[index]]) // Does he have the force power?
{
return qtrue;
}
return qfalse;
}
// Get the player weapons and force power info
static void CG_GetLoadScreenInfo(int *weaponBits,int *forceBits)
{
char s[MAX_STRING_CHARS];
int iDummy,i;
float fDummy;
const char *var;
gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
// Get player weapons and force powers known
if (s[0])
{
// |general info |-force powers
sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i",
&iDummy, // &client->ps.stats[STAT_HEALTH],
&iDummy, // &client->ps.stats[STAT_ARMOR],
&*weaponBits,// &client->ps.stats[STAT_WEAPONS],
&iDummy, // &client->ps.stats[STAT_ITEMS],
&iDummy, // &client->ps.weapon,
&iDummy, // &client->ps.weaponstate,
&iDummy, // &client->ps.batteryCharge,
&fDummy, // &client->ps.viewangles[0],
&fDummy, // &client->ps.viewangles[1],
&fDummy, // &client->ps.viewangles[2],
//force power data
&*forceBits, // &client->ps.forcePowersKnown,
&iDummy // &client->ps.forcePower,
);
}
// the new JK2 stuff - force powers, etc...
//
gi.Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
i=0;
var = strtok( s, " " );
while( var != NULL )
{
/* While there are tokens in "s" */
loadForcePowerLevel[i++] = atoi(var);
/* Get next token: */
var = strtok( NULL, " " );
}
}
/*
====================
CG_DrawLoadingScreen
Load screen displays the map pic, the mission briefing and weapons/force powers
====================
*/
static void CG_DrawLoadingScreen( qhandle_t levelshot, qhandle_t levelshot2, const char *mapName)
{
int xPos,yPos,width,height;
vec4_t color;
qhandle_t background;
int weapons=0, forcepowers=0;
// Get mission briefing for load screen
if (cgi_SP_GetStringTextString( va("BRIEFINGS_%s",mapName), NULL, 0 ) == 0)
{
cgi_Cvar_Set( "ui_missionbriefing", "@BRIEFINGS_NONE" );
}
else
{
cgi_Cvar_Set( "ui_missionbriefing", va("@BRIEFINGS_%s",mapName) );
}
// Print background
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"background",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, background );
}
// Print level pic
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"mappic",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, levelshot );
}
// Print second level pic
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"mappic2",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, levelshot2 );
}
// Removed by BTO - No more icons on the loading screen
/*
// Get player weapons and force power info
CG_GetLoadScreenInfo(&weapons,&forcepowers);
// Print weapon icons
if (weapons)
{
CG_DrawLoadWeapons(weapons);
}
// Print force power icons
if (forcepowers)
{
CG_DrawLoadForcePowers(forcepowers);
}
*/
}
/*
====================
CG_DrawInformation
Draw all the status / pacifier stuff during level loading
====================
*/
void CG_DrawInformation( void ) {
int y;
// draw the dialog background
const char *info = CG_ConfigString( CS_SERVERINFO );
const char *s = Info_ValueForKey( info, "mapname" );
extern SavedGameJustLoaded_e g_eSavedGameJustLoaded; // hack! (hey, it's the last week of coding, ok?
if ( g_eSavedGameJustLoaded != eFULL && (!strcmp(s,"yavin1") || !strcmp(s,"demo")) )//special case for first map!
{
char text[1024]={0};
//
cgi_R_SetColor( colorTable[CT_BLACK] );
CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, cgs.media.whiteShader );
cgi_SP_GetStringTextString( "SP_INGAME_ALONGTIME", text, sizeof(text) );
int w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontMedium, 1.0f);
cgi_R_Font_DrawString((320)-(w/2), 140, text, colorTable[CT_ICON_BLUE], cgs.media.qhFontMedium, -1, 1.0f);
}
else
{
qhandle_t levelshot = cgi_R_RegisterShaderNoMip( va( "levelshots/%s", s ) );
if (!levelshot) {
levelshot = cgi_R_RegisterShaderNoMip( "menu/art/unknownmap" );
}
qhandle_t levelshot2 = cgi_R_RegisterShaderNoMip( va( "levelshots/%s2", s ) );
if (!levelshot2) {
levelshot2 = levelshot;
}
CG_DrawLoadingScreen(levelshot, levelshot2, s);
cgi_UI_MenuPaintAll();
}
CG_LoadBar();
// the first 150 rows are reserved for the client connection
// screen to write into
// if ( cg.processedSnapshotNum == 0 )
{
// still loading
// print the current item being loaded
#ifdef _DEBUG
cgi_R_Font_DrawString( 40, 416, va("LOADING ... %s",cg.infoScreenText),colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, 1.0f );
#endif
}
// draw info string information
y = 20;
// map-specific message (long map name)
s = CG_ConfigString( CS_MESSAGE );
if ( s[0] )
{
if (s[0] == '@')
{
char text[1024]={0};
cgi_SP_GetStringTextString( s+1, text, sizeof(text) );
cgi_R_Font_DrawString( 15, y, va("\"%s\"",text),colorTable[CT_WHITE],cgs.media.qhFontMedium, -1, 1.0f );
}
else
{
cgi_R_Font_DrawString( 15, y, va("\"%s\"",s),colorTable[CT_WHITE],cgs.media.qhFontMedium, -1, 1.0f );
}
y += 20;
}
}

87
code/cgame/cg_lights.cpp Normal file
View File

@@ -0,0 +1,87 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#if !defined(CG_LIGHTS_H_INC)
#include "cg_lights.h"
#endif
static clightstyle_t cl_lightstyle[MAX_LIGHT_STYLES];
static int lastofs;
/*
================
FX_ClearLightStyles
================
*/
void CG_ClearLightStyles (void)
{
int i;
memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
lastofs = -1;
for(i=0;i<MAX_LIGHT_STYLES*3;i++)
{
CG_SetLightstyle (i);
}
}
/*
================
FX_RunLightStyles
================
*/
void CG_RunLightStyles (void)
{
int ofs;
int i;
clightstyle_t *ls;
ofs = cg.time / 50;
// if (ofs == lastofs)
// return;
lastofs = ofs;
for (i=0,ls=cl_lightstyle ; i<MAX_LIGHT_STYLES ; i++, ls++)
{
if (!ls->length)
{
ls->value[0] = ls->value[1] = ls->value[2] = ls->value[3] = 255;
}
else if (ls->length == 1)
{
ls->value[0] = ls->map[0][0];
ls->value[1] = ls->map[0][1];
ls->value[2] = ls->map[0][2];
ls->value[3] = 255; //ls->map[0][3];
}
else
{
ls->value[0] = ls->map[ofs%ls->length][0];
ls->value[1] = ls->map[ofs%ls->length][1];
ls->value[2] = ls->map[ofs%ls->length][2];
ls->value[3] = 255; //ls->map[ofs%ls->length][3];
}
trap_R_SetLightStyle(i, *(int*)ls->value);
}
}
void CG_SetLightstyle (int i)
{
const char *s;
int j, k;
s = CG_ConfigString( i+CS_LIGHT_STYLES );
j = strlen (s);
if (j >= MAX_QPATH)
{
Com_Error (ERR_DROP, "svc_lightstyle length=%i", j);
}
cl_lightstyle[(i/3)].length = j;
for (k=0 ; k<j ; k++)
{
cl_lightstyle[(i/3)].map[k][(i%3)] = (float)(s[k]-'a')/(float)('z'-'a') * 255.0;
}
}

16
code/cgame/cg_lights.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#if !defined(CG_LIGHTS_H_INC)
#define CG_LIGHTS_H_INC
typedef struct
{
int length;
color4ub_t value;
color4ub_t map[MAX_QPATH];
} clightstyle_t;
void CG_ClearLightStyles (void);
void CG_RunLightStyles (void);
void CG_SetLightstyle (int i);
#endif // CG_LIGHTS_H_INC

1233
code/cgame/cg_local.h Normal file

File diff suppressed because it is too large Load Diff

599
code/cgame/cg_localents.cpp Normal file
View File

@@ -0,0 +1,599 @@
// cg_localents.c -- every frame, generate renderer commands for locally
// processed entities, like smoke puffs, gibs, shells, etc.
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_media.h"
#ifdef _XBOX
#define MAX_LOCAL_ENTITIES 128
#else
#define MAX_LOCAL_ENTITIES 512
#endif
localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES];
localEntity_t cg_activeLocalEntities; // double linked list
localEntity_t *cg_freeLocalEntities; // single linked list
/*
===================
CG_InitLocalEntities
This is called at startup and for tournement restarts
===================
*/
void CG_InitLocalEntities( void ) {
int i;
memset( cg_localEntities, 0, sizeof( cg_localEntities ) );
cg_activeLocalEntities.next = &cg_activeLocalEntities;
cg_activeLocalEntities.prev = &cg_activeLocalEntities;
cg_freeLocalEntities = cg_localEntities;
for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) {
cg_localEntities[i].next = &cg_localEntities[i+1];
}
}
/*
==================
CG_FreeLocalEntity
==================
*/
void CG_FreeLocalEntity( localEntity_t *le ) {
if ( !le->prev ) {
CG_Error( "CG_FreeLocalEntity: not active" );
}
// remove from the doubly linked active list
le->prev->next = le->next;
le->next->prev = le->prev;
// the free list is only singly linked
le->next = cg_freeLocalEntities;
cg_freeLocalEntities = le;
}
/*
===================
CG_AllocLocalEntity
Will allways succeed, even if it requires freeing an old active entity
===================
*/
localEntity_t *CG_AllocLocalEntity( void ) {
localEntity_t *le;
if ( !cg_freeLocalEntities ) {
// no free entities, so free the one at the end of the chain
// remove the oldest active entity
CG_FreeLocalEntity( cg_activeLocalEntities.prev );
}
le = cg_freeLocalEntities;
cg_freeLocalEntities = cg_freeLocalEntities->next;
memset( le, 0, sizeof( *le ) );
// link into the active list
le->next = cg_activeLocalEntities.next;
le->prev = &cg_activeLocalEntities;
cg_activeLocalEntities.next->prev = le;
cg_activeLocalEntities.next = le;
le->ownerGentNum = -1;
return le;
}
/*
====================================================================================
FRAGMENT PROCESSING
A fragment localentity interacts with the environment in some way (hitting walls),
or generates more localentities along a trail.
====================================================================================
*/
/*
================
CG_FragmentBounceSound
================
*/
void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace )
{
// half the fragments will make a bounce sounds
if ( rand() & 1 )
{
sfxHandle_t s = 0;
switch( le->leBounceSoundType )
{
case LEBS_ROCK:
s = cgs.media.rockBounceSound[Q_irand(0,1)];
break;
case LEBS_METAL:
s = cgs.media.metalBounceSound[Q_irand(0,1)];// FIXME: make sure that this sound is registered properly...might still be rock bounce sound....
break;
}
if ( s )
{
cgi_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
}
// bouncers only make the sound once...
// FIXME: arbitrary...change if it bugs you
le->leBounceSoundType = LEBS_NONE;
}
else if ( rand() & 1 )
{
// we may end up bouncing again, but each bounce reduces the chance of playing the sound again or they may make a lot of noise when they settle
// FIXME: maybe just always do this??
le->leBounceSoundType = LEBS_NONE;
}
}
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace )
{
vec3_t velocity;
float dot;
int hitTime;
// reflect the velocity on the trace plane
hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
dot = DotProduct( velocity, trace->plane.normal );
VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );
VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );
VectorCopy( trace->endpos, le->pos.trBase );
le->pos.trTime = cg.time;
// check for stop, making sure that even on low FPS systems it doesn't bobble
if ( trace->allsolid ||
( trace->plane.normal[2] > 0 &&
( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) )
{
le->pos.trType = TR_STATIONARY;
}
}
/*
================
CG_AddFragment
================
*/
void CG_AddFragment( localEntity_t *le )
{
vec3_t newOrigin;
trace_t trace;
// used to sink into the ground, but it looks better to maybe just fade them out
int t;
t = le->endTime - cg.time;
if ( t < FRAG_FADE_TIME )
{
le->refEntity.renderfx |= RF_ALPHA_FADE;
le->refEntity.shaderRGBA[0] = le->refEntity.shaderRGBA[1] = le->refEntity.shaderRGBA[2] = 255;
le->refEntity.shaderRGBA[3] = ((float)t / FRAG_FADE_TIME) * 255.0f;
}
if ( le->pos.trType == TR_STATIONARY )
{
if ( !(cgi_CM_PointContents( le->refEntity.origin, 0 ) & CONTENTS_SOLID ))
{
// thing is no longer in solid, so let gravity take it back
VectorCopy( le->refEntity.origin, le->pos.trBase );
VectorClear( le->pos.trDelta );
le->pos.trTime = cg.time;
le->pos.trType = TR_GRAVITY;
}
cgi_R_AddRefEntityToScene( &le->refEntity );
return;
}
// calculate new position
EvaluateTrajectory( &le->pos, cg.time, newOrigin );
le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
VectorCopy( newOrigin, le->refEntity.lightingOrigin );
// trace a line from previous position to new position
CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerGentNum, CONTENTS_SOLID );
if ( trace.fraction == 1.0 ) {
// still in free fall
VectorCopy( newOrigin, le->refEntity.origin );
if ( le->leFlags & LEF_TUMBLE ) {
vec3_t angles;
EvaluateTrajectory( &le->angles, cg.time, angles );
AnglesToAxis( angles, le->refEntity.axis );
for(int k = 0; k < 3; k++)
{
VectorScale(le->refEntity.axis[k], le->radius, le->refEntity.axis[k]);
}
}
cgi_R_AddRefEntityToScene( &le->refEntity );
return;
}
// if it is in a nodrop zone, remove it
// this keeps gibs from waiting at the bottom of pits of death
// and floating levels
if ( cgi_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP )
{
CG_FreeLocalEntity( le );
return;
}
// do a bouncy sound
CG_FragmentBounceSound( le, &trace );
// reflect the velocity on the trace plane
CG_ReflectVelocity( le, &trace );
//FIXME: if LEF_TUMBLE, change avelocity too?
cgi_R_AddRefEntityToScene( &le->refEntity );
}
/*
=====================================================================
TRIVIAL LOCAL ENTITIES
These only do simple scaling or modulation before passing to the renderer
=====================================================================
*/
/*
** CG_AddTeleporterEffect
*/
void CG_AddTeleporterEffect( localEntity_t *le ) {
refEntity_t *re;
float c;
re = &le->refEntity;
c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime );
re->shaderRGBA[0] =
re->shaderRGBA[1] =
re->shaderRGBA[2] =
re->shaderRGBA[3] = 0xff * c;
cgi_R_AddRefEntityToScene( re );
}
/*
** CG_AddFadeRGB
*/
void CG_AddFadeRGB( localEntity_t *le ) {
refEntity_t *re;
float c;
re = &le->refEntity;
c = ( le->endTime - cg.time ) * le->lifeRate;
c *= 0xff;
re->shaderRGBA[0] = le->color[0] * c;
re->shaderRGBA[1] = le->color[1] * c;
re->shaderRGBA[2] = le->color[2] * c;
re->shaderRGBA[3] = le->color[3] * c;
cgi_R_AddRefEntityToScene( re );
}
/*
==================
CG_AddPuff
==================
*/
static void CG_AddPuff( localEntity_t *le ) {
refEntity_t *re;
float c;
vec3_t delta;
float len;
re = &le->refEntity;
// fade / grow time
c = ( le->endTime - cg.time ) / (float)( le->endTime - le->startTime );
re->shaderRGBA[0] = le->color[0] * c;
re->shaderRGBA[1] = le->color[1] * c;
re->shaderRGBA[2] = le->color[2] * c;
if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
re->radius = le->radius * ( 1.0 - c ) + 8;
}
EvaluateTrajectory( &le->pos, cg.time, re->origin );
// if the view would be "inside" the sprite, kill the sprite
// so it doesn't add too much overdraw
VectorSubtract( re->origin, cg.refdef.vieworg, delta );
len = VectorLength( delta );
if ( len < le->radius ) {
CG_FreeLocalEntity( le );
return;
}
cgi_R_AddRefEntityToScene( re );
}
/*
================
CG_AddLocalLight
================
*/
static void CG_AddLocalLight( localEntity_t *le )
{
// There should be a light if this is being used, but hey...
if ( le->light )
{
float light;
light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
if ( light < 0.5 )
{
light = 1.0;
}
else
{
light = 1.0 - ( light - 0.5 ) * 2;
}
light = le->light * light;
cgi_R_AddLightToScene( le->refEntity.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] );
}
}
//---------------------------------------------------
static void CG_AddFadeModel( localEntity_t *le )
{
refEntity_t *ent = &le->refEntity;
if ( cg.time < le->startTime )
{
CG_FreeLocalEntity( le );
return;
}
float frac = 1.0f - ((float)( cg.time - le->startTime )/(float)( le->endTime - le->startTime ));
ent->shaderRGBA[0] = le->color[0] * frac;
ent->shaderRGBA[1] = le->color[1] * frac;
ent->shaderRGBA[2] = le->color[2] * frac;
ent->shaderRGBA[3] = le->color[3] * frac;
EvaluateTrajectory( &le->pos, cg.time, ent->origin );
// add the entity
cgi_R_AddRefEntityToScene( ent );
}
// NOTE: this is 100% for the demp2 alt-fire effect, so changes to the visual effect will affect game side demp2 code
//---------------------------------------------------
static void CG_AddFadeScaleModel( localEntity_t *le )
{
refEntity_t *ent = &le->refEntity;
float frac = ( cg.time - le->startTime )/((float)( le->endTime - le->startTime ));
frac *= frac * frac; // yes, this is completely ridiculous...but it causes the shell to grow slowly then "explode" at the end
ent->nonNormalizedAxes = qtrue;
AxisCopy( axisDefault, ent->axis );
VectorScale( ent->axis[0], le->radius * frac, ent->axis[0] );
VectorScale( ent->axis[1], le->radius * frac, ent->axis[1] );
VectorScale( ent->axis[2], le->radius * 0.5f * frac, ent->axis[2] );
frac = 1.0f - frac;
ent->shaderRGBA[0] = le->color[0] * frac;
ent->shaderRGBA[1] = le->color[1] * frac;
ent->shaderRGBA[2] = le->color[2] * frac;
ent->shaderRGBA[3] = le->color[3] * frac;
// add the entity
cgi_R_AddRefEntityToScene( ent );
}
// create a quad that doesn't use a refEnt. Currently only for use with the DebugNav drawing so it doesn't have to use fx
//------------------------------------------
static void CG_AddQuad( localEntity_t *le )
{
polyVert_t verts[4];
VectorCopy( le->refEntity.origin, verts[0].xyz );
verts[0].xyz[0] -= le->radius;
verts[0].xyz[1] -= le->radius;
verts[0].st[0] = 0;
verts[0].st[1] = 0;
for ( int i = 0; i < 4; i++ )
{
verts[i].modulate[0] = le->color[0];
verts[i].modulate[1] = le->color[1];
verts[i].modulate[2] = le->color[2];
verts[i].modulate[3] = le->color[3];
}
VectorCopy( le->refEntity.origin, verts[1].xyz );
verts[1].xyz[0] -= le->radius;
verts[1].xyz[1] += le->radius;
verts[1].st[0] = 0;
verts[1].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[2].xyz );
verts[2].xyz[0] += le->radius;
verts[2].xyz[1] += le->radius;
verts[2].st[0] = 1;
verts[2].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[3].xyz );
verts[3].xyz[0] += le->radius;
verts[3].xyz[1] -= le->radius;
verts[3].st[0] = 1;
verts[3].st[1] = 0;
cgi_R_AddPolyToScene( le->refEntity.customShader, 4, verts );
}
// create a sprite that doesn't use a refEnt. Currently only for use with the DebugNav drawing so it doesn't have to use fx
//------------------------------------------
static void CG_AddSprite( localEntity_t *le )
{
polyVert_t verts[4];
VectorCopy( le->refEntity.origin, verts[0].xyz );
VectorMA( verts[0].xyz, -le->radius, cg.refdef.viewaxis[2], verts[0].xyz );
VectorMA( verts[0].xyz, -le->radius, cg.refdef.viewaxis[1], verts[0].xyz );
verts[0].st[0] = 0;
verts[0].st[1] = 0;
for ( int i = 0; i < 4; i++ )
{
verts[i].modulate[0] = le->color[0];
verts[i].modulate[1] = le->color[1];
verts[i].modulate[2] = le->color[2];
verts[i].modulate[3] = le->color[3];
}
VectorCopy( le->refEntity.origin, verts[1].xyz );
VectorMA( verts[1].xyz, -le->radius, cg.refdef.viewaxis[2], verts[1].xyz );
VectorMA( verts[1].xyz, le->radius, cg.refdef.viewaxis[1], verts[1].xyz );
verts[1].st[0] = 0;
verts[1].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[2].xyz );
VectorMA( verts[2].xyz, le->radius, cg.refdef.viewaxis[2], verts[2].xyz );
VectorMA( verts[2].xyz, le->radius, cg.refdef.viewaxis[1], verts[2].xyz );
verts[2].st[0] = 1;
verts[2].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[3].xyz );
VectorMA( verts[3].xyz, le->radius, cg.refdef.viewaxis[2], verts[3].xyz );
VectorMA( verts[3].xyz, -le->radius, cg.refdef.viewaxis[1], verts[3].xyz );
verts[3].st[0] = 1;
verts[3].st[1] = 0;
cgi_R_AddPolyToScene( le->refEntity.customShader, 4, verts );
}
/*
===================
CG_AddLine
for beams and the like.
===================
*/
void CG_AddLine( localEntity_t *le )
{
refEntity_t *re;
re = &le->refEntity;
re->reType = RT_LINE;
cgi_R_AddRefEntityToScene( re );
}
//==============================================================================
/*
===================
CG_AddLocalEntities
===================
*/
void CG_AddLocalEntities( void )
{
localEntity_t *le, *next;
// walk the list backwards, so any new local entities generated
// (trails, marks, etc) will be present this frame
le = cg_activeLocalEntities.prev;
for ( ; le != &cg_activeLocalEntities ; le = next ) {
// grab next now, so if the local entity is freed we
// still have it
next = le->prev;
if ( cg.time >= le->endTime ) {
CG_FreeLocalEntity( le );
continue;
}
switch ( le->leType ) {
default:
CG_Error( "Bad leType: %i", le->leType );
break;
case LE_MARK:
break;
case LE_FADE_MODEL:
CG_AddFadeModel( le );
break;
case LE_FADE_SCALE_MODEL:
CG_AddFadeScaleModel( le );
break;
case LE_FRAGMENT:
CG_AddFragment( le );
break;
case LE_PUFF:
CG_AddPuff( le );
break;
case LE_FADE_RGB: // teleporters, railtrails
CG_AddFadeRGB( le );
break;
case LE_LIGHT:
CG_AddLocalLight( le );
break;
case LE_LINE: // oriented lines for FX
CG_AddLine( le );
break;
// Use for debug only
case LE_QUAD:
CG_AddQuad( le );
break;
case LE_SPRITE:
CG_AddSprite( le );
}
}
}

4442
code/cgame/cg_main.cpp Normal file

File diff suppressed because it is too large Load Diff

264
code/cgame/cg_marks.cpp Normal file
View File

@@ -0,0 +1,264 @@
// cg_marks.c -- wall marks
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
===================================================================
MARK POLYS
===================================================================
*/
markPoly_t cg_activeMarkPolys; // double linked list
markPoly_t *cg_freeMarkPolys; // single linked list
markPoly_t cg_markPolys[MAX_MARK_POLYS];
/*
===================
CG_InitMarkPolys
This is called at startup and for tournement restarts
===================
*/
void CG_InitMarkPolys( void ) {
int i;
memset( cg_markPolys, 0, sizeof(cg_markPolys) );
cg_activeMarkPolys.nextMark = &cg_activeMarkPolys;
cg_activeMarkPolys.prevMark = &cg_activeMarkPolys;
cg_freeMarkPolys = cg_markPolys;
for ( i = 0 ; i < MAX_MARK_POLYS - 1 ; i++ ) {
cg_markPolys[i].nextMark = &cg_markPolys[i+1];
}
}
/*
==================
CG_FreeMarkPoly
==================
*/
void CG_FreeMarkPoly( markPoly_t *le ) {
if ( !le->prevMark ) {
CG_Error( "CG_FreeLocalEntity: not active" );
}
// remove from the doubly linked active list
le->prevMark->nextMark = le->nextMark;
le->nextMark->prevMark = le->prevMark;
// the free list is only singly linked
le->nextMark = cg_freeMarkPolys;
cg_freeMarkPolys = le;
}
/*
===================
CG_AllocMark
Will allways succeed, even if it requires freeing an old active mark
===================
*/
markPoly_t *CG_AllocMark( void ) {
markPoly_t *le;
int time;
if ( !cg_freeMarkPolys ) {
// no free entities, so free the one at the end of the chain
// remove the oldest active entity
time = cg_activeMarkPolys.prevMark->time;
while (cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time) {
CG_FreeMarkPoly( cg_activeMarkPolys.prevMark );
}
}
le = cg_freeMarkPolys;
cg_freeMarkPolys = cg_freeMarkPolys->nextMark;
memset( le, 0, sizeof( *le ) );
// link into the active list
le->nextMark = cg_activeMarkPolys.nextMark;
le->prevMark = &cg_activeMarkPolys;
cg_activeMarkPolys.nextMark->prevMark = le;
cg_activeMarkPolys.nextMark = le;
return le;
}
/*
=================
CG_ImpactMark
origin should be a point within a unit of the plane
dir should be the plane normal
temporary marks will not be stored or randomly oriented, but immediately
passed to the renderer.
=================
*/
#define MAX_MARK_FRAGMENTS 128
#define MAX_MARK_POINTS 384
void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir,
float orientation, float red, float green, float blue, float alpha,
qboolean alphaFade, float radius, qboolean temporary ) {
vec3_t axis[3];
float texCoordScale;
vec3_t originalPoints[4];
byte colors[4];
int i, j;
int numFragments;
markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf;
vec3_t markPoints[MAX_MARK_POINTS];
vec3_t projection;
if ( !cg_addMarks.integer ) {
return;
}
if ( radius <= 0 ) {
CG_Error( "CG_ImpactMark called with <= 0 radius" );
}
// create the texture axis
VectorNormalize2( dir, axis[0] );
PerpendicularVector( axis[1], axis[0] );
RotatePointAroundVector( axis[2], axis[0], axis[1], orientation );
CrossProduct( axis[0], axis[2], axis[1] );
texCoordScale = 0.5 * 1.0 / radius;
// create the full polygon
for ( i = 0 ; i < 3 ; i++ ) {
originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i];
originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i];
originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i];
originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i];
}
// get the fragments
VectorScale( dir, -20, projection );
numFragments = cgi_CM_MarkFragments( 4, (const float (*)[3])originalPoints,
projection, MAX_MARK_POINTS, markPoints[0],
MAX_MARK_FRAGMENTS, markFragments );
colors[0] = red * 255;
colors[1] = green * 255;
colors[2] = blue * 255;
colors[3] = alpha * 255;
for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) {
polyVert_t *v;
polyVert_t verts[MAX_VERTS_ON_POLY];
markPoly_t *mark;
// we have an upper limit on the complexity of polygons
// that we store persistantly
if ( mf->numPoints > MAX_VERTS_ON_POLY ) {
mf->numPoints = MAX_VERTS_ON_POLY;
}
for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) {
vec3_t delta;
VectorCopy( markPoints[mf->firstPoint + j], v->xyz );
VectorSubtract( v->xyz, origin, delta );
v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale;
v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale;
*(int *)v->modulate = *(int *)colors;
}
// if it is a temporary (shadow) mark, add it immediately and forget about it
if ( temporary ) {
cgi_R_AddPolyToScene( markShader, mf->numPoints, verts );
continue;
}
// otherwise save it persistantly
mark = CG_AllocMark();
mark->time = cg.time;
mark->alphaFade = alphaFade;
mark->markShader = markShader;
mark->poly.numVerts = mf->numPoints;
mark->color[0] = colors[0];//red;
mark->color[1] = colors[1];//green;
mark->color[2] = colors[2];//blue;
mark->color[3] = colors[3];//alpha;
memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
}
}
/*
===============
CG_AddMarks
===============
*/
#define MARK_TOTAL_TIME 10000
#define MARK_FADE_TIME 1000
void CG_AddMarks( void ) {
int j;
markPoly_t *mp, *next;
int t;
int fade;
if ( !cg_addMarks.integer ) {
return;
}
mp = cg_activeMarkPolys.nextMark;
for ( ; mp != &cg_activeMarkPolys ; mp = next ) {
// grab next now, so if the local entity is freed we
// still have it
next = mp->nextMark;
// see if it is time to completely remove it
if ( cg.time > mp->time + MARK_TOTAL_TIME ) {
CG_FreeMarkPoly( mp );
continue;
}
// fade all marks out with time
t = mp->time + MARK_TOTAL_TIME - cg.time;
if ( t < MARK_FADE_TIME ) {
fade = 255 * t / MARK_FADE_TIME;
if ( mp->alphaFade ) {
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[3] = fade;
}
}
else
{
float f = (float)t / MARK_FADE_TIME;
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[0] = mp->color[0] * f;
mp->verts[j].modulate[1] = mp->color[1] * f;
mp->verts[j].modulate[2] = mp->color[2] * f;
}
}
}
else
{
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[0] = mp->color[0];
mp->verts[j].modulate[1] = mp->color[1];
mp->verts[j].modulate[2] = mp->color[2];
}
}
cgi_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts );
}
}

417
code/cgame/cg_media.h Normal file
View File

@@ -0,0 +1,417 @@
#ifndef __CG_MEDIA_H_
#define __CG_MEDIA_H_
//#define NUM_CROSSHAIRS 9
#define NUM_CROSSHAIRS 1
typedef enum {
FOOTSTEP_STONEWALK,
FOOTSTEP_STONERUN,
FOOTSTEP_METALWALK,
FOOTSTEP_METALRUN,
FOOTSTEP_PIPEWALK,
FOOTSTEP_PIPERUN,
FOOTSTEP_SPLASH,
FOOTSTEP_WADE,
FOOTSTEP_SWIM,
FOOTSTEP_SNOWWALK,
FOOTSTEP_SNOWRUN,
FOOTSTEP_SANDWALK,
FOOTSTEP_SANDRUN,
FOOTSTEP_GRASSWALK,
FOOTSTEP_GRASSRUN,
FOOTSTEP_DIRTWALK,
FOOTSTEP_DIRTRUN,
FOOTSTEP_MUDWALK,
FOOTSTEP_MUDRUN,
FOOTSTEP_GRAVELWALK,
FOOTSTEP_GRAVELRUN,
FOOTSTEP_RUGWALK,
FOOTSTEP_RUGRUN,
FOOTSTEP_WOODWALK,
FOOTSTEP_WOODRUN,
FOOTSTEP_TOTAL
} footstep_t;
#define ICON_WEAPONS 0
#define ICON_FORCE 1
#define ICON_INVENTORY 2
#define MAX_HUD_TICS 4
typedef struct HUDMenuItem_s
{
char *menuName;
char *itemName;
int xPos;
int yPos;
int width;
int height;
vec4_t color;
qhandle_t background;
} HUDMenuItem_t;
extern HUDMenuItem_t healthTics[];
extern HUDMenuItem_t armorTics[];
extern HUDMenuItem_t ammoTics[];
extern HUDMenuItem_t forceTics[];
extern HUDMenuItem_t otherHUDBits[];
typedef enum
{
OHB_HEALTHAMOUNT = 0,
OHB_ARMORAMOUNT,
OHB_FORCEAMOUNT,
OHB_AMMOAMOUNT,
OHB_SABERSTYLE_STRONG,
OHB_SABERSTYLE_MEDIUM,
OHB_SABERSTYLE_FAST,
OHB_SCANLINE_LEFT,
OHB_SCANLINE_RIGHT,
OHB_FRAME_LEFT,
OHB_FRAME_RIGHT,
OHB_MAX
} otherhudbits_t;
#define NUM_CHUNK_MODELS 4
typedef enum
{
CHUNK_METAL1 = 0,
CHUNK_METAL2,
CHUNK_ROCK1,
CHUNK_ROCK2,
CHUNK_ROCK3,
CHUNK_CRATE1,
CHUNK_CRATE2,
CHUNK_WHITE_METAL,
NUM_CHUNK_TYPES
};
// all of the model, shader, and sound references that are
// loaded at gamestate time are stored in cgMedia_t
// Other media that can be tied to clients, weapons, or items are
// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t
typedef struct {
qhandle_t charsetShader;
qhandle_t whiteShader;
qhandle_t crosshairShader[NUM_CROSSHAIRS];
qhandle_t backTileShader;
// qhandle_t noammoShader;
// qhandle_t numberShaders[11];
qhandle_t smallnumberShaders[11];
// qhandle_t chunkyNumberShaders[11];
qhandle_t loadTick;
// qhandle_t loadTickCap;
// HUD artwork
// int currentBackground;
// qhandle_t weaponbox;
// qhandle_t weaponIconBackground;
// qhandle_t forceIconBackground;
// qhandle_t inventoryIconBackground;
qhandle_t turretComputerOverlayShader;
qhandle_t turretCrossHairShader;
//Chunks
qhandle_t chunkModels[NUM_CHUNK_TYPES][4];
sfxHandle_t chunkSound;
sfxHandle_t grateSound;
sfxHandle_t rockBreakSound;
sfxHandle_t rockBounceSound[2];
sfxHandle_t metalBounceSound[2];
sfxHandle_t glassChunkSound;
sfxHandle_t crateBreakSound[2];
// Saber shaders
//-----------------------------
qhandle_t forceCoronaShader;
qhandle_t saberBlurShader;
qhandle_t swordTrailShader;
qhandle_t yellowDroppedSaberShader; // glow
qhandle_t redSaberGlowShader;
qhandle_t redSaberCoreShader;
qhandle_t orangeSaberGlowShader;
qhandle_t orangeSaberCoreShader;
qhandle_t yellowSaberGlowShader;
qhandle_t yellowSaberCoreShader;
qhandle_t greenSaberGlowShader;
qhandle_t greenSaberCoreShader;
qhandle_t blueSaberGlowShader;
qhandle_t blueSaberCoreShader;
qhandle_t purpleSaberGlowShader;
qhandle_t purpleSaberCoreShader;
qhandle_t explosionModel;
qhandle_t surfaceExplosionShader;
qhandle_t halfShieldModel;
qhandle_t solidWhiteShader;
qhandle_t electricBodyShader;
qhandle_t electricBody2Shader;
qhandle_t refractShader;
qhandle_t boltShader;
// Disruptor zoom graphics
qhandle_t disruptorMask;
qhandle_t disruptorInsert;
qhandle_t disruptorLight;
qhandle_t disruptorInsertTick;
// Binocular graphics
/*
qhandle_t binocularCircle;
qhandle_t binocularMask;
qhandle_t binocularArrow;
qhandle_t binocularTri;
qhandle_t binocularStatic;
qhandle_t binocularOverlay;
*/
// LA Goggles graphics
/*
qhandle_t laGogglesStatic;
qhandle_t laGogglesMask;
qhandle_t laGogglesSideBit;
qhandle_t laGogglesBracket;
qhandle_t laGogglesArrow;
*/
// wall mark shaders
qhandle_t scavMarkShader;
qhandle_t rivetMarkShader;
qhandle_t shadowMarkShader;
qhandle_t wakeMarkShader;
qhandle_t fsrMarkShader;
qhandle_t fslMarkShader;
qhandle_t fshrMarkShader;
qhandle_t fshlMarkShader;
qhandle_t damageBlendBlobShader;
// fonts...
//
qhandle_t qhFontSmall;
qhandle_t qhFontMedium;
// special effects models / etc.
qhandle_t personalShieldShader;
qhandle_t cloakedShader;
// Interface media
qhandle_t ammoslider;
// qhandle_t emplacedHealthBarShader;
qhandle_t DPForcePowerOverlay;
qhandle_t bdecal_burnmark1;
qhandle_t bdecal_saberglowmark;
qhandle_t messageLitOn;
qhandle_t messageLitOff;
qhandle_t messageObjCircle;
// qhandle_t batteryChargeShader;
qhandle_t useableHint;
qhandle_t levelLoad;
//new stuff for Jedi Academy
//force power icons
// qhandle_t forcePowerIcons[NUM_FORCE_POWERS];
qhandle_t rageRecShader;
qhandle_t playerShieldDamage;
qhandle_t forceSightBubble;
qhandle_t forceShell;
qhandle_t sightShell;
qhandle_t drainShader;
// sounds
sfxHandle_t disintegrateSound;
sfxHandle_t disintegrate2Sound;
sfxHandle_t disintegrate3Sound;
sfxHandle_t grenadeBounce1;
sfxHandle_t grenadeBounce2;
sfxHandle_t flechetteStickSound;
sfxHandle_t detPackStickSound;
sfxHandle_t tripMineStickSound;
sfxHandle_t selectSound;
sfxHandle_t selectSound2;
sfxHandle_t overchargeSlowSound;
sfxHandle_t overchargeFastSound;
sfxHandle_t overchargeLoopSound;
sfxHandle_t overchargeEndSound;
// sfxHandle_t useNothingSound;
sfxHandle_t footsteps[FOOTSTEP_TOTAL][4];
// sfxHandle_t talkSound;
sfxHandle_t noAmmoSound;
sfxHandle_t landSound;
sfxHandle_t rollSound;
sfxHandle_t messageLitSound;
sfxHandle_t batteryChargeSound;
sfxHandle_t watrInSound;
sfxHandle_t watrOutSound;
sfxHandle_t watrUnSound;
sfxHandle_t lavaInSound;
sfxHandle_t lavaOutSound;
sfxHandle_t lavaUnSound;
sfxHandle_t noforceSound;
// Zoom
sfxHandle_t zoomStart;
sfxHandle_t zoomLoop;
sfxHandle_t zoomEnd;
sfxHandle_t disruptorZoomLoop;
//new stuff for Jedi Academy
sfxHandle_t drainSound;
#ifdef _IMMERSION
//force feedback stuff
ffHandle_t grenadeBounce1Force;
ffHandle_t grenadeBounce2Force;
ffHandle_t selectForce;
ffHandle_t footstepForces[FOOTSTEP_TOTAL][4];
ffHandle_t noAmmoForce;
ffHandle_t landForce;
ffHandle_t messageLitForce;
ffHandle_t watrInForce;
ffHandle_t watrOutForce;
ffHandle_t watrUnForce;
ffHandle_t zoomStartForce;
ffHandle_t zoomLoopForce;
ffHandle_t zoomEndForce;
ffHandle_t disruptorZoomLoopForce;
#endif // _IMMERSION
} cgMedia_t;
// Stored FX handles
//--------------------
typedef struct
{
// BRYAR PISTOL
fxHandle_t bryarShotEffect;
fxHandle_t bryarPowerupShotEffect;
fxHandle_t bryarWallImpactEffect;
fxHandle_t bryarWallImpactEffect2;
fxHandle_t bryarWallImpactEffect3;
fxHandle_t bryarFleshImpactEffect;
// BLASTER
fxHandle_t blasterShotEffect;
fxHandle_t blasterOverchargeEffect;
fxHandle_t blasterWallImpactEffect;
fxHandle_t blasterFleshImpactEffect;
// BOWCASTER
fxHandle_t bowcasterShotEffect;
fxHandle_t bowcasterBounceEffect;
fxHandle_t bowcasterImpactEffect;
// FLECHETTE
fxHandle_t flechetteShotEffect;
fxHandle_t flechetteAltShotEffect;
fxHandle_t flechetteShotDeathEffect;
fxHandle_t flechetteFleshImpactEffect;
fxHandle_t flechetteRicochetEffect;
//FORCE
fxHandle_t forceConfusion;
fxHandle_t forceLightning;
fxHandle_t forceLightningWide;
//fxHandle_t forceInvincibility;
fxHandle_t forceHeal;
//new stuff for Jedi Academy
fxHandle_t forceDrain;
fxHandle_t forceDrainWide;
fxHandle_t forceDrained;
//footstep effects
fxHandle_t footstepMud;
fxHandle_t footstepSand;
fxHandle_t footstepSnow;
fxHandle_t footstepGravel;
//landing effects
fxHandle_t landingMud;
fxHandle_t landingSand;
fxHandle_t landingDirt;
fxHandle_t landingSnow;
fxHandle_t landingGravel;
} cgEffects_t;
// The client game static (cgs) structure hold everything
// loaded or calculated from the gamestate. It will NOT
// be cleared when a tournement restart is done, allowing
// all clients to begin playing instantly
#define STRIPED_LEVELNAME_VARIATIONS 3 // sigh, to cope with levels that use text from >1 SP file (plus 1 for common)
typedef struct {
gameState_t gameState; // gamestate from server
glconfig_t glconfig; // rendering configuration
int serverCommandSequence; // reliable command stream counter
// parsed from serverinfo
int dmflags;
int teamflags;
int timelimit;
int maxclients;
char mapname[MAX_QPATH];
char stripLevelName[STRIPED_LEVELNAME_VARIATIONS][MAX_QPATH];
//
// locally derived information from gamestate
//
qhandle_t model_draw[MAX_MODELS];
sfxHandle_t sound_precache[MAX_SOUNDS];
#ifdef _IMMERSION
ffHandle_t force_precache[MAX_FORCES];
#endif // _IMMERSION
// Ghoul2 start
qhandle_t skins[MAX_CHARSKINS];
// Ghoul2 end
int numInlineModels;
qhandle_t inlineDrawModel[MAX_SUBMODELS];
vec3_t inlineModelMidpoints[MAX_SUBMODELS];
clientInfo_t clientinfo[MAX_CLIENTS];
// media
cgMedia_t media;
// effects
cgEffects_t effects;
} cgs_t;
extern cgs_t cgs;
#endif //__CG_MEDIA_H_

8262
code/cgame/cg_players.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,364 @@
// cg_playerstate.c -- this file acts on changes in a new playerState_t
// With normal play, this will be done after local prediction, but when
// following another player or playing back a demo, it will be checked
// when the snapshot transitions like all the other entities
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
==============
CG_CheckAmmo
If the ammo has gone low enough to generate the warning, play a sound
==============
*/
void CG_CheckAmmo( void )
{
// int i;
int total;
int previous;
// int weapons;
#if 0
// see about how many seconds of ammo we have remaining
weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
total = 0;
for ( i = WP_SABER; i < WP_NUM_WEAPONS i++ )
{
if ( ! ( weapons & ( 1 << i ) ) )
continue;
/*
switch ( i )
{
case WP_ROCKET_LAUNCHER:
case WP_GRENADE_LAUNCHER:
case WP_RAILGUN:
case WP_SHOTGUN:
total += cg.snap->ps.ammo[i] * 1000;
break;
default:
total += cg.snap->ps.ammo[i] * 200;
break;
}
*/
if ( total >= 5000 )
{
cg.lowAmmoWarning = 0;
return;
}
}
#endif
// Don't bother drawing the ammo warning when have no weapon selected
if ( cg.weaponSelect == WP_NONE )
{
return;
}
total = cg.snap->ps.ammo[weaponData[cg.weaponSelect].ammoIndex];
if (total > weaponData[cg.weaponSelect].ammoLow) // Low on ammo?
{
cg.lowAmmoWarning = 0;
return;
}
previous = cg.lowAmmoWarning;
if (!total) // We're completely freak'in out!
{
cg.lowAmmoWarning = 2;
}
else // Got a little left
{
cg.lowAmmoWarning = 1;
}
// play a sound on transitions
if ( cg.lowAmmoWarning != previous ) {
cgi_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); //"sound/weapons/noammo.wav"
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.noAmmoForce, FF_CLIENT_LOCAL );
#endif // _IMMERSION
}
}
/*
==============
CG_DamageFeedback
==============
*/
void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) {
float left, front, up;
float kick;
int health;
float scale;
vec3_t dir;
vec3_t angles;
float dist;
float yaw, pitch;
//FIXME: Based on MOD, do different kinds of damage effects,
// for example, Borg damage could progressively tint screen green and raise FOV?
// the lower on health you are, the greater the view kick will be
health = cg.snap->ps.stats[STAT_HEALTH];
if ( health < 40 ) {
scale = 1;
} else {
scale = 40.0 / health;
}
kick = damage * scale;
if (kick < 5)
kick = 5;
if (kick > 10)
kick = 10;
// if yaw and pitch are both 255, make the damage always centered (falling, etc)
if ( yawByte == 255 && pitchByte == 255 ) {
cg.damageX = 0;
cg.damageY = 0;
cg.v_dmg_roll = 0;
cg.v_dmg_pitch = -kick;
} else {
// positional
pitch = pitchByte / 255.0 * 360;
yaw = yawByte / 255.0 * 360;
angles[PITCH] = pitch;
angles[YAW] = yaw;
angles[ROLL] = 0;
AngleVectors( angles, dir, NULL, NULL );
VectorSubtract( vec3_origin, dir, dir );
front = DotProduct (dir, cg.refdef.viewaxis[0] );
left = DotProduct (dir, cg.refdef.viewaxis[1] );
up = DotProduct (dir, cg.refdef.viewaxis[2] );
dir[0] = front;
dir[1] = left;
dir[2] = 0;
dist = VectorLength( dir );
if ( dist < 0.1 ) {
dist = 0.1f;
}
cg.v_dmg_roll = kick * left;
cg.v_dmg_pitch = -kick * front;
if ( front <= 0.1 ) {
front = 0.1f;
}
cg.damageX = -left / front;
cg.damageY = up / dist;
}
// clamp the position
if ( cg.damageX > 1.0 ) {
cg.damageX = 1.0;
}
if ( cg.damageX < - 1.0 ) {
cg.damageX = -1.0;
}
if ( cg.damageY > 1.0 ) {
cg.damageY = 1.0;
}
if ( cg.damageY < - 1.0 ) {
cg.damageY = -1.0;
}
// don't let the screen flashes vary as much
if ( kick > 10 ) {
kick = 10;
}
cg.damageValue = kick;
cg.v_dmg_time = cg.time + DAMAGE_TIME;
cg.damageTime = cg.snap->serverTime;
#ifdef _IMMERSION
cgi_FF_Start( cgi_FF_Register( "fffx/player/damage", FF_CHANNEL_DAMAGE ), cg.snap->ps.clientNum );
#endif // _IMMERSION
#ifdef _XBOX
cgi_FF_Xbox_Damage(damage, cg.damageX);
#endif
}
/*
================
CG_Respawn
A respawn happened this snapshot
================
*/
void CG_Respawn( void ) {
// no error decay on player movement
cg.thisFrameTeleport = qtrue;
// display weapons available
// cg.weaponSelectTime = cg.time;
SetWeaponSelectTime();
// select the weapon the server says we are using
if (cg.snap->ps.weapon)
cg.weaponSelect = cg.snap->ps.weapon;
}
/*
==============
CG_CheckPlayerstateEvents
==============
*/
void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
int i;
int event;
centity_t *cent;
#if 0
if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) {
cent = &cg_entities[ ps->clientNum ];
cent->currentState.event = ps->externalEvent;
cent->currentState.eventParm = ps->externalEventParm;
CG_EntityEvent( cent, cent->lerpOrigin );
}
#endif
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
if ( ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]
|| i >= ops->eventSequence ) {
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
cent = &cg_entities[ ps->clientNum ];
cent->currentState.event = event;
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
CG_EntityEvent( cent, cent->lerpOrigin );
}
}
}
/*
==================
CG_CheckLocalSounds
==================
*/
/*
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
const char *s;
// hit changes
if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) {
cgi_S_StartLocalSound( "sound/feedback/hit.wav" );
} else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) {
cgi_S_StartLocalSound( "sound/feedback/hit_teammate.wav" );
}
// score up / down changes
if ( ps->persistant[PERS_SCORE] > ops->persistant[PERS_SCORE] ) {
cgi_S_StartLocalSound( "sound/feedback/scoreup.wav" );
} else if ( ps->persistant[PERS_SCORE] < ops->persistant[PERS_SCORE] ) {
cgi_S_StartLocalSound( "sound/feedback/scoredown.wav" );
}
// reward sounds
if ( ps->persistant[PERS_REWARD_COUNT] > ops->persistant[PERS_REWARD_COUNT] ) {
switch ( ps->persistant[PERS_REWARD] ) {
case REWARD_IMPRESSIVE:
cgi_S_StartLocalSound( "sound/feedback/impressive.wav" );
break;
case REWARD_EXCELLENT:
cgi_S_StartLocalSound( "sound/feedback/excellent.wav" );
break;
case REWARD_DENIED:
cgi_S_StartLocalSound( "sound/feedback/denied.wav" );
break;
default:
CG_Error( "Bad reward_t" );
}
}
// timelimit warnings
if ( cgs.timelimit > 0 ) {
if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && cg.time > (cgs.timelimit - 5) * 60 * 1000 ) {
cg.timelimitWarnings |= 1;
cgi_S_StartLocalSound( "sound/feedback/5_minute.wav" );
}
if ( !( cg.timelimitWarnings & 2 ) && cg.time > (cgs.timelimit - 1) * 60 * 1000 ) {
cg.timelimitWarnings |= 2;
cgi_S_StartLocalSound( "sound/feedback/1_minute.wav" );
}
if ( !( cg.timelimitWarnings & 4 ) && cg.time > ( cgs.timelimit * 60 + 2 ) * 1000 ) {
cg.timelimitWarnings |= 4;
cgi_S_StartLocalSound( "sound/feedback/sudden_death.wav" );
}
}
}
*/
/*
===============
CG_TransitionPlayerState
===============
*/
void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) {
// teleporting
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
cg.thisFrameTeleport = qtrue;
} else {
cg.thisFrameTeleport = qfalse;
}
// check for changing follow mode
if ( ps->clientNum != ops->clientNum ) {
cg.thisFrameTeleport = qtrue;
// make sure we don't get any unwanted transition effects
*ops = *ps;
}
// damage events (player is getting wounded)
if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) {
CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
}
// respawning
if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) {
CG_Respawn();
}
// check for going low on ammo
CG_CheckAmmo();
// run events
CG_CheckPlayerstateEvents( ps, ops );
// smooth the ducking viewheight change
if ( ps->viewheight != ops->viewheight )
{
if ( !cg.nextFrameTeleport )
{//when we crouch/uncrouch in mid-air, our viewhieght doesn't actually change in
//absolute world coordinates, just locally.
cg.duckChange = ps->viewheight - ops->viewheight;
cg.duckTime = cg.time;
}
}
}

780
code/cgame/cg_predict.cpp Normal file
View File

@@ -0,0 +1,780 @@
// cg_predict.c -- this file generates cg.predicted_player_state by either
// interpolating between snapshots from the server or locally predicting
// ahead the client's movement
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\g_vehicles.h"
static pmove_t cg_pmove;
static int cg_numSolidEntities;
static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
#if MEM_DEBUG
#include "..\smartheap\heapagnt.h"
#define CG_TRACE_PROFILE (0)
#endif
/*
====================
CG_BuildSolidList
When a new cg.snap has been set, this function builds a sublist
of the entities that are actually solid, to make for more
efficient collision detection
====================
*/
void CG_BuildSolidList( void )
{
int i;
centity_t *cent;
vec3_t difference;
float dsquared;
cg_numSolidEntities = 0;
if(!cg.snap)
{
return;
}
for ( i = 0 ; i < cg.snap->numEntities ; i++ )
{
if ( cg.snap->entities[ i ].number < ENTITYNUM_WORLD )
{
cent = &cg_entities[ cg.snap->entities[ i ].number ];
if ( cent->gent != NULL && cent->gent->s.solid )
{
cg_solidEntities[cg_numSolidEntities] = cent;
cg_numSolidEntities++;
}
}
}
dsquared = 5000+500;
dsquared *= dsquared;
for(i=0;i<cg_numpermanents;i++)
{
cent = cg_permanents[i];
VectorSubtract(cent->lerpOrigin, cg.snap->ps.origin, difference);
if (cent->currentState.eType == ET_TERRAIN ||
((difference[0]*difference[0]) + (difference[1]*difference[1]) + (difference[2]*difference[2])) <= dsquared)
{
cent->currentValid = qtrue;
if ( cent->nextState && cent->nextState->solid )
{
cg_solidEntities[cg_numSolidEntities] = cent;
cg_numSolidEntities++;
}
}
else
{
cent->currentValid = qfalse;
}
}
}
/*
====================
CG_ClipMoveToEntities
====================
*/
void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
int skipNumber, int mask, trace_t *tr ) {
int i, x, zd, zu;
trace_t trace;
entityState_t *ent;
clipHandle_t cmodel;
vec3_t bmins, bmaxs;
vec3_t origin, angles;
centity_t *cent;
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
cent = cg_solidEntities[ i ];
ent = &cent->currentState;
if ( ent->number == skipNumber ) {
continue;
}
if ( ent->eType == ET_PUSH_TRIGGER ) {
continue;
}
if ( ent->eType == ET_TELEPORT_TRIGGER ) {
continue;
}
if ( ent->solid == SOLID_BMODEL ) {
// special value for bmodel
cmodel = cgi_CM_InlineModel( ent->modelindex );
VectorCopy( cent->lerpAngles, angles );
//Hmm... this would cause traces against brush movers to snap at 20fps (as with the third person camera)...
//Let's use the lerpOrigin for now and see if it breaks anything...
//EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, origin );
VectorCopy( cent->lerpOrigin, origin );
} else {
// encoded bbox
x = (ent->solid & 255);
zd = ((ent->solid>>8) & 255);
zu = ((ent->solid>>16) & 255) - 32;
bmins[0] = bmins[1] = -x;
bmaxs[0] = bmaxs[1] = x;
bmins[2] = -zd;
bmaxs[2] = zu;
cmodel = cgi_CM_TempBoxModel( bmins, bmaxs );//, cent->gent->contents );
VectorCopy( vec3_origin, angles );
VectorCopy( cent->lerpOrigin, origin );
}
cgi_CM_TransformedBoxTrace ( &trace, start, end,
mins, maxs, cmodel, mask, origin, angles);
if (trace.allsolid || trace.fraction < tr->fraction) {
trace.entityNum = ent->number;
*tr = trace;
} else if (trace.startsolid) {
tr->startsolid = qtrue;
}
if ( tr->allsolid ) {
return;
}
}
}
/*
================
CG_Trace
================
*/
void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
const int skipNumber, const int mask, const EG2_Collision eG2TraceType/*=G2_NOCOLLIDE*/, const int useLod/*=0*/) {
trace_t t;
#if CG_TRACE_PROFILE
#if MEM_DEBUG
{
int old=dbgMemSetCheckpoint(2004);
malloc(1);
dbgMemSetCheckpoint(old);
}
#endif
#endif
cgi_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
// check all other solid models
CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);
*result = t;
}
/*
================
CG_PointContents
================
*/
#define USE_SV_PNT_CONTENTS (1)
#if USE_SV_PNT_CONTENTS
int CG_PointContents( const vec3_t point, int passEntityNum ) {
return gi.pointcontents(point,passEntityNum );
}
#else
int CG_PointContents( const vec3_t point, int passEntityNum ) {
int i;
entityState_t *ent;
centity_t *cent;
clipHandle_t cmodel;
int contents;
#if CG_TRACE_PROFILE
#if MEM_DEBUG
{
int old=dbgMemSetCheckpoint(2005);
malloc(1);
dbgMemSetCheckpoint(old);
}
#endif
#endif
contents = cgi_CM_PointContents (point, 0);
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
cent = cg_solidEntities[ i ];
ent = &cent->currentState;
if ( ent->number == passEntityNum ) {
continue;
}
if (ent->solid != SOLID_BMODEL) { // special value for bmodel
continue;
}
cmodel = cgi_CM_InlineModel( ent->modelindex );
if ( !cmodel ) {
continue;
}
contents |= cgi_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
}
return contents;
}
#endif
void CG_SetClientViewAngles( vec3_t angles, qboolean overrideViewEnt )
{
if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD || overrideViewEnt )
{//don't clamp angles when looking through a viewEntity
for( int i = 0; i < 3; i++ )
{
cg.predicted_player_state.viewangles[i] = angles[i];
cg.predicted_player_state.delta_angles[i] = 0;
cg.snap->ps.viewangles[i] = angles[i];
cg.snap->ps.delta_angles[i] = 0;
g_entities[0].client->pers.cmd_angles[i] = ANGLE2SHORT(angles[i]);
}
cgi_SetUserCmdAngles( angles[PITCH], angles[YAW], angles[ROLL] );
}
}
extern qboolean PM_AdjustAnglesToGripper( gentity_t *gent, usercmd_t *cmd );
extern qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboolean anglesOnly );
extern qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd );
extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
qboolean CG_CheckModifyUCmd( usercmd_t *cmd, vec3_t viewangles )
{
qboolean overridAngles = qfalse;
if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
{//controlling something else
memset( cmd, 0, sizeof( usercmd_t ) );
/*
//to keep pointing in same dir, need to set cmd.angles
cmd->angles[PITCH] = ANGLE2SHORT( cg.snap->ps.viewangles[PITCH] ) - cg.snap->ps.delta_angles[PITCH];
cmd->angles[YAW] = ANGLE2SHORT( cg.snap->ps.viewangles[YAW] ) - cg.snap->ps.delta_angles[YAW];
cmd->angles[ROLL] = 0;
*/
VectorCopy( g_entities[0].pos4, viewangles );
overridAngles = qtrue;
//CG_SetClientViewAngles( g_entities[cg.snap->ps.viewEntity].client->ps.viewangles, qtrue );
}
else if ( G_IsRidingVehicle( &g_entities[0] ) )
{
overridAngles = qtrue;
/*
int vehIndex = g_entities[0].owner->client->ps.vehicleIndex;
if ( vehIndex != VEHICLE_NONE
&& (vehicleData[vehIndex].type == VH_FIGHTER || (vehicleData[vehIndex].type == VH_SPEEDER )) )
{//in vehicle flight mode
float speed = VectorLength( cg.snap->ps.velocity );
if ( !speed || cg.snap->ps.groundEntityNum != ENTITYNUM_NONE )
{
cmd->rightmove = 0;
cmd->angles[PITCH] = 0;
cmd->angles[YAW] = ANGLE2SHORT( cg.snap->ps.viewangles[YAW] ) - cg.snap->ps.delta_angles[YAW];
CG_SetClientViewAngles( cg.snap->ps.viewangles, qfalse );
}
}
*/
}
if ( &g_entities[0] && g_entities[0].client )
{
if ( !PM_AdjustAnglesToGripper( &g_entities[0], cmd ) )
{
if ( PM_AdjustAnglesForSpinningFlip( &g_entities[0], cmd, qtrue ) )
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
}
else
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
if ( G_CheckClampUcmd( &g_entities[0], cmd ) )
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
}
return overridAngles;
}
qboolean CG_OnMovingPlat( playerState_t *ps )
{
if ( ps->groundEntityNum != ENTITYNUM_NONE )
{
entityState_t *es = &cg_entities[ps->groundEntityNum].currentState;
if ( es->eType == ET_MOVER )
{//on a mover
if ( es->pos.trType != TR_STATIONARY )
{
if ( es->pos.trType != TR_LINEAR_STOP && es->pos.trType != TR_NONLINEAR_STOP )
{//a constant mover
if ( !VectorCompare( vec3_origin, es->pos.trDelta ) )
{//is moving
return qtrue;
}
}
else
{//a linear-stop mover
if ( es->pos.trTime+es->pos.trDuration > cg.time )
{//still moving
return qtrue;
}
}
}
}
}
return qfalse;
}
/*
========================
CG_InterpolatePlayerState
Generates cg.predicted_player_state by interpolating between
cg.snap->player_state and cg.nextFrame->player_state
========================
*/
void CG_InterpolatePlayerState( qboolean grabAngles ) {
float f;
int i;
playerState_t *out;
snapshot_t *prev, *next;
qboolean skip = qfalse;
vec3_t oldOrg;
out = &cg.predicted_player_state;
prev = cg.snap;
next = cg.nextSnap;
VectorCopy(out->origin,oldOrg);
*out = cg.snap->ps;
// if we are still allowing local input, short circuit the view angles
if ( grabAngles ) {
usercmd_t cmd;
int cmdNum;
cmdNum = cgi_GetCurrentCmdNumber();
cgi_GetUserCmd( cmdNum, &cmd );
skip = CG_CheckModifyUCmd( &cmd, out->viewangles );
if ( !skip )
{
//NULL so that it doesn't execute a block of code that must be run from game
PM_UpdateViewAngles( out, &cmd, NULL );
}
}
// if the next frame is a teleport, we can't lerp to it
if ( cg.nextFrameTeleport )
{
return;
}
if (!( !next || next->serverTime <= prev->serverTime ) )
{
f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
i = next->ps.bobCycle;
if ( i < prev->ps.bobCycle )
{
i += 256; // handle wraparound
}
out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
for ( i = 0 ; i < 3 ; i++ )
{
out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
if ( !grabAngles )
{
out->viewangles[i] = LerpAngle(
prev->ps.viewangles[i], next->ps.viewangles[i], f );
}
out->velocity[i] = prev->ps.velocity[i] +
f * (next->ps.velocity[i] - prev->ps.velocity[i] );
}
}
bool onPlat=false;
centity_t *pent=0;
if (out->groundEntityNum>0)
{
pent=&cg_entities[out->groundEntityNum];
if (pent->currentState.eType == ET_MOVER )
{
onPlat=true;
}
}
if (
cg.validPPS &&
cg_smoothPlayerPos.value>0.0f &&
cg_smoothPlayerPos.value<1.0f &&
!onPlat
)
{
// 0 = no smoothing, 1 = no movement
for (i=0;i<3;i++)
{
out->origin[i]=cg_smoothPlayerPos.value*(oldOrg[i]-out->origin[i])+out->origin[i];
}
}
else if (onPlat&&cg_smoothPlayerPlat.value>0.0f&&cg_smoothPlayerPlat.value<1.0f)
{
// if (cg.frametime<150)
// {
assert(pent);
vec3_t p1,p2,vel;
float lerpTime;
EvaluateTrajectory( &pent->currentState.pos,cg.snap->serverTime, p1 );
if ( cg.nextSnap &&cg.nextSnap->serverTime > cg.snap->serverTime && pent->nextState)
{
EvaluateTrajectory( &pent->nextState->pos,cg.nextSnap->serverTime, p2 );
lerpTime=float(cg.nextSnap->serverTime - cg.snap->serverTime);
}
else
{
EvaluateTrajectory( &pent->currentState.pos,cg.snap->serverTime+50, p2 );
lerpTime=50.0f;
}
float accel=cg_smoothPlayerPlatAccel.value*cg.frametime/lerpTime;
if (accel>20.0f)
{
accel=20.0f;
}
for (i=0;i<3;i++)
{
vel[i]=accel*(p2[i]-p1[i]);
}
VectorAdd(out->origin,vel,out->origin);
if (cg.validPPS &&
cg_smoothPlayerPlat.value>0.0f &&
cg_smoothPlayerPlat.value<1.0f
)
{
// 0 = no smoothing, 1 = no movement
for (i=0;i<3;i++)
{
out->origin[i]=cg_smoothPlayerPlat.value*(oldOrg[i]-out->origin[i])+out->origin[i];
}
}
// }
}
}
/*
===================
CG_TouchItem
===================
*/
void CG_TouchItem( centity_t *cent ) {
gitem_t *item;
// never pick an item up twice in a prediction
if ( cent->miscTime == cg.time ) {
return;
}
if ( !BG_PlayerTouchesItem( &cg.predicted_player_state, &cent->currentState, cg.time ) ) {
return;
}
if ( !BG_CanItemBeGrabbed( &cent->currentState, &cg.predicted_player_state ) ) {
return; // can't hold it
}
item = &bg_itemlist[ cent->currentState.modelindex ];
// grab it
AddEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predicted_player_state);
// remove it from the frame so it won't be drawn
cent->currentState.eFlags |= EF_NODRAW;
// don't touch it again this prediction
cent->miscTime = cg.time;
// if its a weapon, give them some predicted ammo so the autoswitch will work
if ( item->giType == IT_WEAPON ) {
int ammotype = weaponData[item->giTag].ammoIndex;
cg.predicted_player_state.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
if ( !cg.predicted_player_state.ammo[ ammotype] ) {
cg.predicted_player_state.ammo[ ammotype ] = 1;
}
}
}
/*
=========================
CG_TouchTriggerPrediction
Predict push triggers and items
Only called for the last command
=========================
*/
void CG_TouchTriggerPrediction( void ) {
int i;
trace_t trace;
entityState_t *ent;
clipHandle_t cmodel;
centity_t *cent;
qboolean spectator;
// dead clients don't activate triggers
if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 ) {
return;
}
spectator = ( cg.predicted_player_state.pm_type == PM_SPECTATOR );
if ( cg.predicted_player_state.pm_type != PM_NORMAL && !spectator ) {
return;
}
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
cent = &cg_entities[ cg.snap->entities[ i ].number ];
ent = &cent->currentState;
if ( ent->eType == ET_ITEM && !spectator ) {
CG_TouchItem( cent );
continue;
}
if ( ent->eType != ET_PUSH_TRIGGER && ent->eType != ET_TELEPORT_TRIGGER ) {
continue;
}
if ( ent->solid != SOLID_BMODEL ) {
continue;
}
cmodel = cgi_CM_InlineModel( ent->modelindex );
if ( !cmodel ) {
continue;
}
cgi_CM_BoxTrace( &trace, cg.predicted_player_state.origin, cg.predicted_player_state.origin,
cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
if ( !trace.startsolid ) {
continue;
}
if ( ent->eType == ET_TELEPORT_TRIGGER ) {
cg.hyperspace = qtrue;
} else {
// we hit this push trigger
if ( spectator ) {
continue;
}
VectorCopy( ent->origin2, cg.predicted_player_state.velocity );
}
}
}
/*
=================
CG_PredictPlayerState
Generates cg.predicted_player_state for the current cg.time
cg.predicted_player_state is guaranteed to be valid after exiting.
For normal gameplay, it will be the result of predicted usercmd_t on
top of the most recent playerState_t received from the server.
Each new refdef will usually have exactly one new usercmd over the last,
but we have to simulate all unacknowledged commands since the last snapshot
received. This means that on an internet connection, quite a few
pmoves may be issued each frame.
OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
differs from the predicted one.
We detect prediction errors and allow them to be decayed off over several frames
to ease the jerk.
=================
*/
extern qboolean player_locked;
void CG_PredictPlayerState( void ) {
int cmdNum, current;
playerState_t oldPlayerState;
cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
// if this is the first frame we must guarantee
// predicted_player_state is valid even if there is some
// other error condition
if ( !cg.validPPS ) {
cg.validPPS = qtrue;
cg.predicted_player_state = cg.snap->ps;
}
if ( 1 )//cg_timescale.value >= 1.0f )
{
// demo playback just copies the moves
/*
if ( (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
CG_InterpolatePlayerState( qfalse );
return;
}
*/
// non-predicting local movement will grab the latest angles
CG_InterpolatePlayerState( qtrue );
return;
}
// prepare for pmove
//FIXME: is this bad???
cg_pmove.gent = NULL;
cg_pmove.ps = &cg.predicted_player_state;
cg_pmove.trace = CG_Trace;
cg_pmove.pointcontents = CG_PointContents;
cg_pmove.tracemask = MASK_PLAYERSOLID;
cg_pmove.noFootsteps = 0;//( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
// save the state before the pmove so we can detect transitions
oldPlayerState = cg.predicted_player_state;
// if we are too far out of date, just freeze
cmdNum = cg.snap->cmdNum;
current = cgi_GetCurrentCmdNumber();
if ( current - cmdNum >= CMD_BACKUP ) {
return;
}
// get the most recent information we have
cg.predicted_player_state = cg.snap->ps;
// we should always be predicting at least one frame
if ( cmdNum >= current ) {
return;
}
// run cmds
do {
// check for a prediction error from last frame
// on a lan, this will often be the exact value
// from the snapshot, but on a wan we will have
// to predict several commands to get to the point
// we want to compare
if ( cmdNum == current - 1 ) {
vec3_t delta;
float len;
if ( cg.thisFrameTeleport ) {
// a teleport will not cause an error decay
VectorClear( cg.predictedError );
cg.thisFrameTeleport = qfalse;
} else {
vec3_t adjusted;
CG_AdjustPositionForMover( cg.predicted_player_state.origin,
cg.predicted_player_state.groundEntityNum, cg.oldTime, adjusted );
VectorSubtract( oldPlayerState.origin, adjusted, delta );
len = VectorLength( delta );
if ( len > 0.1 ) {
if ( cg_errorDecay.integer ) {
int t;
float f;
t = cg.time - cg.predictedErrorTime;
f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
if ( f < 0 ) {
f = 0;
}
VectorScale( cg.predictedError, f, cg.predictedError );
} else {
VectorClear( cg.predictedError );
}
VectorAdd( delta, cg.predictedError, cg.predictedError );
cg.predictedErrorTime = cg.oldTime;
}
}
}
// if the command can't be gotten because it is
// too far out of date, the frame is invalid
// this should never happen, because we check ranges at
// the top of the function
cmdNum++;
if ( !cgi_GetUserCmd( cmdNum, &cg_pmove.cmd ) ) {
break;
}
gentity_t *ent = &g_entities[0];//cheating and dirty, I know, but this is a SP game so prediction can cheat
if ( player_locked ||
(ent && !ent->s.number&&ent->aimDebounceTime>level.time) ||
(ent && ent->client && ent->client->ps.pm_time && (ent->client->ps.pm_flags&PMF_TIME_KNOCKBACK)) ||
(ent && ent->forcePushTime > level.time) )
{//lock out player control unless dead
//VectorClear( cg_pmove.cmd.angles );
cg_pmove.cmd.forwardmove = 0;
cg_pmove.cmd.rightmove = 0;
cg_pmove.cmd.buttons = 0;
cg_pmove.cmd.upmove = 0;
}
CG_CheckModifyUCmd( &cg_pmove.cmd, NULL );
//FIXME: prediction on clients in timescale results in jerky positional translation
Pmove( &cg_pmove );
// add push trigger movement effects
CG_TouchTriggerPrediction();
} while ( cmdNum < current );
// adjust for the movement of the groundentity
CG_AdjustPositionForMover( cg.predicted_player_state.origin,
cg.predicted_player_state.groundEntityNum,
cg.time, cg.predicted_player_state.origin );
// fire events and other transition triggered things
CG_TransitionPlayerState( &cg.predicted_player_state, &oldPlayerState );
}

214
code/cgame/cg_public.h Normal file
View File

@@ -0,0 +1,214 @@
#ifndef _CG_PUBLIC_H
#define _CG_PUBLIC_H
#define NUM_EXPLOSION_SHADERS 8
#define NUM_EXPLOSION_FRAMES 3
#define CMD_BACKUP 64
#define CMD_MASK (CMD_BACKUP - 1)
// allow a lot of command backups for very fast systems
// multiple commands may be combined into a single packet, so this
// needs to be larger than PACKET_BACKUP
#define MAX_ENTITIES_IN_SNAPSHOT 512
#define SNAPFLAG_RATE_DELAYED 1 // the server withheld a packet to save bandwidth
#define SNAPFLAG_DROPPED_COMMANDS 2 // the server lost some cmds coming from the client
// snapshots are a view of the server at a given time
// Snapshots are generated at regular time intervals by the server,
// but they may not be sent if a client's rate level is exceeded, or
// they may be dropped by the network.
struct snapshot_s
{
int snapFlags; // SNAPFLAG_RATE_DELAYED, SNAPFLAG_DROPPED_COMMANDS
int ping;
int serverTime; // server time the message is valid for (in msec)
byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
int cmdNum; // the next cmdNum the server is expecting
// client side prediction should start with this cmd
playerState_t ps; // complete information about the current player at this time
int numEntities; // all of the entities that need to be presented
entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot
int numConfigstringChanges; // configstrings that have changed since the last
int configstringNum; // acknowledged snapshot_t (which is usually NOT the previous snapshot!)
int numServerCommands; // text based server commands to execute when this
int serverCommandSequence; // snapshot becomes current
};
typedef snapshot_s snapshot_t;
/*
==================================================================
functions imported from the main executable
==================================================================
*/
#define CGAME_IMPORT_API_VERSION 4
typedef enum {
CG_PRINT,
CG_ERROR,
CG_MILLISECONDS,
CG_CVAR_REGISTER,
CG_CVAR_UPDATE,
CG_CVAR_SET,
CG_ARGC,
CG_ARGV,
CG_ARGS,
CG_FS_FOPENFILE,
CG_FS_READ,
CG_FS_WRITE,
CG_FS_FCLOSEFILE,
CG_SENDCONSOLECOMMAND,
CG_ADDCOMMAND,
CG_SENDCLIENTCOMMAND,
CG_UPDATESCREEN,
CG_RMG_INIT,
CG_CM_REGISTER_TERRAIN,
CG_RE_INIT_RENDERER_TERRAIN,
CG_CM_LOADMAP,
CG_CM_NUMINLINEMODELS,
CG_CM_INLINEMODEL,
CG_CM_TEMPBOXMODEL,
CG_CM_POINTCONTENTS,
CG_CM_TRANSFORMEDPOINTCONTENTS,
CG_CM_BOXTRACE,
CG_CM_TRANSFORMEDBOXTRACE,
CG_CM_MARKFRAGMENTS,
CG_CM_SNAPPVS,
CG_S_STARTSOUND,
CG_S_STARTLOCALSOUND,
CG_S_CLEARLOOPINGSOUNDS,
CG_S_ADDLOOPINGSOUND,
CG_S_STOPSOUNDS,
CG_S_UPDATEENTITYPOSITION,
CG_S_RESPATIALIZE,
CG_S_REGISTERSOUND,
CG_S_STARTBACKGROUNDTRACK,
#ifdef _IMMERSION
CG_FF_START,
CG_FF_STOP,
CG_FF_STOPALL,
CG_FF_SHAKE,
CG_FF_REGISTER,
CG_FF_ADDLOOPINGFORCE,
#else
CG_FF_STARTFX,
CG_FF_ENSUREFX,
CG_FF_STOPFX,
CG_FF_STOPALLFX,
#endif // _IMMERSION
#ifdef _XBOX
CG_FF_XBOX_SHAKE,
CG_FF_XBOX_DAMAGE,
#endif
CG_R_LOADWORLDMAP,
CG_R_REGISTERMODEL,
CG_R_REGISTERSKIN,
CG_R_REGISTERSHADER,
CG_R_REGISTERSHADERNOMIP,
CG_R_REGISTERFONT,
CG_R_FONTSTRLENPIXELS,
CG_R_FONTSTRLENCHARS,
CG_R_FONTHEIGHTPIXELS,
CG_R_FONTDRAWSTRING,
CG_LANGUAGE_ISASIAN,
CG_LANGUAGE_USESSPACES,
CG_ANYLANGUAGE_READFROMSTRING,
CG_R_SETREFRACTIONPROP,
CG_R_CLEARSCENE,
CG_R_ADDREFENTITYTOSCENE,
CG_R_INPVS,
CG_R_GETLIGHTING,
CG_R_ADDPOLYTOSCENE,
CG_R_ADDLIGHTTOSCENE,
CG_R_RENDERSCENE,
CG_R_SETCOLOR,
CG_R_DRAWSTRETCHPIC,
//CG_R_DRAWSCREENSHOT,
CG_R_MODELBOUNDS,
CG_R_LERPTAG,
CG_R_DRAWROTATEPIC,
CG_R_DRAWROTATEPIC2,
CG_R_SETRANGEFOG,
CG_R_LA_GOGGLES,
CG_R_SCISSOR,
CG_GETGLCONFIG,
CG_GETGAMESTATE,
CG_GETCURRENTSNAPSHOTNUMBER,
CG_GETSNAPSHOT,
CG_GETDEFAULTSTATE,
CG_GETSERVERCOMMAND,
CG_GETCURRENTCMDNUMBER,
CG_GETUSERCMD,
CG_SETUSERCMDVALUE,
CG_SETUSERCMDANGLES,
CG_S_UPDATEAMBIENTSET,
CG_S_ADDLOCALSET,
CG_AS_PARSESETS,
CG_AS_ADDENTRY,
CG_AS_GETBMODELSOUND,
CG_S_GETSAMPLELENGTH,
COM_SETORGANGLES,
/*
Ghoul2 Insert Start
*/
CG_G2_LISTBONES,
CG_G2_LISTSURFACES,
CG_G2_HAVEWEGHOULMODELS,
CG_G2_SETMODELS,
/*
Ghoul2 Insert End
*/
CG_R_GET_LIGHT_STYLE,
CG_R_SET_LIGHT_STYLE,
CG_R_GET_BMODEL_VERTS,
CG_R_WORLD_EFFECT_COMMAND,
CG_CIN_PLAYCINEMATIC,
CG_CIN_STOPCINEMATIC,
CG_CIN_RUNCINEMATIC,
CG_CIN_DRAWCINEMATIC,
CG_CIN_SETEXTENTS,
CG_Z_MALLOC,
CG_Z_FREE,
CG_UI_MENU_RESET,
CG_UI_MENU_NEW,
CG_UI_SETACTIVE_MENU,
CG_UI_MENU_OPENBYNAME,
CG_UI_PARSE_INT,
CG_UI_PARSE_STRING,
CG_UI_PARSE_FLOAT,
CG_UI_STARTPARSESESSION,
CG_UI_ENDPARSESESSION,
CG_UI_PARSEEXT,
CG_UI_MENUPAINT_ALL,
CG_UI_MENUCLOSE_ALL,
CG_UI_STRING_INIT,
CG_UI_GETMENUINFO,
CG_SP_GETSTRINGTEXTSTRING,
CG_UI_GETITEMTEXT,
CG_UI_GETITEMINFO,
} cgameImport_t;
//----------------------------------------------
#endif _CG_PUBLIC_H

View File

@@ -0,0 +1,187 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\objectives.h"
#include "..\game\b_local.h"
#include "..\ui\ui_shared.h"
#define SCOREBOARD_WIDTH (26*BIGCHAR_WIDTH)
/*
static void Scoreboard_Draw( void )
{
vec4_t newColor;
/*
player = g_entities[0];
if( player->client->ps.persistant[PERS_ACCURACY_SHOTS] ) {
accuracy = player->client->ps.persistant[PERS_ACCURACY_HITS] * 100 / player->client->ps.persistant[PERS_ACCURACY_SHOTS];
}
*/
/* cg.LCARSTextTime = 0; // Turn off LCARS screen
// Background
newColor[0] = colorTable[CT_BLACK][0];
newColor[1] = colorTable[CT_BLACK][1];
newColor[2] = colorTable[CT_BLACK][2];
newColor[3] = 0.5;
cgi_R_SetColor(newColor);
CG_DrawPic( 137, 73, 475, 300, cgs.media.whiteShader); // Background
CG_DrawPic( 120, 99, 18, 256, cgs.media.whiteShader); // Background
CG_DrawPic( 40, 94, 66, 266, cgs.media.whiteShader); // Background
// Right side box
cgi_R_SetColor( colorTable[CT_DKBROWN1]);
CG_DrawPic( 120, 354, 32, 32, cgs.media.status_corner_16_18);
CG_DrawPic( 94, 356, 16, 32, cgs.media.status_corner_8_16_b);
CG_DrawPic( 94, 73, 16, 32, cgs.media.status_corner_8_22);
CG_DrawPic(135,73, 302, 22, cgs.media.whiteShader); // Top
CG_DrawPic(120, 100, 18, 12, cgs.media.whiteShader); // Middle Top
CG_DrawPic(120, 353, 18, 4, cgs.media.whiteShader); // Middle Bottom
CG_DrawPic(130,357, 482, 18, cgs.media.whiteShader); // Bottom
// Left side box
cgi_R_SetColor( colorTable[CT_DKBROWN1]);
CG_DrawPic(40,73, 56, 22, cgs.media.whiteShader); // Top
CG_DrawPic(98,95, 8, 17, cgs.media.whiteShader); // Middle Top
CG_DrawPic(98,353, 8, 7, cgs.media.whiteShader); // Middle Bottom
CG_DrawPic(40,357, 58, 18, cgs.media.whiteShader); // Bottom
CG_DrawProportionalString( 356, 208, "%", UI_RIGHT | UI_SMALLFONT, colorTable[CT_LTPURPLE1] );
CG_DrawProportionalString( 610, 72, ingame_text[IGT_MISSIONANALYSIS],UI_RIGHT| CG_BIGFONT, colorTable[CT_LTORANGE] );
CG_PrintScreenGraphics(statsmenu_graphics,SMG_MAX);
}
*/
/*
=================
CG_MissionFailed
=================
*/
int statusTextIndex = -1;
void CG_MissionFailed(void)
{
char *text;
if (!cg.missionFailedScreen)
{
cgi_UI_SetActive_Menu("missionfailed_menu");
// If you're in camera mode when when you lose (Chewie kills you)
// then the above fails. This should check for that. We'll end up
// waiting until the camera stuff stops:
if( !Menu_GetFocused() )
return;
cg.missionFailedScreen = qtrue;
switch (statusTextIndex)
{
case -1: //Our HERO DIED!!!
text = "@SP_INGAME_MISSIONFAILED_PLAYER";
break;
case MISSIONFAILED_JAN:
text = "@SP_INGAME_MISSIONFAILED_JAN";
break;
case MISSIONFAILED_LUKE:
text = "@SP_INGAME_MISSIONFAILED_LUKE";
break;
case MISSIONFAILED_LANDO:
text = "@SP_INGAME_MISSIONFAILED_LANDO";
break;
case MISSIONFAILED_R5D2:
text = "@SP_INGAME_MISSIONFAILED_R5D2";
break;
case MISSIONFAILED_WARDEN:
text = "@SP_INGAME_MISSIONFAILED_WARDEN";
break;
case MISSIONFAILED_PRISONERS:
text = "@SP_INGAME_MISSIONFAILED_PRISONERS";
break;
case MISSIONFAILED_EMPLACEDGUNS:
text = "@SP_INGAME_MISSIONFAILED_EMPLACEDGUNS";
break;
case MISSIONFAILED_LADYLUCK:
text = "@SP_INGAME_MISSIONFAILED_LADYLUCK";
break;
case MISSIONFAILED_KYLECAPTURE:
text = "@SP_INGAME_MISSIONFAILED_KYLECAPTURE";
break;
case MISSIONFAILED_TOOMANYALLIESDIED:
text = "@SP_INGAME_MISSIONFAILED_TOOMANYALLIESDIED";
break;
case MISSIONFAILED_CHEWIE:
text = "@SP_INGAME_MISSIONFAILED_CHEWIE";
break;
case MISSIONFAILED_KYLE:
text = "@SP_INGAME_MISSIONFAILED_KYLE";
break;
case MISSIONFAILED_ROSH:
text = "@SP_INGAME_MISSIONFAILED_ROSH";
break;
case MISSIONFAILED_WEDGE:
text = "@SP_INGAME_MISSIONFAILED_WEDGE";
break;
case MISSIONFAILED_TURNED:
text = "@SP_INGAME_MISSIONFAILED_TURNED";
break;
default:
text = "@SP_INGAME_MISSIONFAILED_UNKNOWN";
break;
}
//done with the variable for this time so reset it.
statusTextIndex = -1;
gi.cvar_set("ui_missionfailed_text", text);
}
}
/*
=================
CG_DrawScoreboard
Draw the normal in-game scoreboard
return value is bool to NOT draw centerstring
=================
*/
qboolean CG_DrawScoreboard( void )
{
// don't draw anything if the menu is up
if ( cg_paused.integer )
{
return qfalse;
}
// Character is either dead, or a script has brought up the screen
if (((cg.predicted_player_state.pm_type == PM_DEAD) && (cg.missionStatusDeadTime < level.time))
|| (cg.missionStatusShow))
{
CG_MissionFailed();
return qtrue;
}
return qfalse;
}
void ScoreBoardReset(void)
{
}
//================================================================================

View File

@@ -0,0 +1,259 @@
// cg_servercmds.c -- text commands sent by the server
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "cg_lights.h"
/*
================
CG_ParseServerinfo
This is called explicitly when the gamestate is first received,
and whenever the server updates any serverinfo flagged cvars
================
*/
void CG_ParseServerinfo( void ) {
const char *info;
char *mapname;
info = CG_ConfigString( CS_SERVERINFO );
cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) );
cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) );
cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) );
cgs.maxclients = 1;
mapname = Info_ValueForKey( info, "mapname" );
Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
char *p = strrchr(mapname,'/');
strcpy( cgs.stripLevelName[0], p?p+1:mapname );
strupr( cgs.stripLevelName[0] );
for (int i=1; i<STRIPED_LEVELNAME_VARIATIONS; i++) // clear retry-array
{
cgs.stripLevelName[i][0]='\0';
}
// be careful with the []-numbers here. Currently I use 0,1,2 for replacements or substitution, and [3] for "INGAME"
// I know, if I'd known there was going to be this much messing about I'd have subroutinised it all and done it
// neater, but it kinda evolved... Feel free to bug me if you want to add to it... ? -Ste.
//
//FIXME: a better way to handle sound-matched strings from other levels (currently uses levelname+sound as key)
// additional String files needed for some levels...
//
// JKA...
if (!stricmp(cgs.stripLevelName[0],"YAVIN1B"))
{
strcpy( cgs.stripLevelName[1], "YAVIN1");
}
/* // JK2...
if (!stricmp(cgs.stripLevelName[0],"KEJIM_BASE") ||
!stricmp(cgs.stripLevelName[0],"KEJIM_POST")
)
{
strcpy( cgs.stripLevelName[1], "ARTUS_MINE" );
}
if (!stricmp(cgs.stripLevelName[0],"DOOM_DETENTION") ||
!stricmp(cgs.stripLevelName[0],"DOOM_SHIELDS")
)
{
strcpy( cgs.stripLevelName[1], "DOOM_COMM" );
}
if (!stricmp(cgs.stripLevelName[0],"DOOM_COMM"))
{
strcpy( cgs.stripLevelName[1], "CAIRN_BAY" );
}
if (!stricmp(cgs.stripLevelName[0],"NS_STARPAD"))
{
strcpy( cgs.stripLevelName[1], "ARTUS_TOPSIDE" ); // for dream sequence...
strcpy( cgs.stripLevelName[2], "BESPIN_UNDERCITY" ); // for dream sequence...
}
if (!stricmp(cgs.stripLevelName[0],"BESPIN_PLATFORM"))
{
strcpy( cgs.stripLevelName[1], "BESPIN_UNDERCITY" );
}
*/
}
/*
================
CG_ConfigStringModified
================
*/
void CG_RegisterClientModels (int entityNum);
extern void cgi_R_WorldEffectCommand( const char *command );
static void CG_ConfigStringModified( void ) {
const char *str;
int num;
num = atoi( CG_Argv( 1 ) );
// get the gamestate from the client system, which will have the
// new configstring already integrated
cgi_GetGameState( &cgs.gameState );
// look up the individual string that was modified
str = CG_ConfigString( num );
// do something with it if necessary
if ( num == CS_ITEMS ) {
int i;
for ( i = 1 ; i < bg_numItems ; i++ ) {
if ( str[ i ] == '1' )
{
if (bg_itemlist[i].classname)
{
CG_RegisterItemSounds( i );
CG_RegisterItemVisuals( i );
}
}
}
}
else if ( num == CS_MUSIC ) {
CG_StartMusic( qtrue );
} else if ( num == CS_SERVERINFO ) {
CG_ParseServerinfo();
} else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) {
cgs.model_draw[ num-CS_MODELS ] = cgi_R_RegisterModel( str );
// OutputDebugString(va("### CG_ConfigStringModified(): cgs.model_draw[%d] = \"%s\"\n",num-CS_MODELS,str));
// GHOUL2 Insert start
} else if ( num >= CS_CHARSKINS && num < CS_CHARSKINS+MAX_CHARSKINS ) {
cgs.skins[ num-CS_CHARSKINS ] = cgi_R_RegisterSkin( str );
// Ghoul2 Insert end
} else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) {
if ( str[0] != '*' ) {
cgs.sound_precache[ num-CS_SOUNDS] = cgi_S_RegisterSound( str );
}
}
else if ( num >= CS_EFFECTS && num < CS_EFFECTS + MAX_FX )
{
theFxScheduler.RegisterEffect( str );
}
else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) {
CG_NewClientinfo( num - CS_PLAYERS );
CG_RegisterClientModels( num - CS_PLAYERS );
}
else if ( num >= CS_LIGHT_STYLES && num < CS_LIGHT_STYLES + (MAX_LIGHT_STYLES*3))
{
CG_SetLightstyle(num - CS_LIGHT_STYLES);
}
else if ( num >= CS_WORLD_FX && num < CS_WORLD_FX + MAX_WORLD_FX )
{
cgi_R_WorldEffectCommand( str );
}
}
/*
=================
CG_ServerCommand
The string has been tokenized and can be retrieved with
Cmd_Argc() / Cmd_Argv()
=================
*/
static void CG_ServerCommand( void ) {
const char *cmd;
cmd = CG_Argv(0);
if ( !strcmp( cmd, "cp" ) ) {
CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.25 );
return;
}
if ( !strcmp( cmd, "cs" ) ) {
CG_ConfigStringModified();
return;
}
if ( !strcmp( cmd, "print" ) ) {
CG_Printf( "%s", CG_Argv(1) );
return;
}
if ( !strcmp( cmd, "chat" ) ) {
// cgi_S_StartLocalSound ( cgs.media.talkSound, CHAN_LOCAL_SOUND );
CG_Printf( "%s\n", CG_Argv(1) );
return;
}
// Scroll text
if ( !strcmp( cmd, "st" ) )
{
#ifdef _XBOX
if(cg.widescreen)
CG_ScrollText( CG_Argv(1), 720 - 16 );
else
#endif
CG_ScrollText( CG_Argv(1), SCREEN_WIDTH - 16 );
return;
}
// Cinematic text
if ( !strcmp( cmd, "ct" ) )
{
CG_CaptionText( CG_Argv(1), cgs.sound_precache[atoi(CG_Argv(2))], SCREEN_HEIGHT * 0.25 );
return;
}
// Text stop
if ( !strcmp( cmd, "cts" ) )
{
CG_CaptionTextStop();
return;
}
// Text to appear in center of screen with an LCARS frame around it.
if ( !strcmp( cmd, "lt" ) )
{
CG_Printf("CG_LCARSText() being called. Tell Ste\nString: \"%s\"\n",CG_Argv(1));
return;
}
// clientLevelShot is sent before taking a special screenshot for
// the menu system during development
if ( !strcmp( cmd, "clientLevelShot" ) ) {
cg.levelShot = qtrue;
return;
}
if ( !strcmp( cmd, "vmsg" ) ) {
#if 0
char snd[MAX_QPATH];
Com_sprintf(snd, sizeof(snd),
"sound/teamplay/vmsg/%s.wav", CG_Argv(1) );
cgi_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO,
cgi_S_RegisterSound (snd) );
#endif
return;
}
CG_Printf( "Unknown client game command: %s\n", cmd );
}
/*
====================
CG_ExecuteNewServerCommands
Execute all of the server commands that were received along
with this this snapshot.
====================
*/
void CG_ExecuteNewServerCommands( int latestSequence ) {
while ( cgs.serverCommandSequence < latestSequence ) {
if ( cgi_GetServerCommand( ++cgs.serverCommandSequence ) ) {
CG_ServerCommand();
}
}
}

407
code/cgame/cg_snapshot.cpp Normal file
View File

@@ -0,0 +1,407 @@
// cg_snapshot.c -- things that happen on snapshot transition,
// not necessarily every single frame
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
/*
==================
CG_ResetEntity
==================
*/
void CG_ResetEntity( centity_t *cent ) {
// if an event is set, assume it is new enough to use
// if the event had timed out, it would have been cleared
cent->previousEvent = 0;
// cent->trailTime = cg.snap->serverTime;
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
VectorCopy (cent->currentState.angles, cent->lerpAngles);
if ( cent->currentState.eType == ET_PLAYER ) {
CG_ResetPlayerEntity( cent );
}
}
/*
===============
CG_TransitionEntity
cent->nextState is moved to cent->currentState and events are fired
===============
*/
void CG_TransitionEntity( centity_t *cent ) {
if (cent->nextState) {
cent->currentState = *cent->nextState;
}
cent->currentValid = qtrue;
// reset if the entity wasn't in the last frame or was teleported
if ( !cent->interpolate ) {
CG_ResetEntity( cent );
}
// clear the next state. if will be set by the next CG_SetNextSnap
cent->interpolate = qfalse;
if ( cent->currentState.number != 0 )
{
// check for events
CG_CheckEvents( cent );
}
}
/*
==================
CG_SetInitialSnapshot
This will only happen on the very first snapshot, or
on tourney restarts. All other times will use
CG_TransitionSnapshot instead.
==================
*/
void CG_SetInitialSnapshot( snapshot_t *snap ) {
int i;
centity_t *cent;
entityState_t *state;
cg.snap = snap;
// sort out solid entities
//CG_BuildSolidList();
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
// set our local weapon selection pointer to
// what the server has indicated the current weapon is
CG_Respawn();
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
state = &cg.snap->entities[ i ];
cent = &cg_entities[ state->number ];
cent->currentState = *state;
cent->interpolate = qfalse;
cent->currentValid = qtrue;
CG_ResetEntity( cent );
// check for events
CG_CheckEvents( cent );
}
}
/*
===================
CG_TransitionSnapshot
The transition point from snap to nextSnap has passed
===================
*/
void CG_TransitionSnapshot( void ) {
centity_t *cent;
snapshot_t *oldFrame;
int i;
if ( !cg.snap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
}
if ( !cg.nextSnap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
}
// execute any server string commands before transitioning entities
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
// clear the currentValid flag for all entities in the existing snapshot
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
cent = &cg_entities[ cg.snap->entities[ i ].number ];
cent->currentValid = qfalse;
}
// move nextSnap to snap and do the transitions
oldFrame = cg.snap;
cg.snap = cg.nextSnap;
// sort out solid entities
//CG_BuildSolidList();
for ( i = 0 ; i < cg.snap->numEntities ; i++ )
{
if ( 1 ) //cg.snap->entities[ i ].number != 0 ) // I guess the player adds his/her events elsewhere, so doing this also gives us double events for the player!
{
cent = &cg_entities[ cg.snap->entities[ i ].number ];
CG_TransitionEntity( cent );
}
}
cg.nextSnap = NULL;
// check for playerstate transition events
if ( oldFrame ) {
// if we are not doing client side movement prediction for any
// reason, then the client events and view changes will be issued now
//if ( cg_timescale.value >= 1.0f )
{
CG_TransitionPlayerState( &cg.snap->ps, &oldFrame->ps );
}
}
}
/*
===============
CG_SetEntityNextState
Determine if the entity can be interpolated between the states
present in cg.snap and cg,nextSnap
===============
*/
void CG_SetEntityNextState( centity_t *cent, entityState_t *state ) {
cent->nextState = state;
// since we can't interpolate ghoul2 stuff from one frame to another, I'm just going to copy the ghoul2 info directly into the current state now
// CGhoul2Info *currentModel = &state->ghoul2[1];
// cent->gent->ghoul2 = state->ghoul2;
// CGhoul2Info *newModel = &cent->gent->ghoul2[1];
// if this frame is a teleport, or the entity wasn't in the
// previous frame, don't interpolate
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ state->eFlags ) & EF_TELEPORT_BIT ) ) {
cent->interpolate = qfalse;
} else {
cent->interpolate = qtrue;
}
}
/*
===================
CG_SetNextSnap
A new snapshot has just been read in from the client system.
===================
*/
void CG_SetNextSnap( snapshot_t *snap ) {
int num;
entityState_t *es;
centity_t *cent;
cg.nextSnap = snap;
// check for extrapolation errors
for ( num = 0 ; num < snap->numEntities ; num++ ) {
es = &snap->entities[num];
cent = &cg_entities[ es->number ];
CG_SetEntityNextState( cent, es );
}
// if the next frame is a teleport for the playerstate,
if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
cg.nextFrameTeleport = qtrue;
} else {
cg.nextFrameTeleport = qfalse;
}
}
/*
========================
CG_ReadNextSnapshot
This is the only place new snapshots are requested
This may increment cg.processedSnapshotNum multiple
times if the client system fails to return a
valid snapshot.
========================
*/
snapshot_t *CG_ReadNextSnapshot( void ) {
qboolean r;
snapshot_t *dest;
while ( cg.processedSnapshotNum < cg.latestSnapshotNum ) {
// decide which of the two slots to load it into
if ( cg.snap == &cg.activeSnapshots[0] ) {
dest = &cg.activeSnapshots[1];
} else {
dest = &cg.activeSnapshots[0];
}
// try to read the snapshot from the client system
cg.processedSnapshotNum++;
r = cgi_GetSnapshot( cg.processedSnapshotNum, dest );
// if it succeeded, return
if ( r ) {
return dest;
}
// a GetSnapshot will return failure if the snapshot
// never arrived, or is so old that its entities
// have been shoved off the end of the circular
// buffer in the client system.
// record as a dropped packet
// CG_AddLagometerSnapshotInfo( NULL );
// If there are additional snapshots, continue trying to
// read them.
}
// nothing left to read
return NULL;
}
/*
=================
CG_RestartLevel
A tournement restart will clear everything, but doesn't
require a reload of all the media
=================
*/
extern void CG_LinkCentsToGents(void);
static void CG_RestartLevel( void ) {
int snapshotNum;
int r;
snapshotNum = cg.processedSnapshotNum;
memset( cg_entities, 0, sizeof( cg_entities ) );
CG_Init_CG();
CG_LinkCentsToGents();
CG_InitLocalEntities();
CG_InitMarkPolys();
// regrab the first snapshot of the restart
cg.processedSnapshotNum = snapshotNum;
r = cgi_GetSnapshot( cg.processedSnapshotNum, &cg.activeSnapshots[0] );
if ( !r ) {
CG_Error( "cgi_GetSnapshot failed on restart" );
}
CG_SetInitialSnapshot( &cg.activeSnapshots[0] );
cg.time = cg.snap->serverTime;
}
/*
============
CG_ProcessSnapshots
We are trying to set up a renderable view, so determine
what the simulated time is, and try to get snapshots
both before and after that time if available.
If we don't have a valid cg.snap after exiting this function,
then a 3D game view cannot be rendered. This should only happen
right after the initial connection. After cg.snap has been valid
once, it will never turn invalid.
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
hasn't arrived yet (it becomes an extrapolating situation instead
of an interpolating one)
============
*/
void CG_ProcessSnapshots( void ) {
snapshot_t *snap;
int n;
qboolean newSnapshots;
// see what the latest snapshot the client system has is
cgi_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
if ( n != cg.latestSnapshotNum ) {
if ( n < cg.latestSnapshotNum ) {
// this should never happen
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
}
cg.latestSnapshotNum = n;
newSnapshots = qtrue;
} else {
newSnapshots = qfalse;
}
// If we have yet to receive a snapshot, check for it.
// Once we have gotten the first snapshot, cg.snap will
// always have valid data for the rest of the game
if ( !cg.snap ) {
snap = CG_ReadNextSnapshot();
if ( !snap ) {
// we can't continue until we get a snapshot
return;
}
// set our weapon selection to what
// the playerstate is currently using
CG_SetInitialSnapshot( snap );
}
// loop until we either have a valid nextSnap with a serverTime
// greater than cg.time to interpolate towards, or we run
// out of available snapshots
do {
// if we don't have a nextframe, try to read a new one in
if ( !cg.nextSnap ) {
snap = CG_ReadNextSnapshot();
// if we still don't have a nextframe, we will just have to
// extrapolate
if ( !snap ) {
break;
}
CG_SetNextSnap( snap );
// if time went backwards, we have a level restart
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
// restart the level
CG_RestartLevel();
continue; // we might also get a nextsnap
}
}
// if our time is < nextFrame's, we have a nice interpolating state
if ( cg.time < cg.nextSnap->serverTime ) {
break;
}
// we have passed the transition from nextFrame to frame
CG_TransitionSnapshot();
} while ( 1 );
if ( cg.snap->serverTime > cg.time )
{
cg.time=cg.snap->serverTime;
#if _DEBUG
Com_Printf("CG_ProcessSnapshots: cg.snap->serverTime > cg.time");
#endif
}
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time )
{
cg.time=cg.nextSnap->serverTime-1;
#if _DEBUG
Com_Printf("CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time");
#endif
}
// assert our valid conditions upon exiting
if ( cg.snap == NULL ) {
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
}
if ( cg.snap->serverTime > cg.time ) {
CG_Error( "CG_ProcessSnapshots: cg.snap->serverTime > cg.time" );
}
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
}
}

646
code/cgame/cg_syscalls.cpp Normal file
View File

@@ -0,0 +1,646 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
// this file is only included when building a dll
#ifdef _IMMERSION
#include "../ff/ff.h"
#else
///////////////////// this is a bit kludgy, but it only gives access to one
// enum table because of the #define. May get changed.
#define CGAME_ONLY
#include "../client/fffx.h"
//
/////////////////////
#endif // _IMMERSION
//prototypes
extern void CG_PreInit();
static int (*syscall)( int arg, ... ) = (int (*)( int, ...))-1;
#ifdef _XBOX
void cg_dllEntry( int (*syscallptr)( int arg,... ) ) {
#else
void dllEntry( int (*syscallptr)( int arg,... ) ) {
#endif
syscall = syscallptr;
CG_PreInit();
}
inline int PASSFLOAT( float x ) {
float floatTemp;
floatTemp = x;
return *(int *)&floatTemp;
}
void cgi_Printf( const char *fmt ) {
syscall( CG_PRINT, fmt );
}
void cgi_Error( const char *fmt ) {
syscall( CG_ERROR, fmt );
}
int cgi_Milliseconds( void ) {
return syscall( CG_MILLISECONDS );
}
void cgi_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) {
syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags );
}
void cgi_Cvar_Update( vmCvar_t *vmCvar ) {
syscall( CG_CVAR_UPDATE, vmCvar );
}
void cgi_Cvar_Set( const char *var_name, const char *value ) {
syscall( CG_CVAR_SET, var_name, value );
}
int cgi_Argc( void ) {
return syscall( CG_ARGC );
}
void cgi_Argv( int n, char *buffer, int bufferLength ) {
syscall( CG_ARGV, n, buffer, bufferLength );
}
void cgi_Args( char *buffer, int bufferLength ) {
syscall( CG_ARGS, buffer, bufferLength );
}
int cgi_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) {
return syscall( CG_FS_FOPENFILE, qpath, f, mode );
}
int cgi_FS_Read( void *buffer, int len, fileHandle_t f ) {
return syscall( CG_FS_READ, buffer, len, f );
}
int cgi_FS_Write( const void *buffer, int len, fileHandle_t f ) {
return syscall( CG_FS_WRITE, buffer, len, f );
}
void cgi_FS_FCloseFile( fileHandle_t f ) {
syscall( CG_FS_FCLOSEFILE, f );
}
void cgi_SendConsoleCommand( const char *text ) {
syscall( CG_SENDCONSOLECOMMAND, text );
}
void cgi_AddCommand( const char *cmdName ) {
syscall( CG_ADDCOMMAND, cmdName );
}
void cgi_SendClientCommand( const char *s ) {
syscall( CG_SENDCLIENTCOMMAND, s );
}
void cgi_UpdateScreen( void ) {
syscall( CG_UPDATESCREEN );
}
//RMG BEGIN
void cgi_RMG_Init(int terrainID, const char *terrainInfo)
{
syscall( CG_RMG_INIT, terrainID, terrainInfo);
}
int cgi_CM_RegisterTerrain(const char *terrainInfo)
{
return syscall( CG_CM_REGISTER_TERRAIN, terrainInfo);
}
void cgi_RE_InitRendererTerrain( const char *terrainInfo )
{
syscall(CG_RE_INIT_RENDERER_TERRAIN, terrainInfo);
}
//RMG END
void cgi_CM_LoadMap( const char *mapname, qboolean subBSP ) {
syscall( CG_CM_LOADMAP, mapname, subBSP );
}
int cgi_CM_NumInlineModels( void ) {
return syscall( CG_CM_NUMINLINEMODELS );
}
clipHandle_t cgi_CM_InlineModel( int index ) {
return syscall( CG_CM_INLINEMODEL, index );
}
clipHandle_t cgi_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) {//, const int contents ) {
return syscall( CG_CM_TEMPBOXMODEL, mins, maxs );//, contents );
}
int cgi_CM_PointContents( const vec3_t p, clipHandle_t model ) {
return syscall( CG_CM_POINTCONTENTS, p, model );
}
int cgi_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) {
return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles );
}
void cgi_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask ) {
syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask );
}
void cgi_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask,
const vec3_t origin, const vec3_t angles ) {
syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
}
int cgi_CM_MarkFragments( int numPoints, const vec3_t *points,
const vec3_t projection,
int maxPoints, vec3_t pointBuffer,
int maxFragments, markFragment_t *fragmentBuffer ) {
return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer );
}
void cgi_CM_SnapPVS(vec3_t origin,byte *buffer)
{
syscall(CG_CM_SNAPPVS,origin,buffer);
}
void cgi_S_StopSounds( void )
{
syscall( CG_S_STOPSOUNDS);
}
void cgi_S_StartSound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) {
syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx );
}
void cgi_AS_ParseSets( void ) {
syscall( CG_AS_PARSESETS );
}
void cgi_AS_AddPrecacheEntry( const char *name ) {
syscall( CG_AS_ADDENTRY, name );
}
void cgi_S_UpdateAmbientSet( const char *name, vec3_t origin ) {
syscall( CG_S_UPDATEAMBIENTSET, name, origin );
}
int cgi_S_AddLocalSet( const char *name, vec3_t listener_origin, vec3_t origin, int entID, int time ) {
return syscall( CG_S_ADDLOCALSET, name, listener_origin, origin, entID, time );
}
sfxHandle_t cgi_AS_GetBModelSound( const char *name, int stage ) {
return syscall( CG_AS_GETBMODELSOUND, name, stage );
}
void cgi_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) {
syscall( CG_S_STARTLOCALSOUND, sfx, channelNum );
}
void cgi_S_ClearLoopingSounds( void ) {
syscall( CG_S_CLEARLOOPINGSOUNDS );
}
void cgi_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, soundChannel_t chan ) {
syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx, chan );
}
void cgi_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin );
}
void cgi_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], qboolean inwater ) {
syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater );
}
sfxHandle_t cgi_S_RegisterSound( const char *sample ) {
return syscall( CG_S_REGISTERSOUND, sample );
}
void cgi_S_StartBackgroundTrack( const char *intro, const char *loop, qboolean bForceStart ) {
syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop, bForceStart );
}
float cgi_S_GetSampleLength( sfxHandle_t sfx ) {
return syscall( CG_S_GETSAMPLELENGTH, sfx);
}
#ifdef _IMMERSION
void cgi_FF_Start( ffHandle_t ff, int clientNum ){
syscall( CG_FF_START, ff, clientNum );
}
void cgi_FF_Stop( ffHandle_t ff, int clientNum ){
syscall( CG_FF_STOP, ff, clientNum );
}
void cgi_FF_StopAll( void ){
syscall( CG_FF_STOPALL );
}
void cgi_FF_Shake( int intensity, int duration ){
syscall( CG_FF_SHAKE, intensity, duration );
}
ffHandle_t cgi_FF_Register( const char *name, int channel ){
return syscall( CG_FF_REGISTER, name, channel );
}
void cgi_FF_AddLoopingForce( ffHandle_t handle, int entNum ){
syscall( CG_FF_ADDLOOPINGFORCE, handle, entNum );
}
#else
void cgi_FF_StartFX( int iFX ){
syscall( CG_FF_STARTFX, iFX );
}
void cgi_FF_EnsureFX( int iFX ){
syscall( CG_FF_ENSUREFX, iFX );
}
void cgi_FF_StopFX( int iFX ){
syscall( CG_FF_STOPFX, iFX );
}
void cgi_FF_StopAllFX( void ){
syscall( CG_FF_STOPALLFX );
}
#endif // _IMMERSION
#ifdef _XBOX
void cgi_FF_Xbox_Shake( float intensity, int duration ){
syscall( CG_FF_XBOX_SHAKE, PASSFLOAT(intensity), duration );
}
void cgi_FF_Xbox_Damage( int damage, float xpos ) {
syscall( CG_FF_XBOX_DAMAGE, damage, PASSFLOAT(xpos) );
}
#endif
void cgi_R_LoadWorldMap( const char *mapname ) {
syscall( CG_R_LOADWORLDMAP, mapname );
}
qhandle_t cgi_R_RegisterModel( const char *name ) {
return syscall( CG_R_REGISTERMODEL, name );
}
qhandle_t cgi_R_RegisterSkin( const char *name ) {
return syscall( CG_R_REGISTERSKIN, name );
}
qhandle_t cgi_R_RegisterShader( const char *name ) {
qhandle_t hShader = syscall( CG_R_REGISTERSHADER, name );
assert (hShader);
return hShader;
}
qhandle_t cgi_R_RegisterShaderNoMip( const char *name ) {
return syscall( CG_R_REGISTERSHADERNOMIP, name );
}
qhandle_t cgi_R_RegisterFont( const char *name ) {
return syscall( CG_R_REGISTERFONT, name );
}
int cgi_R_Font_StrLenPixels(const char *text, const int iFontIndex, const float scale /*= 1.0f*/) {
return syscall( CG_R_FONTSTRLENPIXELS, text, iFontIndex, PASSFLOAT(scale) ) ;
}
int cgi_R_Font_StrLenChars(const char *text) {
return syscall( CG_R_FONTSTRLENCHARS, text ) ;
}
int cgi_R_Font_HeightPixels(const int iFontIndex, const float scale /*= 1.0f*/) {
return syscall( CG_R_FONTHEIGHTPIXELS, iFontIndex, PASSFLOAT(scale) );
}
qboolean cgi_Language_IsAsian( void )
{
return syscall( CG_LANGUAGE_ISASIAN );
}
qboolean cgi_Language_UsesSpaces(void)
{
return syscall( CG_LANGUAGE_USESSPACES );
}
unsigned int cgi_AnyLanguage_ReadCharFromString( const char *psText, int *piAdvanceCount, qboolean *pbIsTrailingPunctuation /* = NULL */ )
{
return syscall( CG_ANYLANGUAGE_READFROMSTRING, psText, piAdvanceCount, pbIsTrailingPunctuation );
}
void cgi_R_Font_DrawString(int ox, int oy, const char *text, const float *rgba, const int setIndex, int iMaxPixelWidth, const float scale /*= 1.0f*/) {
syscall (CG_R_FONTDRAWSTRING, ox, oy, text, rgba, setIndex, iMaxPixelWidth, PASSFLOAT(scale) );
}
//set some properties for the draw layer for my refractive effect (here primarily for mod authors) -rww
void cgi_R_SetRefractProp(float alpha, float stretch, qboolean prepost, qboolean negate)
{
syscall(CG_R_SETREFRACTIONPROP, PASSFLOAT(alpha), PASSFLOAT(stretch), prepost, negate);
}
void cgi_R_ClearScene( void ) {
syscall( CG_R_CLEARSCENE );
}
void cgi_R_AddRefEntityToScene( const refEntity_t *re ) {
syscall( CG_R_ADDREFENTITYTOSCENE, re );
}
qboolean cgi_R_inPVS( vec3_t p1, vec3_t p2 )
{
return syscall( CG_R_INPVS, p1, p2 );
}
void cgi_R_GetLighting( const vec3_t origin, vec3_t ambientLight, vec3_t directedLight, vec3_t ligthDir ) {
syscall( CG_R_GETLIGHTING, origin, ambientLight, directedLight, ligthDir );
}
void cgi_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) {
syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
}
void cgi_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
}
void cgi_R_RenderScene( const refdef_t *fd ) {
syscall( CG_R_RENDERSCENE, fd );
}
void cgi_R_SetColor( const float *rgba ) {
syscall( CG_R_SETCOLOR, rgba );
}
void cgi_R_DrawStretchPic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader ) {
syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader );
}
//void cgi_R_DrawScreenShot( float x, float y, float w, float h){
// syscall( CG_R_DRAWSCREENSHOT, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h) );
//}
void cgi_R_ModelBounds( qhandle_t model, vec3_t mins, vec3_t maxs ) {
syscall( CG_R_MODELBOUNDS, model, mins, maxs );
}
void cgi_R_LerpTag( orientation_t *tag, qhandle_t mod, int startFrame, int endFrame,
float frac, const char *tagName ) {
syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName );
}
void cgi_R_DrawRotatePic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2,float a, qhandle_t hShader )
{
syscall( CG_R_DRAWROTATEPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), PASSFLOAT(a), hShader );
}
void cgi_R_DrawRotatePic2( float x, float y, float w, float h,
float s1, float t1, float s2, float t2,float a, qhandle_t hShader )
{
syscall( CG_R_DRAWROTATEPIC2, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), PASSFLOAT(a), hShader );
}
//linear fogging, with settable range -rww
void cgi_R_SetRangeFog(float range)
{
syscall(CG_R_SETRANGEFOG, PASSFLOAT(range));
}
void cgi_R_LAGoggles( void )
{
syscall( CG_R_LA_GOGGLES );
}
void cgi_R_Scissor( float x, float y, float w, float h)
{
syscall( CG_R_SCISSOR, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h));
}
void cgi_GetGlconfig( glconfig_t *glconfig ) {
syscall( CG_GETGLCONFIG, glconfig );
}
void cgi_GetGameState( gameState_t *gamestate ) {
syscall( CG_GETGAMESTATE, gamestate );
}
void cgi_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime );
}
qboolean cgi_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot );
}
qboolean cgi_GetDefaultState(int entityIndex, entityState_t *state )
{
return syscall( CG_GETDEFAULTSTATE, entityIndex, state );
}
qboolean cgi_GetServerCommand( int serverCommandNumber ) {
return syscall( CG_GETSERVERCOMMAND, serverCommandNumber );
}
int cgi_GetCurrentCmdNumber( void ) {
return syscall( CG_GETCURRENTCMDNUMBER );
}
qboolean cgi_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
return syscall( CG_GETUSERCMD, cmdNumber, ucmd );
}
void cgi_SetUserCmdValue( int stateValue, float sensitivityScale, float mPitchOverride, float mYawOverride ) {
syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale), PASSFLOAT(mPitchOverride), PASSFLOAT(mYawOverride) );
}
void cgi_SetUserCmdAngles( float pitchOverride, float yawOverride, float rollOverride ) {
syscall( CG_SETUSERCMDANGLES, PASSFLOAT(pitchOverride), PASSFLOAT(yawOverride), PASSFLOAT(rollOverride) );
}
/*
Ghoul2 Insert Start
*/
// CG Specific API calls
void trap_G2_SetGhoul2ModelIndexes(CGhoul2Info_v &ghoul2, qhandle_t *modelList, qhandle_t *skinList)
{
syscall( CG_G2_SETMODELS, &ghoul2, modelList, skinList);
}
/*
Ghoul2 Insert End
*/
void trap_Com_SetOrgAngles(vec3_t org,vec3_t angles)
{
syscall(COM_SETORGANGLES,org,angles);
}
void trap_R_GetLightStyle(int style, color4ub_t color)
{
syscall(CG_R_GET_LIGHT_STYLE, style, color);
}
void trap_R_SetLightStyle(int style, int color)
{
syscall(CG_R_SET_LIGHT_STYLE, style, color);
}
void cgi_R_GetBModelVerts(int bmodelIndex, vec3_t *verts, vec3_t normal )
{
syscall( CG_R_GET_BMODEL_VERTS, bmodelIndex, verts, normal );
}
void cgi_R_WorldEffectCommand( const char *command )
{
syscall( CG_R_WORLD_EFFECT_COMMAND, command );
}
// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate)
int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */) {
return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits, psAudioFile);
}
// stops playing the cinematic and ends it. should always return FMV_EOF
// cinematics must be stopped in reverse order of when they are started
e_status trap_CIN_StopCinematic(int handle) {
return (e_status) syscall(CG_CIN_STOPCINEMATIC, handle);
}
// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached.
e_status trap_CIN_RunCinematic (int handle) {
return (e_status) syscall(CG_CIN_RUNCINEMATIC, handle);
}
// draws the current frame
void trap_CIN_DrawCinematic (int handle) {
syscall(CG_CIN_DRAWCINEMATIC, handle);
}
// allows you to resize the animation dynamically
void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) {
syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h);
}
void *cgi_Z_Malloc( int size, int tag )
{
return (void *)syscall(CG_Z_MALLOC,size,tag);
}
void cgi_Z_Free( void *ptr )
{
syscall(CG_Z_FREE,ptr);
}
void cgi_UI_SetActive_Menu(char *name)
{
syscall(CG_UI_SETACTIVE_MENU,name);
}
void cgi_UI_Menu_OpenByName(char *buf)
{
syscall(CG_UI_MENU_OPENBYNAME,buf);
}
void cgi_UI_Menu_Reset(void)
{
syscall(CG_UI_MENU_RESET);
}
void cgi_UI_Menu_New(char *buf)
{
syscall(CG_UI_MENU_NEW,buf);
}
void cgi_UI_Parse_Int(int *value)
{
syscall(CG_UI_PARSE_INT,value);
}
void cgi_UI_Parse_String(char *buf)
{
syscall(CG_UI_PARSE_STRING,buf);
}
void cgi_UI_Parse_Float(float *value)
{
syscall(CG_UI_PARSE_FLOAT,value);
}
int cgi_UI_StartParseSession(char *menuFile,char **buf)
{
return(int) syscall(CG_UI_STARTPARSESESSION,menuFile,buf);
}
void cgi_UI_EndParseSession(char *buf)
{
syscall(CG_UI_ENDPARSESESSION,buf);
}
void cgi_UI_ParseExt(char **token)
{
syscall(CG_UI_PARSEEXT,token);
}
void cgi_UI_MenuCloseAll(void)
{
syscall(CG_UI_MENUCLOSE_ALL);
}
void cgi_UI_MenuPaintAll(void)
{
syscall(CG_UI_MENUPAINT_ALL);
}
void cgi_UI_String_Init(void)
{
syscall(CG_UI_STRING_INIT);
}
int cgi_UI_GetMenuInfo(char *menuFile,int *x,int *y,int *w,int *h)
{
return(int) syscall(CG_UI_GETMENUINFO,menuFile,x,y,w,h);
}
int cgi_UI_GetMenuItemInfo(const char *menuFile,const char *itemName, int *x,int *y,int *w,int *h,vec4_t color,qhandle_t *background)
{
return(int) syscall(CG_UI_GETITEMINFO,menuFile,itemName,x,y,w,h,color,background);
}
int cgi_UI_GetItemText(char *menuFile,char *itemName, char* text)
{
return(int) syscall(CG_UI_GETITEMTEXT,menuFile,itemName,text);
}
int cgi_SP_GetStringTextString(const char *text, char *buffer, int bufferLength)
{
return syscall( CG_SP_GETSTRINGTEXTSTRING, text, buffer, bufferLength );
}
int cgi_EndGame(void)
{
return syscall( CG_SENDCONSOLECOMMAND, "cam_disable; disconnect\n" );//; cinematic outcast
}

786
code/cgame/cg_text.cpp Normal file
View File

@@ -0,0 +1,786 @@
// cg_text.c --
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_media.h"
//int precacheWav_i; // Current high index of precacheWav array
//precacheWav_t precacheWav[MAX_PRECACHEWAV];
//int precacheText_i; // Current high index of precacheText array
//precacheText_t precacheText[MAX_PRECACHETEXT];
#define TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE 0.85
#define TEXT_SCREEN_WIDTH_FRACTION_INGAME 0.85
#define TEXT_SCREEN_HEIGHT_INGAME 0.15f
#define TEXT_CUTSCENE_Y_BOOST 18
#define NUDGE_PERCENTAGE 0.90
extern vec4_t textcolor_caption;
extern vec4_t textcolor_center;
extern vec4_t textcolor_scroll;
// display text in a supplied box, start at top left and going down by however many pixels I feel like internally,
// return value is NULL if all fitted, else char * of next char to continue from that didn't fit.
//
// (coords are in the usual 640x480 virtual space)...
//
// ( if you get the same char * returned as what you passed in, then none of it fitted at all (box too small) )
//
// this is execrable, and should NOT have had to've been done now, but...
//
float gfAdvanceHack = 0.0f; // MUST default to this
int giLinesOutput; // hack-city after release, only used by one function
//
const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
const char *psText, int iFontHandle, float fScale,
const vec4_t v4Color)
{
giLinesOutput = 0;
cgi_R_SetColor( v4Color );
// Setup a reasonable vertical spacing (taiwanese & japanese need 1.5 fontheight, so use that for all)...
//
const int iFontHeight = cgi_R_Font_HeightPixels(iFontHandle, fScale);
const int iFontHeightAdvance = (int) ( ((gfAdvanceHack == 0.0f) ? 1.5f : gfAdvanceHack) * (float) iFontHeight);
int iYpos = iBoxY; // start print pos
// this could probably be simplified now, but it was converted from something else I didn't originally write,
// and it works anyway so wtf...
//
const char *psCurrentTextReadPos = psText;
const char *psReadPosAtLineStart = psCurrentTextReadPos;
const char *psBestLineBreakSrcPos = psCurrentTextReadPos;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *psCurrentTextReadPos && (iYpos + iFontHeight < (iBoxY + iBoxHeight)) )
{
char sLineForDisplay[2048]; // ott
// construct a line...
//
psCurrentTextReadPos = psReadPosAtLineStart;
sLineForDisplay[0] = '\0';
while ( *psCurrentTextReadPos )
{
psLastGood_s = psCurrentTextReadPos;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psCurrentTextReadPos, &iAdvanceCount, &bIsTrailingPunctuation);
psCurrentTextReadPos += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && sLineForDisplay[0] == '\0')
{
psReadPosAtLineStart++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c",uiLetter & 0xFF));
}
if (uiLetter == '\n')
{
// explicit new line...
//
sLineForDisplay[ strlen(sLineForDisplay)-1 ] = '\0'; // kill the CR
psReadPosAtLineStart = psCurrentTextReadPos;
psBestLineBreakSrcPos = psCurrentTextReadPos;
break; // print this line
}
else
if ( cgi_R_Font_StrLenPixels(sLineForDisplay, iFontHandle, fScale) >= iBoxWidth )
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
{
// Special case, don't consider line breaking if you're on an asian punctuation char of
// a language that doesn't use spaces...
//
}
else
{
if (psBestLineBreakSrcPos == psReadPosAtLineStart)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it'psCurrentTextReadPos entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
sLineForDisplay[ psBestLineBreakSrcPos - psReadPosAtLineStart ] = '\0';
psReadPosAtLineStart = psCurrentTextReadPos = psBestLineBreakSrcPos;
break; // print this line
}
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
{
psBestLineBreakSrcPos = psCurrentTextReadPos;
}
}
// ... and print it...
//
cgi_R_Font_DrawString(iBoxX, iYpos, sLineForDisplay, v4Color, iFontHandle, -1, fScale);
iYpos += iFontHeightAdvance;
giLinesOutput++;
// and echo to console in dev mode...
//
if ( cg_developer.integer )
{
// Com_Printf( "%psCurrentTextReadPos\n", sLineForDisplay );
}
}
return psReadPosAtLineStart;
}
/*
===============================================================================
CAPTION TEXT
===============================================================================
*/
void CG_CaptionTextStop(void)
{
cg.captionTextTime = 0;
}
// try and get the correct StripEd text (with retry) for a given reference...
//
// returns 0 if failed, else strlen...
//
static int cg_SP_GetStringTextStringWithRetry( LPCSTR psReference, char *psDest, int iSizeofDest)
{
int iReturn;
if (psReference[0] == '#')
{
// then we know the striped package name is already built in, so do NOT try prepending anything else...
//
return cgi_SP_GetStringTextString( va("%s",psReference+1), psDest, iSizeofDest );
}
for (int i=0; i<STRIPED_LEVELNAME_VARIATIONS; i++)
{
if (cgs.stripLevelName[i][0]) // entry present?
{
iReturn = cgi_SP_GetStringTextString( va("%s_%s",cgs.stripLevelName[i],psReference), psDest, iSizeofDest );
if (iReturn)
{
return iReturn;
}
}
}
return 0;
}
// slightly confusingly, the char arg for this function is an audio filename of the form "path/path/filename",
// the "filename" part of which should be the same as the StripEd reference we're looking for in the current
// level's string package...
//
void CG_CaptionText( const char *str, int sound, int y )
{
const char *s, *holds;
int i;
int holdTime;
char text[8192]={0};
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
holds = strrchr(str,'/');
if (!holds)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_CaptionText given audio filename with no '/':'%s'\n",str);
#endif
return;
}
i = cg_SP_GetStringTextStringWithRetry( holds+1, text, sizeof(text) );
//ensure we found a match
if (!i)
{
#ifndef FINAL_BUILD
// we only care about some sound dirs...
if (!strnicmp(str,"sound/chars/",12)) // whichever language it is, it'll be pathed as english at this point
{
Com_Printf("WARNING: CG_CaptionText given invalid text key :'%s'\n",str);
}
else
{
// anything else is probably stuff we don't care about. It certainly shouldn't be speech, anyway
}
#endif
return;
}
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale)); // taiwanese & japanese need 1.5 fontheight spacing
int lineWidth;
cg.captionTextTime = cg.time;
if (in_camera) {
cg.captionTextY = SCREEN_HEIGHT - (client_camera.bar_height_dest/2)- TEXT_CUTSCENE_Y_BOOST; // ths is now a centre'd Y, not a start Y
#ifdef _XBOX
if(cg.widescreen)
lineWidth = 720 * TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE ;
else
#endif
lineWidth = SCREEN_WIDTH * TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE ;
} else { //get above the hud
cg.captionTextY = (int) (TEXT_SCREEN_HEIGHT_INGAME * ((float)SCREEN_HEIGHT - (float)fontHeight * 1.5f)); // do NOT move this, it has to fit in between the weapon HUD and the datapad update.
#ifdef _XBOX
if(cg.widescreen)
lineWidth = 720 * TEXT_SCREEN_WIDTH_FRACTION_INGAME ;
else
#endif
lineWidth = SCREEN_WIDTH * TEXT_SCREEN_WIDTH_FRACTION_INGAME ;
}
cg.captionTextCurrentLine = 0;
// count the number of lines for centering
cg.scrollTextLines = 1;
memset (cg.captionText, 0, sizeof(cg.captionText));
// Break into individual lines
i = 0; // this could be completely removed and replace by "cg.scrollTextLines-1", but wtf?
s=(const char*)&text;
// tai...
// s="賽卓哥爾博士已經安全了,我也把所有發現報告給「商店」。很不幸地,瑞士警局有些白癡發現了一些狀況,準備在機場逮捕亞歷西‧納克瑞得。他偽裝成外交使節,穿過了層層防備。現在他握有人質,並且威脅要散播病毒。根據最新的報告,納克瑞得以及他的黨羽已經完全佔據了機場。我受命來追捕納克瑞得以及救出所有人質。這並不容易。";
// kor...
// s="Wp:澗顫歜檜棻 詩萼. 斜菟檜 蜓フ渠煎 啻陛 澀ブ雖 晦渠ビ啊棻.澗顫歜檜棻 詩萼. 斜菟檜 蜓フ渠煎 啻陛 澀ブ雖 晦渠ビ啊棻.";
holds = s;
int iPlayingTimeMS = cgi_S_GetSampleLength(sound);
int iLengthInChars = strlen(s);//cgi_R_Font_StrLenChars(s); // strlen is also good for MBCS in this instance, since it's for timing
if (iLengthInChars == 0)
{
iLengthInChars = 1;
}
cg.captionLetterTime = iPlayingTimeMS / iLengthInChars;
const char *psBestLineBreakSrcPos = s;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *s )
{
psLastGood_s = s;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
s += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && cg.captionText[i][0] == '\0')
{
holds++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c",uiLetter & 0xFF));
}
if (uiLetter == '\n')
{
// explicit new line...
//
cg.captionText[i][ strlen(cg.captionText[i])-1 ] = '\0'; // kill the CR
i++;
holds = s;
psBestLineBreakSrcPos = s;
cg.scrollTextLines++;
}
else
if ( cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale) >= lineWidth)
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
{
// Special case, don't consider line breaking if you're on an asian punctuation char of
// a language that doesn't use spaces...
//
}
else
{
if (psBestLineBreakSrcPos == holds)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
cg.captionText[i][ psBestLineBreakSrcPos - holds ] = '\0';
holds = s = psBestLineBreakSrcPos;
i++;
cg.scrollTextLines++;
}
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
{
psBestLineBreakSrcPos = s;
}
}
// calc the length of time to hold each 2 lines of text on the screen.... presumably this works?
//
holdTime = strlen(cg.captionText[0]);
if (cg.scrollTextLines > 1)
{
holdTime += strlen(cg.captionText[1]); // strlen is also good for MBCS in this instance, since it's for timing
}
cg.captionNextTextTime = cg.time + (/*JLF nudge it forward*/NUDGE_PERCENTAGE * holdTime * cg.captionLetterTime);
cg.scrollTextTime = 0; // No scrolling during captions
//Echo to console in dev mode
if ( cg_developer.integer )
{
Com_Printf( "%s\n", cg.captionText[0] ); // ste: was [i], but surely sentence 0 is more useful than last?
}
}
void CG_DrawCaptionText(void)
{
int i;
int x, y, w;
int holdTime;
if ( !cg.captionTextTime )
{
return;
}
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
if (cg_skippingcin.integer != 0)
{
cg.captionTextTime = 0;
return;
}
if ( cg.captionNextTextTime < cg.time )
{
cg.captionTextCurrentLine += 2;
if (cg.captionTextCurrentLine >= cg.scrollTextLines)
{
cg.captionTextTime = 0;
return;
}
else
{
holdTime = strlen(cg.captionText[cg.captionTextCurrentLine]);
if (cg.scrollTextLines >= cg.captionTextCurrentLine)
{
// ( strlen is also good for MBCS in this instance, since it's for timing -ste)
//
holdTime += strlen(cg.captionText[cg.captionTextCurrentLine + 1]);
}
cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);//50);
}
}
// Give a color if one wasn't given
if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) &&
(textcolor_caption[2] == 0) && (textcolor_caption[3] == 0))
{
Vector4Copy( colorTable[CT_WHITE], textcolor_caption );
}
cgi_R_SetColor(textcolor_caption);
// Set Y of the first line (varies if only printing one line of text)
// (this all works, please don't mess with it)
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale));
const bool bPrinting2Lines = !!(cg.captionText[ cg.captionTextCurrentLine+1 ][0]);
y = cg.captionTextY - ( (float)fontHeight * (bPrinting2Lines ? 1 : 0.5f)); // captionTextY was a centered Y pos, not a top one
y -= cgi_Language_IsAsian() ? 0 : 4;
for (i= cg.captionTextCurrentLine;i< cg.captionTextCurrentLine + 2;++i)
{
w = cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale);
if (w)
{
x = (SCREEN_WIDTH-w) / 2;
cgi_R_Font_DrawString(x, y, cg.captionText[i], textcolor_caption, cgs.media.qhFontMedium, -1, fFontScale);
y += fontHeight;
}
}
cgi_R_SetColor( NULL );
}
/*
===============================================================================
SCROLL TEXT
===============================================================================
CG_ScrollText - split text up into seperate lines
'str' arg is StripEd string reference, eg "CREDITS_RAVEN"
*/
int giScrollTextPixelWidth = SCREEN_WIDTH;
void CG_ScrollText( const char *str, int iPixelWidth )
{
const char *s,*holds;
int i;//, len;//, numChars;
giScrollTextPixelWidth = iPixelWidth;
// first, ask the strlen of the final string...
//
i = cgi_SP_GetStringTextString( str, NULL, 0 );
//ensure we found a match
if (!i)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
#endif
return;
}
//
// malloc space to hold it...
//
char *psText = (char *) cgi_Z_Malloc( i+1, TAG_TEMP_WORKSPACE );
//
// now get the string...
//
i = cgi_SP_GetStringTextString( str, psText, i+1 );
//ensure we found a match
if (!i)
{
assert(0); // should never get here now, but wtf?
cgi_Z_Free(psText);
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
#endif
return;
}
cg.scrollTextTime = cg.time;
cg.printTextY = SCREEN_HEIGHT;
cg.scrollTextLines = 1;
s = psText;
i = 0;
holds = s;
const char *psBestLineBreakSrcPos = s;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *s )
{
psLastGood_s = s;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
s += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && cg.printText[i][0] == '\0')
{
holds++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c",uiLetter & 0xFF));
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ')
{
psBestLineBreakSrcPos = s;
}
if (uiLetter == '\n')
{
// explicit new line...
//
cg.printText[i][ strlen(cg.printText[i])-1 ] = '\0'; // kill the CR
i++;
assert (i < (sizeof(cg.printText)/sizeof(cg.printText[0])) );
if (i >= (sizeof(cg.printText)/sizeof(cg.printText[0])) )
{
break;
}
holds = s;
cg.scrollTextLines++;
}
else
if ( cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f) >= iPixelWidth)
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (psBestLineBreakSrcPos == holds)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
cg.printText[i][ psBestLineBreakSrcPos - holds ] = '\0';
holds = s = psBestLineBreakSrcPos;
i++;
assert (i < (sizeof(cg.printText)/sizeof(cg.printText[0])) );
cg.scrollTextLines++;
}
}
cg.captionTextTime = 0; // No captions during scrolling
cgi_Z_Free(psText);
}
// draws using [textcolor_scroll]...
//
#define SCROLL_LPM (1/50.0) // 1 line per 50 ms
void CG_DrawScrollText(void)
{
char *start;
int i;
int x,y;
const int fontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f)); // taiwanese & japanese need 1.5 fontheight spacing
if ( !cg.scrollTextTime )
{
return;
}
cgi_R_SetColor( textcolor_scroll );
y = cg.printTextY - (cg.time - cg.scrollTextTime) * SCROLL_LPM;
// cgi_R_Font_DrawString(320, 200, va("Scrolltext printing @ %d",y), colorTable[CT_LTGOLD1], cgs.media.qhFontMedium, -1, 1.0f);
// See if text has finished scrolling off screen
if ((y + cg.scrollTextLines * fontHeight) < 1)
{
cg.scrollTextTime = 0;
return;
}
for (i=0;i<cg.scrollTextLines;++i)
{
// Is this line off top of screen?
if ((y + ((i +1) * fontHeight)) < 1)
{
y += fontHeight;
continue;
}
// or past bottom of screen?
else if (y > SCREEN_HEIGHT)
{
break;
}
start = cg.printText[i];
// w = cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f);
// if (w)
{
x = (SCREEN_WIDTH - giScrollTextPixelWidth) / 2;
cgi_R_Font_DrawString(x,y, cg.printText[i], textcolor_scroll, cgs.media.qhFontMedium, -1, 1.0f);
y += fontHeight;
}
}
cgi_R_SetColor( NULL );
}
/*
===============================================================================
CENTER PRINTING
===============================================================================
*/
/*
==============
CG_CenterPrint
Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void CG_CenterPrint( const char *str, int y) {
char *s;
// Find text to match the str given
if (*str == '@')
{
int i;
i = cgi_SP_GetStringTextString( str+1, cg.centerPrint, sizeof(cg.centerPrint) );
if (!i)
{
Com_Printf (S_COLOR_RED"CG_CenterPrint: cannot find reference '%s' in StringPackage!\n",str);
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
}
}
else
{
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
}
cg.centerPrintTime = cg.time;
cg.centerPrintY = y;
// count the number of lines for centering
cg.centerPrintLines = 1;
s = cg.centerPrint;
while( *s ) {
if (*s == '\n')
cg.centerPrintLines++;
s++;
}
}
/*
===================
CG_DrawCenterString
===================
*/
void CG_DrawCenterString( void )
{
char *start;
int l;
int x, y, w;
float *color;
if ( !cg.centerPrintTime ) {
return;
}
color = CG_FadeColor( cg.centerPrintTime, 1000 * 3 );
if ( !color ) {
return;
}
if((textcolor_center[0] == 0) && (textcolor_center[1] == 0) &&
(textcolor_center[2] == 0) && (textcolor_center[3] == 0))
{
Vector4Copy( colorTable[CT_WHITE], textcolor_center );
}
start = cg.centerPrint;
const int fontHeight = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
y = cg.centerPrintY - (cg.centerPrintLines * fontHeight) / 2;
while ( 1 ) {
char linebuffer[1024];
// this is kind of unpleasant when dealing with MBCS, but...
//
const char *psString = start;
int iOutIndex = 0;
for ( l = 0; l < sizeof(linebuffer)-1; l++ ) {
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psString, &iAdvanceCount);
psString += iAdvanceCount;
if (!uiLetter || uiLetter == '\n'){
break;
}
if (uiLetter > 255)
{
linebuffer[iOutIndex++] = uiLetter >> 8;
linebuffer[iOutIndex++] = uiLetter & 0xFF;
}
else
{
linebuffer[iOutIndex++] = uiLetter & 0xFF;
}
}
linebuffer[iOutIndex++] = '\0';
w = cgi_R_Font_StrLenPixels(linebuffer, cgs.media.qhFontMedium, 1.0f);
x = ( SCREEN_WIDTH - w ) / 2;
cgi_R_Font_DrawString(x,y,linebuffer, textcolor_center, cgs.media.qhFontMedium, -1, 1.0f);
y += fontHeight;
while ( *start && ( *start != '\n' ) ) {
start++;
}
if ( !*start ) {
break;
}
start++;
}
}

2249
code/cgame/cg_view.cpp Normal file

File diff suppressed because it is too large Load Diff

3164
code/cgame/cg_weapons.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
// I am a dummy header file that takes the place of the PCH files in SoF2. This is done so that we can easily
// transport fx files amongst CHC, SoF2, and the effects editor...( If you are looking at this and have a better
// idea, just let me know what the better solution is. )
// The files can't be shared directly through Source Safe since the projects reside in different SS databases.
//
// - JD

View File

@@ -0,0 +1,82 @@
#ifndef __strip_objectives_h
#define __strip_objectives_h
/********************************************************************************
the objectives... duh...
********************************************************************************/
#define PACKAGE_OBJECTIVES 0x08
enum
{
OBJECTIVES_KEJIM_POST_OBJ1 = 0x0800,
OBJECTIVES_KEJIM_POST_OBJ2 = 0x0801,
OBJECTIVES_KEJIM_BASE_OBJ1 = 0x0802,
OBJECTIVES_KEJIM_BASE_OBJ2 = 0x0803,
OBJECTIVES_KEJIM_BASE_OBJ3 = 0x0804,
OBJECTIVES_ARTUS_MINE_OBJ1 = 0x0805,
OBJECTIVES_ARTUS_MINE_OBJ2 = 0x0806,
OBJECTIVES_ARTUS_MINE_OBJ3 = 0x0807,
OBJECTIVES_ARTUS_DETENTION_OBJ1 = 0x0808,
OBJECTIVES_ARTUS_DETENTION_OBJ2 = 0x0809,
OBJECTIVES_ARTUS_TOPSIDE_OBJ1 = 0x080a,
OBJECTIVES_ARTUS_TOPSIDE_OBJ2 = 0x080b,
OBJECTIVES_YAVIN_TEMPLE_OBJ1 = 0x080c,
OBJECTIVES_YAVIN_TRIAL_OBJ1 = 0x080d,
OBJECTIVES_YAVIN_TRIAL_OBJ2 = 0x080e,
OBJECTIVES_NS_STREETS_OBJ1 = 0x080f,
OBJECTIVES_NS_STREETS_OBJ2 = 0x0810,
OBJECTIVES_NS_STREETS_OBJ3 = 0x0811,
OBJECTIVES_NS_HIDEOUT_OBJ1 = 0x0812,
OBJECTIVES_NS_HIDEOUT_OBJ2 = 0x0813,
OBJECTIVES_NS_STARPAD_OBJ1 = 0x0814,
OBJECTIVES_NS_STARPAD_OBJ2 = 0x0815,
OBJECTIVES_NS_STARPAD_OBJ3 = 0x0816,
OBJECTIVES_NS_STARPAD_OBJ4 = 0x0817,
OBJECTIVES_NS_STARPAD_OBJ5 = 0x0818,
OBJECTIVES_BESPIN_UNDERCITY_OBJ1 = 0x0819,
OBJECTIVES_BESPIN_UNDERCITY_OBJ2 = 0x081a,
OBJECTIVES_BESPIN_STREETS_OBJ1 = 0x081b,
OBJECTIVES_BESPIN_STREETS_OBJ2 = 0x081c,
OBJECTIVES_BESPIN_PLATFORM_OBJ1 = 0x081d,
OBJECTIVES_BESPIN_PLATFORM_OBJ2 = 0x081e,
OBJECTIVES_CAIRN_BAY_OBJ1 = 0x081f,
OBJECTIVES_CAIRN_BAY_OBJ2 = 0x0820,
OBJECTIVES_CAIRN_ASSEMBLY_OBJ1 = 0x0821,
OBJECTIVES_CAIRN_REACTOR_OBJ1 = 0x0822,
OBJECTIVES_CAIRN_REACTOR_OBJ2 = 0x0823,
OBJECTIVES_CAIRN_DOCK1_OBJ1 = 0x0824,
OBJECTIVES_CAIRN_DOCK1_OBJ2 = 0x0825,
OBJECTIVES_DOOM_COMM_OBJ1 = 0x0826,
OBJECTIVES_DOOM_COMM_OBJ2 = 0x0827,
OBJECTIVES_DOOM_COMM_OBJ3 = 0x0828,
OBJECTIVES_DOOM_DETENTION_OBJ1 = 0x0829,
OBJECTIVES_DOOM_DETENTION_OBJ2 = 0x082a,
OBJECTIVES_DOOM_SHIELDS_OBJ1 = 0x082b,
OBJECTIVES_DOOM_SHIELDS_OBJ2 = 0x082c,
OBJECTIVES_YAVIN_SWAMP_OBJ1 = 0x082d,
OBJECTIVES_YAVIN_SWAMP_OBJ2 = 0x082e,
OBJECTIVES_YAVIN_CANYON_OBJ1 = 0x082f,
OBJECTIVES_YAVIN_CANYON_OBJ2 = 0x0830,
OBJECTIVES_YAVIN_COURTYARD_OBJ1 = 0x0831,
OBJECTIVES_YAVIN_COURTYARD_OBJ2 = 0x0832,
OBJECTIVES_YAVIN_FINAL_OBJ1 = 0x0833,
OBJECTIVES_KEJIM_POST_OBJ3 = 0x0834,
OBJECTIVES_KEJIM_POST_OBJ4 = 0x0835,
OBJECTIVES_KEJIM_POST_OBJ5 = 0x0836,
OBJECTIVES_ARTUS_DETENTION_OBJ3 = 0x0837,
OBJECTIVES_DOOM_COMM_OBJ4 = 0x0838,
OBJECTIVES_DOOM_SHIELDS_OBJ3 = 0x0839,
OBJECTIVES_DEMO_OBJ1 = 0x083a,
OBJECTIVES_DEMO_OBJ2 = 0x083b,
OBJECTIVES_DEMO_OBJ3 = 0x083c,
OBJECTIVES_DEMO_OBJ4 = 0x083d
};
#endif // __strip_objectives_h