mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-23 12:08:56 +00:00
5413 lines
144 KiB
C++
5413 lines
144 KiB
C++
/*
|
|
* $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 <None> 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 <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
char **DMFCStringTable;
|
|
int DMFCStringTableSize = 0;
|
|
char *_DMFCErrorString = "DMFC Missing String";
|
|
ubyte 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};
|
|
ubyte seeds2[6] = {70,95,103,102,112,0};
|
|
|
|
|
|
|
|
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
|
|
#ifndef MACINTOSH
|
|
void DMFCBase::LoadFunctions(int *api_func)
|
|
{
|
|
#include "dmfcdllinit.h"
|
|
}
|
|
#endif
|
|
|
|
// 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){
|
|
ushort *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
|
|
sprintf(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){
|
|
sprintf(buffer,DTXT_TEAMFORMAT,GetTeamString(GetPlayerTeam(pnum)));
|
|
strcpy(DMFCPlayerInfo[index],buffer); index++;
|
|
}
|
|
|
|
//Print out playernum
|
|
sprintf(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
|
|
sprintf(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);
|
|
|
|
sprintf(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,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(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(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;
|
|
sprintf(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;
|
|
}
|
|
ubyte DMFCBase::ConvertHUDAlpha(ubyte normal)
|
|
{
|
|
if(!IsMenuUp())
|
|
return normal;
|
|
|
|
float conv = ((float)normal);
|
|
conv = conv * 0.3f;
|
|
return (ubyte)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(ubyte 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(ubyte 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(ubyte *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(ubyte *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 (PInfo *)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){
|
|
((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
|
|
sprintf(buffer,"%d) %s%s",count,(pr->state==STATE_INGAME)?"":"*",pr->callsign);
|
|
DLLcf_WriteString(file,buffer);
|
|
length = strlen(buffer);
|
|
memset(buffer,'=',length);
|
|
buffer[length] = '\0';
|
|
DLLcf_WriteString(file,buffer);
|
|
|
|
if(FindPInfoStatFirst(p,&stat)){
|
|
char tempbuffer[25];
|
|
sprintf(buffer,DTXT_DMFC_STAT_HEADER);
|
|
DLLcf_WriteString(file,buffer);
|
|
memset(buffer,' ',BUFSIZE);
|
|
dpr = GetPlayerRecord(stat.slot);
|
|
|
|
sprintf(tempbuffer,"%s",dpr->callsign);
|
|
memcpy(buffer,tempbuffer,strlen(tempbuffer));
|
|
|
|
sprintf(tempbuffer,"%d",stat.kills);
|
|
memcpy(&buffer[30],tempbuffer,strlen(tempbuffer));
|
|
|
|
sprintf(tempbuffer,"%d",stat.deaths);
|
|
memcpy(&buffer[40],tempbuffer,strlen(tempbuffer));
|
|
|
|
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);
|
|
|
|
sprintf(tempbuffer,"%s",dpr->callsign);
|
|
memcpy(buffer,tempbuffer,strlen(tempbuffer));
|
|
|
|
sprintf(tempbuffer,"%d",stat.kills);
|
|
memcpy(&buffer[30],tempbuffer,strlen(tempbuffer));
|
|
|
|
sprintf(tempbuffer,"%d",stat.deaths);
|
|
memcpy(&buffer[40],tempbuffer,strlen(tempbuffer));
|
|
|
|
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 = ((PInfo *)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 = ((PInfo *)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 = ((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::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(char *weapon_name,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(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(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,ubyte *data),int (*unpack_callback)(void *user_info,ubyte *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<MAX_PLAYER_RECORDS)
|
|
player_record *DMFCBase::GetPlayerRecord(int slot)
|
|
{
|
|
return PRec_GetPRecord(slot);
|
|
}
|
|
|
|
// DMFCBase::GetPlayerRecordByPnum
|
|
//
|
|
// Same as above, but you can specify by a player's pnum, return NULL on error
|
|
player_record *DMFCBase::GetPlayerRecordByPnum(int pnum)
|
|
{
|
|
int slot;
|
|
slot = PRec_GetPlayerSlot(pnum);
|
|
if(slot==-1)
|
|
return NULL;
|
|
return PRec_GetPRecord(slot);
|
|
}
|
|
|
|
// DMFCBase::BanPlayerFromGame
|
|
//
|
|
// Puts a temp ban on a player which will only last the duration of the game
|
|
void DMFCBase::BanPlayerFromGame(int pnum)
|
|
{
|
|
if( pnum>=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,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;
|
|
}
|
|
|
|
unsigned long 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)
|
|
sprintf(buffer,DTXT_TIME_HOUR);
|
|
else
|
|
sprintf(buffer,DTXT_TIME_HOURS,hours,minutes,seconds);
|
|
}else if(minutes){
|
|
if(minutes==1 && seconds==0)
|
|
sprintf(buffer,DTXT_TIME_MINUTE);
|
|
else
|
|
sprintf(buffer,DTXT_TIME_MINUTES,minutes,seconds);
|
|
}else{
|
|
if(seconds==1)
|
|
sprintf(buffer,DTXT_TIME_SECOND);
|
|
else
|
|
sprintf(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(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
|
|
signed char DMFCBase::AddInputCommand(char *command,char *description,void (*handler)(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 = ((PInfo *)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,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;
|
|
}
|
|
|
|
sprintf(timestr,"%s._%s._%d_%d_%02d%02d",day,month,newtime->tm_mday,newtime->tm_year+1900,newtime->tm_hour,newtime->tm_min);
|
|
sprintf(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);
|
|
sprintf(fullbuffer,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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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);
|
|
sprintf(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,NULL);
|
|
}
|
|
|
|
//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(char *title,char type,ubyte 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];
|
|
unsigned long ip_address;
|
|
unsigned long 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[3];
|
|
s_ip[0] = s_mask[0] = '\0';
|
|
|
|
for(count=0;count<4;count++){
|
|
length = strlen(ptr);
|
|
|
|
//Mask
|
|
if( *ptr=='*'){
|
|
value = 0;
|
|
}else{
|
|
value = 255;
|
|
}
|
|
sprintf(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);
|
|
}
|
|
sprintf(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 = 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];
|
|
sprintf(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];
|
|
}
|