From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/game/g_combat.c | 1479 ++++++++++++++++++++------------------------------- 1 file changed, 586 insertions(+), 893 deletions(-) (limited to 'src/game/g_combat.c') 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 + =========================================================================== */ #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,9 +123,110 @@ 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 = ""; + killerName = ""; } else { @@ -183,395 +264,62 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int killerName = ""; } - if( killer < 0 || killer >= MAX_CLIENTS ) - { - killer = ENTITYNUM_WORLD; - killerName = ""; - } - - if( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) - obit = ""; + 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 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 : "" ); + + // 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 ) ); } -} +} -- cgit