sof-sdk/Source/Game/gamecpp/dm_ctf.cpp
2000-06-15 00:00:00 +00:00

1588 lines
39 KiB
C++

// *****************************************************************************
// dm_ctf.cpp
// *****************************************************************************
#include "g_local.h"
#include "dm_private.h"
#include "w_weapons.h"
#include "dm_ctf.h"
#include "p_body.h"
#include "../strings/dm_generic.h"
#include "..\qcommon\configstring.h"
const int DF_CTF_NO_HEALTH = (1<<0);
const int DF_CTF_NO_ITEMS = (1<<1);
const int DF_CTF_WEAPONS_STAY = (1<<2);
const int DF_CTF_NO_FALLING = (1<<3);
const int DF_CTF_REALISTIC_DAMAGE = (1<<4);
const int DF_CTF_SAME_LEVEL = (1<<5);
const int DF_CTF_NOWEAPRELOAD = (1<<6);
const int DF_CTF_NO_FRIENDLY_FIRE = (1<<8);
const int DF_CTF_FORCE_RESPAWN = (1<<9);
const int DF_CTF_NO_ARMOR = (1<<10);
const int DF_CTF_INFINITE_AMMO = (1<<11);
const int DF_CTF_SPINNINGPICKUPS = (1<<12);
const int DF_CTF_BULLET_WPNS_ONLY = (1<<13);
const int DF_CTF_FORCEJOIN = (1<<14);
void hurt_touch(edict_t *self, edict_t *other, cplane_t *plane, struct mtexinfo_s *surf);
int TeamCompare(void const *a, void const *b);
/*
==================
dmctf_c::FlagCaptured
==================
*/
void dmctf_c::FlagCaptured(team_t team)
{
if (team == TEAM1)
blue_collected++;
else
red_collected++;
}
/*
==================
dmctf_c::ClearFlagCount
==================
*/
void dmctf_c::ClearFlagCount(void)
{
blue_collected = red_collected = 0;
// clear out anyone with a flag next to them on the score board.
gi.configstring(CS_CTF_BLUE_STAT, "d");
gi.configstring(CS_CTF_RED_STAT, "d");
// Reset the ctf_loops_count.
gi.cvar_setvalue("ctf_loops_count", 0.0);
gi.cvar_setvalue("ctf_flag_captured", 0.0);
}
/*
==================
dmctf_c::HitByAttacker
==================
*/
void dmctf_c::HitByAttacker(edict_t *body, edict_t *attacker)
{
// if the attacker isn't a player, exit
if (!attacker->client)
return;
// are we a flag carrier? If so, set the time inside of my attacker, so if he is killed soon, the player that kills HIM gets a bonus for protecting me
if (body->ctf_flags)
{
attacker->ctf_hurt_carrier = level.time;
}
}
/*
==================
CountSkinsInFile
given a pointer to a GPL file, count the number of skins for any given team name
==================
*/
int CountSkinsInFile(char *teamname, char *buffer)
{
// Loop through and validate skin names - see if they are attached to this team we are checking against
TeamInfoC team;
char *s;
int count = 0;
s=buffer;
while(s)
{
char shortname[100];
char defModName[100];
strcpy(shortname,COM_Parse(&s));
if(strlen(shortname))
{
// we have a skin name, go load it in and see what team its on
Com_sprintf(defModName, sizeof(defModName), "%s.gpm", shortname);
IPlayerModelInfoC *aPlayer=gi.NewPlayerModelInfo(defModName);
// if we are on the team name provided, inc the counter
aPlayer->GetTeamInfo(team);
if (!(stricmp(teamname, team.name)))
{
count++;
}
delete aPlayer;
}
}
return count;
}
/*
==================
dmctf_c::NumOfSkins
==================
*/
int dmctf_c::NumOfSkins(char *teamname)
{
int i,length ;
char *buffer;
char custom_filename[MAX_QPATH];
// load in the ctf GPL file, that contains all the skins allowed for CTF
if((length=gi.FS_LoadFile("ghoul/pmodels/ctf.gpl",(void **)&buffer))==-1)
{
gi.error("*************************\nCould not open ctf.gpl\n*************************\n");
return 0;
}
i = CountSkinsInFile(teamname, buffer);
// Clean up...
gi.FS_FreeFile(buffer);
// if we have a secondary GPL file...
if (sv_altpmodlistfile->string[0])
{
Com_sprintf(custom_filename, sizeof(custom_filename),"%s.gpl", sv_altpmodlistfile->string);
// now look through the secondary skin file
if((length=gi.FS_LoadFile(custom_filename,(void **)&buffer))==-1)
{
return i;
}
i += CountSkinsInFile(teamname, buffer);
// Clean up...
gi.FS_FreeFile(buffer);
}
return i;
}
/*
==================
dmctf_c::levelInit
==================
*/
#define MAX_DEFINED_TEAMS 6
void dmctf_c::levelInit(void)
{
// Precache string package.
char *teamnames[MAX_DEFINED_TEAMS] =
{
"The Order",
"MeatWagon",
"Ministry of Sin",
"Red Guard",
"The December Brigade",
"The Order"
};
gi.SP_Register("dm_ctf");
// Precache flags.
game_ghoul.FindObject("items/ctf_flag", "flag_hold_idle");
game_ghoul.FindObject("items/ctf_flag", "flag_run");
// Precache sounds.
gi.soundindex("dm/ctf/flaggrab.wav");
gi.soundindex("dm/ctf/flagret.wav");
gi.soundindex("dm/ctf/flagbase.wav");
gi.soundindex("dm/ctf/triumphant.wav");
gi.soundindex("player/flaglp.wav");
// Make certain all the weapons given to clients at respawn are precached.
// View weapons.
AddWeaponTypeForPrecache(SFW_KNIFE);
AddWeaponTypeForPrecache(SFW_PISTOL1);
AddWeaponTypeForPrecache(SFW_HURTHAND);
AddWeaponTypeForPrecache(SFW_THROWHAND);
// Bolt-ons / pickups. Eugh... but necessary to precache. Knife doesn't
// need to be precached for some reason - it's always there.. hmmm.
edict_t *ent=G_Spawn();
I_Spawn(ent,thePickupList.GetPickupFromType(PU_WEAPON,SFW_PISTOL1));
G_FreeEdict(ent);
// are the two teams the same ?
if (!stricmp(ctf_team_blue->string, ctf_team_red->string))
{
// look for which one the red team is
for (int i=0; i<MAX_DEFINED_TEAMS; i++)
{
// if we are the same, then choose the next team
if (!stricmp(ctf_team_red->string, teamnames[i]))
{
// this should never happen since the first and last teamname are the same entry, but you can never be too careful
if (i+1 != MAX_DEFINED_TEAMS)
{
gi.cvar_set("ctf_team_red", teamnames[i+1]);
Com_Printf("CTF Teams are the same - Resetting Team Red to %s\n", ctf_team_red->string);
break;
}
}
}
// we didn't find it in the list - all we can do here is pick a random team and hope it's not the same as blue
if (i == MAX_DEFINED_TEAMS)
{
gi.cvar_set("ctf_team_red", teamnames[gi.irand(0,MAX_DEFINED_TEAMS-1)]);
Com_Printf("CTF Teams are the same - Resetting Team Red to %s\n", ctf_team_red->string);
}
}
// Set the number of blue or red skin.gpm files there are. We have to do this
// here, since we don't want to keep doing this everytime someone joins the
// game, yet we require the num of blue and red skin files to randomise people
// later.
num_blue_skins = NumOfSkins(ctf_team_blue->string);
num_red_skins = NumOfSkins(ctf_team_red->string);
// Set the right team names for the client to be able to set the colors right
// on the team icons.
gi.configstring(CS_CTF_BLUE_TEAM, ctf_team_blue->string);
gi.configstring(CS_CTF_RED_TEAM, ctf_team_red->string);
}
/*
==================
dmctf_c::checkItemSpawn
==================
*/
int dmctf_c::checkItemSpawn(edict_t *ent,Pickup **pickup)
{
if(dmRule_NO_HEALTH())
{
if ((*pickup)->GetType() == PU_HEALTH)
{
G_FreeEdict (ent);
return(0);
}
}
if(dmRule_NO_ITEMS())
{
if ((*pickup)->GetType() == PU_INV)
{
G_FreeEdict (ent);
return(0);
}
}
if(dmRule_NO_ARMOR())
{
if ((*pickup)->GetType() == PU_ARMOR)
{
G_FreeEdict (ent);
return(0);
}
}
if(dmRule_INFINITE_AMMO())
{
if ((*pickup)->GetType() == PU_AMMO)
{
G_FreeEdict (ent);
return(0);
}
}
if(dmRule_BULLET_WPNS_ONLY())
{
if ( ((*pickup)->GetType() == PU_WEAPON) && (!(*pickup)->IsBulletWpn()) ||
((*pickup)->GetType() == PU_AMMO) && (!(*pickup)->IsBulletAmmo()) )
{
G_FreeEdict (ent);
return(0);
}
}
return(-1);
}
/*
==================
dmctf_c::dmRule_xxx
==================
*/
int dmctf_c::dmRule_NO_HEALTH(void)
{
return((int)dmflags->value&DF_CTF_NO_HEALTH);
}
int dmctf_c::dmRule_NO_ITEMS(void)
{
return((int)dmflags->value&DF_CTF_NO_ITEMS);
}
int dmctf_c::dmRule_WEAPONS_STAY(void)
{
return((int)dmflags->value&DF_CTF_WEAPONS_STAY);
}
int dmctf_c::dmRule_NO_FALLING(void)
{
return((int)dmflags->value&DF_CTF_NO_FALLING);
}
int dmctf_c::dmRule_REALISTIC_DAMAGE(void)
{
return((int)dmflags->value&DF_CTF_REALISTIC_DAMAGE);
}
int dmctf_c::dmRule_SAME_LEVEL(void)
{
return((int)dmflags->value&DF_CTF_SAME_LEVEL);
}
int dmctf_c::dmRule_NOWEAPRELOAD(void)
{
return((int)dmflags->value&DF_CTF_NOWEAPRELOAD);
}
int dmctf_c::dmRule_NO_FRIENDLY_FIRE(void)
{
return((int)dmflags->value&DF_CTF_NO_FRIENDLY_FIRE);
}
int dmctf_c::dmRule_FORCE_RESPAWN(void)
{
return((int)dmflags->value&DF_CTF_FORCE_RESPAWN);
}
int dmctf_c::dmRule_NO_ARMOR(void)
{
return((int)dmflags->value&DF_CTF_NO_ARMOR);
}
int dmctf_c::dmRule_INFINITE_AMMO(void)
{
return((int)dmflags->value&DF_CTF_INFINITE_AMMO);
}
int dmctf_c::dmRule_SPINNINGPICKUPS(void)
{
return((int)dmflags->value&DF_CTF_SPINNINGPICKUPS);
}
int dmctf_c::dmRule_BULLET_WPNS_ONLY(void)
{
return((int)dmflags->value&DF_CTF_BULLET_WPNS_ONLY);
}
int dmctf_c::dmRule_FORCEJOIN(void)
{
return((int)dmflags->value&DF_CTF_FORCEJOIN);
}
/*
==================
CheckFlagFloor
==================
*/
void CheckFlagFloor(edict_t *ent)
{
vec3_t point;
int cont, num, i;
edict_t *touch[MAX_EDICTS], *hit;
ent->nextthink = level.time + 1.0;
// is it time to reset this flag?
if (ent->timestamp < level.time)
{
reset_flag(ent, NULL);
gi.sound(ent,CHAN_NO_PHS_ADD,gi.soundindex("dm/ctf/flagret.wav"),1.0,ATTN_NONE,0,SND_LOCALIZE_GLOBAL);
if (ent->ctf_flags == TEAM1)
{
gi.SP_Print(NULL, DM_CTF_RESPAWN_FLAG_BLUE);
}
else
{
gi.SP_Print(NULL, DM_CTF_RESPAWN_FLAG_RED);
}
// remove the current edict entirely, ghoul model and all
G_FreeEdict(ent);
return;
}
VectorCopy(ent->s.origin, point);
// bring us up juuuuust a tad
point[2] += 1.0;
cont = gi.pointcontents(point);
VectorAdd(ent->mins, ent->s.origin, ent->absmin);
VectorAdd(ent->maxs, ent->s.origin, ent->absmax);
num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
, MAX_EDICTS, AREA_TRIGGERS);
for (i=0 ; i<num ; i++)
{
hit = touch[i];
if (!hit->inuse)
continue;
if (!hit->touch)
continue;
if (hit->touch == hurt_touch)
cont = CONTENTS_LAVA;
}
// if we are sitting over a pain or kill brush, reset us home.
if (cont & (CONTENTS_LAVA|CONTENTS_SLIME) )
{
reset_flag(ent, NULL);
gi.sound(ent,CHAN_NO_PHS_ADD,gi.soundindex("dm/ctf/flagret.wav"),1.0,ATTN_NONE,0,SND_LOCALIZE_GLOBAL);
if (ent->ctf_flags == TEAM1)
{
gi.SP_Print(NULL, DM_CTF_INACCESSIBLE_BLUE);
}
else
{
gi.SP_Print(NULL, DM_CTF_INACCESSIBLE_RED);
}
// remove the current edict entirely, ghoul model and all
G_FreeEdict(ent);
}
}
/*
==================
dmctf_c::ClientDropItem
==================
*/
void dmctf_c::clientDropItem(edict_t *ent,int type,int ammoCount)
{
Pickup *pickup = NULL;
if (pickup = thePickupList.GetPickupFromType(PU_INV, type))
{
edict_t *dropped;
Matrix4 ZoneMatrix;
Vect3 zonePos;
vec3_t handPos,
dir;
// Whatever weapon player is holding, throw one off.
dropped=G_Spawn();
dropped->spawnflags|=DROPPED_ITEM;
// before we spawn anything, set the count on the dropped entity so its a CTF flag if need be
if (pickup->GetPickupListIndex() == OBJ_CTF_FLAG)
{
dropped->count = ent->ctf_flags;
assert(dropped->count);
}
I_Spawn(dropped,pickup);
// by this point - ctf_flags should have been set, so we can clear the count field. Only the flag spawn point has a value in the Count field
if (pickup->GetPickupListIndex() == OBJ_CTF_FLAG)
{
dropped->count = 0;
}
dropped->enemy=ent;
dropped->touch_debounce_time=level.time+2.0;
dropped->touch_debounce_owner = ent;
if (ent->ghoulInst)
{
ent->ghoulInst->
GetBoltMatrix(level.time,
ZoneMatrix,
ent->ghoulInst->GetGhoulObject()->FindPart("wbolt_hand_l"),
IGhoulInst::MatrixType::Entity);
ZoneMatrix.GetRow(3,zonePos);
handPos[0]=zonePos[0];
handPos[1]=zonePos[1];
handPos[2]=zonePos[2];
VectorAdd(ent->s.origin,handPos,dropped->s.origin);
AngleVectors(ent->client->ps.viewangles,dir,NULL, NULL);
dir[2] = 0;
VectorNormalize(dir);
VectorMA(dropped->s.origin, 20.0, dir, dropped->s.origin);
VectorScale(dir,250.0,dropped->velocity);
}
else
{
AngleVectors(ent->client->ps.viewangles,dir,NULL,NULL);
dir[2] = 0;
VectorNormalize(dir);
VectorCopy(ent->s.origin, dropped->s.origin);
dropped->s.origin[2] += 40.0;
VectorMA(dropped->s.origin, 20.0, dir, dropped->s.origin);
VectorScale(dir,250.0,dropped->velocity);
}
dropped->velocity[2]+=150.0;
dropped->health = ammoCount;
if (pickup->GetPickupListIndex() == OBJ_CTF_FLAG)
{
// indicate to all clients the state of the flags
if (ent->ctf_flags == TEAM1)
{
gi.configstring(CS_CTF_BLUE_STAT, "d");
}
else
{
gi.configstring(CS_CTF_RED_STAT, "d");
}
// first, rename the class on the dropped flag so its not the same as the spawned flag entity
dropped->classname = "ctf dropped flag";
// give us a think function that watched what we sit on, and re-sets the flag if it lands on something hurtful
dropped->nextthink = level.time + 1.0;
dropped->think = CheckFlagFloor;
dropped->timestamp = level.time + CTF_RESPAWN_TIMEOUT;
// remove flag from player
ent->ctf_flags = 0;
// delete the ghoul object on the players hip so we lose the flag there.
if (ent->ghoulInst)
PB_RemoveFlag(ent);
// if we have the flag klaxon sound going, turn it off
if (ent->s.sound == gi.soundindex("player/flaglp.wav"))
{
ent->s.sound = 0;
}
}
else
{
clientSetDroppedItemThink(dropped);
}
}
}
/*
==================
dmctf_c::AssignTeam
==================
*/
void dmctf_c::FindSkin(char *teamname, int skinnum)
{
int i,length ;
char *buffer,*s;
TeamInfoC team;
char custom_filename[MAX_QPATH];
// load in the ctf GPL file, that contains all the skins allowed for CTF
if((length=gi.FS_LoadFile("ghoul/pmodels/ctf.gpl",(void **)&buffer))==-1)
{
gi.error("*************************\nCould not open ctf.gpl\n*************************\n");
return ;
}
i = 0;
s=buffer;
// Loop through and validate skin names.
while(s)
{
char defModName[100];
strcpy(NewSkin,COM_Parse(&s));
if(strlen(NewSkin))
{
// we have a skin name, go load it in and see what team its on
Com_sprintf(defModName, sizeof(defModName), "%s.gpm", NewSkin);
IPlayerModelInfoC *aPlayer=gi.NewPlayerModelInfo(defModName);
// if we are on the team name provided, inc the counter
aPlayer->GetTeamInfo(team);
delete aPlayer;
if (!(stricmp(teamname, team.name)))
{
// is this the file we are looking for?
if (i == skinnum)
{
gi.FS_FreeFile(buffer);
return ;
}
i++;
}
}
}
// Clean up...
gi.FS_FreeFile(buffer);
// load in the custom GPL file, that contains all the skins allowed for CTF
// if we have a secondary GPL file...
if (sv_altpmodlistfile->string[0])
{
Com_sprintf(custom_filename, sizeof(custom_filename),"%s.gpl", sv_altpmodlistfile->string);
if((length=gi.FS_LoadFile(custom_filename,(void **)&buffer))==-1)
{
return ;
}
s=buffer;
// Loop through and validate skin names.
while(s)
{
char defModName[100];
strcpy(NewSkin,COM_Parse(&s));
if(strlen(NewSkin))
{
// we have a skin name, go load it in and see what team its on
Com_sprintf(defModName, sizeof(defModName), "%s.gpm", NewSkin);
IPlayerModelInfoC *aPlayer=gi.NewPlayerModelInfo(defModName);
// if we are on the team name provided, inc the counter
aPlayer->GetTeamInfo(team);
delete aPlayer;
if (!(stricmp(teamname, team.name)))
{
// is this the file we are looking for?
if (i == skinnum)
{
gi.FS_FreeFile(buffer);
return ;
}
i++;
}
}
}
// Clean up...
gi.FS_FreeFile(buffer);
}
gi.error("FORCEJOIN: Looking for skin that doesn't exit. - teamname %s - skin number %d\n", teamname, skinnum );
}
/*
==================
dmctf_c::AssignTeam
==================
*/
team_t dmctf_c::CompareSkin(char *skinname, char* teamname)
{
int length ;
char *buffer,*s;
TeamInfoC team;
char custom_filename[MAX_QPATH];
// if we haven't even selected the right team, dump out immediately
if ((stricmp(ctf_team_blue->string, teamname)) && (stricmp(ctf_team_red->string, teamname)))
{
return NOTEAM;
}
// load in the ctf GPL file, that contains all the skins allowed for CTF
if((length=gi.FS_LoadFile("ghoul/pmodels/ctf.gpl",(void **)&buffer))==-1)
{
gi.error("*************************\nCould not open ctf.gpl\n*************************\n");
return NOTEAM;
}
s=buffer;
while(s)
{
char defModName[100];
strcpy(NewSkin,COM_Parse(&s));
if(strlen(NewSkin))
{
if (!(stricmp(NewSkin, skinname)))
{
// we have a skin name, go load it in and see what team its on
Com_sprintf(defModName, sizeof(defModName), "%s.gpm", NewSkin);
IPlayerModelInfoC *aPlayer=gi.NewPlayerModelInfo(defModName);
// if we are on the team name provided, then we found both team and skin
aPlayer->GetTeamInfo(team);
delete aPlayer;
if (!(stricmp(teamname, team.name)))
{
// Clean up...
if (!(stricmp(team.name, ctf_team_blue->string)))
{
gi.FS_FreeFile(buffer);
return TEAM1;
}
else
{
gi.FS_FreeFile(buffer);
return TEAM2;
}
}
}
}
}
// Clean up...
gi.FS_FreeFile(buffer);
// load in the custom GPL file, that contains all the skins allowed for CTF
// if we have a secondary GPL file...
if (sv_altpmodlistfile->string[0])
{
Com_sprintf(custom_filename, sizeof(custom_filename),"%s.gpl", sv_altpmodlistfile->string);
if((length=gi.FS_LoadFile(custom_filename,(void **)&buffer))==-1)
{
return NOTEAM;
}
s=buffer;
while(s)
{
char defModName[100];
strcpy(NewSkin,COM_Parse(&s));
if(strlen(NewSkin))
{
if (!(stricmp(NewSkin, skinname)))
{
// we have a skin name, go load it in and see what team its on
Com_sprintf(defModName, sizeof(defModName), "%s.gpm", NewSkin);
IPlayerModelInfoC *aPlayer=gi.NewPlayerModelInfo(defModName);
// if we are on the team name provided, then we found both team and skin
aPlayer->GetTeamInfo(team);
delete aPlayer;
if (!(stricmp(teamname, team.name)))
{
// Clean up...
if (!(stricmp(team.name, ctf_team_blue->string)))
{
gi.FS_FreeFile(buffer);
return TEAM1;
}
else
{
gi.FS_FreeFile(buffer);
return TEAM2;
}
}
}
}
}
// Clean up...
gi.FS_FreeFile(buffer);
}
return NOTEAM;
}
/*
==================
dmctf_c::AssignTeam
==================
*/
void dmctf_c::AssignTeam(edict_t *who, char *userinfo)
{
edict_t *player;
int i;
int team1count = 0, team2count = 0;
int playernum;
bool killed_him = false;
char skin[1000],teamname[1000];
char newskin[1000],newteamname[1000];
// what we want to become
Com_sprintf(teamname,sizeof(teamname),"%s", Info_ValueForKey(userinfo,"teamname"));
// sanity checking
if (strlen(teamname) > 128)
gi.dprintf("CTF AssignTeam: teamname exceeded 128 - thought you'd like to know\n");
Com_sprintf(skin,sizeof(skin),"%s", Info_ValueForKey(userinfo,"skin"));
// sanity checking
if (strlen(skin) > 128)
gi.dprintf("CTF AssignTeam: skin exceeded 128 - thought you'd like to know\n");
// what we are currently
PB_GetActualSkinName(who,newskin);
// sanity checking
if (strlen(newskin) > 128)
gi.dprintf("CTF AssignTeam: newskin exceeded 128 - thought you'd like to know\n");
PB_GetActualTeamName(who,newteamname);
// sanity checking
if (strlen(newteamname) > 128)
gi.dprintf("CTF AssignTeam: newteamname exceeded 128 - thought you'd like to know\n");
// if we already have the skin we wanted, plus we have a team, just dip out, we don't need to do anything.
if((!stricmp(newskin,skin)) && (!stricmp(newteamname, teamname)) && who->client->resp.team)
return;
// only do this if the FORCEJOIN flag is NOT set.
if (!dmRule_FORCEJOIN())
{
team_t new_team;
// decide if the skin selection is appropriate
new_team = CompareSkin(Info_ValueForKey(userinfo,"skin"), Info_ValueForKey(userinfo,"teamname"));
// if this is not the first time through then we are already playing so in case we change teams, we'd better gib the player
if (who->client->resp.team && (who->client->resp.team != new_team) && (who->client->pers.connected) && (who->inuse))
{
gi.dprintf("Start AssignTeamKillPlayer %s\n", who->client->pers.netname);
gi.dprintf(" new_team: %d\n", new_team);
gi.dprintf(" old_team: %d\n", who->client->resp.team);
gi.dprintf(" teamname: %s\n", teamname);
gi.dprintf(" skin: %s\n", skin);
who->flags &= ~FL_GODMODE;
who->health = 0;
meansOfDeath = MOD_SUICIDE;
player_die (who, who, who, 100000, vec3_origin);
// Don't even bother waiting for death frames before respawning.
who->deadflag = DEAD_DEAD;
// since we are changing teams, zero his score
who->client->resp.score = 0;
killed_him = true;
gi.dprintf("End AssignTeamKillPlayer\n");
}
if (new_team)
{
gi.dprintf("Start AssignTeamCreatePlayer %s\n", who->client->pers.netname);
gi.dprintf(" new_team: %d\n", new_team);
gi.dprintf(" teamname: %s\n", teamname);
gi.dprintf(" skin: %s\n", skin);
who->client->resp.team = new_team;
// has to be done
PB_InitBody(*who,userinfo);
// just to be sure, lets force those new skins to old ones
playernum = who-g_edicts-1;
gi.configstring(CS_PLAYERSKINS+playernum,va("%s\\%s\\%s",who->client->pers.netname,teamname,skin));
gi.dprintf("End AssignTeamCreatePlayer\n");
return;
}
// other wise fall through, put up a message and do the random team assign
gi.SP_Print(who, DM_CTF_BAD_SKIN);
}
// ok - we are playing a FORCEJOIN game. If we are already have a team, dump us out. We shouldn't be messing with skins if we've already joined a FORCE JOIN game
else
if (who->client->resp.team)
{
// tell player he can't change his skin
gi.SP_Print(who, DM_CTF_NO_CHANGE);
// reset skin if its changed
if(stricmp(newskin,skin))
{
skin[0]='*';
skin[1]=0;
strcat(skin+1,newskin); //FIXME:
Info_SetValueForKey(userinfo,"skin",newskin);
}
// reset team if its changed
if(stricmp(newteamname,teamname))
{
teamname[0]='*';
teamname[1]=0;
strcat(teamname+1,newteamname); //FIXME:
Info_SetValueForKey(userinfo,"teamname",newteamname);
}
// just to be sure, lets force those new skins to old ones
playernum = who-g_edicts-1;
gi.configstring(CS_PLAYERSKINS+playernum,va("%s\\%s\\%s",who->client->pers.netname,teamname,skin));
return;
}
// count how many are on each team
for (i = 1; i <= (int)maxclients->value; i++)
{
player = &g_edicts[i];
if (player == who)
continue;
switch (player->client->resp.team)
{
case TEAM1:
team1count++;
break;
case TEAM2:
team2count++;
}
}
// assign a team to this player
if (team1count < team2count)
who->client->resp.team = TEAM1;
else
if (team2count < team1count)
who->client->resp.team = TEAM2;
else
if (gi.irand(0,1))
who->client->resp.team = TEAM1;
else
who->client->resp.team = TEAM2;
// ok -now a team has been assigned, lets assign a skin
if (who->client->resp.team == TEAM1)
{
teamname[0]='*';
teamname[1]=0;
strcat(teamname+1,ctf_team_blue->string);
Info_SetValueForKey(userinfo,"teamname",ctf_team_blue->string);
FindSkin(ctf_team_blue->string, gi.irand(0,num_blue_skins-1));
skin[0]='*';
skin[1]=0;
strcat(skin+1,NewSkin);
Info_SetValueForKey(userinfo,"skin",NewSkin);
gi.SP_Print(who, DM_CTF_ASSIGN_BLUE, ctf_team_blue->string,NewSkin);
}
else
{
teamname[0]='*';
teamname[1]=0;
strcat(teamname+1,ctf_team_red->string);
Info_SetValueForKey(userinfo,"teamname",ctf_team_red->string);
FindSkin(ctf_team_red->string, gi.irand(0,num_red_skins-1));
skin[0]='*';
skin[1]=0;
strcat(skin+1,NewSkin);
Info_SetValueForKey(userinfo,"skin",NewSkin);
gi.SP_Print(who, DM_CTF_ASSIGN_RED, ctf_team_red->string,NewSkin);
}
playernum = who-g_edicts-1;
gi.configstring(CS_PLAYERSKINS+playernum,va("%s\\%s\\%s",who->client->pers.netname,teamname,skin));
PB_InitBody(*who,userinfo);
}
/*
==================
dmctf_c::FindFlagEdict
==================
*/
edict_t *dmctf_c::FindFlagEdict(int team)
{
edict_t *other;
int i;
// firstly, look through all the players to see if anyone has it
for(i=1;i<=globals.max_edicts;i++)
{
other=&g_edicts[i];
if(!other->inuse)
continue;
// found it?
if (other->ctf_flags == team)
return other;
}
// ok, couldn't find it - Uh oh.
assert(0);
return NULL;
}
/*
==================
dmctf_c::clientDie
==================
*/
void dmctf_c::clientDie(edict_t &ent, edict_t &inflictor, edict_t &killer)
{
// record this value, since we are about to lose it if when we drop the flag
int ctf_flags = ent.ctf_flags;
int ent_team = ent.client->resp.team;
// drop the flag if we have it
if(ent.client->inv->hasItemType(SFE_CTFFLAG))
{
clientDropItem(&ent, SFE_CTFFLAG, 0);
// reset our flag count to 0 again
ent.client->inv->removeSpecificItem(SFE_CTFFLAG);
}
// Lose a frag?
if((!killer.client)||(&killer==&ent))
{
// I was killed by non player or killed myself.
if (&killer==&ent)
{
// the killer is me
ent.client->resp.score -= sv_suicidepenalty->value;
}
else
{
ent.client->resp.score--;
}
return;
}
// Must've been killed by another player.
if(meansOfDeath & MOD_FRIENDLY_FIRE)
{
killer.client->resp.score--;
// was I the flag carrier? if so, knock off 20 points for killing me
if (ctf_flags)
{
killer.client->resp.score -= 19;
}
}
else
{
edict_t *flag = NULL;
killer.client->resp.score++;
// if we just hurt his flag carrier, give him another bonus
if ((level.time - ent.ctf_hurt_carrier) < CTF_TIME_OUT)
{
if (ent_team == 2)
{
gi.SP_Print(NULL, DM_CTF_FRAG_HURT_CARRIER_BLUE , killer.s.number, ent.s.number, CTF_CARRIER_DANGER_PROTECT_BONUS);
}
else
{
gi.SP_Print(NULL, DM_CTF_FRAG_HURT_CARRIER_RED , killer.s.number, ent.s.number, CTF_CARRIER_DANGER_PROTECT_BONUS);
}
killer.client->resp.score += CTF_CARRIER_DANGER_PROTECT_BONUS;
}
// ok, if we held the killers team flag, give the other guy more points.
if (ctf_flags & killer.client->resp.team)
{
if (ctf_flags == 2)
{
gi.SP_Print(NULL, DM_CTF_FRAG_CARRIER_BLUE , killer.s.number, CTF_FRAG_CARRIER_BONUS);
}
else
{
gi.SP_Print(NULL, DM_CTF_FRAG_CARRIER_RED , killer.s.number, CTF_FRAG_CARRIER_BONUS);
}
killer.client->resp.score += CTF_FRAG_CARRIER_BONUS;
}
else
{
// find the flag edict
flag = FindFlagEdict(killer.client->resp.team);
// just in case
if (flag)
{
vec3_t sub_vec;
float distance;
// figure out the vector length between you and the flag
VectorSubtract(ent.s.origin, flag->s.origin, sub_vec);
distance = VectorLength(sub_vec);
// if we are close enough, give us the bonus
if (distance <= CTF_FLAG_DEFENSE_RADIUS)
{
if (killer.client->resp.team == 1)
{
gi.SP_Print(NULL, DM_CTF_PROTECT_FLAG_BLUE , killer.s.number, CTF_FLAG_DEFENSE_BONUS);
}
else
{
gi.SP_Print(NULL, DM_CTF_PROTECT_FLAG_RED , killer.s.number, CTF_FLAG_DEFENSE_BONUS);
}
killer.client->resp.score += CTF_FLAG_DEFENSE_BONUS;
}
}
}
}
}
/*
==================
dmctf_c::clientDisconnect
==================
*/
void dmctf_c::clientDisconnect(edict_t &ent)
{
ent.client->resp.team = NOTEAM;
// drop the flag if we have it
// if its a flag slot, and we have a flag there, drop it
if(ent.client->inv->hasItemType(SFE_CTFFLAG))
clientDropItem(&ent, SFE_CTFFLAG, 0);
// close the log file if we have one open
closeLogFile();
}
/*
==================
dmctf_c::clientRespawn
==================
*/
void dmctf_c::clientRespawn(edict_t &ent)
{
sharedEdict_t sh;
sh.inv = (inven_c *)ent.client->inv;
sh.edict = &ent;
sh.inv->setOwner(&sh);
sh.inv->clearInv(true);
sh.inv->rulesSetWeaponsUseAmmo(dm->dmRule_INFINITE_AMMO()?0:-1);
sh.inv->rulesSetWeaponsReload(dm->dmRule_NOWEAPRELOAD()?0:-1);
sh.inv->rulesSetBestWeaponMode(Info_ValueForKey(ent.client->pers.userinfo,"bestweap"));
sh.inv->rulesSetDropWeapons(!dm->dmRule_WEAPONS_STAY());
sh.inv->rulesSetNonDroppableWeaponTypes(1<<SFW_KNIFE);
if(dm->dmRule_REALISTIC_DAMAGE())
sh.inv->rulesSetNonDroppableWeaponTypes(1<<SFW_PISTOL1);
sh.inv->addWeaponType(SFW_KNIFE);
sh.inv->addWeaponType(SFW_PISTOL1);
sh.inv->addWeaponType(SFW_HURTHAND);
sh.inv->addWeaponType(SFW_THROWHAND);
if(!dm->dmRule_INFINITE_AMMO())
{
sh.inv->addAmmoType(AMMO_KNIFE,6);
sh.inv->addAmmoType(AMMO_9MM,150);
}
sh.inv->selectWeapon(SFW_PISTOL1);
}
/*
==================
dmctf_c::clientScoreboardMessage
==================
*/
#define BOARD_X_OFFSET 180
void dmctf_c::clientScoreboardMessage(edict_t *ent, edict_t *killer, qboolean log_file)
{
int i,j,k;
int sorted[2][MAX_CLIENTS];
int sortedscores[2][MAX_CLIENTS];
struct TeamInfo_s teams[MAX_CLIENTS];
int score,total[2],total_teams, real_total[2], team;
int x,y,top,z;
gclient_t *cl;
edict_t *cl_ent;
total[0] = total[1] =total_teams = 0;
teams[0].teamname = teams[1].teamname = 0;
memset(sortedscores, 0, sizeof(sortedscores));
// Is logging to a log-file enabled?
if(log_file)
openLogFile();
// Sort all the clients by score from highest to lowest.
for(i=0;i<game.maxclients;i++)
{
cl_ent=g_edicts+1+i;
if(!cl_ent->inuse)
continue;
// Spectators get added separately if there's any room left.
if(cl_ent->client->resp.spectator)
continue;
// get our score and team
score = game.clients[i].resp.score;
team = game.clients[i].resp.team -1;
// figure out where we are in this teams list
for(j=0;j<total[team];j++)
{
if(score > sortedscores[team][j])
{
break;
}
}
// shuffle us a hole to put the new score in
for(k=total[team];k>j;k--)
{
sorted[team][k]=sorted[team][k-1];
sortedscores[team][k]=sortedscores[team][k-1];
}
// insert the score
sorted[team][j]=i;
sortedscores[team][j]=score;
total[team]++;
for(j=0;j<total_teams;j++)
{
if (OnSameTeam(g_edicts + 1 + teams[j].rep, cl_ent))
{
teams[j].score += score;
break;
}
}
if (j == total_teams)
{
teams[total_teams].rep = i;
teams[total_teams].score = score;
teams[total_teams].teamname = Info_ValueForKey (cl_ent->client->pers.userinfo, "teamname");
if (!stricmp(teams[total_teams].teamname, ctf_team_blue->string))
{
teams[total_teams].flags = blue_collected;
teams[total_teams].team = TEAM1;
}
else
{
teams[total_teams].flags = red_collected;
teams[total_teams].team = TEAM2;
}
total_teams++;
}
}
real_total[0]=total[0];
real_total[1]=total[1];
if(total[0]>12)
{
total[0]=12;
}
if(total[1]>12)
{
total[1]=12;
}
// Clear the client's layout.
top=32;
gi.SP_Print(ent,DM_GENERIC_LAYOUT_RESET);
// Handle team aggregate scores.
top = -64;
for(i=0;i<total_teams;i++)
{
x=BOARD_X_OFFSET*(teams[i].team - 1);
gi.SP_Print(ent,DM_CTF_LAYOUT_SCOREBOARD_TEAM,
(short)x,(short)top,
teams[i].rep,
(short)teams[i].score,
(short)teams[i].flags);
}
for (z=0; z<2; z++)
{
// Write the scores to the open log-file if we have one.
if(filePtr)
{
if(teams[z].teamname)
{
fprintf(filePtr,"%sTeam %s\n",log_file_line_header->string,teams[z].teamname);
fprintf(filePtr,"%sScore %i\n",log_file_line_header->string,teams[z].score);
fprintf(filePtr,"%sFlag Captures %i\n\n",log_file_line_header->string,teams[z].flags);
}
}
top = 0;
y = top;
// Send the scores for all active players to the client's layout.
for(i=0;i<total[z];i++)
{
cl=&game.clients[sorted[z][i]];
cl_ent=g_edicts+1+sorted[z][i];
x=(cl->resp.team -1) * BOARD_X_OFFSET;
gi.SP_Print(ent,DM_GENERIC_LAYOUT_CTF_CLIENT,
(short)x,(short)y,
sorted[z][i],
(short)cl->resp.score,
(unsigned short)cl->ping,
(unsigned short)((level.framenum-cl->resp.enterframe)/600),
(short)cl_ent->ctf_flags);
y+=32;
}
// Write the scores to the open log-file if we have one.
if(filePtr)
{
for(i=0;i<real_total[z];i++)
{
cl=&game.clients[sorted[z][i]];
cl_ent=g_edicts+1+sorted[z][i];
fprintf(filePtr,"%sClient %s\n",log_file_line_header->string,cl_ent->client->pers.netname);
fprintf(filePtr,"%sScore %i\n",log_file_line_header->string,cl->resp.score);
fprintf(filePtr,"%sPing %i\n",log_file_line_header->string,cl->ping);
fprintf(filePtr,"%sTime %i\n%s\n",log_file_line_header->string,(level.framenum-cl->resp.enterframe)/600,log_file_line_header->string);
}
}
}
// Send the scores for all spectators to the client's layout - if there's
// any room left on the scoreboard.
i=(total[0]>total[1])?total[0]:total[1];
if(i<12)
{
y=top=i*32;
i=0;
j=0;
while((j<game.maxclients)&&(i<12))
{
cl=&game.clients[j];
cl_ent=g_edicts+1+j;
if((!cl_ent->inuse)||(!cl_ent->client->resp.spectator))
{
j++;
continue;
}
x=(i>=3)?BOARD_X_OFFSET:0;
if(i==3)
y=top;
gi.SP_Print(ent,DM_GENERIC_LAYOUT_SCOREBAORD_SPECTATOR,
(short)x,(short)y,
j,
(unsigned short)cl->ping,
(unsigned short)((level.framenum-cl->resp.enterframe)/600));
y+=32;
i++;
j++;
}
}
// Write the spectators to the open log-file if we have one.
if(filePtr)
{
for(i=0;i<game.maxclients;i++)
{
cl=&game.clients[i];
cl_ent=g_edicts+1+i;
if((!cl_ent->inuse)||(!cl_ent->client->resp.spectator))
continue;
fprintf(filePtr,"%sClient %s\n",log_file_line_header->string,cl_ent->client->pers.netname);
fprintf(filePtr,"%s %s\n",log_file_line_header->string,"spectator");
fprintf(filePtr,"%sPing %i\n",log_file_line_header->string,cl->ping);
fprintf(filePtr,"%sTime %i\n%s\n",log_file_line_header->string,(level.framenum-cl->resp.enterframe)/600,log_file_line_header->string);
}
}
// Close the open log file if we have one.
closeLogFile();
}
/*================
CaptureLimitHit
================*/
bool dmctf_c::CaptureLimitHit(int limit)
{
if (!limit)
{
return false;
}
if (blue_collected >= limit)
{
return true;
}
if (red_collected >= limit)
{
return true;
}
return false;
}
// NOT PART OF THE RULES CLASS - was in g_misc.c but for the sake of consistency, I'm adding it here.
//=================================================================================
void find_flag(char *flag_class_type, team_t team)
{
edict_t *flag = NULL;
int i;
Pickup *pickup = thePickupList.GetPickupFromSpawnName("ctf");
// firstly, look through all the players to see if anyone has it
for(i=1;i<=globals.max_edicts;i++)
{
flag=&g_edicts[i];
if(!flag->inuse)
continue;
// found it?
if (flag->ctf_flags == team && stricmp(flag_class_type, flag->classname))
break;
}
if (i== globals.max_edicts+1)
return;
// ok, did we find a dropped flag?
if (!stricmp(flag->classname, "ctf dropped flag"))
{
reset_flag(flag, NULL);
G_FreeEdict(flag);
}
// what about a flag attached to a player?
else
{
reset_flag(flag, flag);
// remove flag from player
flag->ctf_flags = 0;
// delete the ghoul object on the players hip so we lose the flag there.
if (flag->ghoulInst)
PB_RemoveFlag(flag);
// if we have the flag klaxon sound going, turn it off
if (flag->s.sound == gi.soundindex("player/flaglp.wav"))
{
flag->s.sound = 0;
}
// reset our flag count to 0 again
flag->client->inv->removeSpecificItem(SFE_CTFFLAG);
}
}
void ctf_base_touch (edict_t *self, edict_t *other, cplane_t *plane, mtexinfo_t *surf)
{
if (!other->client)
return;
// determine if we have a flag, and if we do, compare it to the base we standing on - if they aren't the same, we are done
if (!(other->ctf_flags) || (other->ctf_flags == self->count))
{
// nope
return;
}
if (!(ctf_flag_captured->value))
{
edict_t *home = NULL;
char *flagname;
// first thing we have to do is determine if our flag is still on the spawn spot
if (self->count == TEAM1)
{
flagname="ctf_flag_blue";
}
else
{
flagname="ctf_flag_red";
}
// find our flag spawn point
home = G_Find (home, FOFS(classname), flagname);
if (home)
{
// if we don't have a flag there, just return
if (!home->ctf_flags)
{
return;
}
}
gi.sound(other,CHAN_NO_PHS_ADD,gi.soundindex("dm/ctf/flagbase.wav"),1.0,ATTN_NONE,0,SND_LOCALIZE_GLOBAL);
dm->FlagCaptured(other->client->resp.team);
// give us a big old bonus.
if (other->ctf_flags == 1)
{
gi.SP_Print(NULL, DM_CTF_END_GAME_BONUS_BLUE, other->s.number, CTF_CAPTURE_BONUS);
}
else
{
gi.SP_Print(NULL, DM_CTF_END_GAME_BONUS_RED, other->s.number, CTF_CAPTURE_BONUS);
}
other->client->resp.score += CTF_CAPTURE_BONUS;
// now, decide if we should just reset the flag, or if this map is over
gi.cprintf(NULL, PRINT_HIGH,"Loops count %.2f, count %.2f\n",ctf_loops_count->value+1 ,ctf_loops->value);
if (ctf_loops->value && dm->CaptureLimitHit(ctf_loops->value)) //0 is a value too.
{
// otherwise this game is over.
gi.cvar_setvalue("ctf_flag_captured", 1.0);
// remove any center screen messages we should.
gi.Con_ClearNotify ();
}
// ok, we aren't over yet
else
{
gi.cvar_setvalue("ctf_loops_count", ctf_loops_count->value+ 1.0);
// now reset both flags
// find the blue flag
find_flag("ctf_flag_blue", TEAM1);
// find the red flag
find_flag("ctf_flag_red", TEAM2);
}
}
}
/*QUAKED misc_ctf_base (1 0 0) (-32 -32 -24) (32 32 -16)
Stepping onto this base in possesion of the correct flag will end the current CTF game.
count = team base. 1 = team 1, 2 = team 2
*/
void SP_misc_ctf_base (edict_t *ent)
{
// only give us these if we are playing deathmatch, and its a CTF game
if (dm->isDM() && deathmatch->value == DM_CTF)
{
ent->touch = ctf_base_touch;
ent->solid = SOLID_TRIGGER;
VectorSet (ent->mins, -32, -32, -24);
VectorSet (ent->maxs, 32, 32, -16);
gi.linkentity (ent);
}
else
{
G_FreeEdict (ent);
}
}