/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/dmfc/dmfcbase.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003/08/26 03:57:20 $ * $Author: kevinb $ * * Base functions for DMFC implementation * * $Log: dmfcbase.cpp,v $ * Revision 1.1.1.1 2003/08/26 03:57:20 kevinb * initial 1.5 import * * * 170 9/28/01 2:09p Matt * Don't show ping/loss info on HUD if peer-to-peer game. * * 169 9/07/01 8:57a Matt * Fixed hud name bug from previous change. * * 168 9/05/01 6:04p Matt * Added code to save the user's preferred HUD name level setting even if * the server bashes it down. * * 167 10/26/99 10:31a Jeff * fixed COM interface bug * * 166 10/21/99 9:27p Jeff * B.A. Macintosh code merge * * 165 8/21/99 12:33a Jeff * Changed the name of the GetRealGametime function to * GetRealGametimePacket since it was confusing some compilers with the * other GetRealGametime function. * * 164 8/15/99 4:36p Jeff * finished exporting all inventory class functions. export object_info * array. added check for -nooutragelogo to display Outrage logo display. * * 163 8/11/99 6:36p Jeff * don't set input command list root to NULL when creating input commands * (it would create a memleak and ignore any ICs added by a custom mod) * * 162 8/11/99 1:21p Jeff * exported needed functions for camera windows * * 161 7/30/99 11:59a Jeff * fixed bug with autobalance being turned off * * 160 7/14/99 11:47a Jeff * localized text for patch fixes * * 159 7/13/99 10:05a Jeff * text taunt token decoding * * 158 7/11/99 3:31p Jeff * exported game arguments, made command line option to specify * autoexec.dmfc * * 157 7/09/99 7:02p Jeff * put in countdown timer for when a level is about to end * * 156 7/09/99 6:17p Jeff * added $remoteadminlogout and $wait commands * * 155 7/09/99 2:53p Jeff * handle gametime better (pause it when needed) if the server is 'waiting * for players' * * 154 7/08/99 6:25p Jeff * remote admin in and working * * 153 7/08/99 2:39a Jeff * rough implementation of remote administration checked in. Still needs * some polishing, but should work basically. * * 152 7/07/99 5:00p Jeff * removed vararg functions from interface functions, just made different * versions of them * * 151 6/11/99 5:36p Jeff * removed ai_info #ifdefs (better way of doing it) * * 150 6/10/99 12:34p Jeff * GetCounterMeasureOwner doesn't use ->ai_info for non-Outrage versions * * 149 6/10/99 11:10a Jeff * don't display the Outrage logo for non-Outrage games * * 148 5/22/99 1:12a Jeff * correctly handle Viewer_object * * 147 5/20/99 5:32p Jeff * called PlayerStopSounds if respawning a player * * 146 5/20/99 3:51p Jeff * for apply damage to player, server says should be 1 (so it works in * peer-peer) * * 145 5/13/99 4:55p Ardussi * changes for compiling on the Mac * * 144 5/12/99 11:04p Jeff * dmfc and multiplayer games now have endian friendly packets (*whew*) * * 143 5/10/99 2:43a Jeff * handle new scheme of player's joining in a team game, where the team is * set before player enters game in the main code, but the team is * determined via event call to dmfc * * 142 5/09/99 6:20a Jeff * improved Entropy (added sounds, max virii per room). Fixed rendering * bugs for other multiplayer dlls. * * 141 5/08/99 11:06a Jeff * * 140 5/07/99 4:34p Jason * fixed audio taunt icon * * 139 5/07/99 12:52p Jeff * audio taunt icon is ppic if available. coop has hard max team set of 4 * * 138 5/04/99 8:46p Jeff * display icon when someone plays an audio taunt * * 137 5/03/99 8:39a Jeff * fixed apply damage to player * * 136 4/30/99 10:52p Jeff * added $warp command * * 135 4/30/99 7:36p Jeff * exported vis_effects to dmfc * * 134 4/27/99 1:56p Jeff * audio taunts stuff in pilot menu, added stringtables * * 133 4/26/99 3:41p Jeff * put in debug multiplayer command dump to file * * 132 4/25/99 7:18p Jeff * added code to handle suicides in pinfo * * 131 4/23/99 6:15p Jeff * fixed double calls to GameClose * * 130 4/23/99 4:49p Jason * played with loss colors a bit * * 129 4/23/99 4:24p Jason * fixed ping display bug * * 128 4/23/99 12:46p Jeff * lag loss indicator on by default * * 127 4/22/99 11:28a Jeff * changed percent sign as a result of Samir's change * * 126 4/21/99 10:35p Jeff * fixed lag/loss displays * * 125 4/20/99 8:57p Jeff * compile for Linux * * 124 4/14/99 3:03p Jeff * * 123 4/12/99 11:39a Jeff * removed movement smoothing, added permissable cs to netgame info * * 122 4/04/99 4:55p Jeff * added functionality to call osiris functions from multiplayer d3ms * * 121 4/03/99 4:06p Jeff * added loss/ping gauge * * 120 4/02/99 9:02p Jeff * fixed crashes if there was an error initializing module * * 119 3/30/99 9:01p Jeff * exported polymodels * * 118 3/30/99 7:42p Jeff * fixed a misspelling on a function name * * 117 3/22/99 6:20p Jeff * added 2 more audio taunts. a mulitplayer event when someone plays an * audio taunt. option to disable audio taunts. * * 116 3/22/99 1:55p Jeff * make sure initialization happens (possible crashing) * * 115 3/17/99 12:23p Jeff * converted DMFC to be COM interface * * 114 3/09/99 1:13p Jeff * fixed control packet for wait being overflowed out and accidently sent * by the client * * 113 3/05/99 1:29p Jeff * fixed 99% of the high res issues * * 112 3/01/99 8:48p Jeff * fixed banning bug (when not a master tracker game) * * 111 2/25/99 8:54p Jeff * Inventory supports level change persistant items. Inventory supports * time-out objects. Inventory Reset changed (takes a level of reset * now). Quad lasers stay across level change (single player). Guidebot * bug fixed (now back in ship on level start). Quads time out when * spewed. Invulnerability and cloak powerups no longer use game * event/callbacks, so they can be saved in game saves (moved to * MakePlayerInvulnerable and MakeObjectInvisible) * * 110 2/11/99 12:50a Jeff * changed names of exported variables * * 109 2/10/99 1:47p Matt * Changed object handle symbolic constants * * 108 2/09/99 3:32p Jeff * table file parser takes quotes strings for force keywords * * 107 2/07/99 2:06a Jeff * updated coop...fixed bug when getting countermeasure owner, if owner is * observer * * 106 2/07/99 1:19a Jeff * added new multiplayer game events EVT_GAMEOBJKILLED and * EVT_GAMEOBJDESTROYED * * 105 2/05/99 8:24p Jeff * added table file parser macros * * 104 2/03/99 4:09p Jeff * moved function pointers to seperate file. created autoexec.dmfc * * 103 2/02/99 8:43a Chris * I made buildings with AI work correctly (ie really big robots should be * buildings) * anim to and from states are now shorts instead of bytes * * 102 1/31/99 7:26p Matt * Renamed a bunch of functions to have HUD capitalized * * 101 1/21/99 11:16p Jeff * exported vecmat functions * * 100 1/20/99 8:06p Jeff * added members into DLLinfo struct for game change segment events, pass * them over on execute dll packets * * 99 1/19/99 5:34p Jeff * updated monsterball * * 98 1/18/99 7:27p Jeff * localized strings in dmfcbase.cpp * * 97 1/17/99 11:52p Jeff * added some new events, and changed a couple event handlers * * 96 1/15/99 8:29p Jeff * updates to powerball * * 95 1/15/99 7:52p Chris * Updated ObjSetPos() to include a f_update_attach_children flag * * 94 1/15/99 3:53a Jeff * exported a couple more functions. Added event handlers for Weapon * collide event * * 93 1/12/99 2:55p Jeff * added/finished the waiting for player's to join dialog * * 92 1/12/99 11:29a Jason * fixed a broken externed function * * 91 1/07/99 5:01p Jeff * added Int3's and updated all net games to use stats manager...correctly * too * * 90 1/06/99 7:02p Jeff * added a multiplayer event for game controls * * 89 1/06/99 12:53a Jeff * put in support for $piggyback and $observer * * 88 1/04/99 8:11p Jason * fixed packet loss tracking problem * * 87 1/04/99 2:19p Jeff * exported table file management functions * * 86 1/04/99 12:21p Jeff * added support for hosts.allow/deny and updates stats manager a little * * 85 12/13/98 5:32p Jeff * fixed ugly crash due to freeing memory allocated in another heap * * 84 12/09/98 12:38p Jeff * removed possible security bug displaying a player's ip address (now * server only can see it) * * 83 12/08/98 4:47p Jeff * umm, various changes, fixed pilot pics so that they work for everyone * now * * 82 12/08/98 3:29p Jeff * updated the team control dialog so the server can determine if they * want to make the clients wait * * 81 12/08/98 12:17p Jeff * various changes that include an improved Team Control dialog (doesn't * switch teams until exit..) and spew/respawn players that change teams * * 80 12/04/98 7:04p Jeff * almost finished up dmfc stat manager * * 79 12/01/98 6:56p Jeff * put in quick and dirty implementation of pilot pics for testing * * 78 11/19/98 5:56p Jeff * added slider exported and improved Hoard * * 77 11/17/98 6:29p Jeff * mod can specify whether or not to display the team setup dialog on team * game start. Added a menu item to display team setup dialog in mid-game * * 76 11/17/98 12:36p Jeff * fixed dedicated server detection and display a dprintf for setting new * team * * 75 11/16/98 5:35p Jeff * removed log functions, added support for changing team names, fixed ctf * to work better with different team names * * 74 11/13/98 6:36p Jeff * created dmfc_dll (a DLL version of DMFC) and converted current mods to * use it * * 73 11/12/98 12:16p Jeff * more changes to handle (ignore) dedicated server * * 72 11/11/98 7:19p Jeff * changes made so that a dedicated server's team is always -1 (team game * or not) * * 71 11/02/98 4:38p Jeff * added ability to sort and display by efficiency * * 70 11/01/98 1:59a Jeff * made a $help inputcommand for help in a dedicated server environment * * 69 10/30/98 12:47p Jeff * cut down a couple bytes on memory usage * * 68 10/29/98 7:01p Jeff * creation of team placement dialog. Moved TranslateEvent into DMFC * * 67 10/24/98 2:35p Matt * Changed "callsign" to "name" or "player name" in the multiplayer menus * and commands. * * 66 10/24/98 2:18p Jeff * * 65 10/23/98 6:51p Jeff * fixed hud num of teams sort in ctf, and memory overwrite in registry * * 64 10/23/98 11:22a Jeff * changes to handle mixcase, and display the client netgame info * correctly * * 63 10/21/98 5:02p Jeff * removed player from HUD if in observer nide * * 62 10/20/98 4:35p Jeff * added a flag for menu to add a to MIT_PLIST... * * 61 10/20/98 12:16p Jeff * added death message filter, hud callsign filter * * 60 10/19/98 7:19p Matt * Added system to support different types of damage to the player and * have these different types make different sounds. * * 59 10/18/98 7:59p Jeff * functions added to dmfc for client->server objnum matching. Banning * now uses tracker id when available. * * 58 10/17/98 7:30p Jeff * network_address compares don't compare port on somethings * * 57 10/15/98 6:18p Jeff * created the is player banned event, removed prejoin event * * 56 10/15/98 1:34p Jeff * added scrollable onscreen menu. Remove ban in dmfc. prejoin event * * 55 10/13/98 6:01p Jeff * added attaching * * 54 10/13/98 2:15a Jeff * created new event for when a player leaves a multiplayer game. Fixed * some 'english' bugs in the network games. * * 53 10/08/98 3:37p Jeff * general improvements (Netgame info things, save to registry). Changes * so it would send packets on NETSEQ_OBJECTS * * 51 10/05/98 2:49p Jeff * * 50 10/02/98 6:10p Jeff * * 49 10/01/98 11:30a Jeff * made the observer mode events into just a client event * * 48 9/30/98 4:21p Jeff * team changing is handled correctly * * 47 9/30/98 3:50p Jeff * general improvements (many) * * 46 9/29/98 3:04p Jeff * added time in game and start_time support * * 45 9/28/98 5:05p Jeff * made the statisitical death messages an option in the menu * * 44 9/25/98 7:25p Jeff * * 43 9/25/98 4:50p Jeff * * 42 9/24/98 6:54p Jeff * added DisconnectMe() and made use of it when you get kicked/banned * * 41 9/24/98 6:29p Jeff * * 40 9/24/98 5:52p Jeff * starting adding statistical death messages, checked in for testing * * 39 9/23/98 5:27p Jeff * fixed death message bug (using objnum instead of pnum) * * 38 9/23/98 4:17p Jeff * basic changes/improvements, started changing death messages * * 37 9/21/98 7:11p Jeff * made InputCommand interface API and moved existing input commands to * the interface. Changed mprintf/ASSERT so they are valid in DMFC * * $NoKeywords: $ */ #include "gamedll_header.h" #include "DMFC.h" #include "dmfcinternal.h" #include "dmfcinputcommands.h" #include #include #include char **DMFCStringTable; int DMFCStringTableSize = 0; const char *_DMFCErrorString = "DMFC Missing String"; uint8_t seeds1[31] = {49, 73, 0, 44, 87, 253, 35, 74, 62, 250, 4, 247, 251, 72, 244, 30, 59, 61, 60, 52, 50, 237, 23, 48, 56, 55, 65, 232, 231, 230, 0}; uint8_t seeds2[6] = {70, 95, 103, 102, 112, 0}; const char *DMFCGetString(int d) { if ((d < 0) || (d >= DMFCStringTableSize)) return _DMFCErrorString; else return DMFCStringTable[d]; } DMFCBase::DMFCBase(void) { m_cPtrs = 0; m_iServerHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; m_iProtectedFlags = 0; m_bHasInitialized = false; LossGuageEnabled = true; DMFCInit = false; RealGametime = 0; DedicatedLevelWait = 0; m_bDisplayTimelimitCountdown = true; m_iTimeLimitCountdown = 10; ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PLRFIRSTTIME | DMFC_PRF_CANTEAMCHANGE); ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND); ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT | DMFC_PRF_AUTODEATHMSG | DMFC_PRF_AUTOTEAMSELECT | DMFC_PRF_DISPSTATHUDMSGS); DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_INOPTIONS | DMFC_PRF_DISPPLAYERINFO | DMFC_PRF_DISPNETGAMEINFO); DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC | DMFC_PRF_DISPMENUBACKGR | DMFC_PRF_PAUSETIME); m_iNumBanPlayers = 0; m_bMakeClientsWait = false; m_iPlayerDisplayed = 0; DatabaseRegisteredName[0] = '\0'; SPRoot = NULL; // no special packet handlers yet int i; for (i = 0; i < MAX_DEATH_MSGS; i++) { DeathMsgs[i].inuse = false; DeathMsgs[i].message = NULL; SuicideMsgs[i].inuse = false; SuicideMsgs[i].message = NULL; } for (i = 0; i < MAX_WEAPONS; i++) { WeaponMessages[i].inuse = false; WeaponMessages[i].message = NULL; WeaponHash[i] = -1; } m_iDeathMsgCount = 0; m_iSuicideMsgCount = 0; m_iUIWindowID = -1; m_UIUserData = NULL; m_DenyList = NULL; m_AllowList = NULL; m_InputCommandRootNode = NULL; Hard_max_players = DLLMAX_PLAYERS; DecryptData(seeds2, 5); TeamMenuItem = NULL; AutoBalanceItem = NULL; AllowTeamChangeItem = NULL; ObserverItem = NULL; m_BanList = NULL; StatisticMessagesItem = NULL; SaveStatsLevelEndItem = NULL; SaveStatsDisconnectItem = NULL; NetGameInfoItem = NULL; MenuBackgroundItem = NULL; ServerHUDCallsignsItem = NULL; HUDCallsignsItem = NULL; DeathMessageFilterItem = NULL; ShipLogosItem = NULL; HUDIndicatorItem = NULL; DisplayTauntIndicator = false; TauntIndicatorPlayerNum = -1; TauntIndicatorStartTime = 0; TauntIndicatorBMP = -1; for (i = 0; i < DLLMAX_TEAMS; i++) DMFC_team_names[i][0] = '\0'; for (i = 0; i < DLLMAX_PLAYERS; i++) { PilotPicBmpHandles[i] = BAD_BITMAP_HANDLE; } InitializeVirtualHandles(); } DMFCBase::~DMFCBase(void) { int i; ResetPInfo(); RemoveAllBans(); for (i = 0; i < DLLMAX_PLAYERS; i++) { if (PilotPicBmpHandles[i] > BAD_BITMAP_HANDLE) { DLLbm_FreeBitmap(PilotPicBmpHandles[i]); PilotPicBmpHandles[i] = BAD_BITMAP_HANDLE; } } if (TauntIndicatorBMP > BAD_BITMAP_HANDLE) { DLLbm_FreeBitmap(TauntIndicatorBMP); TauntIndicatorBMP = BAD_BITMAP_HANDLE; } for (i = 0; i < MAX_DEATH_MSGS; i++) { if (DeathMsgs[i].inuse) { if (DeathMsgs[i].message) free(DeathMsgs[i].message); DeathMsgs[i].inuse = false; } if (SuicideMsgs[i].inuse) { if (SuicideMsgs[i].message) free(SuicideMsgs[i].message); SuicideMsgs[i].inuse = false; } } for (i = 0; i < MAX_WEAPONS; i++) { if (WeaponMessages[i].inuse) { if (WeaponMessages[i].message) { free(WeaponMessages[i].message); WeaponMessages[i].message = NULL; } } } // remove all the special packet handlers tSPHandler *current, *next; current = next = SPRoot; while (current) { next = current->next; free(current); current = next; } SPRoot = NULL; } // ##################Setup and processing functions####################### // DMFCBase::InitializeForLevel // // void DMFCBase::InitializeForLevel(void) { if (m_bHasInitialized) return; // Reset the timers InitTimers(); m_bHasInitialized = true; } // DMFCBase::LoadSettings // // Loads the settings of DMFC from the registry and initializes those variables void DMFCBase::LoadSettings(void) { // set default values ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT | DMFC_PRF_AUTODEATHMSG | DMFC_PRF_AUTOTEAMSELECT | DMFC_PRF_DISPSTATHUDMSGS); ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND | DMFC_PRF_CANTEAMCHANGE); DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC | DMFC_PRF_DISPMENUBACKGR); m_iServerHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel = HUD_CALLSIGN_LEVEL_FULL; m_iDeathMessageFilter = DM_FILTER_FULL; LossGuageEnabled = true; bool bTemp; if (DatabaseRead3("DMFCAutoTimeLimit", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT); } if (DatabaseRead3("DMFCAutoDeathMessage", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTODEATHMSG); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTODEATHMSG); } if (DatabaseRead3("DMFCAutoTeamSelect", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTEAMSELECT); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTEAMSELECT); } if (DatabaseRead3("DMFCStatHUDMessages", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPSTATHUDMSGS); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPSTATHUDMSGS); } if (DatabaseRead3("DMFCAutoSaveStatLevelEnd", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND); } if (DatabaseRead3("DMFCAutoSaveStatDisconnect", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC); } if (DatabaseRead3("DMFCMenuBackground", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPMENUBACKGR); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPMENUBACKGR); } if (DatabaseRead3("DMFCCanChangeTeams", &bTemp)) { if (bTemp) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_CANTEAMCHANGE); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_CANTEAMCHANGE); } DatabaseRead3("DMFCLagLossIndicator", &LossGuageEnabled); DatabaseRead2("DMFCShowHudCallsigns", &m_iMyPreferredHUDCallsignLevel, sizeof(m_iMyPreferredHUDCallsignLevel)); DatabaseRead2("DMFCServerShowHudCallsigns", &m_iServerHUDCallsignLevel, sizeof(m_iServerHUDCallsignLevel)); DatabaseRead2("DMFCDeathMessageFilter", &m_iDeathMessageFilter, sizeof(m_iDeathMessageFilter)); m_iMyCurrentHUDCallsignLevel = m_iMyPreferredHUDCallsignLevel; } // DMFCBase::SaveSettings // // Saves out the settings of DMFC to the registry void DMFCBase::SaveSettings(void) { bool bTemp; bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_AUTOTIMELIMIT) != 0); DatabaseWrite2("DMFCAutoTimeLimit", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_AUTODEATHMSG) != 0); DatabaseWrite2("DMFCAutoDeathMessage", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_AUTOTEAMSELECT) != 0); DatabaseWrite2("DMFCAutoTeamSelect", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_DISPSTATHUDMSGS) != 0); DatabaseWrite2("DMFCStatHUDMessages", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_AUTOSAVELEVELEND) != 0); DatabaseWrite2("DMFCAutoSaveStatLevelEnd", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_AUTOSAVEDISC) != 0); DatabaseWrite2("DMFCAutoSaveStatDisconnect", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_DISPMENUBACKGR) != 0); DatabaseWrite2("DMFCMenuBackground", bTemp); bTemp = (bool)((m_iProtectedFlags & DMFC_PRF_CANTEAMCHANGE) != 0); DatabaseWrite2("DMFCCanChangeTeams", bTemp); DatabaseWrite2("DMFCLagLossIndicator", LossGuageEnabled); DatabaseWrite2("DMFCShowHudCallsigns", m_iMyPreferredHUDCallsignLevel); DatabaseWrite2("DMFCServerShowHudCallsigns", m_iServerHUDCallsignLevel); DatabaseWrite2("DMFCDeathMessageFilter", m_iDeathMessageFilter); } // DMFCBase::LoadFunctions // // Initialized all the functions for a multiplayer game...must be first thing called void DMFCBase::LoadFunctions(int *api_func) { #include "dmfcdllinit.h" } // DMFCBase::GameInit // // Sets up all the DLL functions and pointers and preps the class for use. This ABSOLUTLY must be // called, so if you override DMFCApp::GameInit, make sure that you put a call to this somewhere in // the override. void DMFCBase::GameInit(int teams) { ASSERT(!DMFCInit); DMFCInit = true; LoadSettings(); DLLCreateStringTable("dmfc.str", &DMFCStringTable, &DMFCStringTableSize); mprintf((0, "DMFC Note: %d strings loaded from string table\n", DMFCStringTableSize)); // initialize player records PRec_Init(); // initialize remote administration Remote_Initialize(); // see if we should display Outrage logo at all if (DLLFindArg("-nooutragelogo")) m_bDisplayOutrageLogo = false; else m_bDisplayOutrageLogo = true; SetNumberOfTeams(teams); SetTeamName(RED_TEAM, DTXT_RED, false); SetTeamName(BLUE_TEAM, DTXT_BLUE, false); SetTeamName(GREEN_TEAM, DTXT_GREEN, false); SetTeamName(YELLOW_TEAM, DTXT_YELLOW, false); // Load bitmaps hBitmapObserver = DLLbm_AllocLoadFileBitmap("observer icon.ogf", 0, BITMAP_FORMAT_1555); if (hBitmapObserver <= BAD_BITMAP_HANDLE) hBitmapObserver = BAD_BITMAP_HANDLE; RegisterPacketReceiver(SPID_TEAMASSIGNMENT, &DMFCBase::GetTeamAssignmentPacket); RegisterPacketReceiver(SPID_CHANGETEAM, &DMFCBase::GetChangeTeamPacket); RegisterPacketReceiver(SPID_REQGAMESTATE, &DMFCBase::GetGameStateRequest); RegisterPacketReceiver(SPID_DMFCGAMEINFO, &DMFCBase::GetDMFCGameInfo); RegisterPacketReceiver(SPID_VERSIONID, &DMFCBase::GetDMFCVersionCheck); RegisterPacketReceiver(SPID_PRECORD, PRec_ReceivePRecFromServer); RegisterPacketReceiver(SPID_PRECORDREQ, &DMFCBase::ReceiveRequestForPlayerRecords); RegisterPacketReceiver(SPID_CONTROLMSG, &DMFCBase::ReceiveControlMessage); RegisterPacketReceiver(SPID_NETGAMESYNC, &DMFCBase::ReceiveNetGameInfoSync); RegisterPacketReceiver(SPID_NEWTEAMNAME, &DMFCBase::ReceiveNewTeamName); RegisterPacketReceiver(SPID_REMOTEKEY, &DMFCBase::GetRemoteKey); RegisterPacketReceiver(SPID_REALGAMETIME, &DMFCBase::GetRealGametimePacket); AddWeaponHash("Laser", "Laser Level 1 - Red", "Laser Level 2 - Blue", "Laser Level 3 - Purple", "Laser Level 4 - Green", NULL); AddWeaponHash("Vauss", "Vauss Spark", NULL); AddWeaponHash("Raygun", NULL); AddWeaponHash("Plasma", "plasma sparks", NULL); AddWeaponHash("Fusion", NULL); AddWeaponHash("Super Laser", "Laser Level 5 -Yellow", "Laser Level 6 -White", NULL); AddWeaponHash("Mass Driver", NULL); AddWeaponHash("Napalm", NULL); AddWeaponHash("EMDyellowleft", "EMDyellowright", "EMDgreenleft", "EMDgreenright", NULL); AddWeaponHash("Omega", NULL); AddWeaponHash("Concussion", NULL); AddWeaponHash("Homing", NULL); AddWeaponHash("Smart", "SmartPlasmaHomers", NULL); AddWeaponHash("Mega", "MegaExplosion", NULL); AddWeaponHash("Frag", "frag particles", NULL); AddWeaponHash("Guided", NULL); AddWeaponHash("NapalmRocket", "NapalmBlob", "NapalmParticles", NULL); AddWeaponHash("Cyclone Pack", "Swarm", NULL); AddWeaponHash("Yellow Flare", NULL); SetWeaponDeathMessage("Laser", DTXT_DEATHLASER, false); SetWeaponDeathMessage("Vauss", DTXT_DEATHVAUSS, false); SetWeaponDeathMessage("RayGun", DTXT_DEATHMICROWAVE, true); SetWeaponDeathMessage("Plasma", DTXT_DEATHPLASMA, false); SetWeaponDeathMessage("Fusion", DTXT_DEATHFUSION, false); SetWeaponDeathMessage("Super Laser", DTXT_DEATHSUPERLASER, false); SetWeaponDeathMessage("Mass Driver", DTXT_DEATHMASS, false); SetWeaponDeathMessage("Napalm", DTXT_DEATHNAPALM, false); SetWeaponDeathMessage("EMDyellowleft", DTXT_DEATHEMD, false); SetWeaponDeathMessage("Omega", DTXT_DEATHOMEGA, false); SetWeaponDeathMessage("Concussion", DTXT_DEATHCONC, true); SetWeaponDeathMessage("Homing", DTXT_DEATHHOMING, false); SetWeaponDeathMessage("Smart", DTXT_DEATHSMART, true); SetWeaponDeathMessage("Mega", DTXT_DEATHMEGA, false); SetWeaponDeathMessage("Frag", DTXT_DEATHFRAG, true); SetWeaponDeathMessage("Guided", DTXT_DEATHGUIDED, false); SetWeaponDeathMessage("NapalmRocket", DTXT_DEATHNAPALMROCKET, true); SetWeaponDeathMessage("Cyclone Pack", DTXT_DEATHCYCLONE, false); SetWeaponDeathMessage("Yellow Flare", DTXT_DEATHFLARE, false); /* $$TABLE_WEAPON "Laser" $$TABLE_WEAPON "Laser Level 1 - Red" $$TABLE_WEAPON "Laser Level 2 - Blue" $$TABLE_WEAPON "Laser Level 3 - Purple" $$TABLE_WEAPON "Laser Level 4 - Green" $$TABLE_WEAPON "Vauss" $$TABLE_WEAPON "Vauss Spark" $$TABLE_WEAPON "Raygun" $$TABLE_WEAPON "Plasma" $$TABLE_WEAPON "plasma sparks" $$TABLE_WEAPON "Fusion" $$TABLE_WEAPON "Super Laser" $$TABLE_WEAPON "Laser Level 5 -Yellow" $$TABLE_WEAPON "Laser Level 6 -White" $$TABLE_WEAPON "Mass Driver" $$TABLE_WEAPON "Napalm" $$TABLE_WEAPON "EMDyellowleft" $$TABLE_WEAPON "EMDyellowright" $$TABLE_WEAPON "EMDgreenleft" $$TABLE_WEAPON "EMDgreenright" $$TABLE_WEAPON "Omega" $$TABLE_WEAPON "Concussion" $$TABLE_WEAPON "Homing" $$TABLE_WEAPON "Smart" $$TABLE_WEAPON "SmartPlasmaHomers" $$TABLE_WEAPON "Mega" $$TABLE_WEAPON "MegaExplosion" $$TABLE_WEAPON "Frag" $$TABLE_WEAPON "frag particles" $$TABLE_WEAPON "Guided" $$TABLE_WEAPON "NapalmRocket" $$TABLE_WEAPON "NapalmBlob" $$TABLE_WEAPON "NapalmParticles" $$TABLE_WEAPON "Cyclone Pack" $$TABLE_WEAPON "Swarm" $$TABLE_WEAPON "Yellow Flare" */ // Set up default messages so we have something AddDeathMessage(DTXT_KILLED1, true); AddSuicideMessage(DTXT_SUICIDE1); // Setup InputCommand handlers InputCommandInit(); ParseStartupScript(); TauntIndicatorBMP = DLLbm_AllocLoadFileBitmap("Taunt.ogf", 0, BITMAP_FORMAT_1555); MenuBackgroundBMP = DLLbm_AllocBitmap(32, 32, 0); if (MenuBackgroundBMP > BAD_BITMAP_HANDLE) { uint16_t *data = DLLbm_data(MenuBackgroundBMP, 0); int j; for (j = 0; j < 32 * 32; j++) data[j] = OPAQUE_FLAG | GR_RGB(0, 0, 0); } MenuItem *lev1, *lev2, *lev3; if (GetLocalRole() == LR_SERVER) { // Start Server Commands Menu lev1 = new MenuItem(DTXT_MNUSRVRCOMMAND, MIT_NORMAL, 0, NULL); // Server lev2 = new MenuItem(DTXT_MNUKICK, MIT_PLIST, 0, KickPlayer); // |- Kick lev1->AddSubMenu(lev2); lev2 = new MenuItem(DTXT_MNUBAN, MIT_PLIST, 0, BanPlayer); // |- Ban lev1->AddSubMenu(lev2); tCustomMenu cm; cm.GetItem = GetBannedPlayerString; cm.GetListCount = GetBanPlayerList; lev2 = new MenuItem(DTXT_OSM_REMOVEBAN, MIT_CUSTOM, 0, RemoveBanByIndex, &cm); lev1->AddSubMenu(lev2); lev2 = new MenuItem(DTXT_OSM_REHASHLIST, MIT_NORMAL, 0, RehashAllowDenyLists); lev1->AddSubMenu(lev2); lev2 = new MenuItem(DTXT_MNUENDLEVEL, MIT_NORMAL, 0, EndMultiLevel); // |- End Level lev1->AddSubMenu(lev2); ServerHUDCallsignsItem = lev2 = new MenuItem(DTXT_OSM_MAXHUDNAME, MIT_STATE, 0, SwitchServerHudPlayerName, 3, m_iServerHUDCallsignLevel, DTXT_PLAIN_NONE, DTXT_OSM_TEAMONLY, DTXT_OSM_FULL); lev1->AddSubMenu(lev2); if (teams > 1) { // Team Game Stuff here lev2 = new MenuItem(DTXT_MNUTEAMCONTROL, MIT_NORMAL, 0, NULL); // |- Team Control lev3 = new MenuItem(DTXT_MNUBALANCE, MIT_NORMAL, 0, BalanceTeams); // |- Balance Teams lev2->AddSubMenu(lev3); // |- AutoTeam Balance Off/On AutoBalanceItem = lev3 = new MenuItem(DTXT_MNUAUTOBALANCE, MIT_STATE, 0, SwitchAutoTeamSelect, 2, (m_iProtectedFlags & DMFC_PRF_AUTOTEAMSELECT) ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); lev2->AddSubMenu(lev3); AllowTeamChangeItem = lev3 = new MenuItem(DTXT_MNUALLOWTEAMCNG, MIT_STATE, 0, SwitchTeamChange, 2, AllowTeamChange() ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); lev2->AddSubMenu(lev3); lev3 = new MenuItem(DTXT_OSM_TEAMCONFIG, MIT_NORMAL, 0, OnScreenDisplayTeamConfig); lev2->AddSubMenu(lev3); lev1->AddSubMenu(lev2); } Menu.AddSubMenu(lev1); // End Server Commands Menu } if (GetLocalRole() == LR_CLIENT) { // Start Client Commands Menu bool peertopeer = (bool)((Netgame->flags & NF_PEER_PEER) != 0); if (!peertopeer) { lev1 = HUDIndicatorItem = new MenuItem(DTXT_LOSSPINGIND, MIT_STATE, 0, SwitchLossPingIndicator, 2, (LossGuageEnabled) ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); Menu.AddSubMenu(lev1); } // End Client Commands Menu } // Do General stuff here lev1 = new MenuItem(DTXT_MNUOBSERVER, MIT_NORMAL, 0, NULL); ObserverItem = lev2 = new MenuItem(DTXT_MNUOBSERVER, MIT_STATE, 0, SwitchObserverMode, 2, 0, DTXT_MNUOFF, DTXT_MNUON); lev1->AddSubMenu(lev2); lev2 = new MenuItem(DTXT_OSM_PIGGYBACK, MIT_PLIST, 0, SwitchPiggyBack); lev1->AddSubMenu(lev2); Menu.AddSubMenu(lev1); /* lev1 = new MenuItem("OnScreen Menu Options",MIT_NORMAL,0,NULL); lev2 = MenuBackgroundItem = new MenuItem("Background",MIT_STATE,0,SwitchMenuBackground,2,0,DTXT_MNUOFF,DTXT_MNUON); lev1->AddSubMenu(lev2); Menu.AddSubMenu(lev1); */ lev1 = MenuBackgroundItem = new MenuItem(DTXT_OSM_BACKGROUND, MIT_STATE, 0, SwitchMenuBackground, 2, 0, DTXT_MNUOFF, DTXT_MNUON); Menu.AddSubMenu(lev1); HUDCallsignsItem = lev1 = new MenuItem(DTXT_OSM_HUDNAMELEVEL, MIT_STATE, 0, SwitchHudPlayerNum, 3, m_iMyPreferredHUDCallsignLevel, DTXT_PLAIN_NONE, DTXT_OSM_TEAMONLY, DTXT_OSM_FULL); Menu.AddSubMenu(lev1); ShipLogosItem = lev1 = new MenuItem(DTXT_OSM_SHIPLOGOS, MIT_STATE, 0, SwitchShipLogoEnable, 2, (AreLogosEnabled()) ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); Menu.AddSubMenu(lev1); AudioTauntsItem = lev1 = new MenuItem(DTXT_OSM_AUDIOTAUNTS, MIT_STATE, 0, SwitchAudioTauntsEnable, 2, (AreTauntsEnabled()) ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); Menu.AddSubMenu(lev1); // File Statitics menu lev1 = new MenuItem(DTXT_OSM_STATSTOFILE, MIT_NORMAL, 0, NULL); lev2 = new MenuItem(DTXT_SAVESTATS, MIT_NORMAL, 0, SaveStatsToFile); lev1->AddSubMenu(lev2); SaveStatsLevelEndItem = lev2 = new MenuItem(DTXT_OSM_STATLEVELEND, MIT_STATE, 0, SwitchSaveStatsLevelEnd, 2, 1, DTXT_MNUOFF, DTXT_MNUON); lev1->AddSubMenu(lev2); SaveStatsDisconnectItem = lev2 = new MenuItem(DTXT_OSM_STATDISCONNECT, MIT_STATE, 0, SwitchSaveStatsDisconnect, 2, 1, DTXT_MNUOFF, DTXT_MNUON); lev1->AddSubMenu(lev2); Menu.AddSubMenu(lev1); lev1 = new MenuItem(DTXT_MNUGETPLYRINFO, MIT_PLIST, MIF_INCLUDENONE, SwitchPlayerInfo); Menu.AddSubMenu(lev1); NetGameInfoItem = lev1 = new MenuItem(DTXT_OSM_NETGAMEINFO, MIT_STATE, 0, SwitchNetGameInfo, 2, (m_iProtectedFlags & DMFC_PRF_DISPNETGAMEINFO) ? 1 : 0, DTXT_MNUOFF, DTXT_MNUON); Menu.AddSubMenu(lev1); lev1 = new MenuItem(DTXT_OSM_HUDFILTER, MIT_NORMAL, 0, NULL); lev2 = DeathMessageFilterItem = new MenuItem(DTXT_OSM_KILLMESSAGES, MIT_STATE, 0, SwitchDeathMessageFilter, 3, m_iDeathMessageFilter, DTXT_PLAIN_NONE, DTXT_OSM_SIMPLE, DTXT_OSM_FULL); lev1->AddSubMenu(lev2); lev2 = StatisticMessagesItem = new MenuItem(DTXT_OSM_STATMESSAGES, MIT_STATE, 0, SwitchStatHUDMessages, 2, 1, DTXT_MNUOFF, DTXT_MNUON); lev1->AddSubMenu(lev2); Menu.AddSubMenu(lev1); if (teams > 1) { // Team Game Stuff here char names[DLLMAX_TEAMS][MAX_TEAMNAME_LEN]; strcpy(names[RED_TEAM], GetTeamString(RED_TEAM)); strcpy(names[BLUE_TEAM], GetTeamString(BLUE_TEAM)); switch (teams) { case 2: TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS, MIT_STATE, 0, ChangeTeams, 2, GetPlayerTeam(GetPlayerNum()), names[RED_TEAM], names[BLUE_TEAM]); break; case 3: strcpy(names[GREEN_TEAM], GetTeamString(GREEN_TEAM)); TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS, MIT_STATE, 0, ChangeTeams, 3, GetPlayerTeam(GetPlayerNum()), names[RED_TEAM], names[BLUE_TEAM], names[GREEN_TEAM]); break; case 4: strcpy(names[GREEN_TEAM], GetTeamString(GREEN_TEAM)); strcpy(names[YELLOW_TEAM], GetTeamString(YELLOW_TEAM)); TeamMenuItem = lev1 = new MenuItem(DTXT_MNUCHANGETEAMS, MIT_STATE, 0, ChangeTeams, 4, GetPlayerTeam(GetPlayerNum()), names[RED_TEAM], names[BLUE_TEAM], names[GREEN_TEAM], names[YELLOW_TEAM]); break; } Menu.AddSubMenu(lev1); } lev1 = new MenuItem(DTXT_OSM_EXIT, MIT_NORMAL, 0, MenuExitMenu); Menu.AddSubMenu(lev1); // save the hud level var int curr_call_level = m_iMyPreferredHUDCallsignLevel; m_iMyCurrentHUDCallsignLevel = -1; // set this to -1 so it definitly gets changes/set SwitchShowHudCallsignLevel(curr_call_level, false); Menu.SetInputFocus(); EnableShipLogos(true); ReadInHostsAllowDeny(); InitializeForLevel(); } // DMFCBase::GameClose // // Closes up any needed DLL and DMFC tasks. This ABSOLUTLY must be called, so if you override // DMFCApp::GameClose, make sure that you put a call to this somewhere in the override void DMFCBase::GameClose(void) { ASSERT(DMFCInit); DMFCInit = false; SaveSettings(); // Free bitmaps if (hBitmapObserver > BAD_BITMAP_HANDLE) { DLLbm_FreeBitmap(hBitmapObserver); hBitmapObserver = BAD_BITMAP_HANDLE; } FreeHostsLists(); // Close up InputCommands InputCommandFree(); // close up and free player records PRec_Close(); if (MenuBackgroundBMP > BAD_BITMAP_HANDLE) DLLbm_FreeBitmap(MenuBackgroundBMP); DLLDestroyStringTable(DMFCStringTable, DMFCStringTableSize); } void DMFCBase::DrawMenu(void) { DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); // Menu.Draw(10,100,DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX])+1,this,(m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?MenuBackgroundBMP:-1); // Menu.Draw(10,100,DLLRenderHUDGetTextHeight("X")+1,this,(m_iProtectedFlags&DMFC_PRF_DISPMENUBACKGR)?MenuBackgroundBMP:-1); int font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); Menu.Draw(10, (font_height * 8) + 10, font_height + 1, (m_iProtectedFlags & DMFC_PRF_DISPMENUBACKGR) ? MenuBackgroundBMP : -1); } char DMFCPlayerInfo[6][100]; void DMFCBase::SetPlayerInfo(int pnum) { if (!CheckPlayerNum(pnum)) { return; } m_iPlayerDisplayed = pnum; int x, y; char buffer[100]; y = 0; x = 15; int index = 0; bool display_addr = false; // Print out callsign snprintf(buffer, sizeof(buffer), "%s (%s)", Players[pnum].callsign, (0 == pnum) ? DTXT_SERVER : DTXT_CLIENT); strcpy(DMFCPlayerInfo[index], buffer); index++; if (m_iNumTeams > 1 && Players[pnum].team != -1) { snprintf(buffer, sizeof(buffer), DTXT_TEAMFORMAT, GetTeamString(GetPlayerTeam(pnum))); strcpy(DMFCPlayerInfo[index], buffer); index++; } // Print out playernum snprintf(buffer, sizeof(buffer), DTXT_PLRNUMFORMAT, pnum); strcpy(DMFCPlayerInfo[index], buffer); index++; #ifdef _DEBUG display_addr = true; #endif if (GetLocalRole() == LR_SERVER) display_addr = true; // Print out net address if (display_addr) { network_protocol proto = NP_NONE; if (pnum != GetPlayerNum()) proto = NetPlayers[pnum].addr.connection_type; else proto = NP_TCP; if (proto != NP_NONE) { char string[32]; if (pnum != 0) { DLLnw_GetNumbersFromHostAddress(&NetPlayers[pnum].addr, string); } else { int addr = DLLnw_GetThisIP(); network_address local_addr; memset(&local_addr, 0, sizeof(network_address)); memcpy(local_addr.address, &addr, sizeof(int)); local_addr.connection_type = NP_TCP; DLLnw_GetNumbersFromHostAddress(&local_addr, string); } strcpy(DMFCPlayerInfo[index], string); index++; } } // Print out Ship info snprintf(buffer, sizeof(buffer), DTXT_SHIPFORM, Ships[Players[pnum].ship_index].name); strcpy(DMFCPlayerInfo[index], buffer); index++; for (; index < 5; index++) { DMFCPlayerInfo[index][0] = '\0'; } } void DMFCBase::DisplayPlayerInfo(int background_bmp, bool dedicated_server) { DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); int height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]) + 1; // int height = DLLRenderHUDGetTextHeight("X")+1; ddgr_color color = GR_RGB(180, 255, 180); int x, y; y = 0; x = 15; int index = 0; if (!CheckPlayerNum(m_iPlayerDisplayed)) { m_iPlayerDisplayed = -1; DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPPLAYERINFO); return; } if ((m_iProtectedFlags & DMFC_PRF_DISPMENUBACKGR) && background_bmp > BAD_BITMAP_HANDLE) { // draw the background bitmap DLLrend_SetAlphaValue(255 * 0.85f); DLLrend_SetZBufferState(0); DLLrend_SetTextureType(TT_LINEAR); DLLrend_SetLighting(LS_NONE); DLLrend_SetAlphaType(AT_CONSTANT_TEXTURE); DLLrend_DrawScaledBitmap(0, 0, *Game_window_w, 6 * height, background_bmp, 0, 0, 1, 1, 1.0, -1, NULL); DLLrend_SetZBufferState(1); } // Print out callsign // DLLRenderHUDText(color,255,0,x,y,DMFCPlayerInfo[index]); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DMFCPlayerInfo[index]); y += height; if (dedicated_server) DPrintf("%s\n", DMFCPlayerInfo[index]); index++; color = GR_RGB(40, 255, 40); for (; index < 5; index++) { // DLLRenderHUDText(color,255,0,x,y,DMFCPlayerInfo[index]); DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DMFCPlayerInfo[index]); if (DMFCPlayerInfo[index][0] != '\0') y += height; if (dedicated_server) DPrintf("%s\n", DMFCPlayerInfo[index]); } // print out the volatile information player_record *pr = PRec_GetPRecordByPnum(m_iPlayerDisplayed); char temp[150]; if (!pr || pr->state != STATE_INGAME) return; int precindex = translate_precptr_to_index(pr); float ti = GetTimeInGame(precindex); snprintf(temp, sizeof(temp), DTXT_PI_TIMEINGAME, GetTimeString(ti)); // DLLRenderHUDText(color,255,0,x,y,temp); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, temp); y += height; if (dedicated_server) DPrintf("%s\n", temp); } // DMFCBase::SwitchPlayerInfoDisplay // // Switches on/off displaying a Playerinfo void DMFCBase::SwitchPlayerInfoDisplay(int pnum) { m_iProtectedFlags ^= DMFC_PRF_DISPPLAYERINFO; if (m_iProtectedFlags & DMFC_PRF_DISPPLAYERINFO) { if (IsDisplayingNetGameInfo()) // if we are displaying netgame info, turn it off SwitchNetGameInfoDisplay(0); if (!CheckPlayerNum(pnum)) { DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPPLAYERINFO); return; } SetPlayerInfo(pnum); } } // DMFCBase::SwitchNetGameInfoDisplay // // Switches on/off displaying netgame info void DMFCBase::SwitchNetGameInfoDisplay(int on) { if (on) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPNETGAMEINFO); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPNETGAMEINFO); if (on) { if (DisplayingPlayerInfo() != -1) { // turn on displaying of player info before displaying netgame info SwitchPlayerInfoDisplay(-1); } } } // DMFCBase::IsDisplayingNetGameInfo // // returns true if we are currently displaying netgame info bool DMFCBase::IsDisplayingNetGameInfo(void) { return (m_iProtectedFlags & DMFC_PRF_DISPNETGAMEINFO) ? true : false; } // DMFCBase::DisplayingPlayerInfo // // Returns the pnum of the player info being displayed, -1 if none int DMFCBase::DisplayingPlayerInfo(void) { if (m_iProtectedFlags & DMFC_PRF_DISPPLAYERINFO) return m_iPlayerDisplayed; else return -1; } // DMFCBase::SetTeamName // // Sets the team name for a given team // team: integer value of the team to change // name: new name for the team // announce: if this is true, and we are the server, it will tell all the clients about the change bool DMFCBase::SetTeamName(int team, const char *name, bool announce) { if (team < 0 || team > DLLMAX_TEAMS) return false; if (!name) return false; char old_teamname[MAX_TEAMNAME_LEN]; strcpy(old_teamname, DMFC_team_names[team]); strncpy(DMFC_team_names[team], name, MAX_TEAMNAME_LEN - 1); DMFC_team_names[team][MAX_TEAMNAME_LEN - 1] = '\0'; CallOnTeamChangeName(team, old_teamname, DMFC_team_names[team]); if (announce && GetLocalRole() == LR_SERVER) { // tell the clients about the new team name SendNewTeamName(team); DPrintf(DTXT_SETTEAMNAME, old_teamname, name); } return true; } // DMFCBase::GetTeamFromString // // Returns the int value of a team based on a string, -1 if not a team int DMFCBase::GetTeamFromString(char *str) { if (!str) return -1; for (int i = 0; i < DLLMAX_TEAMS; i++) { if (!stricmp(str, DMFC_team_names[i])) return i; } return -1; } // DMFCBase::GetPlayerNum // // Returns your playernum, useful when finding out if an event is referring to you (by looking at // the playernum passed to the event handler, compared to your playernum int DMFCBase::GetPlayerNum(void) { return *Player_num; } // DMFCBase::GetLocalRole // // Returns your role in the multiplayer game. The result returned will be either LR_SERVER or LR_CLIENT int DMFCBase::GetLocalRole(void) { return Netgame->local_role; } // DMFCBase::CheckPlayerNum // // Returns true if the player number passed in is a valid player number (the player is connected) bool DMFCBase::CheckPlayerNum(int player_num) { if ((player_num < 0) || (player_num >= DLLMAX_PLAYERS)) return false; if ((NetPlayers[player_num].flags & NPF_CONNECTED) && (NetPlayers[player_num].sequence >= NETSEQ_PLAYING)) return true; else return false; } // DMFCBase::PacketCheckPlayerNum // // Returns true if it's ok to send a packet to this player bool DMFCBase::PacketCheckPlayerNum(int player_num) { if ((player_num < 0) || (player_num >= DLLMAX_PLAYERS)) return false; if ((NetPlayers[player_num].flags & NPF_CONNECTED) && (NetPlayers[player_num].sequence >= NETSEQ_OBJECTS)) return true; else return false; } // DMFCBase::CallClientEvent // // Server Only. This will send an event to a client for it to execute. // event = An EVT_CLIENT_* // me_objnum,it_objnum = Object numbers of the objects to be me and it for the event // destination = Player number of the client to send to, 0or -1 if to send to all void DMFCBase::CallClientEvent(int event, int me_objnum, int it_objnum, int destination, bool parms) { if (GetLocalRole() != LR_SERVER) return; if ((destination < -1) || (destination > DLLMAX_PLAYERS)) return; DLLMultiSendClientExecuteDLL(event, me_objnum, it_objnum, destination, parms ? Data : NULL); } // DMFCBase::GetTimeLeft // // Server Only. This will fill in the float pointer passed to it with how much time is // left in the multiplayer game. The value placed in the float is only valid if GetTimeLeft // returns true. If it returns false it was because either it is not a game with a time limit // or it is being called on a client. bool DMFCBase::GetTimeLeft(float *time) { if (GetLocalRole() != LR_SERVER) return false; if (Netgame->flags & NF_TIMER) { *time = (Netgame->timelimit * 60.0f) - RealGametime; return true; } return false; } // DMFCBase::EndLevel // // Server Only. This will end the current multiplayer level, and will go on to the next level. void DMFCBase::EndLevel(void) { if (GetLocalRole() != LR_SERVER) return; DLLMultiEndLevel(); } // DMFCBase::GetScoreLimit // // Server Only. This will fill in the int pointer passed to it with what the scoring limit // is, set in in the multiplayer options. The value point into the int is only valid if // GetScoreLimit returns true. If it returns false it is because you are either not the // server or this option wasn't set in the multiplayer options bool DMFCBase::GetScoreLimit(int *limit) { if (GetLocalRole() != LR_SERVER) return false; if (Netgame->flags & NF_KILLGOAL) { *limit = Netgame->killgoal; return true; } return false; } // DMFCBase::AutoTimeLimit // // Server Only. This turns off or on the automatic level ending by DMFC of a timed multiplayer game. // If you turn it off, it is your responsibility to end a time multiplayer game when it's time // is up. If it is turned on, DMFC will automatically handle ending the game. By default it is on. void DMFCBase::AutoTimeLimit(bool turnon) { if (GetLocalRole() != LR_SERVER) return; if ((Netgame->flags & NF_TIMER) == 0) return; if (turnon) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTIMELIMIT); } // DMFCBase::AutoDeathMessage // // This turns on or off DMFC's automatic handling of death messages. If it is turned on (Default) // then when a player dies it will display a random death message from the list added by you using // AddDeathMessage. If it is turned off, then it is your responsibility to handle the messages, you // can use DoRandomDeathMessage to display one when appropriate. void DMFCBase::AutoDeathMessage(bool turnon) { if (turnon) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTODEATHMSG); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTODEATHMSG); } // DMFCBase::AddDeathMessage // // This will add a death message to DMFC. // format = string in a "printf" type format (using %s for player callsigns) of the message // victim_first = Set this to true if the victim is listed first in the format void DMFCBase::AddDeathMessage(const char *string, bool victim_first) { if ((m_iDeathMsgCount >= 0) && (m_iDeathMsgCount < MAX_DEATH_MSGS)) { DeathMsgs[m_iDeathMsgCount].message = (char *)malloc(strlen(string) + 1); if (DeathMsgs[m_iDeathMsgCount].message) { DeathMsgs[m_iDeathMsgCount].inuse = true; DeathMsgs[m_iDeathMsgCount].victim_first = victim_first; strcpy(DeathMsgs[m_iDeathMsgCount].message, string); m_iDeathMsgCount++; } } } // DMFCBase::AddSuicideMessage // // This will add a death message to DMFC. // format = string in a "printf" type format (using %s for player callsigns) of the message void DMFCBase::AddSuicideMessage(const char *string) { if ((m_iSuicideMsgCount >= 0) && (m_iSuicideMsgCount < MAX_DEATH_MSGS)) { SuicideMsgs[m_iSuicideMsgCount].message = (char *)malloc(strlen(string) + 1); if (SuicideMsgs[m_iSuicideMsgCount].message) { SuicideMsgs[m_iSuicideMsgCount].inuse = true; strcpy(SuicideMsgs[m_iSuicideMsgCount].message, string); m_iSuicideMsgCount++; } } } // DMFCBase::GetItObjNum // // Returns the it object number of the current event for use. int DMFCBase::GetItObjNum(void) { if (Data->it_handle == OBJECT_HANDLE_NONE) return -1; return Data->it_handle & HANDLE_OBJNUM_MASK; } // DMFCBase::GetMeObjNum // // Returns the me object number of the current event for use. int DMFCBase::GetMeObjNum(void) { if (Data->me_handle == OBJECT_HANDLE_NONE) return -1; return Data->me_handle & HANDLE_OBJNUM_MASK; } // DMFCBase::OnGameStateRequest // // Server only. Override this to listen for Game state requests from the clients // When this function is called a client (who's player number is passed in) is requesting // game state information. Do what you need to do to send game state information to that player void DMFCBase::OnGameStateRequest(int pnum) { if (pnum == -1) return; mprintf((0, "%s is requesting Game State information\n", Players[pnum].callsign)); } // DMFCBase::GetTeamForNewPlayer // // A Helper function (Server only), which will give you the optimum team assignment (whoever has the // lowest amount of players on their team, for a new player. // player_num = player number of the new player // num_teams = The number of teams in your game int DMFCBase::GetTeamForNewPlayer(int player_num, int num_teams) { if ((player_num < 0) || (player_num >= DLLMAX_PLAYERS)) return 0; // see if it's a dedicated server game, and if so, are we the server if (IAmDedicatedServer() && (player_num == GetPlayerNum())) { return -1; // place on the special "no team" } int i; int *Teams; Teams = (int *)malloc(sizeof(int) * num_teams); if (!Teams) return 0; for (i = 0; i < num_teams; i++) Teams[i] = 0; for (i = 0; i < DLLMAX_PLAYERS; i++) { if ((i != player_num) && (CheckPlayerNum(i)) && (Players[i].team != -1)) { if (Players[i].team < num_teams) Teams[Players[i].team]++; } } int low_team, low_count; low_count = Teams[0]; low_team = 0; for (i = 0; i < num_teams; i++) { if (low_count > Teams[i]) { low_count = Teams[i]; low_team = i; } } free(Teams); return low_team; } // DMFCBase::SetNumberOfTeams // // Sets the number of teams to be used in the game. By default there is 1 team (everyone against everyone). // You can set up to a maximum of 4 teams. Call this function as soon as possible. // teams = number of teams void DMFCBase::SetNumberOfTeams(int teams) { if ((teams < 1) || (teams > 4)) return; DLLSetMaxTeams(teams); DLLMultiPaintGoalRooms(NULL); m_iNumTeams = teams; char names[DLLMAX_TEAMS][MAX_TEAMNAME_LEN]; strcpy(names[RED_TEAM], GetTeamString(RED_TEAM)); strcpy(names[BLUE_TEAM], GetTeamString(BLUE_TEAM)); if (TeamMenuItem) { switch (m_iNumTeams) { case 2: TeamMenuItem->SetStateItemList(m_iNumTeams, names[RED_TEAM], names[BLUE_TEAM]); break; case 3: strcpy(names[GREEN_TEAM], GetTeamString(GREEN_TEAM)); TeamMenuItem->SetStateItemList(m_iNumTeams, names[RED_TEAM], names[BLUE_TEAM], names[GREEN_TEAM]); break; case 4: strcpy(names[GREEN_TEAM], GetTeamString(GREEN_TEAM)); strcpy(names[YELLOW_TEAM], GetTeamString(YELLOW_TEAM)); TeamMenuItem->SetStateItemList(m_iNumTeams, names[RED_TEAM], names[BLUE_TEAM], names[GREEN_TEAM], names[YELLOW_TEAM]); break; } } } // DMFCBase::AutoTeamSelect // // Turns on or off DMFC's auto team assignment. If it is on, then when a new player joins, they will // be placed on the team with fewest players. If it is off, then you must handle that. Defualt on. void DMFCBase::AutoTeamSelect(bool turnon) { if (turnon) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTEAMSELECT); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOTEAMSELECT); } // DMFCBase::RequestChangeTeams // // If you are the server it will make the player change teams. If you // are a client then it sends a request to the server to change teams void DMFCBase::RequestTeamChange(int team, int pnum, bool spew_on_respawn) { if (!CheckPlayerNum(pnum)) return; if (Players[pnum].team == -1) // Dedicated server can't change teams return; if ((team < 0) || (team >= m_iNumTeams)) return; if (Players[pnum].team == team) return; if (GetLocalRole() == LR_SERVER) { if (!AllowTeamChange()) { return; } if (!CallOnCanChangeTeam(pnum, team)) { return; } Players[pnum].team = team; CallOnPlayerChangeTeam(pnum, team, true, spew_on_respawn); SendTeamAssignment(pnum, team, spew_on_respawn); } else { if (pnum != GetPlayerNum()) return; SendChangeTeamRequest(team, spew_on_respawn); } } // DMFCBase::AddHudItemCallback // // Adds an item to the hud. Everytime the hud needs to be updated, it will call the // handler passed in. The handler must be declared as: // void func(struct tHUDItem *item); // type = HI_BITMAP for bitmap, HI_TEXT for a text item // func = function callback void DMFCBase::AddHUDItemCallback(int type, void (*func)(struct tHUDItem *)) { // add all the needed Hud Items tHUDItem item; switch (type) { case HI_TEXT: item.type = HUD_ITEM_CUSTOMTEXT; break; case HI_BITMAP: item.type = HUD_ITEM_CUSTOMIMAGE; break; default: item.type = HUD_ITEM_CUSTOMTEXT; break; } item.stat = STAT_CUSTOM; item.flags = HUD_FLAG_PERSISTANT; item.data.text = NULL; item.render_fn = func; item.y = 0; item.x = 0; DLLAddHUDItem(&item); } // DMFCBase::GetMyTeam // // Returns the int value of the team you are on...only useful in a team game. int DMFCBase::GetMyTeam(void) { return Players[GetPlayerNum()].team; } // DMFCBase::GetTeamString // // Returns a pointer to a string name of a team // team = integer value of the team const char *DMFCBase::GetTeamString(int team) { static char name[20]; if (team >= 0 && team < DLLMAX_TEAMS) strcpy(name, DMFC_team_names[team]); else strcpy(name, DTXT_LONER); return name; } // DMFCBase::GetTeamColor // // Returns the color components of a team // team = integer value of the team ddgr_color DMFCBase::GetTeamColor(int team) { switch (team) { case RED_TEAM: return GR_RGB(255, 40, 40); break; case BLUE_TEAM: return GR_RGB(40, 40, 255); break; case GREEN_TEAM: return GR_RGB(40, 255, 40); break; case YELLOW_TEAM: return GR_RGB(255, 255, 40); break; default: return GR_RGB(255, 255, 255); break; } } // DMFCBase::GetNumTeams // // Returns the number of teams in the game int DMFCBase::GetNumTeams(void) { return m_iNumTeams; } // DMFCBase::AllowTeamChange // // Returns true if team changing is allowed bool DMFCBase::AllowTeamChange(void) { return (m_iProtectedFlags & DMFC_PRF_CANTEAMCHANGE) ? true : false; } // DMFCBase::SwitchAllowTeamChange // // Turns on/off allowing of team changing void DMFCBase::SwitchAllowTeamChange(bool turnon) { if (turnon) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_CANTEAMCHANGE); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_CANTEAMCHANGE); } // DMFCBase::IsMenuUp // // Returns true if the on screen menu is being shown bool DMFCBase::IsMenuUp(void) { return (m_iProtectedFlags & DMFC_PRF_INOPTIONS) ? true : false; } // DMFCBase::ClipString // // Given a width, the string will be clipped to that width. If you pass true for arrow, then an arrow will be // appended if the string has been clipped. Based off current font void DMFCBase::ClipString(int width, char *string, bool arrow) { if (!string) return; // float ratio = DEFAULT_HUD_WIDTH / ((float)*Game_window_w); int string_length = strlen(string); char arrow_string[2]; int arrow_length = 0; snprintf(arrow_string, sizeof(arrow_string), "%c", CHAR_RIGHT_ARROW); if (arrow) { // arrow_length = ratio * ((float)DLLRenderHUDGetTextLineWidth(arrow_string)); arrow_length = DLLgrtext_GetTextLineWidth(arrow_string); } if (width < arrow_length) { // Nothing can fit strcpy(string, ""); return; } // adjust width based on the arrow width -= arrow_length; int size = 0; char save = string[0]; while (size < string_length) { string[size] = '\0'; // if((((float)DLLRenderHUDGetTextLineWidth(string))*ratio)>=width){ if (DLLgrtext_GetTextLineWidth(string) >= width) { // We have to clip size--; if (arrow) { string[size] = CHAR_RIGHT_ARROW; string[size + 1] = '\0'; } return; } // replace the char and move to the next string[size] = save; size++; save = string[size]; } // The string didn't need to be clipped } // DMFCBase::ConvertHUDAlpha // // Returns a converted alpha based on what you give, it will be a more transparent if the onscreen menu is up float DMFCBase::ConvertHUDAlpha(float normal) { if (!IsMenuUp()) return normal; return normal * 0.3f; } uint8_t DMFCBase::ConvertHUDAlpha(uint8_t normal) { if (!IsMenuUp()) return normal; float conv = ((float)normal); conv = conv * 0.3f; return (uint8_t)conv; } bool DMFCBase::DMFC_compare_slots(int a, int b) { int ascore, bscore; player_record *apr, *bpr; apr = GetPlayerRecord(a); bpr = GetPlayerRecord(b); if (!apr) return true; if (!bpr) 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 = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[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 = apr->dstats.kills[DSTAT_LEVEL] - apr->dstats.suicides[DSTAT_LEVEL]; bscore = bpr->dstats.kills[DSTAT_LEVEL] - bpr->dstats.suicides[DSTAT_LEVEL]; return (ascore < bscore); } // kills/(kills+deaths+suicides) bool DMFCBase::DMFC_compare_slots_efficiency(int a, int b) { int ascore, bscore; player_record *apr, *bpr; float atemp, btemp; apr = GetPlayerRecord(a); bpr = GetPlayerRecord(b); if (!apr) return true; if (!bpr) 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 atemp = (float)(apr->dstats.kills[DSTAT_LEVEL] + apr->dstats.suicides[DSTAT_LEVEL] + apr->dstats.deaths[DSTAT_LEVEL]); ascore = (float)(apr->dstats.kills[DSTAT_LEVEL]) / ((atemp) ? atemp : 0.0000001f); btemp = (float)(bpr->dstats.kills[DSTAT_LEVEL] + bpr->dstats.suicides[DSTAT_LEVEL] + bpr->dstats.deaths[DSTAT_LEVEL]); bscore = (float)(bpr->dstats.kills[DSTAT_LEVEL]) / ((btemp) ? btemp : 0.0000001f); 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 atemp = (float)(apr->dstats.kills[DSTAT_LEVEL] + apr->dstats.suicides[DSTAT_LEVEL] + apr->dstats.deaths[DSTAT_LEVEL]); ascore = (float)(apr->dstats.kills[DSTAT_LEVEL]) / ((atemp) ? atemp : 0.0000001f); btemp = (float)(bpr->dstats.kills[DSTAT_LEVEL] + bpr->dstats.suicides[DSTAT_LEVEL] + bpr->dstats.deaths[DSTAT_LEVEL]); bscore = (float)(bpr->dstats.kills[DSTAT_LEVEL]) / ((btemp) ? btemp : 0.0000001f); return (ascore < bscore); } // DMFCBase::GetSortedPlayerSlots // // Fills in the passed array (of size maxsize) with the playernums sorted by (kills-suicides) void DMFCBase::GetSortedPlayerSlots(int *sortedindex, int maxsize) { int t; int i, j; int tempsort[MAX_PLAYER_RECORDS]; // copy scores into scoreinfo array 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 && DMFC_compare_slots(tempsort[j], t); j--) { tempsort[j + 1] = tempsort[j]; } // insert tempsort[j + 1] = t; } if (maxsize < MAX_PLAYER_RECORDS) memcpy(sortedindex, tempsort, maxsize * sizeof(int)); else memcpy(sortedindex, tempsort, MAX_PLAYER_RECORDS * sizeof(int)); } // DMFCBase::GetSortedPlayerSlotsByEfficiency // // Fills in the passed array (of size maxsize) with the playernums sorted by kills/(kills+deaths+suicides) void DMFCBase::GetSortedPlayerSlotsByEfficiency(int *sortedindex, int maxsize) { int t; int i, j; int tempsort[MAX_PLAYER_RECORDS]; // copy scores into scoreinfo array 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 && DMFC_compare_slots_efficiency(tempsort[j], t); j--) { tempsort[j + 1] = tempsort[j]; } // insert tempsort[j + 1] = t; } if (maxsize < MAX_PLAYER_RECORDS) memcpy(sortedindex, tempsort, maxsize * sizeof(int)); else memcpy(sortedindex, tempsort, MAX_PLAYER_RECORDS * sizeof(int)); } // DMFCBase::DisplayOutrageLogo(void) // // Displays the Outrage Logo for 5 seconds (call repeatedly, after 5 seconds it will just short circuit) // Doesn't do anything for non-Outrage games void DMFCBase::DisplayOutrageLogo(void) { if (!m_bDisplayOutrageLogo) return; #ifdef OUTRAGE_VERSION static bool done = false; static float time = 0; static bool loadflag = true; static chunked_bitmap bmp; if (done) return; if (loadflag) { // We need to load the handle loadflag = false; int temp = DLLbm_AllocLoadFileBitmap("Outlogo.ogf", 0, BITMAP_FORMAT_1555); DLLbm_CreateChunkedBitmap(temp, &bmp); DLLbm_FreeBitmap(temp); time = 0; } // draw the Outrage Logo if its time if (time < 5.0) { int x, y; x = (*Game_window_w) - bmp.pw; y = (*Game_window_h) - bmp.ph; DLLrend_DrawScaledChunkedBitmap(&bmp, x, y, bmp.pw, bmp.ph, 128); time += (*Frametime); } else { // we are done done = true; DLLbm_DestroyChunkedBitmap(&bmp); } #endif } // DMFCBase::SwitchShowHudCallsignLevel // // // Sets the level for displaying of Player's Callsigns on the HUD void DMFCBase::SwitchShowHudCallsignLevel(uint8_t level, bool announce) { m_iMyPreferredHUDCallsignLevel = level; if (level > m_iServerHUDCallsignLevel) level = m_iServerHUDCallsignLevel; if (m_iMyCurrentHUDCallsignLevel == level) return; m_iMyCurrentHUDCallsignLevel = level; switch (level) { case HUD_CALLSIGN_LEVEL_FULL: DLLPlayerSetHUDNameFOV(10); if (announce) DLLAddHUDMessage(DTXT_HUDLEVEL_FULL); break; case HUD_CALLSIGN_LEVEL_TEAM: DLLPlayerSetHUDNameFOV(10); if (announce) DLLAddHUDMessage(DTXT_HUDLEVEL_TEAM); break; case HUD_CALLSIGN_LEVEL_NONE: DLLPlayerSetHUDNameFOV(-1); if (announce) DLLAddHUDMessage(DTXT_HUDLEVEL_NONE); break; default: mprintf((0, "DMFC: Invalid HUD Name Level\n")); return; break; }; } // DMFCBase::SwitchServerHudCallsignLevel // // // Sets the max level of HUD callsign displayage...determined by the server void DMFCBase::SwitchServerHudCallsignLevel(uint8_t level) { switch (level) { case HUD_CALLSIGN_LEVEL_FULL: m_iServerHUDCallsignLevel = level; DLLAddHUDMessage(DTXT_SHUDLEVEL_FULL); break; case HUD_CALLSIGN_LEVEL_TEAM: m_iServerHUDCallsignLevel = level; DLLAddHUDMessage(DTXT_SHUDLEVEL_TEAM); break; case HUD_CALLSIGN_LEVEL_NONE: m_iServerHUDCallsignLevel = level; DLLAddHUDMessage(DTXT_SHUDLEVEL_NONE); break; default: mprintf((0, "DMFC: Invalid Server HUD Name Level\n")); return; break; } // make sure we're not set higher than the server allows SwitchShowHudCallsignLevel(m_iMyPreferredHUDCallsignLevel); if (GetLocalRole() == LR_SERVER) SendDMFCGameInfo(SP_ALL); } // DMFCBase::ShouldIDisplayHUDName // // Given a player num, it will determine if the callsign should be drawn on the HUD bool DMFCBase::ShouldIDisplayHUDName(int pnum) { switch (m_iMyCurrentHUDCallsignLevel) { case HUD_CALLSIGN_LEVEL_FULL: return true; break; case HUD_CALLSIGN_LEVEL_TEAM: { // see if this is a team game if (GetNumTeams() >= 2) { // ok it's a team game, see if this player is on my team, if so, than disply if (Players[pnum].team == GetMyTeam()) { return true; } else { return false; } } return false; } break; case HUD_CALLSIGN_LEVEL_NONE: return false; break; } return false; } // DMFCBase::GetCounterMeasureOwner // // // Given a counter measure it will determine the pnum of it's owner...if it can't find it, it returns -1 int DMFCBase::GetCounterMeasureOwner(object *robot) { if (!robot) return -1; if (robot->type != OBJ_ROBOT && (!(robot->type == OBJ_BUILDING && robot->ai_info))) return -1; object *killer; DLLGetUltimateParentForObject(&killer, robot); if (killer == robot) return -1; if ((killer->type != OBJ_PLAYER) && (killer->type != OBJ_OBSERVER)) return -1; return killer->id; } // DMFCBase::EncryptData // // Encrypts (weak) a buffer of data void DMFCBase::EncryptData(uint8_t *data, int size) { if (!data) return; if (size <= 0) return; int offset = size; for (int i = 0; i < size; i++) { data[i] = data[i] + i - (offset + i); offset++; } } // DMFCBase::DecryptData // // Decrypts a buffer of data void DMFCBase::DecryptData(uint8_t *data, int size) { if (!data) return; if (size <= 0) return; int offset = size; for (int i = 0; i < size; i++) { data[i] = data[i] - i + (offset + i); offset++; } } void DMFCBase::VersionCheck(int pnum) { SendVersionToClient(pnum); } // DMFCBase::FindPInfo // // Given a player number it will search the list and find that player, returning the pointer, or NULL if it // can't be found or created PInfo *DMFCBase::FindPInfo(int pnum) { ASSERT((pnum >= 0) && (pnum < DLLMAX_PLAYERS)); player_record *pr = GetPlayerRecordByPnum(pnum); if (!pr) return NULL; return pr->pinfo; } // DMFCBase::UpdatePInfo // // // Updates a victim's pinfo stat (pass in player nums) void DMFCBase::UpdatePInfo(int victim, int killer, int amount) { ASSERT((victim >= 0) || (victim < DLLMAX_PLAYERS) || (killer >= 0) || (killer < DLLMAX_PLAYERS)); if ((victim < 0) || (victim >= DLLMAX_PLAYERS) || (killer < 0) || (killer >= DLLMAX_PLAYERS)) { return; } PInfo *id; int victim_slot = PRec_GetPlayerSlot(victim); tPKillerInfo ki; memset(&ki, 0, sizeof(tPKillerInfo)); id = FindPInfo(killer); if ((id == NULL) || (victim_slot == -1)) { mprintf((0, "Unable to find PInfos\n")); return; } // update killer ki.kills = amount; id->Update(victim_slot, &ki); PInfo *kpi, *vpi; kpi = id; vpi = FindPInfo(victim); int killer_slot = PRec_GetPlayerSlot(killer); if ((vpi == NULL) || (killer_slot == -1)) { mprintf((0, "Unable to find PInfos\n")); return; } if (victim != killer) { // regular kill kpi->HandleKill(victim_slot); vpi->HandleDeath(killer_slot); } else { // suicide kpi->HandleSuicide(); } } // DMFCBase::ResetPInfo // // // Resets all the PInfo stats (and frees memory) void DMFCBase::ResetPInfo(void) { for (int i = 0; i < MAX_PLAYER_RECORDS; i++) { player_record *pr = GetPlayerRecord(i); if (pr && pr->pinfo) { pr->pinfo->ResetAll(); } } } // DMFCBase::WriteDMFCStatsToFile // // // This function will write out DMFC's stats to the file given (it must be opened for writing) void DMFCBase::WriteDMFCStatsToFile(CFILE *file) { #define BUFSIZE 150 ASSERT(file != NULL); char buffer[BUFSIZE]; player_record *pr, *dpr; tPInfoStat stat; int count, length; count = 1; for (int p = 0; p < MAX_PLAYER_RECORDS; p++) { pr = GetPlayerRecord(p); if (pr && pr->state != STATE_EMPTY) { // Write out header snprintf(buffer, sizeof(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); if (FindPInfoStatFirst(p, &stat)) { char tempbuffer[25]; snprintf(buffer, sizeof(buffer), "%s", DTXT_DMFC_STAT_HEADER); DLLcf_WriteString(file, buffer); memset(buffer, ' ', BUFSIZE); dpr = GetPlayerRecord(stat.slot); snprintf(tempbuffer, sizeof(tempbuffer), "%s", dpr->callsign); memcpy(buffer, tempbuffer, strlen(tempbuffer)); snprintf(tempbuffer, sizeof(tempbuffer), "%d", stat.kills); memcpy(&buffer[30], tempbuffer, strlen(tempbuffer)); snprintf(tempbuffer, sizeof(tempbuffer), "%d", stat.deaths); memcpy(&buffer[40], tempbuffer, strlen(tempbuffer)); int pos; pos = 40 + strlen(tempbuffer) + 1; if (pos < BUFSIZE) buffer[pos] = '\0'; buffer[BUFSIZE - 1] = '\0'; DLLcf_WriteString(file, buffer); while (FindPInfoStatNext(&stat)) { memset(buffer, ' ', BUFSIZE); dpr = GetPlayerRecord(stat.slot); snprintf(tempbuffer, sizeof(tempbuffer), "%s", dpr->callsign); memcpy(buffer, tempbuffer, strlen(tempbuffer)); snprintf(tempbuffer, sizeof(tempbuffer), "%d", stat.kills); memcpy(&buffer[30], tempbuffer, strlen(tempbuffer)); snprintf(tempbuffer, sizeof(tempbuffer), "%d", stat.deaths); memcpy(&buffer[40], tempbuffer, strlen(tempbuffer)); int pos; pos = 40 + strlen(tempbuffer) + 1; if (pos < BUFSIZE) buffer[pos] = '\0'; buffer[BUFSIZE - 1] = '\0'; DLLcf_WriteString(file, buffer); } } FindPInfoStatClose(); DLLcf_WriteString(file, ""); // skip a line count++; } } } tPInfoStat _CurrentPInfoStat; int _CurrentPInfoSlot = -1; bool _DoneWithKills = false; bool _SlotsUsed[MAX_PLAYER_RECORDS]; // DMFCBase::FindPInfoStatFirst // // Fills in a tPInfoStat that contains information about a given player record slot. Returns true on success, false // if there is no information available for the given slot. Call this first, then follow with repeated calls to // FindPInfoStatNext until you get a false value returned...call FindPInfoStatClose when done. bool DMFCBase::FindPInfoStatFirst(int slot, tPInfoStat *stat) { for (int i = 0; i < MAX_PLAYER_RECORDS; i++) _SlotsUsed[i] = false; if (slot < 0 || slot >= MAX_PLAYER_RECORDS) return false; if (!stat) return false; FindPInfoStatClose(); player_record *pr, *dpr; tPKillerInfo *killer, *victim; int deaths; _SlotsUsed[slot] = true; _CurrentPInfoSlot = slot; memset(&_CurrentPInfoStat, 0, sizeof(tPInfoStat)); // return the given slot pr = GetPlayerRecord(slot); if (!pr || pr->state == STATE_EMPTY || !pr->pinfo) return false; killer = pr->pinfo->GetFirstKiller(); victim = NULL; if (!killer) { _DoneWithKills = true; // go through the slots looking for deaths for (int p = 0; p < MAX_PLAYER_RECORDS; p++) { if (!_SlotsUsed[p]) { _SlotsUsed[p] = true; dpr = GetPlayerRecord(p); deaths = 0; if (dpr && dpr->state != STATE_EMPTY && dpr->pinfo) { victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); if (victim) deaths = victim->kills; } if (deaths > 0) { _CurrentPInfoStat.slot = p; _CurrentPInfoStat.kills = 0; _CurrentPInfoStat.deaths = deaths; memcpy(stat, &_CurrentPInfoStat, sizeof(tPInfoStat)); return true; } } } return false; } _CurrentPInfoStat.slot = killer->slot; _CurrentPInfoStat.kills = killer->kills; deaths = 0; dpr = GetPlayerRecord(killer->slot); if (dpr && dpr->state != STATE_EMPTY && dpr->pinfo) { victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); } if (victim) deaths = victim->kills; _CurrentPInfoStat.deaths = deaths; _SlotsUsed[killer->slot] = true; memcpy(stat, &_CurrentPInfoStat, sizeof(tPInfoStat)); return true; } // DMFCBase::FindPInfoStatNext // // Call this repeatedly until you get a value of false, finish by calling FindPInfoStatClose bool DMFCBase::FindPInfoStatNext(tPInfoStat *stat) { if (_CurrentPInfoSlot == -1) return false; if (!stat) return false; int slot; player_record *pr, *dpr; slot = _CurrentPInfoSlot; tPKillerInfo *killer, *victim; int deaths = 0; memset(&_CurrentPInfoStat, 0, sizeof(tPInfoStat)); // return the given slot pr = GetPlayerRecord(slot); if (!pr || pr->state == STATE_EMPTY || !pr->pinfo) return false; killer = pr->pinfo->GetNextKiller(); victim = NULL; if (!killer) { _DoneWithKills = true; // go through the slots looking for deaths for (int p = 0; p < MAX_PLAYER_RECORDS; p++) { if (!_SlotsUsed[p]) { _SlotsUsed[p] = true; dpr = GetPlayerRecord(p); deaths = 0; if (dpr && dpr->state != STATE_EMPTY && dpr->pinfo) { victim = ((PInfo *)dpr->pinfo)->GetKillerInfo(slot); if (victim) deaths = victim->kills; } if (deaths > 0) { _CurrentPInfoStat.slot = p; _CurrentPInfoStat.kills = 0; _CurrentPInfoStat.deaths = deaths; memcpy(stat, &_CurrentPInfoStat, sizeof(tPInfoStat)); return true; } } } return false; } _CurrentPInfoStat.slot = killer->slot; _CurrentPInfoStat.kills = killer->kills; deaths = 0; dpr = GetPlayerRecord(killer->slot); if (dpr && dpr->state != STATE_EMPTY && dpr->pinfo) { victim = dpr->pinfo->GetKillerInfo(slot); } if (victim) deaths = victim->kills; _CurrentPInfoStat.deaths = deaths; _SlotsUsed[killer->slot] = true; memcpy(stat, &_CurrentPInfoStat, sizeof(tPInfoStat)); return true; } // DMFCBase::FindPInfoStatClose // // Closes up a FindPInfo series of calls void DMFCBase::FindPInfoStatClose(void) { _CurrentPInfoSlot = -1; _DoneWithKills = false; } // DMFCBase::SetWeaponDeathMessage // // Sets a death message for a weapon kill bool DMFCBase::SetWeaponDeathMessage(const char *weapon_name, const char *message, bool victim_first) { ASSERT(weapon_name != NULL); ASSERT(message != NULL); int weapon_index = DLLFindWeaponName(IGNORE_TABLE(weapon_name)); if (weapon_index == -1) { mprintf((0, "Unable to set WeaponMessage for %s...can't find it\n", weapon_name)); return false; } int real_weapon = WeaponHash[weapon_index]; if (real_weapon == -1) { mprintf((0, "You forgot to call AddWeaponHash before adding this Message\n")); return false; } if ((WeaponMessages[real_weapon].inuse) && (WeaponMessages[real_weapon].message)) { free(WeaponMessages[real_weapon].message); WeaponMessages[real_weapon].message = NULL; } WeaponMessages[real_weapon].message = (char *)malloc(strlen(message) + 1); if (WeaponMessages[real_weapon].message) { strcpy(WeaponMessages[real_weapon].message, message); WeaponMessages[real_weapon].inuse = true; WeaponMessages[real_weapon].victim_first = victim_first; } return WeaponMessages[real_weapon].inuse; } // DMFCBase::GetWeaponDeathMessage // // Returns the format string for a weapon death message, NULL if it doesn't exist char *DMFCBase::GetWeaponDeathMessage(int index, bool *victim_first) { if ((index < 0) || (index >= MAX_WEAPONS)) return NULL; int real_weapon; real_weapon = WeaponHash[index]; if (real_weapon == -1) return NULL; if (!WeaponMessages[real_weapon].inuse) return NULL; *victim_first = WeaponMessages[real_weapon].victim_first; return WeaponMessages[real_weapon].message; } // DMFCBase::AddWeaponHash // // Since one weapon may actually consist of many weapons, in order to save space you can create // one weapon where all those other weapon id's will be mapped to it...use WeaponHash[id] to // get the actual weapon. End list of children with a NULL void DMFCBase::AddWeaponHash(const char *parent, ...) { ASSERT(parent != NULL); int parent_id = DLLFindWeaponName(IGNORE_TABLE(parent)); if (parent_id == -1) { mprintf((0, "Unable to find parent weapon ID in AddWeaponHash (%s)\n", parent)); return; } va_list marker; va_start(marker, parent); WeaponHash[parent_id] = parent_id; bool done = false; while (!done) { int id; char *name; name = va_arg(marker, char *); if (name) id = DLLFindWeaponName(IGNORE_TABLE(name)); else id = -1; if (id == -1) { done = true; } else { if ((id < 0) || (id >= MAX_WEAPONS)) done = true; else { WeaponHash[id] = parent_id; } } } va_end(marker); } // DMFCBase::AddWeaponHashArray // // Since one weapon may actually consist of many weapons, in order to save space you can create // one weapon where all those other weapon id's will be mapped to it...use WeaponHash[id] to // get the actual weapon. void DMFCBase::AddWeaponHashArray(const char *parent, int count, char **array) { ASSERT(parent != NULL); int parent_id = DLLFindWeaponName(IGNORE_TABLE(parent)); if (parent_id == -1) { mprintf((0, "Unable to find parent weapon ID in AddWeaponHash (%s)\n", parent)); return; } WeaponHash[parent_id] = parent_id; int index = 0; while (index < count) { int id; char *name; name = array[index++]; if (name) id = DLLFindWeaponName(IGNORE_TABLE(name)); else break; if (id >= 0 && id < MAX_WEAPONS) { WeaponHash[id] = parent_id; } } } // DMFCBase::SetupPlayerRecord // // Sets up memory for multiplayer mod user defined data in the Player Records. Pass in the size (in bytes) // of 1 struct (each player will get one). Use GetPlayerRecordData to retrieve a pointer to the data. // // pack_callback : callback function called when data from the struct needs to be packed // into a packet. It is SUPER important that this function packs the data // in little endian format. This function gets a pointer to the struct that // needs to be packed (user_info), and a buffer in which to pack it to. This // function is to return the number of bytes it has packed. // unpack_callback : callback function called when data from the struct needs to be unpacket // from a packet. This data will be in little endian format. Returns the number of // bytes unpacked. // returns: 1 if size given was <=0 (if so all previous user stats will be removed) // 0 all went ok // -1 out of memory (all user stats memory will be freed) int DMFCBase::SetupPlayerRecord(int sizeof_individual_data, int (*pack_callback)(void *user_info, uint8_t *data), int (*unpack_callback)(void *user_info, uint8_t *data)) { return PRec_SetupUserPRec(sizeof_individual_data, pack_callback, unpack_callback); } // DMFCBase::GetPlayerRecordData // // Returns a pointer to the memory set aside for a particular player (NULL on error) void *DMFCBase::GetPlayerRecordData(int pnum) { int slot; slot = PRec_GetPlayerSlot(pnum); if (slot == -1) return NULL; return PRec_GetUserStats(slot); } // DMFCBase::GetPlayerRecord // // Returns a pointer to the player record at the given slot (passed in slot must be in the range // 0<=slot= 0 && pnum < DLLMAX_PLAYERS) { mprintf((0, "Banning %s\n", Players[pnum].callsign)); DPrintf(DTXT_DEDS_BAN, Players[pnum].callsign); tBanItem *c; c = m_BanList; if (!m_BanList) { c = m_BanList = (tBanItem *)malloc(sizeof(tBanItem)); if (!c) return; } else { while (c->next) { c = c->next; } c->next = (tBanItem *)malloc(sizeof(tBanItem)); c = c->next; if (!c) return; } c->next = NULL; memcpy(&c->addr, &NetPlayers[pnum].addr, sizeof(network_address)); strncpy(c->callsign, Players[pnum].callsign, MAX_CALLSIGN_SIZE - 1); c->callsign[MAX_CALLSIGN_SIZE - 1] = '\0'; c->tracker_id[0] = '\0'; if (IsMasterTrackerGame()) { strcpy(c->tracker_id, Players[pnum].tracker_id); } m_iNumBanPlayers++; } else { mprintf((0, "Unable to ban player...pnum not valid\n")); DPrintf(DTXT_DEDS_BAN_ERROR); } } // DMFCBase::RemoveAllBans // // Removes all the temp bans void DMFCBase::RemoveAllBans(void) { tBanItem *c, *n; c = n = m_BanList; while (c) { n = c->next; free(c); c = n; } m_BanList = NULL; m_iNumBanPlayers = 0; } // DMFCBase::GetNumBannedPlayers // // Returns the number of players banned from the server int DMFCBase::GetNumBannedPlayers(void) { return m_iNumBanPlayers; } // DMCBase::GetBannedPlayerCallsign // // Returns the callsign of the banned player at position index, NULL on error char *DMFCBase::GetBannedPlayerCallsign(int index) { if (index < 0 || index >= m_iNumBanPlayers) return NULL; tBanItem *c; c = m_BanList; while (c) { if (index == 0) { return c->callsign; } index--; c = c->next; } return NULL; } // DMFCBase::RemoveBan // // Removes a temp ban on given the ban #...returns true on success bool DMFCBase::RemoveBan(int index) { if (index < 0 || index >= m_iNumBanPlayers) return false; tBanItem *c, *p; c = m_BanList; p = NULL; while (c) { if (!index) { // we found em, remove em if (p) { p->next = c->next; free(c); m_iNumBanPlayers--; } else { m_BanList = c->next; free(c); m_iNumBanPlayers--; } return true; } p = c; c = c->next; index--; } return false; } // DMFCBase::IsPlayerBanned // // returns true is the given pnum is banned from the game bool DMFCBase::IsPlayerBanned(int pnum) { mprintf((0, "Checking a ban on a player...")); if (pnum < 0 || pnum >= DLLMAX_PLAYERS) { mprintf((0, "Playernum not valid\n")); return false; } if (IsAddressBanned(&NetPlayers[pnum].addr, Players[pnum].tracker_id)) { return true; } return false; } // DMFCBase::IsAddressBanned // // returns true if the given address is banned from the game bool DMFCBase::IsAddressBanned(network_address *addr, const char *tracker_id) { tBanItem *c; c = m_BanList; mprintf((0, "Checking a ban on an address...")); while (c) { if (addr && CompareNetworkAddress(&c->addr, addr, false)) { mprintf((0, "Player addr IS BANNED\n")); return true; } if (*m_bTrackerGame) { if (tracker_id && !strcmp(tracker_id, c->tracker_id)) { mprintf((0, "Player tid IS BANNED\n")); return true; } } c = c->next; } if (!addr) { mprintf((0, "Player not banned\n")); return false; } // ok, the player doesn't have a temp ban on him, now, if it's tcp/ip, check // the hosts.allow/.deny if (addr->connection_type != NP_TCP) { mprintf((0, "Player not banned\n")); return false; } uint32_t address; tHostsNode *curr; memcpy(&address, &addr->address, 4); // check the allow list, if the address is specified on this list, // than it isn't banned curr = m_AllowList; while (curr) { if ((address & curr->mask) == (curr->ip & curr->mask)) { // its a match mprintf((0, "Player not banned\n")); return false; } curr = curr->next; } // check the deny list, if the address is specified on this list, // than it is banned curr = m_DenyList; while (curr) { if ((address & curr->mask) == (curr->ip & curr->mask)) { // its a match mprintf((0, "Player IS banned\n")); return true; } curr = curr->next; } mprintf((0, "Player not banned\n")); return false; } // DMFCBase::DoDamageToPlayer // // Server only...applies damage to player void DMFCBase::DoDamageToPlayer(int pnum, int type, float amount, bool playsound) { if (GetLocalRole() != LR_SERVER) return; if (!CheckPlayerNum(pnum)) return; if (pnum == GetPlayerNum()) { // apply it to myself if (amount <= 0) { // just add it...no damage sound Objects[Players[pnum].objnum].shields -= amount; } else { DLLApplyDamageToPlayer(&Objects[Players[pnum].objnum], NULL, type, amount, 1, 255, playsound); } } else { // add it to additional damage DLLApplyDamageToPlayer(&Objects[Players[pnum].objnum], NULL, type, amount, 1, 255, playsound); } } // DMFCBase::StartUIWindow // // Prepares the game so that you can display UI stuff void DMFCBase::StartUIWindow(int id, void *user_data) { if (m_iUIWindowID != -1) { mprintf((0, "DMFCBase::StartUIWindow(): A Window ID (0x%X) is already defined\n", m_iUIWindowID)); return; } *Game_interface_mode = GAME_DLL_INTERFACE; m_iUIWindowID = id; m_UIUserData = user_data; } // DMFCBase::EnableStatisticalMessages // // Turns on or off the statistical HUD messages that appear due to a player death void DMFCBase::EnableStatisticalMessages(bool on) { if (on) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPSTATHUDMSGS); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPSTATHUDMSGS); } // DMFCBase::GetTimeInGame // // Returns the totaltime the player has been in the game at the moment you call // this function. Pass in the player record slot. float DMFCBase::GetTimeInGame(int slot) { if (slot < 0 || slot >= MAX_PLAYER_RECORDS) return 0; player_record *pr = PRec_GetPRecord(slot); if (!pr || pr->state == STATE_EMPTY) return 0; float ti = pr->total_time_in_game; if (pr->state == STATE_INGAME) ti += RealGametime - pr->start_time; return ti; } // DMFCBase::GetTimeString // // Returns a string that contains a formated time (i.e. 32 seconds, 1:15 minutes, 3:21:23 hours) char *DMFCBase::GetTimeString(float sec) { static char buffer[20]; int minutes, seconds, hours; minutes = seconds = hours = 0; seconds = (int)sec; // we have seconds, so convert to minutes:seconds minutes = seconds / 60; seconds = seconds % 60; // now we have minutes and seconds, so convert minutes to hours:minutes hours = minutes / 60; minutes = minutes % 60; // now form our string hh:mm:ss if (hours) { if (hours == 1 && minutes == 0 && seconds == 0) snprintf(buffer, sizeof(buffer), "%s", DTXT_TIME_HOUR); else snprintf(buffer, sizeof(buffer), DTXT_TIME_HOURS, hours, minutes, seconds); } else if (minutes) { if (minutes == 1 && seconds == 0) snprintf(buffer, sizeof(buffer), "%s", DTXT_TIME_MINUTE); else snprintf(buffer, sizeof(buffer), DTXT_TIME_MINUTES, minutes, seconds); } else { if (seconds == 1) snprintf(buffer, sizeof(buffer), "%s", DTXT_TIME_SECOND); else snprintf(buffer, sizeof(buffer), DTXT_TIME_SECONDS, seconds); } return buffer; } // DMFCBase::PauseRealGameTime // // Pauses/unpauses the realgame time void DMFCBase::PauseRealGameTime(bool enable) { if (GetLocalRole() != LR_SERVER) return; if (enable) { ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PAUSETIME); SendControlMessageToPlayer(SP_ALL, CM_PAUSETIME); } else { DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PAUSETIME); SendControlMessageToPlayer(SP_ALL, CM_UNPAUSETIME); } } /* ******************************************************************************** * * * Input Command Functions ** * * ******************************************************************************** * */ // DMFCBase::InputCommandHandle // // Goes through the list of input commands and calls the associated handle (if it exists). Returns // true if it called a handler, else false. bool DMFCBase::InputCommandHandle(char *command_string) { ASSERT(command_string != NULL); // make sure it's a valid command (starts with a $) if (command_string[0] != '$') return false; /* if(!stricmp(command_string,"$help dump")) { mprintf((0,"Dumping help commands to file\n")); tInputCommandNode *node = m_InputCommandRootNode; CFILE *file; DLLOpenCFILE(&file,"C:\\DMFCHelp.txt","wt"); if(!file) return false; char buffer[1024]; while(node){ sprintf(buffer,"$%s",node->data); DLLcf_WriteString(file,buffer); DLLcf_WriteString(file,"-------------------------------------------"); sprintf(buffer,"%s\n",node->desc); DLLcf_WriteString(file,buffer); node = node->next; } DLLcfclose(file); } */ // now extract the command char command[64], index = 0; char *p = &command_string[1]; // lowercase the command_string while (*p && *p != ' ') { if ((*p >= 'A') && (*p <= 'Z')) { *p = tolower(*p); } p++; } p = &command_string[1]; // break off the first string while ((*p) && (*p != ' ')) { ASSERT(index < 64); command[index] = *p; p++; index++; } command[index] = '\0'; // tack on an ending NULL // now go through our list and see if we can find the command added by the user tInputCommandNode *node = m_InputCommandRootNode; while (node) { if (!stricmp(node->data, command)) { // we found a match! if (node->handler) { (*node->handler)(command_string); return true; } return false; // for some reason the handler is NULL (it should never happen) } node = node->next; } // if we got here than no nodes match return false; } // DMFCBase::InputCommandFree // // Frees up all memory allocated for the input commands ($ messages) void DMFCBase::InputCommandFree(void) { tInputCommandNode *next, *node = m_InputCommandRootNode; // Destroy the linked list of nodes while (node) { next = node->next; if (node->data) free(node->data); if (node->desc) free(node->desc); free(node); node = next; } // set the root to NULL to finish up m_InputCommandRootNode = NULL; } // DMFCBase::InputCommandInit // // Initializes the variables and data for the input commands. Default DMFC commands are to // be placed in here. void DMFCBase::InputCommandInit(void) { if (AddInputCommand(DTXT_IC_ALLOWTEAMCHANGE, DTXT_IC_ALLOWTEAMCHANGED, DMFCInputCommand_AllowTeamChange, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_AUTOBALANCE, DTXT_IC_AUTOBALANCED, DMFCInputCommand_AutoBalance, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_AUTOSAVEDISC, DTXT_IC_AUTOSAVEDISCD, DMFCInputCommand_AutoSaveDisconnect, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_AUTOSAVELEVEL, DTXT_IC_AUTOSAVELEVELD, DMFCInputCommand_AutoSaveLevel, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_BALANCE, DTXT_IC_BALANCED, DMFCInputCommand_Balance, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_BAN, DTXT_IC_BAND, DMFCInputCommand_Ban, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_BANLIST, DTXT_IC_BANLISTD, DMFCInputCommand_BanList) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_CHANGETEAM, DTXT_IC_CHANGETEAMD, DMFCInputCommand_ChangeTeam, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_ENDLEVEL, DTXT_IC_ENDLEVELD, DMFCInputCommand_EndLevel, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_HELP, DTXT_IC_HELPD, DMFCInputCommand_Help) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_HUDNAME, DTXT_IC_HUDNAMED, DMFCInputCommand_HudCallsigns) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_KICK, DTXT_IC_KICKD, DMFCInputCommand_Kick, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_KILLMSGFILTER, DTXT_IC_KILLMSGFILTERD, DMFCInputCommand_KillMsgFilter) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_NETGAMEINFO, DTXT_IC_NETGAMEINFO, DMFCInputCommand_NetGameInfo) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_OBSERVER, DTXT_IC_OBSERVERD, DMFCInputCommand_Observer) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_PIGGYBACK, DTXT_IC_PIGGYBACKD, DMFCInputCommand_Piggyback) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_PLAYERINFO, DTXT_IC_PLAYERINFOD, DMFCInputCommand_PlayerInfo) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_PLAYERS, DTXT_IC_PLAYERSD, DMFCInputCommand_Players) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REHASH, DTXT_IC_REHASHD, DMFCInputCommand_Rehash, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REMOTE, DTXT_IC_REMOTED, DMFCInputCommand_Remote) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REMOTEADMIN, DTXT_IC_REMOTEADMIND, DMFCInputCommand_RemoteAdmin, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REMOTEADMINLOGOUT, DTXT_IC_REMOTEADMINLOGOUTD, DMFCInputCommand_RemoteAdminLogout) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REMOTEADMINPASS, DTXT_IC_REMOTEADMINPASSD, DMFCInputCommand_RemoteAdminPass, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_REMOVEBAN, DTXT_IC_REMOVEBAND, DMFCInputCommand_RemoveBan, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SAVESTATS, DTXT_IC_SAVESTATSD, DMFCInputCommand_SaveStats, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SCORES, DTXT_IC_SCORESD, DMFCInputCommand_Scores) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SERVERHUDNAMES, DTXT_IC_SERVERHUDNAMESD, DMFCInputCommand_ServerHudCallsigns, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETGOALLIMIT, DTXT_IC_SETGOALLIMITD, DMFCInputCommand_SetGoalLimit, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETMAXPLAYERS, DTXT_IC_SETMAXPLAYERSD, DMFCInputCommand_SetMaxPlayers, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETPPS, DTXT_IC_SETPPSD, DMFCInputCommand_SetPPS, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETRESPAWNTIME, DTXT_IC_SETRESPAWNTIMED, DMFCInputCommand_SetRespawnTime, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETTEAMNAME, DTXT_IC_SETTEAMNAMED, DMFCInputCommand_SetTeamName, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_SETTIMELIMIT, DTXT_IC_SETTIMELIMITD, DMFCInputCommand_SetTimeLimit, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_STATMSGS, DTXT_IC_STATMSGSD, DMFCInputCommand_StatMsgs) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_TEAM, DTXT_IC_TEAMD, DMFCInputCommand_Team) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_WAIT, DTXT_IC_WAITD, DMFCInputCommand_Wait, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); if (AddInputCommand(DTXT_IC_WARP, DTXT_IC_WARPD, DMFCInputCommand_Warp, true) < 1) mprintf((0, "DMFC Warning: Error Adding Input Command\n")); } // Returns true if the input command can be called remotely // // Returns 1 if the input command can be called remotely // Returns -1 if the input command is not found // Returns 0 if the input command can be called remotely int DMFCBase::CanInputCommandBeUsedRemotely(char *command) { tInputCommandNode *node = m_InputCommandRootNode; while (node) { if (!stricmp(node->data, command)) { // we found a match! return (node->allow_remote) ? 1 : 0; } node = node->next; } return -1; } // Displays dedicated server help void DMFCBase::DisplayInputCommandHelp(const char *s) { if (!IAmDedicatedServer()) return; tInputCommandNode *node = m_InputCommandRootNode; if (s[0] == '\0') { char buffer[80]; DPrintf(DTXT_IC_HEADEERLIST); DPrintf(DTXT_IC_HEADERINSTRUCT); // just display the list of commands while (node) { memset(buffer, ' ', 80); memcpy(&buffer[0], node->data, strlen(node->data)); node = node->next; if (node) { memcpy(&buffer[30], node->data, strlen(node->data)); int pos = 30 + strlen(node->data); if (pos <= 78) { buffer[pos] = '\n'; buffer[pos + 1] = '\0'; } } buffer[78] = '\n'; buffer[79] = '\0'; DPrintf(buffer); if (node) node = node->next; } return; } // display help description while (node) { if (!stricmp(node->data, s)) { // we found a match! DPrintf("%s:\n", node->data); if (node->desc) DPrintf("%s\n", node->desc); else DPrintf(DTXT_IC_NOADDITIONALINFO); return; } node = node->next; } DPrintf(DTXT_IC_COMMANDNOTFOUND); } // copies a string from src to dest, making dest all lower case void strlowcpy(char *dest, const char *src) { ASSERT((dest != NULL) && (src != NULL)); char *s, *d; s = (char *)src; d = dest; while (*s) { *d = tolower(*s); s++; d++; } // do the final NULL *d = '\0'; } // DMFCBase::AddInputCommand // // When the client (or dedicated server) types a message that begins with a $, DMFC will look through // all the values you passed to AddInputCommand() and see if any match. If so it passes the entire // command string to the given function handler. Returns 1 on success, -1 if out of memory, 0 if it already // exists. These commands are not case sensitive. // Ex. AddInputCommand("team"); //this handles all the '$team' passed in int8_t DMFCBase::AddInputCommand(const char *command, const char *description, void (*handler)(const char *), bool allow_remotely) { ASSERT(command != NULL); ASSERT(handler != NULL); // traverse to the end of the list checking for duplicates along the way tInputCommandNode *node = m_InputCommandRootNode; if (!node) { // there is none currently in the list, add it now node = (tInputCommandNode *)malloc(sizeof(tInputCommandNode)); if (!node) { return -1; } // fill in data node->next = NULL; node->handler = handler; node->allow_remote = allow_remotely; node->data = (char *)malloc(strlen(command) + 1); if (!node->data) { // out of memory free(node); return -1; } strlowcpy(node->data, command); node->desc = (char *)malloc(strlen(description) + 1); if (node->desc) strcpy(node->desc, description); // all went ok m_InputCommandRootNode = node; return 1; } while (node->next) { // check this node to make sure it's not a duplicate if (!stricmp(node->data, command)) { // we found a duplicate return 0; } node = node->next; } // we need to check the current node since while(node->next) breaks out before checking the last node if (!stricmp(node->data, command)) { // we found a duplicate return 0; } // it looks like everything went ok, now just create a new node and tack it on to the end // of the list. All that can go wrong now is an out of memory error. tInputCommandNode *temp; temp = (tInputCommandNode *)malloc(sizeof(tInputCommandNode)); if (!temp) { // out of memory return -1; } temp->next = NULL; temp->handler = handler; temp->allow_remote = allow_remotely; temp->data = (char *)malloc(strlen(command) + 1); if (!temp->data) { // out of memory free(temp); return -1; } strlowcpy(temp->data, command); temp->desc = (char *)malloc(strlen(description) + 1); if (temp->desc) strcpy(temp->desc, description); // now tack it on the end of the list and thats all node->next = temp; return 1; } // DMFCBase::DisconnectMe // // Disconnects yourself from the game. It should be called when you are kicked or banned to make the // whole process a bit nicer/cleaner. Even if it doesn't get called when kicked, you will still // eventually disconnect. void DMFCBase::DisconnectMe(void) { Netgame->flags |= NF_EXIT_NOW; } // Does a check on on the pinfo info making sure it is valid void DMFCBase::CheckPInfo() { for (int slot = 0; slot < MAX_PLAYER_RECORDS; slot++) { player_record *pr = GetPlayerRecord(slot); if (pr && pr->state != STATE_EMPTY) { // now go through the victim list and check tPKillerInfo *node; ASSERT(pr->pinfo != NULL); node = pr->pinfo->GetFirstKiller(); while (node) { ASSERT(node->slot >= 0 && node->slot < 64); node = node->next; } } } } // DMFCBase::EnableOnScreenMenu // // Turns on/off the onscreen menu void DMFCBase::EnableOnScreenMenu(bool enable) { if (enable) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_INOPTIONS); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_INOPTIONS); } // DMFCBase::EnableAutoSaveLevelEnd // // Disables/Enables the autosaving of game stats to file on level end void DMFCBase::EnableAutoSaveLevelEnd(bool enable) { if (enable) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVELEVELEND); } // DMFCBase::EnableAutoSaveDisconnect // // Disables/Enables the autosaving of game stats to file on disconnect from the game void DMFCBase::EnableAutoSaveDisconnect(bool enable) { if (enable) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_AUTOSAVEDISC); } // DMFCBase::GenerateStatFilename // // Given the following information it will return a full path to what // the recommended filename to save stats to should be. // root = Multiplayer DLL Name (filename will start with this) // end_of_level = pass true if this is the end of a level stats void DMFCBase::GenerateStatFilename(char *filename, const char *root, bool end_of_level) { int level = Current_mission->cur_level; char *name = Netgame->name; struct tm *newtime; time_t long_time; time(&long_time); newtime = localtime(&long_time); char fname[256]; char timestr[100]; char day[8], month[8]; switch (newtime->tm_wday) { case 0: strcpy(day, DTXT_SUNDAY); break; case 1: strcpy(day, DTXT_MONDAY); break; case 2: strcpy(day, DTXT_TUESDAY); break; case 3: strcpy(day, DTXT_WEDNESDAY); break; case 4: strcpy(day, DTXT_THURSDAY); break; case 5: strcpy(day, DTXT_FRIDAY); break; case 6: strcpy(day, DTXT_SATURDAY); break; } switch (newtime->tm_mon) { case 0: strcpy(month, DTXT_JANUARY); break; case 1: strcpy(month, DTXT_FEBRUARY); break; case 2: strcpy(month, DTXT_MARCH); break; case 3: strcpy(month, DTXT_APRIL); break; case 4: strcpy(month, DTXT_MAY); break; case 5: strcpy(month, DTXT_JUNE); break; case 6: strcpy(month, DTXT_JULY); break; case 7: strcpy(month, DTXT_AUGUST); break; case 8: strcpy(month, DTXT_SEPTEMBER); break; case 9: strcpy(month, DTXT_OCTOBER); break; case 10: strcpy(month, DTXT_NOVEMBER); break; case 11: strcpy(month, DTXT_DECEMBER); break; } snprintf(timestr, sizeof(timestr), "%s._%s._%d_%d_%02d%02d", day, month, newtime->tm_mday, newtime->tm_year + 1900, newtime->tm_hour, newtime->tm_min); snprintf(fname, sizeof(fname), "%s_%s_%d_%s%s.stats", root, name, level, (end_of_level) ? DTXT_ENDOFLEVELCONCAT : "", timestr); // remove all spaces (convert to _) char *p = fname; while ((*p)) { if (*p == ' ') *p = '_'; p++; } // build the path info here DLLddio_MakePath(filename, LocalD3Dir, "netgames", fname, NULL); } // DMFCBase::IsPlayerObserver // // Returns true if the given pnum is currently an observer in the game, else returns false. // If an invalid pnum is given, then it returns false bool DMFCBase::IsPlayerObserver(int pnum) { if (!CheckPlayerNum(pnum)) return false; if (Objects[Players[pnum].objnum].type == OBJ_OBSERVER) return true; return false; } // DMFCBase::EnableOnScreenMenuBackground // // Enables/disables the onscreen menu background void DMFCBase::EnableOnScreenMenuBackground(bool enable) { if (enable) ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPMENUBACKGR); else DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_DISPMENUBACKGR); } // DMFCBase::DisplayNetGameInfo // // Displays information about the Netgame that is currently being played // on to the screen. void DMFCBase::DisplayNetGameInfo(int background_bmp, bool dedicated_server) { // char server_config_name[PAGENAME_LEN]; // char connection_name[PAGENAME_LEN]; DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); int height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]) + 1; // int height = DLLRenderHUDGetTextHeight("X")+1; ddgr_color color = GR_RGB(180, 255, 180); int x, y; y = 0; x = 15; int w, maxx = 0; char fullbuffer[1024]; int topy = 0; bool peertopeer = (bool)((Netgame->flags & NF_PEER_PEER) != 0); bool permissable = (bool)((Netgame->flags & NF_PERMISSABLE) != 0); if ((m_iProtectedFlags & DMFC_PRF_DISPMENUBACKGR) && background_bmp > BAD_BITMAP_HANDLE) { // draw the background bitmap DLLrend_SetAlphaValue(255 * 0.85f); DLLrend_SetZBufferState(0); DLLrend_SetTextureType(TT_LINEAR); DLLrend_SetLighting(LS_NONE); DLLrend_SetAlphaType(AT_CONSTANT_TEXTURE); DLLrend_DrawScaledBitmap(0, 0, *Game_window_w, 8 * height, background_bmp, 0, 0, 1, 1, 1.0, -1, NULL); DLLrend_SetZBufferState(1); } // Print out Title // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_HEADING); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), "%s", DTXT_NGI_HEADING); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_HEADING); color = GR_RGB(0, 255, 0); topy = y; // save this position // Print out Game name // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_GAMENAME,Netgame->name); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_GAMENAME, Netgame->name); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_GAMENAME, Netgame->name); // Print out Mission name // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_MISSIONNAME,Netgame->mission); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_MISSIONNAME, Netgame->mission); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_MISSIONNAME, Netgame->mission); // Print out Script name // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_SCRIPTNAME,Netgame->scriptname); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_SCRIPTNAME, Netgame->scriptname); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_SCRIPTNAME, Netgame->scriptname); // Print out PPS if (GetLocalRole() == LR_SERVER) { // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PPS,Netgame->packets_per_second); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_PPS, Netgame->packets_per_second); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_PPS, Netgame->packets_per_second); } else { // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PPSSERVER,Netgame->packets_per_second,NetPlayers[GetPlayerNum()].pps); // y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_PPSSERVER, Netgame->packets_per_second, NetPlayers[GetPlayerNum()].pps); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_PPSSERVER, Netgame->packets_per_second, NetPlayers[GetPlayerNum()].pps); } // Print out Max Players // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_MAXPLAYERS,Netgame->max_players); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_MAXPLAYERS, Netgame->max_players); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_MAXPLAYERS, Netgame->max_players); // Accurate Collision Detection bool use_acc_weap = (bool)((Netgame->flags & NF_USE_ACC_WEAP) != 0); // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_ACCURATECOLL,(use_acc_weap)?DTXT_MNUON:DTXT_MNUOFF); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_ACCURATECOLL, (use_acc_weap) ? DTXT_MNUON : DTXT_MNUOFF); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); maxx = (w > maxx) ? w : maxx; y += height; if (dedicated_server) DPrintf(DTXT_NGI_ACCURATECOLL, (use_acc_weap) ? DTXT_MNUON : DTXT_MNUOFF); // move to second column y = topy; x += (maxx + 10); // Rotatitional Velocity bool rot_vel_sent = (bool)((Netgame->flags & NF_SENDROTVEL) != 0); // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_SENDROTATIONAL,(rot_vel_sent)?DTXT_MNUON:DTXT_MNUOFF); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); snprintf(fullbuffer, sizeof(fullbuffer), DTXT_NGI_SENDROTATIONAL, (rot_vel_sent) ? DTXT_MNUON : DTXT_MNUOFF); DLLgrtext_Printf(x, y, fullbuffer); w = DLLgrtext_GetTextLineWidth(fullbuffer); y += height; if (dedicated_server) DPrintf(DTXT_NGI_SENDROTATIONAL, (rot_vel_sent) ? DTXT_MNUON : DTXT_MNUOFF); // Print out time limit if (Netgame->flags & NF_TIMER) { DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_TIMELIMIT, Netgame->timelimit, (Netgame->timelimit == 1) ? DTXT_MINUTE : DTXT_MINUTES); y += height; if (dedicated_server) DPrintf(DTXT_NGI_TIMELIMIT, Netgame->timelimit, (Netgame->timelimit == 1) ? DTXT_MINUTE : DTXT_MINUTES); float t_left = 0; t_left = (Netgame->timelimit * 60.0f) - RealGametime; // Print out time left DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_TIMELEFT, GetTimeString(t_left)); y += height; if (dedicated_server) DPrintf(DTXT_NGI_TIMELEFT, GetTimeString(t_left)); } else { DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_NOTIMELIMIT); y += height; if (dedicated_server) DPrintf(DTXT_NGI_NOTIMELIMIT); } // Print out Goal if (Netgame->killgoal > 0) { // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_GOALLIMIT,Netgame->killgoal); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_GOALLIMIT, Netgame->killgoal); y += height; if (dedicated_server) DPrintf(DTXT_NGI_GOALLIMIT, Netgame->killgoal); } else { // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_NOGOAL); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_NOGOAL); y += height; if (dedicated_server) DPrintf(DTXT_NGI_NOGOAL); } // Print out Respawn time // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_RESPAWNTIME,GetTimeString(Netgame->respawn_time)); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_RESPAWNTIME, GetTimeString(Netgame->respawn_time)); y += height; if (dedicated_server) DPrintf(DTXT_NGI_RESPAWNTIME, GetTimeString(Netgame->respawn_time)); // Print out Client/Server Packet Loss if ((GetPlayerNum() == 0) || peertopeer) { // packet loss info not available // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PACKETLOSSNA); y+= height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_PACKETLOSSNA); y += height; if (dedicated_server) DPrintf(DTXT_NGI_PACKETLOSSNA); } else { float packetloss = NetPlayers[GetPlayerNum()].percent_loss; // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_PACKETLOSS,packetloss); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_PACKETLOSS, packetloss); y += height; if (dedicated_server) DPrintf(DTXT_NGI_PACKETLOSS, packetloss); } // Print out Architecture // DLLRenderHUDText(color,255,0,x,y,DTXT_NGI_NETWORKMODEL,(peertopeer)?DTXT_PEERTOPEER:DTXT_CLIENTSERVER); y+=height; DLLgrtext_SetColor(color); DLLgrtext_SetAlpha(255); DLLgrtext_Printf(x, y, DTXT_NGI_NETWORKMODEL, (peertopeer) ? DTXT_PEERTOPEER : ((permissable) ? DTXT_PERMISSABLE : DTXT_CLIENTSERVER)); y += height; if (dedicated_server) DPrintf(DTXT_NGI_NETWORKMODEL, (peertopeer) ? DTXT_PEERTOPEER : ((permissable) ? DTXT_PERMISSABLE : DTXT_CLIENTSERVER)); } /* **************************************************************** * Registry Database Functions * **************************************************************** */ // DatabaseRegister // Registers your multiplayer game with the database. This must be // called before any other database function, or they will fail until this // is called. void DMFCBase::DatabaseRegister(char *name) { if (name) { strncpy(DatabaseRegisteredName, name, MAX_DBNAME_SIZE - 1); DatabaseRegisteredName[MAX_DBNAME_SIZE - 1] = '\0'; } } // DatabaseRead // Reads a string from the database bool DMFCBase::DatabaseRead(const char *label, char *entry, int *entrylen) { if (DatabaseRegisteredName[0] == '\0') return false; if (!label || !entry || !entrylen) return false; int reg_len = strlen(DatabaseRegisteredName); int copy = MAX_DBLABEL_SIZE - 1 - reg_len; char buffer[MAX_DBLABEL_SIZE]; strcpy(buffer, DatabaseRegisteredName); strncpy(&buffer[reg_len], label, copy); buffer[MAX_DBLABEL_SIZE - 1] = '\0'; return DatabaseRead1(buffer, entry, entrylen); } // DatabaseRead // Reads wordsize bytes from the database bool DMFCBase::DatabaseRead(const char *label, void *entry, int wordsize) { if (DatabaseRegisteredName[0] == '\0') return false; if (!label || !entry) return false; int reg_len = strlen(DatabaseRegisteredName); int copy = MAX_DBLABEL_SIZE - 1 - reg_len; char buffer[MAX_DBLABEL_SIZE]; strcpy(buffer, DatabaseRegisteredName); strncpy(&buffer[reg_len], label, copy); buffer[MAX_DBLABEL_SIZE - 1] = '\0'; return DatabaseRead2(buffer, entry, wordsize); } // DatabaseRead // Reads a bool from the database bool DMFCBase::DatabaseRead(const char *label, bool *entry) { if (DatabaseRegisteredName[0] == '\0') return false; if (!label || !entry) return false; int reg_len = strlen(DatabaseRegisteredName); int copy = MAX_DBLABEL_SIZE - 1 - reg_len; char buffer[MAX_DBLABEL_SIZE]; strcpy(buffer, DatabaseRegisteredName); strncpy(&buffer[reg_len], label, copy); buffer[MAX_DBLABEL_SIZE - 1] = '\0'; return DatabaseRead3(buffer, entry); } // DatabaseWrite // Writes/Updates a string to the database bool DMFCBase::DatabaseWrite(const char *label, const char *entry, int entrylen) { if (DatabaseRegisteredName[0] == '\0') return false; if (!label || !entry) return false; int reg_len = strlen(DatabaseRegisteredName); int copy = MAX_DBLABEL_SIZE - 1 - reg_len; char buffer[MAX_DBLABEL_SIZE]; strcpy(buffer, DatabaseRegisteredName); strncpy(&buffer[reg_len], label, copy); buffer[MAX_DBLABEL_SIZE - 1] = '\0'; return DatabaseWrite(buffer, entry, entrylen); } // DatabaseWrite // Writes/Updates a value to the database bool DMFCBase::DatabaseWrite(const char *label, int entry) { if (DatabaseRegisteredName[0] == '\0') return false; if (!label) return false; int reg_len = strlen(DatabaseRegisteredName); int copy = MAX_DBLABEL_SIZE - 1 - reg_len; char buffer[MAX_DBLABEL_SIZE]; strcpy(buffer, DatabaseRegisteredName); strncpy(&buffer[reg_len], label, copy); buffer[MAX_DBLABEL_SIZE - 1] = '\0'; return DatabaseWrite(buffer, entry); } // CompareNetworkAddress // // Compare's two network addresses, returns true if they are the same, false if not. // use_port: if this is true, than it will consider the port part of the network address bool DMFCBase::CompareNetworkAddress(network_address *one, network_address *two, bool use_port) { if (use_port) { // compare the whole darned thing if (!memcmp(one, two, sizeof(network_address))) return true; else return false; } else { // compare all but the port if (!memcmp(one->address, two->address, 6) && !memcmp(one->net_id, two->net_id, 4) && !memcmp(&one->connection_type, &two->connection_type, sizeof(network_protocol))) { // looks the same to me return true; } else { // something didn't match return false; } } return false; } // IsMasterTrackerGame // // returns true if this game is being played on the master tracker bool DMFCBase::IsMasterTrackerGame(void) { return (bool)((*m_bTrackerGame) != 0); } // ConvertLocalToServerObjnum // // Given an objnum, it will convert the number from your local objnum to the server's objnum // It will return -1 on error int DMFCBase::ConvertLocalToServerObjnum(int objnum) { if (GetLocalRole() == LR_SERVER) return (objnum < 0 || objnum >= MAX_OBJECTS) ? -1 : objnum; if (objnum < 0 || objnum >= MAX_OBJECTS) return -1; int s = Local_object_list[objnum]; return (s == 65535) ? -1 : s; } // ConvertServerToLocalObjnum // // Given an objnum from the server, this function will convert the objnum to your local objnum // It will return -1 on error int DMFCBase::ConvertServerToLocalObjnum(int objnum) { if (GetLocalRole() == LR_SERVER) return (objnum < 0 || objnum >= MAX_OBJECTS) ? -1 : objnum; if (objnum < 0 || objnum >= MAX_OBJECTS) return -1; int s = Server_object_list[objnum]; return (s == 65535) ? -1 : s; } // DMFCBase::AnnounceTeamChangeDeny // // Tells a player that team change request was denied void DMFCBase::AnnounceTeamChangeDeny(int pnum) { if (pnum == *Player_num) { DLLAddHUDMessage(DTXT_CHANGETEAMDENIED); } else { if (GetLocalRole() == LR_SERVER) { // tell the client the change has been denied SendControlMessageToPlayer(pnum, CM_TEAMCHANGEDENIED); } } } // DMFCBase::SetDeathMessageFilter // // Sets the death message filter void DMFCBase::SetDeathMessageFilter(int level) { if (level < 0) level = 0; if (level > 2) level = 2; if (level == m_iDeathMessageFilter) return; m_iDeathMessageFilter = level; switch (level) { case DM_FILTER_FULL: DLLAddHUDMessage(DTXT_KILLMSGFULL); break; case DM_FILTER_SIMPLE: DLLAddHUDMessage(DTXT_KILLMSGSIMPLE); break; case DM_FILTER_NONE: DLLAddHUDMessage(DTXT_KILLMSGNONE); break; }; } // DMFCBase::MakeClientsWait // // If passed true than all joining clients are forced to wait until they are given the signal to // join, either through SignalClientStart() or by passing false to MakeClientsWait void DMFCBase::MakeClientsWait(bool wait) { m_bMakeClientsWait = wait; if (!wait) { // tell all the clients it's time to start for (int i = 0; i < DLLMAX_PLAYERS; i++) { if (CheckPlayerNum(i)) { SignalClientStart(i); } } } else { // tell all the clients to hold up WaitForServer(true); } } // DMFCBase::SignalClientStart // // Given a player num, it tells the client that they can start moving and playing void DMFCBase::SignalClientStart(int pnum) { if (GetLocalRole() != LR_SERVER) { return; } SendControlMessageToPlayer(pnum, CM_SIGNALSTART); if (pnum == GetPlayerNum()) WaitForServer(false); } // DMFCBase::WaitForServer // // Call this to start waiting, until the server tells us to stop (by passing false as a parm) void DMFCBase::WaitForServer(bool wait) { if (wait) { Players[GetPlayerNum()].movement_scalar = 0; Players[GetPlayerNum()].weapon_recharge_scalar = 99999.9f; m_bMakeClientsWait = true; } else { Players[GetPlayerNum()].movement_scalar = 1.0f; Players[GetPlayerNum()].weapon_recharge_scalar = 1.0f; m_bMakeClientsWait = false; } if (GetLocalRole() != LR_SERVER) return; if (!wait) return; for (int i = 0; i < DLLMAX_PLAYERS; i++) { if (CheckPlayerNum(i)) { SendControlMessageToPlayer(i, CM_SIGNALWAIT); } } } // DMFCBase::WaitingForServerLoop // // Called per frame while waiting for the server to signal go void DMFCBase::WaitingForServerLoop(void) { static float time_ac = 0; static bool show = true; time_ac += *Frametime; if (time_ac > 0.5) { time_ac = 0; show = !show; } if (show) { DLLgrtext_SetFont(Game_fonts[MENU_FONT_INDEX]); int height = DLLgrfont_GetHeight(Game_fonts[MENU_FONT_INDEX]); DLLRenderHUDTextFlags(HUDTEXT_CENTERED, GR_WHITE, 255, 0, 0, 240 - (height / 2), DTXT_WAITFORSERVER); DLLgrtext_Flush(); } // Make sure we stay waiting... Players[GetPlayerNum()].movement_scalar = 0; Players[GetPlayerNum()].weapon_recharge_scalar = 99999.9f; } // DMFCBase::IAmDedicatedServer // // Returns true if we are a dedicated server bool DMFCBase::IAmDedicatedServer(void) { return *Dedicated_server; } // DMFCBase::IsPlayerDedicatedServer // // Returns true if the passed in pnum/player_record is a dedicated server bool DMFCBase::IsPlayerDedicatedServer(int pnum) { // since we are looking for a server, the pnum must be 0 if (pnum != 0) return false; if (Players[pnum].team == -1) return true; return false; } bool DMFCBase::IsPlayerDedicatedServer(player_record *pr) { if (pr->state == STATE_EMPTY) return false; if (pr->state == STATE_DISCONNECTED) return (pr->team == -1) ? true : false; return IsPlayerDedicatedServer(pr->pnum); } // DMFCBase::GetPlayerTeam // // Returns the team of the player...call this instead of accessing .team directly int DMFCBase::GetPlayerTeam(int pnum) { return (Players[pnum].team == -1) ? 0 : Players[pnum].team; } // DMFCBase::ConvertHUDCoord // // Given an x,y based on a virtual 640x480 screen, this will convert it to the x,y that should be // used based on the current screen size void DMFCBase::ConvertHUDCoord(int x, int y, int *rx, int *ry) { *rx = ((float)(x) * (*Hud_aspect_x)); *ry = ((float)(y) * (*Hud_aspect_y)); } // DMFCBase::GetPlayerLogoBmp // // Given a player_num, it will return a bitmap handle to that player's ship logo, or // -1 if they don't have a logo for their ship. // if is_vclip comes back as true, than it is not a bitmap handle, but a handle // to a vclip (animated bitmap). It is an index into the DLLGameVClips[]. int DMFCBase::GetPlayerLogoBmp(int player_num, bool *is_vclip) { if (!CheckPlayerNum(player_num)) return -1; // invalid pnum int texture_handle = Players[player_num].custom_texture_handle; if (GameTextures[texture_handle].bm_handle <= BAD_BITMAP_HANDLE) return -1; *is_vclip = (GameTextures[texture_handle].flags & TF_ANIMATED) ? true : false; return GameTextures[texture_handle].bm_handle; } // DMFCBase::EnableShipLogos // // Disables or enables logo displaying for the client void DMFCBase::EnableShipLogos(bool enable) { if (enable) { ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_SHIPLOGOSENABLED); } else { DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_SHIPLOGOSENABLED); } DLLMultiSetLogoState(enable); } // DMFCBase::AreLogosEnabled // // returns true if ship logos are enabled bool DMFCBase::AreLogosEnabled(void) { return (bool)((m_iProtectedFlags & DMFC_PRF_SHIPLOGOSENABLED) != 0); } // DMFCBase::EnableAudioTaunts // // Disables or enables audio taunts void DMFCBase::EnableAudioTaunts(bool enable) { DLLtaunt_Enable(enable); } // DMFCBase::AreTauntsEnabled(void) // // returns true if audio taunts are enabled bool DMFCBase::AreTauntsEnabled(void) { return DLLtaunt_AreEnabled(); } // DMFCBase::RespawnPlayer // // Takes the given player and respawns his at a random start point (in a team game, team respawn // points are taken into consideration, so make sure the player is on the correct team before you // respawn him. spew_everything, if false, will override spew_energy_and_shields void DMFCBase::RespawnPlayer(int pnum, bool spew_energy_and_shields, bool spew_everything) { if (!CheckPlayerNum(pnum)) return; object *obj = &Objects[Players[pnum].objnum]; if (spew_everything) DLLPlayerSpewInventory(obj, spew_energy_and_shields, spew_everything); DLLPlayerStopSounds(pnum); DLLInitPlayerNewShip(pnum, (spew_everything) ? INVRESET_ALL : INVRESET_DEATHSPEW); if (pnum != GetPlayerNum()) return; int slot = DLLPlayerGetRandomStartPosition(pnum); DLLObjSetPos(obj, &Players[slot].start_pos, Players[slot].start_roomnum, &Players[slot].start_orient, false); } // If you are going to create submenus you MUST use this function. along with: // void SetState(int state); // bool SetStateItemList(int count, ... ); for MIT_STATE items MenuItem *DMFCBase::CreateMenuItem(const char *title, char type, uint8_t flags, void (*fp)(int), ...) { MenuItem *p; if (type != MIT_CUSTOM && type != MIT_STATE) { p = new MenuItem(title, type, flags, fp); return p; } else { if (type == MIT_CUSTOM) { tCustomMenu cm; va_list marker; va_start(marker, fp); memcpy(&cm, va_arg(marker, tCustomMenu *), sizeof(tCustomMenu)); va_end(marker); p = new MenuItem(title, type, flags, fp, &cm); return p; } else { p = new MenuItem(title, type, flags, fp, 0, 0); // set initially to 0 state items return p; } } return NULL; } void ParseHostsFile(char *filename, tHostsNode **root) { CFILE *file; DLLOpenCFILE(&file, filename, "rt"); tHostsNode *curr = *root; if (!file) return; char buffer[256]; char save_buffer[256]; char s_ip[16], s_mask[16]; uint32_t ip_address; uint32_t mask; char *ptr; while (!DLLcfeof(file)) { DLLcf_ReadString(buffer, 256, file); // handle the buffer of data ptr = buffer; // parse white space while (*ptr && (*ptr == ' ' || *ptr == '\t')) ptr++; strcpy(save_buffer, ptr); if (*ptr) { // attempt to parse the dotted ip address (only 0-9,'.' and '*' allowed) char *start = ptr; // check to make sure its in a valid form int dot_count = 0; while (*ptr) { if (*ptr == '.') dot_count++; if ((*ptr < '0' || *ptr > '9') && *ptr != '*' && *ptr != '.') goto error_parse; // invalid character ptr++; } if (dot_count != 3) goto error_parse; // not enough dots ptr = start; // check the string length, no matter what it shouldn't be more than 15 if (strlen(ptr) > 15) goto error_parse; // break up the string into seperate strings while (*ptr) { if (*ptr == '.') *ptr = '\0'; ptr++; } ptr = start; // check to make sure the string length of each string isn't more than 3 int length, count; for (count = 0; count < 4; count++) { length = strlen(ptr); if (length > 3 || length == 0) goto error_parse; if (length != 1) { // make sure it's a pure number char *save = ptr; while (*ptr) { if (!isdigit(*ptr)) goto error_parse; ptr++; } ptr = save; int value = atoi(ptr); if (value < 0 || value > 255) goto error_parse; } ptr += length + 1; } ptr = start; // Its a valid IP address mask! yeah...*whew* that was fun int value; char temp_str[16]; s_ip[0] = s_mask[0] = '\0'; for (count = 0; count < 4; count++) { length = strlen(ptr); // Mask if (*ptr == '*') { value = 0; } else { value = 255; } snprintf(temp_str, sizeof(temp_str), "%d", value); strcat(s_mask, temp_str); if (count != 3) strcat(s_mask, "."); // IP Address if (*ptr == '*') { value = 0; } else { value = atoi(ptr); } snprintf(temp_str, sizeof(temp_str), "%d", value); strcat(s_ip, temp_str); if (count != 3) strcat(s_ip, "."); ptr += length + 1; } // we now have a valid mask (s_mask) and a valid ip (s_ip) mprintf((0, "IP: %s Mask: %s\n", s_ip, s_mask)); ip_address = DLLnw_GetHostAddressFromNumbers(s_ip); mask = DLLnw_GetHostAddressFromNumbers(s_mask); // add the node if (!curr) { curr = *root = (tHostsNode *)malloc(sizeof(tHostsNode)); } else { curr->next = (tHostsNode *)malloc(sizeof(tHostsNode)); curr = curr->next; } if (curr) { curr->next = NULL; curr->ip = ip_address; curr->mask = mask; } goto noerror_parse; } error_parse: mprintf((0, "Error parsing IP Address Mask: %s\n", save_buffer)); noerror_parse:; } DLLcfclose(file); } // DMFCBase::ReadInHostsAllowDeny // // Reads in the hosts.allow and hosts.deny files (if available) void DMFCBase::ReadInHostsAllowDeny(void) { char allow_fn[_MAX_PATH], deny_fn[_MAX_PATH]; bool allow_exist, deny_exist; // build the path info here DLLddio_MakePath(allow_fn, LocalD3Dir, "netgames", "hosts.allow", NULL); DLLddio_MakePath(deny_fn, LocalD3Dir, "netgames", "hosts.deny", NULL); allow_exist = (bool)(DLLcfexist(allow_fn) != 0); deny_exist = (bool)(DLLcfexist(deny_fn) != 0); m_DenyList = NULL; m_AllowList = NULL; // parse away if (deny_exist) { mprintf((0, "Parsing hosts.deny\n")); ParseHostsFile(deny_fn, &m_DenyList); } if (allow_exist) { mprintf((0, "Parsing hosts.allow\n")); ParseHostsFile(allow_fn, &m_AllowList); } } // DMFCBase::FreeHostsLists // // Frees all the memory allocated for the host allow/deny lists void DMFCBase::FreeHostsLists(void) { tHostsNode *curr = NULL, *next; curr = m_DenyList; while (curr) { next = curr->next; free(curr); curr = next; } curr = m_AllowList; while (curr) { next = curr->next; free(curr); curr = next; } } // DMFCBase::RehashAllowDeny // // Flushes and reloads the hosts.allow/.deny lists void DMFCBase::RehashAllowDeny(void) { FreeHostsLists(); ReadInHostsAllowDeny(); } // DMFCBase::IsPlayerAlive // // Returns true is the given pnum is a player, flying around the level (not dying, dead or observing) bool DMFCBase::IsPlayerAlive(int pnum) { if (!CheckPlayerNum(pnum)) return false; if (Objects[Players[pnum].objnum].type != OBJ_PLAYER) return false; if (Players[pnum].flags & (PLAYER_FLAGS_DYING | PLAYER_FLAGS_DEAD)) return false; return true; } // DMFCBase::ParseStartupScript // // // Loads up the startup script and sets variables accordingly void DMFCBase::ParseStartupScript(void) { CFILE *file; char path[_MAX_PATH]; char buffer[256]; int size; bool ok_to_read; int autoexec_arg = -1; if ((autoexec_arg = DLLFindArg("-autoexec")) != 0) { // a specific autoexec.dmfc file was specified, use that strcpy(path, GetGameArg(autoexec_arg + 1)); mprintf((0, "Override AUTOEXEC.DMFC to %s\n", path)); } else { // use the default autoexec.dmfc DLLddio_MakePath(path, LocalD3Dir, "netgames", "autoexec.dmfc", NULL); } DLLOpenCFILE(&file, path, "rt"); if (!file) return; while (!DLLcfeof(file)) { ok_to_read = true; size = DLLcf_ReadString(&buffer[1], 254, file); buffer[255] = '\0'; if (size >= 254) { // invalid string, too long..trash the rest of the line ok_to_read = false; while (size >= 254) { size = DLLcf_ReadString(buffer, 254, file); } } if (!ok_to_read) { mprintf((0, "AUTOEXEC.DMFC: Line too long\n")); } else { if (buffer[1] == '$') { InputCommandHandle(&buffer[1]); } else { buffer[0] = '$'; // insert starting $ sign for input command InputCommandHandle(buffer); } } } DLLcfclose(file); } dllinfo *DMFCBase::GetDLLInfoCallData(void) { return Data; } int DMFCBase::GetHighestRoomIndex(void) { return *Highest_room_index; } int DMFCBase::GetGameWindowW(void) { return *Game_window_w; } int DMFCBase::GetGameWindowH(void) { return *Game_window_h; } int DMFCBase::GetGameWindowX(void) { return *Game_window_x; } int DMFCBase::GetGameWindowY(void) { return *Game_window_y; } int *DMFCBase::GetGameFontTranslateArray(void) { return Game_fonts; } int DMFCBase::GetObserverModeBitmap(void) { return hBitmapObserver; } float DMFCBase::GetFrametime(void) { return *Frametime; } float DMFCBase::GetGametime(void) { return *Gametime; } float DMFCBase::GetRealGametime(bool *ispaused) { if (ispaused) { *ispaused = (m_iProtectedFlags & DMFC_PRF_PAUSETIME) ? true : false; } return RealGametime; } float *DMFCBase::GetShieldDeltaArray(void) { return ShieldDelta; } float DMFCBase::GetHudAspectX(void) { return *Hud_aspect_x; } float DMFCBase::GetHudAspectY(void) { return *Hud_aspect_y; } const char *DMFCBase::GetLocalD3Dir(void) { return LocalD3Dir; } const tMission *DMFCBase::GetCurrentMission(void) { return Current_mission; } room *DMFCBase::GetRooms(void) { return Rooms; } object *DMFCBase::GetObjects(void) { return Objects; } terrain_segment *DMFCBase::GetTerrainSegs(void) { return Terrain_seg; } netgame_info *DMFCBase::GetNetgameInfo(void) { return Netgame; } player *DMFCBase::GetPlayers(void) { return Players; } netplayer *DMFCBase::GetNetPlayers(void) { return NetPlayers; } ship *DMFCBase::GetShips(void) { return Ships; } weapon *DMFCBase::GetWeapons(void) { return Weapons; } texture *DMFCBase::GetGameTextures(void) { return GameTextures; } vclip *DMFCBase::GetGameVClips(void) { return GameVClips; } poly_model *DMFCBase::GetGamePolyModels(void) { return Poly_models; } ddgr_color *DMFCBase::GetPlayerColors(void) { return Player_colors; } game_controls DMFCBase::GetLastGameControls(void) { return Last_game_controls; } int *DMFCBase::GetPilotPicBitmapHandles(void) { return PilotPicBmpHandles; } void DMFCBase::GetViewerObjectPtr(object **v_obj) { *v_obj = *Viewer_object; } void DMFCBase::SetViewerObjectPtr(object *v_obj) { *Viewer_object = v_obj; } float DMFCBase::GetRenderZoom(void) { return *Render_zoom; } IMenuItem *DMFCBase::GetOnScreenMenu(void) { return &Menu; } tOSIRISModuleInit *DMFCBase::GetOsirisModuleData(void) { return API.osiris_functions; } vis_effect *DMFCBase::GetVisEffectArray(int **Highviseptr) { if (Highviseptr) *Highviseptr = Highest_vis_effect_index; return VisEffects; } char *DMFCBase::GetGameArg(int arg) { static char EMPTY_ARG[1] = ""; ASSERT(arg >= 0 && arg < MAX_ARGS); if (arg < 0 || arg >= MAX_ARGS) return (char *)EMPTY_ARG; char *arguments = (char *)GameArgs; int position = arg * MAX_CHARS_PER_ARG; return &arguments[position]; } // DMFCBase::EnableLossGuage // // Turns on/off the Loss guage on the screen void DMFCBase::EnableLossGuage(bool enable) { bool peertopeer = (bool)((Netgame->flags & NF_PEER_PEER) != 0); if ((GetLocalRole() == LR_SERVER) || peertopeer) { return; } LossGuageEnabled = enable; } ddgr_color GetTriLinColor(float val, float low, float med, float hi, ddgr_color lowc, ddgr_color medc, ddgr_color hic) { ddgr_color low_col, hi_col; float perc; if (val <= low) { // we're pure lowc perc = 1.0f; low_col = hi_col = lowc; } else if (val <= med) { // we're between lowc and medc low_col = lowc; hi_col = medc; perc = (val - low) / (med - low); } else if (val <= hi) { // we're between medc and hic low_col = medc; hi_col = hic; perc = (val - med) / (hi - med); } else { // we're pure hic perc = 1.0f; low_col = hi_col = hic; } if (perc < 0.0f) perc = 0.0f; if (perc > 1.0f) perc = 1.0f; ddgr_color color_to_use; int r, g, b; float amount_low, amount_hi; amount_low = 1.0 - perc; amount_hi = perc; r = GR_COLOR_RED(low_col) * amount_low + GR_COLOR_RED(hi_col) * amount_hi; g = GR_COLOR_GREEN(low_col) * amount_low + GR_COLOR_GREEN(hi_col) * amount_hi; b = GR_COLOR_BLUE(low_col) * amount_low + GR_COLOR_BLUE(hi_col) * amount_hi; color_to_use = GR_RGB(r, g, b); return color_to_use; } // DMFCBase::LossGuageFrame // // Processes the Loss guage for the frame void DMFCBase::LossGuageFrame(void) { if (!LossGuageEnabled) return; if (GetLocalRole() == LR_SERVER) return; // Don't show if peer-to-peer if (Netgame->flags & NF_PEER_PEER) return; #define LOW_COLOR GR_RGB(20, 255, 20) #define MED_COLOR GR_RGB(255, 255, 40) #define HI_COLOR GR_RGB(255, 20, 20) #define LOW_LOSS 1.0f #define MED_LOSS 5.0f #define HI_LOSS 10.0f float packetloss = NetPlayers[GetPlayerNum()].percent_loss; ddgr_color loss_color = GetTriLinColor(packetloss, LOW_LOSS, MED_LOSS, HI_LOSS, LOW_COLOR, MED_COLOR, HI_COLOR); ddgr_color ping_color = GetTriLinColor(NetPlayers[GetPlayerNum()].ping_time * 1000.0f, 0, 200.0f, 400.0f, LOW_COLOR, MED_COLOR, HI_COLOR); DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); int max_x = 0; int y = 2; DLLRenderHUDText(loss_color, 255, 2, 0, y, DTXT_NETWORK_LOSS); max_x = DLLRenderHUDGetTextLineWidth(DTXT_NETWORK_LOSS); DLLRenderHUDText(ping_color, 255, 2, 0, y + 10, DTXT_NETWORK_PING); max_x = std::max(max_x, DLLRenderHUDGetTextLineWidth(DTXT_NETWORK_PING)); max_x += 10; DLLRenderHUDText(loss_color, 255, 0, 2 + max_x, y, "%.2f%%", packetloss); DLLRenderHUDText(ping_color, 255, 0, 2 + max_x, y + 10, "%dms", (int)(NetPlayers[GetPlayerNum()].ping_time * 1000.0f)); } level_info *DMFCBase::GetLevelInfo(void) { return Level_info; } // 1 - (num_levels) void DMFCBase::WarpToLevel(int lev) { if (GetLocalRole() != LR_SERVER) return; if (lev < 1 || lev > Current_mission->num_levels) return; *Multi_next_level = lev; EndLevel(); } void DMFCBase::SetPlayerTauntIndicator(int pnum) { if (!CheckPlayerNum(pnum)) return; DisplayTauntIndicator = true; TauntIndicatorStartTime = *Gametime; TauntIndicatorPlayerNum = pnum; } void DMFCBase::DisplayTauntIndicatorFrame(void) { if (!DisplayTauntIndicator) return; if (!CheckPlayerNum(TauntIndicatorPlayerNum)) { DisplayTauntIndicator = false; return; } float alpha, perc; int igt = floor((*Gametime)); float igth = (*Gametime) - igt; if (igth > 0.5f) { igth -= 0.5f; } if (igth > 0.25f) { perc = 1.0f - ((igth - 0.25) / 0.25f); } else { perc = (igth / 0.25f); } float base_alpha = 0.40f; float range = 1.0f - base_alpha; alpha = base_alpha + (perc * range); if (alpha < base_alpha) alpha = base_alpha; if (alpha > 1.0f) alpha = 1.0f; int bmp = TauntIndicatorBMP; if (PilotPicBmpHandles[TauntIndicatorPlayerNum] > BAD_BITMAP_HANDLE) { bmp = PilotPicBmpHandles[TauntIndicatorPlayerNum]; } if (bmp <= BAD_BITMAP_HANDLE) { DisplayTauntIndicator = false; return; } int bmh = 32; int bmw = 32; int x, y; x = 10; y = 200; DLLrend_SetAlphaType(AT_CONSTANT_TEXTURE); DLLrend_SetAlphaValue(alpha * 255); DLLrend_SetLighting(LS_NONE); DLLrend_SetColorModel(CM_MONO); DLLrend_SetOverlayType(OT_NONE); DLLrend_SetFiltering(0); DLLrend_SetWrapType(WT_CLAMP); DLLrend_SetZBufferState(0); DLLrend_SetTextureType(TT_LINEAR); DLLrend_DrawScaledBitmap(x, y, x + bmw, y + bmh, bmp, 0, 0, 1, 1, 1, -1, NULL); DLLrend_SetFiltering(1); DLLrend_SetZBufferState(1); int font_height; DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); x = 15 + bmw; y = (y + (bmh / 2)) - (font_height / 2); DLLgrtext_SetColor(GR_GREEN); DLLgrtext_SetAlpha(alpha * 255.0f); DLLgrtext_Printf(x, y, Players[TauntIndicatorPlayerNum].callsign); if ((*Gametime - TauntIndicatorStartTime) > 3.0f) DisplayTauntIndicator = false; } // DMFCBase::SetMaxPlayerHardLimit // // Sets a hard limit to the max number of players allowed in the game // changing the number of players can never go above this...defaults // to DLLMAX_PLAYERS void DMFCBase::SetMaxPlayerHardLimit(int max) { if (GetLocalRole() != LR_SERVER) return; if (max > DLLMAX_PLAYERS || max < 1) return; Hard_max_players = max; if (Netgame->max_players > Hard_max_players) { Netgame->max_players = max; DLLAddHUDMessage(DTXT_MAXPLAYERSSETMSG, max); SendNetGameInfoSync(); } } // DMFCBase::MarkPlayersInGame // // Goes through all the player records and marks those that are in the game // This is needed because at level end, all clients disconnect, so we lose // that information. void DMFCBase::MarkPlayersInGame(void) { player_record *pr; for (int i = 0; i < MAX_PLAYER_RECORDS; i++) { pr = GetPlayerRecord(i); if (pr && pr->state == STATE_INGAME) { players_in_game[i] = pr->pnum; } else { players_in_game[i] = -1; } } } // DMFCBase::ResetPlayersInGame // // Resets the Players-in-game list void DMFCBase::ResetPlayersInGame(void) { for (int i = 0; i < MAX_PLAYER_RECORDS; i++) { players_in_game[i] = -1; } } // DMFCBase::WasPlayerInGameAtLevelEnd // // Given a player record it returns true/false whether // the player was in the game at the time the level ended. int DMFCBase::WasPlayerInGameAtLevelEnd(int prec) { if (prec < 0 || prec >= MAX_PLAYER_RECORDS) return -1; return players_in_game[prec]; } // DMFCBase::GetConnectingPlayerTeam // // Returns the team of a player just connecting, this is to be set by the // game and is DMFC assumes that all players have the correct team on join int DMFCBase::GetConnectingPlayerTeam(int slot) { if (GetLocalRole() != LR_SERVER) return -2; int prec_num; if (IsMasterTrackerGame()) prec_num = PRec_FindPlayer(Players[slot].callsign, NULL, Players[slot].tracker_id); else prec_num = PRec_FindPlayer(Players[slot].callsign, &NetPlayers[slot].addr, NULL); int team = -2; if (prec_num != -1) { // we have a reconnecting player // we need to reconnect the player to the player records before we get the team if (!PRec_ReconnectPlayerToSlot(slot, prec_num, Players, NetPlayers)) { mprintf((0, "Unable to reassign reconnecting player (%s) to Player Record slot #%d\n", Players[slot].callsign, prec_num)); Int3(); } else { team = PRec_GetPlayerTeam(slot); // disconnect the player again since he isn't in the game just yet PRec_DisconnectPlayer(slot); } } else { // we have a new player if (slot == 0 && IAmDedicatedServer()) { team = -1; } else { // get team assignment if (m_iProtectedFlags & DMFC_PRF_AUTOTEAMSELECT) { team = GetTeamForNewPlayer(slot, m_iNumTeams); } else { team = RED_TEAM; } } } mprintf((0, "CONNECTING PLAYER (%s): Team assigned to %d\n", Players[slot].callsign, team)); return team; } // DMFCBase::SetDedicatedWait // // Sets the level wait time for the dedicated server. A dedicated server will make all // clients wait until this time (in seconds) is up each level. void DMFCBase::SetDedicatedWait(float time_secs) { if (time_secs < 0) time_secs = 0; DedicatedLevelWait = time_secs; } // DMFCBase::EnableTimelimitCountdown // // Enables/Disables the timelimit countdown (if there is a level time limit) // Optionally you can specify what time to start the count down. (default 10) void DMFCBase::EnableTimelimitCountdown(bool enable, int seconds) { m_bDisplayTimelimitCountdown = enable; if (enable && seconds > 0) { m_iTimeLimitCountdown = seconds; } } // DMFCBase::DisplayTimelimitCountdown // // Displays (if needed) the time limit countdown void DMFCBase::DisplayTimelimitCountdown(void) { if (!m_bDisplayTimelimitCountdown) return; if (!(Netgame->flags & NF_TIMER)) return; float time_left = (Netgame->timelimit * 60.0f) - RealGametime; if (time_left > m_iTimeLimitCountdown || time_left < 0) return; // display a countdown! DLLgrtext_SetFont(Game_fonts[HUD_FONT_INDEX]); DLLgrtext_SetColor(GR_WHITE); DLLgrtext_SetAlpha(255); char buffer[256]; snprintf(buffer, sizeof(buffer), "Time Left: T - %d", (int)time_left); int font_height = DLLgrfont_GetHeight(Game_fonts[HUD_FONT_INDEX]); DLLgrtext_CenteredPrintf(0, font_height * 10, buffer); } // DMFCBase::TranslateTextMacro // // Given a macro with tokens, this function will replace the tokens // and create a new string for display. // All tokens begin with $$ (i.e. $$TEAMBASE) void DMFCBase::TranslateTextMacro(const char *src, char *destination, int dest_size) { ASSERT(destination != NULL); ASSERT(src != NULL); ASSERT(dest_size >= 0); if (dest_size <= 0) return; char token[128]; char token_string[512]; char *curr_dest = destination; int src_idx = 0; bool done = false; // now we need to go through the src and copy to destination // while looking for tokens, and making sure we don't go over our size while (dest_size > 1 && !done) { switch (src[src_idx]) { case '$': { // possible token!, check the next character if (src[src_idx + 1] == '$') { // TOKEN!!! src_idx += 2; // move to the beginning of the token if (src[src_idx] == ' ' || src[src_idx] == '\0') { // ok, maybe it isn't a token *curr_dest = src[src_idx - 2]; curr_dest++; dest_size--; *curr_dest = src[src_idx - 1]; curr_dest++; dest_size--; *curr_dest = src[src_idx]; if (*curr_dest == '\0') { done = true; } else { src_idx++; } curr_dest++; dest_size--; } else { // process it, it's gotta be a token // extract the token out char *ptr = token; while (src[src_idx] != ' ' && src[src_idx] != '\0') { *ptr = src[src_idx]; ptr++; src_idx++; } *ptr = '\0'; // now replace the token... *token_string = '\0'; mprintf((0, "Looking for token for %s\n", token)); CallOnGetTokenString(token, token_string, 512); // make sure we don't go too far int len = strlen(token_string); if (len > (dest_size - 1)) len = dest_size - 1; ptr = token_string; while (len > 0) { *curr_dest = *ptr; curr_dest++; dest_size--; ptr++; len--; } } } else { // just copy the character over *curr_dest = src[src_idx]; src_idx++; curr_dest++; dest_size--; } } break; case '\0': done = true; break; default: { // just copy the character over *curr_dest = src[src_idx]; src_idx++; curr_dest++; dest_size--; } break; } } *curr_dest = '\0'; // NULL terminate } // DMFCBase::SelectNextCameraView // // This function, given which window (corresponds to left, middle, right), switches the // current view of the small camera windows on the screen void DMFCBase::SelectNextCameraView(int window) { if (window < 0 || window >= NUM_CAMERA_VIEWS) return; DLLSelectNextCameraView(window); } int DMFCBase::GetCameraViewType(int window) { if (window < 0 || window >= NUM_CAMERA_VIEWS) return -1; return Camera_view_mode[window]; } // Given a generic object (OBJ_POWERUP,OBJ_ROBOT,OBJ_BUILDING or OBJ_CLUTTER) id // in the range of 0 to MAX_OBJECT_IDS, this returns a pointer to it's information (see objinfo.h) // It returns NULL if an invalid id is given (or it's not used) object_info *DMFCBase::GetObjectInfo(int objinfo_id) { if (objinfo_id < 0 || objinfo_id >= MAX_OBJECT_IDS) return NULL; if (Object_info[objinfo_id].type == OBJ_NONE) return NULL; return &Object_info[objinfo_id]; }