Descent3/netgames/ctf/ctf.cpp
2024-04-15 21:43:29 -06:00

2838 lines
79 KiB
C++

/*
* $Logfile: /DescentIII/Main/ctf/ctf.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003/08/26 03:56:47 $
* $Author: kevinb $
*
* <insert description of file here>
*
* $Log: ctf.cpp,v $
* Revision 1.1.1.1 2003/08/26 03:56:47 kevinb
* initial 1.5 import
*
*
* 140 10/03/01 1:05p Matt
* Made team_name buffer large enough to hold the team number *plus* the
* number of players on the team.
*
* 139 9/24/01 2:28p Matt
* Allowed room for longer team name on results screen.
*
* 138 9/20/01 12:58p Matt
* Added a team member list to the stats file.
*
* 137 10/21/99 9:27p Jeff
* B.A. Macintosh code merge
*
* 136 9/03/99 5:30p Jeff
* use the flag's roomnumber instead of the player's roomnum when checking
* for a score (in case of a really thin room that the flag is in)
*
* 135 8/17/99 5:53p Jeff
* track ranks on PXO
*
* 134 7/15/99 1:17a Jeff
* fixed up $scores
*
* 133 7/13/99 12:10p Jeff
* added some specific text taunt token decoding
*
* 132 7/12/99 2:27p Jeff
* fixed PLR to only display the team label for the disconnected list if
* there are people in the list
*
* 131 7/11/99 6:54p Jeff
* fixed PLR so it doesn't go off the screen on long lists and active
* players in the game are shown first
*
* 130 6/10/99 5:21p Jeff
* fixed crash in french version, due to local array being to small
*
* 129 5/26/99 4:38a Jeff
* fixed ctf bad scoring bugs
*
* 128 5/23/99 3:04a Jason
* fixed bug with player rankings not being updated correctly
*
* 127 5/20/99 7:54p 3dsmax
* changed number of attach points
*
* 126 5/12/99 11:04p Jeff
* dmfc and multiplayer games now have endian friendly packets (*whew*)
*
* 125 5/12/99 11:28a Jeff
* added sourcesafe comment block
*
* $NoKeywords: $
*/
#include "gamedll_header.h"
#include <string.h>
#include "idmfc.h"
#include "ctf.h"
#include "CTFstr.h"
IDMFC *DMFCBase;
IDmfcStats *dstat;
object *dObjects;
player *dPlayers;
room *dRooms;
netplayer *dNetPlayers;
#define SPID_GAMESTATE 0x01
#define SPID_ADDDELFLAG 0x02
#define SPID_COLLIDE 0x03
//Inventory flags
#define FLAGMASK_REDTEAM 0x01
#define FLAGMASK_BLUETEAM 0x02
#define FLAGMASK_GREENTEAM 0x04
#define FLAGMASK_YELLOWTEAM 0x08
//athome masks
#define REDAH 0x001
#define BLUEAH 0x002
#define GREENAH 0x004
#define YELLOWAH 0x008
#define FLAG_TIMEOUT_VALUE 120
//Sound name defines
#define SOUND_PICKUPFLAG_OWNTEAM "CTFPickupFlag1"
#define SOUND_PICKUPFLAG_OTHERTEAM "CTFPickupFlag1"
#define SOUND_SCORE_OWNTEAM "CTFScore1"
#define SOUND_SCORE_OTHERTEAM "CTFScore1"
#define SOUND_LOSEFLAG_OWNTEAM "CTFLostFlag1"
#define SOUND_LOSEFLAG_OTHERTEAM "CTFLostFlag1"
#define SOUND_FLAGRETURNED_OWNTEAM "CTFReturnedFlag1"
#define SOUND_FLAGRETURNED_OTHERTEAM "CTFReturnedFlag1"
#define SOUND_HATTRICK_FIRST "CTFHatTrick"
#define SOUND_HATTRICK_REGULAR "CTFHatTrick"
/*
$$TABLE_SOUND "CTFPickupFlag1"
$$TABLE_SOUND "CTFPickupFlag1"
$$TABLE_SOUND "CTFScore1"
$$TABLE_SOUND "CTFScore1"
$$TABLE_SOUND "CTFLostFlag1"
$$TABLE_SOUND "CTFLostFlag1"
$$TABLE_SOUND "CTFReturnedFlag1"
$$TABLE_SOUND "CTFReturnedFlag1"
$$TABLE_SOUND "CTFHatTrick"
$$TABLE_SOUND "CTFHatTrick"
*/
int snd_return_ownteam = -1;
int snd_return_otherteam = -1;
int snd_pickedup_otherteam = -1;
int snd_pickedup_ownteam = -1;
int snd_score_ownteam = -1;
int snd_score_otherteam = -1;
int snd_lose_ownteam = -1;
int snd_lose_otherteam = -1;
int snd_hattrick_first = -1;
int snd_hattrick_reg = -1;
typedef struct
{
int Score[2];
}tPlayerStat; //Overall scores (throughout the game)
int pack_pstat(tPlayerStat *user_info,ubyte *data)
{
int count = 0;
MultiAddInt(user_info->Score[0],data,&count);
MultiAddInt(user_info->Score[1],data,&count);
return count;
}
int unpack_pstat(tPlayerStat *user_info,ubyte *data)
{
int count = 0;
user_info->Score[0] = MultiGetInt(data,&count);
user_info->Score[1] = MultiGetInt(data,&count);
return count;
}
int SortedPlayers[MAX_PLAYER_RECORDS]; //sorted player nums
int SortedPLRPlayers[DLLMAX_TEAMS][MAX_PLAYER_RECORDS];
int TeamScores[DLLMAX_TEAMS]; //teams scores
int OverallTeamScores[DLLMAX_TEAMS]; //overall scores per team
int SortedTeams[DLLMAX_TEAMS]; //sorted team scores
int Highlight_bmp = -1;
bool DisplayScoreScreen;
bool Someone_has_hattrick = false; //this is false if no one has had a hattrick this level
bool First_game_frame = false;
int FlagIDs[DLLMAX_TEAMS]; //Flag Object ID's
int AFlagIDs[DLLMAX_TEAMS]; //Attached Flag Object ID's
int GoalRooms[DLLMAX_TEAMS]; //Goal Rooms for Teams
int FlagBmp[DLLMAX_TEAMS]; //Flag Bitmap handles
int FlagAHBmp[DLLMAX_TEAMS]; //Flag At Home Bitmap handles
int DimFlagAHBmp[DLLMAX_TEAMS]; //Dimmed versions of the Flag At Home Bitmaps
bool FlagAtHome[DLLMAX_TEAMS]; //Flag At Home bools
int HasFlag[DLLMAX_TEAMS]; //List of Playernums of who has what flag, -1 if no one does
bool DisplayFlagBlink=true,DisplayScoreBlink=true;
int WhoJustFlagged=-1,WhoJustFlaggedTimer=-1;
int WhoJustScored=-1,WhoJustScoredTimer=-1;
int CTFNumOfTeams = 2;
int ChildFlags[DLLMAX_TEAMS]; //Object handles of attached flags as a player has em
float Flag_timeout_timers[DLLMAX_TEAMS];
bool display_my_welcome = false;
void OnTimerScoreKill(void); //timer callback: when a score flash timer ends
void OnTimerScore(void); //timer callback: on a score flash interval
void OnTimer(void); //timer callback: when a flag taken flash timer ends
void OnTimerKill(void); //timer callback: on a flag taken flash interval
void DisplayWelcomeMessage(int player_num); //displays a welcome message to the player when he joins
void SortTeamScores(int *sortedindex,int *scores); //sorts an array of team scores, filling in the sorted index numbers
void DisplayHUDScores(struct tHUDItem *hitem); //callback when the HUD info is to be drawn
void ReceiveGameState(ubyte *data); //callback when a gamestate packet is received from the server
void SendGameState(int playernum); //called when the server is to send gamestate packet to a client
void SetColoredBalls(int playernum,bool reset=false);//sets the colored balls around a player (determined by what is in their inventory)
void ChangeNumberOfTeams(int newsize); //called when the number of teams in the game is changed or to be changed
void DoFlagReturnedHome(int team); //called to handle any events when a flag is returned home for a team
void DoLoseFlag(int team); //called to handle any events when a team loses their flag
void TellClientsToAddorDelFlag(int pnum,int team,int objnum,bool add);
void ServerIsTellingMeToAddorDelAFlag(ubyte *data);
void OnGetTokenString(char *src,char *dest,int dest_size);
// returns the number of flags a player has, 0 if none, or an invalid pnum
int GetFlagCountForPlayer(int pnum);
// returns the mask of which flags this player currently has
ubyte GetFlagMaskForPlayer(int pnum);
// adds a flag to a player, as a precaution, it will go through all the players and makes sure that no one
// has the flag that is being added. If they are adding the flag, than remove that flag from whoever we thought had it
// it will return false if it had to remove a flag from a player
bool GivePlayerFlag(int pnum,ubyte team);
//this function takes a flag away from the player, useful for when he scores, spews, disconnects, or observer modes
void LoseFlagForPlayer(int pnum,ubyte team,bool remove_from_inven=true);
///////////////////////////////////////////////
//localization info/functions
char **StringTable;
int StringTableSize = 0;
char *_ErrorString = "Missing String";
char *GetString(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];}
void SaveStatsToFile(char *filename);
#ifdef MACINTOSH
#pragma export on
#endif
// This function gets called by the game when it wants to learn some info about the game
void DLLFUNCCALL DLLGetGameInfo (tDLLOptions *options)
{
options->flags = DOF_MAXTEAMS|DOF_MINTEAMS;
options->max_teams = 4;
options->min_teams = 2;
strcpy(options->game_name,TXT_CTF);
strcpy(options->requirements,"MINGOALS2,GOALPERTEAM");
}
void DetermineScore(int precord_num,int column_num,char *buffer,int buffer_size)
{
player_record *pr = DMFCBase->GetPlayerRecord(precord_num);
if(!pr || pr->state==STATE_EMPTY){
buffer[0] = '\0';
return;
}
tPlayerStat *stat = (tPlayerStat *)pr->user_info;
sprintf(buffer,"%d",(stat)?stat->Score[DSTAT_LEVEL]:0);
}
void TeamScoreCallback(int team,char *buffer,int buffer_size)
{
ASSERT(team>=0 && team<DLLMAX_TEAMS);
sprintf(buffer," %d",TeamScores[team]);
}
void ShowStatBitmap(int precord_num,int column_num,int x,int y,int w,int h,ubyte alpha_to_use)
{
player_record *pr = DMFCBase->GetPlayerRecord(precord_num);
int flagcount,flagmask;
if(pr && pr->state==STATE_INGAME ){
flagcount = GetFlagCountForPlayer(pr->pnum);
if(!flagcount)
return;
flagmask = GetFlagMaskForPlayer(pr->pnum);
//Display the flags that this player has
x = x + w - (11*flagcount);
for(int q=0;q<CTFNumOfTeams;q++){
//draw all the flags the player has in his inventory
if(flagmask&0x01){
//player has this flag
DLLRenderHUDQuad(x,y,10,10,0,0,1,1,FlagBmp[q],alpha_to_use,0);
x += 11;
}
//rotate 1 bit for next flag
flagmask = flagmask>>1;
}
}
}
///////////////////////////////////////////////
// Initializes the game function pointers
void DLLFUNCCALL DLLGameInit(int *api_func,ubyte *all_ok,int num_teams_to_use)
{
*all_ok = 1;
DMFCBase = CreateDMFC();
if(!DMFCBase)
{
*all_ok = 0;
return;
}
dstat = CreateDmfcStats();
if(!dstat)
{
*all_ok = 0;
return;
}
DMFCBase->LoadFunctions(api_func);
// Setup event handlers
DMFCBase->Set_OnInterval(OnInterval);
DMFCBase->Set_OnHUDInterval(OnHUDInterval);
DMFCBase->Set_OnKeypress(OnKeypress);
DMFCBase->Set_OnServerGameCreated(OnServerGameCreated);
DMFCBase->Set_OnClientLevelStart(OnClientLevelStart);
DMFCBase->Set_OnClientLevelEnd(OnClientLevelEnd);
DMFCBase->Set_OnGameStateRequest(OnGameStateRequest);
DMFCBase->Set_OnPlayerConnect(OnPlayerConnect);
DMFCBase->Set_OnClientPlayerKilled(OnClientPlayerKilled);
DMFCBase->Set_OnServerCollide(OnServerCollide);
//DMFCBase->Set_OnClientCollide(OnClientCollide);
DMFCBase->Set_OnPLRInterval(OnPLRInterval);
DMFCBase->Set_OnPLRInit(OnPLRInit);
DMFCBase->Set_OnCanChangeTeam(OnCanChangeTeam);
DMFCBase->Set_OnSaveStatsToFile(OnSaveStatsToFile);
DMFCBase->Set_OnLevelEndSaveStatsToFile(OnLevelEndSaveStatsToFile);
DMFCBase->Set_OnDisconnectSaveStatsToFile(OnDisconnectSaveStatsToFile);
DMFCBase->Set_OnPrintScores(OnPrintScores);
DMFCBase->Set_OnPlayerEntersObserver(OnPlayerEntersObserver);
DMFCBase->Set_OnClientPlayerDisconnect(OnClientPlayerDisconnect);
DMFCBase->Set_OnPlayerChangeTeam(OnPlayerChangeTeam);
DMFCBase->Set_OnGetTokenString(OnGetTokenString);
// Setup arrays for easier to read code
dObjects = DMFCBase->GetObjects();
dPlayers = DMFCBase->GetPlayers();
dRooms = DMFCBase->GetRooms();
dNetPlayers = DMFCBase->GetNetPlayers();
netgame_info *Netgame = DMFCBase->GetNetgameInfo();
Netgame->flags |= (NF_TRACK_RANK);
CTFNumOfTeams = num_teams_to_use;
DMFCBase->GameInit(CTFNumOfTeams);
DLLCreateStringTable("CTF.str",&StringTable,&StringTableSize);
DLLmprintf((0,"%d strings loaded from string table\n",StringTableSize));
if(!StringTableSize){
*all_ok = 0;
return;
}
ChangeNumberOfTeams(CTFNumOfTeams);
//add the death and suicide messages
DMFCBase->AddDeathMessage(TXT_DEATH1,true);
DMFCBase->AddDeathMessage(TXT_DEATH2,true);
DMFCBase->AddDeathMessage(TXT_DEATH3,false);
DMFCBase->AddDeathMessage(TXT_DEATH4,false);
DMFCBase->AddDeathMessage(TXT_DEATH5,true);
DMFCBase->AddDeathMessage(TXT_DEATH6,true);
DMFCBase->AddDeathMessage(TXT_DEATH7,false);
DMFCBase->AddDeathMessage(TXT_DEATH8,true);
DMFCBase->AddDeathMessage(TXT_DEATH9,true);
DMFCBase->AddDeathMessage(TXT_DEATH10,true);
DMFCBase->AddSuicideMessage(TXT_SUICIDE1);
DMFCBase->AddSuicideMessage(TXT_SUICIDE2);
DMFCBase->AddSuicideMessage(TXT_SUICIDE3);
DMFCBase->AddSuicideMessage(TXT_SUICIDE4);
DMFCBase->AddSuicideMessage(TXT_SUICIDE5);
DMFCBase->AddSuicideMessage(TXT_SUICIDE6);
//setup the Playerstats struct so DMFC can handle it automatically when a new player enters the game
DMFCBase->SetupPlayerRecord(sizeof(tPlayerStat),(int (*)(void *,ubyte *))pack_pstat,(int (*)(void *,ubyte *))unpack_pstat);
DMFCBase->AddHUDItemCallback(HI_TEXT,DisplayHUDScores);
DMFCBase->RegisterPacketReceiver(SPID_GAMESTATE,ReceiveGameState);
DMFCBase->RegisterPacketReceiver(SPID_ADDDELFLAG,ServerIsTellingMeToAddorDelAFlag);
DMFCBase->RegisterPacketReceiver(SPID_COLLIDE,OnClientCollide);
//Load all the bitmaps
FlagBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIcon.ogf",0,BITMAP_FORMAT_1555);
//athome bitmaps
FlagAHBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagAHBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagAHBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIcon.ogf",0,BITMAP_FORMAT_1555);
FlagAHBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIcon.ogf",0,BITMAP_FORMAT_1555);
DimFlagAHBmp[RED_TEAM] = DLLbm_AllocLoadFileBitmap("RedFlagIconDim.ogf",0,BITMAP_FORMAT_1555);
DimFlagAHBmp[BLUE_TEAM] = DLLbm_AllocLoadFileBitmap("BlueFlagIconDim.ogf",0,BITMAP_FORMAT_1555);
DimFlagAHBmp[GREEN_TEAM] = DLLbm_AllocLoadFileBitmap("GreenFlagIconDim.ogf",0,BITMAP_FORMAT_1555);
DimFlagAHBmp[YELLOW_TEAM] = DLLbm_AllocLoadFileBitmap("YellowFlagIconDim.ogf",0,BITMAP_FORMAT_1555);
//fill in the IDs
FlagIDs[RED_TEAM] = DLLFindObjectIDName("FlagRed");
FlagIDs[BLUE_TEAM] = DLLFindObjectIDName("Flagblue");
FlagIDs[GREEN_TEAM] = DLLFindObjectIDName("FlagGreen");
FlagIDs[YELLOW_TEAM] = DLLFindObjectIDName("FlagYellow");
AFlagIDs[RED_TEAM] = DLLFindObjectIDName("ShipRedFlag");
AFlagIDs[BLUE_TEAM] = DLLFindObjectIDName("ShipBlueFlag");
AFlagIDs[GREEN_TEAM] = DLLFindObjectIDName("ShipGreenFlag");
AFlagIDs[YELLOW_TEAM] = DLLFindObjectIDName("ShipYellowFlag");
//give initial values for the at home so they're set
FlagAtHome[RED_TEAM] = FlagAtHome[BLUE_TEAM] =
FlagAtHome[GREEN_TEAM] = FlagAtHome[YELLOW_TEAM] = false;
//make sure all was init ok
for(int i=0;i<DLLMAX_TEAMS;i++){
if((FlagBmp[i]<=BAD_BITMAP_HANDLE)||(FlagAHBmp[i]<=BAD_BITMAP_HANDLE)||
(DimFlagAHBmp[i]<=BAD_BITMAP_HANDLE)||(FlagIDs[i]<0)||(AFlagIDs[i]<0)){
*all_ok = 0;
return;
}
}
Highlight_bmp = DLLbm_AllocBitmap(32,32,0);
if(Highlight_bmp>BAD_BITMAP_HANDLE){
ushort *data = DLLbm_data(Highlight_bmp,0);
if(!data){
//bail on out of here
*all_ok = 0;
return;
}
for(int x=0;x<32*32;x++){
data[x] = GR_RGB16(50,50,50)|OPAQUE_FLAG;
}
}
//Load in and touch all the sounds so they're ready to rock
snd_score_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_SCORE_OWNTEAM));
if(snd_score_ownteam!=-1)
DLLTouchSound(snd_score_ownteam);
snd_score_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_SCORE_OTHERTEAM));
if(snd_score_otherteam!=-1)
DLLTouchSound(snd_score_otherteam);
snd_pickedup_otherteam =DLLFindSoundName(IGNORE_TABLE(SOUND_PICKUPFLAG_OWNTEAM));
if(snd_pickedup_otherteam!=-1)
DLLTouchSound(snd_pickedup_otherteam);
snd_pickedup_ownteam =DLLFindSoundName(IGNORE_TABLE(SOUND_PICKUPFLAG_OTHERTEAM));
if(snd_pickedup_ownteam!=-1)
DLLTouchSound(snd_pickedup_ownteam);
snd_return_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_FLAGRETURNED_OWNTEAM));
if(snd_return_ownteam!=-1)
DLLTouchSound(snd_return_ownteam);
snd_return_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_FLAGRETURNED_OTHERTEAM));
if(snd_return_otherteam!=-1)
DLLTouchSound(snd_return_otherteam);
snd_lose_ownteam = DLLFindSoundName(IGNORE_TABLE(SOUND_LOSEFLAG_OWNTEAM));
if(snd_lose_ownteam!=-1)
DLLTouchSound(snd_lose_ownteam);
snd_lose_otherteam = DLLFindSoundName(IGNORE_TABLE(SOUND_LOSEFLAG_OTHERTEAM));
if(snd_lose_otherteam!=-1)
DLLTouchSound(snd_lose_otherteam);
snd_hattrick_first = DLLFindSoundName(IGNORE_TABLE(SOUND_HATTRICK_FIRST));
if(snd_hattrick_first!=-1)
DLLTouchSound(snd_hattrick_first);
snd_hattrick_reg = DLLFindSoundName(IGNORE_TABLE(SOUND_HATTRICK_REGULAR));
if(snd_hattrick_reg!=-1)
DLLTouchSound(snd_hattrick_reg);
DisplayScoreScreen = false;
// Initialize the Stats Manager
// ----------------------------
tDmfcStatsInit tsi;
tDmfcStatsColumnInfo pl_col[7];
char gname[32];
strcpy(gname,TXT_CTF);
tsi.flags = DSIF_SHOW_PIC|DSIF_SHOW_OBSERVERICON|DSIF_SEPERATE_BY_TEAM;
tsi.cColumnCountDetailed = 0;
tsi.cColumnCountPlayerList = 7;
tsi.clbDetailedColumnBMP = NULL;
tsi.clbDetailedColumn = NULL;
tsi.clbPlayerColumn = DetermineScore;
tsi.clbPlayerColumnBMP = ShowStatBitmap;
tsi.DetailedColumns = NULL;
tsi.GameName = gname;
tsi.MaxNumberDisplayed = NULL;
tsi.PlayerListColumns = pl_col;
tsi.SortedPlayerRecords = SortedPlayers;
tsi.clTeamLine = TeamScoreCallback;
pl_col[0].color_type = DSCOLOR_TEAM;
pl_col[0].title[0] = '\0';
pl_col[0].type = DSCOL_BMP;
pl_col[0].width = 33;
pl_col[1].color_type = DSCOLOR_TEAM;
strcpy(pl_col[1].title,TXT_PILOT);
pl_col[1].type = DSCOL_PILOT_NAME;
pl_col[1].width = 120;
pl_col[2].color_type = DSCOLOR_TEAM;
strcpy(pl_col[2].title,TXT_POINTS);
pl_col[2].type = DSCOL_CUSTOM;
pl_col[2].width = 52;
pl_col[3].color_type = DSCOLOR_TEAM;
strcpy(pl_col[3].title,TXT_KILLS_SHORT);
pl_col[3].type = DSCOL_KILLS_LEVEL;
pl_col[3].width = 47;
pl_col[4].color_type = DSCOLOR_TEAM;
strcpy(pl_col[4].title,TXT_DEATHS_SHORT);
pl_col[4].type = DSCOL_DEATHS_LEVEL;
pl_col[4].width = 47;
pl_col[5].color_type = DSCOLOR_TEAM;
strcpy(pl_col[5].title,TXT_SUICIDES_SHORT);
pl_col[5].type = DSCOL_SUICIDES_LEVEL;
pl_col[5].width = 47;
pl_col[6].color_type = DSCOLOR_TEAM;
strcpy(pl_col[6].title,TXT_PING);
pl_col[6].type = DSCOL_PING;
pl_col[6].width = 40;
dstat->Initialize(&tsi);
}
// Called when the DLL is shutdown
void DLLFUNCCALL DLLGameClose ()
{
//@@SaveConfig();
//@@DMFCBase->CFGClose();
//Free all the bitmaps
DLLbm_FreeBitmap(FlagBmp[RED_TEAM]);
DLLbm_FreeBitmap(FlagBmp[BLUE_TEAM]);
DLLbm_FreeBitmap(FlagBmp[GREEN_TEAM]);
DLLbm_FreeBitmap(FlagBmp[YELLOW_TEAM]);
DLLbm_FreeBitmap(DimFlagAHBmp[RED_TEAM]);
DLLbm_FreeBitmap(DimFlagAHBmp[BLUE_TEAM]);
DLLbm_FreeBitmap(DimFlagAHBmp[GREEN_TEAM]);
DLLbm_FreeBitmap(DimFlagAHBmp[YELLOW_TEAM]);
DLLbm_FreeBitmap(FlagAHBmp[RED_TEAM]);
DLLbm_FreeBitmap(FlagAHBmp[BLUE_TEAM]);
DLLbm_FreeBitmap(FlagAHBmp[GREEN_TEAM]);
DLLbm_FreeBitmap(FlagAHBmp[YELLOW_TEAM]);
if(Highlight_bmp>BAD_BITMAP_HANDLE)
DLLbm_FreeBitmap(Highlight_bmp);
DLLDestroyStringTable(StringTable,StringTableSize);
if(dstat)
{
dstat->DestroyPointer();
dstat = NULL;
}
if(DMFCBase)
{
DMFCBase->GameClose();
DMFCBase->DestroyPointer();
DMFCBase = NULL;
}
}
/////////////////////////////////////////////////////////////
//DMFC Overrides
//OnHUDInterval
void OnHUDInterval(void)
{
dstat->DoFrame();
DMFCBase->DisplayOutrageLogo();
DMFCBase->OnHUDInterval();
}
//OnInterval
void OnInterval(void)
{
DMFCBase->GetSortedPlayerSlots(SortedPlayers,MAX_PLAYER_RECORDS);
SortTeamScores(SortedTeams,TeamScores);
//Determine if a flag has timed out yet
for(int i=0;i<CTFNumOfTeams;i++){
if((HasFlag[i]==-1)&&(!FlagAtHome[i])){
//check the time out values
if(DMFCBase->GetGametime()>=Flag_timeout_timers[i]){
//the timeout has expired, move home!
vector home_pos;
int home,objnum;
Flag_timeout_timers[i] = 0;
FlagAtHome[i] = true;
HasFlag[i] = -1;
home = GoalRooms[i];
objnum = -1;
//find the flag objnum
for(int o=0;o<MAX_OBJECTS;o++){
if(dObjects[o].type==OBJ_POWERUP && dObjects[o].id==FlagIDs[i]){
//found it
objnum = o;
break;
}
}
if(objnum==-1){
//hmm couldn't find the flag
//this is messed up...will create the flag on the server and all will be happy
if(DMFCBase->GetLocalRole()==LR_SERVER){
int flagid = FlagIDs[i];
if( (flagid!=-1) && (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) ){
//Safe to create the flag
DLLComputeRoomCenter(&home_pos,&dRooms[home]);
objnum = DLLObjCreate(OBJ_POWERUP,flagid,home,&home_pos,NULL,OBJECT_HANDLE_NONE);
DLLMultiSendObject(&dObjects[objnum],0,true);
}
}
}else{
if( (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) ){
DLLComputeRoomCenter(&home_pos,&dRooms[home]);
DLLObjSetPos(&dObjects[objnum],&home_pos,home,NULL,false);
}
}
}
}
}
DMFCBase->OnInterval();
First_game_frame = true;
}
//OnKeypress
// What it needs to do:
// 1) Test for F7 keypress
void OnKeypress(int key)
{
dllinfo *Data;
Data = DMFCBase->GetDLLInfoCallData();
switch(key){
case K_F7:
DisplayScoreScreen = !DisplayScoreScreen;
DMFCBase->EnableOnScreenMenu(false);
dstat->Enable(DisplayScoreScreen);
break;
case K_PAGEDOWN:
if(DisplayScoreScreen){
dstat->ScrollDown();
Data->iRet = 1;
}
break;
case K_PAGEUP:
if(DisplayScoreScreen){
dstat->ScrollUp();
Data->iRet = 1;
}
break;
case K_F6:
DisplayScoreScreen = false;
dstat->Enable(false);
break;
case K_ESC:
if(DisplayScoreScreen){
dstat->Enable(false);
DisplayScoreScreen = false;
Data->iRet = 1;
}
break;
}
DMFCBase->OnKeypress(key);
}
//OnServerGameCreated
// What it needs to do:
// 1) Zero out all the stats for every player
// 2) Zero out team scores
// 3) Display welcome message to server
void OnServerGameCreated(void)
{
DMFCBase->OnServerGameCreated();
tPlayerStat *stat;
player_record *pr;
int i;
for(i=0;i<MAX_PLAYER_RECORDS;i++)
{
pr = DMFCBase->GetPlayerRecord(i);
if(pr){
stat = (tPlayerStat *)pr->user_info;
if(stat){
stat->Score[DSTAT_LEVEL] = 0;
stat->Score[DSTAT_OVERALL] = 0;
}
}
}
for(i=0;i<CTFNumOfTeams;i++){
TeamScores[i] = 0;
OverallTeamScores[i] = 0;
}
}
//OnClientLevelStart
// What it needs to do:
// 1) Zero out level scores for everyone
// 2) Reset colored balls
// 3) Zero out team scores
// 4) Set number of teams
// 5) If we are the server, create the flags and send them to the clients
void OnClientLevelStart(void)
{
int i;
DMFCBase->OnClientLevelStart();
First_game_frame = false;
for(i=0;i<MAX_PLAYER_RECORDS;i++){
SortedPlayers[i] = -1;
}
for(i=0;i<DLLMAX_TEAMS;i++){
SortedTeams[i] = -1;
Flag_timeout_timers[i] = 0;
ChildFlags[i] = OBJECT_HANDLE_NONE;
}
Someone_has_hattrick = false;
tPlayerStat *stat;
player_record *pr;
for(i=0;i<MAX_PLAYER_RECORDS;i++)
{
pr = DMFCBase->GetPlayerRecord(i);
if(pr){
stat = (tPlayerStat *)pr->user_info;
if(stat){
stat->Score[DSTAT_LEVEL] = 0;
}
}
}
for(i=0;i<DLLMAX_PLAYERS;i++)
{
SetColoredBalls(i,true);
}
//fill in the goal room #'s
for(i=0;i<DLLMAX_TEAMS;i++){
GoalRooms[i] = DLLGetGoalRoomForTeam(i);
FlagAtHome[i] = true;
HasFlag[i] = -1;
TeamScores[i] = 0;
}
DMFCBase->SetNumberOfTeams(CTFNumOfTeams);
DLLMultiPaintGoalRooms(NULL);
//Bash the inventory since it doesn't get reset for the server
DLLInvReset(DMFCBase->GetPlayerNum(),true);
//Display our welcome message
display_my_welcome = true;
//If we are the server create the flags
if(DMFCBase->GetLocalRole()!=LR_SERVER){
//Get the game state
DMFCBase->RequestGameState();
return;
}
vector vpos;
int objnum;
int flagid,goalroom;
for(i=0;i<CTFNumOfTeams;i++){
flagid = FlagIDs[i];
goalroom = GoalRooms[i];
if( (flagid!=-1) && (goalroom>=0) && (goalroom<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(goalroom)) && (dRooms[goalroom].used) ){
//Safe to create the flag
DLLmprintf((0,"Creating %s Flag\n",DMFCBase->GetTeamString(i)));
DLLComputeRoomCenter(&vpos,&dRooms[goalroom]);
objnum = DLLObjCreate(OBJ_POWERUP,flagid,goalroom,&vpos,NULL,OBJECT_HANDLE_NONE);
DLLMultiSendObject(&dObjects[objnum],0,true);
}
}
}
//OnServerCollide
// What it needs to do:
// 1) See if the player has collided with a flag, if so pass to clients
void OnServerCollide(object *me_obj,object *it_obj)
{
//see if we need to send this event to the clients
//only if me==player it==flag powerup
if(!me_obj || !it_obj)
return;
if( (me_obj->type==OBJ_PLAYER||me_obj->type==OBJ_GHOST) && (it_obj->type==OBJ_POWERUP) )
if( (it_obj->id==FlagIDs[RED_TEAM]) ||
(it_obj->id==FlagIDs[BLUE_TEAM]) ||
(it_obj->id==FlagIDs[GREEN_TEAM]) ||
(it_obj->id==FlagIDs[YELLOW_TEAM]) ){
//Start a packet for the collide
ubyte data[MAX_GAME_DATA_SIZE];
int count = 0;
DMFCBase->StartPacket(data,SPID_COLLIDE,&count);
int start = count;
//we need me, it and me roomnum
MultiAddUshort((me_obj-dObjects),data,&count);
MultiAddUshort((it_obj-dObjects),data,&count);
MultiAddInt(it_obj->roomnum,data,&count);
DMFCBase->SendPacket(data,count,SP_ALL);
OnClientCollide(&data[start]);
}
}
//OnClientCollide
// What it needs to do:
// 1) See if Player collided with own flag
// 1.1) Check to see if in home goal
// 1.1.1) Reset colored balls
// 1.1.2) Remove all the flags from the inventory, send them back to their home goal
// 1.1.3) Add scores
// 1.1.4) Print out score
// 1.1.5) Do Kill Goal check
// 1,2) Move home flag to center of base
// 2) else collide with another team's flag
// 2.1) Add flag to inventory
// 2.2) Set rotating balls
// 2.3) Print out message
void OnClientCollide(ubyte *data)
{
object *me_obj,*it_obj;
int me_roomnum;
int count = 0;
int me_serv_objnum,it_serv_objnum,me_client_objnum,it_client_objnum;
me_serv_objnum = MultiGetUshort(data,&count);
it_serv_objnum = MultiGetUshort(data,&count);
me_roomnum = MultiGetInt(data,&count);
me_client_objnum = DMFCBase->ConvertServerToLocalObjnum(me_serv_objnum);
it_client_objnum = DMFCBase->ConvertServerToLocalObjnum(it_serv_objnum);
if(me_client_objnum==-1 || it_client_objnum==-1)
{
Int3();
return;
}
me_obj = &dObjects[me_client_objnum];
it_obj = &dObjects[it_client_objnum];
// now process the collide
int fteam=-1; //flags team
int pnum = me_obj->id; //player number
int pteam=DMFCBase->GetPlayerTeam(pnum); //player's team
int i;
bool play_return_home_msg = false;
for(i=0;i<CTFNumOfTeams;i++){
if(it_obj->id==FlagIDs[i])
fteam = i;
}
if(fteam==-1){
return; //something wrong, as none of the IDs are one of the flag's IDs
}
// Reset the timer for this flag since it's guaranteed someone owns it now
// or it has been sent home.
Flag_timeout_timers[fteam] = 0;
//Did player collide with his own team's flag?
if(fteam==pteam)
{
short flag_count = 0;
vector fpos;
int groom;
int flagcount;
int flagmask;
bool captured_flags[DLLMAX_TEAMS];
for(i=0;i<DLLMAX_TEAMS;i++){
captured_flags[i] = false;
}
//Check what room the player is in, if they're not in home base, then it's no score
if(GoalRooms[fteam]==me_roomnum){
//ok so he is in his home base, now find out how many flags he has, score for each and return them home
flagcount = GetFlagCountForPlayer(pnum);
flagmask = GetFlagMaskForPlayer(pnum);
//now loop through the flags, check to see if we have them and handle it
for(i=0;i<CTFNumOfTeams;i++){
if(flagmask&0x01){
//we have the flag we're currently working on
captured_flags[i] = true;
groom = GoalRooms[i];
if( (groom>=0) && (groom<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(groom)) && (dRooms[groom].used)){
//the room is valid, so move it back to home
LoseFlagForPlayer(pnum,i);
if(DMFCBase->GetLocalRole()==LR_SERVER){
//if we are the server create the object and send it on it's way to the player's
DLLComputeRoomCenter(&fpos,&dRooms[groom]);
int objnum = DLLObjCreate(OBJ_POWERUP,FlagIDs[i],groom,&fpos,NULL,OBJECT_HANDLE_NONE);
DLLMultiSendObject(&dObjects[objnum],1,true);
}//end server create
}//end room ok
//set it's at home flag
FlagAtHome[i] = true;
}//end flag check
//rotate the flagmask over one to check for the next flag
flagmask = flagmask >> 1;
}//end for loop
//remove any rotating balls
SetColoredBalls(pnum,true);
//Now handle any scoring
int score = 0;
switch(flagcount)
{
case 0:
break;
case 1:
score = 1;
break;
case 2:
score = 3;
break;
case 3:
score = 9;
break;
}
//print out the message and add to the scores
if(score)
{
tPlayerStat *stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(pnum);
if(stat){
stat->Score[DSTAT_LEVEL] +=score;
stat->Score[DSTAT_OVERALL] +=score;
if(stat->Score[DSTAT_LEVEL]==3){
if(Someone_has_hattrick){
DLLAddHUDMessage(TXT_HATTRICK,dPlayers[pnum].callsign);
if(snd_hattrick_reg!=-1)
DLLPlay2dSound(snd_hattrick_reg,MAX_GAME_VOLUME/2);
}else{
DLLAddHUDMessage(TXT_HATTRICKFIRST,dPlayers[pnum].callsign);
if(snd_hattrick_first!=-1)
DLLPlay2dSound(snd_hattrick_first,MAX_GAME_VOLUME/2);
Someone_has_hattrick = true;
}
}
}
TeamScores[pteam]+=score;
OverallTeamScores[pteam]+=score;
int newscore=TeamScores[pteam];
switch(flagcount){
case 1:
{
char t[20];
t[0] = '\0';
for(int c=0;c<DLLMAX_TEAMS;c++){
if(captured_flags[c]){
strcpy(t,DMFCBase->GetTeamString(c));
break;
}
}
DLLAddHUDMessage(TXT_CAPTURE,dPlayers[pnum].callsign,DMFCBase->GetTeamString(pteam),t);
}break;
case 2:
{
char t[2][20];
t[0][0] = '\0';
t[1][0] = '\0';
int index = 0;
for(int c=0;c<DLLMAX_TEAMS;c++){
if(captured_flags[c]){
strcpy(t[index],DMFCBase->GetTeamString(c));
index++;
if(index==2)
break;
}
}
DLLAddHUDMessage(TXT_CAPTURETWO,dPlayers[pnum].callsign,
DMFCBase->GetTeamString(pteam),t[0],t[1]);
}break;
case 3:
{
char t[3][20];
t[0][0] = '\0';
t[1][0] = '\0';
t[2][0] = '\0';
int index = 0;
for(int c=0;c<DLLMAX_TEAMS;c++){
if(captured_flags[c]){
strcpy(t[index],DMFCBase->GetTeamString(c));
index++;
if(index==3)
break;
}
}
DLLAddHUDMessage(TXT_CAPTURETHREE,dPlayers[pnum].callsign,
DMFCBase->GetTeamString(pteam),t[0],t[1],t[2]);
}
}
if(dPlayers[DMFCBase->GetPlayerNum()].team==pteam){
if(snd_score_ownteam!=-1)
DLLPlay2dSound(snd_score_ownteam,MAX_GAME_VOLUME/2);
}else{
if(snd_score_otherteam!=-1)
DLLPlay2dSound(snd_score_otherteam,MAX_GAME_VOLUME/2);
}
//Set a Timer to display
if(WhoJustScoredTimer!=-1)
DMFCBase->KillTimer(WhoJustScoredTimer);
WhoJustScoredTimer = DMFCBase->SetTimerInterval(OnTimerScore,0.5f,5.0f,OnTimerScoreKill);
WhoJustScored = pteam;
}
//do the killgoal check
int killgoal;
if(DMFCBase->GetScoreLimit(&killgoal)){
if(TeamScores[pteam]>=killgoal){
//That's all she wrote for this level
DLLmprintf((0,"OnClientCollide:Kill Goal Reached!\n"));
DMFCBase->EndLevel();
}
}
}else{
DoFlagReturnedHome(pteam);
if(DMFCBase->CheckPlayerNum(pnum)){
char parm1[20],parm2[20];
strcpy(parm1,DMFCBase->GetTeamString(pteam));
strcpy(parm2,DMFCBase->GetTeamString(fteam));
DLLAddHUDMessage(TXT_RETURN,dPlayers[pnum].callsign,parm1,parm2);
}
}
//since the player collided with his own team's flag we need to move the flag back home
FlagAtHome[pteam] = true;
HasFlag[pteam] = -1;
vector home_pos;
int home = GoalRooms[pteam];
if( (home>=0) && (home<=DMFCBase->GetHighestRoomIndex()) && (!ROOMNUM_OUTSIDE(home)) && (dRooms[home].used) )
{
DLLComputeRoomCenter(&home_pos,&dRooms[home]);
DLLObjSetPos(it_obj,&home_pos,home,NULL,false);
}
}//end Player Collides with own team's flag
else{
//The Player collide with another team's flag
FlagAtHome[fteam] = false;
//add the flag to the player
GivePlayerFlag(pnum,fteam);
if(dPlayers[DMFCBase->GetPlayerNum()].team==dPlayers[pnum].team){
if(snd_pickedup_ownteam!=-1)
DLLPlay2dSound(snd_pickedup_ownteam,MAX_GAME_VOLUME/2);
}else{
if(snd_pickedup_otherteam!=-1)
DLLPlay2dSound(snd_pickedup_otherteam,MAX_GAME_VOLUME/2);
}
//Set a Timer to display
if(WhoJustFlaggedTimer!=-1)
DMFCBase->KillTimer(WhoJustFlaggedTimer);
WhoJustFlaggedTimer = DMFCBase->SetTimerInterval(OnTimer,0.5f,5.0f,OnTimerKill);
WhoJustFlagged = fteam;
char parm1[20],parm2[20];
strcpy(parm1,DMFCBase->GetTeamString(pteam));
strcpy(parm2,DMFCBase->GetTeamString(fteam));
if(dObjects[dPlayers[pnum].objnum].roomnum==GoalRooms[fteam]){
DLLAddHUDMessage(TXT_PICKUPFLAG,dPlayers[pnum].callsign,parm1,parm2);
}else{
DLLAddHUDMessage(TXT_PICKUPFLAGSPEW,dPlayers[pnum].callsign,parm1,parm2);
}
//remove the flag from the world
if(DMFCBase->GetLocalRole()==LR_SERVER){
//tell the clients to remove this flag
DLLSetObjectDeadFlag(it_obj,true,false);
}
}
}
//OnGameStateRequest
// 1) Send game state to new player
void OnGameStateRequest(int player_num)
{
SendGameState(player_num);
DMFCBase->OnGameStateRequest(player_num);
}
void OnPlayerConnect(int player_num)
{
DMFCBase->OnPlayerConnect(player_num);
tPlayerStat *stat;
stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(player_num);
if(stat){
stat->Score[DSTAT_LEVEL] = 0;
stat->Score[DSTAT_OVERALL] = 0;
}
if(player_num!=DMFCBase->GetPlayerNum())
{
//announce the arrival of the player
DisplayWelcomeMessage(player_num);
}
}
void OnClientLevelEnd(void)
{
DMFCBase->OnClientLevelEnd();
}
void OnPlayerChangeTeam(int player_num,int newteam,bool announce,bool spew_onrespawn)
{
DMFCBase->OnPlayerChangeTeam(player_num,newteam,announce,spew_onrespawn);
player_record *pr = DMFCBase->GetPlayerRecordByPnum(player_num);
}
//OnClientPlayerKilled
// 1) If suicide add to suicides
// 2) Else add to killer's kill, add to victim's deaths
// 3) Reset colored balls
// 4) If killed in a flag's home goal than put it in the center of the room
void OnClientPlayerKilled(object *killer_obj,int victim_pnum)
{
int kpnum = -1;
if(killer_obj){
if((killer_obj->type==OBJ_PLAYER)||(killer_obj->type==OBJ_GHOST)||(killer_obj->type==OBJ_OBSERVER))
kpnum = killer_obj->id;
else if(killer_obj->type==OBJ_ROBOT || killer_obj->type == OBJ_BUILDING){
//countermeasure kill
kpnum = DMFCBase->GetCounterMeasureOwner(killer_obj);
}else{
kpnum = -1;
}
}
HandlePlayerSpew(victim_pnum);
DMFCBase->OnClientPlayerKilled(killer_obj,victim_pnum);
}
void OnClientPlayerDisconnect(int player_num)
{
HandlePlayerSpew(player_num);
DMFCBase->OnClientPlayerDisconnect(player_num);
}
void OnPlayerEntersObserver(int pnum,object *piggy)
{
HandlePlayerSpew(pnum);
DMFCBase->OnPlayerEntersObserver(pnum,piggy);
}
bool OnCanChangeTeam(int pnum,int newteam)
{
if(!DMFCBase->OnCanChangeTeam(pnum,newteam))
return false;
if(GetFlagCountForPlayer(pnum)){
DMFCBase->AnnounceTeamChangeDeny(pnum);
return false;
}
return true;
}
bool compare_slots(int a,int b)
{
int ascore,bscore;
player_record *apr,*bpr;
tPlayerStat *astat,*bstat;
apr = DMFCBase->GetPlayerRecord(a);
bpr = DMFCBase->GetPlayerRecord(b);
if( !apr )
return true;
if( !bpr )
return false;
if( apr->state==STATE_EMPTY )
return true;
if( bpr->state==STATE_EMPTY )
return false;
astat = (tPlayerStat *)apr->user_info;
bstat = (tPlayerStat *)bpr->user_info;
if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){
//both players were in the game
ascore = (astat)?astat->Score[DSTAT_LEVEL]:0;
bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0;
return (ascore<bscore);
}
if( (apr->state==STATE_INGAME) && (bpr->state==STATE_DISCONNECTED) ){
//apr gets priority since he was in the game on exit
return false;
}
if( (apr->state==STATE_DISCONNECTED) && (bpr->state==STATE_INGAME) ){
//bpr gets priority since he was in the game on exit
return true;
}
//if we got here then both players were disconnected
ascore = (astat)?astat->Score[DSTAT_LEVEL]:0;
bscore = (bstat)?bstat->Score[DSTAT_LEVEL]:0;
return (ascore<bscore);
}
void OnPLRInit(void)
{
int tempsort[MAX_PLAYER_RECORDS];
int i,t,j;
for(i=0;i<MAX_PLAYER_RECORDS;i++){
tempsort[i] = i;
}
for(i=1;i<=MAX_PLAYER_RECORDS-1;i++){
t=tempsort[i];
// Shift elements down until
// insertion point found.
for(j=i-1;j>=0 && compare_slots(tempsort[j],t); j--){
tempsort[j+1] = tempsort[j];
}
// insert
tempsort[j+1] = t;
}
//copy the array over
memcpy(SortedPlayers,tempsort,MAX_PLAYER_RECORDS*sizeof(int));
//Now fill in the final structure of sorted names
int TeamCount[DLLMAX_TEAMS];
int team;
for(i=0;i<DLLMAX_TEAMS;i++)
TeamCount[i] = 0;
for(i=0;i<MAX_PLAYER_RECORDS;i++){
int slot = SortedPlayers[i];
player_record *pr = DMFCBase->GetPlayerRecord(slot);
if(pr && pr->state!=STATE_EMPTY){
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue; //skip dedicated server
team = (pr->state==STATE_INGAME)?dPlayers[pr->pnum].team:pr->team;
if(team>=CTFNumOfTeams)
team = 0;
SortedPLRPlayers[team][TeamCount[team]] = slot;
TeamCount[team]++;
}
}
for(i=0;i<DLLMAX_TEAMS;i++){
if(TeamCount[i]<MAX_PLAYER_RECORDS)
SortedPLRPlayers[i][TeamCount[i]] = -1;
}
DMFCBase->OnPLRInit();
}
void OnPLRInterval(void)
{
DMFCBase->OnPLRInterval();
int TeamCol = 35;
int NameCol = 180;
int PointsCol = 320;
int KillsCol = 370;
int DeathsCol = 410;
int SuicidesCol = 450;
int y = 40;
int slot;
bool had_members;
player_record *pr;
tPlayerStat *stat;
DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]);
int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]) + 1;
//print out header
DLLgrtext_SetColor(GR_RGB(255,255,150));
DLLgrtext_Printf(PointsCol,y,TXT_POINTS);
DLLgrtext_Printf(NameCol,y,TXT_PILOT);
DLLgrtext_Printf(KillsCol,y,TXT_KILLS_SHORT);
DLLgrtext_Printf(DeathsCol,y,TXT_DEATHS_SHORT);
DLLgrtext_Printf(SuicidesCol,y,TXT_SUICIDES_SHORT);
y+=height;
bool doing_connected = true;
do_disconnected_folk:
for(int team=0;team<CTFNumOfTeams;team++){
//process this team
bool show_team_label;
show_team_label = false;
if(!doing_connected)
{
int temp_idx;
temp_idx = 0;
while(SortedPLRPlayers[team][temp_idx]!=-1)
{
int pnum=DMFCBase->WasPlayerInGameAtLevelEnd(SortedPLRPlayers[team][temp_idx]);
if(pnum==-1)
{
show_team_label = true;
break;
}
temp_idx++;
}
}else
{
show_team_label = true;
}
if(show_team_label){
//is there anyone on this team?
DLLgrtext_SetColor(DMFCBase->GetTeamColor(team));
DLLgrtext_Printf(TeamCol,y,TXT_TEAMSCOREFORM,DMFCBase->GetTeamString(team),TeamScores[team],OverallTeamScores[team]);
}
had_members = false;
for(int index=0;index<MAX_PLAYER_RECORDS;index++){
//get the player num
slot = SortedPLRPlayers[team][index];
if(slot==-1)//we are done with this team
break;
pr = DMFCBase->GetPlayerRecord(slot);
if(!pr || pr->state==STATE_EMPTY)
continue;
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue;
if( (doing_connected && DMFCBase->WasPlayerInGameAtLevelEnd(slot)==-1) ||
(!doing_connected && DMFCBase->WasPlayerInGameAtLevelEnd(slot)!=-1) )
continue;//we're not handling them right now
stat = (tPlayerStat *)pr->user_info;
int pnum=DMFCBase->WasPlayerInGameAtLevelEnd(slot);
if(pnum!=-1)
{
DLLgrtext_SetColor(DMFCBase->GetTeamColor(team));
}else
{
DLLgrtext_SetColor(GR_RGB(128,128,128));
}
//valid player
char temp[120];
sprintf(temp,"%s",pr->callsign);
DMFCBase->ClipString(PointsCol - NameCol - 10,temp,true);
DLLgrtext_Printf(NameCol,y,"%s",temp);
DLLgrtext_Printf(PointsCol,y,"%d",(stat)?stat->Score[DSTAT_LEVEL]:0);
DLLgrtext_Printf(KillsCol,y,"%d",pr->dstats.kills[DSTAT_LEVEL]);
DLLgrtext_Printf(DeathsCol,y,"%d",pr->dstats.deaths[DSTAT_LEVEL]);
DLLgrtext_Printf(SuicidesCol,y,"%d",pr->dstats.suicides[DSTAT_LEVEL]);
y+=height;
if(y>=440)
goto quick_exit;
had_members = true;
}//end for
if(!had_members)
{
//skip a line
y+=height;
if(y>=440)
goto quick_exit;
}
//on to the next team
}//end for
if(doing_connected)
{
doing_connected = false;
goto do_disconnected_folk;
}
quick_exit:;
}
void SaveStatsToFile(char *filename)
{
CFILE *file;
DLLOpenCFILE(&file,filename,"wt");
if(!file){
DLLmprintf((0,"Unable to open output file\n"));
return;
}
//write out game stats
#define BUFSIZE 150
char buffer[BUFSIZE];
char tempbuffer[25];
int sortedslots[MAX_PLAYER_RECORDS];
player_record *pr,*dpr;
tPInfoStat stat;
tPlayerStat *ps;
int count,length,p;
int team;
//sort the stats
DMFCBase->GetSortedPlayerSlots(sortedslots,MAX_PLAYER_RECORDS);
int i,t,j;
for(i=0;i<MAX_PLAYER_RECORDS;i++){
sortedslots[i] = i;
}
for(i=1;i<=MAX_PLAYER_RECORDS-1;i++){
t=sortedslots[i];
// Shift elements down until
// insertion point found.
for(j=i-1;j>=0 && compare_slots(sortedslots[j],t); j--){
sortedslots[j+1] = sortedslots[j];
}
// insert
sortedslots[j+1] = t;
}
count = 1;
sprintf(buffer,TXT_STAT_HEADING,(DMFCBase->GetNetgameInfo())->name,(DMFCBase->GetCurrentMission())->cur_level);
DLLcf_WriteString(file,buffer);
for(i=0;i<DMFCBase->GetNumTeams();i++){
int team = SortedTeams[i];
sprintf(buffer,"%s: %d[%d]",DMFCBase->GetTeamString(team),TeamScores[team],OverallTeamScores[team]);
DLLcf_WriteString(file,buffer);
}
strcpy(buffer,"\r\n");
DLLcf_WriteString(file,buffer);
//Write team members
DLLcf_WriteString(file,""); //blank line
for(t=0;t<DMFCBase->GetNumTeams();t++){
int team_i = SortedTeams[t];
sprintf(buffer,"%s:",DMFCBase->GetTeamString(team_i));
DLLcf_WriteString(file,buffer);
for(p=0;p<MAX_PLAYER_RECORDS;p++){
pr = DMFCBase->GetPlayerRecord(sortedslots[p]);
if( pr && pr->state!=STATE_EMPTY) {
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue;//skip dedicated server
if (pr->team == team_i) { //Check if current team
sprintf(buffer," %s",pr->callsign);
DLLcf_WriteString(file,buffer);
}
}
}
}
DLLcf_WriteString(file,""); //blank line
sprintf(buffer,TXT_STAT_HEADINGA);
DLLcf_WriteString(file,buffer);
sprintf(buffer,"--------------------------------------------------------------------------------------------------");
DLLcf_WriteString(file,buffer);
for(int pslot=0;pslot<MAX_PLAYER_RECORDS;pslot++){
int real_slot = sortedslots[pslot];
pr = DMFCBase->GetPlayerRecord(real_slot);
if( pr && pr->state!=STATE_EMPTY){
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue;//skip dedicated server
//we have a valid play
ps = (tPlayerStat *)pr->user_info;
memset(buffer,' ',BUFSIZE);
team = pr->team;
sprintf(tempbuffer,"%d)",count);
memcpy(&buffer[0],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%.6s",DMFCBase->GetTeamString(team));
memcpy(&buffer[5],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%s%s",(pr->state==STATE_INGAME)?"":"*",pr->callsign);
memcpy(&buffer[12],tempbuffer,strlen(tempbuffer));
if(ps){
sprintf(tempbuffer,"%d[%d]",ps->Score[DSTAT_LEVEL],ps->Score[DSTAT_OVERALL]);
memcpy(&buffer[41],tempbuffer,strlen(tempbuffer));
}
sprintf(tempbuffer,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]);
memcpy(&buffer[53],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]);
memcpy(&buffer[65],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]);
memcpy(&buffer[76],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%s",DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(real_slot)));
memcpy(&buffer[86],tempbuffer,strlen(tempbuffer));
int pos;
pos = 86 + strlen(tempbuffer) + 1;
if(pos<BUFSIZE)
buffer[pos] = '\0';
buffer[BUFSIZE-1] = '\0';
DLLcf_WriteString(file,buffer);
count++;
}
}//end for
DLLcf_WriteString(file,TXT_STAT_HEADINGB);
count = 1;
for(int s=0;s<MAX_PLAYER_RECORDS;s++){
p = sortedslots[s];
pr = DMFCBase->GetPlayerRecord(p);
if( pr && pr->state!=STATE_EMPTY) {
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue;//skip dedicated server
//Write out header
sprintf(buffer,"%d) %s%s",count,(pr->state==STATE_INGAME)?"":"*",pr->callsign);
DLLcf_WriteString(file,buffer);
length = strlen(buffer);
memset(buffer,'=',length);
buffer[length] = '\0';
DLLcf_WriteString(file,buffer);
//time in game
sprintf(buffer,TXT_STAT_TIG,DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(p)));
DLLcf_WriteString(file,buffer);
if(DMFCBase->FindPInfoStatFirst(p,&stat)){
sprintf(buffer,TXT_STAT_HEADINGC);
DLLcf_WriteString(file,buffer);
if(stat.slot!=p){
memset(buffer,' ',BUFSIZE);
dpr = DMFCBase->GetPlayerRecord(stat.slot);
int pos;
if(dpr)
{
sprintf(tempbuffer,"%s",dpr->callsign);
memcpy(buffer,tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d",stat.kills);
memcpy(&buffer[30],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d",stat.deaths);
memcpy(&buffer[40],tempbuffer,strlen(tempbuffer));
pos = 40 + strlen(tempbuffer) + 1;
if(pos<BUFSIZE)
buffer[pos] = '\0';
buffer[BUFSIZE-1] = '\0';
DLLcf_WriteString(file,buffer);
}
}
while(DMFCBase->FindPInfoStatNext(&stat)){
if(stat.slot!=p){
int pos;
memset(buffer,' ',BUFSIZE);
dpr = DMFCBase->GetPlayerRecord(stat.slot);
if(dpr)
{
sprintf(tempbuffer,"%s",dpr->callsign);
memcpy(buffer,tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d",stat.kills);
memcpy(&buffer[30],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d",stat.deaths);
memcpy(&buffer[40],tempbuffer,strlen(tempbuffer));
pos = 40 + strlen(tempbuffer) + 1;
if(pos<BUFSIZE)
buffer[pos] = '\0';
buffer[BUFSIZE-1] = '\0';
DLLcf_WriteString(file,buffer);
}
}
}
}
DMFCBase->FindPInfoStatClose();
DLLcf_WriteString(file,""); //skip a line
count++;
}
}
//done writing stats
DLLcfclose(file);
DLLAddHUDMessage(TXT_STAT_SAVED);
}
#define ROOTFILENAME "CTF"
void OnSaveStatsToFile(void)
{
char filename[256];
DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false);
SaveStatsToFile(filename);
}
void OnLevelEndSaveStatsToFile(void)
{
char filename[256];
DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,true);
SaveStatsToFile(filename);
}
void OnDisconnectSaveStatsToFile(void)
{
char filename[256];
DMFCBase->GenerateStatFilename(filename,ROOTFILENAME,false);
SaveStatsToFile(filename);
}
void OnPrintScores(int level)
{
char buffer[256];
char name[70];
int t,i;
int pos[6];
int len[6];
for(i=0;i<CTFNumOfTeams;i++)
{
sprintf(buffer,"%s:%d\n",DMFCBase->GetTeamString(i),TeamScores[i]);
DPrintf(buffer);
}
memset(buffer,' ',256);
pos[0] = 0; t = len[0] = 30; //give ample room for pilot name
pos[1] = pos[0] + t + 1; t = len[1] = strlen(TXT_POINTS);
pos[2] = pos[1] + t + 1; t = len[2] = strlen(TXT_KILLS_SHORT);
pos[3] = pos[2] + t + 1; t = len[3] = strlen(TXT_DEATHS_SHORT);
pos[4] = pos[3] + t + 1; t = len[4] = strlen(TXT_SUICIDES_SHORT);
pos[5] = pos[4] + t + 1; t = len[5] = strlen(TXT_PING);
memcpy(&buffer[pos[0]],TXT_PILOT,strlen(TXT_PILOT));
memcpy(&buffer[pos[1]],TXT_POINTS,len[1]);
memcpy(&buffer[pos[2]],TXT_KILLS_SHORT,len[2]);
memcpy(&buffer[pos[3]],TXT_DEATHS_SHORT,len[3]);
memcpy(&buffer[pos[4]],TXT_SUICIDES_SHORT,len[4]);
memcpy(&buffer[pos[5]],TXT_PING,len[5]);
buffer[pos[5]+len[5]+1] = '\n';
buffer[pos[5]+len[5]+2] = '\0';
DPrintf(buffer);
int slot;
player_record *pr;
int pcount;
if(level<0 || level>=MAX_PLAYER_RECORDS)
pcount = MAX_PLAYER_RECORDS;
else
pcount = level;
int sortedplayers[MAX_PLAYER_RECORDS];
DMFCBase->GetSortedPlayerSlots(sortedplayers,MAX_PLAYER_RECORDS);
for(i=0;i<pcount;i++){
slot = sortedplayers[i];
pr = DMFCBase->GetPlayerRecord(slot);
if((pr)&&(pr->state!=STATE_EMPTY)){
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue; //skip dedicated server
sprintf(name,"%s%s: %.8s",(pr->state==STATE_DISCONNECTED)?"*":"",pr->callsign,DMFCBase->GetTeamString(pr->team));
name[29] = '\0';
tPlayerStat *stat;
stat = (tPlayerStat *)pr->user_info;
memset(buffer,' ',256);
t = strlen(name); memcpy(&buffer[pos[0]],name,(t<len[0])?t:len[0]);
sprintf(name,"%d",(stat)?stat->Score[DSTAT_LEVEL]:0);
t = strlen(name); memcpy(&buffer[pos[1]],name,(t<len[1])?t:len[1]);
sprintf(name,"%d",pr->dstats.kills[DSTAT_LEVEL]);
t = strlen(name); memcpy(&buffer[pos[2]],name,(t<len[2])?t:len[2]);
sprintf(name,"%d",pr->dstats.deaths[DSTAT_LEVEL]);
t = strlen(name); memcpy(&buffer[pos[3]],name,(t<len[3])?t:len[3]);
sprintf(name,"%d",pr->dstats.suicides[DSTAT_LEVEL]);
t = strlen(name); memcpy(&buffer[pos[4]],name,(t<len[4])?t:len[4]);
if(pr->state==STATE_INGAME)
sprintf(name,"%.0f",dNetPlayers[pr->pnum].ping_time*1000.0f);
else
strcpy(name,"---");
t = strlen(name); memcpy(&buffer[pos[5]],name,(t<len[5])?t:len[5]);
buffer[pos[5]+len[5]+1] = '\n';
buffer[pos[5]+len[5]+2] = '\0';
DPrintf(buffer);
}
}
}
// DMFCApp::HandlePlayerSpew
//
// If a player dies, becomes an observer or disconnects, it's possible that they
// have a flag or more. This function will handle setting certain variables, timer
// and other various settings related to a flag spew
void HandlePlayerSpew(int pnum)
{
//Handle dropping the flag if the player had one
ubyte flaginfo = GetFlagCountForPlayer(pnum);
ubyte flagmask = GetFlagMaskForPlayer(pnum);
if(flaginfo==0){
return;
}
//Ok the player has flags, lets make sure everything that needs to happen, happens
// 1) We need to clear flags and colored ball indicator
// 2) If they are dying in a homebase of one of the flags they have, than set the
// flag home.
// 3) Start a timer if the flag spews outside of a base
//If we got here than the player has a flag
vector fpos; //used to hold the center pos of the room
bool play_lose = false; //set true if the player lost a flag (ummm, this is guaranteed)
//loop through all the possible flags and check to see what they had
for(int color=0;color<DLLMAX_TEAMS;color++){
//check if the ID exists, and if this player had that color flag
if( (FlagIDs[color]!=-1) && (flagmask&0x01) ){
play_lose = true;
//now check to see if they are in the flag's home goal
if(GoalRooms[color]==dObjects[pnum].roomnum){
//we are!
//so remove the flag from the inventory (so it don't get spewed)
DLLInvRemove(pnum,OBJ_POWERUP,FlagIDs[color]);
DoFlagReturnedHome(color);
//set the at home flag
FlagAtHome[color] = true;
if(DMFCBase->GetLocalRole()==LR_SERVER){
//if we are the server put the flag back in the center of the goal and tell all the players to do the same
DLLComputeRoomCenter(&fpos,&dRooms[GoalRooms[color]]);
int objnum = DLLObjCreate(OBJ_POWERUP,FlagIDs[color],GoalRooms[color],&fpos,NULL,OBJECT_HANDLE_NONE);
DLLMultiSendObject(&dObjects[objnum],1,true);
}
}else{
//ok the flag is spewing out into the great beyond
//Start the timer!
Flag_timeout_timers[color] = DMFCBase->GetGametime() + FLAG_TIMEOUT_VALUE;
}
//player has the flag
LoseFlagForPlayer(pnum,color,false);
}
//rotate flagmask so it is pointing at the next bit
flagmask = (flagmask>>1);
}
// Reset colored balls
SetColoredBalls(pnum,true);
if(play_lose)
DoLoseFlag(DMFCBase->GetPlayerTeam(pnum));
}
/////////////////////////////////////////////////////////////
//CTF Functions
void DisplayHUDScores(struct tHUDItem *hitem)
{
if(display_my_welcome)
{
//announce our arrival to ourselves (as server)
DisplayWelcomeMessage(DMFCBase->GetPlayerNum());
display_my_welcome = false;
}
if(!First_game_frame || DisplayScoreScreen) //interval hasn't been called yet or we are display the stats
return;
ubyte alpha = DMFCBase->ConvertHUDAlpha((ubyte)((DisplayScoreScreen)?128:255));
int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[HUD_FONT_INDEX]) + 3;
int y = 170;
int x = 540;
int team = DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum());
int myteam = team;
int i;
//Flag bitmaps
//if (HasFlag[team]!=-1) then draw a dimmed flag with the team flag of who has it on top
//if (HasFlag[team]==-1) && (FlagAtHome[team]==true) then draw normal flag
//if (HasFlag[team]==-1) && (FlagAtHome[team]==false) then draw dimmed flag
int cx,cy;
for(i=0;i<CTFNumOfTeams;i++){
//first determine the x and y
switch(i){
case RED_TEAM:
cx = x;
cy = height + 5;
break;
case BLUE_TEAM:
cx = x + 33;
cy = height + 5;
break;
case GREEN_TEAM:
cx = x;
cy = 2*height + 41;
break;
case YELLOW_TEAM:
cx = x + 33;
cy = 2*height + 41;
break;
}
if( (WhoJustFlagged!=i) || (DisplayFlagBlink) ){
if(HasFlag[i]!=-1){
//draw dimmed flag with corner as the team who has the flag
DLLRenderHUDQuad(cx,cy,DLLbm_w(DimFlagAHBmp[i],0),DLLbm_h(DimFlagAHBmp[i],0),0,0,1,1,DimFlagAHBmp[i],alpha,0);
DLLRenderHUDQuad(cx+16,cy,16,16,0,0,1,1,FlagBmp[DMFCBase->GetPlayerTeam(HasFlag[i])],alpha,0);
}
else{
if(FlagAtHome[i]){
//draw normal flag
DLLRenderHUDQuad(cx,cy,DLLbm_w(FlagAHBmp[i],0),DLLbm_h(FlagAHBmp[i],0),0,0,1,1,FlagAHBmp[i],alpha,0);
}
else{
//draw dimmed flag
int f_height = DLLbm_h(DimFlagAHBmp[i],0);
DLLRenderHUDQuad(cx,cy,DLLbm_w(DimFlagAHBmp[i],0),f_height,0,0,1,1,DimFlagAHBmp[i],alpha,0);
//Draw timeout time
int time_left;
time_left = (int)( Flag_timeout_timers[i] - DMFCBase->GetGametime());
if(time_left>0 && Flag_timeout_timers[i]!=0){
int minutes;
minutes = time_left/60;
time_left = time_left%60;
DLLRenderHUDText(GR_GREEN,alpha,0,cx,cy+2+f_height,"%01d:%02d",minutes,time_left);
}
}
}
}
}
x = 520;
if(Highlight_bmp<=BAD_BITMAP_HANDLE){
//write the name of your team at the top of the screen since for some reason we don't a highlight bmp
DLLRenderHUDText(DMFCBase->GetTeamColor(team),alpha,0,x,y,TXT_TEAMFORM,DMFCBase->GetTeamString(team)); y+=height;
}
//determine coordinates to use here
//we'll use a virtual width of 85 pixels on a 640x480 screen
//so first determine the new width
int name_width = 85.0f * DMFCBase->GetHudAspectX();
int score_width = DLLgrtext_GetTextLineWidth("888");
int name_x = DMFCBase->GetGameWindowW() - name_width - score_width - 10;
int score_x = DMFCBase->GetGameWindowW() - score_width - 5;
y = (DMFCBase->GetGameWindowH()/2) - ((height*DMFCBase->GetNumTeams())/2);
//draw the team scores
for(team=0;team<DLLMAX_TEAMS;team++){
i = SortedTeams[team];
if(i>=CTFNumOfTeams)
continue;
if((WhoJustScored!=i) || (DisplayScoreBlink) ){
//determine the number of players on the team
int num_players;
num_players = 0;
for(int w=0;w<DLLMAX_PLAYERS;w++){
if(DMFCBase->CheckPlayerNum(w) && dPlayers[w].team==i && !DMFCBase->IsPlayerDedicatedServer(w))
num_players++;
}
if(i==myteam && Highlight_bmp>BAD_BITMAP_HANDLE){
//draw the highlite bar in the background
DLLrend_SetAlphaValue(alpha*0.50f);
DLLrend_SetZBufferState (0);
DLLrend_SetTextureType (TT_LINEAR);
DLLrend_SetLighting (LS_NONE);
DLLrend_SetAlphaType (AT_CONSTANT_TEXTURE);
DLLrend_DrawScaledBitmap(name_x-2,y-2,score_x+score_width+2,y+height-1,Highlight_bmp,0,0,1,1,1.0,BITMAP_FORMAT_1555,NULL);
DLLrend_SetZBufferState (1);
}
char team_name[MAX_TEAMNAME_LEN+5];
sprintf(team_name,"[%d]%s",num_players,DMFCBase->GetTeamString(i));
//DMFCBase->ClipString(615 - x - 10,team_name,true);
//DLLRenderHUDText(DMFCBase->GetTeamColor(i),alpha,0,x,y,team_name);
//DLLRenderHUDText(DMFCBase->GetTeamColor(i),alpha,0,615,y,"%d",TeamScores[i]);
DMFCBase->ClipString(name_width,team_name,true);
DLLgrtext_SetAlpha(alpha);
DLLgrtext_SetColor(DMFCBase->GetTeamColor(i));
DLLgrtext_Printf(name_x,y,team_name);
DLLgrtext_Printf(score_x,y,"%d",TeamScores[i]);
}
y+=height;
}
//draw the bitmap for any flags you have currently
int flagcount;
int flagmask;
flagcount = GetFlagCountForPlayer(DMFCBase->GetPlayerNum());
flagmask = GetFlagMaskForPlayer(DMFCBase->GetPlayerNum());
if(flagcount==0)
return;
y+=5;
for(i=0;i<CTFNumOfTeams;i++){
if(flagmask&0x01){
//we have the flag
DLLRenderHUDQuad(x,y,DLLbm_w(FlagBmp[i],0)/2,DLLbm_h(FlagBmp[i],0)/2,0,0,1,1,FlagBmp[i],alpha,0);
x+=DLLbm_w(FlagBmp[i],0)+3;
}
//adjust the flagmask for the next flag
flagmask = flagmask>>1;
}
}
#define compGT(a,b) (a < b)
void SortTeamScores(int *sortedindex,int *scores)
{
int t;
int i, j;
//copy scores into scoreinfo array
for(i=0;i<DLLMAX_TEAMS;i++){
sortedindex[i] = i;
}
for(i=1;i<=DLLMAX_TEAMS-1;i++){
t=sortedindex[i];
// Shift elements down until
// insertion point found.
for(j=i-1;j>=0 && compGT(scores[sortedindex[j]],scores[t]); j--){
sortedindex[j+1] = sortedindex[j];
}
// insert
sortedindex[j+1] = t;
}
}
void DisplayWelcomeMessage(int player_num)
{
char name_buffer[64];
strcpy(name_buffer,(DMFCBase->GetPlayers())[player_num].callsign);
if(player_num==DMFCBase->GetPlayerNum())
{
DLLAddHUDMessage(TXT_WELCOME,name_buffer);
}
else
{
DLLAddHUDMessage(TXT_JOINED,name_buffer);
}
}
//adds a colored ball to a player (without removing what he has already)
void SetColoredBalls(int playernum,bool reset)
{
float redb[4];
float greenb[4];
float blueb[4];
if(reset){
DLLPlayerSetRotatingBall(playernum,0,0,NULL,NULL,NULL);
return;
}
int flagcount;
int flagmask;
bool red,blue,green,yellow;
red=blue=green=yellow = false;
flagcount = GetFlagCountForPlayer(playernum);
flagmask = GetFlagMaskForPlayer(playernum);
if(flagcount==0){
DLLPlayerSetRotatingBall(playernum,0,0,NULL,NULL,NULL);
return;
}
if(flagmask&FLAGMASK_REDTEAM)
red = true;
if(flagmask&FLAGMASK_BLUETEAM)
blue = true;
if(flagmask&FLAGMASK_GREENTEAM)
green = true;
if(flagmask&FLAGMASK_YELLOWTEAM)
yellow = true;
for(int i=0;i<flagcount;i++){
if(red){
red = false;
redb[i] = 1;
greenb[i] = 0;
blueb[i] = 0;
}else
if(blue){
blue = false;
redb[i] = 0;
greenb[i] = 0;
blueb[i] = 1;
}else
if(green){
green = false;
redb[i] = 0;
greenb[i] = 1;
blueb[i] = 0;
}else
if(yellow){
yellow = false;
redb[i] = 1;
greenb[i] = 1;
blueb[i] = 0;
}
}
DLLPlayerSetRotatingBall(playernum,flagcount,0,redb,greenb,blueb);
}
//Allows for dynamic changing of the number of teams in the game
void ChangeNumberOfTeams(int newsize)
{
CTFNumOfTeams = newsize;
DMFCBase->SetNumberOfTeams(CTFNumOfTeams);
}
void DoFlagReturnedHome(int team)
{
if(DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum())==team){
if(snd_return_ownteam!=-1)
DLLPlay2dSound(snd_return_ownteam,MAX_GAME_VOLUME/2);
}else{
if(snd_return_otherteam!=-1)
DLLPlay2dSound(snd_return_otherteam,MAX_GAME_VOLUME/2);
}
}
void DoLoseFlag(int team)
{
if(DMFCBase->GetPlayerTeam(DMFCBase->GetPlayerNum())==team){
if(snd_lose_ownteam!=-1)
DLLPlay2dSound(snd_lose_ownteam,MAX_GAME_VOLUME/2);
}else{
if(snd_lose_otherteam!=-1)
DLLPlay2dSound(snd_lose_otherteam,MAX_GAME_VOLUME/2);
}
}
// VerifyFlagPosition
// Call this at certain intervals to verify a flags position. It will look at the
// flags room and makes sure that if it's in it's home room, the correct values
// are set, and vice versa.
void VerifyFlagPosition(void)
{
}
// AddFlagToPlayer
// gives a player a flag
bool AddFlagToPlayer(int pnum,int team,int flagobjnum)
{
object *pobj = &dObjects[dPlayers[pnum].objnum];
bool ret = true;
if(DMFCBase->GetLocalRole()==LR_SERVER){
//We're the server, so we need to create the flag and tell the clients, and then attach it
flagobjnum = DLLObjCreate(OBJ_POWERUP,AFlagIDs[team],pobj->roomnum,&pobj->pos,NULL,OBJECT_HANDLE_NONE);
if(flagobjnum!=-1){
//tell the clients that the flag has been created and they should attach it
TellClientsToAddorDelFlag(pnum,team,flagobjnum,true);
}
// Attach the flag
// 1st figure out how many flags the guy already has
// -------------------------------------------------
int max_num_attach = 3; //based off chrishack for multiplayer
int num_attached = 0;
if(!pobj->attach_children)
ret = false;
else
{
for(int ap=0;ap<max_num_attach;ap++)
{
if(pobj->attach_children[ap]!=OBJECT_HANDLE_NONE)
{
//attempt to make sure it's a valid object
object *tobj;
if(DLLObjGet(pobj->attach_children[ap],&tobj))
{
num_attached++;
}
}
}
// 2nd num_attached is the current number of flags this object has
// ---------------------------------------------------------------
switch(num_attached)
{
case 0:
{
//the easy case, nothing is attached, so just attach it to ap0
ret = DLLAttachObject(pobj,0,&dObjects[flagobjnum],0,true);
}break;
case 1:
{
bool retval;
object *tobj;
//we should have a flag at ap0, move it to ap1
ASSERT(pobj->attach_children[0]!=OBJECT_HANDLE_NONE);
int saved_ap0 = pobj->attach_children[0];
retval = DLLUnattachChild(pobj,0);
ASSERT(retval);
retval = DLLObjGet(saved_ap0,&tobj);
ASSERT(retval);
//attach to ap1
retval = DLLAttachObject(pobj,1,tobj,0,true);
ASSERT(retval);
//attach new flag to ap2
retval = DLLAttachObject(pobj,2,&dObjects[flagobjnum],0,true);
ASSERT(retval);
ret = retval;
}break;
case 2:
{
//we should have a flag at ap1 and ap2
//so just add this to ap3
//attach new flag to ap3
ret = DLLAttachObject(pobj,0,&dObjects[flagobjnum],0,true);
}break;
case 3:
default:
{
Int3(); //Get Jeff
ret = false;
}break;
}
}
if(!ret){
//couldn't attach the flag
mprintf((0,"CTF: COULDN'T ATTACH FLAG TO PLAYER, DELETING\n"));
//tell the clients to remove this flag
DLLSetObjectDeadFlag(&dObjects[flagobjnum],true,false);
}
}
if(flagobjnum==-1){
//there was an error creating the flag...not good
mprintf((0,"CTF: Couldn't create/unhash flag for attachment\n"));
DMFCBase->DisconnectMe();
return false;
}
ChildFlags[team] = dObjects[flagobjnum].handle;
return ret;
}
// RemoveFlagFromPlayer
// removes a flag from a player
bool RemoveFlagFromPlayer(int pnum,int team)
{
int flagobjnum = -1;
if ( DMFCBase->GetLocalRole()==LR_SERVER) {
if(ChildFlags[team]==OBJECT_HANDLE_NONE)
return false;
object *fptr;
if(!DLLObjGet(ChildFlags[team],&fptr)){
//the flag is already dead?!?
return false;
}
DLLSetObjectDeadFlag(fptr,true,false);
//tell the clients to remove the flag
TellClientsToAddorDelFlag(pnum,team,-1,false);
}
ChildFlags[team] = OBJECT_HANDLE_NONE;
return true;
}
void TellClientsToAddorDelFlag(int pnum,int team,int objnum,bool add)
{
if(add){
//if we are adding a flag, than we need to send the object to them to make sure that
//they have it for when we go to process it
DLLMultiSendObject(&dObjects[objnum],false,true);
}
ubyte data[MAX_GAME_DATA_SIZE];
int size = 0;
DMFCBase->StartPacket(data,SPID_ADDDELFLAG,&size);
MultiAddByte(pnum,data,&size);
MultiAddByte(team,data,&size);
MultiAddByte(add,data,&size);
if(add){
//add the objnum of the flag and pack it into the packet
MultiAddInt(objnum,data,&size);
}
DMFCBase->SendPacket(data,size,SP_ALL);
}
void ServerIsTellingMeToAddorDelAFlag(ubyte *data)
{
int size = 0;
int pnum,team;
bool add;
pnum = MultiGetByte(data,&size);
team = MultiGetByte(data,&size);
add = (MultiGetByte(data,&size))?true:false;
//determine if we should add or delete a flag
if(add){
int objnum;
objnum = MultiGetInt(data,&size);
objnum = DMFCBase->ConvertServerToLocalObjnum(objnum);
if(objnum==-1){
//uh oh...corruption???
FatalError("CTF: Server->Local Object Corruption\n");
return;
}
AddFlagToPlayer(pnum,team,objnum);
}else{
RemoveFlagFromPlayer(pnum,team);
}
}
/////////////////////////////////
//Timer event handlers
void OnTimer(void)
{
DisplayFlagBlink = !DisplayFlagBlink;
}
void OnTimerKill(void)
{
DisplayFlagBlink = true;
WhoJustFlagged = WhoJustFlaggedTimer = -1;
}
void OnTimerScore(void)
{
DisplayScoreBlink = !DisplayScoreBlink;
}
void OnTimerScoreKill(void)
{
DisplayScoreBlink = true;
WhoJustScored = WhoJustScoredTimer = -1;
}
///////////////////////////////////////////////////////////////////////////////////
int PackByte(ubyte byte,ubyte *buffer,int pos)
{
buffer[pos] = byte;
pos++;
return pos;
}
int UnPackByte(ubyte *byte,ubyte *buffer,int pos)
{
*byte = buffer[pos];
pos++;
return pos;
}
int PackBytes(ubyte *bytes,int count,ubyte *buffer,int pos)
{
memcpy(&buffer[pos],bytes,count);
pos += count;
return pos;
}
int UnPackBytes(ubyte *bytes,int count,ubyte *buffer,int pos)
{
memcpy(bytes,&buffer[pos],count);
pos += count;
return pos;
}
int PackWord(ushort word,ubyte *buffer,int pos)
{
return PackBytes((ubyte *)&word,sizeof(ushort),buffer,pos);
}
int UnPackWord(ushort *word,ubyte *buffer,int pos)
{
return UnPackBytes((ubyte *)word,sizeof(ushort),buffer,pos);
}
int PackInt(int data,ubyte *buffer,int pos)
{
return PackBytes((ubyte *)&data,sizeof(int),buffer,pos);
}
int UnPackInt(int *data,ubyte *buffer,int pos)
{
return UnPackBytes((ubyte *)data,sizeof(int),buffer,pos);
}
int PackArray(ubyte *data,int size,int count,ubyte *buffer,int pos)
{
return PackBytes(data,size*count,buffer,pos);
}
int UnPackArray(ubyte *data,int size,int count,ubyte *buffer,int pos)
{
return UnPackBytes(data,size*count,buffer,pos);
}
void SendGameState(int pnum)
{
ubyte data[MAX_GAME_DATA_SIZE];
int count = 0;
int i;
DMFCBase->StartPacket(data,SPID_GAMESTATE,&count);
//pack number of teams in this game
MultiAddInt(CTFNumOfTeams,data,&count);
//pack flag whether someone has scored a hattrick
MultiAddByte(Someone_has_hattrick,data,&count);
for(i=0;i<DLLMAX_TEAMS;i++)
{
//Pack FlagAtHome
MultiAddByte(FlagAtHome[i],data,&count);
//pack flag timeout timers
MultiAddFloat(Flag_timeout_timers[i],data,&count);
//pack team scores
MultiAddInt(TeamScores[i],data,&count);
//pack overall team scores
MultiAddInt(OverallTeamScores[i],data,&count);
//pack who has flag data
MultiAddInt(HasFlag[i],data,&count);
}
ushort server_objnums[DLLMAX_TEAMS];
for(i=0;i<DLLMAX_TEAMS;i++){
if(ChildFlags[i]==OBJECT_HANDLE_NONE){
//slot not in use
server_objnums[i] = 65535;
}else{
//should be a valid slot
object *obj;
if(!DLLObjGet(ChildFlags[i],&obj)){
//ok it's not a valid slot...hrm
ChildFlags[i] = OBJECT_HANDLE_NONE;
server_objnums[i] = 65535;
}else{
//it's valid
int objnum = (obj - dObjects);
server_objnums[i] = objnum;
}
}
}
//pack attached flag handles
for(i=0;i<DLLMAX_TEAMS;i++)
{
MultiAddUshort(server_objnums[i],data,&count);
}
DMFCBase->SendPacket(data,count,pnum);
}
void ReceiveGameState(ubyte *data)
{
int count = 0;
int num_teams;
int i;
for( i=0;i<DLLMAX_PLAYERS;i++){
//reset everything
SetColoredBalls(i,true);
for(int t=0;t<DLLMAX_TEAMS;t++){
while(DLLInvCheckItem(i,OBJ_POWERUP,FlagIDs[t])){
DLLInvRemove(i,OBJ_POWERUP,FlagIDs[t]);
}
}
}
// Read num teams
num_teams = MultiGetInt(data,&count);
ChangeNumberOfTeams(num_teams);
// see if someone has had a hattrick yet
Someone_has_hattrick = (MultiGetByte(data,&count))?true:false;
for(i=0;i<DLLMAX_TEAMS;i++)
{
//UnPack FlagAtHome
FlagAtHome[i] = (MultiGetByte(data,&count))?true:false;
//unpack flag timeout timers
Flag_timeout_timers[i] = MultiGetFloat(data,&count);
//unpack team scores
TeamScores[i] = MultiGetInt(data,&count);
//unpack overall team scores
OverallTeamScores[i] = MultiGetInt(data,&count);
//unpack who has flag data
HasFlag[i] = MultiGetInt(data,&count);
}
//go through the HasFlag array and put items in the inventory, and add colored balls
for(i = 0; i < DLLMAX_TEAMS; i++ ){
if(HasFlag[i]!=-1){
DLLInvAddTypeID(HasFlag[i],OBJ_POWERUP,FlagIDs[i],-1,-1,0,NULL);
//put some balls on the guy
SetColoredBalls(HasFlag[i]);
}
}
ushort server_objnums[DLLMAX_TEAMS];
//unpack attached flag handles
for(i=0;i<DLLMAX_TEAMS;i++)
{
server_objnums[i] = MultiGetUshort(data,&count);
}
//the final step, match the server_objnums->ChildFlags and Attach the flags
for(i = 0; i <DLLMAX_TEAMS; i++ ){
if(server_objnums[i]!=65535){
//we should have a real object here
ushort our_objnum = DMFCBase->ConvertServerToLocalObjnum(server_objnums[i]);
if(our_objnum==-1){
//fatal error
mprintf((0,"CTF: Local Objnums don't match server objnums\n"));
ChildFlags[i] = OBJECT_HANDLE_NONE;
DMFCBase->DisconnectMe();
}else{
//yeah! a valid objnum, attach it!!!
if(HasFlag[i]!=-1)
AddFlagToPlayer(HasFlag[i],i,our_objnum);
else{
//hmm, HasFlag doesn't match ChildFlags
mprintf((0,"CTF: HasFlag doesn't match ChildFlags!!!!\n"));
ChildFlags[i] = OBJECT_HANDLE_NONE;
}
}
}else{
//ok invalid...
ChildFlags[i] = OBJECT_HANDLE_NONE;
}
}
}
/*
********************************************************************************************
*/
// returns the number of flags a player has, 0 if none, or an invalid pnum
int GetFlagCountForPlayer(int pnum)
{
//1st check the pnum, make sure it is OK, is it isn't, return 0
if(pnum<0 || pnum>=DLLMAX_PLAYERS){
//invalid player number, return 0 flags
mprintf((0,"CTF: Invalid PNUM passed to GetFlagCountForPlayer()\n"));
return 0;
}
int flag_count = 0;
//2nd, go through all the team flags, and check the player's inventory, see if they have the ID,
//if so, add it to their count
for(int i = 0; i < DLLMAX_TEAMS; i++){
if(DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[i])){
//they have this flag
flag_count++;
}
}
return flag_count;
}
// returns the mask of which flags this player currently has
ubyte GetFlagMaskForPlayer(int pnum)
{
//1st check the pnum, make sure it is OK, if it isn't, return 0, meaning no flags
if(pnum<0 || pnum>=DLLMAX_PLAYERS){
//invalid player number, return 0 flags
mprintf((0,"CTF: Invalid PNUM passed to GetFlagMaskForPlayer()\n"));
return 0;
}
int flag_mask = 0;
ubyte mask = 0x01;
//2nd go through all the teams flags, and check the player's inventory, see if they have the ID,
//if so, OR the current mask to the running flag_mask
for(int i = 0; i < DLLMAX_TEAMS; i++){
if(DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[i])){
//the have this flag
flag_mask |= mask;
}
//adjust the mask because we are moving to the next flag
mask = mask << 1;
}
return flag_mask;
}
// adds a flag to a player, as a precaution, it will go through all the players and makes sure that no one
// has the flag that is being added. If they are adding the flag, than remove that flag from whoever we thought had it
// it will return false if it had to remove a flag from a player
bool GivePlayerFlag(int pnum,ubyte team)
{
//1st check the player num, make sure it is valid
if(!DMFCBase->CheckPlayerNum(pnum)){
//not a valid player
mprintf((0,"CTF: Invalid pnum passed to GivePlayerFlag()\n"));
return false;
}
//2nd check to make sure the team given is valid, and not our own team
if(team >= CTFNumOfTeams){
//not a valid team
mprintf((0,"CTF: Invalid team passed to GivePlayerFlag() (team>=CTFNumOfTeams)\n"));
return false;
}
if(team == DMFCBase->GetPlayerTeam(pnum)){
//we can't add a flag of the same team to a player
mprintf((0,"CTF: In GivePlayerFlag(), trying to add a player's home team flag\n"));
return false;
}
//3rd, make sure no one else currently has this flag
//we'll check our HasFlags[] first
if(HasFlag[team]!=-1){
//hmm, we have someone listed as already having this flag...odd
mprintf((0,"CTF: In GivePlayerFlag(), trying to add a flag, but we see someone else should already have it\n"));
int player = HasFlag[team];
if(DMFCBase->CheckPlayerNum(player)){
//this player is in the game...
//make sure this player doesn't have the flag in his inventory
while(DLLInvCheckItem(player,OBJ_POWERUP,FlagIDs[team])){
//we have it listed that he does
mprintf((0,"CTF: In GivePlayerFlag(), we detected the flag in someone elses inventory\n"));
//remove all the flags that this player has of this team...very weird
DLLInvRemove(player,OBJ_POWERUP,FlagIDs[team]);
SetColoredBalls(player,false);
//check to see if the player had a flag attached to him
if(ChildFlags[team] != OBJECT_HANDLE_NONE && DMFCBase->GetLocalRole()==LR_SERVER){
//he does have a flag attached to him...kill it
RemoveFlagFromPlayer(player,team);
}
}
}
//reset this value of the array
HasFlag[team] = -1;
if(DMFCBase->GetLocalRole()!=LR_SERVER){
mprintf((0,"CTF: Game must be out of sync, requesting game state\n"));
DMFCBase->RequestGameState();
}
}
//loop through all the players and make sure they don't have this flag
for(int player = 0; player < DLLMAX_PLAYERS; player++ ){
//check to see if it's an active player
if(!DMFCBase->CheckPlayerNum(player))
continue;
//remove all the flags the player has
while(DLLInvCheckItem(player,OBJ_POWERUP,FlagIDs[team])){
mprintf((0,"CTF: In GivePlayerFlag(), detected a flag in a stranger's inventory\n"));
DLLInvRemove(player,OBJ_POWERUP,FlagIDs[team]);
SetColoredBalls(player,false);
//check to see if the player had a flag attached to him
if(ChildFlags[team]!=OBJECT_HANDLE_NONE && DMFCBase->GetLocalRole()==LR_SERVER){
//he does have a flag attached to him...kill it
RemoveFlagFromPlayer(player,team);
}
}
}
//ok, when we get here everything should be ducky, just add the flag and do the necessary things
DLLInvAddTypeID(pnum,OBJ_POWERUP,FlagIDs[team],-1,-1,0,NULL);
HasFlag[team] = pnum;
SetColoredBalls(pnum);
if(DMFCBase->GetLocalRole()==LR_SERVER){
//so we got here and added a flag to the player, now we need to attach the flag to the player
if(!AddFlagToPlayer(pnum,team)){
//there was an error adding the flag,,,,ack!
mprintf((0,"CTF: In GivePlayerFlag(), couldn't attach the flag to the player\n"));
}
}
return true;
}
//this function takes a flag away from the player, useful for when he scores, spews, disconnects, or observer modes
void LoseFlagForPlayer(int pnum,ubyte team,bool remove_from_inven)
{
//1st check the player number
if(pnum<0 || pnum>=DLLMAX_PLAYERS){
mprintf((0,"CTF:Invalid pnum passed to LoseFlagForPlayer()\n"));
return;
}
//2nd check the team number
if(team>=CTFNumOfTeams){
mprintf((0,"CTF:Invalid team passed to LoseFlagForPlayer()\n"));
return;
}
if(team==DMFCBase->GetPlayerTeam(pnum)){
mprintf((0,"CTF:Invalid team passed to LoseFlagForPlayer()...same team as player\n"));
return;
}
//ok, we have it registered that the flag belongs to us
while(remove_from_inven && DLLInvCheckItem(pnum,OBJ_POWERUP,FlagIDs[team])){
DLLInvRemove(pnum,OBJ_POWERUP,FlagIDs[team]);
}
SetColoredBalls(pnum);
HasFlag[team] = -1;
//check to see if the player had a flag attached to him
if(ChildFlags[team] != OBJECT_HANDLE_NONE){
//he does have a flag attached to him...kill it
if(DMFCBase->GetLocalRole()==LR_SERVER){
//remove this flag and tell the clients to do so, cause we're the server
RemoveFlagFromPlayer(pnum,team);
}
}
}
void OnGetTokenString(char *src,char *dest,int dest_size)
{
if(!stricmp(src,"flag"))
{
int mypnum = DMFCBase->GetPlayerNum();
int flagcount = GetFlagCountForPlayer(mypnum);
int flagmask = GetFlagMaskForPlayer(mypnum);
int team;
bool hasflag[DLLMAX_TEAMS];
for(team=0;team<CTFNumOfTeams;team++){
//draw all the flags the player has in his inventory
if(flagmask&0x01){
//player has this flag
hasflag[team] = true;
}else
{
hasflag[team] = false;
}
//rotate 1 bit for next flag
flagmask = flagmask>>1;
}
for(;team<DLLMAX_TEAMS;team++)
{
hasflag[team] = false;
}
switch(flagcount)
{
case 1:
{
char t[64];
for(team=0;team<CTFNumOfTeams;team++)
{
if(hasflag[team])
{
strcpy(t,DMFCBase->GetTeamString(team));
break;
}
}
strncpy(dest,t,dest_size-1);
dest[dest_size-1] = '\0';
return;
}break;
case 2:
{
char t[2][64];
int found = 0;
for(team=0;team<CTFNumOfTeams;team++)
{
if(hasflag[team])
{
strcpy(t[found],DMFCBase->GetTeamString(team));
found++;
if(found==flagcount)
break;
}
}
char buffer[256];
sprintf(buffer,"%s and %s",t[0],t[1]);
strncpy(dest,buffer,dest_size-1);
dest[dest_size-1] = '\0';
return;
}break;
case 3:
{
char t[3][64];
int found = 0;
for(team=0;team<CTFNumOfTeams;team++)
{
if(hasflag[team])
{
strcpy(t[found],DMFCBase->GetTeamString(team));
found++;
if(found==flagcount)
break;
}
}
char buffer[256];
sprintf(buffer,"%s, %s and %s",t[0],t[1],t[2]);
strncpy(dest,buffer,dest_size-1);
dest[dest_size-1] = '\0';
return;
}break;
default:
strncpy(dest,"No",dest_size-1);
dest[dest_size-1] = '\0';
return;
break;
}
}
DMFCBase->OnGetTokenString(src,dest,dest_size);
}
#ifdef MACINTOSH
#pragma export off
#endif