From d34172288e4d5313a6c97ea56987315b67b022d0 Mon Sep 17 00:00:00 2001 From: Christopher Schwarz Date: Mon, 8 Aug 2011 04:26:53 +0000 Subject: * (bug 4999) Marauder+ zap bugfixes (/dev/humancontroller) - Use spherical distance checks instead of cubic - Fix buildables' ability to be zapped by two marauder+'s at once - Chain to targets visible to each other, rather than visible to the zapper - Fix zap effect disappearing too early when target dies --- src/game/g_local.h | 1 + src/game/g_misc.c | 3 + src/game/g_team.c | 3 + src/game/g_weapon.c | 218 +++++++++++++++++++++++++++------------------------- 4 files changed, 121 insertions(+), 104 deletions(-) (limited to 'src/game') diff --git a/src/game/g_local.h b/src/game/g_local.h index 214f4f25..824c7f68 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -953,6 +953,7 @@ 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( int msec ); +void G_ClearPlayerZapEffects( gentity_t *player ); // diff --git a/src/game/g_misc.c b/src/game/g_misc.c index 0572684d..861fc642 100644 --- a/src/game/g_misc.c +++ b/src/game/g_misc.c @@ -88,6 +88,9 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) player->client->ps.eFlags ^= EF_TELEPORT_BIT; G_UnlaggedClear( player ); + // cut all relevant zap beams + G_ClearPlayerZapEffects( player ); + // set angles G_SetClientViewAngle( player, angles ); diff --git a/src/game/g_team.c b/src/game/g_team.c index 6f407a3f..a527ff91 100644 --- a/src/game/g_team.c +++ b/src/game/g_team.c @@ -202,6 +202,9 @@ void G_LeaveTeam( gentity_t *self ) G_FreeEntity( ent ); } + // cut all relevant zap beams + G_ClearPlayerZapEffects( self ); + G_namelog_update_score( self->client ); } diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index accc664e..2218bec2 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -1025,25 +1025,27 @@ LEVEL2 ====================================================================== */ -#define MAX_ZAPS 64 +#define MAX_ZAPS MAX_CLIENTS -static zap_t zaps[ MAX_CLIENTS ]; +static zap_t zaps[ MAX_ZAPS ]; /* =============== -G_FindNewZapTarget +G_FindZapChainTargets =============== */ -static gentity_t *G_FindNewZapTarget( gentity_t *ent ) +static void G_FindZapChainTargets( zap_t *zap ) { + gentity_t *ent = zap->targets[ 0 ]; // the source int entityList[ MAX_GENTITIES ]; - vec3_t range = { LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE }; + vec3_t range = { LEVEL2_AREAZAP_RANGE, + LEVEL2_AREAZAP_RANGE, + LEVEL2_AREAZAP_RANGE }; vec3_t mins, maxs; - int i, j, k, num; + int i, num; gentity_t *enemy; trace_t tr; - VectorScale( range, 1.0f / M_ROOT3, range ); VectorAdd( ent->s.origin, range, maxs ); VectorSubtract( ent->s.origin, range, mins ); @@ -1052,45 +1054,29 @@ static gentity_t *G_FindNewZapTarget( gentity_t *ent ) for( i = 0; i < num; i++ ) { enemy = &g_entities[ entityList[ i ] ]; + // don't chain to self; noclippers can be listed, don't chain to them either + if( enemy == ent || ( enemy->client && enemy->client->noclip ) ) + continue; - 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 ) + 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 && // only chain to living targets + Distance( ent->s.origin, enemy->s.origin ) <= LEVEL2_AREAZAP_RANGE ) { - 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; + // world-LOS check: trace against the world, ignoring other BODY entities + trap_Trace( &tr, ent->s.origin, NULL, NULL, + enemy->s.origin, ent->s.number, CONTENTS_SOLID ); - for( j = 0; j < MAX_ZAPS; j++ ) + if( tr.entityNum == ENTITYNUM_NONE ) { - zap_t *zap = &zaps[ j ]; - - for( k = 0; k < zap->numTargets; k++ ) - { - if( zap->targets[ k ] == enemy ) - { - foundOldTarget = qtrue; - break; - } - } - - if( foundOldTarget ) - break; + zap->targets[ zap->numTargets++ ] = enemy; + if( zap->numTargets >= LEVEL2_AREAZAP_MAX_TARGETS ) + return; } - - // enemy is already targetted - if( foundOldTarget ) - continue; - - return enemy; } } - - return &g_entities[ ENTITYNUM_NONE ]; } /* @@ -1100,23 +1086,19 @@ G_UpdateZapEffect */ static void G_UpdateZapEffect( zap_t *zap ) { - int i; - gentity_t *effect = zap->effectChannel; - int entityNums[ LEVEL2_AREAZAP_MAX_TARGETS + 1 ]; - - effect->s.eType = ET_LEV2_ZAP_CHAIN; - effect->classname = "lev2zapchain"; - G_SetOrigin( effect, zap->creator->s.origin ); + int i; + int entityNums[ LEVEL2_AREAZAP_MAX_TARGETS + 1 ]; entityNums[ 0 ] = zap->creator->s.number; for( i = 0; i < zap->numTargets; i++ ) - { entityNums[ i + 1 ] = zap->targets[ i ]->s.number; - } - BG_PackEntityNumbers( &effect->s, entityNums, zap->numTargets + 1 ); - trap_LinkEntity( effect ); + BG_PackEntityNumbers( &zap->effectChannel->s, + entityNums, zap->numTargets + 1 ); + + VectorCopy( zap->creator->s.origin, zap->effectChannel->r.currentOrigin ); + trap_LinkEntity( zap->effectChannel ); } /* @@ -1126,46 +1108,47 @@ G_CreateNewZap */ static void G_CreateNewZap( gentity_t *creator, gentity_t *target ) { - int i, j; - zap_t *zap; + int i; + zap_t *zap; for( i = 0; i < MAX_ZAPS; i++ ) { zap = &zaps[ i ]; + if( zap->used ) + continue; - if( !zap->used ) - { - G_Damage( target, creator, creator, forward, target->s.origin, - LEVEL2_AREAZAP_DMG, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, - MOD_LEVEL2_ZAP ); - - zap->used = qtrue; + zap->used = qtrue; + zap->timeToLive = LEVEL2_AREAZAP_TIME; - zap->timeToLive = LEVEL2_AREAZAP_TIME; + zap->creator = creator; + zap->targets[ 0 ] = target; + zap->numTargets = 1; - zap->creator = creator; + // the zap chains only through living entities + if( target->health > 0 ) + { + G_Damage( target, creator, creator, forward, + target->s.origin, LEVEL2_AREAZAP_DMG, + DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, + MOD_LEVEL2_ZAP ); - zap->targets[ 0 ] = target; - zap->numTargets = 1; + G_FindZapChainTargets( zap ); - for( j = 1; j < LEVEL2_AREAZAP_MAX_TARGETS; j++ ) + for( i = 1; i < zap->numTargets; i++ ) { - zap->targets[ j ] = G_FindNewZapTarget( target ); - - if( zap->targets[ j ] ) - { - zap->numTargets++; - G_Damage( zap->targets[ j ], target, zap->creator, forward, - target->s.origin, LEVEL2_AREAZAP_DMG, - DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP ); - } + G_Damage( zap->targets[ i ], target, zap->creator, forward, + target->s.origin, LEVEL2_AREAZAP_DMG, + DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, + MOD_LEVEL2_ZAP ); } + } - zap->effectChannel = G_Spawn( ); - G_UpdateZapEffect( zap ); + zap->effectChannel = G_Spawn( ); + zap->effectChannel->s.eType = ET_LEV2_ZAP_CHAIN; + zap->effectChannel->classname = "lev2zapchain"; + G_UpdateZapEffect( zap ); - return; - } + return; } } @@ -1183,36 +1166,63 @@ void G_UpdateZaps( int msec ) for( i = 0; i < MAX_ZAPS; i++ ) { zap = &zaps[ i ]; + if( !zap->used ) + continue; - if( zap->used ) + zap->timeToLive -= msec; + + // first, the disappearance of players is handled immediately in G_ClearPlayerZapEffects() + + // the deconstruction or gibbing of a directly targeted buildable destroys the whole zap effect + if( zap->timeToLive <= 0 || !zap->targets[ 0 ]->inuse ) { - //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 ); - } - } + G_FreeEntity( zap->effectChannel ); + zap->used = qfalse; + continue; + } + + // the deconstruction or gibbing of chained buildables destroy the appropriate beams + for( j = 1; j < zap->numTargets; j++ ) + { + if( !zap->targets[ j ]->inuse ) + zap->targets[ j-- ] = zap->targets[ --zap->numTargets ]; + } - G_UpdateZapEffect( zap ); + G_UpdateZapEffect( zap ); + } +} - zap->timeToLive -= msec; +/* +=============== +G_ClearPlayerZapEffects - if( zap->timeToLive <= 0 || zap->numTargets == 0 || zap->creator->health <= 0 ) - { - zap->used = qfalse; - G_FreeEntity( zap->effectChannel ); - } +called from G_LeaveTeam() and TeleportPlayer() +=============== +*/ +void G_ClearPlayerZapEffects( gentity_t *player ) +{ + int i, j; + zap_t *zap; + + for( i = 0; i < MAX_ZAPS; i++ ) + { + zap = &zaps[ i ]; + if( !zap->used ) + continue; + + // the disappearance of the creator or the first target destroys the whole zap effect + if( zap->creator == player || zap->targets[ 0 ] == player ) + { + G_FreeEntity( zap->effectChannel ); + zap->used = qfalse; + continue; + } + + // the disappearance of chained players destroy the appropriate beams + for( j = 1; j < zap->numTargets; j++ ) + { + if( zap->targets[ j ] == player ) + zap->targets[ j-- ] = zap->targets[ --zap->numTargets ]; } } } @@ -1232,9 +1242,9 @@ void areaZapFire( gentity_t *ent ) if( traceEnt == NULL ) return; - if( ( ( traceEnt->client && traceEnt->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) || + 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 ) + BG_Buildable( traceEnt->s.modelindex )->team == TEAM_HUMANS ) ) { G_CreateNewZap( ent, traceEnt ); } -- cgit