diff options
Diffstat (limited to 'src/setup/multiplayer.c')
-rw-r--r-- | src/setup/multiplayer.c | 1140 |
1 files changed, 1140 insertions, 0 deletions
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c new file mode 100644 index 00000000..dc0643e3 --- /dev/null +++ b/src/setup/multiplayer.c @@ -0,0 +1,1140 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2006 Simon Howard +// +// 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 2 +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "doomtype.h" +#include "doomfeatures.h" + +#include "textscreen.h" + +#include "d_iwad.h" +#include "m_config.h" +#include "doom/d_englsh.h" +#include "m_controls.h" + +#include "multiplayer.h" +#include "mode.h" +#include "execute.h" + +#include "net_io.h" +#include "net_query.h" + +#define NUM_WADS 10 +#define NUM_EXTRA_PARAMS 10 + +typedef enum +{ + WARP_ExMy, + WARP_MAPxy, +} warptype_t; + +// Fallback IWAD if none are found to be installed + +static iwad_t fallback_iwad = { "doom2.wad", doom2, commercial, "Doom II" }; +static iwad_t *fallback_iwad_list[2] = { &fallback_iwad, NULL }; + +// Array of IWADs found to be installed + +static iwad_t **found_iwads; +static char *iwad_labels[8]; + +// Index of the currently selected IWAD + +static int found_iwad_selected; + +// Filename to pass to '-iwad'. + +static char *iwadfile; + +static char *doom_skills[] = +{ + "I'm too young to die.", + "Hey, not too rough.", + "Hurt me plenty.", + "Ultra-Violence.", + "NIGHTMARE!", +}; + +static char *chex_skills[] = +{ + "Easy does it", + "Not so sticky", + "Gobs of goo", + "Extreme ooze", + "SUPER SLIMEY!" +}; + +static char *heretic_skills[] = +{ + "Thou needeth a wet-nurse", + "Yellowbellies-R-us", + "Bringest them oneth", + "Thou art a smite-meister", + "Black plague possesses thee" +}; + +static char *hexen_skills[] = +{ + "Squire/Altar boy/Apprentice", + "Knight/Acolyte/Enchanter", + "Warrior/Priest/Sorceror", + "Berserker/Cardinal/Warlock", + "Titan/Pope/Archimage" +}; + +static char *character_classes[] = +{ + "Fighter", + "Cleric", + "Mage" +}; + +static struct +{ + GameMission_t mission; + char **strings; +} skills[] = +{ + { doom, doom_skills }, + { heretic, heretic_skills }, + { hexen, hexen_skills } +}; + +static char *gamemodes[] = +{ + "Co-operative", + "Deathmatch", + "Deathmatch 2.0", +}; + +static char *strife_gamemodes[] = +{ + "Normal deathmatch", + "Items respawn", // (altdeath) +}; + +static char *net_player_name; +static char *chat_macros[10]; + +static char *wads[NUM_WADS]; +static char *extra_params[NUM_EXTRA_PARAMS]; +static int character_class = 0; +static int skill = 2; +static int nomonsters = 0; +static int deathmatch = 0; +static int strife_altdeath = 0; +static int fast = 0; +static int respawn = 0; +static int udpport = 2342; +static int timer = 0; +static int privateserver = 0; + +static txt_dropdown_list_t *skillbutton; +static txt_button_t *warpbutton; +static warptype_t warptype = WARP_MAPxy; +static int warpepisode = 1; +static int warpmap = 1; + +// Address to connect to when joining a game + +static char *connect_address = NULL; + +static txt_window_t *query_window; +static int query_servers_found; + +// Find an IWAD from its description + +static iwad_t *GetCurrentIWAD(void) +{ + return found_iwads[found_iwad_selected]; +} + +// Is the currently selected IWAD the Chex Quest chex.wad? + +static boolean IsChexQuest(iwad_t *iwad) +{ + return !strcmp(iwad->name, "chex.wad"); +} + +static void AddWADs(execute_context_t *exec) +{ + int have_wads = 0; + int i; + + for (i=0; i<NUM_WADS; ++i) + { + if (wads[i] != NULL && strlen(wads[i]) > 0) + { + if (!have_wads) + { + AddCmdLineParameter(exec, "-file"); + } + + AddCmdLineParameter(exec, "\"%s\"", wads[i]); + } + } +} + +static void AddExtraParameters(execute_context_t *exec) +{ + int i; + + for (i=0; i<NUM_EXTRA_PARAMS; ++i) + { + if (extra_params[i] != NULL && strlen(extra_params[i]) > 0) + { + AddCmdLineParameter(exec, extra_params[i]); + } + } +} + +static void AddIWADParameter(execute_context_t *exec) +{ + if (iwadfile != NULL) + { + AddCmdLineParameter(exec, "-iwad %s", iwadfile); + } +} + +// Callback function invoked to launch the game. +// This is used when starting a server and also when starting a +// single player game via the "warp" menu. + +static void StartGame(int multiplayer) +{ + execute_context_t *exec; + + exec = NewExecuteContext(); + + // Extra parameters come first, before all others; this way, + // they can override any of the options set in the dialog. + + AddExtraParameters(exec); + + AddIWADParameter(exec); + AddCmdLineParameter(exec, "-skill %i", skill + 1); + + if (gamemission == hexen) + { + AddCmdLineParameter(exec, "-class %i", character_class); + } + + if (nomonsters) + { + AddCmdLineParameter(exec, "-nomonsters"); + } + + if (fast) + { + AddCmdLineParameter(exec, "-fast"); + } + + if (respawn) + { + AddCmdLineParameter(exec, "-respawn"); + } + + if (warptype == WARP_ExMy) + { + // TODO: select IWAD based on warp type + AddCmdLineParameter(exec, "-warp %i %i", warpepisode, warpmap); + } + else if (warptype == WARP_MAPxy) + { + AddCmdLineParameter(exec, "-warp %i", warpmap); + } + + // Multiplayer-specific options: + + if (multiplayer) + { + AddCmdLineParameter(exec, "-server"); + AddCmdLineParameter(exec, "-port %i", udpport); + + if (deathmatch == 1) + { + AddCmdLineParameter(exec, "-deathmatch"); + } + else if (deathmatch == 2 || strife_altdeath != 0) + { + AddCmdLineParameter(exec, "-altdeath"); + } + + if (timer > 0) + { + AddCmdLineParameter(exec, "-timer %i", timer); + } + + if (privateserver) + { + AddCmdLineParameter(exec, "-privateserver"); + } + } + + AddWADs(exec); + + TXT_Shutdown(); + + M_SaveDefaults(); + PassThroughArguments(exec); + + ExecuteDoom(exec); + + exit(0); +} + +static void StartServerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + StartGame(1); +} + +static void StartSinglePlayerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + StartGame(0); +} + +static void UpdateWarpButton(void) +{ + char buf[10]; + + if (warptype == WARP_ExMy) + { + sprintf(buf, "E%iM%i", warpepisode, warpmap); + } + else if (warptype == WARP_MAPxy) + { + sprintf(buf, "MAP%02i", warpmap); + } + + TXT_SetButtonLabel(warpbutton, buf); +} + +static void UpdateSkillButton(void) +{ + iwad_t *iwad = GetCurrentIWAD(); + int i; + + if (IsChexQuest(iwad)) + { + skillbutton->values = chex_skills; + } + else + { + for (i=0; i<arrlen(skills); ++i) + { + if (gamemission == skills[i].mission) + { + skillbutton->values = skills[i].strings; + break; + } + } + } +} + +static void SetExMyWarp(TXT_UNCAST_ARG(widget), void *val) +{ + int l; + + l = (int) val; + + warpepisode = l / 10; + warpmap = l % 10; + + UpdateWarpButton(); +} + +static void SetMAPxyWarp(TXT_UNCAST_ARG(widget), void *val) +{ + int l; + + l = (int) val; + + warpmap = l; + + UpdateWarpButton(); +} + +static void CloseLevelSelectDialog(TXT_UNCAST_ARG(button), TXT_UNCAST_ARG(window)) +{ + TXT_CAST_ARG(txt_window_t, window); + + TXT_CloseWindow(window); +} + +static void LevelSelectDialog(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) +{ + txt_window_t *window; + txt_table_t *table; + txt_button_t *button; + iwad_t *iwad; + char buf[10]; + int episodes; + int x, y; + int l; + int i; + + window = TXT_NewWindow("Select level"); + iwad = GetCurrentIWAD(); + + if (warptype == WARP_ExMy) + { + episodes = D_GetNumEpisodes(iwad->mission, iwad->mode); + table = TXT_NewTable(episodes); + + // ExMy levels + + for (y=1; y<10; ++y) + { + for (x=1; x<=episodes; ++x) + { + if (IsChexQuest(iwad) && (x > 1 || y > 5)) + { + continue; + } + + if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, x, y)) + { + TXT_AddWidget(table, NULL); + continue; + } + + sprintf(buf, " E%iM%i ", x, y); + button = TXT_NewButton(buf); + TXT_SignalConnect(button, "pressed", + SetExMyWarp, (void *) (x * 10 + y)); + TXT_SignalConnect(button, "pressed", + CloseLevelSelectDialog, window); + TXT_AddWidget(table, button); + + if (warpepisode == x && warpmap == y) + { + TXT_SelectWidget(table, button); + } + } + } + } + else + { + table = TXT_NewTable(4); + + for (i=0; i<40; ++i) + { + x = i % 4; + y = i / 4; + + l = x * 10 + y + 1; + + if (!D_ValidEpisodeMap(iwad->mission, iwad->mode, 1, l)) + { + TXT_AddWidget(table, NULL); + continue; + } + + sprintf(buf, " MAP%02i ", l); + button = TXT_NewButton(buf); + TXT_SignalConnect(button, "pressed", + SetMAPxyWarp, (void *) l); + TXT_SignalConnect(button, "pressed", + CloseLevelSelectDialog, window); + TXT_AddWidget(table, button); + + if (warpmap == l) + { + TXT_SelectWidget(table, button); + } + } + } + + TXT_AddWidget(window, table); +} + +static void IWADSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + iwad_t *iwad; + + // Find the iwad_t selected + + iwad = GetCurrentIWAD(); + + // Update iwadfile + + iwadfile = iwad->name; +} + +// Called when the IWAD button is changed, to update warptype. + +static void UpdateWarpType(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + warptype_t new_warptype; + iwad_t *iwad; + + // Get the selected IWAD + + iwad = GetCurrentIWAD(); + + // Find the new warp type + + if (D_IsEpisodeMap(iwad->mission)) + { + new_warptype = WARP_ExMy; + } + else + { + new_warptype = WARP_MAPxy; + } + + // Reset to E1M1 / MAP01 when the warp type is changed. + + if (new_warptype != warptype) + { + warpepisode = 1; + warpmap = 1; + } + + warptype = new_warptype; + + UpdateWarpButton(); + UpdateSkillButton(); +} + +static txt_widget_t *IWADSelector(void) +{ + txt_dropdown_list_t *dropdown; + txt_widget_t *result; + int num_iwads; + unsigned int i; + + // Find out what WADs are installed + + found_iwads = GetIwads(); + + // Build a list of the descriptions for all installed IWADs + + num_iwads = 0; + + for (i=0; found_iwads[i] != NULL; ++i) + { + iwad_labels[i] = found_iwads[i]->description; + ++num_iwads; + } + + // If no IWADs are found, provide Doom 2 as an option, but + // we're probably screwed. + + if (num_iwads == 0) + { + found_iwads = fallback_iwad_list; + num_iwads = 1; + } + + // Build a dropdown list of IWADs + + if (num_iwads < 2) + { + // We have only one IWAD. Show as a label. + + result = (txt_widget_t *) TXT_NewLabel(found_iwads[0]->description); + } + else + { + // Dropdown list allowing IWAD to be selected. + + dropdown = TXT_NewDropdownList(&found_iwad_selected, + iwad_labels, num_iwads); + + TXT_SignalConnect(dropdown, "changed", IWADSelected, NULL); + + result = (txt_widget_t *) dropdown; + } + + // Select first in the list. + + found_iwad_selected = 0; + IWADSelected(NULL, NULL); + + return result; +} + +// Create the window action button to start the game. This invokes +// a different callback depending on whether to start a multiplayer +// or single player game. + +static txt_window_action_t *StartGameAction(int multiplayer) +{ + txt_window_action_t *action; + TxtWidgetSignalFunc callback; + + action = TXT_NewWindowAction(KEY_F10, "Start"); + + if (multiplayer) + { + callback = StartServerGame; + } + else + { + callback = StartSinglePlayerGame; + } + + TXT_SignalConnect(action, "pressed", callback, NULL); + + return action; +} + +static void OpenWadsWindow(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data)) +{ + txt_window_t *window; + int i; + + window = TXT_NewWindow("Add WADs"); + + for (i=0; i<NUM_WADS; ++i) + { + TXT_AddWidget(window, TXT_NewInputBox(&wads[i], 60)); + } +} + +static void OpenExtraParamsWindow(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(user_data)) +{ + txt_window_t *window; + int i; + + window = TXT_NewWindow("Extra command line parameters"); + + for (i=0; i<NUM_EXTRA_PARAMS; ++i) + { + TXT_AddWidget(window, TXT_NewInputBox(&extra_params[i], 70)); + } +} + +static txt_window_action_t *WadWindowAction(void) +{ + txt_window_action_t *action; + + action = TXT_NewWindowAction('w', "Add WADs"); + TXT_SignalConnect(action, "pressed", OpenWadsWindow, NULL); + + return action; +} + +static txt_dropdown_list_t *GameTypeDropdown(void) +{ + switch (gamemission) + { + case doom: + default: + return TXT_NewDropdownList(&deathmatch, gamemodes, 3); + + // Heretic and Hexen don't support Deathmatch II: + + case heretic: + case hexen: + return TXT_NewDropdownList(&deathmatch, gamemodes, 2); + + // Strife supports both deathmatch modes, but doesn't support + // multiplayer co-op. Use a different variable to indicate whether + // to use altdeath or not. + + case strife: + return TXT_NewDropdownList(&strife_altdeath, strife_gamemodes, 2); + } +} + +// "Start game" menu. This is used for the start server window +// and the single player warp menu. The parameters specify +// the window title and whether to display multiplayer options. + +static void StartGameMenu(char *window_title, int multiplayer) +{ + txt_window_t *window; + txt_table_t *gameopt_table; + txt_table_t *advanced_table; + txt_widget_t *iwad_selector; + + window = TXT_NewWindow(window_title); + + TXT_AddWidgets(window, + gameopt_table = TXT_NewTable(2), + TXT_NewSeparator("Monster options"), + TXT_NewInvertedCheckBox("Monsters enabled", &nomonsters), + TXT_NewCheckBox("Fast monsters", &fast), + TXT_NewCheckBox("Respawning monsters", &respawn), + TXT_NewSeparator("Advanced"), + advanced_table = TXT_NewTable(2), + NULL); + + TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction()); + TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction(multiplayer)); + + TXT_SetColumnWidths(gameopt_table, 12, 6); + + TXT_AddWidgets(gameopt_table, + TXT_NewLabel("Game"), + iwad_selector = IWADSelector(), + TXT_NewLabel("Skill"), + skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5), + TXT_NewLabel("Level warp"), + warpbutton = TXT_NewButton2("????", LevelSelectDialog, NULL), + NULL); + + if (gamemission == hexen) + { + TXT_AddWidgets(gameopt_table, + TXT_NewLabel("Character class "), + TXT_NewDropdownList(&character_class, + character_classes, 3), + NULL); + } + + if (multiplayer) + { + TXT_AddWidgets(gameopt_table, + TXT_NewLabel("Game type"), + GameTypeDropdown(), + TXT_NewLabel("Time limit"), + TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2), + TXT_NewLabel("minutes"), + NULL), + NULL); + + TXT_AddWidget(window, + TXT_NewInvertedCheckBox("Register with master server", + &privateserver)); + + TXT_AddWidgets(advanced_table, + TXT_NewLabel("UDP port"), + TXT_NewIntInputBox(&udpport, 5), + NULL); + } + + TXT_AddWidget(window, + TXT_NewButton2("Add extra parameters...", + OpenExtraParamsWindow, NULL)); + + TXT_SetColumnWidths(advanced_table, 12, 6); + + TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL); + + UpdateWarpType(NULL, NULL); + UpdateWarpButton(); +} + +void StartMultiGame(void) +{ + StartGameMenu("Start multiplayer game", 1); +} + +void WarpMenu(void) +{ + StartGameMenu("Level Warp", 0); +} + +static void DoJoinGame(void *unused1, void *unused2) +{ + execute_context_t *exec; + + exec = NewExecuteContext(); + + AddCmdLineParameter(exec, "-connect %s", connect_address); + + if (gamemission == hexen) + { + AddCmdLineParameter(exec, "-class %i", character_class); + } + + // Extra parameters come first, so that they can be used to override + // the other parameters. + + AddExtraParameters(exec); + AddIWADParameter(exec); + AddWADs(exec); + + TXT_Shutdown(); + + M_SaveDefaults(); + + PassThroughArguments(exec); + + ExecuteDoom(exec); + + exit(0); +} + +static txt_window_action_t *JoinGameAction(void) +{ + txt_window_action_t *action; + + action = TXT_NewWindowAction(KEY_F10, "Connect"); + TXT_SignalConnect(action, "pressed", DoJoinGame, NULL); + + return action; +} + +static void SelectQueryAddress(TXT_UNCAST_ARG(button), + TXT_UNCAST_ARG(querydata)) +{ + TXT_CAST_ARG(txt_button_t, button); + TXT_CAST_ARG(net_querydata_t, querydata); + int i; + + if (querydata->server_state != 0) + { + TXT_MessageBox("Cannot connect to server", + "Gameplay is already in progress\n" + "on this server."); + return; + } + + // Set address to connect to: + + free(connect_address); + connect_address = strdup(button->label); + + // Auto-choose IWAD if there is already a player connected. + + if (querydata->num_players > 0) + { + for (i = 0; found_iwads[i] != NULL; ++i) + { + if (found_iwads[i]->mode == querydata->gamemode + && found_iwads[i]->mission == querydata->gamemission) + { + found_iwad_selected = i; + break; + } + } + + if (found_iwads[i] == NULL) + { + TXT_MessageBox(NULL, + "The game on this server seems to be:\n" + "\n" + " %s\n" + "\n" + "but the IWAD file %s is not found!\n" + "Without the required IWAD file, it may not be\n" + "possible to join this game.", + D_SuggestGameName(querydata->gamemission, + querydata->gamemode), + D_SuggestIWADName(querydata->gamemission, + querydata->gamemode)); + } + } + + // Finished with search. + + TXT_CloseWindow(query_window); +} + +static void QueryResponseCallback(net_addr_t *addr, + net_querydata_t *querydata, + unsigned int ping_time, + TXT_UNCAST_ARG(results_table)) +{ + TXT_CAST_ARG(txt_table_t, results_table); + char ping_time_str[16]; + char description[47]; + + sprintf(ping_time_str, "%ims", ping_time); + strncpy(description, querydata->description, 46); + description[46] = '\0'; + + TXT_AddWidgets(results_table, + TXT_NewLabel(ping_time_str), + TXT_NewButton2(NET_AddrToString(addr), + SelectQueryAddress, querydata), + TXT_NewLabel(description), + NULL); + + ++query_servers_found; +} + +static void QueryPeriodicCallback(TXT_UNCAST_ARG(results_table)) +{ + TXT_CAST_ARG(txt_table_t, results_table); + + if (!NET_Query_Poll(QueryResponseCallback, results_table)) + { + TXT_SetPeriodicCallback(NULL, NULL, 0); + + if (query_servers_found == 0) + { + TXT_AddWidget(results_table, NULL); + TXT_AddWidget(results_table, TXT_NewLabel("No servers found.")); + } + } +} + +static void QueryWindowClosed(TXT_UNCAST_ARG(window), void *unused) +{ + TXT_SetPeriodicCallback(NULL, NULL, 0); +} + +static void ServerQueryWindow(char *title) +{ + txt_table_t *results_table; + + query_servers_found = 0; + + query_window = TXT_NewWindow(title); + + TXT_AddWidget(query_window, + TXT_NewScrollPane(70, 10, + results_table = TXT_NewTable(3))); + + TXT_SetColumnWidths(results_table, 7, 16, 46); + TXT_SetPeriodicCallback(QueryPeriodicCallback, results_table, 1); + + TXT_SignalConnect(query_window, "closed", QueryWindowClosed, NULL); +} + +static void FindInternetServer(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(user_data)) +{ + NET_StartMasterQuery(); + ServerQueryWindow("Find internet server"); +} + +static void FindLANServer(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(user_data)) +{ + NET_StartLANQuery(); + ServerQueryWindow("Find LAN server"); +} + +void JoinMultiGame(void) +{ + txt_window_t *window; + txt_table_t *gameopt_table; + txt_table_t *serveropt_table; + txt_inputbox_t *address_box; + + window = TXT_NewWindow("Join multiplayer game"); + + TXT_AddWidgets(window, + gameopt_table = TXT_NewTable(2), + TXT_NewSeparator("Server"), + serveropt_table = TXT_NewTable(1), + TXT_NewStrut(0, 1), + TXT_NewButton2("Add extra parameters...", OpenExtraParamsWindow, NULL), + NULL); + + TXT_SetColumnWidths(gameopt_table, 12, 12); + + TXT_AddWidgets(gameopt_table, + TXT_NewLabel("Game"), + IWADSelector(), + NULL); + + if (gamemission == hexen) + { + TXT_AddWidgets(gameopt_table, + TXT_NewLabel("Character class "), + TXT_NewDropdownList(&character_class, + character_classes, 3), + NULL); + } + + TXT_AddWidgets(serveropt_table, + TXT_NewHorizBox( + TXT_NewLabel("Connect to address: "), + address_box = TXT_NewInputBox(&connect_address, 30), + NULL), + TXT_NewButton2("Find server on Internet...", + FindInternetServer, NULL), + TXT_NewButton2("Find server on local network...", + FindLANServer, NULL), + NULL); + + TXT_SelectWidget(window, address_box); + + TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction()); + TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, JoinGameAction()); +} + +void SetChatMacroDefaults(void) +{ + int i; + char *defaults[] = + { + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9, + }; + + // If the chat macros have not been set, initialize with defaults. + + for (i=0; i<10; ++i) + { + if (chat_macros[i] == NULL) + { + chat_macros[i] = strdup(defaults[i]); + } + } +} + +#ifdef _WIN32 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +char *M_OEMToUTF8(const char *oem) +{ + unsigned int len = strlen(oem) + 1; + wchar_t *tmp; + char *result; + + tmp = malloc(len * sizeof(wchar_t)); + MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len); + result = malloc(len * 4); + WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL); + free(tmp); + + return result; +} + +#endif + +void SetPlayerNameDefault(void) +{ + if (net_player_name == NULL) + { + net_player_name = strdup(getenv("USER")); + } + + if (net_player_name == NULL) + { + net_player_name = strdup(getenv("USERNAME")); + } + + // On Windows, environment variables are in OEM codepage + // encoding, so convert to UTF8: + +#ifdef _WIN32 + if (net_player_name != NULL) + { + net_player_name = M_OEMToUTF8(net_player_name); + } +#endif + + if (net_player_name == NULL) + { + net_player_name = strdup("player"); + } +} + +void MultiplayerConfig(void) +{ + txt_window_t *window; + txt_label_t *label; + txt_table_t *table; + char buf[10]; + int i; + + window = TXT_NewWindow("Multiplayer Configuration"); + + TXT_AddWidgets(window, + TXT_NewStrut(0, 1), + TXT_NewHorizBox(TXT_NewLabel("Player name: "), + TXT_NewInputBox(&net_player_name, 25), + NULL), + TXT_NewStrut(0, 1), + TXT_NewSeparator("Chat macros"), + NULL); + + table = TXT_NewTable(2); + + for (i=0; i<10; ++i) + { + sprintf(buf, "#%i ", i + 1); + + label = TXT_NewLabel(buf); + TXT_SetFGColor(label, TXT_COLOR_BRIGHT_CYAN); + + TXT_AddWidgets(table, + label, + TXT_NewInputBox(&chat_macros[(i + 1) % 10], 40), + NULL); + } + + TXT_AddWidget(window, table); +} + +void BindMultiplayerVariables(void) +{ + char buf[15]; + int i; + +#ifdef FEATURE_MULTIPLAYER + M_BindVariable("player_name", &net_player_name); +#endif + + for (i=0; i<10; ++i) + { + sprintf(buf, "chatmacro%i", i); + M_BindVariable(buf, &chat_macros[i]); + } + + switch (gamemission) + { + case doom: + M_BindChatControls(4); + key_multi_msgplayer[0] = 'g'; + key_multi_msgplayer[1] = 'i'; + key_multi_msgplayer[2] = 'b'; + key_multi_msgplayer[3] = 'r'; + break; + + case heretic: + M_BindChatControls(4); + key_multi_msgplayer[0] = 'g'; + key_multi_msgplayer[1] = 'y'; + key_multi_msgplayer[2] = 'r'; + key_multi_msgplayer[3] = 'b'; + break; + + case hexen: + M_BindChatControls(8); + key_multi_msgplayer[0] = 'b'; + key_multi_msgplayer[1] = 'r'; + key_multi_msgplayer[2] = 'y'; + key_multi_msgplayer[3] = 'g'; + key_multi_msgplayer[4] = 'j'; + key_multi_msgplayer[5] = 'w'; + key_multi_msgplayer[6] = 'h'; + key_multi_msgplayer[7] = 'p'; + break; + + default: + break; + } +} + |