UFO: Alien Invasion
Loading...
Searching...
No Matches
cl_game_multiplayer.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24*/
25
26#include "../../cl_shared.h"
27#include "../cl_game.h"
28#include "cl_game_multiplayer.h"
29#include "mp_callbacks.h"
30#include "mp_serverlist.h"
31#include "../../ui/ui_data.h"
32
33static const cgame_import_t* cgi;
34
36
37static void GAME_MP_StartBattlescape (bool isTeamPlay)
38{
39 cgi->UI_ExecuteConfunc("multiplayer_setTeamplay %i", isTeamPlay);
40 cgi->UI_InitStack("multiplayer_wait", nullptr);
41 rcon_client_password->modified = true;
42}
43
44static void GAME_MP_NotifyEvent (event_t eventType)
45{
46 if (eventType != EV_RESET)
47 return;
48
49 cgi->HUD_InitUI("missionoptions");
50}
51
52static void GAME_MP_EndRoundAnnounce (int playerNum, int team)
53{
54 char buf[128];
55
56 /* it was our own turn */
57 if (cgi->CL_GetPlayerNum() == playerNum) {
58 Com_sprintf(buf, sizeof(buf), _("You've ended your turn.\n"));
59 } else {
60 const char* playerName = cgi->CL_PlayerGetName(playerNum);
61 Com_sprintf(buf, sizeof(buf), _("%s ended his turn (team %i).\n"), playerName, team);
62 }
63 /* add translated message to chat buffer */
64 cgi->HUD_DisplayMessage(buf);
65}
66
71static void GAME_MP_StartServer_f (void)
72{
73 if (!cgi->Cvar_GetInteger("sv_dedicated") && cgi->GAME_IsTeamEmpty())
74 cgi->GAME_AutoTeam("multiplayer_initial", cgi->GAME_GetCharacterArraySize());
75
76 if (cgi->Cvar_GetInteger("sv_teamplay")
77 && cl_maxsoldiersperplayer->integer > cl_maxsoldiersperteam->integer) {
78 cgi->UI_Popup(_("Settings doesn't make sense"), _("Set soldiers per player lower than soldiers per team"));
79 return;
80 }
81
82 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
83 if (!md || !md->multiplayer)
84 return;
85 assert(md->mapTheme);
86
88 cgi->Cvar_Set("rm_drop", "%s", "");
89 cgi->Cvar_Set("rm_ufo", "%s", "");
90 cgi->Cvar_Set("sv_hurtaliens", "0");
91
92 if (md->mapTheme[0] == '+') {
93 const linkedList_t* const ufos = md->ufos;
94 const linkedList_t* const crafts = md->aircraft;
95 if (ufos)
96 cgi->Cvar_Set("rm_ufo", "%s",
97 cgi->Com_GetRandomMapAssemblyNameForCraft((const char*)ufos->data));
98 if (crafts)
99 cgi->Cvar_Set("rm_drop", "%s",
100 cgi->Com_GetRandomMapAssemblyNameForCraft((const char*)crafts->data));
101 }
102
103 if (md->teams)
104 cgi->Cvar_SetValue("sv_maxteams", md->teams);
105 else
106 cgi->Cvar_SetValue("sv_maxteams", 2);
107
108 cgi->Cmd_ExecuteString("map %s %s %s", cgi->Cvar_GetInteger("mn_serverday") ? "day" : "night", md->mapTheme, md->params ? (const char*)cgi->LIST_GetRandom(md->params) : "");
109
110 cgi->UI_InitStack("multiplayer_wait", "missionoptions");
111}
112
125static void GAME_MP_Results (dbuffer* msg, int winner, int* numSpawned, int* numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
126{
127 /* HACK: Change to the main menu now so cgame shutdown won't kill the results screen by doing it later */
128 cgi->UI_InitStack("main", "");
129
130 linkedList_t* list = nullptr;
131 int enemiesKilled = 0, enemiesStunned = 0;
132 const int team = cgi->GAME_GetCurrentTeam();
133
134 for (int i = 0; i < MAX_TEAMS; i++) {
135 if (i == team)
136 continue;
137 enemiesKilled += numKilled[team][i];
138 enemiesStunned += numStunned[team][i];
139 }
140
141 cgi->LIST_AddString(&list, va(_("Enemies killed:\t%i"), enemiesKilled + enemiesStunned));
142 cgi->LIST_AddString(&list, va(_("Team survivors:\t%i"), numAlive[team]));
143 cgi->UI_RegisterLinkedListText(TEXT_LIST2, list);
144 if (winner == team) {
145 cgi->UI_PushWindow("won");
146 } else {
147 cgi->UI_PushWindow("lost");
148 }
149}
150
151static const mapDef_t* GAME_MP_MapInfo (int step)
152{
153 int i = 0;
154 const char* gameType = cgi->Cvar_GetString("sv_gametype");
155 for (;;) {
156 i++;
157 if (i > 100000)
158 break;
159
160 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
161 if (md == nullptr)
162 break;
163 if (!md->multiplayer || !cgi->LIST_ContainsString(md->gameTypes, gameType)) {
164 cgi->GAME_SwitchCurrentSelectedMap(step ? step : 1);
165 continue;
166 }
167 linkedList_t* gameNames = nullptr;
168 for (int j = 0; j < cgi->csi->numGTs; j++) {
169 const gametype_t* gt = &cgi->csi->gts[j];
170 if (cgi->LIST_ContainsString(md->gameTypes, gt->id)) {
171 cgi->LIST_AddString(&gameNames, _(gt->name));
172 }
173 }
174 cgi->UI_RegisterLinkedListText(TEXT_LIST2, gameNames);
175 cgi->Cvar_SetValue("ai_singleplayeraliens", md->maxAliens);
176
177 return md;
178 }
179
180 cgi->Com_Printf("no multiplayer map found for the current selected gametype: '%s'", gameType);
181 return nullptr;
182}
183
187static void GAME_MP_UpdateGametype_f (void)
188{
189 const int numGTs = cgi->csi->numGTs;
190 /* no types defined or parsed */
191 if (numGTs == 0)
192 return;
193
194 cgi->Com_SetGameType();
195
196 const char* gameType = cgi->Cvar_GetString("sv_gametype");
197 const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
198 if (md != nullptr && md->multiplayer && cgi->LIST_ContainsString(md->gameTypes, gameType)) {
199 /* no change needed, gametype is supported */
200 return;
201 }
202
204}
205
207
211static void GAME_MP_AddChatMessage (const char* text)
212{
213 char message[2048];
214 Q_strncpyz(message, text, sizeof(message));
215
216 const char* msg = Com_Trim(message);
217 cgi->LIST_AddString(&mp_chatMessageStack, msg);
218 cgi->HUD_DisplayMessage(msg);
219 cgi->UI_RegisterLinkedListText(TEXT_CHAT_WINDOW, mp_chatMessageStack);
220 cgi->UI_TextScrollEnd("hud_chat.allchats.chatscreen.chat");
221}
222
223static bool GAME_MP_HandleServerCommand (const char* command, dbuffer* msg)
224{
225 if (Q_streq(command, SV_CMD_TEAMINFO)) {
227 return true;
228 }
229
230 return false;
231}
232
233static const cmdList_t multiplayerCmds[] = {
234 {"mp_startserver", GAME_MP_StartServer_f, nullptr},
235 {"mp_updategametype", GAME_MP_UpdateGametype_f, "Update the menu values with current gametype values"},
236 {nullptr, nullptr, nullptr}
237};
238static void GAME_MP_InitStartup (void)
239{
240 cgi->Cvar_ForceSet("sv_maxclients", "2");
242 cgi->Cvar_Set("cl_equip", "multiplayer_initial");
243
244 cgi->Cmd_TableAddList(multiplayerCmds);
247}
248
249static void GAME_MP_Shutdown (void)
250{
251 cgi->Cmd_TableRemoveList(multiplayerCmds);
254
255 cgi->SV_Shutdown("Game mode shutdown", false);
256
258}
259
260static void GAME_MP_RunFrame (float secondsSinceLastFrame)
261{
262 if (!cgi->Com_ServerState() && cgi->CL_GetClientState() < ca_connected && Q_strnull(cgi->Cvar_GetString("rcon_address")))
263 return;
264
265 if (rcon_client_password->modified) {
266 rcon_client_password->modified = false;
267 if (!cgi->Com_ServerState() && Q_strnull(rcon_client_password->string)) {
268 cgi->UI_ExecuteConfunc("multiplayer_admin_panel 0");
269 } else {
270 cgi->UI_ExecuteConfunc("multiplayer_admin_panel 1");
271 }
272 }
273
275 for (int i = 0; i < lengthof(cvars); i++) {
276 if (!cvars[i]->modified) {
277 continue;
278 }
279 cvars[i]->modified = false;
280 if (!cgi->Com_ServerState()) {
281 cgi->Cmd_ExecuteString(SV_CMD_RCON " set %s %s", cvars[i]->name, cvars[i]->string);
282 }
283 }
284}
285
286#ifndef HARD_LINKED_CGAME
288#else
289const cgame_export_t* GetCGameMultiplayerAPI (const cgame_import_t* import)
290#endif
291{
292 static cgame_export_t e;
293
294 OBJZERO(e);
295
296 e.name = "Multiplayer mode";
297 e.menu = "multiplayer";
298 e.isMultiplayer = 1;
299 e.Init = GAME_MP_InitStartup;
300 e.Shutdown = GAME_MP_Shutdown;
302 e.Results = GAME_MP_Results;
303 e.EndRoundAnnounce = GAME_MP_EndRoundAnnounce;
304 e.StartBattlescape = GAME_MP_StartBattlescape;
305 e.NotifyEvent = GAME_MP_NotifyEvent;
306 e.AddChatMessage = GAME_MP_AddChatMessage;
307 e.HandleServerCommand = GAME_MP_HandleServerCommand;
308 e.RunFrame = GAME_MP_RunFrame;
309
310 cgi = import;
311
312 return &e;
313}
Shared game type headers.
#define CGAME_HARD_LINKED_FUNCTIONS
Definition cl_game.h:91
static void GAME_MP_Results(dbuffer *msg, int winner, int *numSpawned, int *numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
After a mission was finished this function is called.
static void GAME_MP_UpdateGametype_f(void)
Update the map according to the gametype.
static CGAME_HARD_LINKED_FUNCTIONS void GAME_MP_StartBattlescape(bool isTeamPlay)
static void GAME_MP_NotifyEvent(event_t eventType)
static void GAME_MP_StartServer_f(void)
Starts a server and checks if the server loads a team unless he is a dedicated server admin.
static void GAME_MP_InitStartup(void)
static void GAME_MP_AddChatMessage(const char *text)
Displays a chat on the hud and add it to the chat buffer.
static linkedList_t * mp_chatMessageStack
static const mapDef_t * GAME_MP_MapInfo(int step)
static const cmdList_t multiplayerCmds[]
static void GAME_MP_RunFrame(float secondsSinceLastFrame)
static void GAME_MP_EndRoundAnnounce(int playerNum, int team)
const cgame_export_t * GetCGameAPI(const cgame_import_t *import)
static void GAME_MP_Shutdown(void)
static bool GAME_MP_HandleServerCommand(const char *command, dbuffer *msg)
Multiplayer game type headers.
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
@ ca_connected
Definition cl_shared.h:79
const cgame_import_t * cgi
#define MAX_TEAMS
Definition defines.h:98
voidpf void * buf
Definition ioapi.h:42
void GAME_MP_CallbacksInit(const cgame_import_t *import)
void GAME_MP_CallbacksShutdown(void)
cvar_t * cl_roundtimelimit
teamData_t teamData
cvar_t * rcon_client_password
cvar_t * cl_maxsoldiersperteam
cvar_t * cl_maxsoldiersperplayer
Serverlist menu callbacks headers for multiplayer.
void GAME_MP_ServerListShutdown(void)
void GAME_MP_ParseTeamInfoMessage(dbuffer *msg)
Team selection text.
void GAME_MP_ServerListInit(const cgame_import_t *import)
Serverlist management headers for multiplayer.
event_t
Possible event values.
Definition q_shared.h:79
@ EV_RESET
Definition q_shared.h:81
#define SV_CMD_RCON
Definition q_shared.h:589
#define SV_CMD_TEAMINFO
Definition q_shared.h:591
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
#define Q_streq(a, b)
Definition shared.h:136
bool Q_strnull(const char *string)
Definition shared.h:138
#define OBJZERO(obj)
Definition shared.h:178
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
char * Com_Trim(char *s)
Removed leading and trailing whitespaces.
Definition shared.cpp:65
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
const mapDef_t *EXPORT * MapInfo(int step)
int isMultiplayer
Definition cgame.h:40
const char * menu
Definition cgame.h:39
const char * name
Definition cgame.h:38
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
bool modified
Definition cvar.h:79
char name[MAX_VAR]
Definition q_shared.h:343
char id[MAX_VAR]
Definition q_shared.h:342
void * data
Definition list.h:31
linkedList_t * gameTypes
Definition q_shared.h:476
linkedList_t * aircraft
Definition q_shared.h:492
int teams
Definition q_shared.h:475
char * mapTheme
Definition q_shared.h:464
linkedList_t * ufos
Definition q_shared.h:491
int maxAliens
Definition q_shared.h:483
linkedList_t * params
Definition q_shared.h:465
bool multiplayer
Definition q_shared.h:474
Data and interface to share data.
@ TEXT_LIST2
Definition ui_dataids.h:32
@ TEXT_CHAT_WINDOW
Definition ui_dataids.h:51