Import OA 0.8.8 tree
This commit is contained in:
517
code/game/g_killspree.c
Normal file
517
code/game/g_killspree.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2009 Karl Kuglin
|
||||
|
||||
This file is part of the Open Arena source code.
|
||||
Parts of this file utilize code originally written for Tremulous by
|
||||
Tony J. White.
|
||||
Use of that code is governed by GPL version 2 and any later versions.
|
||||
|
||||
Open Arena source code 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,
|
||||
or (at your option) any later version.
|
||||
|
||||
Open Arena source code is distributed in the hope that it will be
|
||||
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
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 Open Arena source code; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
// NOTE: This code is by no means complete.
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
killspree_t *killSprees[ MAX_KSPREE ];
|
||||
deathspree_t *deathSprees[ MAX_DSPREE ];
|
||||
multikill_t *multiKills[ MAX_MULTIKILLS ];
|
||||
|
||||
/*
|
||||
=================
|
||||
G_ReadAltKillSettings
|
||||
Since this is cvar dependent, it has to be placed in G_InitGame after cvars are registered.
|
||||
=================
|
||||
*/
|
||||
qboolean G_ReadAltKillSettings( gentity_t *ent, int skiparg )
|
||||
{
|
||||
//Let's Initialize some Spree structs/objects
|
||||
killspree_t *k = NULL;
|
||||
deathspree_t *d = NULL;
|
||||
multikill_t *m = NULL;
|
||||
//spree counters
|
||||
int ksc = 0, dsc = 0, mc = 0;
|
||||
int spreeDivisor;
|
||||
|
||||
//Give us an int to use in "for" loops
|
||||
int i = 0;
|
||||
|
||||
//File Stuff
|
||||
fileHandle_t file;
|
||||
int length;
|
||||
qboolean kspree_read;
|
||||
qboolean dspree_read;
|
||||
qboolean mkill_read;
|
||||
char *cnf, *cnf2;
|
||||
char *t;
|
||||
|
||||
//Let's clear out any existing killing sprees/death sprees. YAYY BG_FREE!!!!!
|
||||
for( i = 0; i < MAX_KSPREE && killSprees[ i ]; i++ ) {
|
||||
BG_Free( killSprees[ i ] );
|
||||
killSprees[ i ] = NULL;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_KSPREE && deathSprees[ i ]; i++ ) {
|
||||
BG_Free( deathSprees[ i ] );
|
||||
deathSprees[ i ] = NULL;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAX_MULTIKILLS && multiKills[ i ]; i++ ) {
|
||||
BG_Free( multiKills[ i ] );
|
||||
multiKills[ i ] = NULL;
|
||||
}
|
||||
|
||||
// If the config file is not defined...forget reading/loading
|
||||
if( !g_sprees.string[0] ) {
|
||||
//Let's disable multikills to keep stock excellent sound
|
||||
if( g_altExcellent.integer == 1 )
|
||||
{
|
||||
trap_Cvar_Set( "g_altExcellent", "0" );
|
||||
}
|
||||
return qfalse;
|
||||
}
|
||||
/*
|
||||
only set spreeDivisor to cvar g_spreeDiv if g_spreeDiv is >= "2".
|
||||
0 will cause problems and having 1 is just a fool who wants to hear something
|
||||
on every kill.
|
||||
*/
|
||||
if( g_spreeDiv.integer >= 2 ) {
|
||||
level.spreeDivisor = g_spreeDiv.integer;
|
||||
spreeDivisor = level.spreeDivisor;
|
||||
} else {
|
||||
level.spreeDivisor = 5;
|
||||
// We don't want to change the value, keep reminding the server operator.
|
||||
//g_spreeDiv.integer = 5;
|
||||
spreeDivisor = 5;
|
||||
G_Printf( "Error: cvar g_spreeDiv must not be set to 0 or 1, reverting to default settings!\n" );
|
||||
G_Printf( "Error: Set g_spreeDiv higher than 1 if 5 is not desired!\n" );
|
||||
}
|
||||
|
||||
length = trap_FS_FOpenFile( g_sprees.string, &file, FS_READ );
|
||||
|
||||
//If the file can't be accessed/opened.
|
||||
if( length < 0 ) {
|
||||
G_Printf( "Could not open configuration file for Sprees and Multikills %s\n", g_sprees.string );
|
||||
trap_Cvar_Set( "g_altExcellent", "0" );
|
||||
return qfalse;
|
||||
}
|
||||
//Allocate some memory.
|
||||
cnf = BG_Alloc( length + 1 );
|
||||
cnf2 = cnf;
|
||||
|
||||
//Load the whole file up.
|
||||
trap_FS_Read( cnf, length, file );
|
||||
*( cnf + length ) = '\0';
|
||||
trap_FS_FCloseFile( file );
|
||||
|
||||
kspree_read = dspree_read = mkill_read = qfalse;
|
||||
|
||||
//Let's start parsing em.
|
||||
COM_BeginParseSession( g_sprees.string );
|
||||
while( 1 )
|
||||
{
|
||||
t = COM_Parse( &cnf );
|
||||
if( !*t )
|
||||
break;
|
||||
if( !Q_stricmp( t, "[kspree]" ) ) {
|
||||
|
||||
if( ksc >= MAX_KSPREE )
|
||||
return qfalse;
|
||||
k = BG_Alloc( sizeof( killspree_t ) );
|
||||
killSprees[ ksc++ ] = k;
|
||||
kspree_read = qtrue;
|
||||
dspree_read = qfalse;
|
||||
mkill_read = qfalse;
|
||||
|
||||
} else if ( !Q_stricmp( t, "[dspree]" ) ) {
|
||||
|
||||
if( dsc >= MAX_DSPREE )
|
||||
return qfalse;
|
||||
d = BG_Alloc( sizeof( deathspree_t ) );
|
||||
deathSprees[ dsc++ ] = d;
|
||||
dspree_read = qtrue;
|
||||
kspree_read = qfalse;
|
||||
mkill_read = qfalse;
|
||||
} else if ( !Q_stricmp( t, "[mkill]" ) ) {
|
||||
|
||||
if( mc >= MAX_MULTIKILLS )
|
||||
return qfalse;
|
||||
m = BG_Alloc( sizeof( multikill_t ) );
|
||||
multiKills[ mc++ ] = m;
|
||||
mkill_read = qtrue;
|
||||
kspree_read = qfalse;
|
||||
dspree_read = qfalse;
|
||||
//Parse a killing spree
|
||||
} else if ( kspree_read ) {
|
||||
if( !Q_stricmp( t, "level" ) ) {
|
||||
readFile_int( &cnf, &k->spreeLevel );
|
||||
//Let's take the spreeLevel and multiply it by the spreeDivisor to give us our count
|
||||
k->streakCount = ( ( k->spreeLevel ) * ( spreeDivisor ) );
|
||||
} else if ( !Q_stricmp( t, "message" ) ) {
|
||||
readFile_string( &cnf, k->spreeMsg, sizeof( k->spreeMsg ) );
|
||||
} else if ( !Q_stricmp( t, "printpos" ) ) {
|
||||
readFile_int( &cnf, &k->position );
|
||||
} else if ( !Q_stricmp( t, "sound" ) ) {
|
||||
readFile_string( &cnf, k->sound2Play, sizeof( k->sound2Play ) );
|
||||
} else {
|
||||
COM_ParseError( "Killing Spree unrecognized token \"%s\"", t );
|
||||
}
|
||||
} else if ( dspree_read ) {
|
||||
if( !Q_stricmp( t, "level" ) ) {
|
||||
readFile_int( &cnf, &d->spreeLevel );
|
||||
//Let's take the spreeLevel and multiply it by the spreeDivisor to give us our count
|
||||
d->streakCount = ( ( d->spreeLevel ) * ( spreeDivisor ) );
|
||||
} else if ( !Q_stricmp( t, "message" ) ) {
|
||||
readFile_string( &cnf, d->spreeMsg, sizeof( d->spreeMsg ) );
|
||||
} else if ( !Q_stricmp( t, "printpos" ) ) {
|
||||
readFile_int( &cnf, &d->position );
|
||||
} else if ( !Q_stricmp( t, "sound" ) ) {
|
||||
readFile_string( &cnf, d->sound2Play, sizeof( d->sound2Play ) );
|
||||
} else {
|
||||
COM_ParseError( "Death Spree unrecognized token \"%s\"", t );
|
||||
}
|
||||
} else if ( mkill_read ) {
|
||||
if ( !Q_stricmp( t, "kills" ) ) {
|
||||
readFile_int( &cnf, &m->kills );
|
||||
} else if ( !Q_stricmp( t, "message" ) ) {
|
||||
readFile_string( &cnf, m->killMsg, sizeof( m->killMsg ) );
|
||||
} else if ( !Q_stricmp( t, "sound" ) ) {
|
||||
readFile_string( &cnf, m->sound2Play, sizeof( m->sound2Play ) );
|
||||
} else {
|
||||
COM_ParseError( "Multikill unrecognized token \"%s\"", t );
|
||||
}
|
||||
} else {
|
||||
COM_ParseError( "unexpected token \"%s\"", t );
|
||||
}
|
||||
}
|
||||
//Let's "free" some memory now.
|
||||
BG_Free( cnf2 );
|
||||
G_Printf("Sprees/Kills: loaded %d killing sprees, %d death sprees, and %d multikills.\n", ksc, dsc, mc );
|
||||
//Mark the Upper Bounds of the Arrays (Since they start numbering at 0, We subtract 1 )
|
||||
level.kSpreeUBound = ( ksc - 1 );
|
||||
level.dSpreeUBound = ( dsc - 1 );
|
||||
if( mc > 0 ) {
|
||||
level.mKillUBound = ( mc - 1 );
|
||||
} else {
|
||||
level.mKillUBound = -1;
|
||||
//KK-OAX We don't have any kills defined, revert to stock.
|
||||
//FIXME: Make sure this change shows up in the console...
|
||||
if( g_altExcellent.integer == 1 ) {
|
||||
trap_Cvar_Set( "g_altExcellent", "0" );
|
||||
}
|
||||
|
||||
}
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
static char *fillPlaceHolder( char *stringToSearch, char *placeHolder, char *replaceWith )
|
||||
{
|
||||
static char output[ MAX_SAY_TEXT ];
|
||||
char *p;
|
||||
|
||||
if( !( p = strstr( stringToSearch, placeHolder ) ) )
|
||||
return stringToSearch;
|
||||
|
||||
strncpy( output, stringToSearch, p - stringToSearch );
|
||||
output[ p - stringToSearch ] = '\0';
|
||||
|
||||
Q_snprintf( output + ( p - stringToSearch ), output - stringToSearch, "%s%s", replaceWith, p + strlen( placeHolder ) );
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//This concatenate's the message to broadcast to the clients.
|
||||
static char *CreateMessage( gentity_t *ent, char *message, char *spreeNumber )
|
||||
{
|
||||
static char output[ MAX_SAY_TEXT ] = { "" };
|
||||
|
||||
char name[ MAX_NAME_LENGTH ];
|
||||
//Do some sanity checks
|
||||
if( !ent ) {
|
||||
return output;
|
||||
} else if ( !*message ) {
|
||||
return output;
|
||||
} else if ( !spreeNumber ) {
|
||||
return output;
|
||||
}
|
||||
//Get the player name.
|
||||
Q_strncpyz( name, ent->client->pers.netname, sizeof( name ) );
|
||||
//Do Our Replacements
|
||||
Q_strncpyz( output, fillPlaceHolder( message, "[n]", name ), sizeof( output ) );
|
||||
Q_strncpyz( output, fillPlaceHolder( output, "[k]", spreeNumber ), sizeof( output ) );
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_RunStreakLogic
|
||||
KK-OAX This is called from player_die.
|
||||
It does all the adding resetting of kill/death streaks
|
||||
to be compared against the kill/death spree levels.
|
||||
================
|
||||
*/
|
||||
void G_RunStreakLogic( gentity_t *attacker, gentity_t *victim )
|
||||
{
|
||||
//We only want to sanity check for the victim at this point.
|
||||
if( !victim || !victim->client )
|
||||
return;
|
||||
|
||||
//We will reset the victim's killstreak counter
|
||||
victim->client->pers.killstreak = 0;
|
||||
//Add one to the death streak counter
|
||||
victim->client->pers.deathstreak++;
|
||||
|
||||
//Let's check for a deathspree for the victim
|
||||
G_CheckForSpree( victim, victim->client->pers.deathstreak, qfalse );
|
||||
|
||||
//Move on to the attacker
|
||||
//Make sure they are a client and that the attacker and victim are not the same client
|
||||
//We don't want suicide to count towards a killstreak...
|
||||
if( ( attacker )
|
||||
&& ( attacker->client )
|
||||
&& ( attacker != victim ) ) {
|
||||
|
||||
//Check the gametype--If FFA enabled, everybody's on the same team.
|
||||
if( g_gametype.integer >= GT_TEAM && g_ffa_gt!= 1 ) {
|
||||
//If they are on the same team we don't want to count it towards a killing spree.
|
||||
if( OnSameTeam( victim, attacker ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//Add to the killstreak, reset the deathstreak
|
||||
attacker->client->pers.deathstreak = 0;
|
||||
attacker->client->pers.killstreak++;
|
||||
|
||||
//Let's check for a killingspree for the attacker
|
||||
G_CheckForSpree( attacker, attacker->client->pers.killstreak, qtrue );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//If the streak / spree divisor is a whole number, we have a spree
|
||||
static qboolean TestSpreeWhole( int streak2Test ) {
|
||||
float float2Test;
|
||||
float spreeFDiv;
|
||||
float resultf;
|
||||
int spreeDiv;
|
||||
int result;
|
||||
|
||||
float2Test = streak2Test;
|
||||
spreeFDiv = level.spreeDivisor;
|
||||
spreeDiv = level.spreeDivisor;
|
||||
result = ( streak2Test / spreeDiv );
|
||||
resultf = ( float2Test / spreeFDiv );
|
||||
|
||||
if( result == resultf ) {
|
||||
return qtrue;
|
||||
} else {
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
G_CheckForSpree
|
||||
==================
|
||||
*/
|
||||
void G_CheckForSpree( gentity_t *ent, int streak2Test, qboolean checkKillSpree )
|
||||
{
|
||||
int i;
|
||||
char *returnedString;
|
||||
//If somebody want's to award killing sprees above 99 kills he/she can mod this his/herself!!! :)
|
||||
char streakcount[ 3 ];
|
||||
char *sound;
|
||||
int position;
|
||||
int soundIndex;
|
||||
|
||||
qboolean isSpree = qfalse;
|
||||
int divisionHolder;
|
||||
|
||||
//Probably Not Needed, but to protect Server Ops from Crashing their Stuff MidMatch
|
||||
if( level.spreeDivisor < 1 ) {
|
||||
return;
|
||||
}
|
||||
divisionHolder = ( streak2Test / level.spreeDivisor );
|
||||
//if it's a deathspree
|
||||
if( !checkKillSpree ) {
|
||||
//Is the streak higher than the largest level defined?
|
||||
if( divisionHolder > level.dSpreeUBound ) {
|
||||
//Let's make sure it's a whole number to mimic the other sprees
|
||||
isSpree = TestSpreeWhole( streak2Test );
|
||||
if( !isSpree ) {
|
||||
return;
|
||||
}
|
||||
//We've made it this far...now do the largest spree defined.
|
||||
Q_snprintf( streakcount, sizeof( streakcount ), "%i", streak2Test );
|
||||
//Check if deathSprees is NULL (actual problem!)
|
||||
if(!deathSprees[ level.dSpreeUBound ])
|
||||
return;
|
||||
returnedString = CreateMessage( ent, deathSprees[ level.dSpreeUBound ]->spreeMsg, streakcount );
|
||||
position = deathSprees[ level.dSpreeUBound ]->position;
|
||||
sound = deathSprees[ level.dSpreeUBound ]->sound2Play;
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
//Play the Sound
|
||||
G_GlobalSound( soundIndex );
|
||||
//Print the Message
|
||||
if( position == CENTER_PRINT ) {
|
||||
AP( va("cp \"%s\"", returnedString ) );
|
||||
} else {
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
}
|
||||
} else {
|
||||
for( i = 0; deathSprees[ i ]; i++ ) {
|
||||
//If the deathSpree is equal to the streak to test
|
||||
if( deathSprees[ i ]->streakCount == streak2Test ) {
|
||||
//Using Q_snprintf to change the int into a char for replacement.
|
||||
Q_snprintf( streakcount, sizeof( streakcount ), "%i", deathSprees[ i ]->streakCount );
|
||||
//Let's grab the message to show, fill up the placeholders and concatenate it.
|
||||
returnedString = CreateMessage ( ent, deathSprees[ i ]->spreeMsg, streakcount );
|
||||
//Grab the Print Position ( 1 for Center Printing, 2 for Chat )
|
||||
position = deathSprees[ i ]->position;
|
||||
//Grab the Sound
|
||||
sound = deathSprees[ i ]->sound2Play;
|
||||
//Index the Sound
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
//Play the Sound
|
||||
G_GlobalSound( soundIndex );
|
||||
//Print the Message
|
||||
if( position == CENTER_PRINT ) {
|
||||
AP( va("cp \"%s\"", returnedString ) );
|
||||
} else {
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else /*if( checkKillSpree )*/ {
|
||||
//Is the streak higher than the largest level defined?
|
||||
if( divisionHolder > level.kSpreeUBound ) {
|
||||
//Let's make sure it's a whole number to mimic the other sprees
|
||||
isSpree = TestSpreeWhole( streak2Test );
|
||||
if( !isSpree ) {
|
||||
return;
|
||||
}
|
||||
//We've made it this far...now do the largest spree defined.
|
||||
Q_snprintf( streakcount, sizeof( streakcount ), "%i", streak2Test );
|
||||
//Check if killSprees is NULL (actual problem!)
|
||||
if(!killSprees[ level.kSpreeUBound ])
|
||||
return;
|
||||
returnedString = CreateMessage( ent, killSprees[ level.kSpreeUBound ]->spreeMsg, streakcount );
|
||||
position = killSprees[ level.kSpreeUBound ]->position;
|
||||
sound = killSprees[ level.kSpreeUBound ]->sound2Play;
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
//G_GlobalSound( soundIndex );
|
||||
G_Sound(ent,0,soundIndex);
|
||||
/* Doesn't do anything at the moment. cp does not work while kill message is displayed
|
||||
* if( position == CENTER_PRINT ) {
|
||||
//Only Center print for player doing the killing spree
|
||||
CP( va("cp \"%s\"", returnedString ) );
|
||||
}*/
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
} else {
|
||||
for( i = 0; killSprees[ i ]; i++ ) {
|
||||
if( killSprees[ i ]->streakCount == streak2Test ) {
|
||||
Q_snprintf( streakcount, sizeof( streakcount ), "%i", killSprees[ i ]->streakCount );
|
||||
returnedString = CreateMessage ( ent, killSprees[ i ]->spreeMsg, streakcount );
|
||||
position = killSprees[ i ]->position;
|
||||
sound = killSprees[ i ]->sound2Play;
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
//G_GlobalSound( soundIndex );
|
||||
G_Sound(ent,0,soundIndex);
|
||||
/*if( position == CENTER_PRINT ) {
|
||||
//Only Center print for player doing the killing spree
|
||||
CP( va("cp \"%s\"", returnedString ) );
|
||||
}*/
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /*else {
|
||||
G_Printf("Killing Spree Error in G_CheckForSpree\n");
|
||||
return;
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_checkForMultiKill
|
||||
===============
|
||||
*/
|
||||
void G_checkForMultiKill( gentity_t *ent ) {
|
||||
|
||||
int i;
|
||||
char *returnedString;
|
||||
char *sound;
|
||||
int soundIndex;
|
||||
int multiKillCount;
|
||||
char multiKillString[ 2 ];
|
||||
gclient_t *client;
|
||||
int clientNum;
|
||||
|
||||
client = ent->client;
|
||||
clientNum = client - level.clients;
|
||||
|
||||
//Let's grab the multikill count for the player first
|
||||
multiKillCount = ent->client->pers.multiKillCount;
|
||||
|
||||
if( multiKillCount > multiKills[ level.mKillUBound ]->kills ) {
|
||||
Q_snprintf( multiKillString, sizeof( multiKillString ), "%i", multiKillCount );
|
||||
if(!multiKills[ level.mKillUBound ])
|
||||
return; //If null
|
||||
returnedString = CreateMessage ( ent, multiKills[ level.mKillUBound ]->killMsg, multiKillString );
|
||||
sound = multiKills[ level.mKillUBound ]->sound2Play;
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
G_Sound(ent, 0, soundIndex );
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
return;
|
||||
} else {
|
||||
for( i = 0; multiKills[ i ]; i++ ) {
|
||||
//If the multikill count is equal to a killLevel let's do this.
|
||||
|
||||
if( multiKills[ i ]->kills == multiKillCount ) {
|
||||
Q_snprintf( multiKillString, sizeof( multiKillString ), "%i", multiKills[ i ]->kills );
|
||||
//Build the Message
|
||||
returnedString = CreateMessage ( ent, multiKills[ i ]->killMsg, multiKillString );
|
||||
//Grab the sound
|
||||
sound = multiKills[ i ]->sound2Play;
|
||||
//Index the sound
|
||||
soundIndex = G_SoundIndex( sound );
|
||||
//Play the sound
|
||||
G_Sound(ent, 0, soundIndex );
|
||||
/* Print the String
|
||||
Since we don't want to clutter screens (the player is already going to get the excellent icon)
|
||||
we won't give them an option to centerprint.
|
||||
*/
|
||||
AP( va("chat \"%s\"", returnedString ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user