mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 11:28:56 +00:00
1119 lines
36 KiB
C++
1119 lines
36 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/dmfc/dmfcclient.cpp $
|
|
* $Revision: 1.1.1.1 $
|
|
* $Date: 2003/08/26 03:57:20 $
|
|
* $Author: kevinb $
|
|
*
|
|
* Client Event handlers
|
|
*
|
|
* $Log: dmfcclient.cpp,v $
|
|
* Revision 1.1.1.1 2003/08/26 03:57:20 kevinb
|
|
* initial 1.5 import
|
|
*
|
|
*
|
|
* 92 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.
|
|
*
|
|
* 91 7/15/99 3:33p Jeff
|
|
* play a sound when a player disconnects/leaves the game
|
|
*
|
|
* 90 7/13/99 12:11p Jeff
|
|
* added some specific text taunt token decoding
|
|
*
|
|
* 89 7/13/99 10:05a Jeff
|
|
* text taunt token decoding
|
|
*
|
|
* 88 7/09/99 7:02p Jeff
|
|
* put in countdown timer for when a level is about to end
|
|
*
|
|
* 87 7/09/99 6:17p Jeff
|
|
* added $remoteadminlogout and $wait commands
|
|
*
|
|
* 86 7/09/99 2:53p Jeff
|
|
* handle gametime better (pause it when needed) if the server is 'waiting
|
|
* for players'
|
|
*
|
|
* 85 7/08/99 9:56p Jeff
|
|
* added event handler for weapon fired event
|
|
*
|
|
* 84 7/08/99 2:39a Jeff
|
|
* rough implementation of remote administration checked in. Still needs
|
|
* some polishing, but should work basically.
|
|
*
|
|
* 83 7/07/99 6:10p Jeff
|
|
* don't count the time that a player is in observer mode for death/kill
|
|
* messages
|
|
*
|
|
* 82 6/22/99 5:41p Jeff
|
|
* removed int3 (causing probs in Linux)
|
|
*
|
|
* 81 6/11/99 5:36p Jeff
|
|
* removed ai_info #ifdefs (better way of doing it)
|
|
*
|
|
* 80 6/10/99 12:41p Jeff
|
|
* no ai_info in non-Outrage builds
|
|
*
|
|
* 79 5/24/99 11:05p Jeff
|
|
* fixed bug for dedicated server team getting reset on endlevel
|
|
*
|
|
* 78 5/11/99 9:42p Jeff
|
|
* fixed bug changing teams while dead
|
|
*
|
|
* 77 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
|
|
*
|
|
* 76 5/08/99 4:45a Jeff
|
|
* fixed printscreen position
|
|
*
|
|
* 75 5/08/99 4:30a Jeff
|
|
* fixed sequencing bug where clients never got a level end event for the
|
|
* multiplayer games
|
|
*
|
|
* 74 5/07/99 5:16p Jeff
|
|
* fixed player disconnect at level end
|
|
*
|
|
* 73 5/07/99 2:21p Jeff
|
|
* fixed (?) not disconnecting clients on level end
|
|
*
|
|
* 72 5/04/99 8:46p Jeff
|
|
* display icon when someone plays an audio taunt
|
|
*
|
|
* 71 4/03/99 4:06p Jeff
|
|
* added loss/ping gauge
|
|
*
|
|
* 70 3/30/99 7:42p Jeff
|
|
* fixed a misspelling on a function name
|
|
*
|
|
* 69 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.
|
|
*
|
|
* 68 3/22/99 1:55p Jeff
|
|
* make sure initialization happens (possible crashing)
|
|
*
|
|
* 67 3/17/99 12:23p Jeff
|
|
* converted DMFC to be COM interface
|
|
*
|
|
* 66 2/11/99 12:50a Jeff
|
|
* changed names of exported variables
|
|
*
|
|
* 65 2/09/99 3:32p Jeff
|
|
* table file parser takes quotes strings for force keywords
|
|
*
|
|
* 64 2/07/99 1:19a Jeff
|
|
* added new multiplayer game events EVT_GAMEOBJKILLED and
|
|
* EVT_GAMEOBJDESTROYED
|
|
*
|
|
* 63 2/05/99 8:24p Jeff
|
|
* added table file parser macros
|
|
*
|
|
* 62 2/04/99 7:17p Jeff
|
|
* put in sounds for hud messages
|
|
*
|
|
* 61 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
|
|
*
|
|
* 60 2/01/99 10:52p Jeff
|
|
* added sounds for kills (teammate and regular)
|
|
*
|
|
* 59 1/31/99 7:26p Matt
|
|
* Renamed a bunch of functions to have HUD capitalized
|
|
*
|
|
* 58 1/19/99 3:55a Jeff
|
|
* all strings localized out
|
|
*
|
|
* 56 1/15/99 3:54a Jeff
|
|
* exported a couple more functions. Added event handlers for Weapon
|
|
* collide event
|
|
*
|
|
* 55 1/12/99 2:55p Jeff
|
|
* added/finished the waiting for player's to join dialog
|
|
*
|
|
* 54 1/10/99 7:41p Jeff
|
|
* added initial version of waitforplayers dialog
|
|
*
|
|
* 53 1/07/99 5:01p Jeff
|
|
* added Int3's and updated all net games to use stats manager...correctly
|
|
* too
|
|
*
|
|
* 52 1/06/99 7:02p Jeff
|
|
* added a multiplayer event for game controls
|
|
*
|
|
* 51 12/23/98 6:38p Kevin
|
|
* All UDP data (except gamespy) now uses one (registered) port number
|
|
*
|
|
* 50 12/08/98 4:47p Jeff
|
|
* umm, various changes, fixed pilot pics so that they work for everyone
|
|
* now
|
|
*
|
|
* 49 12/08/98 3:29p Jeff
|
|
* updated the team control dialog so the server can determine if they
|
|
* want to make the clients wait
|
|
*
|
|
* 48 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
|
|
*
|
|
* 47 12/03/98 7:05p Jeff
|
|
* updated new stats
|
|
*
|
|
* 46 12/01/98 6:56p Jeff
|
|
* put in quick and dirty implementation of pilot pics for testing
|
|
*
|
|
* 45 11/19/98 5:56p Jeff
|
|
* added slider exported and improved Hoard
|
|
*
|
|
* 44 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
|
|
*
|
|
* 43 11/16/98 5:35p Jeff
|
|
* removed log functions, added support for changing team names, fixed ctf
|
|
* to work better with different team names
|
|
*
|
|
* 42 11/11/98 7:19p Jeff
|
|
* changes made so that a dedicated server's team is always -1 (team game
|
|
* or not)
|
|
*
|
|
* 41 11/11/98 12:47p Jeff
|
|
* fixed bug where it would give team change messages in a non-team game
|
|
*
|
|
* 40 11/01/98 1:59a Jeff
|
|
* made a $help inputcommand for help in a dedicated server environment
|
|
*
|
|
* 39 10/30/98 12:47p Jeff
|
|
* cut down a couple bytes on memory usage
|
|
*
|
|
* 38 10/29/98 7:01p Jeff
|
|
* creation of team placement dialog. Moved TranslateEvent into DMFC
|
|
*
|
|
* 37 10/20/98 12:16p Jeff
|
|
* added death message filter, hud callsign filter
|
|
*
|
|
* 36 10/18/98 7:59p Jeff
|
|
* functions added to dmfc for client->server objnum matching. Banning
|
|
* now uses tracker id when available.
|
|
*
|
|
* 35 10/15/98 6:18p Jeff
|
|
* created the is player banned event, removed prejoin event
|
|
*
|
|
* 34 10/15/98 1:34p Jeff
|
|
* added scrollable onscreen menu. Remove ban in dmfc. prejoin event
|
|
*
|
|
* 33 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.
|
|
*
|
|
* 32 10/11/98 2:57a Jeff
|
|
* added new multiplayer event EVT_GAME_INTERVAL, which is to be called on
|
|
* interval, and EVT_HUD_INTERVAL is only to be called when the hud is to
|
|
* be rendered
|
|
*
|
|
* 31 10/08/98 3:37p Jeff
|
|
* general improvements (Netgame info things, save to registry). Changes
|
|
* so it would send packets on NETSEQ_OBJECTS
|
|
*
|
|
* 30 10/05/98 2:49p Jeff
|
|
*
|
|
* 29 10/01/98 7:02p Jeff
|
|
* implemented colored HUD callsigns
|
|
*
|
|
* 28 10/01/98 11:30a Jeff
|
|
* made the observer mode events into just a client event
|
|
*
|
|
* 27 9/30/98 4:21p Jeff
|
|
* team changing is handled correctly
|
|
*
|
|
* 26 9/30/98 4:07p Jeff
|
|
*
|
|
* 25 9/30/98 3:50p Jeff
|
|
* general improvements (many)
|
|
*
|
|
* 24 9/28/98 5:05p Jeff
|
|
* made the statisitical death messages an option in the menu
|
|
*
|
|
* 23 9/25/98 7:57p Jeff
|
|
* removed CheckPInfo call
|
|
*
|
|
* 22 9/25/98 7:25p Jeff
|
|
*
|
|
* 21 9/25/98 4:50p Jeff
|
|
*
|
|
* 20 9/24/98 6:54p Jeff
|
|
* added DisconnectMe() and made use of it when you get kicked/banned
|
|
*
|
|
* 19 9/24/98 5:52p Jeff
|
|
* starting adding statistical death messages, checked in for testing
|
|
*
|
|
* 18 9/23/98 4:56p Jeff
|
|
* set teams always for 1 team game
|
|
*
|
|
* 17 9/23/98 4:17p Jeff
|
|
* basic changes/improvements, started changing death messages
|
|
*
|
|
* 16 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"
|
|
|
|
#define SND_MYTEAM_TEAMMATE_KILLED "PlayerDeath"
|
|
#define SND_NONTEAM_KILL "PlayerDeath"
|
|
|
|
/*
|
|
$$TABLE_SOUND "PlayerDeath"
|
|
*/
|
|
|
|
// ######################### Client allowed event handlers############################
|
|
|
|
// DMFCBase::OnClientPlayerKilled (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a player gets killed, either by another player or some other way.
|
|
// killer_obj = object pointer to the object that killed the player
|
|
// victim_pnum = player number of the player that got killed
|
|
void DMFCBase::OnClientPlayerKilled(object *killer_obj, int victim_pnum) {
|
|
uint32_t hash = 0xFFFFFFFF;
|
|
int kpnum;
|
|
player_record *kpr, *vpr;
|
|
|
|
if (!killer_obj) {
|
|
kpnum = -1;
|
|
} else {
|
|
if ((killer_obj->type == OBJ_PLAYER) || (killer_obj->type == OBJ_GHOST) || (killer_obj->type == OBJ_OBSERVER)) {
|
|
kpnum = killer_obj->id;
|
|
|
|
// now we need to extract out the weapon of the kill
|
|
if (Data->iParam != -1)
|
|
hash = (uint32_t)Data->iParam;
|
|
} else if (killer_obj->type == OBJ_ROBOT || (killer_obj->type == OBJ_BUILDING && killer_obj->ai_info)) {
|
|
// countermeasure kill
|
|
kpnum = GetCounterMeasureOwner(killer_obj);
|
|
} else
|
|
kpnum = -1;
|
|
}
|
|
|
|
if (victim_pnum != -1) {
|
|
if (kpnum == victim_pnum) {
|
|
// suicide
|
|
vpr = PRec_GetPRecordByPnum(victim_pnum);
|
|
if (vpr) {
|
|
vpr->dstats.suicides[DSTAT_LEVEL]++;
|
|
vpr->dstats.suicides[DSTAT_OVERALL]++;
|
|
}
|
|
UpdatePInfo(kpnum, kpnum, 1);
|
|
} else {
|
|
// regular death
|
|
vpr = PRec_GetPRecordByPnum(victim_pnum);
|
|
|
|
if (vpr) {
|
|
vpr->dstats.deaths[DSTAT_LEVEL]++;
|
|
vpr->dstats.deaths[DSTAT_OVERALL]++;
|
|
}
|
|
|
|
if (kpnum != -1) {
|
|
kpr = PRec_GetPRecordByPnum(kpnum);
|
|
if (kpr) {
|
|
kpr->dstats.kills[DSTAT_LEVEL]++;
|
|
kpr->dstats.kills[DSTAT_OVERALL]++;
|
|
}
|
|
UpdatePInfo(victim_pnum, kpnum, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int teammate_kill_id = -2;
|
|
static int nonteam_kill_id = -2;
|
|
|
|
if (killer_obj && killer_obj->type == OBJ_PLAYER && victim_pnum != -1) {
|
|
|
|
if (Players[killer_obj->id].team == Players[victim_pnum].team && (GetNumTeams() >= 2) &&
|
|
(killer_obj->id != victim_pnum)) {
|
|
// a teammate killed another teammate!
|
|
if (teammate_kill_id == -2) {
|
|
teammate_kill_id = DLLFindSoundName(IGNORE_TABLE(SND_MYTEAM_TEAMMATE_KILLED));
|
|
}
|
|
if (teammate_kill_id != -1) {
|
|
DLLPlay2dSound(teammate_kill_id, MAX_GAME_VOLUME / 2);
|
|
}
|
|
} else {
|
|
// normal kill sound
|
|
if (nonteam_kill_id == -2) {
|
|
nonteam_kill_id = DLLFindSoundName(IGNORE_TABLE(SND_NONTEAM_KILL));
|
|
}
|
|
if (nonteam_kill_id != -1) {
|
|
DLLPlay2dSound(nonteam_kill_id, MAX_GAME_VOLUME / 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if DMFC is supposed to handle Deathmessages, than do one
|
|
if (m_iProtectedFlags & DMFC_PRF_AUTODEATHMSG)
|
|
DoRandomDeathMessage(GetItObjNum(), GetMeObjNum(), hash);
|
|
}
|
|
|
|
// DMFCBase::OnClientPlayerExploded (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a player explodes. Gets called after a EVT_GAMEPLAYERKILLED event.
|
|
// player_num = player number of the player exploding
|
|
void DMFCBase::OnClientPlayerExploded(int player_num) {}
|
|
|
|
// DMFCBase::OnClientCollide (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when two objects collide. At least one of the two objects is a player or a robot.
|
|
// Be aware that this event will be called twice per collision, the second time it is called the me and
|
|
// it objects will be flipped.
|
|
// ALSO NOTE: In order for the OnClientCollide() that passes the point and normal to have valid values
|
|
// the server must pass true to the parameter to send arguments in CallClientEvent().
|
|
// me_obj = object pointer to the me object
|
|
// it_obj = object pointer to the it object
|
|
void DMFCBase::OnClientCollide(object *me_obj, object *it_obj) {}
|
|
void DMFCBase::OnClientCollide(object *me_obj, object *it_obj, vector *point, vector *normal) {}
|
|
|
|
// DMFCBase::OnClientPlayerChangeSegment (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a player changes rooms or a "large" terrain cell
|
|
// (8x8 Terrain block).
|
|
// player_num = player number of the player who just changed the room/segment
|
|
// newseg = New room/cell location
|
|
// oldseg = Old room/cell location
|
|
void DMFCBase::OnClientPlayerChangeSegment(int player_num, int newseg, int oldseg) {}
|
|
|
|
// DMFCBase::OnClientObjectChangeSegment (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a player changes rooms or a "large" terrain cell
|
|
// (8x8 Terrain block).
|
|
// obj = Object pointer of the object who just changed the room/segment
|
|
// newseg = New room/cell location
|
|
// oldseg = Old room/cell location
|
|
void DMFCBase::OnClientObjectChangeSegment(object *obj, int newseg, int oldseg) {}
|
|
|
|
// DMFCBase::OnClientPlayerEntersGame (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a player enters the game. This will only get called once per
|
|
// player, it usually gets called right after they connect to the server to start playing.
|
|
// player_num = player number of the player entering the game
|
|
void DMFCBase::OnClientPlayerEntersGame(int player_num) {
|
|
if ((GetLocalRole() == LR_SERVER) || (player_num != GetPlayerNum())) {
|
|
int slot;
|
|
|
|
if (IsMasterTrackerGame())
|
|
slot = PRec_FindPlayer(Players[player_num].callsign, NULL, Players[player_num].tracker_id);
|
|
else
|
|
slot = PRec_FindPlayer(Players[player_num].callsign, &NetPlayers[player_num].addr, NULL);
|
|
|
|
if (slot == -1) {
|
|
// we have a new player
|
|
slot = PRec_GetFreeSlot();
|
|
if (!PRec_AssignPlayerToSlot(player_num, slot, Players, NetPlayers))
|
|
mprintf(0, "Unable to assign New Player (%s) to Player Record slot #%d\n", Players[player_num].callsign, slot);
|
|
else
|
|
mprintf(0, "(%s) has been assigned to Player Record slot #%d\n", Players[player_num].callsign, slot);
|
|
CallOnPlayerConnect(player_num);
|
|
} else {
|
|
// we have a reconnecting player
|
|
if (!PRec_ReconnectPlayerToSlot(player_num, slot, Players, NetPlayers))
|
|
mprintf(0, "Unable to reassign reconnecting player (%s) to Player Record slot #%d\n",
|
|
Players[player_num].callsign, slot);
|
|
else
|
|
mprintf(0, "Reconnected player (%s) to Player Record slot #%d\n", Players[player_num].callsign, slot);
|
|
CallOnPlayerReconnect(player_num);
|
|
}
|
|
}
|
|
|
|
// If the correct options are set, go into the D1/D2 style wait screen
|
|
if (GetLocalRole() == LR_SERVER && (player_num == GetPlayerNum())) {
|
|
|
|
if (GetNumTeams() > 1 && (!(m_iProtectedFlags & DMFC_PRF_NOTEAMSELECTONSTART))) {
|
|
// Display the team placement dialog
|
|
static char enable;
|
|
enable = 1;
|
|
StartUIWindow(UIID_TEAMPLACEMENT, &enable);
|
|
}
|
|
|
|
if (GetNumTeams() < 2 && (!(m_iProtectedFlags & DMFC_PRF_NOWAITFORPLAYERS))) {
|
|
StartUIWindow(UIID_WAITFORPLAYERS, NULL);
|
|
}
|
|
}
|
|
|
|
// Load up the pilot picture bitmap
|
|
//--------------------------------
|
|
if (player_num == GetPlayerNum()) {
|
|
// since we are entering the game, go through all the players and setup their bitmaps
|
|
for (int w = 0; w < DLLMAX_PLAYERS; w++) {
|
|
if (CheckPlayerNum(w) || (w == 0 && GetLocalRole() == LR_SERVER)) {
|
|
if (PilotPicBmpHandles[w] > BAD_BITMAP_HANDLE) {
|
|
DLLbm_FreeBitmap(PilotPicBmpHandles[w]);
|
|
PilotPicBmpHandles[w] = BAD_BITMAP_HANDLE;
|
|
}
|
|
|
|
mprintf(0, "PPIC ID: %d is %d\n", w, NetPlayers[w].pilot_pic_id);
|
|
if (NetPlayers[w].pilot_pic_id != 65535) {
|
|
PilotPicBmpHandles[w] = DLLPPic_GetBitmapHandle(NetPlayers[w].pilot_pic_id);
|
|
if (PilotPicBmpHandles[w] < BAD_BITMAP_HANDLE)
|
|
PilotPicBmpHandles[w] = BAD_BITMAP_HANDLE;
|
|
} else {
|
|
PilotPicBmpHandles[w] = BAD_BITMAP_HANDLE;
|
|
}
|
|
} else {
|
|
PilotPicBmpHandles[w] = BAD_BITMAP_HANDLE;
|
|
}
|
|
}
|
|
} else {
|
|
if (PilotPicBmpHandles[player_num] > BAD_BITMAP_HANDLE) {
|
|
DLLbm_FreeBitmap(PilotPicBmpHandles[player_num]);
|
|
PilotPicBmpHandles[player_num] = BAD_BITMAP_HANDLE;
|
|
}
|
|
|
|
mprintf(0, "PPIC ID: %d is %d\n", player_num, NetPlayers[player_num].pilot_pic_id);
|
|
if (NetPlayers[player_num].pilot_pic_id != 65535) {
|
|
PilotPicBmpHandles[player_num] = DLLPPic_GetBitmapHandle(NetPlayers[player_num].pilot_pic_id);
|
|
if (PilotPicBmpHandles[player_num] < BAD_BITMAP_HANDLE)
|
|
PilotPicBmpHandles[player_num] = BAD_BITMAP_HANDLE;
|
|
} else {
|
|
PilotPicBmpHandles[player_num] = BAD_BITMAP_HANDLE;
|
|
}
|
|
}
|
|
|
|
// Play the player hud message sound
|
|
static int sound_id = -2;
|
|
if (sound_id == -2) {
|
|
sound_id = DLLFindSoundName("Hudmessage");
|
|
}
|
|
|
|
if (sound_id > -1 && GetPlayerNum() != player_num) {
|
|
DLLPlay2dSound(sound_id, MAX_GAME_VOLUME / 2);
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnClientPlayerDisconnect
|
|
//
|
|
// Event handler for when a player disconnects from the server.
|
|
// player_num = player number of the player that just disconnected
|
|
void DMFCBase::OnClientPlayerDisconnect(int player_num) {
|
|
// Play the player hud message sound
|
|
static int sound_id = -2;
|
|
if (sound_id == -2) {
|
|
sound_id = DLLFindSoundName("Hudmessage");
|
|
}
|
|
|
|
if (sound_id > -1 && GetPlayerNum() != player_num) {
|
|
DLLPlay2dSound(sound_id, MAX_GAME_VOLUME / 2);
|
|
}
|
|
|
|
// save the team
|
|
PRec_SetPlayerTeam(player_num, Players[player_num].team);
|
|
|
|
if (!PRec_DisconnectPlayer(player_num))
|
|
mprintf(0, "Unable to disconnect player (%s) from Player Records\n", Players[player_num].callsign);
|
|
else
|
|
mprintf(0, "Disconnected player (%s) from Player Records\n", Players[player_num].callsign);
|
|
}
|
|
|
|
// DMFCBase::OnMeDisconnectFromServer
|
|
//
|
|
// Event handler that gets called if we disconnect from the server for some reason (not purposesly quit)
|
|
void DMFCBase::OnMeDisconnectFromServer(void) {
|
|
// We're disconnecting so save the stats to file
|
|
if (m_iProtectedFlags & DMFC_PRF_AUTOSAVEDISC)
|
|
CallOnDisconnectSaveStatsToFile();
|
|
}
|
|
|
|
// DMFCBase::OnClientGameCreated (Only called if server tells client to execute)
|
|
//
|
|
// Event handler when the server's game first gets started. This will only be called once
|
|
// while the server is running, and that is when the server first starts up.
|
|
void DMFCBase::OnClientGameCreated(void) {}
|
|
|
|
// DMFCBase::OnClientLevelChange (Only called if server tells client to execute)
|
|
//
|
|
// Event handler when the server changes levels. This will get called after a level ends and a server
|
|
// is starting a new level.
|
|
void DMFCBase::OnClientLevelChange(void) {}
|
|
|
|
// DMFCBase::OnClientLevelStart (Only called if server tells client to execute)
|
|
//
|
|
// Event handler for when a multiplayer level is starting up. This will get called right before the level
|
|
// starts.
|
|
void DMFCBase::OnClientLevelStart(void) {
|
|
// Clear this array so we don't get weird anomalies
|
|
ResetPlayersInGame();
|
|
|
|
// Reset the timers
|
|
InitTimers();
|
|
|
|
// Reset level stats of Player Records
|
|
PRec_LevelReset();
|
|
|
|
// reset the pinfo list
|
|
ResetPInfo();
|
|
|
|
// Request Player Records
|
|
SendRequestForPlayerRecords();
|
|
|
|
DisplayTauntIndicator = false;
|
|
}
|
|
|
|
// DMFCBase::OnClientLevelEnd
|
|
//
|
|
// Event handler for when a multiplayer level is ending.
|
|
void DMFCBase::OnClientLevelEnd(void) {
|
|
MarkPlayersInGame();
|
|
|
|
if (m_iProtectedFlags & DMFC_PRF_AUTOSAVELEVELEND)
|
|
CallOnLevelEndSaveStatsToFile();
|
|
|
|
KillAllTimers();
|
|
ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PLRFIRSTTIME);
|
|
|
|
// Call PLRInit so data can be initialized
|
|
CallOnPLRInit();
|
|
|
|
for (int i = 0; i < DLLMAX_PLAYERS; i++) {
|
|
if ((NetPlayers[i].flags & NPF_CONNECTED) &&
|
|
(NetPlayers[i].sequence >= NETSEQ_PLAYING || NetPlayers[i].sequence == NETSEQ_WAITING_FOR_LEVEL)) {
|
|
|
|
// save the team
|
|
PRec_SetPlayerTeam(i, Players[i].team);
|
|
|
|
if (!PRec_DisconnectPlayer(i))
|
|
mprintf(0, "Unable to disconnect player (%s) from Player Records\n", Players[i].callsign);
|
|
else
|
|
mprintf(0, "Disconnected player (%s) from Player Records\n", Players[i].callsign);
|
|
}
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnPlayerEntersObserver
|
|
//
|
|
// Event handler when a player becomes an observer mode
|
|
// If they are piggybacking another player than piggy is the object pointer, else it's NULL
|
|
void DMFCBase::OnPlayerEntersObserver(int pnum, object *piggy) {
|
|
mprintf(0, "Player %d entering observermode %s\n", pnum, (piggy) ? "Piggyback" : "Roam");
|
|
player_record *pr = PRec_GetPRecordByPnum(pnum);
|
|
if (pr && pr->state == STATE_INGAME) {
|
|
PInfo *pi = pr->pinfo;
|
|
ASSERT(pi != NULL);
|
|
pi->EnterObserverMode();
|
|
} else {
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnPlayerExitsObserver
|
|
//
|
|
// Event handler when a player is leaving observer mode
|
|
void DMFCBase::OnPlayerExitsObserver(int pnum) {
|
|
mprintf(0, "Player %d leaving observer mode\n", pnum);
|
|
player_record *pr = PRec_GetPRecordByPnum(pnum);
|
|
if (pr && pr->state == STATE_INGAME) {
|
|
PInfo *pi = pr->pinfo;
|
|
ASSERT(pi != NULL);
|
|
pi->ExitObserverMode();
|
|
} else {
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnCanChangeTeam
|
|
//
|
|
// Called to determine if a player can change teams, you can override this so the server won't
|
|
// let a player change teams if it is not desired (for that player)
|
|
bool DMFCBase::OnCanChangeTeam(int pnum, int newteam) {
|
|
if (Players[pnum].team == -1)
|
|
return false; // no team switching for dedicated server
|
|
|
|
// AnnounceTeamChangeDeny(pnum);
|
|
return true;
|
|
}
|
|
|
|
// DMFCBase::OnSaveStatsToFile
|
|
//
|
|
//
|
|
// The user is requesting the game stats to be saved to file, you must handle this completly on
|
|
// the game's side, nothing is done in DMFC
|
|
void DMFCBase::OnSaveStatsToFile(void) { mprintf(0, "User requested stats to be saved!\n"); }
|
|
|
|
// DMFCBase::OnLevelEndSaveStatsToFile
|
|
//
|
|
//
|
|
// The game should save the "End of Level" stats to file. You must handle this completly on
|
|
// the game's side, nothing is done in DMFC
|
|
void DMFCBase::OnLevelEndSaveStatsToFile(void) {}
|
|
|
|
// DMFCBase::OnDisconnectSaveStatsToFile
|
|
//
|
|
//
|
|
// The game should save the stats because the player (only ourself as the client in the game),
|
|
// has disconnected.
|
|
void DMFCBase::OnDisconnectSaveStatsToFile(void) {}
|
|
|
|
// DMFCBase::OnSpecialPacket
|
|
//
|
|
// Event handler for when a special packet arrives and needs to be processed.
|
|
// Both the server and client can get this event, although it is more common for the
|
|
// client to recieve these.
|
|
void DMFCBase::OnSpecialPacket(void) {
|
|
if (!SPRoot)
|
|
return;
|
|
|
|
// see if we have a handler for the ID, if so, call the handler, else do nothing
|
|
uint8_t *data = Data->special_data;
|
|
int id = data[0];
|
|
|
|
tSPHandler *current;
|
|
current = SPRoot;
|
|
while (current) {
|
|
if (current->ID == id) {
|
|
// we got a winner
|
|
switch (current->type) {
|
|
case SPH_DMFCFUNC:
|
|
if (current->DMFCfunc)
|
|
(this->*current->DMFCfunc)(data + 1);
|
|
break;
|
|
case SPH_FUNC:
|
|
if (current->func)
|
|
(*current->func)(data + 1);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnHUDInterval
|
|
//
|
|
// Event handler that gets called once a frame when it's time to render the HUD images
|
|
void DMFCBase::OnHUDInterval(void) {
|
|
if (m_bMakeClientsWait)
|
|
WaitingForServerLoop();
|
|
if (m_iProtectedFlags & DMFC_PRF_DISPPLAYERINFO)
|
|
DisplayPlayerInfo(MenuBackgroundBMP);
|
|
if (m_iProtectedFlags & DMFC_PRF_DISPNETGAMEINFO)
|
|
DisplayNetGameInfo(MenuBackgroundBMP);
|
|
LossGuageFrame();
|
|
DisplayTauntIndicatorFrame();
|
|
|
|
if (m_iProtectedFlags & DMFC_PRF_INOPTIONS) {
|
|
// Update all the state submenus so they are reporting the correct value
|
|
if (TeamMenuItem)
|
|
TeamMenuItem->SetState(GetPlayerTeam(GetPlayerNum()));
|
|
if (AutoBalanceItem)
|
|
AutoBalanceItem->SetState((m_iProtectedFlags & DMFC_PRF_AUTOTEAMSELECT) ? 1 : 0);
|
|
if (AllowTeamChangeItem)
|
|
AllowTeamChangeItem->SetState(AllowTeamChange() ? 1 : 0);
|
|
if (ObserverItem)
|
|
ObserverItem->SetState((Objects[Players[GetPlayerNum()].objnum].type == OBJ_OBSERVER) ? 1 : 0);
|
|
if (StatisticMessagesItem)
|
|
StatisticMessagesItem->SetState((m_iProtectedFlags & DMFC_PRF_DISPSTATHUDMSGS) ? 1 : 0);
|
|
if (SaveStatsLevelEndItem)
|
|
SaveStatsLevelEndItem->SetState((m_iProtectedFlags & DMFC_PRF_AUTOSAVELEVELEND) ? 1 : 0);
|
|
if (SaveStatsDisconnectItem)
|
|
SaveStatsDisconnectItem->SetState((m_iProtectedFlags & DMFC_PRF_AUTOSAVEDISC) ? 1 : 0);
|
|
if (MenuBackgroundItem)
|
|
MenuBackgroundItem->SetState((m_iProtectedFlags & DMFC_PRF_DISPMENUBACKGR) ? 1 : 0);
|
|
if (NetGameInfoItem)
|
|
NetGameInfoItem->SetState(IsDisplayingNetGameInfo() ? 1 : 0);
|
|
if (HUDCallsignsItem)
|
|
HUDCallsignsItem->SetState(m_iMyCurrentHUDCallsignLevel);
|
|
if (ServerHUDCallsignsItem)
|
|
ServerHUDCallsignsItem->SetState(m_iServerHUDCallsignLevel);
|
|
if (DeathMessageFilterItem)
|
|
DeathMessageFilterItem->SetState(m_iDeathMessageFilter);
|
|
if (ShipLogosItem)
|
|
ShipLogosItem->SetState(AreLogosEnabled() ? 1 : 0);
|
|
if (HUDIndicatorItem)
|
|
HUDIndicatorItem->SetState(LossGuageEnabled ? 1 : 0);
|
|
|
|
// We have an options screen up, so draw it
|
|
DrawMenu();
|
|
}
|
|
|
|
// DisplayPilotPicsFrame();
|
|
|
|
// Display countdown if needed
|
|
DisplayTimelimitCountdown();
|
|
|
|
DLLgrtext_Flush();
|
|
}
|
|
|
|
// DMFCBase::OnInterval
|
|
//
|
|
// Event handler that gets called once a frame
|
|
void DMFCBase::OnInterval(void) {
|
|
if (!(m_iProtectedFlags & DMFC_PRF_PAUSETIME)) {
|
|
RealGametime += *Frametime; // increment the timer
|
|
}
|
|
|
|
// If we are a dedicated server, we are waiting, and there is a wait time
|
|
// check to see if it's time to disable
|
|
if (GetLocalRole() == LR_SERVER && IAmDedicatedServer() && m_bMakeClientsWait) {
|
|
if (DedicatedLevelWait > 0 && DedicatedLevelWait <= (*Gametime)) {
|
|
// time to stop waiting!
|
|
mprintf(0, "Telling clients that they can play!\n");
|
|
DPrintf("Allowing Clients To Play\n");
|
|
PauseRealGameTime(false);
|
|
MakeClientsWait(false);
|
|
}
|
|
}
|
|
|
|
// CheckPInfo();
|
|
|
|
// Process remote admin frame
|
|
Remote_ProcessFrame();
|
|
|
|
// Process all currently running timers
|
|
ProcessTimers();
|
|
|
|
// Handle time limit games
|
|
float tleft;
|
|
if (GetLocalRole() == LR_SERVER) {
|
|
if ((m_iProtectedFlags & DMFC_PRF_AUTOTIMELIMIT) && (GetTimeLeft(&tleft))) {
|
|
if (tleft <= 0) {
|
|
EndLevel();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnPLRInterval
|
|
//
|
|
// Event handler that gets called once a frame when the Post Level Results screen is being display
|
|
void DMFCBase::OnPLRInterval(void) {
|
|
DLLgrtext_SetFont(Game_fonts[SMALL_UI_FONT_INDEX]);
|
|
DLLgrtext_SetColor(GR_RGB(255, 40, 40));
|
|
DLLgrtext_CenteredPrintf(0, 13, Netgame->scriptname);
|
|
DLLgrtext_CenteredPrintf(0, 461, DTXT_PRESSPRNTSCRN);
|
|
}
|
|
|
|
// DMFCBase::OnPLRInit
|
|
//
|
|
// Event handler that gets called the first frame of the PLR screen for each level
|
|
void DMFCBase::OnPLRInit(void) { DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PLRFIRSTTIME); }
|
|
|
|
// DMFCBase::OnKeypress
|
|
//
|
|
// Event handler for when a user presses a key while in the game
|
|
// key = Key code of the key being pressed
|
|
void DMFCBase::OnKeypress(int key) {
|
|
if (m_iProtectedFlags & DMFC_PRF_INOPTIONS) {
|
|
Data->iRet = 1;
|
|
// Handle keyboard input for an options menu
|
|
if (key == K_F6 || key == K_ESC) { // escape out of options
|
|
DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_INOPTIONS);
|
|
}
|
|
if (key == K_ENTER) {
|
|
Menu.Execute();
|
|
}
|
|
switch (key) {
|
|
case K_DOWN: {
|
|
Menu.Down();
|
|
} break;
|
|
case K_UP: {
|
|
Menu.Up();
|
|
} break;
|
|
case K_RIGHT: {
|
|
Menu.Forward();
|
|
} break;
|
|
case K_LEFT: {
|
|
Menu.Back();
|
|
} break;
|
|
}
|
|
} else if (key == K_F6) {
|
|
ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_INOPTIONS);
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnInputString
|
|
//
|
|
// Event handler for when the user types a message at the console (F8) that begins with a $
|
|
// input_string = string that was typed
|
|
void DMFCBase::OnInputString(char *input_string) {
|
|
if (!InputCommandHandle(input_string)) {
|
|
// The input command wasn't handled!
|
|
mprintf(0, "DMFC Warning: Input Command '%s' wasn't handled\n", input_string);
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnPlayerChangeTeam
|
|
//
|
|
// Called when a player changes team
|
|
void DMFCBase::OnPlayerChangeTeam(int player_num, int newteam, bool announce, bool spew_everything) {
|
|
player_record *pr = GetPlayerRecordByPnum(player_num);
|
|
if (pr) {
|
|
if (GetNumTeams() > 1 && announce && newteam != -1)
|
|
DLLAddHUDMessage(DTXT_CHANGETEAM, pr->callsign, GetTeamString(newteam));
|
|
pr->team = newteam;
|
|
|
|
// only respawn them if they are not dead
|
|
if (!((Players[player_num].flags & PLAYER_FLAGS_DYING) || (Players[player_num].flags & PLAYER_FLAGS_DEAD))) {
|
|
RespawnPlayer(player_num, false, spew_everything);
|
|
}
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnClientObjectShieldsChanged
|
|
//
|
|
// Event handler for when an objects shield's change
|
|
void DMFCBase::OnClientObjectShieldsChanged(object *obj, float amount) {}
|
|
|
|
// DMFCBase::OnWeaponFired
|
|
//
|
|
// Event handler for when an object fires a weapon
|
|
void DMFCBase::OnWeaponFired(object *weapon_obj, object *shooter) {}
|
|
|
|
// DMFCBase::OnPlayerReconnect
|
|
//
|
|
//
|
|
// This player is reconnecting to the game
|
|
void DMFCBase::OnPlayerReconnect(int player_num) {
|
|
if (GetLocalRole() == LR_SERVER) {
|
|
// restore their team
|
|
int team = PRec_GetPlayerTeam(player_num);
|
|
if (team != Players[player_num].team) {
|
|
// hey! Jason hasn't set the correct team for this player
|
|
ASSERT(player_num == 0); // this is only going to happen for the server
|
|
}
|
|
|
|
SendTeamAssignment(player_num, team, false);
|
|
Players[player_num].team = team;
|
|
CallOnPlayerChangeTeam(player_num, team, false, false);
|
|
mprintf(0, "Reassigning (%s) to %s team\n", Players[player_num].callsign, GetTeamString(team));
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnPlayerConnect
|
|
//
|
|
//
|
|
// This player is connecting to the game for the first time
|
|
void DMFCBase::OnPlayerConnect(int player_num) {
|
|
if (GetLocalRole() == LR_SERVER) {
|
|
|
|
// a dedicated server MUST be given a -1 team
|
|
//------------------------------------------
|
|
if (player_num == 0 && IAmDedicatedServer()) {
|
|
Players[0].team = -1;
|
|
}
|
|
|
|
// get team assignment
|
|
if (m_iProtectedFlags & DMFC_PRF_AUTOTEAMSELECT) {
|
|
// JEFF: Commented out since team's are now determined right when a client connects
|
|
//@@int team = GetTeamForNewPlayer(player_num,m_iNumTeams);
|
|
int team = Players[player_num].team;
|
|
SendTeamAssignment(player_num, team, false);
|
|
//@@Players[player_num].team = team;
|
|
CallOnPlayerChangeTeam(player_num, team, false, false);
|
|
mprintf(0, "Assigning (%s) to %s team\n", Players[player_num].callsign, GetTeamString(team));
|
|
}
|
|
}
|
|
}
|
|
|
|
// DMFCBase::OnControlMessage
|
|
//
|
|
//
|
|
// There is a control message sent from someone
|
|
void DMFCBase::OnControlMessage(uint8_t msg, int from_pnum) {
|
|
switch (msg) {
|
|
case CM_KICKED: {
|
|
DLLAddHUDMessage(DTXT_KICKEDMSG);
|
|
DisconnectMe();
|
|
} break;
|
|
case CM_BANNED: {
|
|
DLLAddHUDMessage(DTXT_BANNEDMSG);
|
|
DisconnectMe();
|
|
} break;
|
|
case CM_TEAMCHANGEDENIED: {
|
|
// if we get this, the server is telling us that the request for a
|
|
// team change has been denied
|
|
AnnounceTeamChangeDeny(*Player_num);
|
|
} break;
|
|
case CM_SIGNALSTART: {
|
|
if (from_pnum == 0) {
|
|
// we can move!!
|
|
WaitForServer(false);
|
|
}
|
|
} break;
|
|
case CM_SIGNALWAIT: {
|
|
if (from_pnum == 0) {
|
|
// we got to wait
|
|
WaitForServer(true);
|
|
}
|
|
} break;
|
|
case CM_PAUSETIME: {
|
|
ENABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PAUSETIME);
|
|
} break;
|
|
case CM_UNPAUSETIME: {
|
|
DISABLE_FLAGS(m_iProtectedFlags, DMFC_PRF_PAUSETIME);
|
|
} break;
|
|
};
|
|
}
|
|
|
|
// DMFCBase::OnAllowObserverChange
|
|
//
|
|
//
|
|
// returns true if the requested change for observer mode should go through
|
|
bool DMFCBase::OnAllowObserverChange(bool turnonobserver) { return true; }
|
|
|
|
// DMFCBase::OnClientShowUI
|
|
//
|
|
//
|
|
// The game is saying it's ok to do any UI
|
|
void DMFCBase::OnClientShowUI(int id, void *user_data) {
|
|
switch (id) {
|
|
case UIID_TEAMPLACEMENT: {
|
|
bool clients_wait;
|
|
bool call_from_game;
|
|
uint8_t d = *(uint8_t *)user_data;
|
|
|
|
clients_wait = (d & 0x01) ? true : false;
|
|
call_from_game = (d & 0x02) ? false : true;
|
|
|
|
DoDMFCUITeamPlacement(clients_wait, call_from_game);
|
|
} break;
|
|
case UIID_WAITFORPLAYERS: {
|
|
DoDMFCUIWaitForPlayers(true);
|
|
} break;
|
|
};
|
|
}
|
|
|
|
// DMFCBase::OnPrintScores
|
|
//
|
|
//
|
|
// The user is requesting that the scores be printed out (For Dedicated Server use). Use DPrintf
|
|
// level: -1 Requesting all the available scores
|
|
// n Print only the top n scores
|
|
// Override this to how you see fit, but it should conform to the above.
|
|
void DMFCBase::OnPrintScores(int level) {}
|
|
|
|
// DMFCBase::OnGetHudCallSignColor
|
|
//
|
|
// This is an event sent in by the game requesting what color it should draw the HUD callsign
|
|
// of the passed in playernum. Using GR_RGB return the color from the function. This function
|
|
// gets called every frame that the player is on the clients HUD.
|
|
ddgr_color DMFCBase::OnGetHudCallSignColor(int playernum) {
|
|
if (!CheckPlayerNum(playernum))
|
|
return GR_RGB(255, 255, 255);
|
|
|
|
if (GetNumTeams() <= 1)
|
|
return GR_RGB(0, 255, 0);
|
|
|
|
return GetTeamColor(GetPlayerTeam(playernum));
|
|
}
|
|
|
|
// DMFCBase::OnTeamChangeName
|
|
//
|
|
// This event occurs on the client when a team's name has just changed
|
|
void DMFCBase::OnTeamChangeName(int team, char *oldname, char *newname) {
|
|
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::OnDoControls
|
|
//
|
|
// This event occurs when there is a new back of game controls data
|
|
void DMFCBase::OnDoControls(game_controls *controls) {
|
|
memcpy(&Last_game_controls, controls, sizeof(Last_game_controls));
|
|
}
|
|
|
|
// DMFCBase::OnClientWallCollide
|
|
//
|
|
// Called by the game when their is a collision between an object and a wall
|
|
void DMFCBase::OnClientWallCollide(object *obj, float hitspeed, int hitseg, int hitwall, vector *hitpt,
|
|
vector *wall_normal, float hit_dot) {}
|
|
|
|
// DMFCBase::OnClientObjectKilled
|
|
//
|
|
// Called when an object is being killed
|
|
// Not automatically sent to clients (OnClientObjectKilled)
|
|
// killer might not be valid (NULL)
|
|
void DMFCBase::OnClientObjectKilled(object *obj, object *killer) {}
|
|
|
|
// DMFCBase::OnClientObjectDestroyed
|
|
//
|
|
// Called when an object is about to be deleted
|
|
// Not automatically sent to clients
|
|
void DMFCBase::OnClientObjectDestroyed(object *obj) {}
|
|
|
|
// DMFCBase::OnPlayAudioTaunt
|
|
//
|
|
// This event occurs when a player plays an audio taunt
|
|
void DMFCBase::OnPlayAudioTaunt(int pnum) {
|
|
mprintf(0, "%s plays an audio taunt\n", Players[pnum].callsign);
|
|
SetPlayerTauntIndicator(pnum);
|
|
}
|
|
|
|
// DMFCBase::OnGetTokenString
|
|
//
|
|
// This event occurs when a text macro is being used that has tokens
|
|
// in it, if you have a specific token, override this event to handle it.
|
|
void DMFCBase::OnGetTokenString(char *src, char *dest, int dest_size) {
|
|
*dest = '\0';
|
|
|
|
if (!stricmp(src, "e")) {
|
|
// energy
|
|
char buffer[64];
|
|
int mypnum = GetPlayerNum();
|
|
snprintf(buffer, sizeof(buffer), "%.0f", Players[mypnum].energy);
|
|
strncpy(dest, buffer, dest_size - 1);
|
|
dest[dest_size - 1] = '\0';
|
|
return;
|
|
}
|
|
|
|
if (!stricmp(src, "s")) {
|
|
// shields
|
|
char buffer[64];
|
|
int mypnum = GetPlayerNum();
|
|
snprintf(buffer, sizeof(buffer), "%.0f", Objects[Players[mypnum].objnum].shields);
|
|
strncpy(dest, buffer, dest_size - 1);
|
|
dest[dest_size - 1] = '\0';
|
|
return;
|
|
}
|
|
}
|