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

2097 lines
56 KiB
C++

/*
* $Logfile: /DescentIII/Main/entropy/EntropyBase.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003/08/26 03:57:41 $
* $Author: kevinb $
*
* <insert description of file here>
*
* $Log: EntropyBase.cpp,v $
* Revision 1.1.1.1 2003/08/26 03:57:41 kevinb
* initial 1.5 import
*
*
* 59 9/24/01 2:28p Matt
* Allowed room for longer team name on results screen.
*
* 58 9/13/01 5:31p Matt
* Added a team member list to the stats display.
* Fixed a non-terminated string bug.
*
* 57 10/21/99 9:27p Jeff
* B.A. Macintosh code merge
*
* 56 9/03/99 5:27p Jeff
* fixed energy bug (going into an energy center with >100 energy)
*
* 55 8/17/99 5:53p Jeff
* track ranks on PXO
*
* 54 7/23/99 2:31p Jeff
* fixed bug where if the killer is not known, a player would still be
* able to carry virus after dying.
*
* 53 7/15/99 1:18a Jeff
* fixed up $scores
*
* 52 7/15/99 1:01a Jeff
* added $scores support
*
* 51 7/13/99 12:11p Jeff
* added some specific text taunt token decoding
*
* 50 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. Play fanfare and print out a message is
* someone takes over the last base
*
* 49 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
*
* 48 6/08/99 7:08p Jeff
* begining to comment for SDK release
*
* 47 5/23/99 3:04a Jason
* fixed bug with player rankings not being updated correctly
*
* 46 5/23/99 2:22a Jeff
* added countdown. fixed takeover time...off by .5 seconds
*
* 45 5/22/99 1:12a Jeff
* play a sound on the server when picking up a virus
*
* 44 5/21/99 10:30p Jason
* made some rule changes to make more fun
*
* 43 5/21/99 10:03a Jeff
* hopefully filled in any remaining gaps related to have more than you
* can carry virus loads
*
* 42 5/12/99 11:04p Jeff
* dmfc and multiplayer games now have endian friendly packets (*whew*)
*
* 41 5/12/99 11:28a Jeff
* when a player scores, dont remove all virii, just what they used
*
* $NoKeywords: $
*/
#include "gamedll_header.h" //included by all mods, it includes all needed headers, etc.
#include <string.h>
#include "idmfc.h" //dmfc! (required)
#include "Entropy.h"
#include "Entropystr.h" //our string table for Entropy
#include "EntropyAux.h"
// the DMFC object, used throughout, and required by all mods
IDMFC *DMFCBase = NULL;
// our DmfcStats object, used for the F7 screen
IDmfcStats *dstat = NULL;
// these are some helper pointers, so we don't have to constantly query
// DMFC for these arrays, we can query once and just save it for just future use.
object *dObjects;
player *dPlayers;
room *dRooms;
////////////////////////////////////////////////////
// Customization flags
// These are 'the rules' to Entropy basically
//scoring
#define SCORE_PLYR_TAKEOVER_ROOM 5 //num points for a player when he/she takes over a room
#define SCORE_TEAM_WINSGAME 10 //num points for a team when they win a round
#define SCORE_TEAM_TAKEOVER_ROOM 3 //num points for a team when a teammate of their's takes over a room
//rates
#define REPAIR_RATE 5.0f //units/sec of the repair room
#define ENERGY_RATE 5.0f //units/sec of the energy room
#define DAMAGE_RATE 5.0f //units/sec of damage when in an enemy room
#define ENERGY_CAP 100.0f //max energy given to a player via an Energy room
#define SHIELD_CAP 100.0f //max shields given to a player via a Repair room
#define VIRUS_SPEW 20.0f //How long it takes for a lab to spew a virus
#define VIRUS_PER_KILL 2.0f //How many virii a player can carry per kill-in-a-row.
#define MINIMUM_VIRUS_COUNT 5 //Minimum number of virii needed to take over a room
#define TAKEOVER_TIME 3.0f //How long a player must stand still to take over a room
////////////////////////////////////////////////////
typedef struct
{
float total_time;
vector last_pos;
}tPlayerPos;
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 TeamOwnedRooms[NUM_TEAMS];
int TeamVirii[NUM_TEAMS][MAX_VIRII];
int NumberOfKillsSinceLastDeath[DLLMAX_PLAYERS];
int SortedPLRPlayers[NUM_TEAMS][MAX_PLAYER_RECORDS];
int TeamScore[NUM_TEAMS];
int SortedTeams[NUM_TEAMS];
int SortedPlayers[MAX_PLAYER_RECORDS];
int EnergyText[NUM_TEAMS];
int RepairText[NUM_TEAMS];
int LabText[NUM_TEAMS];
tPlayerPos TimeInRoom[DLLMAX_PLAYERS];
bool DisplayScoreScreen;
int virus_id = -1;
int *RoomList = NULL;
int RoomCount;
int Highlight_bmp = -1;
bool display_my_welcome = false;
float Server_last_play_damage_sound = 0;
int Player_who_took_over_last_base = -1;
int snd_repair_center = -1;
int snd_energy_center = -1;
int snd_score = -1;
int snd_virus_destroy = -1;
int snd_damage = -1;
int snd_virus_pickup = -1;
//void DisplayScores(void);
void DisplayHUDScores(struct tHUDItem *hitem);
void DisplayWelcomeMessage(int player_num);
void SortTeamScores(int *sortedindex,int *scores);
void SaveStatsToFile(char *filename);
void OnLabSpewTimer(void);
void RemoveVirusFromPlayer(int player_num,bool remove_all);
bool ScanForLaboratory(int team,int *newlab);
bool CompareDistanceTravel(vector *curr_pos,vector *last_pos);
void OnDisconnectSaveStatsToFile(void);
void OnLevelEndSaveStatsToFile(void);
void OnGetTokenString(char *src,char *dest,int dest_size);
//Player in special room functions
void DoIntervalPlayerFrame(void);
void DoPlayerInLab(int pnum,float time);
void DoPlayerInEnergy(int pnum,float time);
void DoPlayerInRepair(int pnum,float time);
void DoPlayerInEnemy(int pnum,float time);
void DoServerConfigureDialog(void);
void OnPrintScores(int level);
///////////////////////////////////////////////
//localization info
char **StringTable;
int StringTableSize = 0;
char *_ErrorString = "Missing String";
char *GetStringFromTable(int d){if( (d<0) || (d>=StringTableSize) ) return _ErrorString; else return StringTable[d];}
///////////////////////////////////////////////
#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 = NUM_TEAMS;
options->min_teams = NUM_TEAMS;
strcpy(options->game_name,TXT_GAMENAME);
strcpy(options->requirements,"ENTROPY");
}
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[%d]",(stat)?stat->Score[DSTAT_LEVEL]:0,(stat)?stat->Score[DSTAT_OVERALL]:0);
}
void TeamScoreCallback(int team,char *buffer,int buffer_size)
{
ASSERT(team>=0 && team<NUM_TEAMS);
sprintf(buffer," %d",TeamScore[team]);
}
// 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_OnGameStateRequest(OnGameStateRequest);
DMFCBase->Set_OnClientPlayerKilled(OnClientPlayerKilled);
DMFCBase->Set_OnServerGameCreated(OnServerGameCreated);
DMFCBase->Set_OnClientLevelStart(OnClientLevelStart);
DMFCBase->Set_OnClientLevelEnd(OnClientLevelEnd);
DMFCBase->Set_OnClientPlayerEntersGame(OnClientPlayerEntersGame);
DMFCBase->Set_OnInterval(OnInterval);
DMFCBase->Set_OnHUDInterval(OnHUDInterval);
DMFCBase->Set_OnKeypress(OnKeypress);
DMFCBase->Set_OnPLRInterval(OnPLRInterval);
DMFCBase->Set_OnPLRInit(OnPLRInit);
DMFCBase->Set_OnSaveStatsToFile(OnSaveStatsToFile);
DMFCBase->Set_OnLevelEndSaveStatsToFile(OnLevelEndSaveStatsToFile);
DMFCBase->Set_OnDisconnectSaveStatsToFile(OnDisconnectSaveStatsToFile);
DMFCBase->Set_OnClientPlayerDisconnect(OnClientPlayerDisconnect);
DMFCBase->Set_OnServerPlayerChangeSegment(OnServerPlayerChangeSegment);
DMFCBase->Set_OnClientPlayerChangeSegment(OnClientPlayerChangeSegment);
DMFCBase->Set_OnServerCollide(OnServerCollide);
DMFCBase->Set_OnControlMessage(OnControlMessage);
DMFCBase->Set_OnClientShowUI(OnClientShowUI);
DMFCBase->Set_OnGetTokenString(OnGetTokenString);
DMFCBase->Set_OnPrintScores(OnPrintScores);
// Setup arrays for easier to read code
dObjects = DMFCBase->GetObjects();
dPlayers = DMFCBase->GetPlayers();
dRooms = DMFCBase->GetRooms();
netgame_info *Netgame = DMFCBase->GetNetgameInfo();
Netgame->flags |= (NF_TRACK_RANK);
DMFCBase->GameInit(NUM_TEAMS);
DLLCreateStringTable("entropy.str",&StringTable,&StringTableSize);
DLLmprintf((0,"%d strings loaded from string table\n",StringTableSize));
if(!StringTableSize){
*all_ok = 0;
return;
}
//add the death and suicide messages
DMFCBase->AddDeathMessage(TXT_KILLA,true);
DMFCBase->AddSuicideMessage(TXT_SUICIDEA);
//register special packet receivers
DMFCBase->RegisterPacketReceiver(SPID_NEWPLAYER,GetGameStartPacket);
DMFCBase->RegisterPacketReceiver(SPID_TAKEOVER,ReceiveTakeOverPacket);
DMFCBase->RegisterPacketReceiver(SPID_ROOMINFO,ReceiveRoomInfo);
DMFCBase->RegisterPacketReceiver(SPID_PICKUPVIRUS,ReceivePickupVirus);
DMFCBase->RegisterPacketReceiver(SPID_VIRUSCREATE,DoVirusCreate);
DMFCBase->SetNumberOfTeams(NUM_TEAMS);
DMFCBase->AddHUDItemCallback(HI_TEXT,DisplayHUDScores);
EnergyText[RED_TEAM] = DLLFindTextureName("RedEnergy");
EnergyText[BLUE_TEAM] = DLLFindTextureName("BlueEnergy");
RepairText[RED_TEAM] = DLLFindTextureName("RedRepair");
RepairText[BLUE_TEAM] = DLLFindTextureName("BlueRepair");
LabText[RED_TEAM] = DLLFindTextureName("RedLab");
LabText[BLUE_TEAM] = DLLFindTextureName("BlueLab");
snd_repair_center = DLLFindSoundName("EntropyRepair");
snd_energy_center = DLLFindSoundName("EntropyEnergy");
snd_score = DLLFindSoundName("EntropyScore");
snd_virus_destroy = DLLFindSoundName("EntropyDestroy");
snd_damage = DLLFindSoundName("HitEnergy");
snd_virus_pickup = DLLFindSoundName("Powerup pickup");
DMFCBase->SetupPlayerRecord(sizeof(tPlayerStat),(int (*)(void *,ubyte *))pack_pstat,(int (*)(void *,ubyte *))unpack_pstat);
virus_id = DLLFindObjectIDName("EntropyVirus");
DisplayScoreScreen = false;
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;
}
}
// Initialize the Stats Manager
// ----------------------------
tDmfcStatsInit tsi;
tDmfcStatsColumnInfo pl_col[6];
char gname[20];
strcpy(gname,TXT_STATSGAMENAME);
tsi.flags = DSIF_SHOW_PIC|DSIF_SHOW_OBSERVERICON|DSIF_SEPERATE_BY_TEAM;
tsi.cColumnCountDetailed = 0;
tsi.cColumnCountPlayerList = 6;
tsi.clbDetailedColumnBMP = NULL;
tsi.clbDetailedColumn = NULL;
tsi.clbPlayerColumn = DetermineScore;
tsi.clbPlayerColumnBMP = NULL;
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;
strcpy(pl_col[0].title,TXT_PILOT);
pl_col[0].type = DSCOL_PILOT_NAME;
pl_col[0].width = 120;
pl_col[1].color_type = DSCOLOR_TEAM;
strcpy(pl_col[1].title,TXT_SCORE);
pl_col[1].type = DSCOL_CUSTOM;
pl_col[1].width = 47;
pl_col[2].color_type = DSCOLOR_TEAM;
strcpy(pl_col[2].title,TXT_KILLS);
pl_col[2].type = DSCOL_KILLS_LEVEL;
pl_col[2].width = 47;
pl_col[3].color_type = DSCOLOR_TEAM;
strcpy(pl_col[3].title,TXT_DEATHS);
pl_col[3].type = DSCOL_DEATHS_LEVEL;
pl_col[3].width = 57;
pl_col[4].color_type = DSCOLOR_TEAM;
strcpy(pl_col[4].title,TXT_SUICIDES);
pl_col[4].type = DSCOL_SUICIDES_LEVEL;
pl_col[4].width = 62;
pl_col[5].color_type = DSCOLOR_TEAM;
strcpy(pl_col[5].title,TXT_PING);
pl_col[5].type = DSCOL_PING;
pl_col[5].width = 40;
dstat->Initialize(&tsi);
}
// Called when the DLL is shutdown
void DLLFUNCCALL DLLGameClose ()
{
if(Highlight_bmp>BAD_BITMAP_HANDLE)
DLLbm_FreeBitmap(Highlight_bmp);
if(RoomList){
free(RoomList);
RoomList = NULL;
}
DLLDestroyStringTable(StringTable,StringTableSize);
if(dstat)
{
dstat->DestroyPointer();
dstat = NULL;
}
if(DMFCBase)
{
DMFCBase->GameClose();
DMFCBase->DestroyPointer();
DMFCBase = NULL;
}
}
// The server has just started, so clear out all the stats and game info
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<NUM_TEAMS;i++){
TeamScore[i] = 0;
}
}
// The server has started a new level, so clear out level scores
void OnClientLevelStart(void)
{
DMFCBase->OnClientLevelStart();
int i;
for(i=0;i<NUM_TEAMS;i++)
{
TeamOwnedRooms[i] = 0;
for(int v=0;v<MAX_VIRII;v++)
TeamVirii[i][v] = -1;
}
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;
}
}
}
//reset damage timer
Server_last_play_damage_sound = 0;
//reset the id to -1, so we are ready for the new level
Player_who_took_over_last_base = -1;
if(DMFCBase->GetLocalRole()==LR_SERVER){
DMFCBase->SetTimerInterval(OnLabSpewTimer,VIRUS_SPEW,-1.0f);
}
if(RoomList){
free(RoomList);
RoomList = NULL;
}
RoomCount = 0;
int r;
for(r=0;r<=DMFCBase->GetHighestRoomIndex();r++){
if(dRooms[r].used){
int flags = dRooms[r].flags;
if(flags&RF_SPECIAL1||flags&RF_SPECIAL2||flags&RF_SPECIAL3||
flags&RF_SPECIAL4||flags&RF_SPECIAL5||flags&RF_SPECIAL6){
RoomCount++;
}
}
}
if(RoomCount==0){
FatalError("No Special Rooms Defined");
return;
}
RoomList = (int *)malloc(sizeof(int)*RoomCount);
if(!RoomList){
FatalError("Out of memory");
return;
}
bool room_used_already;
int special_room_index = 0;
for(r=0;r<=DMFCBase->GetHighestRoomIndex();r++){
if(dRooms[r].used){
if(dRooms[r].flags&RF_FUELCEN)
{
dRooms[r].flags &= ~RF_FUELCEN;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
room_used_already = false;
if(dRooms[r].flags&RF_SPECIAL1){ //Red Team Laboratory
TeamOwnedRooms[RED_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(LabText[RED_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
if(dRooms[r].flags&RF_SPECIAL4){ //Blue Team Laboratory
if(room_used_already)
{
dRooms[r].flags &= ~RF_SPECIAL4;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
else
{
TeamOwnedRooms[BLUE_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(LabText[BLUE_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
}
if(dRooms[r].flags&RF_SPECIAL2){ //Red Team Energy Room
if(room_used_already)
{
dRooms[r].flags &= ~RF_SPECIAL2;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
else{
TeamOwnedRooms[RED_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(EnergyText[RED_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
}
if(dRooms[r].flags&RF_SPECIAL5){ //Blue Team Energy Room
if(room_used_already)
{
dRooms[r].flags &= ~RF_SPECIAL5;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
else{
TeamOwnedRooms[BLUE_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(EnergyText[BLUE_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
}
if(dRooms[r].flags&RF_SPECIAL3){ //Red Team Repair Room
if(room_used_already)
{
dRooms[r].flags &= ~RF_SPECIAL3;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
else{
TeamOwnedRooms[RED_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(RepairText[RED_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
}
if(dRooms[r].flags&RF_SPECIAL6){ //Blue Team Repair Room
if(room_used_already)
{
dRooms[r].flags &= ~RF_SPECIAL6;
dRooms[r].room_change_flags |= RCF_GOALSPECIAL_FLAGS;
}
else{
TeamOwnedRooms[BLUE_TEAM]++;
room_used_already = true;
PaintRoomWithTexture(RepairText[BLUE_TEAM],r);
RoomList[special_room_index] = r;
special_room_index++;
}
}
}
}
//zero out other arrays
for(i=0;i<DLLMAX_PLAYERS;i++){
NumberOfKillsSinceLastDeath[i] = 0;
TimeInRoom[i].total_time = 0;
RemoveVirusFromPlayer(i,true);
}
if(DMFCBase->GetLocalRole()==LR_SERVER){
//spew 3 virii in each lab
for(i=0;i<3;i++)
OnLabSpewTimer();
}
DMFCBase->RequestGameState();
}
void OnClientLevelEnd(void)
{
DMFCBase->OnClientLevelEnd();
}
// A New Player has entered the game, so we want to send him a game status packet that
// has information about the game
void OnGameStateRequest(int player_num)
{
SendGameStartPacket(player_num);
DMFCBase->OnGameStateRequest(player_num);
}
// A new player has entered the game, zero there stats out
void OnClientPlayerEntersGame(int player_num)
{
DMFCBase->OnClientPlayerEntersGame(player_num);
if(player_num!=DMFCBase->GetPlayerNum())
DisplayWelcomeMessage(player_num);
else
display_my_welcome = true;
if(player_num!=-1){
NumberOfKillsSinceLastDeath[player_num] = 0;
}
TimeInRoom[player_num].total_time = 0;
RemoveVirusFromPlayer(player_num,true);
}
void OnServerPlayerChangeSegment(int player_num,int newseg,int oldseg)
{
if(player_num==-1){
DMFCBase->OnServerPlayerChangeSegment(player_num,newseg,oldseg);
return;
}
//check to make sure it is an 'indoor' room and not terrain
if(!ROOMNUM_OUTSIDE(newseg))
{
//Check the room flags, see if we entered a special room
room *rp = &dRooms[newseg];
if(rp->flags&RF_EXTERNAL){
DMFCBase->OnServerPlayerChangeSegment(player_num,newseg,oldseg);
return;
}
if( (rp->flags&RF_SPECIAL1) || (rp->flags&RF_SPECIAL2) || (rp->flags&RF_SPECIAL3) ||
(rp->flags&RF_SPECIAL4) || (rp->flags&RF_SPECIAL5) || (rp->flags&RF_SPECIAL6) ){
DMFCBase->CallClientEvent(EVT_CLIENT_GAMEPLAYERCHANGESEG,DMFCBase->GetMeObjNum(),DMFCBase->GetItObjNum(),-1);
DMFCBase->CallOnClientPlayerChangeSegment(player_num,newseg,oldseg);
}
}
DMFCBase->OnServerPlayerChangeSegment(player_num,newseg,oldseg);
}
void OnClientPlayerChangeSegment(int player_num,int newseg,int oldseg)
{
//someone has entered a special room
TimeInRoom[player_num].total_time = 0;
DMFCBase->OnClientPlayerChangeSegment(player_num,newseg,oldseg);
}
// We need to adjust the scores
void OnClientPlayerKilled(object *killer_obj,int victim_pnum)
{
int kpnum;
if(victim_pnum!=-1)
RemoveVirusFromPlayer(victim_pnum,true);
if(killer_obj){
if((killer_obj->type==OBJ_PLAYER)||(killer_obj->type==OBJ_GHOST))
kpnum = killer_obj->id;
else if(killer_obj->type==OBJ_ROBOT || (killer_obj->type == OBJ_BUILDING && killer_obj->ai_info)){
//countermeasure kill
kpnum = DMFCBase->GetCounterMeasureOwner(killer_obj);
}else{
kpnum = -1;
}
}else
kpnum = -1;
if(kpnum!=-1){
if(kpnum==victim_pnum){
NumberOfKillsSinceLastDeath[kpnum] = 0;
}else{
NumberOfKillsSinceLastDeath[kpnum]++;
if(victim_pnum!=-1)
NumberOfKillsSinceLastDeath[victim_pnum] = 0;
}
}else
{
//this would be the case if the room killed a player
if(victim_pnum!=-1)
NumberOfKillsSinceLastDeath[victim_pnum] = 0;
}
//TODO: Handle a kill if killed by a room
DMFCBase->OnClientPlayerKilled(killer_obj,victim_pnum);
}
void OnHUDInterval(void)
{
dstat->DoFrame();
DMFCBase->DisplayOutrageLogo();
DMFCBase->OnHUDInterval();
}
void OnInterval(void)
{
SortTeamScores(SortedTeams,TeamScore);
//TODO: Correct Sorting based on score
DMFCBase->GetSortedPlayerSlots(SortedPlayers,MAX_PLAYER_RECORDS);
DoIntervalPlayerFrame();
DMFCBase->OnInterval();
}
void OnClientPlayerDisconnect(int pnum)
{
if(pnum!=-1){
NumberOfKillsSinceLastDeath[pnum] = 0;
}
DMFCBase->OnClientPlayerDisconnect(pnum);
}
void OnKeypress(int key)
{
dllinfo *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;
}
//if(key==K_E && DMFCBase->GetLocalRole()==LR_SERVER){
// DMFCBase->StartUIWindow(1);
//}
DMFCBase->OnKeypress(key);
}
void OnClientShowUI(int id,void *user_data)
{
if(id==1)
DoServerConfigureDialog();
DMFCBase->OnClientShowUI(id,user_data);
}
void OnServerCollide(object *me_obj,object *it_obj)
{
if( !me_obj || !it_obj )
return;
if(virus_id==-1){
FatalError("Unable to find Virus Object");
return;
}
if( (me_obj->type==OBJ_PLAYER) && (it_obj->type==OBJ_POWERUP) && (it_obj->id==virus_id) )
{
//determine what to do with this collision
int virus_objnum = it_obj - dObjects;
int virus_team = -1;
int virus_index = -1;
int player_num = me_obj->id;
//find which team the virus belongs to
for(int v=0;v<MAX_VIRII;v++){
if(virus_objnum==TeamVirii[RED_TEAM][v]){
virus_team = RED_TEAM;
virus_index = v;
break;
}
if(virus_objnum==TeamVirii[BLUE_TEAM][v]){
virus_team = BLUE_TEAM;
virus_index = v;
break;
}
}
if(virus_team==-1){
//hey! we hit a virus that doesn't belong to any team!!
DLLmprintf((0,"Virus (%d) doesn't belong to any team, removing...\n",virus_objnum));
DMFCBase->OnServerCollide(me_obj,it_obj);
DLLSetObjectDeadFlag(it_obj,true,false);
return;
}
int curr_count = DLLInvGetTypeIDCount(player_num,OBJ_POWERUP,it_obj->id);
if(dPlayers[player_num].team==virus_team){
//a player has collided with a virus from his team
//see if the player has enough kills to pick up this virus
int max_carry = NumberOfKillsSinceLastDeath[player_num]*VIRUS_PER_KILL;
if(curr_count+1>max_carry){
//the player can't carry another virii yet
if(player_num==DMFCBase->GetPlayerNum()){
DLLAddHUDMessage(TXT_CANTCARRY);
}else{
DMFCBase->SendControlMessageToPlayer(player_num,VIRUS_NOTENOUGHKILLS);
}
}else{
//pick this guy up
TeamVirii[virus_team][virus_index] = -1;
SendClientPickupVirus(player_num);
DLLSetObjectDeadFlag(it_obj,true,true);
if(snd_virus_pickup!=-1)
{
DLLPlay2dSound(snd_virus_pickup,MAX_GAME_VOLUME);
}
}
}
else
{
//a player has collided with a virus from another team
//kill this object
TeamVirii[virus_team][virus_index] = -1;
DLLSetObjectDeadFlag(it_obj,true,false);
if(player_num==DMFCBase->GetPlayerNum())
{
DLLAddHUDMessage(TXT_VIRUSDESTROYED);
if(snd_virus_destroy!=-1)
{
DLLPlay2dSound(snd_virus_destroy,MAX_GAME_VOLUME);
}
}
else
{
DMFCBase->SendControlMessageToPlayer(player_num,VIRUS_DESTROYED);
}
}
}
DMFCBase->OnServerCollide(me_obj,it_obj);
}
void TakeOverRoom(int newteam,int oldteam,int roomnum,int victor)
{
if(ROOMNUM_OUTSIDE(roomnum))
{
Int3();
return;
}
bool server = false;
bool success = false;
if(DMFCBase->GetLocalRole()==LR_SERVER)
server = true;
TeamOwnedRooms[newteam]++;
TeamOwnedRooms[oldteam]--;
tPlayerStat *stat = (tPlayerStat *)DMFCBase->GetPlayerRecordData(victor);
if(stat){
stat->Score[DSTAT_LEVEL]+=SCORE_PLYR_TAKEOVER_ROOM;
stat->Score[DSTAT_OVERALL]+=SCORE_PLYR_TAKEOVER_ROOM;
TeamScore[DMFCBase->GetPlayerTeam(victor)]+=SCORE_TEAM_TAKEOVER_ROOM;
if(snd_score!=-1)
{
DLLPlay2dSound(snd_score,MAX_GAME_VOLUME);
}
}
char room_buf[40];
room *rp = &dRooms[roomnum];
if(oldteam==RED_TEAM){
if(rp->flags&RF_SPECIAL1){
rp->flags &= ~RF_SPECIAL1;
rp->flags |= RF_SPECIAL4;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(LabText[BLUE_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Laboratory");
success = true;
}
if(rp->flags&RF_SPECIAL2){
rp->flags &= ~RF_SPECIAL2;
rp->flags |= RF_SPECIAL5;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(EnergyText[BLUE_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Energy");
success = true;
}
if(rp->flags&RF_SPECIAL3){
rp->flags &= ~RF_SPECIAL3;
rp->flags |= RF_SPECIAL6;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(RepairText[BLUE_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Repair");
success = true;
}
}else{
if(rp->flags&RF_SPECIAL4){
rp->flags &= ~RF_SPECIAL4;
rp->flags |= RF_SPECIAL1;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(LabText[RED_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Laboratory");
success = true;
}
if(rp->flags&RF_SPECIAL5){
rp->flags &= ~RF_SPECIAL5;
rp->flags |= RF_SPECIAL2;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(EnergyText[RED_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Energy");
success = true;
}
if(rp->flags&RF_SPECIAL6){
rp->flags &= ~RF_SPECIAL6;
rp->flags |= RF_SPECIAL3;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(RepairText[RED_TEAM],roomnum);
if(server)
SendTakeOverPacket(newteam,oldteam,roomnum,victor);
strcpy(room_buf,"Repair");
success = true;
}
}
if(!success){
DLLmprintf((0,"Invalid Takeover!!!!!!!\n"));
return;
}
//print out hud message (and sound?)
char buffer[256];
DLLmprintf((0,"old=%d new=%d\n",oldteam,newteam));
sprintf(buffer,TXT_TAKEOVER,(victor!=-1)?dPlayers[victor].callsign:TXT_NONAME,DMFCBase->GetTeamString(oldteam),room_buf);
DLLAddHUDMessage(buffer);
if(victor!=-1)
RemoveVirusFromPlayer(victor,false);
if(TeamOwnedRooms[oldteam]<=0){
TeamScore[newteam]+=SCORE_TEAM_WINSGAME;
Player_who_took_over_last_base = victor;
if(server){
DMFCBase->EndLevel();
}
}
int newlab;
if(!ScanForLaboratory(oldteam,&newlab)){
//we need to turn newlab into a new laboratory
int flag = 0;
room *rp = &dRooms[newlab];
if(oldteam==RED_TEAM){
rp->flags &= ~RF_SPECIAL2;
rp->flags &= ~RF_SPECIAL3;
flag = RF_SPECIAL1;
}else{
rp->flags &= ~RF_SPECIAL5;
rp->flags &= ~RF_SPECIAL6;
flag = RF_SPECIAL4;
}
rp->flags |= flag;
rp->room_change_flags |= RCF_GOALSPECIAL_FLAGS;
PaintRoomWithTexture(LabText[oldteam],newlab);
}
}
void DoPlayerInLab(int pnum,float time)
{
//nothing to do in here
}
void DoPlayerInEnergy(int pnum,float time)
{
//restore player's energy
float amount = REPAIR_RATE*time;
if(dPlayers[pnum].energy>=ENERGY_CAP)
return;
if(dPlayers[pnum].energy+amount>ENERGY_CAP)
dPlayers[pnum].energy = ENERGY_CAP;
else
dPlayers[pnum].energy += amount;
if(dPlayers[pnum].energy<ENERGY_CAP && pnum==DMFCBase->GetPlayerNum() && snd_energy_center>=0)
{
DLLPlay2dSound(snd_energy_center,1);
}
}
void DoPlayerInRepair(int pnum,float time)
{
//restore player's shields, give at a rate of 3 per second
float amount = REPAIR_RATE*time;
float curr_shields = dObjects[dPlayers[pnum].objnum].shields;
float max_amount = SHIELD_CAP - curr_shields;
if(amount>max_amount)
amount = max_amount;
if(curr_shields>=SHIELD_CAP)
return;
DMFCBase->DoDamageToPlayer(pnum,PD_ENERGY_WEAPON,-amount);
if(amount>0 && pnum==DMFCBase->GetPlayerNum() && snd_repair_center>=0)
{
DLLPlay2dSound(snd_repair_center,MAX_GAME_VOLUME*0.75f);
}
}
void DoPlayerInEnemy(int pnum,float time)
{
float damage_amount = DAMAGE_RATE*time;
if(dPlayers[pnum].flags&PLAYER_FLAGS_DYING || dPlayers[pnum].flags&PLAYER_FLAGS_DEAD)
return;
//only play sound if player is a client, else do our own sound
//so it isn't framerate dependant
if(DMFCBase->GetLocalRole()==LR_SERVER)
{
if(pnum!=DMFCBase->GetPlayerNum())
{
DMFCBase->DoDamageToPlayer(pnum,PD_ENERGY_WEAPON,damage_amount,true);
}else
{
DMFCBase->DoDamageToPlayer(pnum,PD_ENERGY_WEAPON,damage_amount,false);
//time to play sound?
float gt = DMFCBase->GetGametime();
if(gt - Server_last_play_damage_sound > (1.0f/5.0f) )//fake 5pps
{
//time to play sound
Server_last_play_damage_sound = gt;
if(snd_damage!=-1)
{
DLLPlay2dSound(snd_damage,1);
}
}
}
}
//check to see if the player has 5 virii in his inventory, if he does, he's
//trying to take over this room
if(DLLInvGetTypeIDCount(pnum,OBJ_POWERUP,virus_id)<MINIMUM_VIRUS_COUNT)
return;
if(TimeInRoom[pnum].total_time==0){
//update player position
memcpy(&TimeInRoom[pnum].last_pos,&dObjects[dPlayers[pnum].objnum].pos,sizeof(vector));
}
vector curr_pos;
memcpy(&curr_pos,&dObjects[dPlayers[pnum].objnum].pos,sizeof(vector));
if(!CompareDistanceTravel(&curr_pos,&TimeInRoom[pnum].last_pos)){
//the player has moved!!!
TimeInRoom[pnum].total_time = 0;
}else{
//still standing still
TimeInRoom[pnum].total_time += time;
}
float time_remaining = (TAKEOVER_TIME - TimeInRoom[pnum].total_time);
if(time_remaining<=0){
//Take over the room!!!!
if(DMFCBase->GetLocalRole()==LR_SERVER){
int newteam = DMFCBase->GetPlayerTeam(pnum);
int oldteam = (newteam==RED_TEAM)?BLUE_TEAM:RED_TEAM;
int room = dObjects[dPlayers[pnum].objnum].roomnum;
TakeOverRoom(newteam,oldteam,room,pnum);
}
}
}
void DoIntervalPlayerFrame(void)
{
static float last_frametime = 0;
static bool update_this_frame = false;
float frametime = 0;
if(update_this_frame){
frametime = last_frametime + DMFCBase->GetFrametime();
last_frametime = 0;
update_this_frame = false;
}else{
last_frametime = DMFCBase->GetFrametime();
update_this_frame = true;
return;
}
for(int i=0;i<DLLMAX_PLAYERS;i++){
if(DMFCBase->CheckPlayerNum(i)){
if( i==DMFCBase->GetPlayerNum() || DMFCBase->GetLocalRole()==LR_SERVER ){
if(dObjects[dPlayers[i].objnum].type!=OBJ_PLAYER)
continue;//only work with OBJ_PLAYERS...this automatically handles observers,dedicated server
//only need to be here if we are the player that has to do with this
//or we are the server
//first check to see if this player is in a special room
if(ROOMNUM_OUTSIDE(dObjects[dPlayers[i].objnum].roomnum))
continue;
int team = DMFCBase->GetPlayerTeam(i);
room *rp = &dRooms[dObjects[dPlayers[i].objnum].roomnum];
if(rp->flags&RF_SPECIAL1){
if(team==RED_TEAM)
DoPlayerInLab(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
if(rp->flags&RF_SPECIAL2){
if(team==RED_TEAM)
DoPlayerInEnergy(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
if(rp->flags&RF_SPECIAL3){
if(team==RED_TEAM)
DoPlayerInRepair(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
if(rp->flags&RF_SPECIAL4){
if(team==BLUE_TEAM)
DoPlayerInLab(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
if(rp->flags&RF_SPECIAL5){
if(team==BLUE_TEAM)
DoPlayerInEnergy(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
if(rp->flags&RF_SPECIAL6){
if(team==BLUE_TEAM)
DoPlayerInRepair(i,frametime);
else
DoPlayerInEnemy(i,frametime);
}
}
}
}
}
bool compare_slots(int a,int b)
{
int ascore,bscore;
player_record *apr,*bpr;
tPlayerStat *as,*bs;
apr = DMFCBase->GetPlayerRecord(a);
bpr = DMFCBase->GetPlayerRecord(b);
if( !apr )
return true;
if( !bpr )
return false;
as = (tPlayerStat *)apr->user_info;
bs = (tPlayerStat *)bpr->user_info;
if( !as )
return true;
if( !bs )
return false;
if( apr->state==STATE_EMPTY )
return true;
if( bpr->state==STATE_EMPTY )
return false;
if( (apr->state==STATE_INGAME) && (bpr->state==STATE_INGAME) ){
//both players were in the game
ascore = as->Score[DSTAT_LEVEL];
bscore = bs->Score[DSTAT_LEVEL];
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 = as->Score[DSTAT_LEVEL];
bscore = bs->Score[DSTAT_LEVEL];
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[NUM_TEAMS];
int team;
for(i=0;i<NUM_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;
SortedPLRPlayers[team][TeamCount[team]] = slot;
TeamCount[team]++;
}
}
for(i=0;i<NUM_TEAMS;i++){
if(TeamCount[i]<MAX_PLAYER_RECORDS)
SortedPLRPlayers[i][TeamCount[i]] = -1;
}
if(Player_who_took_over_last_base!=-1)
{
//play a fanfare!
if(snd_score!=-1)
{
DLLPlay2dSound(snd_score,MAX_GAME_VOLUME);
}
}
DMFCBase->OnPLRInit();
}
void OnPLRInterval(void)
{
DMFCBase->OnPLRInterval();
int TeamCol = 35;
int NameCol = 165;
int KillsCol = 335;
int DeathsCol = 375;
int SuicidesCol = 425;
int y = 40;
int slot;
player_record *pr;
DLLgrtext_SetFont((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]);
int height = DLLgrfont_GetHeight((DMFCBase->GetGameFontTranslateArray())[SMALL_UI_FONT_INDEX]) + 1;
if(Player_who_took_over_last_base!=-1)
{
//print out at the top who took over the last room
int team = (DMFCBase->GetPlayerTeam(Player_who_took_over_last_base)==RED_TEAM)?BLUE_TEAM:RED_TEAM;
DLLgrtext_SetColor(GR_RGB(40,255,40));
char message[384];
sprintf(message,"%s successfully took over %s team's last base!",dPlayers[Player_who_took_over_last_base].callsign,DMFCBase->GetTeamString(team));
DLLgrtext_CenteredPrintf(0,y,message);
y+=height;
}
//print out header
DLLgrtext_SetColor(GR_RGB(255,255,150));
DLLgrtext_Printf(NameCol,y,TXT_PILOT);
DLLgrtext_Printf(KillsCol,y,TXT_KILLS);
DLLgrtext_Printf(DeathsCol,y,TXT_DEATHS);
DLLgrtext_Printf(SuicidesCol,y,TXT_SUICIDES);
y+=height;
tPlayerStat *stat;
bool has_members;
bool doing_connected = true;
do_disconnected_folk:
for(int team=0;team<NUM_TEAMS;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_HUDDISPLAY,DMFCBase->GetTeamString(team),TeamScore[team]);
}
has_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){
if(DMFCBase->IsPlayerDedicatedServer(pr))
continue;//dedicated server
int pnum=DMFCBase->WasPlayerInGameAtLevelEnd(slot);
if( (doing_connected && pnum==-1) ||
(!doing_connected && pnum!=-1) )
continue;//we're not handling them right now
if(pnum!=-1)
{
DLLgrtext_SetColor(DMFCBase->GetTeamColor(team));
}else
{
DLLgrtext_SetColor(GR_RGB(128,128,128));
}
stat = (tPlayerStat *)pr->user_info;
//valid player
DLLgrtext_Printf(NameCol,y,"%s %d[%d]",pr->callsign,(stat)?stat->Score[DSTAT_LEVEL]:0,(stat)?stat->Score[DSTAT_OVERALL]:0);
DLLgrtext_Printf(KillsCol,y,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]);
DLLgrtext_Printf(DeathsCol,y,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]);
DLLgrtext_Printf(SuicidesCol,y,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]);
y+=height;
has_members = true;
if(y>=440)
goto quick_exit;
}
}//end for
//on to the next team
if(!has_members)
y+=height; //on to the next line
if(y>=440)
goto quick_exit;
}//end for
if(doing_connected)
{
doing_connected = false;
goto do_disconnected_folk;
}
quick_exit:;
}
void OnControlMessage(ubyte msg,int from_pnum)
{
switch(msg){
case VIRUS_NOTENOUGHKILLS:
DLLAddHUDMessage(TXT_CANTCARRY);
break;
case VIRUS_CANTKILL:
DLLAddHUDMessage(TXT_CANTKILLVIRUS);
break;
case VIRUS_DESTROYED:
DLLAddHUDMessage(TXT_VIRUSDESTROYED);
if(snd_virus_destroy!=-1)
{
DLLPlay2dSound(snd_virus_destroy,MAX_GAME_VOLUME);
}
break;
default:
DMFCBase->OnControlMessage(msg,from_pnum);
break;
}
}
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 *st;
int count,length,p;
//sort the stats
DMFCBase->GetSortedPlayerSlots(sortedslots,MAX_PLAYER_RECORDS);
SortTeamScores(SortedTeams,TeamScore);
count = 1;
sprintf(buffer,TXT_SAVESTATSA,(DMFCBase->GetNetgameInfo())->name,(DMFCBase->GetCurrentMission())->cur_level);
DLLcf_WriteString(file,buffer);
for(p=0;p<NUM_TEAMS;p++){
int team_i = SortedTeams[p];
memset(buffer,' ',BUFSIZE);
sprintf(tempbuffer,TXT_TEAMINFO,DMFCBase->GetTeamString(team_i));
memcpy(&buffer[0],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"[%d]",TeamScore[team_i]);
memcpy(&buffer[20],tempbuffer,strlen(tempbuffer));
buffer[20 + strlen(tempbuffer)] = '\0';
DLLcf_WriteString(file,buffer);
}
//Write team members
DLLcf_WriteString(file,""); //blank line
for(int t=0;t<NUM_TEAMS;t++){
int team_i = SortedTeams[t];
sprintf(buffer,TXT_TEAMINFO,DMFCBase->GetTeamString(team_i));
strcat(buffer,":");
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,"%s",TXT_CURRRANKINGS);
DLLcf_WriteString(file,buffer);
sprintf(buffer,"%s",TXT_SAVESTATSB);
DLLcf_WriteString(file,buffer);
sprintf(buffer,"-----------------------------------------------------------------------------");
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
st = (tPlayerStat *)pr->user_info;
memset(buffer,' ',BUFSIZE);
sprintf(tempbuffer,"%d)",count);
memcpy(&buffer[0],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%s%s",(pr->state==STATE_INGAME)?"":"*",pr->callsign);
memcpy(&buffer[5],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",(st)?st->Score[DSTAT_LEVEL]:0,(st)?st->Score[DSTAT_OVERALL]:0);
memcpy(&buffer[34],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",pr->dstats.kills[DSTAT_LEVEL],pr->dstats.kills[DSTAT_OVERALL]);
memcpy(&buffer[46],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",pr->dstats.deaths[DSTAT_LEVEL],pr->dstats.deaths[DSTAT_OVERALL]);
memcpy(&buffer[58],tempbuffer,strlen(tempbuffer));
sprintf(tempbuffer,"%d[%d]",pr->dstats.suicides[DSTAT_LEVEL],pr->dstats.suicides[DSTAT_OVERALL]);
memcpy(&buffer[69],tempbuffer,strlen(tempbuffer));
int pos;
pos = 69 + strlen(tempbuffer) + 1;
if(pos<BUFSIZE)
buffer[pos] = '\0';
buffer[BUFSIZE-1] = '\0';
DLLcf_WriteString(file,buffer);
count++;
}
}
DLLcf_WriteString(file,TXT_SAVESTATSC);
count =1;
for(p=0;p<MAX_PLAYER_RECORDS;p++){
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_TIMEINGAME,DMFCBase->GetTimeString(DMFCBase->GetTimeInGame(p)));
DLLcf_WriteString(file,buffer);
if(DMFCBase->FindPInfoStatFirst(p,&stat)){
sprintf(buffer,"%s",TXT_SAVESTATSD);
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_SAVED);
}
#define ROOTFILENAME "Entropy"
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 DisplayHUDScores(struct tHUDItem *hitem)
{
if(display_my_welcome)
{
DisplayWelcomeMessage(DMFCBase->GetPlayerNum());
display_my_welcome = false;
}
if(DisplayScoreScreen)
return;
// Display the countdown if there should be one
if(IsPlayerInEnemy(DMFCBase->GetPlayerNum()) && DLLInvGetTypeIDCount(DMFCBase->GetPlayerNum(),OBJ_POWERUP,virus_id)>=MINIMUM_VIRUS_COUNT)
{
float time_remaining = (TAKEOVER_TIME - TimeInRoom[DMFCBase->GetPlayerNum()].total_time);
int font=(DMFCBase->GetGameFontTranslateArray())[BIG_FONT_INDEX];
DLLgrtext_SetFont(font);
int fontheight=DLLgrfont_GetHeight(font);
char buffer[255];
int max_w=DMFCBase->GetGameWindowW();
sprintf(buffer,"T - %.1f",(time_remaining>=0)?time_remaining:0);
int lwidth = DLLgrtext_GetTextLineWidth(buffer);
DLLgrtext_SetColor(GR_RGB(40,255,40));
DLLgrtext_SetAlpha(255);
DLLgrtext_Printf((max_w/2) - (lwidth/2),fontheight*5,buffer);
}
int font = (DMFCBase->GetGameFontTranslateArray())[HUD_FONT_INDEX];
int height = DLLgrfont_GetHeight(font) + 3;
DLLgrtext_SetFont(font);
int y = (DMFCBase->GetGameWindowH()/2) - ((height*3)/2);
int x = 520;
int team;
ubyte alpha = DMFCBase->ConvertHUDAlpha((ubyte)((DisplayScoreScreen)?128:255));
team = DMFCBase->GetMyTeam();
DLLRenderHUDText(DMFCBase->GetTeamColor(team),alpha,0,x,0,TXT_TEAMINFO,DMFCBase->GetTeamString(team));
//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 = 100.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;
char buffer[256];
for(int i=0;i<NUM_TEAMS;i++){
team = SortedTeams[i];
if(team==DMFCBase->GetMyTeam())
{
if(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,-1,NULL);
DLLrend_SetZBufferState (1);
}
}
sprintf(buffer,TXT_HUDSCORE,DMFCBase->GetTeamString(team),TeamOwnedRooms[team]);
DMFCBase->ClipString(name_width,buffer,true);
DLLgrtext_SetAlpha(alpha);
DLLgrtext_SetColor(DMFCBase->GetTeamColor(team));
DLLgrtext_Printf(name_x,y,buffer);
DLLgrtext_Printf(score_x,y,"[%d]",TeamScore[team]);
y+=height;
}
DLLgrtext_SetColor(GR_WHITE);
DLLgrtext_Printf(name_x,y,TXT_VIRUSLOAD,DLLInvGetTypeIDCount(DMFCBase->GetPlayerNum(),OBJ_POWERUP,virus_id),(int)(NumberOfKillsSinceLastDeath[DMFCBase->GetPlayerNum()]*VIRUS_PER_KILL)); y+=height;
}
// insert sort
#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<NUM_TEAMS;i++)
{
sortedindex[i] = i;
}
for(i=1;i<=NUM_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())
{
int team = DMFCBase->GetMyTeam();
if(team==-1)
return;
DLLAddHUDMessage(TXT_WELCOME,name_buffer);
DLLAddColoredHUDMessage(DMFCBase->GetTeamColor(team),TXT_TEAMJOIN,DMFCBase->GetTeamString(team));
}
else
{
int team = dPlayers[player_num].team;
if(team==-1)
return;
DLLAddColoredHUDMessage(DMFCBase->GetTeamColor(team),TXT_JOIN,name_buffer,DMFCBase->GetTeamString(team));
}
}
void OnLabSpewTimer(void)
{
int max_rooms = DMFCBase->GetHighestRoomIndex() + 1;
int objnum;
int index;
int virus_count;
for(int r=0;r<RoomCount;r++){
if(dRooms[RoomList[r]].flags&RF_SPECIAL1){
index = -1;
virus_count = 0;
for(int v=0;v<MAX_VIRII;v++){
if(TeamVirii[RED_TEAM][v]==-1){
//insert here
index = v;
}else
{
if(dObjects[TeamVirii[RED_TEAM][v]].roomnum==RoomList[r])
virus_count++;
}
}
//only create virus if we found a slot and there are less than MAX_VIRII_PER_ROOM
//in the room
if(virus_count<MAX_VIRII_PER_ROOM && index!=-1){
objnum = SpewObjectInRoom(OBJ_POWERUP,virus_id,RoomList[r]);
TeamVirii[RED_TEAM][index] = objnum;
}
}
if(dRooms[RoomList[r]].flags&RF_SPECIAL4){
index = -1;
virus_count = 0;
for(int v=0;v<MAX_VIRII;v++){
if(TeamVirii[BLUE_TEAM][v]==-1){
//insert here
index = v;
}else
{
if(dObjects[TeamVirii[BLUE_TEAM][v]].roomnum==RoomList[r])
virus_count++;
}
}
//only create virus if we found a slot and there are less than MAX_VIRII_PER_ROOM
//in the room
if(virus_count<MAX_VIRII_PER_ROOM && index!=-1)
{
objnum = SpewObjectInRoom(OBJ_POWERUP,virus_id,RoomList[r]);
TeamVirii[BLUE_TEAM][index] = objnum;
}
}
}
}
void RemoveVirusFromPlayer(int player_num,bool remove_all)
{
int inv_count = DLLInvGetTypeIDCount(player_num,OBJ_POWERUP,virus_id);
if(!remove_all)
{
ASSERT(inv_count>=MINIMUM_VIRUS_COUNT);
inv_count = MINIMUM_VIRUS_COUNT;
}
for(int i=0;i<inv_count;i++){
DLLInvRemove(player_num,OBJ_POWERUP,virus_id);
}
}
//returns true if there is at least 1 lab, else returns the room which sould be a lab in newlab
bool ScanForLaboratory(int team,int *newlab)
{
*newlab = 0;
int flag;
int ef,rf;
bool found = false;
flag = (team==RED_TEAM)?RF_SPECIAL1:RF_SPECIAL4;
ef = (team==RED_TEAM)?RF_SPECIAL2:RF_SPECIAL5;
rf = (team==RED_TEAM)?RF_SPECIAL3:RF_SPECIAL6;
for(int r=0;r<RoomCount;r++){
room *rp = &dRooms[RoomList[r]];
if(rp->flags&flag){
//we found a lab
return true;
}
if( (rp->flags&ef) && (!found) ){
found = true;
*newlab = RoomList[r];
}
if( (rp->flags&rf) && (!found) ){
found = true;
*newlab = RoomList[r];
}
}
return false;
}
bool CompareDistanceTravel(vector *curr_pos,vector *last_pos)
{
vector result;
float a,b,c,bc,dist;
result.x = curr_pos->x - last_pos->x;
result.y = curr_pos->y - last_pos->y;
result.z = curr_pos->z - last_pos->z;
a = fabs(result.x);
b = fabs(result.y);
c = fabs(result.z);
if (a < b) {
float t=a; a=b; b=t;
}
if (b < c) {
float t=b; b=c; c=t;
if (a < b) {
float t=a; a=b; b=t;
}
}
bc = (b/4) + (c/8);
dist = a + bc + (bc/2);
if(dist>5){
memcpy(last_pos,curr_pos,sizeof(vector));
return false;
}else{
return true;
}
}
bool IsPlayerInEnemy(int pnum)
{
if(!DMFCBase->CheckPlayerNum(pnum))
return false;
if(dObjects[dPlayers[pnum].objnum].type!=OBJ_PLAYER)
return false;//only work with OBJ_PLAYERS...this automatically handles observers,dedicated server
//first check to see if this player is in a special room
if(ROOMNUM_OUTSIDE(dObjects[dPlayers[pnum].objnum].roomnum))
return false;
int team = DMFCBase->GetPlayerTeam(pnum);
room *rp = &dRooms[dObjects[dPlayers[pnum].objnum].roomnum];
if(team!=RED_TEAM)
{
if( (rp->flags&RF_SPECIAL1) || (rp->flags&RF_SPECIAL2) || (rp->flags&RF_SPECIAL3) )
{
return true;
}
}
if(team!=BLUE_TEAM)
{
if( (rp->flags&RF_SPECIAL4) || (rp->flags&RF_SPECIAL5) || (rp->flags&RF_SPECIAL6) )
{
return true;
}
}
return false;
}
void OnGetTokenString(char *src,char *dest,int dest_size)
{
if(!stricmp(src,"virus"))
{
int count = DLLInvGetTypeIDCount(DMFCBase->GetPlayerNum(),OBJ_POWERUP,virus_id);
char buffer[100];
sprintf(buffer,"%d",count);
strncpy(dest,buffer,dest_size-1);
dest[dest_size-1] = '\0';
return;
}
DMFCBase->OnGetTokenString(src,dest,dest_size);
}
void OnPrintScores(int level)
{
char buffer[256];
char name[70];
int t,i;
int pos[6];
int len[6];
netplayer *dNetPlayers = DMFCBase->GetNetPlayers();
for(i=0;i<NUM_TEAMS;i++)
{
sprintf(buffer,"%s:%d\n",DMFCBase->GetTeamString(i),TeamScore[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_SCORE);
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_SCORE,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);
}
}
}
#ifdef MACINTOSH
#pragma export off
#endif