summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Tetelman <kevlarman@gmail.com>2009-10-03 12:38:09 +0000
committerTim Angus <tim@ngus.net>2013-01-03 00:16:07 +0000
commit5ac8f1a7907d7c4d9ddd5ae30ddaff872ff32edd (patch)
tree4d76d153295a4bbb670a081ce207cedc3d404209
parent919e7694290b4ac307034a9e0bfd4e8ab41c4663 (diff)
* New zap (fixes #40)
FIXME: currently limited to 3 targets due to netcode restrictions FIXME: this code could probably use some general cleanup as well
-rw-r--r--src/cgame/cg_draw.c1
-rw-r--r--src/cgame/cg_ents.c105
-rw-r--r--src/game/bg_public.h1
-rw-r--r--src/game/g_active.c2
-rw-r--r--src/game/g_local.h2
-rw-r--r--src/game/g_main.c1
-rw-r--r--src/game/g_weapon.c279
-rw-r--r--src/game/tremulous.h9
8 files changed, 282 insertions, 118 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c
index 9469410f..82d0aff2 100644
--- a/src/cgame/cg_draw.c
+++ b/src/cgame/cg_draw.c
@@ -1905,7 +1905,6 @@ static void CG_DrawLagometer( rectDef_t *rect, float text_x, float text_y,
int color;
vec4_t adjustedColor;
float vscale;
- vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f };
char *ping;
if( cg.snap->ps.pm_type == PM_INTERMISSION )
diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c
index 54409aee..1cdcfde7 100644
--- a/src/cgame/cg_ents.c
+++ b/src/cgame/cg_ents.c
@@ -216,8 +216,6 @@ Add continuous entity effects, like local entity emission and lighting
*/
static void CG_EntityEffects( centity_t *cent )
{
- int i;
-
// update sound origins
CG_SetEntitySoundPosition( cent );
@@ -253,7 +251,7 @@ static void CG_EntityEffects( centity_t *cent )
if( CG_IsTrailSystemValid( &cent->muzzleTS ) )
{
- //FIXME hack to prevent tesla trails reaching too far
+ //FIXME hack to prevent tesla trails reaching too far
if( cent->currentState.eType == ET_BUILDABLE )
{
vec3_t front, back;
@@ -268,33 +266,6 @@ static void CG_EntityEffects( centity_t *cent )
if( cg.time > cent->muzzleTSDeathTime && CG_IsTrailSystemValid( &cent->muzzleTS ) )
CG_DestroyTrailSystem( &cent->muzzleTS );
}
-
-
- if( cent->currentState.eType == ET_PLAYER )
- {
- centity_t *pcent = cent;;
-
- // predicted entity doesn't have local cgame vars
- if( cent == &cg.predictedPlayerEntity )
- pcent = &cg_entities[ cg.clientNum ];
-
- for( i = 0; i <= 2; i++ )
- {
- if( CG_IsTrailSystemValid( &pcent->level2ZapTS[ i ] ) )
- {
- vec3_t front, back;
-
- CG_AttachmentPoint( &pcent->level2ZapTS[ i ]->frontAttachment, front );
- CG_AttachmentPoint( &pcent->level2ZapTS[ i ]->backAttachment, back );
-
- if( cg.time - pcent->level2ZapTime > 100 ||
- Distance( front, back ) > LEVEL2_AREAZAP_CUTOFF )
- {
- CG_DestroyTrailSystem( &pcent->level2ZapTS[ i ] );
- }
- }
- }
- }
}
@@ -831,6 +802,63 @@ void CG_LinkLocation( centity_t *cent )
/*
=========================
+CG_Lev2ZapChain
+=========================
+*/
+static void CG_Lev2ZapChain( centity_t *cent )
+{
+ int i;
+ entityState_t *es;
+ centity_t *source = NULL, *target = NULL;
+
+ es = &cent->currentState;
+
+ //FIXME: find a better way to send zap targets
+ for( i = 0; i <= 2; i++ )
+ {
+ switch( i )
+ {
+ case 0:
+ if( es->time <= 0 )
+ continue;
+
+ source = &cg_entities[ es->misc ];
+ target = &cg_entities[ es->time ];
+ break;
+
+ case 1:
+ if( es->time2 <= 0 )
+ continue;
+
+ source = &cg_entities[ es->time ];
+ target = &cg_entities[ es->time2 ];
+ break;
+
+ case 2:
+ if( es->constantLight <= 0 )
+ continue;
+
+ source = &cg_entities[ es->time ];
+ target = &cg_entities[ es->constantLight ];
+ break;
+
+ }
+
+ if( !CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
+
+ if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ {
+ CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->frontAttachment, source );
+ CG_SetAttachmentCent( &cent->level2ZapTS[ i ]->backAttachment, target );
+ CG_AttachToCent( &cent->level2ZapTS[ i ]->frontAttachment );
+ CG_AttachToCent( &cent->level2ZapTS[ i ]->backAttachment );
+ }
+ }
+}
+
+/*
+=========================
CG_AdjustPositionForMover
Also called by client movement prediction code
@@ -1025,8 +1053,21 @@ CG_CEntityPVSLeave
*/
static void CG_CEntityPVSLeave( centity_t *cent )
{
+ int i;
+ entityState_t *es = &cent->currentState;
+
if( cg_debugPVS.integer )
CG_Printf( "Entity %d left PVS\n", cent->currentState.number );
+ switch( es->eType )
+ {
+ case ET_LEV2_ZAP_CHAIN:
+ for( i = 0; i <= 2; i++ )
+ {
+ if( CG_IsTrailSystemValid( &cent->level2ZapTS[ i ] ) )
+ CG_DestroyTrailSystem( &cent->level2ZapTS[ i ] );
+ }
+ break;
+ }
}
@@ -1111,6 +1152,10 @@ static void CG_AddCEntity( centity_t *cent )
CG_LightFlare( cent );
break;
+ case ET_LEV2_ZAP_CHAIN:
+ CG_Lev2ZapChain( cent );
+ break;
+
case ET_LOCATION:
CG_LinkLocation( cent );
break;
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index 7f7f576f..2c0d1b31 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -1190,6 +1190,7 @@ typedef enum
ET_ANIMMAPOBJ,
ET_MODELDOOR,
ET_LIGHTFLARE,
+ ET_LEV2_ZAP_CHAIN,
ET_EVENTS // any of the EV_* events can be added freestanding
// by setting eType to ET_EVENTS + eventNum
diff --git a/src/game/g_active.c b/src/game/g_active.c
index c74dda37..29b57c4f 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -1917,8 +1917,6 @@ void ClientEndFrame( gentity_t *ent )
G_SetClientSound( ent );
- G_UpdateZaps( ent );
-
// set the latest infor
if( g_smoothClients.integer )
BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
diff --git a/src/game/g_local.h b/src/game/g_local.h
index f1432ebc..c7b8dcd0 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -895,7 +895,7 @@ qboolean CheckPounceAttack( gentity_t *ent );
void CheckCkitRepair( gentity_t *ent );
void G_ChargeAttack( gentity_t *ent, gentity_t *victim );
void G_CrushAttack( gentity_t *ent, gentity_t *victim );
-void G_UpdateZaps( gentity_t *ent );
+void G_UpdateZaps( int msec );
//
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 63b8930d..0b1c639c 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -2552,6 +2552,7 @@ void G_RunFrame( int levelTime )
G_SpawnClients( TEAM_ALIENS );
G_SpawnClients( TEAM_HUMANS );
G_CalculateAvgPlayers( );
+ G_UpdateZaps( msec );
// see if it is time to end the level
CheckExitRules( );
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 5ef638a5..1c58e212 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -1084,49 +1084,23 @@ LEVEL2
======================================================================
*/
+#define MAX_ZAPS 64
-static vec3_t sortReference;
-static int QDECL G_SortDistance( const void *a, const void *b )
-{
- gentity_t *aent, *bent;
- float adist, bdist;
-
- aent = &g_entities[ *(int *)a ];
- bent = &g_entities[ *(int *)b ];
- adist = Distance( sortReference, aent->s.origin );
- bdist = Distance( sortReference, bent->s.origin );
- if( adist > bdist )
- return -1;
- else if( adist < bdist )
- return 1;
- else
- return 0;
-}
+static zap_t zaps[ MAX_CLIENTS ];
/*
===============
-G_UpdateZaps
+G_FindNewZapTarget
===============
*/
-void G_UpdateZaps( gentity_t *ent )
+static gentity_t *G_FindNewZapTarget( gentity_t *ent )
{
int entityList[ MAX_GENTITIES ];
- int hitList[ MAX_GENTITIES ];
- vec3_t range = { LEVEL2_AREAZAP_CUTOFF,
- LEVEL2_AREAZAP_CUTOFF,
- LEVEL2_AREAZAP_CUTOFF };
+ vec3_t range = { LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE };
vec3_t mins, maxs;
- int i, j;
- int hit = 0;
- int num;
+ int i, j, k, num;
gentity_t *enemy;
- gentity_t *effect;
trace_t tr;
- qboolean alreadyTargeted = qfalse;
- int damage;
-
- if( !ent->zapping || ent->health <= 0 )
- return;
VectorScale( range, 1.0f / M_ROOT3, range );
VectorAdd( ent->s.origin, range, maxs );
@@ -1138,86 +1112,227 @@ void G_UpdateZaps( gentity_t *ent )
{
enemy = &g_entities[ entityList[ i ] ];
- if( ( ( enemy->client &&
- enemy->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) ||
+ if( ( ( enemy->client && enemy->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) ||
( enemy->s.eType == ET_BUILDABLE &&
- BG_Buildable( enemy->s.modelindex )->team == TEAM_HUMANS ) ) &&
- enemy->health > 0 )
+ BG_Buildable( enemy->s.modelindex )->team == TEAM_HUMANS ) ) && enemy->health > 0 )
{
+ qboolean foundOldTarget = qfalse;
+
+ trap_Trace( &tr, muzzle, NULL, NULL, enemy->s.origin, ent->s.number, MASK_SHOT );
+
+ //can't see target from here
+ if( tr.entityNum == ENTITYNUM_WORLD )
+ continue;
- alreadyTargeted = qfalse;
- for( j = 0; j < LEVEL2_AREAZAP_MAX_TARGETS; j++ )
+ for( j = 0; j < MAX_ZAPS; j++ )
{
- if( ent->zapTargets[ j ] == entityList[ i ] )
+ zap_t *zap = &zaps[ j ];
+
+ for( k = 0; k < zap->numTargets; k++ )
{
- alreadyTargeted = qtrue;
+ if( zap->targets[ k ] == enemy )
+ {
+ foundOldTarget = qtrue;
break;
+ }
}
+
+ if( foundOldTarget )
+ break;
}
- if( !alreadyTargeted &&
- Distance( ent->s.origin, enemy->s.origin ) > LEVEL2_AREAZAP_RANGE )
- {
+ // enemy is already targetted
+ if( foundOldTarget )
continue;
- }
- trap_Trace( &tr, ent->s.origin, NULL, NULL, enemy->s.origin,
- ent-g_entities, MASK_SHOT );
- if( tr.entityNum == enemy-g_entities )
- hitList[ hit++ ] = tr.entityNum;
+ return enemy;
}
}
- for( i = 0; i < LEVEL2_AREAZAP_MAX_TARGETS; i++ )
- ent->zapTargets[ i ] = -1;
-
- if( !hit )
- return;
-
- ent->zapDmg += ( (float)( level.time - level.previousTime ) / 1000.0f )
- * LEVEL2_AREAZAP_DMG;
- damage = (int)ent->zapDmg;
- // wait until we've accumulated enough damage for bsuit to take at
- // least 1 HP
- if( damage < 5 )
- damage = 0;
- else
- ent->zapDmg -= (int)damage;
+ return NULL;
+}
+/*
+===============
+G_UpdateZapEffect
+===============
+*/
+static void G_UpdateZapEffect( zap_t *zap )
+{
+ int j;
+ gentity_t *effect = zap->effectChannel;
- VectorCopy( ent->s.origin, sortReference );
- qsort( hitList, hit, sizeof( int ), G_SortDistance );
+ effect->s.eType = ET_LEV2_ZAP_CHAIN;
+ effect->classname = "lev2zapchain";
+ G_SetOrigin( effect, zap->creator->s.origin );
+ effect->s.misc = zap->creator->s.number;
- effect = G_TempEntity( ent->s.origin, EV_LEV2_ZAP );
- effect->s.misc = ent-g_entities;
effect->s.time = effect->s.time2 = effect->s.constantLight = -1;
- for( i = 0; i < hit; i++ )
+
+ for( j = 0; j < zap->numTargets; j++ )
{
- if( i >= LEVEL2_AREAZAP_MAX_TARGETS )
- break;
+ int number = zap->targets[ j ]->s.number;
- ent->zapTargets[ i ] = hitList[ i ];
+ switch( j )
+ {
+ case 0: effect->s.time = number; break;
+ case 1: effect->s.time2 = number; break;
+ case 2: effect->s.constantLight = number; break;
+ default: break;
+ }
+ }
- enemy = &g_entities[ hitList[ i ] ];
+ trap_LinkEntity( effect );
+}
+/*
+===============
+G_CreateNewZap
+===============
+*/
+static void G_CreateNewZap( gentity_t *creator, gentity_t *target )
+{
+ int i, j;
+ zap_t *zap;
+
+ for( i = 0; i < MAX_ZAPS; i++ )
+ {
+ zap = &zaps[ i ];
- if( damage > 0 )
+ if( !zap->used )
{
- G_Damage( enemy, ent, ent, NULL, enemy->s.origin,
- damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP );
+ zap->used = qtrue;
+
+ zap->timeToLive = LEVEL2_AREAZAP_TIME;
+ zap->damageUsed = 0;
+
+ zap->creator = creator;
+
+ zap->targets[ 0 ] = target;
+ zap->numTargets = 1;
+
+ for( j = 1; j < MAX_ZAP_TARGETS && zap->targets[ j - 1 ]; j++ )
+ {
+ zap->targets[ j ] = G_FindNewZapTarget( zap->targets[ 0 ] );
+
+ if( zap->targets[ j ] )
+ zap->numTargets++;
+ }
+
+ zap->effectChannel = G_Spawn( );
+ G_UpdateZapEffect( zap );
+
+ return;
}
+ }
+}
+
+
+/*
+===============
+G_UpdateZaps
+===============
+*/
+void G_UpdateZaps( int msec )
+{
+ int i, j;
+ zap_t *zap;
+ int damage;
+
+ for( i = 0; i < MAX_ZAPS; i++ )
+ {
+ zap = &zaps[ i ];
- switch( i )
+ if( zap->used )
{
- case 0: effect->s.time = hitList[ i ]; break;
- case 1: effect->s.time2 = hitList[ i ]; break;
- case 2: effect->s.constantLight = hitList[ i ]; break;
- default: break;
+ //check each target is valid
+ for( j = 0; j < zap->numTargets; j++ )
+ {
+ gentity_t *source;
+ gentity_t *target = zap->targets[ j ];
+
+ if( j == 0 )
+ source = zap->creator;
+ else
+ source = zap->targets[ 0 ];
+
+ if( target->health <= 0 || !target->inuse || //early out
+ Distance( source->s.origin, target->s.origin ) > LEVEL2_AREAZAP_RANGE )
+ {
+ target = zap->targets[ j ] = G_FindNewZapTarget( source );
+ }
+ }
+
+ if( zap->numTargets )
+ {
+ damage = ceil( ( (float)msec / LEVEL2_AREAZAP_TIME ) *
+ LEVEL2_AREAZAP_DMG );
+
+ // don't let a high msec value inflate the total damage
+ if( damage + zap->damageUsed > LEVEL2_AREAZAP_DMG )
+ damage = LEVEL2_AREAZAP_DMG - zap->damageUsed;
+
+ for( j = 0; j < zap->numTargets; j++ )
+ {
+ gentity_t *source;
+ gentity_t *target = zap->targets[ j ];
+ vec3_t forward;
+
+ if( j == 0 )
+ source = zap->creator;
+ else
+ source = zap->targets[ 0 ];
+
+ VectorSubtract( target->s.origin, source->s.origin, forward );
+ VectorNormalize( forward );
+
+ //do the damage
+ if( damage )
+ {
+ G_Damage( target, source, zap->creator, forward, target->s.origin,
+ damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP );
+ }
+ }
+ }
+
+ G_UpdateZapEffect( zap );
+
+ zap->timeToLive -= msec;
+
+ if( zap->timeToLive <= 0 || zap->numTargets == 0 || zap->creator->health <= 0 )
+ {
+ zap->used = qfalse;
+ G_FreeEntity( zap->effectChannel );
+ }
}
}
}
/*
+===============
+areaZapFire
+===============
+*/
+void areaZapFire( gentity_t *ent )
+{
+ trace_t tr;
+ gentity_t *traceEnt;
+
+ G_WideTrace( &tr, ent, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_WIDTH, LEVEL2_AREAZAP_WIDTH, &traceEnt );
+
+ if( traceEnt == NULL )
+ return;
+
+ if( ( ( traceEnt->client && traceEnt->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) ||
+ ( traceEnt->s.eType == ET_BUILDABLE &&
+ BG_Buildable( traceEnt->s.modelindex )->team == TEAM_HUMANS ) ) && traceEnt->health > 0 )
+ {
+ G_CreateNewZap( ent, traceEnt );
+ }
+}
+
+
+/*
======================================================================
LEVEL3
@@ -1462,6 +1577,10 @@ void FireWeapon2( gentity_t *ent )
LCChargeFire( ent, qtrue );
break;
+ case WP_ALEVEL2_UPG:
+ areaZapFire( ent );
+ break;
+
case WP_ABUILD:
case WP_ABUILD2:
case WP_HBUILD:
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index 55db6bce..7c414e89 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -77,10 +77,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define LEVEL2_CLAW_K_SCALE 1.0f
#define LEVEL2_CLAW_U_REPEAT 400
#define LEVEL2_CLAW_U_K_SCALE 1.0f
-#define LEVEL2_AREAZAP_DMG ADM(40)
-#define LEVEL2_AREAZAP_RANGE 120.0f
-#define LEVEL2_AREAZAP_CUTOFF 300.0f
-#define LEVEL2_AREAZAP_REPEAT 500
+#define LEVEL2_AREAZAP_DMG ADM(60)
+#define LEVEL2_AREAZAP_RANGE 200.0f
+#define LEVEL2_AREAZAP_WIDTH 15.0f
+#define LEVEL2_AREAZAP_REPEAT 1500
+#define LEVEL2_AREAZAP_TIME 1000
#define LEVEL2_AREAZAP_MAX_TARGETS 5
#define LEVEL2_WALLJUMP_MAXSPEED 1000.0f