summaryrefslogtreecommitdiff
path: root/src/game/g_weapon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/g_weapon.c')
-rw-r--r--src/game/g_weapon.c667
1 files changed, 316 insertions, 351 deletions
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index ebfa230e..5bfb1fce 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -142,13 +142,15 @@ Trace a bounding box against entities, but not the world
Also check there is a line of sight between the start and end point
================
*/
-static void G_WideTrace( trace_t *tr, gentity_t *ent, float range, float width, gentity_t **target )
+static void G_WideTrace( trace_t *tr, gentity_t *ent, float range,
+ float width, float height, gentity_t **target )
{
vec3_t mins, maxs;
vec3_t end;
VectorSet( mins, -width, -width, -width );
VectorSet( maxs, width, width, width );
+ mins[ 2 ] -= height - width;
*target = NULL;
@@ -156,15 +158,25 @@ static void G_WideTrace( trace_t *tr, gentity_t *ent, float range, float width,
return;
// Set aiming directions
- AngleVectors( ent->client->ps.viewangles, forward, right, up );
- CalcMuzzlePoint( ent, forward, right, up, muzzle );
VectorMA( muzzle, range, forward, end );
G_UnlaggedOn( muzzle, range );
// Trace against entities
trap_Trace( tr, muzzle, mins, maxs, end, ent->s.number, CONTENTS_BODY );
- if( tr->entityNum != ENTITYNUM_NONE )
+
+ // If we started in a solid that means someone is within our muzzle box,
+ // the trace didn't give us the entity number though so do a trace
+ // from the entity origin to the muzzle
+ if( tr->startsolid )
+ {
+ trap_Trace( tr, ent->client->ps.origin, mins, maxs, muzzle,
+ ent->s.number, CONTENTS_BODY );
+ if( tr->entityNum != ENTITYNUM_NONE )
+ *target = &g_entities[ tr->entityNum ];
+ }
+
+ else if( tr->entityNum != ENTITYNUM_NONE )
{
*target = &g_entities[ tr->entityNum ];
@@ -207,32 +219,91 @@ void SnapVectorTowards( vec3_t v, vec3_t to )
/*
===============
-meleeAttack
+BloodSpurt
+
+Generates a blood spurt event for traces with accurate end points
===============
*/
-void meleeAttack( gentity_t *ent, float range, float width, int damage, meansOfDeath_t mod )
+static void BloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr )
{
- trace_t tr;
gentity_t *tent;
- gentity_t *traceEnt;
- G_WideTrace( &tr, ent, range, width, &traceEnt );
+ if( !attacker->client || !victim->client )
+ return;
+ tent = G_TempEntity( tr->endpos, EV_MISSILE_HIT );
+ tent->s.otherEntityNum = victim->s.number;
+ tent->s.eventParm = DirToByte( tr->plane.normal );
+ tent->s.weapon = attacker->s.weapon;
+ tent->s.generic1 = attacker->s.generic1; // weaponMode
+ }
+
+/*
+===============
+WideBloodSpurt
- if( traceEnt == NULL )
+Calculates the position of a blood spurt for wide traces and generates an event
+===============
+*/
+static void WideBloodSpurt( gentity_t *attacker, gentity_t *victim, trace_t *tr )
+{
+ gentity_t *tent;
+ vec3_t normal, origin;
+ float mag, radius;
+
+ if( !attacker->client || !victim->client )
return;
- // send blood impact
- if( traceEnt->takedamage && traceEnt->client )
+ if( tr )
+ VectorSubtract( tr->endpos, victim->client->ps.origin, normal );
+ else
+ VectorSubtract( attacker->client->ps.origin,
+ victim->client->ps.origin, normal );
+
+ // Normalize the horizontal components of the vector difference to the
+ // "radius" of the bounding box
+ mag = sqrt( normal[ 0 ] * normal[ 0 ] + normal[ 1 ] * normal[ 1 ] );
+ radius = victim->r.maxs[ 0 ] * 1.21f;
+ if( mag > radius )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ normal[ 0 ] = normal[ 0 ] / mag * radius;
+ normal[ 1 ] = normal[ 1 ] / mag * radius;
}
- if( traceEnt->takedamage )
- G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, mod );
+ // Clamp origin to be within bounding box vertically
+ if( normal[ 2 ] > victim->r.maxs[ 2 ] )
+ normal[ 2 ] = victim->r.maxs[ 2 ];
+ if( normal[ 2 ] < victim->r.mins[ 2 ] )
+ normal[ 2 ] = victim->r.mins[ 2 ];
+
+ VectorAdd( victim->client->ps.origin, normal, origin );
+ VectorNegate( normal, normal );
+ VectorNormalize( normal );
+
+ // Create the blood spurt effect entity
+ tent = G_TempEntity( origin, EV_MISSILE_HIT );
+ tent->s.eventParm = DirToByte( normal );
+ tent->s.otherEntityNum = victim->s.number;
+ tent->s.weapon = attacker->s.weapon;
+ tent->s.generic1 = attacker->s.generic1; // weaponMode
+}
+
+/*
+===============
+meleeAttack
+===============
+*/
+void meleeAttack( gentity_t *ent, float range, float width, float height,
+ int damage, meansOfDeath_t mod )
+{
+ trace_t tr;
+ gentity_t *traceEnt;
+
+ G_WideTrace( &tr, ent, range, width, height, &traceEnt );
+ if( traceEnt == NULL || !traceEnt->takedamage )
+ return;
+
+ WideBloodSpurt( ent, traceEnt, &tr );
+ G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, mod );
}
/*
@@ -326,7 +397,7 @@ void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent )
{
r = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
u = Q_crandom( &seed ) * SHOTGUN_SPREAD * 16;
- VectorMA( origin, 8192 * 16, forward, end );
+ VectorMA( origin, SHOTGUN_RANGE, forward, end );
VectorMA( end, r, right, end );
VectorMA( end, u, up, end );
@@ -353,7 +424,7 @@ void shotgunFire( gentity_t *ent )
SnapVector( tent->s.origin2 );
tent->s.eventParm = rand() & 255; // seed for spread pattern
tent->s.otherEntityNum = ent->s.number;
- G_UnlaggedOn( muzzle, 8192 * 16 );
+ G_UnlaggedOn( muzzle, SHOTGUN_RANGE );
ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
G_UnlaggedOff();
}
@@ -390,11 +461,7 @@ void massDriverFire( gentity_t *ent )
// send impact
if( traceEnt->takedamage && traceEnt->client )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ BloodSpurt( ent, traceEnt, &tr );
}
else
{
@@ -546,11 +613,7 @@ void lasGunFire( gentity_t *ent )
// send impact
if( traceEnt->takedamage && traceEnt->client )
{
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ BloodSpurt( ent, traceEnt, &tr );
}
else
{
@@ -575,49 +638,31 @@ PAIN SAW
void painSawFire( gentity_t *ent )
{
trace_t tr;
- vec3_t end;
- gentity_t *tent;
- gentity_t *traceEnt;
+ vec3_t temp;
+ gentity_t *tent, *traceEnt;
- // set aiming directions
- AngleVectors( ent->client->ps.viewangles, forward, right, up );
-
- CalcMuzzlePoint( ent, forward, right, up, muzzle );
-
- VectorMA( muzzle, PAINSAW_RANGE, forward, end );
-
- G_UnlaggedOn( muzzle, PAINSAW_RANGE );
- trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
- G_UnlaggedOff( );
-
- if( tr.surfaceFlags & SURF_NOIMPACT )
+ G_WideTrace( &tr, ent, PAINSAW_RANGE, PAINSAW_WIDTH, PAINSAW_HEIGHT,
+ &traceEnt );
+ if( !traceEnt || !traceEnt->takedamage )
return;
- traceEnt = &g_entities[ tr.entityNum ];
+ // hack to line up particle system with weapon model
+ tr.endpos[ 2 ] -= 5.0f;
// send blood impact
- if( traceEnt->takedamage )
- {
- vec3_t temp;
-
- //hack to get the particle system to line up with the weapon
- VectorCopy( tr.endpos, temp );
- temp[ 2 ] -= 10.0f;
-
if( traceEnt->client )
{
- tent = G_TempEntity( temp, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
+ BloodSpurt( ent, traceEnt, &tr );
}
else
+ {
+ VectorCopy( tr.endpos, temp );
tent = G_TempEntity( temp, EV_MISSILE_MISS );
-
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
tent->s.generic1 = ent->s.generic1; //weaponMode
}
- if( traceEnt->takedamage )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, PAINSAW_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PAINSAW );
}
@@ -638,16 +683,18 @@ void LCChargeFire( gentity_t *ent, qboolean secondary )
{
gentity_t *m;
- if( secondary )
+ if( secondary && ent->client->ps.stats[ STAT_MISC ] <= 0 )
{
m = fire_luciferCannon( ent, muzzle, forward, LCANNON_SECONDARY_DAMAGE,
- LCANNON_SECONDARY_RADIUS );
- ent->client->ps.weaponTime = LCANNON_REPEAT;
+ LCANNON_SECONDARY_RADIUS, LCANNON_SECONDARY_SPEED );
+ ent->client->ps.stats[ STAT_MISC2 ] = LCANNON_REPEAT;
}
else
{
- m = fire_luciferCannon( ent, muzzle, forward, ent->client->ps.stats[ STAT_MISC ], LCANNON_RADIUS );
- ent->client->ps.weaponTime = LCANNON_CHARGEREPEAT;
+ m = fire_luciferCannon( ent, muzzle, forward,
+ ent->client->ps.stats[ STAT_MISC ], LCANNON_RADIUS, LCANNON_SPEED );
+ ent->client->ps.stats[ STAT_MISC2 ] = LCANNON_CHARGEREPEAT -
+ ( level.time - ent->client->lcannonStartTime );
}
ent->client->ps.stats[ STAT_MISC ] = 0;
@@ -769,9 +816,10 @@ void cancelBuildFire( gentity_t *ent )
G_AddEvent( ent, EV_BUILD_REPAIR, 0 );
}
}
- else if( ent->client->ps.weapon == WP_ABUILD2 )
+ else if( ent->client->ps.weapon == WP_ABUILD ||
+ ent->client->ps.weapon == WP_ABUILD2 )
meleeAttack( ent, ABUILDER_CLAW_RANGE, ABUILDER_CLAW_WIDTH,
- ABUILDER_CLAW_DMG, MOD_ABUILDER_CLAW ); //melee attack for alien builder
+ ABUILDER_CLAW_WIDTH, ABUILDER_CLAW_DMG, MOD_ABUILDER_CLAW );
}
/*
@@ -781,7 +829,10 @@ buildFire
*/
void buildFire( gentity_t *ent, dynMenu_t menu )
{
- if( ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE )
+ buildable_t buildable = ( ent->client->ps.stats[ STAT_BUILDABLE ]
+ & ~SB_VALID_TOGGLEBIT );
+
+ if( buildable > BA_NONE )
{
if( ent->client->ps.stats[ STAT_MISC ] > 0 )
{
@@ -789,27 +840,17 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
return;
}
- if( G_BuildIfValid( ent, ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) )
+ if( G_BuildIfValid( ent, buildable ) )
{
if( g_cheats.integer )
{
ent->client->ps.stats[ STAT_MISC ] = 0;
}
- else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && !G_IsOvermindBuilt( ) )
- {
- ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2;
- }
- else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
- G_IsPowered( muzzle ) == BA_NONE &&
- ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) != BA_H_REPEATER ) //hack
+ else
{
ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2;
+ BG_FindBuildTimeForBuildable( buildable );
}
- else
- ent->client->ps.stats[ STAT_MISC ] +=
- BG_FindBuildDelayForWeapon( ent->s.weapon );
ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
@@ -849,11 +890,18 @@ CheckVenomAttack
qboolean CheckVenomAttack( gentity_t *ent )
{
trace_t tr;
- gentity_t *tent;
gentity_t *traceEnt;
int damage = LEVEL0_BITE_DMG;
- G_WideTrace( &tr, ent, LEVEL0_BITE_RANGE, LEVEL0_BITE_WIDTH, &traceEnt );
+ if( ent->client->ps.weaponTime )
+ return qfalse;
+
+ // Calculate muzzle point
+ AngleVectors( ent->client->ps.viewangles, forward, right, up );
+ CalcMuzzlePoint( ent, forward, right, up, muzzle );
+
+ G_WideTrace( &tr, ent, LEVEL0_BITE_RANGE, LEVEL0_BITE_WIDTH,
+ LEVEL0_BITE_WIDTH, &traceEnt );
if( traceEnt == NULL )
return qfalse;
@@ -864,15 +912,14 @@ qboolean CheckVenomAttack( gentity_t *ent )
if( !traceEnt->client && !traceEnt->s.eType == ET_BUILDABLE )
return qfalse;
- //allow bites to work against defensive buildables only
+ // only allow bites to work against buildings as they are constructing
if( traceEnt->s.eType == ET_BUILDABLE )
{
- if( traceEnt->s.modelindex != BA_H_MGTURRET &&
- traceEnt->s.modelindex != BA_H_TESLAGEN )
+ if( traceEnt->spawned )
return qfalse;
- //hackery
- damage *= 0.5f;
+ if( traceEnt->biteam == BIT_ALIENS )
+ return qfalse;
}
if( traceEnt->client )
@@ -884,17 +931,10 @@ qboolean CheckVenomAttack( gentity_t *ent )
}
// send blood impact
- if( traceEnt->takedamage && traceEnt->client )
- {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
- }
+ WideBloodSpurt( ent, traceEnt, &tr );
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_LEVEL0_BITE );
-
+ ent->client->ps.weaponTime += LEVEL0_BITE_REPEAT;
return qtrue;
}
@@ -993,12 +1033,6 @@ void poisonCloud( gentity_t *ent )
if( humanPlayer->client && humanPlayer->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
{
- if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, humanPlayer->client->ps.stats ) )
- continue;
-
- if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, humanPlayer->client->ps.stats ) )
- continue;
-
trap_Trace( &tr, muzzle, NULL, NULL, humanPlayer->s.origin, humanPlayer->s.number, MASK_SHOT );
//can't see target from here
@@ -1010,6 +1044,7 @@ void poisonCloud( gentity_t *ent )
humanPlayer->client->ps.stats[ STAT_STATE ] |= SS_POISONCLOUDED;
humanPlayer->client->lastPoisonCloudedTime = level.time;
humanPlayer->client->lastPoisonCloudedClient = ent;
+
trap_SendServerCommand( humanPlayer->client->ps.clientNum, "poisoncloud" );
}
}
@@ -1026,23 +1061,48 @@ LEVEL2
======================================================================
*/
-#define MAX_ZAPS 64
-
-static zap_t zaps[ MAX_CLIENTS ];
+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;
+}
/*
===============
-G_FindNewZapTarget
+G_UpdateZaps
===============
*/
-static gentity_t *G_FindNewZapTarget( gentity_t *ent )
+void G_UpdateZaps( gentity_t *ent )
{
int entityList[ MAX_GENTITIES ];
- vec3_t range = { LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_RANGE };
+ int hitList[ MAX_GENTITIES ];
+ vec3_t range = { LEVEL2_AREAZAP_CUTOFF,
+ LEVEL2_AREAZAP_CUTOFF,
+ LEVEL2_AREAZAP_CUTOFF };
vec3_t mins, maxs;
- int i, j, k, num;
+ int i, j;
+ int hit = 0;
+ int 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 );
@@ -1054,233 +1114,85 @@ static gentity_t *G_FindNewZapTarget( gentity_t *ent )
{
enemy = &g_entities[ entityList[ i ] ];
- if( ( ( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
+ if( ( ( enemy->client &&
+ enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
( enemy->s.eType == ET_BUILDABLE &&
- BG_FindTeamForBuildable( enemy->s.modelindex ) == BIT_HUMANS ) ) && enemy->health > 0 )
+ BG_FindTeamForBuildable( enemy->s.modelindex ) == BIT_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;
- for( j = 0; j < MAX_ZAPS; j++ )
+ alreadyTargeted = qfalse;
+ for( j = 0; j < LEVEL2_AREAZAP_MAX_TARGETS; j++ )
{
- zap_t *zap = &zaps[ j ];
-
- for( k = 0; k < zap->numTargets; k++ )
+ if( ent->zapTargets[ j ] == entityList[ i ] )
{
- if( zap->targets[ k ] == enemy )
- {
- foundOldTarget = qtrue;
+ alreadyTargeted = qtrue;
break;
- }
}
-
- if( foundOldTarget )
- break;
}
- // enemy is already targetted
- if( foundOldTarget )
+ if( !alreadyTargeted &&
+ Distance( ent->s.origin, enemy->s.origin ) > LEVEL2_AREAZAP_RANGE )
+ {
continue;
+ }
- return enemy;
+ 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 NULL;
-}
-
-/*
-===============
-G_UpdateZapEffect
-===============
-*/
-static void G_UpdateZapEffect( zap_t *zap )
-{
- int j;
- gentity_t *effect = zap->effectChannel;
+ for( i = 0; i < LEVEL2_AREAZAP_MAX_TARGETS; i++ )
+ ent->zapTargets[ i ] = -1;
- 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->s.time = effect->s.time2 = effect->s.constantLight = -1;
-
- for( j = 0; j < zap->numTargets; j++ )
- {
- int number = zap->targets[ j ]->s.number;
+ if( !hit )
+ return;
- 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;
- }
- }
+ 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;
- trap_LinkEntity( effect );
-}
-/*
-===============
-G_CreateNewZap
-===============
-*/
-static void G_CreateNewZap( gentity_t *creator, gentity_t *target )
-{
- int i, j;
- zap_t *zap;
+ VectorCopy( ent->s.origin, sortReference );
+ qsort( hitList, hit, sizeof( int ), G_SortDistance );
- for( i = 0; i < MAX_ZAPS; i++ )
+ 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++ )
{
- zap = &zaps[ i ];
-
- if( !zap->used )
- {
- 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[ j - 1 ] );
-
- if( zap->targets[ j ] )
- zap->numTargets++;
- }
-
- zap->effectChannel = G_Spawn( );
- G_UpdateZapEffect( zap );
-
- return;
- }
- }
-}
+ if( i >= LEVEL2_AREAZAP_MAX_TARGETS )
+ break;
+ ent->zapTargets[ i ] = hitList[ i ];
-/*
-===============
-G_UpdateZaps
-===============
-*/
-void G_UpdateZaps( int msec )
-{
- int i, j;
- zap_t *zap;
- int damage;
+ enemy = &g_entities[ hitList[ i ] ];
- for( i = 0; i < MAX_ZAPS; i++ )
- {
- zap = &zaps[ i ];
- if( zap->used )
+ if( damage > 0 )
{
- //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[ j - 1 ];
-
- 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 );
-
- //couldn't find a target, so forget about the rest of the chain
- if( !target )
- zap->numTargets = j;
- }
- }
-
- if( zap->numTargets )
- {
- for( j = 0; j < zap->numTargets; j++ )
- {
- gentity_t *source;
- gentity_t *target = zap->targets[ j ];
- float r = 1.0f / zap->numTargets;
- float damageFraction = 2 * r - 2 * j * r * r - r * r;
- vec3_t forward;
-
- if( j == 0 )
- source = zap->creator;
- else
- source = zap->targets[ j - 1 ];
-
- damage = ceil( ( (float)msec / LEVEL2_AREAZAP_TIME ) *
- LEVEL2_AREAZAP_DMG * damageFraction );
-
- // don't let a high msec value inflate the total damage
- if( damage + zap->damageUsed > LEVEL2_AREAZAP_DMG )
- damage = LEVEL2_AREAZAP_DMG - zap->damageUsed;
-
- 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,
+ G_Damage( enemy, ent, ent, NULL, enemy->s.origin,
damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP );
- zap->damageUsed += damage;
- }
- }
- }
-
- 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, &traceEnt );
-
- if( traceEnt == NULL )
- return;
- if( ( ( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
- ( traceEnt->s.eType == ET_BUILDABLE &&
- BG_FindTeamForBuildable( traceEnt->s.modelindex ) == BIT_HUMANS ) ) && traceEnt->health > 0 )
- {
- G_CreateNewZap( ent, traceEnt );
+ switch( i )
+ {
+ 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;
+ }
}
}
-
/*
======================================================================
@@ -1297,50 +1209,47 @@ CheckPounceAttack
qboolean CheckPounceAttack( gentity_t *ent )
{
trace_t tr;
- gentity_t *tent;
gentity_t *traceEnt;
int damage;
+ int payload;
- if( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {
- ent->client->allowedToPounce = qfalse;
+ if( ent->client->pmext.pouncePayload <= 0 )
+ return qfalse;
+
+ // in case the goon lands on his target, he get's one shot after landing
+ payload = ent->client->pmext.pouncePayload;
+ if( !( ent->client->ps.pm_flags & PMF_CHARGE ) )
ent->client->pmext.pouncePayload = 0;
- }
- if( !ent->client->allowedToPounce )
+ if( ent->client->ps.weaponTime > 0 )
return qfalse;
- if( ent->client->ps.weaponTime )
- return qfalse;
+ // Calculate muzzle point
+ AngleVectors( ent->client->ps.viewangles, forward, right, up );
+ CalcMuzzlePoint( ent, forward, right, up, muzzle );
- G_WideTrace( &tr, ent, LEVEL3_POUNCE_RANGE, LEVEL3_POUNCE_WIDTH, &traceEnt );
+ G_WideTrace( &tr, ent, LEVEL3_POUNCE_RANGE, LEVEL3_POUNCE_WIDTH,
+ LEVEL3_POUNCE_WIDTH, &traceEnt );
if( traceEnt == NULL )
return qfalse;
// send blood impact
- if( traceEnt->takedamage && traceEnt->client )
- {
- tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
- tent->s.otherEntityNum = traceEnt->s.number;
- tent->s.eventParm = DirToByte( tr.plane.normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
- }
+ if( traceEnt->takedamage )
+ WideBloodSpurt( ent, traceEnt, &tr );
if( !traceEnt->takedamage )
return qfalse;
- damage = (int)( ( (float)ent->client->pmext.pouncePayload
- / (float)LEVEL3_POUNCE_SPEED ) * LEVEL3_POUNCE_DMG );
+ damage = (int)( ( (float)payload / (float)LEVEL3_POUNCE_SPEED )
+ * LEVEL3_POUNCE_DMG );
ent->client->pmext.pouncePayload = 0;
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage,
- DAMAGE_NO_KNOCKBACK|DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE );
+ DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE );
ent->client->ps.weaponTime += LEVEL3_POUNCE_TIME;
- ent->client->allowedToPounce = qfalse;
return qtrue;
}
@@ -1365,39 +1274,90 @@ LEVEL4
/*
===============
-ChargeAttack
+G_ChargeAttack
===============
*/
-void ChargeAttack( gentity_t *ent, gentity_t *victim )
+void G_ChargeAttack( gentity_t *ent, gentity_t *victim )
{
- gentity_t *tent;
int damage;
vec3_t forward, normal;
- if( level.time < victim->chargeRepeat )
+ if( ent->client->ps.stats[ STAT_MISC ] <= 0 || !ent->client->charging )
return;
- victim->chargeRepeat = level.time + LEVEL4_CHARGE_REPEAT;
-
VectorSubtract( victim->s.origin, ent->s.origin, forward );
VectorNormalize( forward );
VectorNegate( forward, normal );
- if( victim->client )
+ if( !victim->takedamage )
+ return;
+
+ WideBloodSpurt( ent, victim, NULL );
+
+ damage = (int)( ( (float)ent->client->ps.stats[ STAT_MISC ] /
+ (float)LEVEL4_TRAMPLE_CHARGE_MAX ) * LEVEL4_TRAMPLE_DMG );
+
+ G_Damage( victim, ent, ent, forward, victim->s.origin, damage,
+ 0, MOD_LEVEL4_TRAMPLE );
+
+ if( !victim->client )
+ ent->client->ps.stats[ STAT_MISC ] = 0;
+}
+
+/*
+===============
+G_CrushAttack
+
+Should only be called if there was an impact between a tyrant and another player
+===============
+*/
+void G_CrushAttack( gentity_t *ent, gentity_t *victim, float sec )
+{
+ vec3_t dir;
+ float jump;
+ int damage;
+
+ if( !victim->takedamage ||
+ ent->client->ps.origin[ 2 ] + ent->r.mins[ 2 ] <
+ victim->s.origin[ 2 ] + victim->r.maxs[ 2 ] ||
+ ( victim->client &&
+ victim->client->ps.groundEntityNum == ENTITYNUM_NONE ) )
+ return;
+
+ // Force target to crouch first if they can
+ if( victim->client &&
+ !BG_InventoryContainsUpgrade( UP_BATTLESUIT, victim->client->ps.stats ) &&
+ victim->client->ps.pm_type != PM_JETPACK &&
+ victim->client->pers.cmd.upmove >= 0 &&
+ !( victim->client->ps.pm_flags &
+ ( PMF_CROUCH_HELD | PMF_FORCE_CROUCH ) ) )
{
- tent = G_TempEntity( victim->s.origin, EV_MISSILE_HIT );
- tent->s.otherEntityNum = victim->s.number;
- tent->s.eventParm = DirToByte( normal );
- tent->s.weapon = ent->s.weapon;
- tent->s.generic1 = ent->s.generic1; //weaponMode
+ victim->client->forceCrouchTime = level.time;
+ return;
}
- if( !victim->takedamage )
- return;
+ // Deal velocity based damage to target
+ jump = BG_FindJumpMagnitudeForClass( ent->client->ps.stats[ STAT_PCLASS ] );
+ damage = ( ent->client->pmext.fallVelocity + jump ) *
+ -LEVEL4_CRUSH_DAMAGE_PER_V;
- damage = (int)( ( (float)ent->client->ps.stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME ) * LEVEL4_CHARGE_DMG );
+ if( damage < 0 )
+ damage = 0;
- G_Damage( victim, ent, ent, forward, victim->s.origin, damage, 0, MOD_LEVEL4_CHARGE );
+ if( victim->client &&
+ ent->client->lastCrushTime + LEVEL4_CRUSH_REPEAT < level.time )
+ {
+ ent->client->lastCrushTime = level.time;
+ damage += LEVEL4_CRUSH_DAMAGE;
+ }
+
+ if( damage < 1 )
+ return;
+
+ // Crush the victim over a period of time
+ VectorSubtract( victim->s.origin, ent->client->ps.origin, dir );
+ G_Damage( victim, ent, ent, dir, victim->s.origin, damage,
+ DAMAGE_NO_LOCDAMAGE, MOD_LEVEL4_CRUSH );
}
//======================================================================
@@ -1413,7 +1373,7 @@ void CalcMuzzlePoint( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, v
{
vec3_t normal;
- VectorCopy( ent->s.pos.trBase, muzzlePoint );
+ VectorCopy( ent->client->ps.origin, muzzlePoint );
BG_GetClientNormal( &ent->client->ps, normal );
VectorMA( muzzlePoint, ent->client->ps.viewheight, normal, muzzlePoint );
VectorMA( muzzlePoint, 1, forward, muzzlePoint );
@@ -1481,9 +1441,6 @@ void FireWeapon2( gentity_t *ent )
case WP_ALEVEL1_UPG:
poisonCloud( ent );
break;
- case WP_ALEVEL2_UPG:
- areaZapFire( ent );
- break;
case WP_LUCIFER_CANNON:
LCChargeFire( ent, qtrue );
@@ -1524,20 +1481,28 @@ void FireWeapon( gentity_t *ent )
{
case WP_ALEVEL1:
case WP_ALEVEL1_UPG:
- meleeAttack( ent, LEVEL1_CLAW_RANGE, LEVEL1_CLAW_WIDTH, LEVEL1_CLAW_DMG, MOD_LEVEL1_CLAW );
+ meleeAttack( ent, LEVEL1_CLAW_RANGE, LEVEL1_CLAW_WIDTH, LEVEL1_CLAW_WIDTH,
+ LEVEL1_CLAW_DMG, MOD_LEVEL1_CLAW );
break;
case WP_ALEVEL3:
+ meleeAttack( ent, LEVEL3_CLAW_RANGE, LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_WIDTH,
+ LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
+ break;
case WP_ALEVEL3_UPG:
- meleeAttack( ent, LEVEL3_CLAW_RANGE, LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
+ meleeAttack( ent, LEVEL3_CLAW_UPG_RANGE, LEVEL3_CLAW_WIDTH,
+ LEVEL3_CLAW_WIDTH, LEVEL3_CLAW_DMG, MOD_LEVEL3_CLAW );
break;
case WP_ALEVEL2:
- meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
+ meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_WIDTH,
+ LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
break;
case WP_ALEVEL2_UPG:
- meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
+ meleeAttack( ent, LEVEL2_CLAW_RANGE, LEVEL2_CLAW_WIDTH, LEVEL2_CLAW_WIDTH,
+ LEVEL2_CLAW_DMG, MOD_LEVEL2_CLAW );
break;
case WP_ALEVEL4:
- meleeAttack( ent, LEVEL4_CLAW_RANGE, LEVEL4_CLAW_WIDTH, LEVEL4_CLAW_DMG, MOD_LEVEL4_CLAW );
+ meleeAttack( ent, LEVEL4_CLAW_RANGE, LEVEL4_CLAW_WIDTH,
+ LEVEL4_CLAW_HEIGHT, LEVEL4_CLAW_DMG, MOD_LEVEL4_CLAW );
break;
case WP_BLASTER: