summaryrefslogtreecommitdiff
path: root/src/game/g_combat.c
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/game/g_combat.c
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/game/g_combat.c')
-rw-r--r--src/game/g_combat.c1479
1 files changed, 586 insertions, 893 deletions
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index 5350895..cb15147 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
This file is part of Tremulous.
Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,24 +17,24 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with Tremulous; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
#include "g_local.h"
-damageRegion_t g_damageRegions[ PCL_NUM_CLASSES ][ MAX_LOCDAMAGE_REGIONS ];
+damageRegion_t g_damageRegions[ PCL_NUM_CLASSES ][ MAX_DAMAGE_REGIONS ];
int g_numDamageRegions[ PCL_NUM_CLASSES ];
-armourRegion_t g_armourRegions[ UP_NUM_UPGRADES ][ MAX_ARMOUR_REGIONS ];
+damageRegion_t g_armourRegions[ UP_NUM_UPGRADES ][ MAX_DAMAGE_REGIONS ];
int g_numArmourRegions[ UP_NUM_UPGRADES ];
/*
============
AddScore
-Adds score to both the client and his team
+Adds score to the client
============
*/
void AddScore( gentity_t *ent, int score )
@@ -41,6 +42,15 @@ void AddScore( gentity_t *ent, int score )
if( !ent->client )
return;
+ // make alien and human scores equivalent
+ if ( ent->client->pers.teamSelection == TEAM_ALIENS )
+ {
+ score = rint( ((float)score) / 2.0f );
+ }
+
+ // scale values down to fit the scoreboard better
+ score = rint( ((float)score) / 50.0f );
+
ent->client->ps.persistant[ PERS_SCORE ] += score;
CalculateRanks( );
}
@@ -52,19 +62,13 @@ LookAtKiller
*/
void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker )
{
- vec3_t dir;
if ( attacker && attacker != self )
- VectorSubtract( attacker->s.pos.trBase, self->s.pos.trBase, dir );
+ self->client->ps.stats[ STAT_VIEWLOCK ] = attacker - g_entities;
else if( inflictor && inflictor != self )
- VectorSubtract( inflictor->s.pos.trBase, self->s.pos.trBase, dir );
+ self->client->ps.stats[ STAT_VIEWLOCK ] = inflictor - g_entities;
else
- {
- self->client->ps.stats[ STAT_VIEWLOCK ] = self->s.angles[ YAW ];
- return;
- }
-
- self->client->ps.stats[ STAT_VIEWLOCK ] = vectoyaw( dir );
+ self->client->ps.stats[ STAT_VIEWLOCK ] = self - g_entities;
}
// these are just for logging, the client prints its own messages
@@ -104,7 +108,8 @@ char *modNames[ ] =
"MOD_LEVEL2_CLAW",
"MOD_LEVEL2_ZAP",
"MOD_LEVEL4_CLAW",
- "MOD_LEVEL4_CHARGE",
+ "MOD_LEVEL4_TRAMPLE",
+ "MOD_LEVEL4_CRUSH",
"MOD_SLOWBLOB",
"MOD_POISON",
@@ -118,11 +123,112 @@ char *modNames[ ] =
"MOD_ASPAWN",
"MOD_ATUBE",
"MOD_OVERMIND",
- "MOD_SLAP"
+ "MOD_DECONSTRUCT",
+ "MOD_REPLACE",
+ "MOD_NOCREEP"
};
/*
==================
+G_RewardAttackers
+
+Function to distribute rewards to entities that killed this one.
+Returns the total damage dealt.
+==================
+*/
+float G_RewardAttackers( gentity_t *self )
+{
+ float value, totalDamage = 0;
+ int team, i, maxHealth = 0;
+ int alienCredits = 0, humanCredits = 0;
+ gentity_t *player;
+
+ // Total up all the damage done by non-teammates
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ player = g_entities + i;
+
+ if( !OnSameTeam( self, player ) ||
+ self->buildableTeam != player->client->ps.stats[ STAT_TEAM ] )
+ totalDamage += (float)self->credits[ i ];
+ }
+
+ if( totalDamage <= 0.0f )
+ return 0.0f;
+
+ // Only give credits for killing players and buildables
+ if( self->client )
+ {
+ value = BG_GetValueOfPlayer( &self->client->ps );
+ team = self->client->pers.teamSelection;
+ maxHealth = self->client->ps.stats[ STAT_MAX_HEALTH ];
+ }
+ else if( self->s.eType == ET_BUILDABLE )
+ {
+ value = BG_Buildable( self->s.modelindex )->value;
+
+ // only give partial credits for a buildable not yet completed
+ if( !self->spawned )
+ {
+ value *= (float)( level.time - self->buildTime ) /
+ BG_Buildable( self->s.modelindex )->buildTime;
+ }
+
+ team = self->buildableTeam;
+ maxHealth = BG_Buildable( self->s.modelindex )->health;
+ }
+ else
+ return totalDamage;
+
+ // Give credits and empty the array
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ int stageValue = value * self->credits[ i ] / totalDamage;
+ player = g_entities + i;
+
+ if( player->client->pers.teamSelection != team )
+ {
+ if( totalDamage < maxHealth )
+ stageValue *= totalDamage / maxHealth;
+
+ if( !self->credits[ i ] || player->client->ps.stats[ STAT_TEAM ] == team )
+ continue;
+
+ AddScore( player, stageValue );
+
+ // killing buildables earns score, but not credits
+ if( self->s.eType != ET_BUILDABLE )
+ {
+ G_AddCreditToClient( player->client, stageValue, qtrue );
+
+ // add to stage counters
+ if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ alienCredits += stageValue;
+ else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ humanCredits += stageValue;
+ }
+ }
+ self->credits[ i ] = 0;
+ }
+
+ if( alienCredits )
+ {
+ trap_Cvar_Set( "g_alienCredits",
+ va( "%d", g_alienCredits.integer + alienCredits ) );
+ trap_Cvar_Update( &g_alienCredits );
+ }
+ if( humanCredits )
+ {
+ trap_Cvar_Set( "g_humanCredits",
+ va( "%d", g_humanCredits.integer + humanCredits ) );
+ trap_Cvar_Update( &g_humanCredits );
+ }
+
+ return totalDamage;
+}
+
+/*
+==================
player_die
==================
*/
@@ -131,18 +237,11 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
gentity_t *ent;
int anim;
int killer;
- int i, j;
- char *killerName, *obit;
- float totalTK = 0;
- float totalDamage = 0.0f;
- float percentDamage = 0.0f;
- gentity_t *player;
- qboolean tk = qfalse;
-
+ int i;
+ const char *killerName, *obit;
if( self->client->ps.pm_type == PM_DEAD )
return;
-
if( level.intermissiontime )
return;
@@ -155,27 +254,9 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
killer = attacker->s.number;
if( attacker->client )
- {
killerName = attacker->client->pers.netname;
- tk = ( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ]
- == self->client->ps.stats[ STAT_PTEAM ] );
-
- if( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] == self->client->ps.stats[ STAT_PTEAM ] )
- {
- attacker->client->pers.statscounters.teamkills++;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.teamkills++;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.teamkills++;
- }
- }
-
- }
else
- killerName = "<non-client>";
+ killerName = "<world>";
}
else
{
@@ -183,395 +264,62 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
killerName = "<world>";
}
- if( killer < 0 || killer >= MAX_CLIENTS )
- {
- killer = ENTITYNUM_WORLD;
- killerName = "<world>";
- }
-
- if( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) )
- obit = "<bad obituary>";
+ if( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) )
+ // fall back on the number
+ obit = va( "%d", meansOfDeath );
else
obit = modNames[ meansOfDeath ];
- G_LogPrintf("Kill: %i %i %i: %s^7 killed %s^7 by %s\n",
- killer, self->s.number, meansOfDeath, killerName,
- self->client->pers.netname, obit );
+ G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n",
+ killer,
+ (int)( self - g_entities ),
+ obit,
+ killerName,
+ self->client->pers.netname );
- //TA: deactivate all upgrades
+ // deactivate all upgrades
for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
BG_DeactivateUpgrade( i, self->client->ps.stats );
- if( meansOfDeath == MOD_SLAP )
- {
- trap_SendServerCommand( -1,
- va( "print \"%s^7 felt %s^7's authority\n\"",
- self->client->pers.netname, killerName ) );
- goto finish_dying;
- }
-
// broadcast the death event to everyone
- if( !tk )
- {
- ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
- ent->s.eventParm = meansOfDeath;
- ent->s.otherEntityNum = self->s.number;
- ent->s.otherEntityNum2 = killer;
- ent->r.svFlags = SVF_BROADCAST; // send to everyone
- }
- else if( attacker && attacker->client )
- {
- // tjw: obviously this is a hack and belongs in the client, but
- // this works as a temporary fix.
- trap_SendServerCommand( -1,
- va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n\"",
- self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ) );
- trap_SendServerCommand( attacker - g_entities,
- va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) );
- G_LogOnlyPrintf("%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n",
- self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] );
- G_TeamKill_Repent( attacker );
- }
+ ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
+ ent->s.eventParm = meansOfDeath;
+ ent->s.otherEntityNum = self->s.number;
+ ent->s.otherEntityNum2 = killer;
+ ent->r.svFlags = SVF_BROADCAST; // send to everyone
self->enemy = attacker;
-
self->client->ps.persistant[ PERS_KILLED ]++;
- self->client->pers.statscounters.deaths++;
- if( self->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.deaths++;
- }
- else if( self->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.deaths++;
- }
if( attacker && attacker->client )
{
attacker->client->lastkilled_client = self->s.number;
- if( g_killerHP.integer ||
- ( g_devmapKillerHP.integer && g_cheats.integer ) )
- {
- trap_SendServerCommand( self-g_entities,
- va( "print \"Your killer, %s^7, had %3i HP.\n\"",
- killerName, attacker->health ) );
- }
-
- if( attacker == self || OnSameTeam( self, attacker ) )
- {
- AddScore( attacker, -1 );
-
- // Normal teamkill penalty
- if( !g_retribution.integer )
- {
- if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- G_AddCreditToClient( attacker->client, -FREEKILL_ALIEN, qtrue );
- else if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- G_AddCreditToClient( attacker->client, -FREEKILL_HUMAN, qtrue );
- }
- }
- else
- {
- AddScore( attacker, 1 );
-
- if( g_gradualFreeFunds.integer < 2 )
- attacker->client->pers.lastFreekillTime = level.time;
- attacker->client->pers.statscounters.kills++;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.kills++;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.kills++;
- }
- }
-
- if( attacker == self )
+ if( ( attacker == self || OnSameTeam( self, attacker ) ) && meansOfDeath != MOD_HSPAWN )
{
- attacker->client->pers.statscounters.suicides++;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
+ //punish team kills and suicides
+ if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
- level.alienStatsCounters.suicides++;
+ G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue );
+ AddScore( attacker, -ALIEN_TK_SUICIDE_PENALTY );
}
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
+ else if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
- level.humanStatsCounters.suicides++;
+ G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue );
+ AddScore( attacker, -HUMAN_TK_SUICIDE_PENALTY );
}
}
}
else if( attacker->s.eType != ET_BUILDABLE )
- AddScore( self, -1 );
-
- //total up all the damage done by every client
- for( i = 0; i < MAX_CLIENTS; i++ )
{
- totalDamage += (float)self->credits[ i ];
- totalTK += (float)self->client->tkcredits[ i ];
+ if( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
+ AddScore( self, -ALIEN_TK_SUICIDE_PENALTY );
+ else if( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
+ AddScore( self, -HUMAN_TK_SUICIDE_PENALTY );
}
- // punish players for damaging teammates
- if ( g_retribution.integer && totalTK )
- {
- int totalPrice;
- int max = HUMAN_MAX_CREDITS;
-
- if ( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- totalPrice = BG_ClassCanEvolveFromTo( PCL_ALIEN_LEVEL0, self->client->ps.stats[ STAT_PCLASS ], ALIEN_MAX_KILLS, 0 );
- max = ALIEN_MAX_KILLS;
- }
- else
- {
- totalPrice = BG_GetValueOfEquipment( &self->client->ps );
- }
-
- if ( self->client->ps.persistant[ PERS_CREDIT ] + totalPrice > max )
- totalPrice = max - self->client->ps.persistant[ PERS_CREDIT ];
- if ( totalPrice > 0 )
- {
- totalTK += totalDamage;
- if( totalTK < self->client->ps.stats[ STAT_MAX_HEALTH ] )
- totalTK = self->client->ps.stats[ STAT_MAX_HEALTH ];
-
- if ( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- int price;
- // no retribution if self damage or enemy damage or building damage or no damage from this client
- if ( i == self - g_entities || !g_entities[ i ].client ||
- !OnSameTeam( &g_entities[ i ], self ) ||
- !self->client->tkcredits[ i ] )
- continue;
-
- // calculate retribution price (rounded up)
- price = ( totalPrice * self->client->tkcredits[ i ] ) / totalTK + 0.5f;
- self->client->tkcredits[ i ] = 0;
-
- // check for enough credits
- if ( g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] < price )
- price = g_entities[ i ].client->ps.persistant[ PERS_CREDIT ];
- if ( price )
- {
- G_AddCreditToClient( self->client, price, qtrue );
- G_AddCreditToClient( g_entities[ i ].client, -price, qtrue );
-
- trap_SendServerCommand( self->client->ps.clientNum,
- va( "print \"Received ^3%d credits ^7from %s ^7in retribution.\n\"",
- price, g_entities[ i ].client->pers.netname ) );
- trap_SendServerCommand( g_entities[ i ].client->ps.clientNum,
- va( "print \"Transfered ^3%d credits ^7to %s ^7in retribution.\n\"",
- price, self->client->pers.netname ) );
- }
- }
- }
- else
- {
- int toPay[ MAX_CLIENTS ] = { 0 };
- int frags = totalPrice;
- int damageForEvo = totalTK / totalPrice;
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- // no retribution if self damage or enemy damage or building damage or no damage from this client
- if ( i == self - g_entities || !g_entities[ i ].client ||
- !OnSameTeam( &g_entities[ i ], self ) ||
- !self->client->tkcredits[ i ] )
- continue;
-
- // find out how many full evos this client needs to pay
- toPay[ i ] = ( totalPrice * self->client->tkcredits[ i ] ) / totalTK;
- if ( toPay[ i ] > g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] )
- toPay[ i ] = g_entities[ i ].client->ps.persistant[ PERS_CREDIT ];
- frags -= toPay[ i ];
- self->client->tkcredits[ i ] -= damageForEvo * toPay[ i ];
- }
-
- // if we have not met the evo count, continue stealing evos
- while ( 1 )
- {
- int maximum = 0;
- int topClient = 0;
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- if ( self->client->tkcredits[ i ] > maximum && g_entities[ i ].client->ps.persistant[ PERS_CREDIT ] )
- {
- maximum = self->client->tkcredits[ i ];
- topClient = i;
- }
- }
- if ( !maximum )
- break;
- toPay[ topClient ]++;
- self->client->tkcredits[ topClient ] = 0;
- frags--;
- if ( !frags )
- break;
- }
-
- // now move the evos around
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- if ( !toPay[ i ] )
- continue;
-
- G_AddCreditToClient( self->client, toPay[ i ], qtrue );
- G_AddCreditToClient( g_entities[ i ].client, -toPay[ i ], qtrue );
-
- trap_SendServerCommand( self->client->ps.clientNum,
- va( "print \"Received ^3%d ^7evos from %s ^7in retribution.\n\"",
- toPay[ i ], g_entities[ i ].client->pers.netname ) );
- trap_SendServerCommand( g_entities[ i ].client->ps.clientNum,
- va( "print \"Transfered ^3%d ^7evos to %s ^7in retribution.\n\"",
- toPay[ i ], self->client->pers.netname ) );
- }
- }
- }
- }
-
- // if players did more than DAMAGE_FRACTION_FOR_KILL increment the stage counters
- if( !OnSameTeam( self, attacker ) && totalDamage >= ( self->client->ps.stats[ STAT_MAX_HEALTH ] * DAMAGE_FRACTION_FOR_KILL ) )
- {
- if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- trap_Cvar_Set( "g_alienKills", va( "%d", g_alienKills.integer + 1 ) );
- if( g_alienStage.integer < 2 )
- {
- self->client->pers.statscounters.feeds++;
- level.humanStatsCounters.feeds++;
- }
- }
- else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- trap_Cvar_Set( "g_humanKills", va( "%d", g_humanKills.integer + 1 ) );
- if( g_humanStage.integer < 2 )
- {
- self->client->pers.statscounters.feeds++;
- level.alienStatsCounters.feeds++;
- }
- }
- }
-
- if( totalDamage > 0.0f )
- {
- if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- //nice simple happy bouncy human land
- float classValue = BG_FindValueOfClass( self->client->ps.stats[ STAT_PCLASS ] );
-
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- player = g_entities + i;
-
- if( !player->client )
- continue;
-
- if( player->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
- continue;
-
- if( !self->credits[ i ] )
- continue;
-
- percentDamage = (float)self->credits[ i ] / totalDamage;
- if( percentDamage > 0 && percentDamage < 1)
- {
- player->client->pers.statscounters.assists++;
- level.humanStatsCounters.assists++;
- }
-
- //add credit
- G_AddCreditToClient( player->client,
- (int)( classValue * percentDamage ), qtrue );
- }
- }
- else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- //horribly complex nasty alien land
- float humanValue = BG_GetValueOfHuman( &self->client->ps );
- int frags;
- int unclaimedFrags = (int)humanValue;
-
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- player = g_entities + i;
-
- if( !player->client )
- continue;
-
- if( player->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
- continue;
-
- //this client did no damage
- if( !self->credits[ i ] )
- continue;
-
- //nothing left to claim
- if( !unclaimedFrags )
- break;
-
- percentDamage = (float)self->credits[ i ] / totalDamage;
- if( percentDamage > 0 && percentDamage < 1)
- {
- player->client->pers.statscounters.assists++;
- level.alienStatsCounters.assists++;
- }
-
- frags = (int)floor( humanValue * percentDamage);
-
- if( frags > 0 )
- {
- //add kills
- G_AddCreditToClient( player->client, frags, qtrue );
-
- //can't revist this account later
- self->credits[ i ] = 0;
-
- //reduce frags left to be claimed
- unclaimedFrags -= frags;
- }
- }
-
- //there are frags still to be claimed
- if( unclaimedFrags )
- {
- //the clients remaining at this point do not
- //have enough credit to claim even one frag
- //so simply give the top <unclaimedFrags> clients
- //a frag each
-
- for( i = 0; i < unclaimedFrags; i++ )
- {
- int maximum = 0;
- int topClient = 0;
-
- for( j = 0; j < MAX_CLIENTS; j++ )
- {
- //this client did no damage
- if( !self->credits[ j ] )
- continue;
-
- if( self->credits[ j ] > maximum )
- {
- maximum = self->credits[ j ];
- topClient = j;
- }
- }
-
- if( maximum > 0 )
- {
- player = g_entities + topClient;
-
- //add kills
- G_AddCreditToClient( player->client, 1, qtrue );
-
- //can't revist this account again
- self->credits[ topClient ] = 0;
- }
- }
- }
- }
- }
+ // give credits for killing this player
+ G_RewardAttackers( self );
ScoreboardMessage( self ); // show scores
@@ -585,29 +333,28 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( client->pers.connected != CON_CONNECTED )
continue;
- if( client->sess.sessionTeam != TEAM_SPECTATOR )
+ if( client->sess.spectatorState == SPECTATOR_NOT )
continue;
if( client->sess.spectatorClient == self->s.number )
ScoreboardMessage( g_entities + i );
}
-finish_dying: // from MOD_SLAP
-
- VectorCopy( self->s.origin, self->client->pers.lastDeathLocation );
+ VectorCopy( self->r.currentOrigin, self->client->pers.lastDeathLocation );
self->takedamage = qfalse; // can still be gibbed
self->s.weapon = WP_NONE;
- self->r.contents = CONTENTS_CORPSE;
+ if( self->client->noclip )
+ self->client->cliprcontents = CONTENTS_CORPSE;
+ else
+ self->r.contents = CONTENTS_CORPSE;
- self->s.angles[ PITCH ] = 0;
- self->s.angles[ ROLL ] = 0;
- self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ];
+ self->client->ps.viewangles[ PITCH ] = 0; // zomg
+ self->client->ps.viewangles[ YAW ] = self->s.apos.trBase[ YAW ];
+ self->client->ps.viewangles[ ROLL ] = 0;
LookAtKiller( self, inflictor, attacker );
- VectorCopy( self->s.angles, self->client->ps.viewangles );
-
self->s.loopSound = 0;
self->r.maxs[ 2 ] = -8;
@@ -674,57 +421,55 @@ finish_dying: // from MOD_SLAP
}
trap_LinkEntity( self );
-}
-
-////////TA: locdamage
+ self->client->pers.infoChangeTime = level.time;
+}
/*
===============
-G_ParseArmourScript
+G_ParseDmgScript
===============
*/
-void G_ParseArmourScript( char *buf, int upgrade )
+static int G_ParseDmgScript( damageRegion_t *regions, char *buf )
{
char *token;
+ float angleSpan, heightSpan;
int count;
- count = 0;
-
- while( 1 )
+ for( count = 0; ; count++ )
{
token = COM_Parse( &buf );
-
- if( !token[0] )
+ if( !token[ 0 ] )
break;
if( strcmp( token, "{" ) )
{
- G_Printf( "Missing { in armour file\n" );
+ COM_ParseError( "Missing {" );
break;
}
- if( count == MAX_ARMOUR_REGIONS )
+ if( count >= MAX_DAMAGE_REGIONS )
{
- G_Printf( "Max armour regions exceeded in locdamage file\n" );
+ COM_ParseError( "Max damage regions exceeded" );
break;
}
- //default
- g_armourRegions[ upgrade ][ count ].minHeight = 0.0;
- g_armourRegions[ upgrade ][ count ].maxHeight = 1.0;
- g_armourRegions[ upgrade ][ count ].minAngle = 0;
- g_armourRegions[ upgrade ][ count ].maxAngle = 360;
- g_armourRegions[ upgrade ][ count ].modifier = 1.0;
- g_armourRegions[ upgrade ][ count ].crouch = qfalse;
+ // defaults
+ regions[ count ].name[ 0 ] = '\0';
+ regions[ count ].minHeight = 0.0f;
+ regions[ count ].maxHeight = 1.0f;
+ regions[ count ].minAngle = 0.0f;
+ regions[ count ].maxAngle = 360.0f;
+ regions[ count ].modifier = 1.0f;
+ regions[ count ].crouch = qfalse;
while( 1 )
{
token = COM_ParseExt( &buf, qtrue );
- if( !token[0] )
+ if( !token[ 0 ] )
{
- G_Printf( "Unexpected end of armour file\n" );
+ COM_ParseError( "Unexpected end of file" );
break;
}
@@ -732,172 +477,260 @@ void G_ParseArmourScript( char *buf, int upgrade )
{
break;
}
+ else if( !strcmp( token, "name" ) )
+ {
+ token = COM_ParseExt( &buf, qfalse );
+ if( token[ 0 ] )
+ Q_strncpyz( regions[ count ].name, token,
+ sizeof( regions[ count ].name ) );
+ }
else if( !strcmp( token, "minHeight" ) )
{
token = COM_ParseExt( &buf, qfalse );
-
- if ( !token[0] )
+ if( !token[ 0 ] )
strcpy( token, "0" );
-
- g_armourRegions[ upgrade ][ count ].minHeight = atof( token );
+ regions[ count ].minHeight = atof( token );
}
else if( !strcmp( token, "maxHeight" ) )
{
token = COM_ParseExt( &buf, qfalse );
-
- if ( !token[0] )
+ if( !token[ 0 ] )
strcpy( token, "100" );
-
- g_armourRegions[ upgrade ][ count ].maxHeight = atof( token );
+ regions[ count ].maxHeight = atof( token );
}
else if( !strcmp( token, "minAngle" ) )
{
token = COM_ParseExt( &buf, qfalse );
-
- if ( !token[0] )
+ if( !token[ 0 ] )
strcpy( token, "0" );
-
- g_armourRegions[ upgrade ][ count ].minAngle = atoi( token );
+ regions[ count ].minAngle = atoi( token );
}
else if( !strcmp( token, "maxAngle" ) )
{
token = COM_ParseExt( &buf, qfalse );
-
- if ( !token[0] )
+ if( !token[ 0 ] )
strcpy( token, "360" );
-
- g_armourRegions[ upgrade ][ count ].maxAngle = atoi( token );
+ regions[ count ].maxAngle = atoi( token );
}
else if( !strcmp( token, "modifier" ) )
{
token = COM_ParseExt( &buf, qfalse );
-
- if ( !token[0] )
+ if( !token[ 0 ] )
strcpy( token, "1.0" );
-
- g_armourRegions[ upgrade ][ count ].modifier = atof( token );
+ regions[ count ].modifier = atof( token );
}
else if( !strcmp( token, "crouch" ) )
{
- g_armourRegions[ upgrade ][ count ].crouch = qtrue;
+ regions[ count ].crouch = qtrue;
+ }
+ else
+ {
+ COM_ParseWarning("Unknown token \"%s\"", token);
}
}
-
- g_numArmourRegions[ upgrade ]++;
- count++;
+
+ // Angle portion covered
+ angleSpan = regions[ count ].maxAngle - regions[ count ].minAngle;
+ if( angleSpan < 0.0f )
+ angleSpan += 360.0f;
+ angleSpan /= 360.0f;
+
+ // Height portion covered
+ heightSpan = regions[ count ].maxHeight - regions[ count ].minHeight;
+ if( heightSpan < 0.0f )
+ heightSpan = -heightSpan;
+ if( heightSpan > 1.0f )
+ heightSpan = 1.0f;
+
+ regions[ count ].area = angleSpan * heightSpan;
+ if( !regions[ count ].area )
+ regions[ count ].area = 0.00001f;
}
+
+ return count;
}
-
/*
-===============
-G_ParseDmgScript
-===============
+============
+GetRegionDamageModifier
+============
*/
-void G_ParseDmgScript( char *buf, int class )
+static float GetRegionDamageModifier( gentity_t *targ, int class, int piece )
{
- char *token;
- int count;
-
- count = 0;
-
- while( 1 )
+ damageRegion_t *regions, *overlap;
+ float modifier = 0.0f, areaSum = 0.0f;
+ int j, i;
+ qboolean crouch;
+
+ crouch = targ->client->ps.pm_flags & PMF_DUCKED;
+ overlap = &g_damageRegions[ class ][ piece ];
+
+ if( g_debugDamage.integer > 2 )
+ G_Printf( "GetRegionDamageModifier():\n"
+ ". bodyRegion = [%d %d %f %f] (%s)\n"
+ ". modifier = %f\n",
+ overlap->minAngle, overlap->maxAngle,
+ overlap->minHeight, overlap->maxHeight,
+ overlap->name, overlap->modifier );
+
+ // Find the armour layer modifier, assuming that none of the armour regions
+ // overlap and that any areas that are not covered have a modifier of 1.0
+ for( j = UP_NONE + 1; j < UP_NUM_UPGRADES; j++ )
{
- token = COM_Parse( &buf );
-
- if( !token[0] )
- break;
-
- if( strcmp( token, "{" ) )
- {
- G_Printf( "Missing { in locdamage file\n" );
- break;
- }
-
- if( count == MAX_LOCDAMAGE_REGIONS )
- {
- G_Printf( "Max damage regions exceeded in locdamage file\n" );
- break;
- }
-
- //default
- g_damageRegions[ class ][ count ].minHeight = 0.0;
- g_damageRegions[ class ][ count ].maxHeight = 1.0;
- g_damageRegions[ class ][ count ].minAngle = 0;
- g_damageRegions[ class ][ count ].maxAngle = 360;
- g_damageRegions[ class ][ count ].modifier = 1.0;
- g_damageRegions[ class ][ count ].crouch = qfalse;
-
- while( 1 )
+ if( !BG_InventoryContainsUpgrade( j, targ->client->ps.stats ) ||
+ !g_numArmourRegions[ j ] )
+ continue;
+ regions = g_armourRegions[ j ];
+
+ for( i = 0; i < g_numArmourRegions[ j ]; i++ )
{
- token = COM_ParseExt( &buf, qtrue );
-
- if( !token[0] )
+ float overlapMaxA, regionMinA, regionMaxA, angleSpan, heightSpan, area;
+
+ if( regions[ i ].crouch != crouch )
+ continue;
+
+ // Convert overlap angle to 0 to max
+ overlapMaxA = overlap->maxAngle - overlap->minAngle;
+ if( overlapMaxA < 0.0f )
+ overlapMaxA += 360.0f;
+
+ // Convert region angles to match overlap
+ regionMinA = regions[ i ].minAngle - overlap->minAngle;
+ if( regionMinA < 0.0f )
+ regionMinA += 360.0f;
+ regionMaxA = regions[ i ].maxAngle - overlap->minAngle;
+ if( regionMaxA < 0.0f )
+ regionMaxA += 360.0f;
+
+ // Overlapping Angle portion
+ if( regionMinA <= regionMaxA )
{
- G_Printf( "Unexpected end of locdamage file\n" );
- break;
+ angleSpan = 0.0f;
+ if( regionMinA < overlapMaxA )
+ {
+ if( regionMaxA > overlapMaxA )
+ regionMaxA = overlapMaxA;
+ angleSpan = regionMaxA - regionMinA;
+ }
}
-
- if( !Q_stricmp( token, "}" ) )
+ else
{
- break;
+ if( regionMaxA > overlapMaxA )
+ regionMaxA = overlapMaxA;
+ angleSpan = regionMaxA;
+ if( regionMinA < overlapMaxA )
+ angleSpan += overlapMaxA - regionMinA;
}
- else if( !strcmp( token, "minHeight" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
+ angleSpan /= 360.0f;
+
+ // Overlapping height portion
+ heightSpan = MIN( overlap->maxHeight, regions[ i ].maxHeight ) -
+ MAX( overlap->minHeight, regions[ i ].minHeight );
+ if( heightSpan < 0.0f )
+ heightSpan = 0.0f;
+ if( heightSpan > 1.0f )
+ heightSpan = 1.0f;
+
+ if( g_debugDamage.integer > 2 )
+ G_Printf( ". armourRegion = [%d %d %f %f] (%s)\n"
+ ". . modifier = %f\n"
+ ". . angleSpan = %f\n"
+ ". . heightSpan = %f\n",
+ regions[ i ].minAngle, regions[ i ].maxAngle,
+ regions[ i ].minHeight, regions[ i ].maxHeight,
+ regions[ i ].name, regions[ i ].modifier,
+ angleSpan, heightSpan );
+
+ areaSum += area = angleSpan * heightSpan;
+ modifier += regions[ i ].modifier * area;
+ }
+ }
- if ( !token[0] )
- strcpy( token, "0" );
+ if( g_debugDamage.integer > 2 )
+ G_Printf( ". areaSum = %f\n"
+ ". armourModifier = %f\n", areaSum, modifier );
- g_damageRegions[ class ][ count ].minHeight = atof( token );
- }
- else if( !strcmp( token, "maxHeight" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
+ return overlap->modifier * ( overlap->area + modifier - areaSum );
+}
- if ( !token[0] )
- strcpy( token, "100" );
+/*
+============
+GetNonLocDamageModifier
+============
+*/
+static float GetNonLocDamageModifier( gentity_t *targ, int class )
+{
+ float modifier = 0.0f, area = 0.0f, scale = 0.0f;
+ int i;
+ qboolean crouch;
+
+ // For every body region, use stretch-armor formula to apply armour modifier
+ // for any overlapping area that armour shares with the body region
+ crouch = targ->client->ps.pm_flags & PMF_DUCKED;
+ for( i = 0; i < g_numDamageRegions[ class ]; i++ )
+ {
+ damageRegion_t *region;
- g_damageRegions[ class ][ count ].maxHeight = atof( token );
- }
- else if( !strcmp( token, "minAngle" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
+ region = &g_damageRegions[ class ][ i ];
- if ( !token[0] )
- strcpy( token, "0" );
+ if( region->crouch != crouch )
+ continue;
- g_damageRegions[ class ][ count ].minAngle = atoi( token );
- }
- else if( !strcmp( token, "maxAngle" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
+ modifier += GetRegionDamageModifier( targ, class, i );
- if ( !token[0] )
- strcpy( token, "360" );
+ scale += region->modifier * region->area;
+ area += region->area;
- g_damageRegions[ class ][ count ].maxAngle = atoi( token );
- }
- else if( !strcmp( token, "modifier" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
+ }
- if ( !token[0] )
- strcpy( token, "1.0" );
+ modifier = !scale ? 1.0f : 1.0f + ( modifier / scale - 1.0f ) * area;
+
+ if( g_debugDamage.integer > 1 )
+ G_Printf( "GetNonLocDamageModifier() modifier:%f, area:%f, scale:%f\n",
+ modifier, area, scale );
+
+ return modifier;
+}
- g_damageRegions[ class ][ count ].modifier = atof( token );
- }
- else if( !strcmp( token, "crouch" ) )
- {
- g_damageRegions[ class ][ count ].crouch = qtrue;
- }
- }
+/*
+============
+GetPointDamageModifier
+
+Returns the damage region given an angle and a height proportion
+============
+*/
+static float GetPointDamageModifier( gentity_t *targ, damageRegion_t *regions,
+ int len, float angle, float height )
+{
+ float modifier = 1.0f;
+ int i;
- g_numDamageRegions[ class ]++;
- count++;
+ for( i = 0; i < len; i++ )
+ {
+ if( regions[ i ].crouch != ( targ->client->ps.pm_flags & PMF_DUCKED ) )
+ continue;
+
+ // Angle must be within range
+ if( ( regions[ i ].minAngle <= regions[ i ].maxAngle &&
+ ( angle < regions[ i ].minAngle ||
+ angle > regions[ i ].maxAngle ) ) ||
+ ( regions[ i ].minAngle > regions[ i ].maxAngle &&
+ angle > regions[ i ].maxAngle && angle < regions[ i ].minAngle ) )
+ continue;
+
+ // Height must be within range
+ if( height < regions[ i ].minHeight || height > regions[ i ].maxHeight )
+ continue;
+
+ modifier *= regions[ i ].modifier;
}
-}
+ if( g_debugDamage.integer )
+ G_Printf( "GetDamageRegionModifier(angle = %f, height = %f): %f\n",
+ angle, height, modifier );
+
+ return modifier;
+}
/*
============
@@ -906,34 +739,33 @@ G_CalcDamageModifier
*/
static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *attacker, int class, int dflags )
{
- vec3_t targOrigin;
- vec3_t bulletPath;
- vec3_t bulletAngle;
- vec3_t pMINUSfloor, floor, normal;
-
- float clientHeight, hitRelative, hitRatio;
- int bulletRotation, clientRotation, hitRotation;
- float modifier = 1.0f;
- int i, j;
+ vec3_t targOrigin, bulletPath, bulletAngle, pMINUSfloor, floor, normal;
+ float clientHeight, hitRelative, hitRatio, modifier;
+ int hitRotation, i;
if( point == NULL )
return 1.0f;
+ // Don't need to calculate angles and height for non-locational damage
+ if( dflags & DAMAGE_NO_LOCDAMAGE )
+ return GetNonLocDamageModifier( targ, class );
+
+ // Get the point location relative to the floor under the target
if( g_unlagged.integer && targ->client && targ->client->unlaggedCalc.used )
VectorCopy( targ->client->unlaggedCalc.origin, targOrigin );
else
VectorCopy( targ->r.currentOrigin, targOrigin );
- clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ];
-
- if( targ->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorCopy( targ->client->ps.grapplePoint, normal );
- else
- VectorSet( normal, 0, 0, 1 );
-
+ BG_GetClientNormal( &targ->client->ps, normal );
VectorMA( targOrigin, targ->r.mins[ 2 ], normal, floor );
VectorSubtract( point, floor, pMINUSfloor );
+ // Get the proportion of the target height where the hit landed
+ clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ];
+
+ if( !clientHeight )
+ clientHeight = 1.0f;
+
hitRelative = DotProduct( normal, pMINUSfloor ) / VectorLength( normal );
if( hitRelative < 0.0f )
@@ -944,105 +776,25 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att
hitRatio = hitRelative / clientHeight;
- VectorSubtract( targOrigin, point, bulletPath );
+ // Get the yaw of the attack relative to the target's view yaw
+ VectorSubtract( point, targOrigin, bulletPath );
vectoangles( bulletPath, bulletAngle );
- clientRotation = targ->client->ps.viewangles[ YAW ];
- bulletRotation = bulletAngle[ YAW ];
-
- hitRotation = abs( clientRotation - bulletRotation );
+ hitRotation = AngleNormalize360( targ->client->ps.viewangles[ YAW ] -
+ bulletAngle[ YAW ] );
- hitRotation = hitRotation % 360; // Keep it in the 0-359 range
+ // Get modifiers from the target's damage regions
+ modifier = GetPointDamageModifier( targ, g_damageRegions[ class ],
+ g_numDamageRegions[ class ],
+ hitRotation, hitRatio );
- if( dflags & DAMAGE_NO_LOCDAMAGE )
+ for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
{
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
+ if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
{
- float totalModifier = 0.0f;
- float averageModifier = 1.0f;
-
- //average all of this upgrade's armour regions together
- if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
- {
- for( j = 0; j < g_numArmourRegions[ i ]; j++ )
- totalModifier += g_armourRegions[ i ][ j ].modifier;
-
- if( g_numArmourRegions[ i ] )
- averageModifier = totalModifier / g_numArmourRegions[ i ];
- else
- averageModifier = 1.0f;
- }
-
- modifier *= averageModifier;
- }
- }
- else
- {
- if( attacker && attacker->client )
- {
- attacker->client->pers.statscounters.hitslocational++;
- level.alienStatsCounters.hitslocational++;
- }
- for( i = 0; i < g_numDamageRegions[ class ]; i++ )
- {
- qboolean rotationBound;
-
- if( g_damageRegions[ class ][ i ].minAngle >
- g_damageRegions[ class ][ i ].maxAngle )
- {
- rotationBound = ( hitRotation >= g_damageRegions[ class ][ i ].minAngle &&
- hitRotation <= 360 ) || ( hitRotation >= 0 &&
- hitRotation <= g_damageRegions[ class ][ i ].maxAngle );
- }
- else
- {
- rotationBound = ( hitRotation >= g_damageRegions[ class ][ i ].minAngle &&
- hitRotation <= g_damageRegions[ class ][ i ].maxAngle );
- }
-
- if( rotationBound &&
- hitRatio >= g_damageRegions[ class ][ i ].minHeight &&
- hitRatio <= g_damageRegions[ class ][ i ].maxHeight &&
- ( g_damageRegions[ class ][ i ].crouch ==
- ( targ->client->ps.pm_flags & PMF_DUCKED ) ) )
- modifier *= g_damageRegions[ class ][ i ].modifier;
- }
-
- if( attacker && attacker->client && modifier == 2 )
- {
- attacker->client->pers.statscounters.headshots++;
- level.alienStatsCounters.headshots++;
- }
-
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
- {
- for( j = 0; j < g_numArmourRegions[ i ]; j++ )
- {
- qboolean rotationBound;
-
- if( g_armourRegions[ i ][ j ].minAngle >
- g_armourRegions[ i ][ j ].maxAngle )
- {
- rotationBound = ( hitRotation >= g_armourRegions[ i ][ j ].minAngle &&
- hitRotation <= 360 ) || ( hitRotation >= 0 &&
- hitRotation <= g_armourRegions[ i ][ j ].maxAngle );
- }
- else
- {
- rotationBound = ( hitRotation >= g_armourRegions[ i ][ j ].minAngle &&
- hitRotation <= g_armourRegions[ i ][ j ].maxAngle );
- }
-
- if( rotationBound &&
- hitRatio >= g_armourRegions[ i ][ j ].minHeight &&
- hitRatio <= g_armourRegions[ i ][ j ].maxHeight &&
- ( g_armourRegions[ i ][ j ].crouch ==
- ( targ->client->ps.pm_flags & PMF_DUCKED ) ) )
- modifier *= g_armourRegions[ i ][ j ].modifier;
- }
- }
+ modifier *= GetPointDamageModifier( targ, g_armourRegions[ i ],
+ g_numArmourRegions[ i ],
+ hitRotation, hitRatio );
}
}
@@ -1062,37 +814,41 @@ void G_InitDamageLocations( void )
int i;
int len;
fileHandle_t fileHandle;
- char buffer[ MAX_LOCDAMAGE_TEXT ];
+ char buffer[ MAX_DAMAGE_REGION_TEXT ];
for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
{
- modelName = BG_FindModelNameForClass( i );
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/locdamage.cfg", modelName );
+ modelName = BG_ClassConfig( i )->modelName;
+ Com_sprintf( filename, sizeof( filename ),
+ "models/players/%s/locdamage.cfg", modelName );
len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );
if ( !fileHandle )
{
- G_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
+ G_Printf( S_COLOR_RED "file not found: %s\n", filename );
continue;
}
- if( len >= MAX_LOCDAMAGE_TEXT )
+ if( len >= MAX_DAMAGE_REGION_TEXT )
{
- G_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_LOCDAMAGE_TEXT ) );
+ G_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i",
+ filename, len, MAX_DAMAGE_REGION_TEXT );
trap_FS_FCloseFile( fileHandle );
continue;
}
+ COM_BeginParseSession( filename );
+
trap_FS_Read( buffer, len, fileHandle );
buffer[len] = 0;
trap_FS_FCloseFile( fileHandle );
- G_ParseDmgScript( buffer, i );
+ g_numDamageRegions[ i ] = G_ParseDmgScript( g_damageRegions[ i ], buffer );
}
for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
{
- modelName = BG_FindNameForUpgrade( i );
+ modelName = BG_Upgrade( i )->name;
Com_sprintf( filename, sizeof( filename ), "armour/%s.armour", modelName );
len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );
@@ -1101,23 +857,24 @@ void G_InitDamageLocations( void )
if ( !fileHandle )
continue;
- if( len >= MAX_LOCDAMAGE_TEXT )
+ if( len >= MAX_DAMAGE_REGION_TEXT )
{
- G_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_LOCDAMAGE_TEXT ) );
+ G_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i",
+ filename, len, MAX_DAMAGE_REGION_TEXT );
trap_FS_FCloseFile( fileHandle );
continue;
}
+ COM_BeginParseSession( filename );
+
trap_FS_Read( buffer, len, fileHandle );
buffer[len] = 0;
trap_FS_FCloseFile( fileHandle );
- G_ParseArmourScript( buffer, i );
+ g_numArmourRegions[ i ] = G_ParseDmgScript( g_armourRegions[ i ], buffer );
}
}
-////////TA: locdamage
-
/*
============
@@ -1138,16 +895,16 @@ inflictor, attacker, dir, and point can be NULL for environmental effects
dflags these flags are used to control how T_Damage works
DAMAGE_RADIUS damage was indirect (from a nearby explosion)
DAMAGE_NO_ARMOR armor does not protect from this damage
- DAMAGE_KNOCKBACK affect velocity, not just view angles
- DAMAGE_NO_PROTECTION kills godmode, armor, everything
+ DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
+ DAMAGE_NO_PROTECTION kills everything except godmode
============
*/
-//TA: team is the team that is immune to this damage
+// team is the team that is immune to this damage
void G_SelectiveDamage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
vec3_t dir, vec3_t point, int damage, int dflags, int mod, int team )
{
- if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] ) )
+ if( targ->client && ( team != targ->client->ps.stats[ STAT_TEAM ] ) )
G_Damage( targ, inflictor, attacker, dir, point, damage, dflags, mod );
}
@@ -1156,18 +913,11 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
{
gclient_t *client;
int take;
- int save;
int asave = 0;
- int knockback = 0;
- float damagemodifier=0.0;
- int takeNoOverkill;
-
- if( !targ->takedamage )
- return;
+ int knockback;
- // the intermission has allready been qualified for, so don't
- // allow any extra scoring
- if( level.intermissionQueued )
+ // Can't deal damage sometimes
+ if( !targ->takedamage || targ->health <= 0 || level.intermissionQueued )
return;
if( !inflictor )
@@ -1176,9 +926,6 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
if( !attacker )
attacker = &g_entities[ ENTITYNUM_WORLD ];
- if( attacker->client && attacker->client->pers.paused )
- return;
-
// shootable doors / buttons don't actually have any health
if( targ->s.eType == ET_MOVER )
{
@@ -1190,42 +937,41 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
client = targ->client;
-
- if( client )
- {
- if( client->noclip && !g_devmapNoGod.integer)
- return;
- if( client->pers.paused )
- return;
- }
+ if( client && client->noclip )
+ return;
if( !dir )
- dflags &= ~DAMAGE_KNOCKBACK;
+ dflags |= DAMAGE_NO_KNOCKBACK;
else
VectorNormalize( dir );
- if( dflags & DAMAGE_KNOCKBACK )
+ knockback = damage;
+
+ if( inflictor->s.weapon != WP_NONE )
+ {
+ knockback = (int)( (float)knockback *
+ BG_Weapon( inflictor->s.weapon )->knockbackScale );
+ }
+
+ if( targ->client )
{
- knockback = damage;
+ knockback = (int)( (float)knockback *
+ BG_Class( targ->client->ps.stats[ STAT_CLASS ] )->knockbackScale );
+ }
- if( inflictor->s.weapon != WP_NONE )
- {
- knockback = (int)( (float)knockback *
- BG_FindKnockbackScaleForWeapon( inflictor->s.weapon ) );
- }
+ // Too much knockback from falling really far makes you "bounce" and
+ // looks silly. However, none at all also looks bad. Cap it.
+ if( mod == MOD_FALLING && knockback > 50 )
+ knockback = 50;
- if( targ->client )
- {
- knockback = (int)( (float)knockback *
- BG_FindKnockbackScaleForClass( targ->client->ps.stats[ STAT_PCLASS ] ) );
- }
+ if( knockback > 200 )
+ knockback = 200;
- if( knockback > 200 )
- knockback = 200;
+ if( targ->flags & FL_NO_KNOCKBACK )
+ knockback = 0;
- if( targ->flags & FL_NO_KNOCKBACK )
- knockback = 0;
- }
+ if( dflags & DAMAGE_NO_KNOCKBACK )
+ knockback = 0;
// figure momentum add, even if the damage won't be taken
if( knockback && targ->client )
@@ -1256,6 +1002,18 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
}
+ // check for godmode
+ if( targ->flags & FL_GODMODE )
+ return;
+
+ // don't do friendly fire on movement attacks
+ if( ( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
+ mod == MOD_LEVEL4_CRUSH ) &&
+ targ->s.eType == ET_BUILDABLE && targ->buildableTeam == TEAM_ALIENS )
+ {
+ return;
+ }
+
// check for completely getting out of the damage
if( !( dflags & DAMAGE_NO_PROTECTION ) )
{
@@ -1264,8 +1022,14 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
// if the attacker was on the same team
if( targ != attacker && OnSameTeam( targ, attacker ) )
{
+ // don't do friendly fire on movement attacks
+ if( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
+ mod == MOD_LEVEL4_CRUSH )
+ return;
+
+ // if dretchpunt is enabled and this is a dretch, do dretchpunt instead of damage
if( g_dretchPunt.integer &&
- targ->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL0 )
+ targ->client->ps.stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL0 )
{
vec3_t dir, push;
@@ -1275,67 +1039,33 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
push[2] = 64.0f;
VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity );
return;
- }
- else if(mod == MOD_LEVEL4_CHARGE || mod == MOD_LEVEL3_POUNCE )
- { // don't do friendly fire on movement attacks
- if( g_friendlyFireMovementAttacks.value <= 0 || ( g_friendlyFire.value<=0 && g_friendlyFireAliens.value<=0 ) )
- return;
- else if( g_friendlyFireMovementAttacks.value > 0 && g_friendlyFireMovementAttacks.value < 1 )
- damage =(int)(0.5 + g_friendlyFireMovementAttacks.value * (float) damage);
- }
- else if( g_friendlyFire.value <=0)
- {
- if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- if(g_friendlyFireHumans.value<=0)
- return;
- else if( g_friendlyFireHumans.value > 0 && g_friendlyFireHumans.value < 1 )
- damage =(int)(0.5 + g_friendlyFireHumans.value * (float) damage);
- }
- if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- if(g_friendlyFireAliens.value==0)
- return;
- else if( g_friendlyFireAliens.value > 0 && g_friendlyFireAliens.value < 1 )
- damage =(int)(0.5 + g_friendlyFireAliens.value * (float) damage);
- }
}
- else if( g_friendlyFire.value > 0 && g_friendlyFire.value < 1 )
+
+ // check if friendly fire has been disabled
+ if( !g_friendlyFire.integer )
{
- damage =(int)(0.5 + g_friendlyFire.value * (float) damage);
+ return;
}
}
- // If target is buildable on the same team as the attacking client
if( targ->s.eType == ET_BUILDABLE && attacker->client &&
- targ->biteam == attacker->client->pers.teamSelection )
+ mod != MOD_DECONSTRUCT && mod != MOD_SUICIDE &&
+ mod != MOD_REPLACE && mod != MOD_NOCREEP )
{
- if(mod == MOD_LEVEL4_CHARGE || mod == MOD_LEVEL3_POUNCE )
- {
- if(g_friendlyFireMovementAttacks.value <= 0)
- return;
- else if(g_friendlyFireMovementAttacks.value > 0 && g_friendlyFireMovementAttacks.value < 1)
- damage =(int)(0.5 + g_friendlyFireMovementAttacks.value * (float) damage);
- }
- if( g_friendlyBuildableFire.value <= 0 )
+ if( targ->buildableTeam == attacker->client->pers.teamSelection &&
+ !g_friendlyBuildableFire.integer )
{
return;
}
- else if( g_friendlyBuildableFire.value > 0 && g_friendlyBuildableFire.value < 1 )
+
+ // base is under attack warning if DCC'd
+ if( targ->buildableTeam == TEAM_HUMANS && G_FindDCC( targ ) &&
+ level.time > level.humanBaseAttackTimer )
{
- damage =(int)(0.5 + g_friendlyBuildableFire.value * (float) damage);
+ level.humanBaseAttackTimer = level.time + DC_ATTACK_PERIOD;
+ G_BroadcastEvent( EV_DCC_ATTACK, 0 );
}
}
-
- // check for godmode
- if ( targ->flags & FL_GODMODE && !g_devmapNoGod.integer)
- return;
-
- if( level.paused )
- return;
-
- if(targ->s.eType == ET_BUILDABLE && g_cheats.integer && g_devmapNoStructDmg.integer)
- return;
}
// add to the attacker's hit counter
@@ -1350,7 +1080,6 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
}
take = damage;
- save = 0;
// add to the damage inflicted on a player this frame
// the total will be turned into screen blends and view angle kicks
@@ -1380,23 +1109,21 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
// set the last client who damaged the target
targ->client->lasthurt_client = attacker->s.number;
targ->client->lasthurt_mod = mod;
-
- damagemodifier = G_CalcDamageModifier( point, targ, attacker, client->ps.stats[ STAT_PCLASS ], dflags );
- take = (int)( (float)take * damagemodifier );
+ take = (int)( take * G_CalcDamageModifier( point, targ, attacker,
+ client->ps.stats[ STAT_CLASS ],
+ dflags ) + 0.5f );
//if boosted poison every attack
if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
{
- if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
- !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) &&
- mod != MOD_LEVEL2_ZAP &&
- targ->client->poisonImmunityTime < level.time )
+ if( targ->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS &&
+ mod != MOD_LEVEL2_ZAP && mod != MOD_POISON &&
+ mod != MOD_LEVEL1_PCLOUD && mod != MOD_HSPAWN &&
+ mod != MOD_ASPAWN && targ->client->poisonImmunityTime < level.time )
{
targ->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
targ->client->lastPoisonTime = level.time;
targ->client->lastPoisonClient = attacker;
- attacker->client->pers.statscounters.repairspoisons++;
- level.alienStatsCounters.repairspoisons++;
}
}
}
@@ -1410,90 +1137,23 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
targ->health, take, asave );
}
- takeNoOverkill = take;
- if( takeNoOverkill > targ->health )
- {
- if(targ->health > 0)
- takeNoOverkill = targ->health;
- else
- takeNoOverkill = 0;
- }
-
+ // do the damage
if( take )
{
- //Increment some stats counters
- if( attacker && attacker->client )
- {
- if( targ->biteam == attacker->client->pers.teamSelection || OnSameTeam( targ, attacker ) )
- {
- attacker->client->pers.statscounters.ffdmgdone += takeNoOverkill;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.ffdmgdone+=takeNoOverkill;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.ffdmgdone+=takeNoOverkill;
- }
- }
- else if( targ->s.eType == ET_BUILDABLE )
- {
- attacker->client->pers.statscounters.structdmgdone += takeNoOverkill;
-
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.structdmgdone+=takeNoOverkill;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.structdmgdone+=takeNoOverkill;
- }
-
- if( targ->health > 0 && ( targ->health - take ) <=0 )
- {
- attacker->client->pers.statscounters.structskilled++;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.structskilled++;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.structskilled++;
- }
- }
- }
- else if( targ->client )
- {
- attacker->client->pers.statscounters.dmgdone +=takeNoOverkill;
- attacker->client->pers.statscounters.hits++;
- if( attacker->client->pers.teamSelection == PTE_ALIENS )
- {
- level.alienStatsCounters.dmgdone+=takeNoOverkill;
- }
- else if( attacker->client->pers.teamSelection == PTE_HUMANS )
- {
- level.humanStatsCounters.dmgdone+=takeNoOverkill;
- }
- }
- }
-
-
- //Do the damage
targ->health = targ->health - take;
if( targ->client )
+ {
targ->client->ps.stats[ STAT_HEALTH ] = targ->health;
+ targ->client->pers.infoChangeTime = level.time;
+ }
targ->lastDamageTime = level.time;
+ targ->nextRegenTime = level.time + ALIEN_REGEN_DAMAGE_TIME;
- //TA: add to the attackers "account" on the target
- if( targ->client && attacker->client )
- {
- if( attacker != targ && !OnSameTeam( targ, attacker ) )
- targ->credits[ attacker->client->ps.clientNum ] += take;
- else if( attacker != targ && OnSameTeam( targ, attacker ) )
- targ->client->tkcredits[ attacker->client->ps.clientNum ] += takeNoOverkill;
- }
+ // add to the attackers "account" on the target
+ if( attacker->client && attacker != targ )
+ targ->credits[ attacker->client->ps.clientNum ] += take;
if( targ->health <= 0 )
{
@@ -1501,7 +1161,11 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
targ->flags |= FL_NO_KNOCKBACK;
if( targ->health < -999 )
+ {
targ->health = -999;
+ if( targ->client )
+ targ->client->ps.stats[ STAT_HEALTH ] = -999;
+ }
targ->enemy = attacker;
targ->die( targ, inflictor, attacker, take, mod );
@@ -1570,8 +1234,6 @@ qboolean CanDamage( gentity_t *targ, vec3_t origin )
return qfalse;
}
-
-//TA:
/*
============
G_SelectiveRadiusDamage
@@ -1611,6 +1273,9 @@ qboolean G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float dama
if( !ent->takedamage )
continue;
+ if( ent->flags & FL_NOTARGET )
+ continue;
+
// find the distance from the edge of the bounding box
for( i = 0 ; i < 3 ; i++ )
{
@@ -1628,14 +1293,16 @@ qboolean G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float dama
points = damage * ( 1.0 - dist / radius );
- if( CanDamage( ent, origin ) )
+ if( CanDamage( ent, origin ) && ent->client &&
+ ent->client->ps.stats[ STAT_TEAM ] != team )
{
VectorSubtract( ent->r.currentOrigin, origin, dir );
// push the center of mass higher than the origin so players
// get knocked into the air more
dir[ 2 ] += 24;
- G_SelectiveDamage( ent, NULL, attacker, dir, origin,
- (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod, team );
+ hitClient = qtrue;
+ G_Damage( ent, NULL, attacker, dir, origin,
+ (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod );
}
}
@@ -1649,7 +1316,7 @@ G_RadiusDamage
============
*/
qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
- float radius, gentity_t *ignore, int dflags, int mod )
+ float radius, gentity_t *ignore, int mod )
{
float points, dist;
gentity_t *ent;
@@ -1705,8 +1372,9 @@ qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
// push the center of mass higher than the origin so players
// get knocked into the air more
dir[ 2 ] += 24;
+ hitClient = qtrue;
G_Damage( ent, NULL, attacker, dir, origin,
- (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE|dflags, mod );
+ (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod );
}
}
@@ -1714,46 +1382,71 @@ qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
}
/*
-============
-G_Knockback
-============
+================
+G_LogDestruction
+
+Log deconstruct/destroy events
+================
*/
-void G_Knockback( gentity_t *targ, vec3_t dir, int knockback )
+void G_LogDestruction( gentity_t *self, gentity_t *actor, int mod )
{
- if( knockback && targ->client )
- {
- vec3_t kvel;
- float mass;
+ buildFate_t fate;
- mass = 200;
-
- // Halve knockback for bsuits
- if( targ->client &&
- targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
- BG_InventoryContainsUpgrade( UP_BATTLESUIT, targ->client->ps.stats ) )
- mass += 400;
-
- // Halve knockback for crouching players
- if(targ->client->ps.pm_flags&PMF_DUCKED) knockback /= 2;
-
- VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel );
- VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
+ switch( mod )
+ {
+ case MOD_DECONSTRUCT:
+ fate = BF_DECONSTRUCT;
+ break;
+ case MOD_REPLACE:
+ fate = BF_REPLACE;
+ break;
+ case MOD_NOCREEP:
+ fate = ( actor->client ) ? BF_UNPOWER : BF_AUTO;
+ break;
+ default:
+ if( actor->client )
+ {
+ if( actor->client->pers.teamSelection ==
+ BG_Buildable( self->s.modelindex )->team )
+ {
+ fate = BF_TEAMKILL;
+ }
+ else
+ fate = BF_DESTROY;
+ }
+ else
+ fate = BF_AUTO;
+ break;
+ }
+ G_BuildLogAuto( actor, self, fate );
- // set the timer so that the other client can't cancel
- // out the movement immediately
- if( !targ->client->ps.pm_time )
- {
- int t;
+ // don't log when marked structures are removed
+ if( mod == MOD_REPLACE )
+ return;
- t = knockback * 2;
- if( t < 50 )
- t = 50;
+ G_LogPrintf( S_COLOR_YELLOW "Deconstruct: %d %d %s %s: %s %s by %s\n",
+ (int)( actor - g_entities ),
+ (int)( self - g_entities ),
+ BG_Buildable( self->s.modelindex )->name,
+ modNames[ mod ],
+ BG_Buildable( self->s.modelindex )->humanName,
+ mod == MOD_DECONSTRUCT ? "deconstructed" : "destroyed",
+ actor->client ? actor->client->pers.netname : "<world>" );
+
+ // No-power deaths for humans come after some minutes and it's confusing
+ // when the messages appear attributed to the deconner. Just don't print them.
+ if( mod == MOD_NOCREEP && actor->client &&
+ actor->client->pers.teamSelection == TEAM_HUMANS )
+ return;
- if( t > 200 )
- t = 200;
- targ->client->ps.pm_time = t;
- targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- }
+ if( actor->client && actor->client->pers.teamSelection ==
+ BG_Buildable( self->s.modelindex )->team )
+ {
+ G_TeamCommand( actor->client->ps.stats[ STAT_TEAM ],
+ va( "print \"%s ^3%s^7 by %s\n\"",
+ BG_Buildable( self->s.modelindex )->humanName,
+ mod == MOD_DECONSTRUCT ? "DECONSTRUCTED" : "DESTROYED",
+ actor->client->pers.netname ) );
}
-}
+}