• R/O
  • SSH
  • HTTPS

marathon: コミット


コミットメタ情報

リビジョン496 (tree)
日時2011-07-07 23:02:04
作者logue

ログメッセージ

1.0rc3リリース。
フォントを更新。日本語化をすすめる。

変更サマリ

差分

--- marathon/trunk/configure (revision 495)
+++ marathon/trunk/configure (revision 496)
@@ -560,8 +560,8 @@
560560 # Identity of this package.
561561 PACKAGE_NAME='Aleph One JP/SDL'
562562 PACKAGE_TARNAME='AlephOneJP'
563-PACKAGE_VERSION='20110705'
564-PACKAGE_STRING='Aleph One JP/SDL 20110705'
563+PACKAGE_VERSION='20110707'
564+PACKAGE_STRING='Aleph One JP/SDL 20110707'
565565 PACKAGE_BUGREPORT='http://sourceforge.net/bugs/?group_id=1997'
566566 PACKAGE_URL=''
567567
--- marathon/trunk/Source_Files/RenderOther/computer_interface.cpp (revision 495)
+++ marathon/trunk/Source_Files/RenderOther/computer_interface.cpp (revision 496)
@@ -451,7 +451,11 @@
451451 // terminal_font no longer a global, since it may change
452452 font_info *terminal_font = GetInterfaceFont(_computer_interface_font);
453453 TTF_Font* font = ((ttf_font_info*)terminal_font)->font;
454+
454455 while (running_width < width && base_text[index] && base_text[index] != MAC_LINE_END) {
456+// uint16 c;
457+// int index_k = start_index;
458+// while (running_width < width && (c = sjisChar(&base_text[index],&index_k)) && c != MAC_LINE_END) {
455459 if (isJChar(base_text[index])){
456460 running_width += char_width(base_text[index], terminal_font, current_style)+char_width(base_text[index+1], terminal_font, current_style);
457461 index ++;
@@ -460,7 +464,6 @@
460464 }
461465 index ++;
462466 }
463-
464467 // Now go backwards, looking for whitespace to split on
465468 if (base_text[index] == MAC_LINE_END)
466469 index++;
--- marathon/trunk/Source_Files/RenderOther/sdl_fonts.cpp (revision 495)
+++ marathon/trunk/Source_Files/RenderOther/sdl_fonts.cpp (revision 496)
@@ -248,6 +248,8 @@
248248 TTF_Font *font = 0;
249249 if (path == "mono")
250250 {
251+ // Japanese Font cannot render as embeded font.
252+ // then, if Fonts.ttf exsists, read external font forcely.
251253 FILE* fp = fopen( FONT_PATH, "r" );
252254 if( !fp ){
253255 // fprintf(stderr, "TTF Font %s is not found. Load internal font.\n", FONT_PATH);
--- marathon/trunk/Source_Files/shell.cpp (revision 495)
+++ marathon/trunk/Source_Files/shell.cpp (revision 496)
@@ -1,1502 +1,1506 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 2 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20-*/
21-
22-/*
23- * shell.cpp - Main game loop and input handling
24- */
25-
26-#include "cseries.h"
27-
28-#include "map.h"
29-#include "monsters.h"
30-#include "player.h"
31-#include "render.h"
32-#include "shell.h"
33-#include "interface.h"
34-#include "SoundManager.h"
35-#include "fades.h"
36-#include "screen.h"
37-#include "Music.h"
38-#include "images.h"
39-#include "vbl.h"
40-#include "preferences.h"
41-#include "tags.h" /* for scenario file type.. */
42-#include "network_sound.h"
43-#include "mouse.h"
44-#include "screen_drawing.h"
45-#include "computer_interface.h"
46-#include "game_wad.h" /* yuck... */
47-#include "game_window.h" /* for draw_interface() */
48-#include "extensions.h"
49-#include "items.h"
50-#include "interface_menus.h"
51-#include "weapons.h"
52-#include "lua_script.h"
53-
54-#include "Crosshairs.h"
55-#include "OGL_Render.h"
56-#include "OGL_Blitter.h"
57-#include "XML_ParseTreeRoot.h"
58-#include "FileHandler.h"
59-#include "Plugins.h"
60-#include "FilmProfile.h"
61-
62-#include "mytm.h" // mytm_initialize(), for platform-specific shell_*.h
63-
64-#include <stdlib.h>
65-#include <string.h>
66-#include <ctype.h>
67-#include <vector>
68-
69-#include <sstream>
70-#include <boost/lexical_cast.hpp>
71-
72-#include "XML_Loader_SDL.h"
73-#include "resource_manager.h"
74-#include "sdl_dialogs.h"
75-#include "sdl_fonts.h"
76-#include "sdl_widgets.h"
77-
78-#include "TextStrings.h"
79-
80-#ifdef HAVE_CONFIG_H
81-#include "confpaths.h"
82-#endif
83-
84-#include <ctime>
85-#include <exception>
86-#include <algorithm>
87-#include <vector>
88-
89-#ifdef HAVE_UNISTD_H
90-#include <unistd.h>
91-#endif
92-
93-#ifdef HAVE_OPENGL
94-#include "OGL_Headers.h"
95-#endif
96-
97-#ifdef HAVE_SDL_NET_H
98-#include <SDL_net.h>
99-#endif
100-
101-#ifdef HAVE_PNG
102-#include "IMG_savepng.h"
103-#endif
104-
105-#ifdef HAVE_SDL_IMAGE
106-#include <SDL_image.h>
107-#if defined(__WIN32__)
108-#include "alephone32.xpm"
109-#elif !(defined(__APPLE__) && defined(__MACH__)) && !defined(__MACOS__)
110-#include "alephone.xpm"
111-#endif
112-#endif
113-
114-#ifdef __WIN32__
115-#include <windows.h>
116-#include <shlobj.h>
117-#endif
118-
119-#include "alephversion.h"
120-
121-#include "Logging.h"
122-#include "network.h"
123-#include "Console.h"
124-
125-extern void initSJIS2UTF16();
126-
127-// LP addition: whether or not the cheats are active
128-// Defined in shell_misc.cpp
129-extern bool CheatsActive;
130-
131-// Data directories
132-vector <DirectorySpecifier> data_search_path; // List of directories in which data files are searched for
133-DirectorySpecifier local_data_dir; // Local (per-user) data file directory
134-DirectorySpecifier preferences_dir; // Directory for preferences
135-DirectorySpecifier saved_games_dir; // Directory for saved games
136-DirectorySpecifier recordings_dir; // Directory for recordings (except film buffer, which is stored in local_data_dir)
137-DirectorySpecifier screenshots_dir; // Directory for screenshots
138-DirectorySpecifier log_dir; // Directory for Aleph One Log.txt
139-std::string arg_directory;
140-std::vector<std::string> arg_files;
141-
142-// Command-line options
143-bool option_nogl = false; // Disable OpenGL
144-bool option_nosound = false; // Disable sound output
145-bool option_nogamma = false; // Disable gamma table effects (menu fades)
146-bool option_debug = false;
147-bool option_nojoystick = false;
148-bool insecure_lua = false;
149-static bool force_fullscreen = false; // Force fullscreen mode
150-static bool force_windowed = false; // Force windowed mode
151-
152-// Prototypes
153-static void main_event_loop(void);
154-extern int process_keyword_key(char key);
155-extern void handle_keyword(int type_of_cheat);
156-
157-void PlayInterfaceButtonSound(short SoundID);
158-
159-#ifdef __BEOS__
160-// From csfiles_beos.cpp
161-extern string get_application_directory(void);
162-extern string get_preferences_directory(void);
163-#endif
164-
165-// From preprocess_map_sdl.cpp
166-extern bool get_default_music_spec(FileSpecifier &file);
167-
168-// From vbl_sdl.cpp
169-void execute_timer_tasks(uint32 time);
170-
171-// Prototypes
172-static void initialize_application(void);
173-void shutdown_application(void);
174-static void initialize_marathon_music_handler(void);
175-static void process_event(const SDL_Event &event);
176-
177-// cross-platform static variables
178-short vidmasterStringSetID = -1; // can be set with MML
179-
180-static void usage(const char *prg_name)
181-{
182-#ifdef __WIN32__
183- MessageBox(NULL, "コマンドラインスイッチ:\n\n"
184-#else
185- printf("\n使用方法:%s [オプション] [ディレクトリ] [ファイル]\n"
186-#endif
187- "\t[-h | --help] このヘルプメッセージを表\示します。\n"
188- "\t[-v | --version] ゲームのバージョンを表\示します。\n"
189- "\t[-d | --debug] コアダンプを出力するようにします。\n"
190- "\t (SDL parachuteを無効化します)\n"
191- "\t[-f | --fullscreen] ゲームをフルスクリーンで起動します。\n"
192- "\t[-w | --windowed] ゲームをウィンドウモードで起動します。\n"
193-#ifdef HAVE_OPENGL
194- "\t[-g | --nogl] OpenGLを使用せずに起動します。\n"
195-#endif
196- "\t[-s | --nosound] サウンドを無効化します。\n"
197- "\t[-m | --nogamma] ガンマエフェクトを無効化します。\n"
198- "\t (メニューのフェードなど)\n"
199- "\t[-j | --nojoystick] ジョイスティックの初期化を行いません。\n"
200- // Documenting this might be a bad idea?
201- // "\t[-i | --insecure_lua] Allow Lua netscripts to take over your computer\n"
202- "\tディレクトリ シナリオのデーターが含まれているディレクトリ\n"
203- "\tファイル 保存されたゲームやフィルムを再生します。\n"
204- "\nこの他にも、環境変数「ALEPHONE_DATA」の値を変更することで、\n"
205- "データディレクトリを指定することができます。\n"
206-#ifdef __WIN32__
207- , "使用方法", MB_OK | MB_ICONINFORMATION
208-#else
209- , prg_name
210-#endif
211- );
212- exit(0);
213-}
214-
215-extern bool handle_open_replay(FileSpecifier& File);
216-extern bool load_and_start_game(FileSpecifier& file);
217-
218-bool handle_open_document(const std::string& filename)
219-{
220- bool done = false;
221- FileSpecifier file(filename);
222- switch (file.GetType())
223- {
224- case _typecode_scenario:
225- set_map_file(file);
226- break;
227- case _typecode_savegame:
228- if (load_and_start_game(file))
229- {
230- done = true;
231- }
232- break;
233- case _typecode_film:
234- if (handle_open_replay(file))
235- {
236- done = true;
237- }
238- break;
239- case _typecode_physics:
240- set_physics_file(file);
241- break;
242- case _typecode_shapes:
243- open_shapes_file(file);
244- break;
245- case _typecode_sounds:
246- SoundManager::instance()->OpenSoundFile(file);
247- break;
248- default:
249- break;
250- }
251-
252- return done;
253-}
254-
255-int main(int argc, char **argv)
256-{
257- // Print banner (don't bother if this doesn't appear when started from a GUI)
258- printf ("Aleph One JP" A1_VERSION_STRING "\n"
259- "http://marathon.sourceforge.jp/\n\n"
260- "オリジナルのコードは、Bungie Software <http://www.bungie.com/>によるものです。\n"
261- "この他にLoren Petrich, Chris Pruett, Rhys Hill氏らによって書かれています。\n"
262- "TCP/IP ネットワーク by Woody Zenfell\n"
263- "Expat XMLライブラリ by James Clark\n"
264- "SDLポート by Christian Bauer <Christian.Bauer@uni-mainz.de>\n"
265- "日本語化 by saiten <http://www.isidesystem.net/>, ookawa_mi, Logue <http://logue.be/>\n"
266-#if defined(__MACH__) && defined(__APPLE__)
267- "Mac OS X/SDLバージョンは、Chris Lovell, Alexander Strange, and Woody Zenfell氏らによって作られました。\n"
268-#endif
269- "\nこのプログラムは有用であることを願って頒布されますが、*全くの無保証 *です。\n"
270- "商業可能\性の保証や特定目的への適合性は、言外に示されたものも 含め、全く存在しません。\n"
271- "詳しくはGNU 一般公衆利用許諾書をご覧ください。\n"
272-#if defined(__BEOS__) || defined(__WIN32__)
273- // BeOS and Windows are statically linked against SDL, so we have to include this:
274- "\nSimple DirectMedia Layer (SDL) ライブラリは、\n"
275- "GNU 一般公衆利用許諾書によってライセンスされています。\n"
276- "詳細については、COPYING.SDLを参考にしてください。\n"
277-#endif
278-#ifdef HAVE_SDL_NET
279- "\nこのビルドは、ネットワークプレイが有効です。\n"
280-#endif
281-#ifdef HAVE_LUA
282- "\nこのビルドは、Luaスクリプトが有効です。\n"
283-#endif
284- );
285-
286- // Parse arguments
287- char *prg_name = argv[0];
288- argc--;
289- argv++;
290- while (argc > 0) {
291- if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "--help") == 0) {
292- usage(prg_name);
293- } else if (strcmp(*argv, "-v") == 0 || strcmp(*argv, "--version") == 0) {
294- printf("Aleph One " A1_VERSION_STRING "\n");
295- exit(0);
296- } else if (strcmp(*argv, "-f") == 0 || strcmp(*argv, "--fullscreen") == 0) {
297- force_fullscreen = true;
298- } else if (strcmp(*argv, "-w") == 0 || strcmp(*argv, "--windowed") == 0) {
299- force_windowed = true;
300- } else if (strcmp(*argv, "-g") == 0 || strcmp(*argv, "--nogl") == 0) {
301- option_nogl = true;
302- } else if (strcmp(*argv, "-s") == 0 || strcmp(*argv, "--nosound") == 0) {
303- option_nosound = true;
304- } else if (strcmp(*argv, "-j") == 0 || strcmp(*argv, "--nojoystick") == 0) {
305- option_nojoystick = true;
306- } else if (strcmp(*argv, "-m") == 0 || strcmp(*argv, "--nogamma") == 0) {
307- option_nogamma = true;
308- } else if (strcmp(*argv, "-i") == 0 || strcmp(*argv, "--insecure_lua") == 0) {
309- insecure_lua = true;
310- } else if (strcmp(*argv, "-d") == 0 || strcmp(*argv, "--debug") == 0) {
311- option_debug = true;
312- } else if (*argv[0] != '-') {
313- // if it's a directory, make it the default data dir
314- // otherwise push it and handle it later
315- FileSpecifier f(*argv);
316- if (f.IsDir())
317- {
318- arg_directory = *argv;
319- }
320- else
321- {
322- arg_files.push_back(*argv);
323- }
324- } else {
325- printf("不明なスイッチ指定です:'%s'.\n", *argv);
326- usage(prg_name);
327- }
328- argc--;
329- argv++;
330- }
331-
332- try {
333-
334- // Initialize everything
335- initialize_application();
336-
337- for (std::vector<std::string>::iterator it = arg_files.begin(); it != arg_files.end(); ++it)
338- {
339- if (handle_open_document(*it))
340- {
341- break;
342- }
343- }
344-
345- // Run the main loop
346- main_event_loop();
347-
348- } catch (exception &e) {
349- try
350- {
351- logFatal("捕捉されなかった例外が発生しました:%s", e.what());
352- }
353- catch (...)
354- {
355- }
356- exit(1);
357- } catch (...) {
358- try
359- {
360- logFatal("例外が発生しました。");
361- }
362- catch (...)
363- {
364- }
365- exit(1);
366- }
367-
368- return 0;
369-}
370-
371-static void initialize_application(void)
372-{
373-#if defined(__WIN32__) && defined(__MINGW32__)
374- if (LoadLibrary("exchndl.dll")) option_debug = true;
375-#endif
376-
377- // Find data directories, construct search path
378- DirectorySpecifier default_data_dir;
379-
380-#if defined(unix) || defined(__NetBSD__) || defined(__OpenBSD__) || (defined(__APPLE__) && defined(__MACH__) && !defined(HAVE_BUNDLE_NAME))
381-
382- default_data_dir = PKGDATADIR;
383- const char *home = getenv("HOME");
384- if (home)
385- local_data_dir = home;
386- local_data_dir += ".alephone";
387- log_dir = local_data_dir;
388-
389-#elif defined(__APPLE__) && defined(__MACH__)
390- extern char *bundle_name; // SDLMain.m
391- DirectorySpecifier bundle_data_dir = bundle_name;
392- bundle_data_dir += "Contents/Resources/DataFiles";
393-
394- data_search_path.push_back(bundle_data_dir);
395-
396- {
397- char* buf = getcwd(0, 0);
398- default_data_dir = buf;
399- free(buf);
400- }
401-
402- const char *home = getenv("HOME");
403- if (home)
404- {
405- local_data_dir = home;
406- preferences_dir = home;
407- log_dir = home;
408- log_dir += "Library";
409- log_dir += "Logs";
410- }
411-
412- local_data_dir += "Library";
413- local_data_dir += "Application Support";
414- local_data_dir += "AlephOne";
415-
416- preferences_dir += "Library";
417- preferences_dir += "Preferences";
418- preferences_dir += "org.bungie.source.AlephOne";
419-
420-#elif defined(__BEOS__)
421-
422- default_data_dir = get_application_directory();
423- local_data_dir = get_preferences_directory();
424-
425-#elif defined(__WIN32__)
426-
427- char file_name[MAX_PATH];
428- GetModuleFileName(NULL, file_name, sizeof(file_name));
429- char *sep = strrchr(file_name, '\\');
430- *sep = '\0';
431-
432- default_data_dir = file_name;
433-
434- char login[17];
435- DWORD len = 17;
436-
437- bool hasName = (GetUserName((LPSTR) login, &len) == TRUE);
438- if (!hasName || strpbrk(login, "\\/:*?\"<>|") != NULL)
439- strcpy(login, "Bob User");
440-
441- DirectorySpecifier legacy_data_dir = file_name;
442- legacy_data_dir += "Prefs";
443- legacy_data_dir += login;
444-
445- SHGetFolderPath(NULL,
446- CSIDL_PERSONAL | CSIDL_FLAG_CREATE,
447- NULL,
448- 0,
449- file_name);
450- local_data_dir = file_name;
451- local_data_dir += "AlephOne";
452-
453- log_dir = local_data_dir;
454-
455-#else
456- default_data_dir = "";
457- local_data_dir = "";
458-//#error Data file paths must be set for this platform.
459-#endif
460-
461-#if defined(__WIN32__)
462-#define LIST_SEP ';'
463-#else
464-#define LIST_SEP ':'
465-#endif
466-
467- // in case we need to redo search path later:
468- size_t dsp_insert_pos = data_search_path.size();
469- size_t dsp_delete_pos = (size_t)-1;
470-
471- if (arg_directory != "")
472- {
473- dsp_delete_pos = data_search_path.size();
474- data_search_path.push_back(arg_directory);
475- }
476-
477- const char *data_env = getenv("ALEPHONE_DATA");
478- if (data_env) {
479- // Read colon-separated list of directories
480- string path = data_env;
481- string::size_type pos;
482- while ((pos = path.find(LIST_SEP)) != string::npos) {
483- if (pos) {
484- string element = path.substr(0, pos);
485- data_search_path.push_back(element);
486- }
487- path.erase(0, pos + 1);
488- }
489- if (!path.empty())
490- data_search_path.push_back(path);
491- } else {
492- if (arg_directory == "")
493- {
494- dsp_delete_pos = data_search_path.size();
495- data_search_path.push_back(default_data_dir);
496- }
497-#if defined(__WIN32__)
498- data_search_path.push_back(legacy_data_dir);
499-#endif
500-#ifndef __MACOS__
501- data_search_path.push_back(local_data_dir);
502-#endif
503- }
504-
505- // Subdirectories
506-#if defined(__MACH__) && defined(__APPLE__)
507- DirectorySpecifier legacy_preferences_dir = local_data_dir;
508-#elif defined(__WIN32__)
509- DirectorySpecifier legacy_preferences_dir = legacy_data_dir;
510- SHGetFolderPath(NULL,
511- CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
512- NULL,
513- 0,
514- file_name);
515- preferences_dir = file_name;
516- preferences_dir += "AlephOne";
517-#else
518- preferences_dir = local_data_dir;
519-#endif
520- saved_games_dir = local_data_dir + "Saved Games";
521- recordings_dir = local_data_dir + "Recordings";
522- screenshots_dir = local_data_dir + "Screenshots";
523-
524-
525- DirectorySpecifier local_mml_dir = local_data_dir + "MML";
526- DirectorySpecifier local_themes_dir = local_data_dir + "Themes";
527-
528- // Setup resource manager
529- initialize_resources();
530-
531- init_physics_wad_data();
532-
533- load_film_profile(FILM_PROFILE_DEFAULT, false);
534-
535- // Parse MML files
536- SetupParseTree();
537- LoadBaseMMLScripts();
538-
539- // Check for presence of strings
540- if (!TS_IsPresent(strERRORS) || !TS_IsPresent(strFILENAMES)) {
541- fprintf(stderr, "起動に必要なテキストが見つかりませんでした。(MMLが存在しない?)\n");
542- exit(1);
543- }
544-
545- // Check for presence of files (one last chance to change data_search_path)
546- if (!have_default_files()) {
547- char chosen_dir[256];
548- if (alert_choose_scenario(chosen_dir)) {
549- // remove original argument (or fallback) from search path
550- if (dsp_delete_pos < data_search_path.size())
551- data_search_path.erase(data_search_path.begin() + dsp_delete_pos);
552- // add selected directory where command-line argument would go
553- data_search_path.insert(data_search_path.begin() + dsp_insert_pos, chosen_dir);
554-
555- // Parse MML files again, now that we have a new dir to search
556- SetupParseTree();
557- LoadBaseMMLScripts();
558- }
559- }
560-
561- initialize_fonts();
562- Plugins::instance()->enumerate();
563-
564-#if defined(__WIN32__) || (defined(__MACH__) && defined(__APPLE__))
565- preferences_dir.CreateDirectory();
566- transition_preferences(legacy_preferences_dir);
567-#endif
568-
569- // Load preferences
570- initialize_preferences();
571-
572- local_data_dir.CreateDirectory();
573- saved_games_dir.CreateDirectory();
574- recordings_dir.CreateDirectory();
575- screenshots_dir.CreateDirectory();
576- local_mml_dir.CreateDirectory();
577- local_themes_dir.CreateDirectory();
578-
579-#ifndef HAVE_OPENGL
580- graphics_preferences->screen_mode.acceleration = _no_acceleration;
581-#endif
582- if (force_fullscreen)
583- graphics_preferences->screen_mode.fullscreen = true;
584- if (force_windowed) // takes precedence over fullscreen because windowed is safer
585- graphics_preferences->screen_mode.fullscreen = false;
586- write_preferences();
587-
588- Plugins::instance()->load_mml();
589-
590- SDL_putenv(const_cast<char*>("SDL_VIDEO_ALLOW_SCREENSAVER=1"));
591-
592- // Initialize SDL
593- int retval = SDL_Init(SDL_INIT_VIDEO |
594- (option_nosound ? 0 : SDL_INIT_AUDIO) |
595- (option_nojoystick ? 0 : SDL_INIT_JOYSTICK) |
596- (option_debug ? SDL_INIT_NOPARACHUTE : 0));
597- if (retval < 0)
598- {
599- fprintf(stderr, "SDLの初期化に失敗しました。(%s)\n", SDL_GetError());
600- exit(1);
601- }
602- SDL_WM_SetCaption("Aleph One", "Aleph One");
603-
604-#if defined(HAVE_SDL_IMAGE) && (SDL_IMAGE_PATCHLEVEL >= 8)
605- IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
606-#endif
607-
608-#if defined(HAVE_SDL_IMAGE) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__MACOS__)
609- SDL_WM_SetIcon(IMG_ReadXPMFromArray(const_cast<char**>(alephone_xpm)), 0);
610-#endif
611- atexit(shutdown_application);
612-
613-#ifdef HAVE_SDL_NET
614- // Initialize SDL_net
615- if (SDLNet_Init () < 0) {
616- fprintf (stderr, "SDL_netの初期化に失敗しました。(%s)\n", SDLNet_GetError());
617- exit(1);
618- }
619-#endif
620-
621-#ifdef HAVE_SDL_TTF
622- if (TTF_Init() < 0) {
623- fprintf (stderr, "SDL_ttfの初期化に失敗しました。(%s)\n", TTF_GetError());
624- exit(1);
625- }
626-#endif
627-
628- // Initialize everything
629- mytm_initialize();
630-// initialize_fonts();
631- SoundManager::instance()->Initialize(*sound_preferences);
632- initialize_marathon_music_handler();
633- initialize_keyboard_controller();
634- initialize_gamma();
635- alephone::Screen::instance()->Initialize(&graphics_preferences->screen_mode);
636- initialize_marathon();
637- initialize_screen_drawing();
638- FileSpecifier theme = environment_preferences->theme_dir;
639- const Plugin* theme_plugin = Plugins::instance()->find_theme();
640- if (theme_plugin)
641- {
642- theme = theme_plugin->directory + theme_plugin->theme;
643- }
644- initialize_dialogs(theme);
645- initialize_terminal_manager();
646- initialize_shape_handler();
647- initialize_fades();
648- initialize_images_manager();
649- load_environment_from_preferences();
650- initialize_game_state();
651-}
652-
653-void shutdown_application(void)
654-{
655- // ZZZ: seem to be having weird recursive shutdown problems esp. with fullscreen modes...
656- static bool already_shutting_down = false;
657- if(already_shutting_down)
658- return;
659-
660- already_shutting_down = true;
661-
662- restore_gamma();
663-#if defined(HAVE_SDL_IMAGE) && (SDL_IMAGE_PATCHLEVEL >= 8)
664- IMG_Quit();
665-#endif
666-#ifdef HAVE_SDL_NET
667- SDLNet_Quit();
668-#endif
669-#ifdef HAVE_SDL_TTF
670- TTF_Quit();
671-#endif
672- SDL_Quit();
673-}
674-
675-bool networking_available(void)
676-{
677-#ifdef HAVE_SDL_NET
678- return true;
679-#else
680- return false;
681-#endif
682-}
683-
684-static void initialize_marathon_music_handler(void)
685-{
686- FileSpecifier file;
687- if (get_default_music_spec(file))
688- Music::instance()->SetupIntroMusic(file);
689-}
690-
691-bool quit_without_saving(void)
692-{
693- dialog d;
694- vertical_placer *placer = new vertical_placer;
695- placer->dual_add (new w_static_text("本当にゲームを中断しても"), d);
696- placer->dual_add (new w_static_text("よろしいですか?"), d);
697- placer->add (new w_spacer(), true);
698-
699- horizontal_placer *button_placer = new horizontal_placer;
700- w_button *default_button = new w_button("はい", dialog_ok, &d);
701- button_placer->dual_add (default_button, d);
702- button_placer->dual_add (new w_button("いいえ", dialog_cancel, &d), d);
703- d.activate_widget(default_button);
704- placer->add(button_placer, true);
705- d.set_widget_placer(placer);
706- return d.run() == 0;
707-}
708-
709-// ZZZ: moved level-numbers widget into sdl_widgets for a wider audience.
710-
711-const int32 AllPlayableLevels = _single_player_entry_point | _multiplayer_carnage_entry_point | _multiplayer_cooperative_entry_point | _kill_the_man_with_the_ball_entry_point | _king_of_hill_entry_point | _rugby_entry_point | _capture_the_flag_entry_point;
712-
713-short get_level_number_from_user(void)
714-{
715- // Get levels
716- vector<entry_point> levels;
717- if (!get_entry_points(levels, AllPlayableLevels)) {
718- entry_point dummy;
719- dummy.level_number = 0;
720- strcpy(dummy.level_name, "Untitled Level");
721- levels.push_back(dummy);
722- }
723-
724- // Create dialog
725- dialog d;
726- vertical_placer *placer = new vertical_placer;
727- if (vidmasterStringSetID != -1 && TS_IsPresent(vidmasterStringSetID) && TS_CountStrings(vidmasterStringSetID) > 0) {
728- // if we there's a stringset present for it, load the message from there
729- int num_lines = TS_CountStrings(vidmasterStringSetID);
730-
731- for (size_t i = 0; i < num_lines; i++) {
732- bool message_font_title_color = true;
733- char *string = TS_GetCString(vidmasterStringSetID, i);
734- if (!strncmp(string, "[QUOTE]", 7)) {
735- string = string + 7;
736- message_font_title_color = false;
737- }
738- if (!strlen(string))
739- placer->add(new w_spacer(), true);
740- else if (message_font_title_color)
741- placer->dual_add(new w_static_text(string), d);
742- else
743- placer->dual_add(new w_static_text(string), d);
744- }
745-
746- } else {
747- // no stringset or no strings in stringset - use default message
748- placer->dual_add(new w_static_text("ここからは、ヴィドマスターの宣誓を誓わないといけないぜ。"), d);
749-
750- placer->add(new w_spacer(), true);
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 2 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+*/
21+
22+/*
23+ * shell.cpp - Main game loop and input handling
24+ */
25+
26+#include "cseries.h"
27+
28+#include "map.h"
29+#include "monsters.h"
30+#include "player.h"
31+#include "render.h"
32+#include "shell.h"
33+#include "interface.h"
34+#include "SoundManager.h"
35+#include "fades.h"
36+#include "screen.h"
37+#include "Music.h"
38+#include "images.h"
39+#include "vbl.h"
40+#include "preferences.h"
41+#include "tags.h" /* for scenario file type.. */
42+#include "network_sound.h"
43+#include "mouse.h"
44+#include "screen_drawing.h"
45+#include "computer_interface.h"
46+#include "game_wad.h" /* yuck... */
47+#include "game_window.h" /* for draw_interface() */
48+#include "extensions.h"
49+#include "items.h"
50+#include "interface_menus.h"
51+#include "weapons.h"
52+#include "lua_script.h"
53+
54+#include "Crosshairs.h"
55+#include "OGL_Render.h"
56+#include "OGL_Blitter.h"
57+#include "XML_ParseTreeRoot.h"
58+#include "FileHandler.h"
59+#include "Plugins.h"
60+#include "FilmProfile.h"
61+
62+#include "mytm.h" // mytm_initialize(), for platform-specific shell_*.h
63+
64+#include <stdlib.h>
65+#include <string.h>
66+#include <ctype.h>
67+#include <vector>
68+
69+#include <sstream>
70+#include <boost/lexical_cast.hpp>
71+
72+#include "XML_Loader_SDL.h"
73+#include "resource_manager.h"
74+#include "sdl_dialogs.h"
75+#include "sdl_fonts.h"
76+#include "sdl_widgets.h"
77+
78+#include "TextStrings.h"
79+
80+#ifdef HAVE_CONFIG_H
81+#include "confpaths.h"
82+#endif
83+
84+#include <ctime>
85+#include <exception>
86+#include <algorithm>
87+#include <vector>
88+
89+#ifdef HAVE_UNISTD_H
90+#include <unistd.h>
91+#endif
92+
93+#ifdef HAVE_OPENGL
94+#include "OGL_Headers.h"
95+#endif
96+
97+#ifdef HAVE_SDL_NET_H
98+#include <SDL_net.h>
99+#endif
100+
101+#ifdef HAVE_PNG
102+#include "IMG_savepng.h"
103+#endif
104+
105+#ifdef HAVE_SDL_IMAGE
106+#include <SDL_image.h>
107+#if defined(__WIN32__)
108+#include "alephone32.xpm"
109+#elif !(defined(__APPLE__) && defined(__MACH__)) && !defined(__MACOS__)
110+#include "alephone.xpm"
111+#endif
112+#endif
113+
114+#ifdef __WIN32__
115+#include <windows.h>
116+#include <shlobj.h>
117+#endif
118+
119+#include "alephversion.h"
120+
121+#include "Logging.h"
122+#include "network.h"
123+#include "Console.h"
124+
125+// JP fix
126+#include <locale.h>
127+
128+extern void initSJIS2UTF16();
129+
130+// LP addition: whether or not the cheats are active
131+// Defined in shell_misc.cpp
132+extern bool CheatsActive;
133+
134+// Data directories
135+vector <DirectorySpecifier> data_search_path; // List of directories in which data files are searched for
136+DirectorySpecifier local_data_dir; // Local (per-user) data file directory
137+DirectorySpecifier preferences_dir; // Directory for preferences
138+DirectorySpecifier saved_games_dir; // Directory for saved games
139+DirectorySpecifier recordings_dir; // Directory for recordings (except film buffer, which is stored in local_data_dir)
140+DirectorySpecifier screenshots_dir; // Directory for screenshots
141+DirectorySpecifier log_dir; // Directory for Aleph One Log.txt
142+std::string arg_directory;
143+std::vector<std::string> arg_files;
144+
145+// Command-line options
146+bool option_nogl = false; // Disable OpenGL
147+bool option_nosound = false; // Disable sound output
148+bool option_nogamma = false; // Disable gamma table effects (menu fades)
149+bool option_debug = false;
150+bool option_nojoystick = false;
151+bool insecure_lua = false;
152+static bool force_fullscreen = false; // Force fullscreen mode
153+static bool force_windowed = false; // Force windowed mode
154+
155+// Prototypes
156+static void main_event_loop(void);
157+extern int process_keyword_key(char key);
158+extern void handle_keyword(int type_of_cheat);
159+
160+void PlayInterfaceButtonSound(short SoundID);
161+
162+#ifdef __BEOS__
163+// From csfiles_beos.cpp
164+extern string get_application_directory(void);
165+extern string get_preferences_directory(void);
166+#endif
167+
168+// From preprocess_map_sdl.cpp
169+extern bool get_default_music_spec(FileSpecifier &file);
170+
171+// From vbl_sdl.cpp
172+void execute_timer_tasks(uint32 time);
173+
174+// Prototypes
175+static void initialize_application(void);
176+void shutdown_application(void);
177+static void initialize_marathon_music_handler(void);
178+static void process_event(const SDL_Event &event);
179+
180+// cross-platform static variables
181+short vidmasterStringSetID = -1; // can be set with MML
182+
183+static void usage(const char *prg_name)
184+{
185+#ifdef __WIN32__
186+ MessageBox(NULL, "コマンドラインスイッチ:\n\n"
187+#else
188+ printf("\n使用方法:%s [オプション] [ディレクトリ] [ファイル]\n"
189+#endif
190+ "\t[-h | --help] このヘルプメッセージを表\示します。\n"
191+ "\t[-v | --version] ゲームのバージョンを表\示します。\n"
192+ "\t[-d | --debug] コアダンプを出力するようにします。\n"
193+ "\t (SDL parachuteを無効化します)\n"
194+ "\t[-f | --fullscreen] ゲームをフルスクリーンで起動します。\n"
195+ "\t[-w | --windowed] ゲームをウィンドウモードで起動します。\n"
196+#ifdef HAVE_OPENGL
197+ "\t[-g | --nogl] OpenGLを使用せずに起動します。\n"
198+#endif
199+ "\t[-s | --nosound] サウンドを無効化します。\n"
200+ "\t[-m | --nogamma] ガンマエフェクトを無効化します。\n"
201+ "\t (メニューのフェードなど)\n"
202+ "\t[-j | --nojoystick] ジョイスティックの初期化を行いません。\n"
203+ // Documenting this might be a bad idea?
204+ // "\t[-i | --insecure_lua] Allow Lua netscripts to take over your computer\n"
205+ "\tディレクトリ データーが含まれているディレクトリ\n"
206+ "\tファイル 保存されたゲームやフィルムの再生\n"
207+ "\nこの他にも、環境変数「ALEPHONE_DATA」の値を変更することで、\n"
208+ "データディレクトリを指定することができます。\n"
209+#ifdef __WIN32__
210+ , "使用方法", MB_OK | MB_ICONINFORMATION
211+#else
212+ , prg_name
213+#endif
214+ );
215+ exit(0);
216+}
217+
218+extern bool handle_open_replay(FileSpecifier& File);
219+extern bool load_and_start_game(FileSpecifier& file);
220+
221+bool handle_open_document(const std::string& filename)
222+{
223+ bool done = false;
224+ FileSpecifier file(filename);
225+ switch (file.GetType())
226+ {
227+ case _typecode_scenario:
228+ set_map_file(file);
229+ break;
230+ case _typecode_savegame:
231+ if (load_and_start_game(file))
232+ {
233+ done = true;
234+ }
235+ break;
236+ case _typecode_film:
237+ if (handle_open_replay(file))
238+ {
239+ done = true;
240+ }
241+ break;
242+ case _typecode_physics:
243+ set_physics_file(file);
244+ break;
245+ case _typecode_shapes:
246+ open_shapes_file(file);
247+ break;
248+ case _typecode_sounds:
249+ SoundManager::instance()->OpenSoundFile(file);
250+ break;
251+ default:
252+ break;
253+ }
254+
255+ return done;
256+}
257+
258+int main(int argc, char **argv)
259+{
260+ // Print banner (don't bother if this doesn't appear when started from a GUI)
261+ printf ("Aleph One JP" A1_VERSION_STRING "\n"
262+ "http://marathon.sourceforge.jp/\n\n"
263+ "オリジナルのコードは、Bungie Software <http://www.bungie.com/>によるものです。\n"
264+ "この他にLoren Petrich, Chris Pruett, Rhys Hill氏らによって書かれています。\n"
265+ "TCP/IP ネットワーク by Woody Zenfell\n"
266+ "Expat XMLライブラリ by James Clark\n"
267+ "SDLポート by Christian Bauer <Christian.Bauer@uni-mainz.de>\n"
268+ "日本語化 by saiten <http://www.isidesystem.net/>, ookawa_mi, Logue <http://logue.be/>\n"
269+#if defined(__MACH__) && defined(__APPLE__)
270+ "Mac OS X/SDLバージョンは、Chris Lovell, Alexander Strange, and Woody Zenfell氏らによって作られました。\n"
271+#endif
272+ "\nこのプログラムは有用であることを願って頒布されますが、*全くの無保証 *です。\n"
273+ "商業可能\性の保証や特定目的への適合性は、言外に示されたものも 含め、全く存在しません。\n"
274+ "詳しくはGNU 一般公衆利用許諾書をご覧ください。\n"
275+#if defined(__BEOS__) || defined(__WIN32__)
276+ // BeOS and Windows are statically linked against SDL, so we have to include this:
277+ "\nSimple DirectMedia Layer (SDL) ライブラリは、\n"
278+ "GNU 一般公衆利用許諾書によってライセンスされています。\n"
279+ "詳細については、COPYING.SDLを参考にしてください。\n"
280+#endif
281+#ifdef HAVE_SDL_NET
282+ "\nこのビルドは、ネットワークプレイが有効です。\n"
283+#endif
284+#ifdef HAVE_LUA
285+ "\nこのビルドは、Luaスクリプトが有効です。\n"
286+#endif
287+ );
288+
289+ // Parse arguments
290+ char *prg_name = argv[0];
291+ argc--;
292+ argv++;
293+ while (argc > 0) {
294+ if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "--help") == 0) {
295+ usage(prg_name);
296+ } else if (strcmp(*argv, "-v") == 0 || strcmp(*argv, "--version") == 0) {
297+ printf("Aleph One " A1_VERSION_STRING "\n");
298+ exit(0);
299+ } else if (strcmp(*argv, "-f") == 0 || strcmp(*argv, "--fullscreen") == 0) {
300+ force_fullscreen = true;
301+ } else if (strcmp(*argv, "-w") == 0 || strcmp(*argv, "--windowed") == 0) {
302+ force_windowed = true;
303+ } else if (strcmp(*argv, "-g") == 0 || strcmp(*argv, "--nogl") == 0) {
304+ option_nogl = true;
305+ } else if (strcmp(*argv, "-s") == 0 || strcmp(*argv, "--nosound") == 0) {
306+ option_nosound = true;
307+ } else if (strcmp(*argv, "-j") == 0 || strcmp(*argv, "--nojoystick") == 0) {
308+ option_nojoystick = true;
309+ } else if (strcmp(*argv, "-m") == 0 || strcmp(*argv, "--nogamma") == 0) {
310+ option_nogamma = true;
311+ } else if (strcmp(*argv, "-i") == 0 || strcmp(*argv, "--insecure_lua") == 0) {
312+ insecure_lua = true;
313+ } else if (strcmp(*argv, "-d") == 0 || strcmp(*argv, "--debug") == 0) {
314+ option_debug = true;
315+ } else if (*argv[0] != '-') {
316+ // if it's a directory, make it the default data dir
317+ // otherwise push it and handle it later
318+ FileSpecifier f(*argv);
319+ if (f.IsDir())
320+ {
321+ arg_directory = *argv;
322+ }
323+ else
324+ {
325+ arg_files.push_back(*argv);
326+ }
327+ } else {
328+ printf("不明なスイッチ指定です:'%s'.\n", *argv);
329+ usage(prg_name);
330+ }
331+ argc--;
332+ argv++;
333+ }
334+
335+ try {
336+
337+ // Initialize everything
338+ initialize_application();
339+
340+ for (std::vector<std::string>::iterator it = arg_files.begin(); it != arg_files.end(); ++it)
341+ {
342+ if (handle_open_document(*it))
343+ {
344+ break;
345+ }
346+ }
347+
348+ // Run the main loop
349+ main_event_loop();
350+
351+ } catch (exception &e) {
352+ try
353+ {
354+ logFatal("捕捉されなかった例外が発生しました:%s", e.what());
355+ }
356+ catch (...)
357+ {
358+ }
359+ exit(1);
360+ } catch (...) {
361+ try
362+ {
363+ logFatal("例外が発生しました。");
364+ }
365+ catch (...)
366+ {
367+ }
368+ exit(1);
369+ }
370+
371+ return 0;
372+}
373+
374+static void initialize_application(void)
375+{
376+ setlocale( LC_ALL, "JPN"); // for wchar_t type.
377+#if defined(__WIN32__) && defined(__MINGW32__)
378+ if (LoadLibrary("exchndl.dll")) option_debug = true;
379+#endif
380+
381+ // Find data directories, construct search path
382+ DirectorySpecifier default_data_dir;
383+
384+#if defined(unix) || defined(__NetBSD__) || defined(__OpenBSD__) || (defined(__APPLE__) && defined(__MACH__) && !defined(HAVE_BUNDLE_NAME))
385+
386+ default_data_dir = PKGDATADIR;
387+ const char *home = getenv("HOME");
388+ if (home)
389+ local_data_dir = home;
390+ local_data_dir += ".alephone";
391+ log_dir = local_data_dir;
392+
393+#elif defined(__APPLE__) && defined(__MACH__)
394+ extern char *bundle_name; // SDLMain.m
395+ DirectorySpecifier bundle_data_dir = bundle_name;
396+ bundle_data_dir += "Contents/Resources/DataFiles";
397+
398+ data_search_path.push_back(bundle_data_dir);
399+
400+ {
401+ char* buf = getcwd(0, 0);
402+ default_data_dir = buf;
403+ free(buf);
404+ }
405+
406+ const char *home = getenv("HOME");
407+ if (home)
408+ {
409+ local_data_dir = home;
410+ preferences_dir = home;
411+ log_dir = home;
412+ log_dir += "Library";
413+ log_dir += "Logs";
414+ }
415+
416+ local_data_dir += "Library";
417+ local_data_dir += "Application Support";
418+ local_data_dir += "AlephOne";
419+
420+ preferences_dir += "Library";
421+ preferences_dir += "Preferences";
422+ preferences_dir += "org.bungie.source.AlephOne";
423+
424+#elif defined(__BEOS__)
425+
426+ default_data_dir = get_application_directory();
427+ local_data_dir = get_preferences_directory();
428+
429+#elif defined(__WIN32__)
430+
431+ char file_name[MAX_PATH];
432+ GetModuleFileName(NULL, file_name, sizeof(file_name));
433+ char *sep = strrchr(file_name, '\\');
434+ *sep = '\0';
435+
436+ default_data_dir = file_name;
437+
438+ char login[17];
439+ DWORD len = 17;
440+
441+ bool hasName = (GetUserName((LPSTR) login, &len) == TRUE);
442+ if (!hasName || strpbrk(login, "\\/:*?\"<>|") != NULL)
443+ strcpy(login, "Bob User");
444+
445+ DirectorySpecifier legacy_data_dir = file_name;
446+ legacy_data_dir += "Prefs";
447+ legacy_data_dir += login;
448+
449+ SHGetFolderPath(NULL,
450+ CSIDL_PERSONAL | CSIDL_FLAG_CREATE,
451+ NULL,
452+ 0,
453+ file_name);
454+ local_data_dir = file_name;
455+ local_data_dir += "AlephOne";
456+
457+ log_dir = local_data_dir;
458+
459+#else
460+ default_data_dir = "";
461+ local_data_dir = "";
462+//#error Data file paths must be set for this platform.
463+#endif
464+
465+#if defined(__WIN32__)
466+#define LIST_SEP ';'
467+#else
468+#define LIST_SEP ':'
469+#endif
470+
471+ // in case we need to redo search path later:
472+ size_t dsp_insert_pos = data_search_path.size();
473+ size_t dsp_delete_pos = (size_t)-1;
474+
475+ if (arg_directory != "")
476+ {
477+ dsp_delete_pos = data_search_path.size();
478+ data_search_path.push_back(arg_directory);
479+ }
480+
481+ const char *data_env = getenv("ALEPHONE_DATA");
482+ if (data_env) {
483+ // Read colon-separated list of directories
484+ string path = data_env;
485+ string::size_type pos;
486+ while ((pos = path.find(LIST_SEP)) != string::npos) {
487+ if (pos) {
488+ string element = path.substr(0, pos);
489+ data_search_path.push_back(element);
490+ }
491+ path.erase(0, pos + 1);
492+ }
493+ if (!path.empty())
494+ data_search_path.push_back(path);
495+ } else {
496+ if (arg_directory == "")
497+ {
498+ dsp_delete_pos = data_search_path.size();
499+ data_search_path.push_back(default_data_dir);
500+ }
501+#if defined(__WIN32__)
502+ data_search_path.push_back(legacy_data_dir);
503+#endif
504+#ifndef __MACOS__
505+ data_search_path.push_back(local_data_dir);
506+#endif
507+ }
508+
509+ // Subdirectories
510+#if defined(__MACH__) && defined(__APPLE__)
511+ DirectorySpecifier legacy_preferences_dir = local_data_dir;
512+#elif defined(__WIN32__)
513+ DirectorySpecifier legacy_preferences_dir = legacy_data_dir;
514+ SHGetFolderPath(NULL,
515+ CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
516+ NULL,
517+ 0,
518+ file_name);
519+ preferences_dir = file_name;
520+ preferences_dir += "AlephOne";
521+#else
522+ preferences_dir = local_data_dir;
523+#endif
524+ saved_games_dir = local_data_dir + "Saved Games";
525+ recordings_dir = local_data_dir + "Recordings";
526+ screenshots_dir = local_data_dir + "Screenshots";
527+
528+
529+ DirectorySpecifier local_mml_dir = local_data_dir + "MML";
530+ DirectorySpecifier local_themes_dir = local_data_dir + "Themes";
531+
532+ // Setup resource manager
533+ initialize_resources();
534+
535+ init_physics_wad_data();
536+
537+ load_film_profile(FILM_PROFILE_DEFAULT, false);
538+
539+ // Parse MML files
540+ SetupParseTree();
541+ LoadBaseMMLScripts();
542+
543+ // Check for presence of strings
544+ if (!TS_IsPresent(strERRORS) || !TS_IsPresent(strFILENAMES)) {
545+ fprintf(stderr, "起動に必要なテキストが見つかりませんでした。(MMLが存在しない?)\n");
546+ exit(1);
547+ }
548+
549+ // Check for presence of files (one last chance to change data_search_path)
550+ if (!have_default_files()) {
551+ char chosen_dir[256];
552+ if (alert_choose_scenario(chosen_dir)) {
553+ // remove original argument (or fallback) from search path
554+ if (dsp_delete_pos < data_search_path.size())
555+ data_search_path.erase(data_search_path.begin() + dsp_delete_pos);
556+ // add selected directory where command-line argument would go
557+ data_search_path.insert(data_search_path.begin() + dsp_insert_pos, chosen_dir);
558+
559+ // Parse MML files again, now that we have a new dir to search
560+ SetupParseTree();
561+ LoadBaseMMLScripts();
562+ }
563+ }
564+
565+ initialize_fonts();
566+ Plugins::instance()->enumerate();
567+
568+#if defined(__WIN32__) || (defined(__MACH__) && defined(__APPLE__))
569+ preferences_dir.CreateDirectory();
570+ transition_preferences(legacy_preferences_dir);
571+#endif
572+
573+ // Load preferences
574+ initialize_preferences();
575+
576+ local_data_dir.CreateDirectory();
577+ saved_games_dir.CreateDirectory();
578+ recordings_dir.CreateDirectory();
579+ screenshots_dir.CreateDirectory();
580+ local_mml_dir.CreateDirectory();
581+ local_themes_dir.CreateDirectory();
582+
583+#ifndef HAVE_OPENGL
584+ graphics_preferences->screen_mode.acceleration = _no_acceleration;
585+#endif
586+ if (force_fullscreen)
587+ graphics_preferences->screen_mode.fullscreen = true;
588+ if (force_windowed) // takes precedence over fullscreen because windowed is safer
589+ graphics_preferences->screen_mode.fullscreen = false;
590+ write_preferences();
591+
592+ Plugins::instance()->load_mml();
593+
594+ SDL_putenv(const_cast<char*>("SDL_VIDEO_ALLOW_SCREENSAVER=1"));
595+
596+ // Initialize SDL
597+ int retval = SDL_Init(SDL_INIT_VIDEO |
598+ (option_nosound ? 0 : SDL_INIT_AUDIO) |
599+ (option_nojoystick ? 0 : SDL_INIT_JOYSTICK) |
600+ (option_debug ? SDL_INIT_NOPARACHUTE : 0));
601+ if (retval < 0)
602+ {
603+ fprintf(stderr, "SDLの初期化に失敗しました。(%s)\n", SDL_GetError());
604+ exit(1);
605+ }
606+ SDL_WM_SetCaption("Aleph One", "Aleph One");
607+
608+#if defined(HAVE_SDL_IMAGE) && (SDL_IMAGE_PATCHLEVEL >= 8)
609+ IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
610+#endif
611+
612+#if defined(HAVE_SDL_IMAGE) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__MACOS__)
613+ SDL_WM_SetIcon(IMG_ReadXPMFromArray(const_cast<char**>(alephone_xpm)), 0);
614+#endif
615+ atexit(shutdown_application);
616+
617+#ifdef HAVE_SDL_NET
618+ // Initialize SDL_net
619+ if (SDLNet_Init () < 0) {
620+ fprintf (stderr, "SDL_netの初期化に失敗しました。(%s)\n", SDLNet_GetError());
621+ exit(1);
622+ }
623+#endif
624+
625+#ifdef HAVE_SDL_TTF
626+ if (TTF_Init() < 0) {
627+ fprintf (stderr, "SDL_ttfの初期化に失敗しました。(%s)\n", TTF_GetError());
628+ exit(1);
629+ }
630+#endif
631+
632+ // Initialize everything
633+ mytm_initialize();
634+// initialize_fonts();
635+ SoundManager::instance()->Initialize(*sound_preferences);
636+ initialize_marathon_music_handler();
637+ initialize_keyboard_controller();
638+ initialize_gamma();
639+ alephone::Screen::instance()->Initialize(&graphics_preferences->screen_mode);
640+ initialize_marathon();
641+ initialize_screen_drawing();
642+ FileSpecifier theme = environment_preferences->theme_dir;
643+ const Plugin* theme_plugin = Plugins::instance()->find_theme();
644+ if (theme_plugin)
645+ {
646+ theme = theme_plugin->directory + theme_plugin->theme;
647+ }
648+ initialize_dialogs(theme);
649+ initialize_terminal_manager();
650+ initialize_shape_handler();
651+ initialize_fades();
652+ initialize_images_manager();
653+ load_environment_from_preferences();
654+ initialize_game_state();
655+}
656+
657+void shutdown_application(void)
658+{
659+ // ZZZ: seem to be having weird recursive shutdown problems esp. with fullscreen modes...
660+ static bool already_shutting_down = false;
661+ if(already_shutting_down)
662+ return;
663+
664+ already_shutting_down = true;
665+
666+ restore_gamma();
667+#if defined(HAVE_SDL_IMAGE) && (SDL_IMAGE_PATCHLEVEL >= 8)
668+ IMG_Quit();
669+#endif
670+#ifdef HAVE_SDL_NET
671+ SDLNet_Quit();
672+#endif
673+#ifdef HAVE_SDL_TTF
674+ TTF_Quit();
675+#endif
676+ SDL_Quit();
677+}
678+
679+bool networking_available(void)
680+{
681+#ifdef HAVE_SDL_NET
682+ return true;
683+#else
684+ return false;
685+#endif
686+}
687+
688+static void initialize_marathon_music_handler(void)
689+{
690+ FileSpecifier file;
691+ if (get_default_music_spec(file))
692+ Music::instance()->SetupIntroMusic(file);
693+}
694+
695+bool quit_without_saving(void)
696+{
697+ dialog d;
698+ vertical_placer *placer = new vertical_placer;
699+ placer->dual_add (new w_static_text("本当にゲームを中断しても"), d);
700+ placer->dual_add (new w_static_text("よろしいですか?"), d);
701+ placer->add (new w_spacer(), true);
702+
703+ horizontal_placer *button_placer = new horizontal_placer;
704+ w_button *default_button = new w_button("はい", dialog_ok, &d);
705+ button_placer->dual_add (default_button, d);
706+ button_placer->dual_add (new w_button("いいえ", dialog_cancel, &d), d);
707+ d.activate_widget(default_button);
708+ placer->add(button_placer, true);
709+ d.set_widget_placer(placer);
710+ return d.run() == 0;
711+}
712+
713+// ZZZ: moved level-numbers widget into sdl_widgets for a wider audience.
714+
715+const int32 AllPlayableLevels = _single_player_entry_point | _multiplayer_carnage_entry_point | _multiplayer_cooperative_entry_point | _kill_the_man_with_the_ball_entry_point | _king_of_hill_entry_point | _rugby_entry_point | _capture_the_flag_entry_point;
716+
717+short get_level_number_from_user(void)
718+{
719+ // Get levels
720+ vector<entry_point> levels;
721+ if (!get_entry_points(levels, AllPlayableLevels)) {
722+ entry_point dummy;
723+ dummy.level_number = 0;
724+ strcpy(dummy.level_name, "Untitled Level");
725+ levels.push_back(dummy);
726+ }
727+
728+ // Create dialog
729+ dialog d;
730+ vertical_placer *placer = new vertical_placer;
731+ if (vidmasterStringSetID != -1 && TS_IsPresent(vidmasterStringSetID) && TS_CountStrings(vidmasterStringSetID) > 0) {
732+ // if we there's a stringset present for it, load the message from there
733+ int num_lines = TS_CountStrings(vidmasterStringSetID);
734+
735+ for (size_t i = 0; i < num_lines; i++) {
736+ bool message_font_title_color = true;
737+ char *string = TS_GetCString(vidmasterStringSetID, i);
738+ if (!strncmp(string, "[QUOTE]", 7)) {
739+ string = string + 7;
740+ message_font_title_color = false;
741+ }
742+ if (!strlen(string))
743+ placer->add(new w_spacer(), true);
744+ else if (message_font_title_color)
745+ placer->dual_add(new w_static_text(string), d);
746+ else
747+ placer->dual_add(new w_static_text(string), d);
748+ }
749+
750+ } else {
751+ // no stringset or no strings in stringset - use default message
752+ placer->dual_add(new w_static_text("ここからは、ヴィドマスターの宣誓を誓わないといけないぜ。"), d);
753+
754+ placer->add(new w_spacer(), true);
751755 placer->dual_add(new w_static_text("『宣誓、"), d);
752756 placer->dual_add(new w_static_text("全てのスイッチをこぶしで殴ってオンにし、"), d);
753757 placer->dual_add(new w_static_text("グレネードを使える場所でも決して発射せず、"), d);
754758 placer->dual_add(new w_static_text("最高難易度「虐殺」以外で遊ばず、"), d);
755759 placer->dual_add(new w_static_text("Caps Loockを「走る」キーとしては決して使わず、"), d);
756- placer->dual_add(new w_static_text("そして、一人残らずボブ市民を皆殺しにしま〜す。』"), d);
757- }
758-
759- placer->add(new w_spacer(), true);
760- placer->dual_add(new w_static_text("開始レベル:"), d);
761-
762- w_levels *level_w = new w_levels(levels, &d);
763- placer->dual_add(level_w, d);
764- placer->add(new w_spacer(), true);
765- placer->dual_add(new w_button("キャンセル", dialog_cancel, &d), d);
766-
767- d.activate_widget(level_w);
768- d.set_widget_placer(placer);
769-
770- // Run dialog
771- short level;
772- if (d.run() == 0) // OK
773- // Should do noncontiguous map files OK
774- level = levels[level_w->get_selection()].level_number;
775- else
776- level = NONE;
777-
778- // Redraw main menu
779- update_game_window();
780- return level;
781-}
782-
783-const uint32 TICKS_BETWEEN_EVENT_POLL = 167; // 6 Hz
784-static void main_event_loop(void)
785-{
786- uint32 last_event_poll = 0;
787- short game_state;
788-
789- while ((game_state = get_game_state()) != _quit_game) {
790- uint32 cur_time = SDL_GetTicks();
791- bool yield_time = false;
792- bool poll_event = false;
793-
794- switch (game_state) {
795- case _game_in_progress:
796- case _change_level:
797- if (Console::instance()->input_active() || cur_time - last_event_poll >= TICKS_BETWEEN_EVENT_POLL) {
798- poll_event = true;
799- last_event_poll = cur_time;
800- } else {
801- SDL_PumpEvents (); // This ensures a responsive keyboard control
802- }
803- break;
804-
805- case _display_intro_screens:
806- case _display_main_menu:
807- case _display_chapter_heading:
808- case _display_prologue:
809- case _display_epilogue:
810- case _begin_display_of_epilogue:
811- case _display_credits:
812- case _display_intro_screens_for_demo:
813- case _display_quit_screens:
814- case _displaying_network_game_dialogs:
815- yield_time = interface_fade_finished();
816- poll_event = true;
817- break;
818-
819- case _close_game:
820- case _switch_demo:
821- case _revert_game:
822- yield_time = poll_event = true;
823- break;
824- }
825-
826- if (poll_event) {
827- global_idle_proc();
828-
829- while (true) {
830- SDL_Event event;
831- event.type = SDL_NOEVENT;
832- SDL_PollEvent(&event);
833-
834- if (yield_time) {
835- // The game is not in a "hot" state, yield time to other
836- // processes by calling SDL_Delay() but only try for a maximum
837- // of 30ms
838- int num_tries = 0;
839- while (event.type == SDL_NOEVENT && num_tries < 3) {
840- SDL_Delay(10);
841- SDL_PollEvent(&event);
842- num_tries++;
843- }
844- yield_time = false;
845- } else if (event.type == SDL_NOEVENT)
846- break;
847-
848- process_event(event);
849- }
850- }
851-
852- execute_timer_tasks(SDL_GetTicks());
853- idle_game_state(SDL_GetTicks());
854-
855-#ifndef __MACOS__
856- if (game_state == _game_in_progress && !graphics_preferences->hog_the_cpu && (TICKS_PER_SECOND - (SDL_GetTicks() - cur_time)) > 10)
857- {
858- SDL_Delay(1);
859- }
860-#endif
861- }
862-}
863-
864-static bool has_cheat_modifiers(void)
865-{
866- SDLMod m = SDL_GetModState();
867-#if (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOS__)
868- return ((m & KMOD_SHIFT) && (m & KMOD_CTRL)) || ((m & KMOD_ALT) && (m & KMOD_META));
869-#else
870- return (m & KMOD_SHIFT) && (m & KMOD_CTRL) && !(m & KMOD_ALT) && !(m & KMOD_META);
871-#endif
872-}
873-
874-static void process_screen_click(const SDL_Event &event)
875-{
876- int x = event.button.x, y = event.button.y;
877-#ifdef HAVE_OPENGL
878- if (OGL_IsActive())
879- OGL_Blitter::WindowToScreen(x, y);
880-#endif
881- portable_process_screen_click(x, y, has_cheat_modifiers());
882-}
883-
884-static void handle_game_key(const SDL_Event &event)
885-{
886- SDLKey key = event.key.keysym.sym;
887- bool changed_screen_mode = false;
888- bool changed_prefs = false;
889-
890- if (!game_is_networked && (event.key.keysym.mod & KMOD_CTRL) && CheatsActive) {
891- int type_of_cheat = process_keyword_key(key);
892- if (type_of_cheat != NONE)
893- handle_keyword(type_of_cheat);
894- }
895- if (Console::instance()->input_active()) {
896- switch(key) {
897- case SDLK_RETURN:
898- Console::instance()->enter();
899- break;
900- case SDLK_ESCAPE:
901- Console::instance()->abort();
902- break;
903- case SDLK_BACKSPACE:
904- case SDLK_DELETE:
905- Console::instance()->backspace();
906- break;
907- default:
908- if (event.key.keysym.unicode == 8) // Crtl-H
909- {
910- Console::instance()->backspace();
911- }
912- else if (event.key.keysym.unicode == 21) // Crtl-U
913- {
914- Console::instance()->clear();
915- }
916- else if (event.key.keysym.unicode >= ' ') {
917- Console::instance()->key(unicode_to_mac_roman(event.key.keysym.unicode));
918- }
919- }
920- }
921- else
922- {
923- if (key == SDLK_ESCAPE) // (ZZZ) Quit gesture (now safer)
924- {
925- if(!player_controlling_game())
926- do_menu_item_command(mGame, iQuitGame, false);
927- else {
928- if(get_ticks_since_local_player_in_terminal() > 1 * TICKS_PER_SECOND) {
929- if(!game_is_networked) {
930- do_menu_item_command(mGame, iQuitGame, false);
931- }
932- else {
933-#if defined(__APPLE__) && defined(__MACH__)
934- screen_printf("終了したい場合は、コマンドキーを押しながらQを押してください。");
935-#else
936- screen_printf("終了したい場合は、Altキーを押しながらQを押してください。");
937-#endif
938- }
939- }
940- }
941- }
942- else if (key == input_preferences->shell_keycodes[_key_volume_up])
943- {
944- changed_prefs = SoundManager::instance()->AdjustVolumeUp(_snd_adjust_volume);
945- }
946- else if (key == input_preferences->shell_keycodes[_key_volume_down])
947- {
948- changed_prefs = SoundManager::instance()->AdjustVolumeDown(_snd_adjust_volume);
949- }
950- else if (key == input_preferences->shell_keycodes[_key_switch_view])
951- {
952- walk_player_list();
953- render_screen(NONE);
954- }
955- else if (key == input_preferences->shell_keycodes[_key_zoom_in])
956- {
957- if (zoom_overhead_map_in())
958- PlayInterfaceButtonSound(Sound_ButtonSuccess());
959- else
960- PlayInterfaceButtonSound(Sound_ButtonFailure());
961- }
962- else if (key == input_preferences->shell_keycodes[_key_zoom_out])
963- {
964- if (zoom_overhead_map_out())
965- PlayInterfaceButtonSound(Sound_ButtonSuccess());
966- else
967- PlayInterfaceButtonSound(Sound_ButtonFailure());
968- }
969- else if (key == input_preferences->shell_keycodes[_key_inventory_left])
970- {
971- if (player_controlling_game()) {
972- PlayInterfaceButtonSound(Sound_ButtonSuccess());
973- scroll_inventory(-1);
974- } else
975- decrement_replay_speed();
976- }
977- else if (key == input_preferences->shell_keycodes[_key_inventory_right])
978- {
979- if (player_controlling_game()) {
980- PlayInterfaceButtonSound(Sound_ButtonSuccess());
981- scroll_inventory(1);
982- } else
983- increment_replay_speed();
984- }
985- else if (key == input_preferences->shell_keycodes[_key_toggle_fps])
986- {
987- PlayInterfaceButtonSound(Sound_ButtonSuccess());
988- extern bool displaying_fps;
989- displaying_fps = !displaying_fps;
990- }
991- else if (key == input_preferences->shell_keycodes[_key_activate_console])
992- {
993- if (game_is_networked) {
994-#if !defined(DISABLE_NETWORKING)
995- Console::instance()->activate_input(InGameChatCallbacks::SendChatMessage, InGameChatCallbacks::prompt());
996-#endif
997- PlayInterfaceButtonSound(Sound_ButtonSuccess());
998- }
999- else if (Console::instance()->use_lua_console())
1000- {
1001- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1002- Console::instance()->activate_input(ExecuteLuaString, ">");
1003- }
1004- else
1005- {
1006- PlayInterfaceButtonSound(Sound_ButtonFailure());
1007- }
1008- }
1009- else if (key == input_preferences->shell_keycodes[_key_show_scores])
1010- {
1011- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1012- {
1013- extern bool ShowScores;
1014- ShowScores = !ShowScores;
1015- }
1016- }
1017- else if (key == SDLK_F1) // Decrease screen size
1018- {
1019- if (!graphics_preferences->screen_mode.hud)
1020- {
1021- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1022- graphics_preferences->screen_mode.hud = true;
1023- changed_screen_mode = changed_prefs = true;
1024- }
1025- else
1026- {
1027- int mode = alephone::Screen::instance()->FindMode(graphics_preferences->screen_mode.width, graphics_preferences->screen_mode.height);
1028- if (mode < alephone::Screen::instance()->GetModes().size() - 1)
1029- {
1030- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1031- graphics_preferences->screen_mode.width = alephone::Screen::instance()->ModeWidth(mode + 1);
1032- graphics_preferences->screen_mode.height = alephone::Screen::instance()->ModeHeight(mode + 1);
1033- graphics_preferences->screen_mode.hud = false;
1034- changed_screen_mode = changed_prefs = true;
1035- } else
1036- PlayInterfaceButtonSound(Sound_ButtonFailure());
1037- }
1038- }
1039- else if (key == SDLK_F2) // Increase screen size
1040- {
1041- if (graphics_preferences->screen_mode.hud)
1042- {
1043- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1044- graphics_preferences->screen_mode.hud = false;
1045- changed_screen_mode = changed_prefs = true;
1046- }
1047- else
1048- {
1049- int mode = alephone::Screen::instance()->FindMode(graphics_preferences->screen_mode.width, graphics_preferences->screen_mode.height);
1050- if (mode > 0)
1051- {
1052- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1053- graphics_preferences->screen_mode.width = alephone::Screen::instance()->ModeWidth(mode - 1);
1054- graphics_preferences->screen_mode.height = alephone::Screen::instance()->ModeHeight(mode - 1);
1055- graphics_preferences->screen_mode.hud = true;
1056- changed_screen_mode = changed_prefs = true;
1057- } else
1058- PlayInterfaceButtonSound(Sound_ButtonFailure());
1059- }
1060- }
1061- else if (key == SDLK_F3) // Resolution toggle
1062- {
1063- if (!OGL_IsActive()) {
1064- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1065- graphics_preferences->screen_mode.high_resolution = !graphics_preferences->screen_mode.high_resolution;
1066- changed_screen_mode = changed_prefs = true;
1067- } else
1068- PlayInterfaceButtonSound(Sound_ButtonFailure());
1069- }
1070- else if (key == SDLK_F4) // Reset OpenGL textures
1071- {
1072-#ifdef HAVE_OPENGL
1073- if (OGL_IsActive()) {
1074- // Play the button sound in advance to get the full effect of the sound
1075- PlayInterfaceButtonSound(Sound_OGL_Reset());
1076- OGL_ResetTextures();
1077- } else
1078-#endif
1079- PlayInterfaceButtonSound(Sound_ButtonInoperative());
1080- }
1081- else if (key == SDLK_F5) // Make the chase cam switch sides
1082- {
1083- if (ChaseCam_IsActive())
1084- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1085- else
1086- PlayInterfaceButtonSound(Sound_ButtonInoperative());
1087- ChaseCam_SwitchSides();
1088- }
1089- else if (key == SDLK_F6) // Toggle the chase cam
1090- {
1091- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1092- ChaseCam_SetActive(!ChaseCam_IsActive());
1093- }
1094- else if (key == SDLK_F7) // Toggle tunnel vision
1095- {
1096- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1097- SetTunnelVision(!GetTunnelVision());
1098- }
1099- else if (key == SDLK_F8) // Toggle the crosshairs
1100- {
1101- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1102- Crosshairs_SetActive(!Crosshairs_IsActive());
1103- }
1104- else if (key == SDLK_F9) // Screen dump
1105- {
1106- dump_screen();
1107- }
1108- else if (key == SDLK_F10) // Toggle the position display
1109- {
1110- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1111- {
1112- extern bool ShowPosition;
1113- ShowPosition = !ShowPosition;
1114- }
1115- }
1116- else if (key == SDLK_F11) // Decrease gamma level
1117- {
1118- if (graphics_preferences->screen_mode.gamma_level) {
1119- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1120- graphics_preferences->screen_mode.gamma_level--;
1121- change_gamma_level(graphics_preferences->screen_mode.gamma_level);
1122- changed_prefs = true;
1123- } else
1124- PlayInterfaceButtonSound(Sound_ButtonFailure());
1125- }
1126- else if (key == SDLK_F12) // Increase gamma level
1127- {
1128- if (graphics_preferences->screen_mode.gamma_level < NUMBER_OF_GAMMA_LEVELS - 1) {
1129- PlayInterfaceButtonSound(Sound_ButtonSuccess());
1130- graphics_preferences->screen_mode.gamma_level++;
1131- change_gamma_level(graphics_preferences->screen_mode.gamma_level);
1132- changed_prefs = true;
1133- } else
1134- PlayInterfaceButtonSound(Sound_ButtonFailure());
1135- }
1136- else
1137- {
1138- if (get_game_controller() == _demo)
1139- set_game_state(_close_game);
1140- }
1141- }
1142-
1143- if (changed_screen_mode) {
1144- screen_mode_data temp_screen_mode = graphics_preferences->screen_mode;
1145- temp_screen_mode.fullscreen = get_screen_mode()->fullscreen;
1146- change_screen_mode(&temp_screen_mode, true);
1147- render_screen(0);
1148- }
1149-
1150- if (changed_prefs)
1151- write_preferences();
1152-}
1153-
1154-static void process_game_key(const SDL_Event &event)
1155-{
1156- switch (get_game_state()) {
1157- case _game_in_progress:
1158-#if defined(__APPLE__) && defined(__MACH__)
1159- if ((event.key.keysym.mod & KMOD_META))
1160-#else
1161- if ((event.key.keysym.mod & KMOD_ALT) || (event.key.keysym.mod & KMOD_META))
1162-#endif
1163- {
1164- int item = -1;
1165- switch (event.key.keysym.sym) {
1166- case SDLK_p:
1167- item = iPause;
1168- break;
1169- case SDLK_s:
1170- item = iSave;
1171- break;
1172- case SDLK_r:
1173- item = iRevert;
1174- break;
1175-// ZZZ: Alt+F4 is also a quit gesture in Windows
1176-#ifdef __WIN32__
1177- case SDLK_F4:
1178-#endif
1179- case SDLK_q:
1180- item = iQuitGame;
1181- break;
1182- case SDLK_RETURN:
1183- item = 0;
1184- toggle_fullscreen();
1185- break;
1186- default:
1187- break;
1188- }
1189- if (item > 0)
1190- do_menu_item_command(mGame, item, has_cheat_modifiers());
1191- else if (item != 0)
1192- handle_game_key(event);
1193- } else
1194- handle_game_key(event);
1195- break;
1196- case _display_intro_screens:
1197- case _display_chapter_heading:
1198- case _display_prologue:
1199- case _display_epilogue:
1200- case _display_credits:
1201- case _display_quit_screens:
1202- if (interface_fade_finished())
1203- force_game_state_change();
1204- else
1205- stop_interface_fade();
1206- break;
1207-
1208- case _display_intro_screens_for_demo:
1209- stop_interface_fade();
1210- display_main_menu();
1211- break;
1212-
1213- case _quit_game:
1214- case _close_game:
1215- case _revert_game:
1216- case _switch_demo:
1217- case _change_level:
1218- case _begin_display_of_epilogue:
1219- case _displaying_network_game_dialogs:
1220- break;
1221-
1222- case _display_main_menu:
1223- {
1224- if (!interface_fade_finished())
1225- stop_interface_fade();
1226- int item = -1;
1227- switch (event.key.keysym.sym) {
1228- case SDLK_n:
1229- item = iNewGame;
1230- break;
1231- case SDLK_o:
1232- item = iLoadGame;
1233- break;
1234- case SDLK_g:
1235- item = iGatherGame;
1236- break;
1237- case SDLK_j:
1238- item = iJoinGame;
1239- break;
1240- case SDLK_p:
1241- item = iPreferences;
1242- break;
1243- case SDLK_r:
1244- item = iReplaySavedFilm;
1245- break;
1246- case SDLK_c:
1247- item = iCredits;
1248- break;
1249-// ZZZ: Alt+F4 is also a quit gesture in Windows
1250-#ifdef __WIN32__
1251- case SDLK_F4:
1252-#endif
1253- case SDLK_q:
1254- item = iQuit;
1255- break;
1256- case SDLK_F9:
1257- dump_screen();
1258- break;
1259- case SDLK_RETURN:
1260-#if defined(__APPLE__) && defined(__MACH__)
1261- if ((event.key.keysym.mod & KMOD_META))
1262-#else
1263- if ((event.key.keysym.mod & KMOD_META) || (event.key.keysym.mod & KMOD_ALT))
1264-#endif
1265- {
1266- toggle_fullscreen();
1267- }
1268- break;
1269- default:
1270- break;
1271- }
1272- if (item > 0) {
1273- draw_menu_button_for_command(item);
1274- do_menu_item_command(mInterface, item, has_cheat_modifiers());
1275- }
1276- break;
1277- }
1278- }
1279-}
1280-
1281-static void process_event(const SDL_Event &event)
1282-{
1283- switch (event.type) {
1284- case SDL_MOUSEBUTTONDOWN:
1285- if (get_game_state() == _game_in_progress)
1286- {
1287- if (event.button.button == 4 || event.button.button == 5)
1288- {
1289- mouse_scroll(event.button.button == 4);
1290- }
1291- else if (!get_keyboard_controller_status())
1292- {
1293- hide_cursor();
1294- validate_world_window();
1295- set_keyboard_controller_status(true);
1296- }
1297- }
1298- else
1299- process_screen_click(event);
1300- break;
1301-
1302- case SDL_KEYDOWN:
1303- process_game_key(event);
1304- break;
1305-
1306- case SDL_QUIT:
1307- set_game_state(_quit_game);
1308- break;
1309-
1310- case SDL_ACTIVEEVENT:
1311- if (event.active.state & SDL_APPINPUTFOCUS) {
1312- if (!event.active.gain && !(SDL_GetAppState() & SDL_APPINPUTFOCUS)) {
1313- if (get_game_state() == _game_in_progress && get_keyboard_controller_status()) {
1314- darken_world_window();
1315- set_keyboard_controller_status(false);
1316- show_cursor();
1317- }
1318- }
1319- }
1320- break;
1321- case SDL_VIDEOEXPOSE:
1322-#if !defined(__APPLE__) && !defined(__MACH__) // double buffering :)
1323-#ifdef HAVE_OPENGL
1324- if (SDL_GetVideoSurface()->flags & SDL_OPENGL)
1325- SDL_GL_SwapBuffers();
1326- else
1327-#endif
1328- update_game_window();
1329-#endif
1330- break;
1331- }
1332-
1333-}
1334-
1335-#ifdef HAVE_PNG
1336-extern view_data *world_view;
1337-#endif
1338-
1339-std::string to_alnum(const std::string& input)
1340-{
1341- std::string output;
1342- for (std::string::const_iterator it = input.begin(); it != input.end(); ++it)
1343- {
1344- if (isalnum(*it))
1345- {
1346- output += *it;
1347- }
1348- }
1349-
1350- return output;
1351-}
1352-
1353-void dump_screen(void)
1354-{
1355- // Find suitable file name
1356- FileSpecifier file;
1357- int i = 0;
1358- do {
1359- char name[256];
1360- const char* suffix;
1361-#ifdef HAVE_PNG
1362- suffix = "png";
1363-#else
1364- suffix = "bmp";
1365-#endif
1366- if (get_game_state() == _game_in_progress)
1367- {
1368- sprintf(name, "%s_%04d.%s", to_alnum(static_world->level_name).c_str(), i, suffix);
1369- }
1370- else
1371- {
1372- sprintf(name, "Screenshot_%04d.%s", i, suffix);
1373- }
1374-
1375- file = screenshots_dir + name;
1376- i++;
1377- } while (file.Exists());
1378-
1379-#ifdef HAVE_PNG
1380- // build some nice metadata
1381- std::vector<IMG_PNG_text> texts;
1382- std::map<std::string, std::string> metadata;
1383-
1384- metadata["Source"] = string("Aleph One ") + A1_DISPLAY_VERSION + " (" + A1_DISPLAY_PLATFORM + ")";
1385-
1386- time_t rawtime;
1387- time(&rawtime);
1388-
1389- char time_string[32];
1390- strftime(time_string, 32,"%d %b %Y %H:%M:%S +0000", gmtime(&rawtime));
1391- metadata["Creation Time"] = time_string;
1392-
1393- if (get_game_state() == _game_in_progress)
1394- {
1395- const float FLOAT_WORLD_ONE = float(WORLD_ONE);
1396- const float AngleConvert = 360/float(FULL_CIRCLE);
1397-
1398- metadata["Level"] = static_world->level_name;
1399-
1400- char map_file_name[256];
1401- FileSpecifier fs = environment_preferences->map_file;
1402- fs.GetName(map_file_name);
1403- metadata["Map File"] = map_file_name;
1404-
1405- if (Scenario::instance()->GetName().size())
1406- {
1407- metadata["Scenario"] = Scenario::instance()->GetName();
1408- }
1409-
1410- metadata["Polygon"] = boost::lexical_cast<std::string>(world_view->origin_polygon_index);
1411- metadata["X"] = boost::lexical_cast<std::string>(world_view->origin.x / FLOAT_WORLD_ONE);
1412- metadata["Y"] = boost::lexical_cast<std::string>(world_view->origin.y / FLOAT_WORLD_ONE);
1413- metadata["Z"] = boost::lexical_cast<std::string>(world_view->origin.z / FLOAT_WORLD_ONE);
1414- metadata["Yaw"] = boost::lexical_cast<std::string>(world_view->yaw * AngleConvert);
1415-
1416-
1417- short pitch = world_view->pitch;
1418- if (pitch > HALF_CIRCLE) pitch -= HALF_CIRCLE;
1419- metadata["Pitch"] = boost::lexical_cast<std::string>(pitch * AngleConvert);
1420- }
1421-
1422- for (std::map<std::string, std::string>::const_iterator it = metadata.begin(); it != metadata.end(); ++it)
1423- {
1424- IMG_PNG_text text;
1425- text.key = const_cast<char*>(it->first.c_str());
1426- text.value = const_cast<char*>(it->second.c_str());
1427- texts.push_back(text);
1428- }
1429-
1430- IMG_PNG_text* textp = texts.size() ? &texts[0] : 0;
1431-#endif
1432-
1433- // Without OpenGL, dumping the screen is easy
1434- SDL_Surface *video = SDL_GetVideoSurface();
1435- if (!(video->flags & SDL_OPENGL)) {
1436-#ifdef HAVE_PNG
1437- IMG_SavePNG(file.GetPath(), SDL_GetVideoSurface(), IMG_COMPRESS_DEFAULT, textp, texts.size());
1438-#else
1439- SDL_SaveBMP(SDL_GetVideoSurface(), file.GetPath());
1440-#endif
1441- return;
1442- }
1443-
1444-#ifdef HAVE_OPENGL
1445- // Otherwise, allocate temporary surface...
1446- SDL_Surface *t = SDL_CreateRGBSurface(SDL_SWSURFACE, video->w, video->h, 24,
1447-#if SDL_BYTEORDER == SDL_LIL_ENDIAN
1448- 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1449-#else
1450- 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
1451-#endif
1452- if (t == NULL)
1453- return;
1454-
1455- // ...and pixel buffer
1456- void *pixels = malloc(video->w * video->h * 3);
1457- if (pixels == NULL) {
1458- SDL_FreeSurface(t);
1459- return;
1460- }
1461-
1462- // Read OpenGL frame buffer
1463- glReadPixels(0, 0, video->w, video->h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
1464-
1465- // Copy pixel buffer (which is upside-down) to surface
1466- for (int y = 0; y < video->h; y++)
1467- memcpy((uint8 *)t->pixels + t->pitch * y, (uint8 *)pixels + video->w * 3 * (video->h - y - 1), video->w * 3);
1468- free(pixels);
1469-
1470- // Save surface
1471-#ifdef HAVE_PNG
1472- IMG_SavePNG(file.GetPath(), t, IMG_COMPRESS_DEFAULT, textp, texts.size());
1473-#else
1474- SDL_SaveBMP(t, file.GetPath());
1475-#endif
1476- SDL_FreeSurface(t);
1477-#endif
1478-}
1479-
1480-void LoadBaseMMLScripts()
1481-{
1482- XML_Loader_SDL loader;
1483- loader.CurrentElement = &RootParser;
1484- {
1485- vector <DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
1486- while (i != end) {
1487- DirectorySpecifier path = *i + "MML";
1488- loader.ParseDirectory(path);
1489- path = *i + "Scripts";
1490- loader.ParseDirectory(path);
1491- i++;
1492- }
1493- }
1494-}
1495-
1496-// LP: the rest of the code has been moved to Jeremy's shell_misc.file.
1497-
1498-void PlayInterfaceButtonSound(short SoundID)
1499-{
1500- if (TEST_FLAG(input_preferences->modifiers,_inputmod_use_button_sounds))
1501- SoundManager::instance()->PlaySound(SoundID, (world_location3d *) NULL, NONE);
1502-}
760+ placer->dual_add(new w_static_text("そして、一人残らずボブ市民を皆殺しにしま〜す。』"), d);
761+ }
762+
763+ placer->add(new w_spacer(), true);
764+ placer->dual_add(new w_static_text("開始レベル:"), d);
765+
766+ w_levels *level_w = new w_levels(levels, &d);
767+ placer->dual_add(level_w, d);
768+ placer->add(new w_spacer(), true);
769+ placer->dual_add(new w_button("キャンセル", dialog_cancel, &d), d);
770+
771+ d.activate_widget(level_w);
772+ d.set_widget_placer(placer);
773+
774+ // Run dialog
775+ short level;
776+ if (d.run() == 0) // OK
777+ // Should do noncontiguous map files OK
778+ level = levels[level_w->get_selection()].level_number;
779+ else
780+ level = NONE;
781+
782+ // Redraw main menu
783+ update_game_window();
784+ return level;
785+}
786+
787+const uint32 TICKS_BETWEEN_EVENT_POLL = 167; // 6 Hz
788+static void main_event_loop(void)
789+{
790+ uint32 last_event_poll = 0;
791+ short game_state;
792+
793+ while ((game_state = get_game_state()) != _quit_game) {
794+ uint32 cur_time = SDL_GetTicks();
795+ bool yield_time = false;
796+ bool poll_event = false;
797+
798+ switch (game_state) {
799+ case _game_in_progress:
800+ case _change_level:
801+ if (Console::instance()->input_active() || cur_time - last_event_poll >= TICKS_BETWEEN_EVENT_POLL) {
802+ poll_event = true;
803+ last_event_poll = cur_time;
804+ } else {
805+ SDL_PumpEvents (); // This ensures a responsive keyboard control
806+ }
807+ break;
808+
809+ case _display_intro_screens:
810+ case _display_main_menu:
811+ case _display_chapter_heading:
812+ case _display_prologue:
813+ case _display_epilogue:
814+ case _begin_display_of_epilogue:
815+ case _display_credits:
816+ case _display_intro_screens_for_demo:
817+ case _display_quit_screens:
818+ case _displaying_network_game_dialogs:
819+ yield_time = interface_fade_finished();
820+ poll_event = true;
821+ break;
822+
823+ case _close_game:
824+ case _switch_demo:
825+ case _revert_game:
826+ yield_time = poll_event = true;
827+ break;
828+ }
829+
830+ if (poll_event) {
831+ global_idle_proc();
832+
833+ while (true) {
834+ SDL_Event event;
835+ event.type = SDL_NOEVENT;
836+ SDL_PollEvent(&event);
837+
838+ if (yield_time) {
839+ // The game is not in a "hot" state, yield time to other
840+ // processes by calling SDL_Delay() but only try for a maximum
841+ // of 30ms
842+ int num_tries = 0;
843+ while (event.type == SDL_NOEVENT && num_tries < 3) {
844+ SDL_Delay(10);
845+ SDL_PollEvent(&event);
846+ num_tries++;
847+ }
848+ yield_time = false;
849+ } else if (event.type == SDL_NOEVENT)
850+ break;
851+
852+ process_event(event);
853+ }
854+ }
855+
856+ execute_timer_tasks(SDL_GetTicks());
857+ idle_game_state(SDL_GetTicks());
858+
859+#ifndef __MACOS__
860+ if (game_state == _game_in_progress && !graphics_preferences->hog_the_cpu && (TICKS_PER_SECOND - (SDL_GetTicks() - cur_time)) > 10)
861+ {
862+ SDL_Delay(1);
863+ }
864+#endif
865+ }
866+}
867+
868+static bool has_cheat_modifiers(void)
869+{
870+ SDLMod m = SDL_GetModState();
871+#if (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOS__)
872+ return ((m & KMOD_SHIFT) && (m & KMOD_CTRL)) || ((m & KMOD_ALT) && (m & KMOD_META));
873+#else
874+ return (m & KMOD_SHIFT) && (m & KMOD_CTRL) && !(m & KMOD_ALT) && !(m & KMOD_META);
875+#endif
876+}
877+
878+static void process_screen_click(const SDL_Event &event)
879+{
880+ int x = event.button.x, y = event.button.y;
881+#ifdef HAVE_OPENGL
882+ if (OGL_IsActive())
883+ OGL_Blitter::WindowToScreen(x, y);
884+#endif
885+ portable_process_screen_click(x, y, has_cheat_modifiers());
886+}
887+
888+static void handle_game_key(const SDL_Event &event)
889+{
890+ SDLKey key = event.key.keysym.sym;
891+ bool changed_screen_mode = false;
892+ bool changed_prefs = false;
893+
894+ if (!game_is_networked && (event.key.keysym.mod & KMOD_CTRL) && CheatsActive) {
895+ int type_of_cheat = process_keyword_key(key);
896+ if (type_of_cheat != NONE)
897+ handle_keyword(type_of_cheat);
898+ }
899+ if (Console::instance()->input_active()) {
900+ switch(key) {
901+ case SDLK_RETURN:
902+ Console::instance()->enter();
903+ break;
904+ case SDLK_ESCAPE:
905+ Console::instance()->abort();
906+ break;
907+ case SDLK_BACKSPACE:
908+ case SDLK_DELETE:
909+ Console::instance()->backspace();
910+ break;
911+ default:
912+ if (event.key.keysym.unicode == 8) // Crtl-H
913+ {
914+ Console::instance()->backspace();
915+ }
916+ else if (event.key.keysym.unicode == 21) // Crtl-U
917+ {
918+ Console::instance()->clear();
919+ }
920+ else if (event.key.keysym.unicode >= ' ') {
921+ Console::instance()->key(unicode_to_mac_roman(event.key.keysym.unicode));
922+ }
923+ }
924+ }
925+ else
926+ {
927+ if (key == SDLK_ESCAPE) // (ZZZ) Quit gesture (now safer)
928+ {
929+ if(!player_controlling_game())
930+ do_menu_item_command(mGame, iQuitGame, false);
931+ else {
932+ if(get_ticks_since_local_player_in_terminal() > 1 * TICKS_PER_SECOND) {
933+ if(!game_is_networked) {
934+ do_menu_item_command(mGame, iQuitGame, false);
935+ }
936+ else {
937+#if defined(__APPLE__) && defined(__MACH__)
938+ screen_printf("終了したい場合は、コマンドキーを押しながらQを押してください。");
939+#else
940+ screen_printf("終了したい場合は、Altキーを押しながらQを押してください。");
941+#endif
942+ }
943+ }
944+ }
945+ }
946+ else if (key == input_preferences->shell_keycodes[_key_volume_up])
947+ {
948+ changed_prefs = SoundManager::instance()->AdjustVolumeUp(_snd_adjust_volume);
949+ }
950+ else if (key == input_preferences->shell_keycodes[_key_volume_down])
951+ {
952+ changed_prefs = SoundManager::instance()->AdjustVolumeDown(_snd_adjust_volume);
953+ }
954+ else if (key == input_preferences->shell_keycodes[_key_switch_view])
955+ {
956+ walk_player_list();
957+ render_screen(NONE);
958+ }
959+ else if (key == input_preferences->shell_keycodes[_key_zoom_in])
960+ {
961+ if (zoom_overhead_map_in())
962+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
963+ else
964+ PlayInterfaceButtonSound(Sound_ButtonFailure());
965+ }
966+ else if (key == input_preferences->shell_keycodes[_key_zoom_out])
967+ {
968+ if (zoom_overhead_map_out())
969+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
970+ else
971+ PlayInterfaceButtonSound(Sound_ButtonFailure());
972+ }
973+ else if (key == input_preferences->shell_keycodes[_key_inventory_left])
974+ {
975+ if (player_controlling_game()) {
976+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
977+ scroll_inventory(-1);
978+ } else
979+ decrement_replay_speed();
980+ }
981+ else if (key == input_preferences->shell_keycodes[_key_inventory_right])
982+ {
983+ if (player_controlling_game()) {
984+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
985+ scroll_inventory(1);
986+ } else
987+ increment_replay_speed();
988+ }
989+ else if (key == input_preferences->shell_keycodes[_key_toggle_fps])
990+ {
991+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
992+ extern bool displaying_fps;
993+ displaying_fps = !displaying_fps;
994+ }
995+ else if (key == input_preferences->shell_keycodes[_key_activate_console])
996+ {
997+ if (game_is_networked) {
998+#if !defined(DISABLE_NETWORKING)
999+ Console::instance()->activate_input(InGameChatCallbacks::SendChatMessage, InGameChatCallbacks::prompt());
1000+#endif
1001+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1002+ }
1003+ else if (Console::instance()->use_lua_console())
1004+ {
1005+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1006+ Console::instance()->activate_input(ExecuteLuaString, ">");
1007+ }
1008+ else
1009+ {
1010+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1011+ }
1012+ }
1013+ else if (key == input_preferences->shell_keycodes[_key_show_scores])
1014+ {
1015+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1016+ {
1017+ extern bool ShowScores;
1018+ ShowScores = !ShowScores;
1019+ }
1020+ }
1021+ else if (key == SDLK_F1) // Decrease screen size
1022+ {
1023+ if (!graphics_preferences->screen_mode.hud)
1024+ {
1025+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1026+ graphics_preferences->screen_mode.hud = true;
1027+ changed_screen_mode = changed_prefs = true;
1028+ }
1029+ else
1030+ {
1031+ int mode = alephone::Screen::instance()->FindMode(graphics_preferences->screen_mode.width, graphics_preferences->screen_mode.height);
1032+ if (mode < alephone::Screen::instance()->GetModes().size() - 1)
1033+ {
1034+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1035+ graphics_preferences->screen_mode.width = alephone::Screen::instance()->ModeWidth(mode + 1);
1036+ graphics_preferences->screen_mode.height = alephone::Screen::instance()->ModeHeight(mode + 1);
1037+ graphics_preferences->screen_mode.hud = false;
1038+ changed_screen_mode = changed_prefs = true;
1039+ } else
1040+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1041+ }
1042+ }
1043+ else if (key == SDLK_F2) // Increase screen size
1044+ {
1045+ if (graphics_preferences->screen_mode.hud)
1046+ {
1047+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1048+ graphics_preferences->screen_mode.hud = false;
1049+ changed_screen_mode = changed_prefs = true;
1050+ }
1051+ else
1052+ {
1053+ int mode = alephone::Screen::instance()->FindMode(graphics_preferences->screen_mode.width, graphics_preferences->screen_mode.height);
1054+ if (mode > 0)
1055+ {
1056+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1057+ graphics_preferences->screen_mode.width = alephone::Screen::instance()->ModeWidth(mode - 1);
1058+ graphics_preferences->screen_mode.height = alephone::Screen::instance()->ModeHeight(mode - 1);
1059+ graphics_preferences->screen_mode.hud = true;
1060+ changed_screen_mode = changed_prefs = true;
1061+ } else
1062+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1063+ }
1064+ }
1065+ else if (key == SDLK_F3) // Resolution toggle
1066+ {
1067+ if (!OGL_IsActive()) {
1068+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1069+ graphics_preferences->screen_mode.high_resolution = !graphics_preferences->screen_mode.high_resolution;
1070+ changed_screen_mode = changed_prefs = true;
1071+ } else
1072+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1073+ }
1074+ else if (key == SDLK_F4) // Reset OpenGL textures
1075+ {
1076+#ifdef HAVE_OPENGL
1077+ if (OGL_IsActive()) {
1078+ // Play the button sound in advance to get the full effect of the sound
1079+ PlayInterfaceButtonSound(Sound_OGL_Reset());
1080+ OGL_ResetTextures();
1081+ } else
1082+#endif
1083+ PlayInterfaceButtonSound(Sound_ButtonInoperative());
1084+ }
1085+ else if (key == SDLK_F5) // Make the chase cam switch sides
1086+ {
1087+ if (ChaseCam_IsActive())
1088+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1089+ else
1090+ PlayInterfaceButtonSound(Sound_ButtonInoperative());
1091+ ChaseCam_SwitchSides();
1092+ }
1093+ else if (key == SDLK_F6) // Toggle the chase cam
1094+ {
1095+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1096+ ChaseCam_SetActive(!ChaseCam_IsActive());
1097+ }
1098+ else if (key == SDLK_F7) // Toggle tunnel vision
1099+ {
1100+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1101+ SetTunnelVision(!GetTunnelVision());
1102+ }
1103+ else if (key == SDLK_F8) // Toggle the crosshairs
1104+ {
1105+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1106+ Crosshairs_SetActive(!Crosshairs_IsActive());
1107+ }
1108+ else if (key == SDLK_F9) // Screen dump
1109+ {
1110+ dump_screen();
1111+ }
1112+ else if (key == SDLK_F10) // Toggle the position display
1113+ {
1114+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1115+ {
1116+ extern bool ShowPosition;
1117+ ShowPosition = !ShowPosition;
1118+ }
1119+ }
1120+ else if (key == SDLK_F11) // Decrease gamma level
1121+ {
1122+ if (graphics_preferences->screen_mode.gamma_level) {
1123+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1124+ graphics_preferences->screen_mode.gamma_level--;
1125+ change_gamma_level(graphics_preferences->screen_mode.gamma_level);
1126+ changed_prefs = true;
1127+ } else
1128+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1129+ }
1130+ else if (key == SDLK_F12) // Increase gamma level
1131+ {
1132+ if (graphics_preferences->screen_mode.gamma_level < NUMBER_OF_GAMMA_LEVELS - 1) {
1133+ PlayInterfaceButtonSound(Sound_ButtonSuccess());
1134+ graphics_preferences->screen_mode.gamma_level++;
1135+ change_gamma_level(graphics_preferences->screen_mode.gamma_level);
1136+ changed_prefs = true;
1137+ } else
1138+ PlayInterfaceButtonSound(Sound_ButtonFailure());
1139+ }
1140+ else
1141+ {
1142+ if (get_game_controller() == _demo)
1143+ set_game_state(_close_game);
1144+ }
1145+ }
1146+
1147+ if (changed_screen_mode) {
1148+ screen_mode_data temp_screen_mode = graphics_preferences->screen_mode;
1149+ temp_screen_mode.fullscreen = get_screen_mode()->fullscreen;
1150+ change_screen_mode(&temp_screen_mode, true);
1151+ render_screen(0);
1152+ }
1153+
1154+ if (changed_prefs)
1155+ write_preferences();
1156+}
1157+
1158+static void process_game_key(const SDL_Event &event)
1159+{
1160+ switch (get_game_state()) {
1161+ case _game_in_progress:
1162+#if defined(__APPLE__) && defined(__MACH__)
1163+ if ((event.key.keysym.mod & KMOD_META))
1164+#else
1165+ if ((event.key.keysym.mod & KMOD_ALT) || (event.key.keysym.mod & KMOD_META))
1166+#endif
1167+ {
1168+ int item = -1;
1169+ switch (event.key.keysym.sym) {
1170+ case SDLK_p:
1171+ item = iPause;
1172+ break;
1173+ case SDLK_s:
1174+ item = iSave;
1175+ break;
1176+ case SDLK_r:
1177+ item = iRevert;
1178+ break;
1179+// ZZZ: Alt+F4 is also a quit gesture in Windows
1180+#ifdef __WIN32__
1181+ case SDLK_F4:
1182+#endif
1183+ case SDLK_q:
1184+ item = iQuitGame;
1185+ break;
1186+ case SDLK_RETURN:
1187+ item = 0;
1188+ toggle_fullscreen();
1189+ break;
1190+ default:
1191+ break;
1192+ }
1193+ if (item > 0)
1194+ do_menu_item_command(mGame, item, has_cheat_modifiers());
1195+ else if (item != 0)
1196+ handle_game_key(event);
1197+ } else
1198+ handle_game_key(event);
1199+ break;
1200+ case _display_intro_screens:
1201+ case _display_chapter_heading:
1202+ case _display_prologue:
1203+ case _display_epilogue:
1204+ case _display_credits:
1205+ case _display_quit_screens:
1206+ if (interface_fade_finished())
1207+ force_game_state_change();
1208+ else
1209+ stop_interface_fade();
1210+ break;
1211+
1212+ case _display_intro_screens_for_demo:
1213+ stop_interface_fade();
1214+ display_main_menu();
1215+ break;
1216+
1217+ case _quit_game:
1218+ case _close_game:
1219+ case _revert_game:
1220+ case _switch_demo:
1221+ case _change_level:
1222+ case _begin_display_of_epilogue:
1223+ case _displaying_network_game_dialogs:
1224+ break;
1225+
1226+ case _display_main_menu:
1227+ {
1228+ if (!interface_fade_finished())
1229+ stop_interface_fade();
1230+ int item = -1;
1231+ switch (event.key.keysym.sym) {
1232+ case SDLK_n:
1233+ item = iNewGame;
1234+ break;
1235+ case SDLK_o:
1236+ item = iLoadGame;
1237+ break;
1238+ case SDLK_g:
1239+ item = iGatherGame;
1240+ break;
1241+ case SDLK_j:
1242+ item = iJoinGame;
1243+ break;
1244+ case SDLK_p:
1245+ item = iPreferences;
1246+ break;
1247+ case SDLK_r:
1248+ item = iReplaySavedFilm;
1249+ break;
1250+ case SDLK_c:
1251+ item = iCredits;
1252+ break;
1253+// ZZZ: Alt+F4 is also a quit gesture in Windows
1254+#ifdef __WIN32__
1255+ case SDLK_F4:
1256+#endif
1257+ case SDLK_q:
1258+ item = iQuit;
1259+ break;
1260+ case SDLK_F9:
1261+ dump_screen();
1262+ break;
1263+ case SDLK_RETURN:
1264+#if defined(__APPLE__) && defined(__MACH__)
1265+ if ((event.key.keysym.mod & KMOD_META))
1266+#else
1267+ if ((event.key.keysym.mod & KMOD_META) || (event.key.keysym.mod & KMOD_ALT))
1268+#endif
1269+ {
1270+ toggle_fullscreen();
1271+ }
1272+ break;
1273+ default:
1274+ break;
1275+ }
1276+ if (item > 0) {
1277+ draw_menu_button_for_command(item);
1278+ do_menu_item_command(mInterface, item, has_cheat_modifiers());
1279+ }
1280+ break;
1281+ }
1282+ }
1283+}
1284+
1285+static void process_event(const SDL_Event &event)
1286+{
1287+ switch (event.type) {
1288+ case SDL_MOUSEBUTTONDOWN:
1289+ if (get_game_state() == _game_in_progress)
1290+ {
1291+ if (event.button.button == 4 || event.button.button == 5)
1292+ {
1293+ mouse_scroll(event.button.button == 4);
1294+ }
1295+ else if (!get_keyboard_controller_status())
1296+ {
1297+ hide_cursor();
1298+ validate_world_window();
1299+ set_keyboard_controller_status(true);
1300+ }
1301+ }
1302+ else
1303+ process_screen_click(event);
1304+ break;
1305+
1306+ case SDL_KEYDOWN:
1307+ process_game_key(event);
1308+ break;
1309+
1310+ case SDL_QUIT:
1311+ set_game_state(_quit_game);
1312+ break;
1313+
1314+ case SDL_ACTIVEEVENT:
1315+ if (event.active.state & SDL_APPINPUTFOCUS) {
1316+ if (!event.active.gain && !(SDL_GetAppState() & SDL_APPINPUTFOCUS)) {
1317+ if (get_game_state() == _game_in_progress && get_keyboard_controller_status()) {
1318+ darken_world_window();
1319+ set_keyboard_controller_status(false);
1320+ show_cursor();
1321+ }
1322+ }
1323+ }
1324+ break;
1325+ case SDL_VIDEOEXPOSE:
1326+#if !defined(__APPLE__) && !defined(__MACH__) // double buffering :)
1327+#ifdef HAVE_OPENGL
1328+ if (SDL_GetVideoSurface()->flags & SDL_OPENGL)
1329+ SDL_GL_SwapBuffers();
1330+ else
1331+#endif
1332+ update_game_window();
1333+#endif
1334+ break;
1335+ }
1336+
1337+}
1338+
1339+#ifdef HAVE_PNG
1340+extern view_data *world_view;
1341+#endif
1342+
1343+std::string to_alnum(const std::string& input)
1344+{
1345+ std::string output;
1346+ for (std::string::const_iterator it = input.begin(); it != input.end(); ++it)
1347+ {
1348+ if (isalnum(*it))
1349+ {
1350+ output += *it;
1351+ }
1352+ }
1353+
1354+ return output;
1355+}
1356+
1357+void dump_screen(void)
1358+{
1359+ // Find suitable file name
1360+ FileSpecifier file;
1361+ int i = 0;
1362+ do {
1363+ char name[256];
1364+ const char* suffix;
1365+#ifdef HAVE_PNG
1366+ suffix = "png";
1367+#else
1368+ suffix = "bmp";
1369+#endif
1370+ if (get_game_state() == _game_in_progress)
1371+ {
1372+ sprintf(name, "%s_%04d.%s", to_alnum(static_world->level_name).c_str(), i, suffix);
1373+ }
1374+ else
1375+ {
1376+ sprintf(name, "Screenshot_%04d.%s", i, suffix);
1377+ }
1378+
1379+ file = screenshots_dir + name;
1380+ i++;
1381+ } while (file.Exists());
1382+
1383+#ifdef HAVE_PNG
1384+ // build some nice metadata
1385+ std::vector<IMG_PNG_text> texts;
1386+ std::map<std::string, std::string> metadata;
1387+
1388+ metadata["Source"] = string("Aleph One ") + A1_DISPLAY_VERSION + " (" + A1_DISPLAY_PLATFORM + ")";
1389+
1390+ time_t rawtime;
1391+ time(&rawtime);
1392+
1393+ char time_string[32];
1394+ strftime(time_string, 32,"%d %b %Y %H:%M:%S +0000", gmtime(&rawtime));
1395+ metadata["Creation Time"] = time_string;
1396+
1397+ if (get_game_state() == _game_in_progress)
1398+ {
1399+ const float FLOAT_WORLD_ONE = float(WORLD_ONE);
1400+ const float AngleConvert = 360/float(FULL_CIRCLE);
1401+
1402+ metadata["Level"] = static_world->level_name;
1403+
1404+ char map_file_name[256];
1405+ FileSpecifier fs = environment_preferences->map_file;
1406+ fs.GetName(map_file_name);
1407+ metadata["Map File"] = map_file_name;
1408+
1409+ if (Scenario::instance()->GetName().size())
1410+ {
1411+ metadata["Scenario"] = Scenario::instance()->GetName();
1412+ }
1413+
1414+ metadata["Polygon"] = boost::lexical_cast<std::string>(world_view->origin_polygon_index);
1415+ metadata["X"] = boost::lexical_cast<std::string>(world_view->origin.x / FLOAT_WORLD_ONE);
1416+ metadata["Y"] = boost::lexical_cast<std::string>(world_view->origin.y / FLOAT_WORLD_ONE);
1417+ metadata["Z"] = boost::lexical_cast<std::string>(world_view->origin.z / FLOAT_WORLD_ONE);
1418+ metadata["Yaw"] = boost::lexical_cast<std::string>(world_view->yaw * AngleConvert);
1419+
1420+
1421+ short pitch = world_view->pitch;
1422+ if (pitch > HALF_CIRCLE) pitch -= HALF_CIRCLE;
1423+ metadata["Pitch"] = boost::lexical_cast<std::string>(pitch * AngleConvert);
1424+ }
1425+
1426+ for (std::map<std::string, std::string>::const_iterator it = metadata.begin(); it != metadata.end(); ++it)
1427+ {
1428+ IMG_PNG_text text;
1429+ text.key = const_cast<char*>(it->first.c_str());
1430+ text.value = const_cast<char*>(it->second.c_str());
1431+ texts.push_back(text);
1432+ }
1433+
1434+ IMG_PNG_text* textp = texts.size() ? &texts[0] : 0;
1435+#endif
1436+
1437+ // Without OpenGL, dumping the screen is easy
1438+ SDL_Surface *video = SDL_GetVideoSurface();
1439+ if (!(video->flags & SDL_OPENGL)) {
1440+#ifdef HAVE_PNG
1441+ IMG_SavePNG(file.GetPath(), SDL_GetVideoSurface(), IMG_COMPRESS_DEFAULT, textp, texts.size());
1442+#else
1443+ SDL_SaveBMP(SDL_GetVideoSurface(), file.GetPath());
1444+#endif
1445+ return;
1446+ }
1447+
1448+#ifdef HAVE_OPENGL
1449+ // Otherwise, allocate temporary surface...
1450+ SDL_Surface *t = SDL_CreateRGBSurface(SDL_SWSURFACE, video->w, video->h, 24,
1451+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
1452+ 0x000000ff, 0x0000ff00, 0x00ff0000, 0);
1453+#else
1454+ 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
1455+#endif
1456+ if (t == NULL)
1457+ return;
1458+
1459+ // ...and pixel buffer
1460+ void *pixels = malloc(video->w * video->h * 3);
1461+ if (pixels == NULL) {
1462+ SDL_FreeSurface(t);
1463+ return;
1464+ }
1465+
1466+ // Read OpenGL frame buffer
1467+ glReadPixels(0, 0, video->w, video->h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
1468+
1469+ // Copy pixel buffer (which is upside-down) to surface
1470+ for (int y = 0; y < video->h; y++)
1471+ memcpy((uint8 *)t->pixels + t->pitch * y, (uint8 *)pixels + video->w * 3 * (video->h - y - 1), video->w * 3);
1472+ free(pixels);
1473+
1474+ // Save surface
1475+#ifdef HAVE_PNG
1476+ IMG_SavePNG(file.GetPath(), t, IMG_COMPRESS_DEFAULT, textp, texts.size());
1477+#else
1478+ SDL_SaveBMP(t, file.GetPath());
1479+#endif
1480+ SDL_FreeSurface(t);
1481+#endif
1482+}
1483+
1484+void LoadBaseMMLScripts()
1485+{
1486+ XML_Loader_SDL loader;
1487+ loader.CurrentElement = &RootParser;
1488+ {
1489+ vector <DirectorySpecifier>::const_iterator i = data_search_path.begin(), end = data_search_path.end();
1490+ while (i != end) {
1491+ DirectorySpecifier path = *i + "MML";
1492+ loader.ParseDirectory(path);
1493+ path = *i + "Scripts";
1494+ loader.ParseDirectory(path);
1495+ i++;
1496+ }
1497+ }
1498+}
1499+
1500+// LP: the rest of the code has been moved to Jeremy's shell_misc.file.
1501+
1502+void PlayInterfaceButtonSound(short SoundID)
1503+{
1504+ if (TEST_FLAG(input_preferences->modifiers,_inputmod_use_button_sounds))
1505+ SoundManager::instance()->PlaySound(SoundID, (world_location3d *) NULL, NONE);
1506+}
--- marathon/trunk/Source_Files/Network/network_dialogs.cpp (revision 495)
+++ marathon/trunk/Source_Files/Network/network_dialogs.cpp (revision 496)
@@ -1,3270 +1,3270 @@
1-/*
2-NETWORK_DIALOGS.C (network_dialogs.cpp)
3-
4- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5- and the "Aleph One" developers.
6-
7- This program is free software; you can redistribute it and/or modify
8- it under the terms of the GNU General Public License as published by
9- the Free Software Foundation; either version 2 of the License, or
10- (at your option) any later version.
11-
12- This program is distributed in the hope that it will be useful,
13- but WITHOUT ANY WARRANTY; without even the implied warranty of
14- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15- GNU General Public License for more details.
16-
17- This license is contained in the file "COPYING",
18- which is included with this source code; it is available online at
19- http://www.gnu.org/licenses/gpl.html
20-
21-Monday, June 20, 1994 12:36:39 PM
22-Thursday, June 30, 1994 6:27:43 PM (ajr)
23- Made some UPPs for the dialogs
24-Tuesday, July 19, 1994 7:16:54 PM (ajr)
25- fixed up dialogs. added dialog for net game stats
26-Tuesday, September 6, 1994 3:50:01 PM (ajr)
27- recently, the net game stats dialog has been rewritten to be a graph and some other
28- stuff has been cleaned up a bit.
29-
30-Jan 30, 2000 (Loren Petrich):
31- Added some typecasts
32-
33-Feb. 4, 2000 (Loren Petrich):
34- Changed halt() to assert(false) for better debugging
35-
36-Apr 30, 2000 (Loren Petrich):
37- Did change for getting default player name from outside
38-
39-Jul 1, 2000 (Loren Petrich):
40- Added Benad's netgame stuff
41-
42-Sept-Nov 2001 (Woody Zenfell):
43- This file was split into Mac-specific code (in network_dialogs_macintosh.cpp) and
44- shared code (this file, network_dialogs.cpp).
45-
46-Feb 27, 2002 (Br'fin (Jeremy Parsons)):
47- Moved shared SDL hint address info here from network_dialogs_sdl.cpp
48- Reworked #ifdef mac to #if !defined(HAVE_SDL_NET)
49-
50-Mar 1, 2002 (Woody Zenfell):
51- Reworked SDL dialog-box level-choosing code; interface is different now (can't use
52- get_selection_control_value(), gives and takes level numbers instead). SDL prefs now store
53- level number instead of menu index. Using #ifdef mac to decide which interface to use.
54-
55-Mar 8, 2002 (Woody Zenfell):
56- Network microphone UI is now handled for SDL version as well (since SDL has net-audio now)
57-
58-Feb 12, 2003 (Woody Zenfell):
59- Support for resuming netgames (optionally get game options from saved-game not prefs, optionally don't save options into prefs)
60-
61-Apr 10, 2003 (Woody Zenfell):
62- Join hinting and autogathering have Preferences entries now
63-
64- August 27, 2003 (Woody Zenfell):
65- Reworked netscript selection stuff to use Preferences and to be more cross-platform
66- and more consistent with other dialog code
67-*/
68-
69-#if !defined(DISABLE_NETWORKING)
70-
71-#include "cseries.h"
72-#include "map.h"
73-#include "shell.h"
74-#include "preferences.h"
75-#include "network.h"
76-#include "network_dialogs.h"
77-#include "network_games.h"
78-#include "player.h" // ZZZ: for MAXIMUM_NUMBER_OF_PLAYERS, for reassign_player_colors
79-#include "metaserver_dialogs.h" // GameAvailableMetaserverAnnouncer
80-#include "wad.h" // jkvw: for read_wad_file_checksum
81-#include "game_wad.h" // get_map_file
82-
83-#include <map>
84-
85-// For LAN netgame location services
86-#include <sstream>
87-#include "network_private.h" // actually just need "network_dialogs_private.h"
88-#include "SSLP_API.h"
89-
90-#ifdef USES_NIBS
91- #include "NibsUiHelpers.h"
92-#endif
93-
94-// for game types...
95-#include "network_dialogs.h"
96-#include "TextStrings.h"
97-
98-#include "network_dialog_widgets_sdl.h"
99-#include "screen.h"
100-#include "SoundManager.h"
101-#include "progress.h"
102-
103-
104-extern void NetRetargetJoinAttempts(const IPaddress* inAddress);
105-
106-
107-// Metaserver Globals
108-// We can't construct a MetaserverClient until MetaserverClient::s_instances is initialised.
109-MetaserverClient* gMetaserverClient = NULL;
110-
111-// Chat History Globals
112-ChatHistory gMetaserverChatHistory;
113-ChatHistory gPregameChatHistory;
114-
115-
116-
117-
118-////////////////////////////////////////////////////////////////////////////////
119-// LAN game-location services support
120-
121-static const string
122-get_sslp_service_type()
123-{
124- return kNetworkSetupProtocolID;
125-}
126-
127-
128-GathererAvailableAnnouncer::GathererAvailableAnnouncer()
129-{
130- strncpy(mServiceInstance.sslps_type, get_sslp_service_type().c_str(), SSLP_MAX_TYPE_LENGTH);
131- strncpy(mServiceInstance.sslps_name, "Boomer", SSLP_MAX_NAME_LENGTH);
132- memset(&(mServiceInstance.sslps_address), '\0', sizeof(mServiceInstance.sslps_address));
133- SSLP_Allow_Service_Discovery(&mServiceInstance);
134-}
135-
136-GathererAvailableAnnouncer::~GathererAvailableAnnouncer()
137-{
138- SSLP_Disallow_Service_Discovery(&mServiceInstance);
139-}
140-
141-void // static
142-GathererAvailableAnnouncer::pump()
143-{
144- SSLP_Pump();
145-}
146-
147-
148-JoinerSeekingGathererAnnouncer::JoinerSeekingGathererAnnouncer(bool shouldSeek) : mShouldSeek(shouldSeek)
149-{
150- if(mShouldSeek)
151- SSLP_Locate_Service_Instances(
152- get_sslp_service_type().c_str(),
153- found_gatherer_callback,
154- lost_gatherer_callback,
155- found_gatherer_callback
156- );
157-}
158-
159-JoinerSeekingGathererAnnouncer::~JoinerSeekingGathererAnnouncer()
160-{
161- if(mShouldSeek)
162- SSLP_Stop_Locating_Service_Instances(get_sslp_service_type().c_str());
163-}
164-
165-void // static
166-JoinerSeekingGathererAnnouncer::pump()
167-{
168- SSLP_Pump();
169-}
170-
171-void // static
172-JoinerSeekingGathererAnnouncer::found_gatherer_callback(const SSLP_ServiceInstance* instance)
173-{
174- NetRetargetJoinAttempts(&instance->sslps_address);
175-}
176-
177-void // static
178-JoinerSeekingGathererAnnouncer::lost_gatherer_callback(const SSLP_ServiceInstance* instance)
179-{
180- NetRetargetJoinAttempts(NULL);
181-}
182-
183-
184-/****************************************************
185- *
186- * Shared Network Gather Dialog Code
187- *
188- ****************************************************/
189-
190-bool network_gather(bool inResumingGame)
191-{
192- bool successful= false;
193- game_info myGameInfo;
194- player_info myPlayerInfo;
195- bool advertiseOnMetaserver = false;
196-
197- show_cursor(); // JTP: Hidden one way or another
198- if (network_game_setup(&myPlayerInfo, &myGameInfo, inResumingGame, advertiseOnMetaserver))
199- {
200- myPlayerInfo.desired_color= myPlayerInfo.color;
201- memcpy(myPlayerInfo.long_serial_number, serial_preferences->long_serial_number, LONG_SERIAL_NUMBER_LENGTH);
202-
203- auto_ptr<GameAvailableMetaserverAnnouncer> metaserverAnnouncer;
204- if(NetEnter())
205- {
206- bool gather_dialog_result;
207-
208- if(NetGather(&myGameInfo, sizeof(game_info), (void*) &myPlayerInfo,
209- sizeof(myPlayerInfo), inResumingGame))
210- {
211- GathererAvailableAnnouncer announcer;
212-
213- if (!gMetaserverClient) gMetaserverClient = new MetaserverClient ();
214-
215- if(advertiseOnMetaserver)
216- {
217- try
218- {
219- metaserverAnnouncer.reset(new GameAvailableMetaserverAnnouncer(myGameInfo));
220- }
221- catch (const MetaserverClient::LoginDeniedException& e)
222- {
223- char message[1024];
224- if (e.code() == MetaserverClient::LoginDeniedException::BadUserOrPassword)
225- {
226- strcpy(message, "Login denied: bad username or password. Your game could not be advertised on the Internet.");
227- }
228- else if (e.code() == MetaserverClient::LoginDeniedException::UserAlreadyLoggedIn)
229- {
230- strcpy(message, "Login denied: that user is already logged in. Your game could not be advertised on the Internet.");
231- }
232- else if (e.code() == MetaserverClient::LoginDeniedException::AccountAlreadyLoggedIn)
233- {
234- strcpy(message, "Login denied: that account is already logged in. Your game could not be advertised on the Internet.");
235- }
236- else if (e.code() == MetaserverClient::LoginDeniedException::RoomFull)
237- {
238- strcpy(message, "Login denied: room full! Your game could not be advertised on the Internet.");
239- }
240- else if (e.code() == MetaserverClient::LoginDeniedException::AccountLocked)
241- {
242- strcpy(message, "Login denied: your account is locked. Your game could not be advertised on the Internet.");
243- }
244- else
245- {
246- sprintf(message, "There was a problem connecting to the server that tracks Internet games (%s). Please try again later.", e.what());
247- }
248-
249- alert_user(message, 0);
250- }
251- catch (const MetaserverClient::ServerConnectException&)
252- {
253- alert_user(infoError, strNETWORK_ERRORS, netWarnCouldNotAdvertiseOnMetaserver, 0);
254- }
255- }
256-
257- gather_dialog_result = GatherDialog::Create()->GatherNetworkGameByRunning();
258-
259- } else {
260- gather_dialog_result = false;
261- }
262-
263- if (gather_dialog_result) {
264- NetDoneGathering();
265- if (advertiseOnMetaserver)
266- {
267- metaserverAnnouncer->Start(myGameInfo.time_limit);
268- gMetaserverClient->setMode(1);
269- gMetaserverClient->pump();
270- }
271- successful= true;
272- }
273- else
274- {
275- delete gMetaserverClient;
276- gMetaserverClient = new MetaserverClient();
277- NetCancelGather();
278- NetExit();
279- }
280- } else {
281- /* error correction handled in the network code now.. */
282- }
283- }
284-
285- hide_cursor();
286- return successful;
287-}
288-
289-GatherDialog::GatherDialog() { }
290-
291-GatherDialog::~GatherDialog()
292-{
293- delete m_cancelWidget;
294- delete m_startWidget;
295- delete m_autogatherWidget;
296- delete m_ungatheredWidget;
297- delete m_pigWidget;
298- delete m_chatEntryWidget;
299- delete m_chatWidget;
300- delete m_chatChoiceWidget;
301-
302- gMetaserverClient->associateNotificationAdapter(0);
303-
304-}
305-
306-bool GatherDialog::GatherNetworkGameByRunning ()
307-{
308- vector<string> chat_choice_labels;
309- chat_choice_labels.push_back ("with joiners");
310- chat_choice_labels.push_back ("with Internet players");
311- m_chatChoiceWidget->set_labels (chat_choice_labels);
312-
313- m_cancelWidget->set_callback(boost::bind(&GatherDialog::Stop, this, false));
314- m_startWidget->set_callback(boost::bind(&GatherDialog::StartGameHit, this));
315- m_ungatheredWidget->SetItemSelectedCallback(boost::bind(&GatherDialog::gathered_player, this, _1));
316-
317- m_startWidget->deactivate ();
318-
319- NetSetGatherCallbacks(this);
320-
321- m_chatChoiceWidget->set_callback(boost::bind(&GatherDialog::chatChoiceHit, this));
322- m_chatEntryWidget->set_callback(boost::bind(&GatherDialog::chatTextEntered, this, _1));
323-
324- gPregameChatHistory.clear ();
325- NetSetChatCallbacks(this);
326-
327- BoolPref autoGatherPref (network_preferences->autogather);
328- Binder<bool> binder (m_autogatherWidget, &autoGatherPref);
329- binder.migrate_second_to_first ();
330-
331- if (gMetaserverClient->isConnected ()) {
332- gMetaserverClient->associateNotificationAdapter(this);
333- m_chatChoiceWidget->set_value (kMetaserverChat);
334- gMetaserverChatHistory.clear ();
335- m_chatWidget->attachHistory (&gMetaserverChatHistory);
336- } else {
337- m_chatChoiceWidget->deactivate ();
338- m_chatChoiceWidget->set_value (kPregameChat);
339- gMetaserverChatHistory.clear ();
340- m_chatWidget->attachHistory (&gPregameChatHistory);
341- }
342-
343- bool result = Run ();
344-
345- binder.migrate_first_to_second ();
346-
347- // Save autogather setting, even if we cancel the dialog
348- write_preferences ();
349-
350- return result;
351-}
352-
353-void GatherDialog::idle ()
354-{
355- MetaserverClient::pumpAll();
356-
357- prospective_joiner_info info;
358- if (player_search(info)) {
359- m_ungathered_players[info.stream_id] = info;
360- update_ungathered_widget ();
361- }
362-
363- if (m_autogatherWidget->get_value ()) {
364- map<int, prospective_joiner_info>::iterator it;
365- it = m_ungathered_players.begin ();
366- while (it != m_ungathered_players.end () && NetGetNumberOfPlayers() < MAXIMUM_NUMBER_OF_PLAYERS) {
367- gathered_player ((it++)->second);
368- }
369- }
370-}
371-
372-void GatherDialog::update_ungathered_widget ()
373-{
374- vector<prospective_joiner_info> temp;
375-
376- for (map<int, prospective_joiner_info>::iterator it = m_ungathered_players.begin (); it != m_ungathered_players.end (); ++it)
377- temp.push_back ((*it).second);
378-
379- m_ungatheredWidget->SetItems (temp);
380-}
381-
382-bool GatherDialog::player_search (prospective_joiner_info& player)
383-{
384- GathererAvailableAnnouncer::pump();
385-
386- if (NetCheckForNewJoiner(player)) {
387- m_ungathered_players[player.stream_id] = player;
388- update_ungathered_widget ();
389- return true;
390- } else
391- return false;
392-}
393-
394-bool GatherDialog::gathered_player (const prospective_joiner_info& player)
395-{
396- if (NetGetNumberOfPlayers() >= MAXIMUM_NUMBER_OF_PLAYERS) return false;
397- int theGatherPlayerResult = NetGatherPlayer(player, reassign_player_colors);
398-
399- if (theGatherPlayerResult != kGatherPlayerFailed) {
400- m_ungathered_players.erase (m_ungathered_players.find (player.stream_id));
401- update_ungathered_widget ();
402- return true;
403- } else
404- return false;
405-}
406-
407-void GatherDialog::StartGameHit ()
408-{
409- for (map<int, prospective_joiner_info>::iterator it = m_ungathered_players.begin (); it != m_ungathered_players.end (); ++it)
410- NetHandleUngatheredPlayer ((*it).second);
411-
412- Stop (true);
413-}
414-
415-void GatherDialog::JoinSucceeded(const prospective_joiner_info* player)
416-{
417- if (NetGetNumberOfPlayers () > 1)
418- m_startWidget->activate ();
419-
420- m_pigWidget->redraw ();
421-
422- if (gMetaserverClient->isConnected())
423- {
424- gMetaserverClient->announcePlayersInGame(NetGetNumberOfPlayers());
425- }
426-}
427-
428-void GatherDialog::JoiningPlayerDropped(const prospective_joiner_info* player)
429-{
430- map<int, prospective_joiner_info>::iterator it = m_ungathered_players.find (player->stream_id);
431- if (it != m_ungathered_players.end ())
432- m_ungathered_players.erase (it);
433-
434- update_ungathered_widget ();
435-}
436-
437-void GatherDialog::JoinedPlayerDropped(const prospective_joiner_info* player)
438-{
439- if (NetGetNumberOfPlayers () < 2)
440- m_startWidget->deactivate ();
441-
442- m_pigWidget->redraw ();
443-
444- if (gMetaserverClient->isConnected())
445- {
446- gMetaserverClient->announcePlayersInGame(NetGetNumberOfPlayers());
447- }
448-}
449-
450-void GatherDialog::JoinedPlayerChanged(const prospective_joiner_info* player)
451-{
452- m_pigWidget->redraw ();
453-}
454-
455-void GatherDialog::sendChat ()
456-{
457- string message = m_chatEntryWidget->get_text();
458-
459-#ifndef SDL
460- // It's just a little semantic difference, really. :)
461- message = string(message.data (), message.length () - 1); // lose the last character, i.e. '\r'.
462-#endif
463-
464- if (m_chatChoiceWidget->get_value () == kMetaserverChat)
465- gMetaserverClient->sendChatMessage(message);
466- else
467- SendChatMessage(message);
468-
469- m_chatEntryWidget->set_text(string());
470-}
471-
472-void GatherDialog::chatTextEntered (char character)
473-{
474- if (character == '\r')
475- sendChat();
476-}
477-
478-void GatherDialog::chatChoiceHit ()
479-{
480- if (m_chatChoiceWidget->get_value () == kPregameChat)
481- m_chatWidget->attachHistory (&gPregameChatHistory);
482- else
483- m_chatWidget->attachHistory (&gMetaserverChatHistory);
484-}
485-
486-void GatherDialog::ReceivedMessageFromPlayer(
487- const char *player_name,
488- const char *message)
489-{
490- ColoredChatEntry e;
491- e.type = ColoredChatEntry::ChatMessage;
492- e.sender = player_name;
493- e.message = message;
494-
495- gPregameChatHistory.append(e);
496-}
497-
498-/****************************************************
499- *
500- * Shared Network Join Dialog Code
501- *
502- ****************************************************/
503-
504-int network_join(void)
505-{
506- int join_dialog_result;
507-
508- show_cursor(); // Hidden one way or another
509-
510- /* If we can enter the network... */
511- if(NetEnter())
512- {
513-
514- join_dialog_result = JoinDialog::Create()->JoinNetworkGameByRunning();
515-
516- if (join_dialog_result == kNetworkJoinedNewGame || join_dialog_result == kNetworkJoinedResumeGame)
517- {
518- write_preferences ();
519-
520- game_info* myGameInfo= (game_info *)NetGetGameData();
521- NetSetInitialParameters(myGameInfo->initial_updates_per_packet, myGameInfo->initial_update_latency);
522- if (gMetaserverClient && gMetaserverClient->isConnected())
523- {
524- gMetaserverClient->setMode(1);
525- gMetaserverClient->pump();
526- }
527- }
528- else
529- {
530- read_preferences ();
531-
532- if (join_dialog_result == kNetworkJoinFailedJoined)
533- NetCancelJoin();
534-
535- NetExit();
536- }
537- } else { // Failed NetEnter
538- join_dialog_result = kNetworkJoinFailedUnjoined;
539- }
540-
541- hide_cursor();
542- return join_dialog_result;
543-}
544-
545-JoinDialog::JoinDialog() : got_gathered(false), skipToMetaserver(false)
546- { if (!gMetaserverClient) gMetaserverClient = new MetaserverClient (); }
547-
548-JoinDialog::~JoinDialog ()
549-{
550- gMetaserverClient->associateNotificationAdapter(0);
551-
552- delete m_cancelWidget;
553- delete m_joinWidget;
554- delete m_joinMetaserverWidget;
555- delete m_joinAddressWidget;
556- delete m_joinByAddressWidget;
557- delete m_nameWidget;
558- delete m_colourWidget;
559- delete m_teamWidget;
560- delete m_messagesWidget;
561- delete m_pigWidget;
562- delete m_chatEntryWidget;
563- delete m_chatChoiceWidget;
564- delete m_chatWidget;
565-}
566-
567-const int JoinDialog::JoinNetworkGameByRunning ()
568-{
569- join_result = kNetworkJoinFailedUnjoined;
570-
571- vector<string> chat_choice_labels;
572- chat_choice_labels.push_back ("with joiners/gatherer");
573- chat_choice_labels.push_back ("with Internet players");
574- m_chatChoiceWidget->set_labels (chat_choice_labels);
575-
576- m_colourWidget->set_labels (kTeamColorsStringSetID);
577- m_teamWidget->set_labels (kTeamColorsStringSetID);
578-
579- m_cancelWidget->set_callback(boost::bind(&JoinDialog::Stop, this));
580- m_joinWidget->set_callback(boost::bind(&JoinDialog::attemptJoin, this));
581- m_joinMetaserverWidget->set_callback(boost::bind(&JoinDialog::getJoinAddressFromMetaserver, this));
582-
583- m_chatChoiceWidget->set_value (kPregameChat);
584- m_chatChoiceWidget->deactivate ();
585- m_chatEntryWidget->deactivate ();
586- m_chatChoiceWidget->set_callback(boost::bind(&JoinDialog::chatChoiceHit, this));
587- m_chatEntryWidget->set_callback(boost::bind(&JoinDialog::chatTextEntered, this, _1));
588-
589- getpstr(ptemporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string);
590- m_messagesWidget->set_text(pstring_to_string(ptemporary));
591-
592- CStringPref joinAddressPref (network_preferences->join_address, 255);
593- binders.insert<std::string> (m_joinAddressWidget, &joinAddressPref);
594- BoolPref joinByAddressPref (network_preferences->join_by_address);
595- binders.insert<bool> (m_joinByAddressWidget, &joinByAddressPref);
596-
597- PStringPref namePref (player_preferences->name, MAX_NET_PLAYER_NAME_LENGTH);
598- binders.insert<std::string> (m_nameWidget, &namePref);
599- Int16Pref colourPref (player_preferences->color);
600- binders.insert<int> (m_colourWidget, &colourPref);
601- Int16Pref teamPref (player_preferences->team);
602- binders.insert<int> (m_teamWidget, &teamPref);
603-
604- binders.migrate_all_second_to_first ();
605-
606- Run ();
607-
608- binders.migrate_all_first_to_second ();
609-
610- return join_result;
611-
612- // We'll choose to use old prefs or new changes when we return into network_join
613-}
614-
615-void JoinDialog::respondToJoinHit()
616-{
617- gPregameChatHistory.clear ();
618- if (gMetaserverClient->isConnected ()) {
619- m_chatChoiceWidget->activate ();
620- m_chatChoiceWidget->set_value (kMetaserverChat);
621- gMetaserverClient->associateNotificationAdapter(this);
622- m_chatWidget->attachHistory (&gMetaserverChatHistory);
623- } else {
624- m_chatWidget->attachHistory (&gPregameChatHistory);
625- }
626- m_chatEntryWidget->activate ();
627- NetSetChatCallbacks(this);
628-}
629-
630-void JoinDialog::attemptJoin ()
631-{
632- char* hintString = NULL;
633-
634- if(m_joinByAddressWidget->get_value()) {
635- hintString = new char[256];
636- copy_string_to_cstring (m_joinAddressWidget->get_text (), hintString);
637- }
638-
639- player_info myPlayerInfo;
640- copy_string_to_pstring (m_nameWidget->get_text (), myPlayerInfo.name, MAX_NET_PLAYER_NAME_LENGTH);
641- myPlayerInfo.team = m_teamWidget->get_value ();
642- myPlayerInfo.desired_color = m_colourWidget->get_value ();
643-
644- // jkvw: It may look like we're passing our player name into NetGameJoin,
645- // but network code will later draw the name directly from prefs.
646- binders.migrate_all_first_to_second ();
647- bool result = NetGameJoin((void *) &myPlayerInfo, sizeof(myPlayerInfo), hintString);
648-
649- if (hintString)
650- delete [] hintString;
651-
652- if (result) {
653- m_nameWidget->deactivate ();
654- m_teamWidget->deactivate ();
655- m_colourWidget->deactivate ();
656-
657- m_joinAddressWidget->deactivate ();
658- m_joinByAddressWidget->deactivate ();
659- m_joinWidget->deactivate ();
660- m_joinMetaserverWidget->deactivate ();
661-
662- getpstr(ptemporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string);
663- m_messagesWidget->set_text(pstring_to_string(ptemporary));
664-
665- if (!m_joinByAddressWidget->get_value()) {
666- join_announcer.reset(new JoinerSeekingGathererAnnouncer(true));
667- }
668-
669- respondToJoinHit ();
670- }
671-}
672-
673-void JoinDialog::gathererSearch ()
674-{
675- if (skipToMetaserver)
676- {
677- skipToMetaserver = false;
678- getJoinAddressFromMetaserver();
679- }
680-
681- JoinerSeekingGathererAnnouncer::pump();
682- MetaserverClient::pumpAll();
683-
684- switch (NetUpdateJoinState())
685- {
686- case NONE: // haven't Joined yet.
687- join_result = kNetworkJoinFailedUnjoined;
688- break;
689-
690- case netConnecting:
691- case netJoining:
692- join_result = kNetworkJoinFailedJoined;
693- break;
694-
695- case netCancelled: // the server cancelled the game; force bail
696- join_result = kNetworkJoinFailedJoined;
697- Stop ();
698- break;
699-
700- case netWaiting:
701- join_result = kNetworkJoinFailedJoined;
702- break;
703-
704- case netStartingUp: // the game is starting up (we have the network topography)
705- join_result = kNetworkJoinedNewGame;
706- Stop ();
707- break;
708-
709- case netStartingResumeGame: // the game is starting up a resume game (we have the network topography)
710- join_result = kNetworkJoinedResumeGame;
711- Stop ();
712- break;
713-
714- case netPlayerChanged:
715- case netPlayerAdded:
716- case netPlayerDropped:
717- if (!got_gathered) {
718- // Do this stuff only once - when we become gathered
719- got_gathered = true;
720- char joinMessage[256];
721- game_info *info= (game_info *)NetGetGameData();
722- get_network_joined_message(joinMessage, info->net_game_type);
723- m_messagesWidget->set_text(std::string(joinMessage));
724- m_teamWidget->activate ();
725- m_colourWidget->activate ();
726- m_colourWidget->set_callback(boost::bind(&JoinDialog::changeColours, this));
727- m_teamWidget->set_callback(boost::bind(&JoinDialog::changeColours, this));
728- }
729- m_pigWidget->redraw ();
730- join_result = kNetworkJoinFailedJoined;
731- break;
732-
733- case netJoinErrorOccurred:
734- join_result = kNetworkJoinFailedJoined;
735- Stop ();
736- break;
737-
738- case netChatMessageReceived:
739- // Show chat message
740- join_result = kNetworkJoinFailedJoined;
741- break;
742-
743- default:
744- assert(false);
745- }
746-}
747-
748-void JoinDialog::changeColours ()
749-{
750- int requested_colour = m_colourWidget->get_value ();
751- int requested_team = m_teamWidget->get_value ();
752- NetChangeColors(requested_colour, requested_team);
753-}
754-
755-void JoinDialog::getJoinAddressFromMetaserver ()
756-{
757- // jkvw: The network metaserver code will draw our name and colour info directly from prefs.
758- binders.migrate_all_first_to_second ();
759-
760- try
761- {
762- IPaddress result = run_network_metaserver_ui();
763- if(result.host != 0)
764- {
765- uint8* hostBytes = reinterpret_cast<uint8*>(&(result.host));
766- char buffer[16];
767- snprintf(buffer, sizeof(buffer), "%u.%u.%u.%u", hostBytes[0], hostBytes[1], hostBytes[2], hostBytes[3]);
768- m_joinByAddressWidget->set_value (true);
769- m_joinAddressWidget->set_text (string (buffer));
770- m_joinWidget->push ();
771- }
772- }
773- catch (const MetaserverClient::LoginDeniedException& e)
774- {
775- char message[1024];
776- if (e.code() == MetaserverClient::LoginDeniedException::BadUserOrPassword)
777- {
778- strcpy(message, "Login denied: bad username or password.");
779- }
780- else if (e.code() == MetaserverClient::LoginDeniedException::UserAlreadyLoggedIn)
781- {
782- strcpy(message, "Login denied: that user is already logged in.");
783- }
784- else if (e.code() == MetaserverClient::LoginDeniedException::AccountAlreadyLoggedIn)
785- {
786- strcpy(message, "Login denied: that account is already logged in.");
787- }
788- else if (e.code() == MetaserverClient::LoginDeniedException::RoomFull)
789- {
790- strcpy(message, "Login denied: room is full!?");
791- }
792- else if (e.code() == MetaserverClient::LoginDeniedException::AccountLocked)
793- {
794- strcpy(message, "Login denied: your account is locked.");
795- }
796- else
797- {
798- sprintf(message, "There was a problem connecting to the server that tracks Internet games (%s). Please try again later.", e.what());
799- }
800-
801- alert_user(message, 0);
802- }
803- catch (const MetaserverClient::ServerConnectException&)
804- {
805- alert_user(infoError, strNETWORK_ERRORS, netErrMetaserverConnectionFailure, 0);
806- }
807-}
808-
809-void JoinDialog::sendChat ()
810-{
811- string message = m_chatEntryWidget->get_text();
812-
813- // jkvw: Why don't we need NIBs to strip a trailing \r here, like in
814- // GatherDialog::sendChat and MetaserverClientUi::sendChat?
815- // Good question, burrito.
816-
817- if (m_chatChoiceWidget->get_value () == kMetaserverChat)
818- gMetaserverClient->sendChatMessage(message);
819- else
820- SendChatMessage(message);
821-
822- m_chatEntryWidget->set_text(string());
823-}
824-
825-void JoinDialog::chatTextEntered (char character)
826-{
827- if (character == '\r')
828- sendChat();
829-}
830-
831-void JoinDialog::chatChoiceHit ()
832-{
833- if (m_chatChoiceWidget->get_value () == kPregameChat)
834- m_chatWidget->attachHistory (&gPregameChatHistory);
835- else
836- m_chatWidget->attachHistory (&gMetaserverChatHistory);
837-}
838-
839-void JoinDialog::ReceivedMessageFromPlayer(const char *player_name, const char *message)
840-{
841- ColoredChatEntry e;
842- e.type = ColoredChatEntry::ChatMessage;
843- e.sender = player_name;
844- e.message = message;
845-
846- gPregameChatHistory.append(e);
847-}
848-
849-/****************************************************
850- *
851- * Shared Network Game Setup Code
852- *
853- ****************************************************/
854-
855-bool network_game_setup(
856- player_info *player_information,
857- game_info *game_information,
858- bool ResumingGame,
859- bool& outAdvertiseGameOnMetaserver)
860-{
861- if (SetupNetgameDialog::Create ()->SetupNetworkGameByRunning (player_information, game_information, ResumingGame, outAdvertiseGameOnMetaserver)) {
862- write_preferences ();
863- return true;
864- } else {
865- read_preferences ();
866- load_environment_from_preferences(); // In case user changed map
867- return false;
868- }
869-}
870-
871-// converts menu index <---> level index
872-class LevelInt16Pref : public Bindable<int>
873-{
874-public:
875- LevelInt16Pref (int16& pref, int& gametype) : m_pref (pref), m_gametype (gametype) {}
876-
877- virtual int bind_export ()
878- {
879- int32 entry_flags = get_entry_point_flags_for_game_type (m_gametype);
880- return level_index_to_menu_index (m_pref, entry_flags);
881- }
882-
883- virtual void bind_import (int value)
884- {
885- int32 entry_flags = get_entry_point_flags_for_game_type (m_gametype);
886- m_pref = menu_index_to_level_index (value, entry_flags);
887- }
888-
889-protected:
890- int16& m_pref;
891- int& m_gametype;
892-};
893-
894-class TimerInt32Pref : public Bindable<int>
895-{
896-public:
897- TimerInt32Pref (int32& pref) : m_pref (pref) {}
898-
899- virtual int bind_export ()
900- {
901- return m_pref / (60 * TICKS_PER_SECOND);
902- }
903-
904- virtual void bind_import (int value)
905- {
906- m_pref = static_cast<int32>(value) * (60 * TICKS_PER_SECOND);
907- }
908-
909-protected:
910- int32& m_pref;
911-};
912-
913-// limit type is represented by a bool and a bit in game options
914-class LimitTypePref : public Bindable<int>
915-{
916-public:
917- LimitTypePref (bool& untimed_pref, uint16& options_pref, uint16 kill_limit_mask)
918- : m_untimed (untimed_pref)
919- , m_kill_limited (options_pref, kill_limit_mask)
920- {}
921-
922- virtual int bind_export ()
923- {
924- if (!m_untimed.bind_export ())
925- return duration_time_limit;
926- else if (m_kill_limited.bind_export ())
927- return duration_kill_limit;
928- else
929- return duration_no_time_limit;
930- }
931-
932- virtual void bind_import (int value)
933- {
934- if (value == duration_no_time_limit) {
935- m_untimed.bind_import (true);
936- m_kill_limited.bind_import (false);
937- } else if (value == duration_time_limit) {
938- m_untimed.bind_import (false);
939- m_kill_limited.bind_import (false);
940- } else {
941- m_untimed.bind_import (true);
942- m_kill_limited.bind_import (true);
943- }
944- }
945-
946-protected:
947- BoolPref m_untimed;
948- BitPref m_kill_limited;
949-};
950-
951-class GametypePref : public Bindable<int>
952-{
953-public:
954- GametypePref (int16& pref) : m_pref (pref) {}
955-
956- virtual int bind_export ()
957- {
958- return ((m_pref < 5) ? m_pref : m_pref - 1);
959- }
960-
961- virtual void bind_import (int value)
962- {
963- m_pref = ((value < 5) ? value : value + 1);
964- }
965-
966-protected:
967- int16& m_pref;
968-};
969-
970-class LatencyTolerancePref : public Bindable<int>
971-{
972-public:
973- LatencyTolerancePref (int32& pref) : m_pref(pref) { }
974-
975- virtual int bind_export () {
976- return (m_pref == 0) ? 5 : (m_pref - 1);
977- }
978-
979- virtual void bind_import(int value) {
980- m_pref = (value == 5) ? 0 : (value + 1);
981- }
982-protected:
983- int32& m_pref;
984-};
985-
986-static const vector<string> make_entry_vector (int32 entry_flags)
987-{
988- vector<string> result;
989-
990- entry_point ep;
991- short index = 0;
992-
993- while (get_indexed_entry_point (&ep, &index, entry_flags))
994- result.push_back (string (ep.level_name));
995-
996- return result;
997-}
998-
999-SetupNetgameDialog::SetupNetgameDialog()
1000-{ }
1001-
1002-SetupNetgameDialog::~SetupNetgameDialog ()
1003-{
1004- delete m_cancelWidget;
1005- delete m_okWidget;
1006-
1007- delete m_nameWidget;
1008- delete m_colourWidget;
1009- delete m_teamWidget;
1010-
1011- delete m_mapWidget;
1012- delete m_levelWidget;
1013- delete m_gameTypeWidget;
1014- delete m_difficultyWidget;
1015-
1016- delete m_timeLimitWidget;
1017- delete m_scoreLimitWidget;
1018-
1019- delete m_aliensWidget;
1020- delete m_allowTeamsWidget;
1021- delete m_deadPlayersDropItemsWidget;
1022- delete m_penalizeDeathWidget;
1023- delete m_penalizeSuicideWidget;
1024-
1025- delete m_useMetaserverWidget;
1026-
1027- delete m_useScriptWidget;
1028- delete m_scriptWidget;
1029-
1030- delete m_allowMicWidget;
1031-
1032- delete m_liveCarnageWidget;
1033- delete m_motionSensorWidget;
1034-
1035- delete m_zoomWidget;
1036- delete m_crosshairWidget;
1037- delete m_overlayWidget;
1038- delete m_laraCroftWidget;
1039- delete m_carnageMessagesWidget;
1040-
1041- delete m_useUpnpWidget;
1042-}
1043-
1044-extern int32& hub_get_minimum_send_period();
1045-
1046-bool SetupNetgameDialog::SetupNetworkGameByRunning (
1047- player_info *player_information,
1048- game_info *game_information,
1049- bool resuming_game,
1050- bool& outAdvertiseGameOnMetaserver)
1051-{
1052- int32 entry_flags;
1053-
1054- m_allow_all_levels = allLevelsAllowed ();
1055-
1056- // We use a temporary structure so that we can change things without messing with the real preferences
1057- network_preferences_data theAdjustedPreferences = *network_preferences;
1058- if (resuming_game)
1059- {
1060- // Adjust the apparent preferences to get values from the loaded game (dynamic_world)
1061- // rather than from the actual network_preferences.
1062- theAdjustedPreferences.game_type = GET_GAME_TYPE();
1063- theAdjustedPreferences.difficulty_level = dynamic_world->game_information.difficulty_level;
1064- theAdjustedPreferences.entry_point = dynamic_world->current_level_number;
1065- theAdjustedPreferences.kill_limit = dynamic_world->game_information.kill_limit;
1066- theAdjustedPreferences.time_limit = dynamic_world->game_information.game_time_remaining;
1067- theAdjustedPreferences.game_options = GET_GAME_OPTIONS();
1068- // If the time limit is longer than a week, we figure it's untimed ( ;)
1069- theAdjustedPreferences.game_is_untimed = (dynamic_world->game_information.game_time_remaining > 7 * 24 * 3600 * TICKS_PER_SECOND);
1070- // If they are resuming a single-player game, assume they want cooperative play now.
1071- if (dynamic_world->player_count == 1 && GET_GAME_TYPE() == _game_of_kill_monsters)
1072- {
1073- theAdjustedPreferences.game_type = _game_of_cooperative_play;
1074- theAdjustedPreferences.game_options |= _live_network_stats; // single-player game doesn't, and they probably want it
1075- }
1076- m_allow_all_levels = true;
1077-
1078- // If resuming an untimed game, show the "time limit" from the prefs in the grayed-out widget
1079- // rather than some ridiculously large number
1080- if (theAdjustedPreferences.game_is_untimed)
1081- theAdjustedPreferences.time_limit = theAdjustedPreferences.time_limit/TICKS_PER_SECOND/60;
1082-
1083- // Disable certain elements when resuming a game
1084- m_gameTypeWidget->deactivate ();
1085- m_levelWidget->deactivate ();
1086- m_scoreLimitWidget->deactivate ();
1087- m_timeLimitWidget->deactivate ();
1088- m_limitTypeWidget->deactivate ();
1089- m_mapWidget->deactivate ();
1090- }
1091-
1092- // if we're resuming, use the temporary prefs structure, otherwise use the prefs as usual
1093- network_preferences_data* active_network_preferences = resuming_game ? &theAdjustedPreferences : network_preferences;
1094-
1095- m_old_game_type = active_network_preferences->game_type;
1096-
1097- /* Fill in the entry points */
1098- if(m_allow_all_levels)
1099- {
1100- entry_flags= NONE;
1101- } else {
1102- entry_flags= get_entry_point_flags_for_game_type(active_network_preferences->game_type);
1103- }
1104- m_levelWidget->set_labels (make_entry_vector (entry_flags));
1105- m_gameTypeWidget->set_labels (kNetworkGameTypesStringSetID);
1106- m_colourWidget->set_labels (kTeamColorsStringSetID);
1107- m_teamWidget->set_labels (kTeamColorsStringSetID);
1108- m_difficultyWidget->set_labels (kDifficultyLevelsStringSetID);
1109- m_limitTypeWidget->set_labels (kEndConditionTypeStringSetID);
1110-
1111- vector<string> toleranceLabels;
1112- toleranceLabels.push_back("33 ms");
1113- toleranceLabels.push_back("66 ms");
1114- toleranceLabels.push_back("100 ms");
1115- toleranceLabels.push_back("133 ms");
1116- toleranceLabels.push_back("166 ms");
1117- toleranceLabels.push_back("2 sec");
1118- m_latencyToleranceWidget->set_labels (toleranceLabels);
1119-
1120- BinderSet binders;
1121-
1122- PStringPref namePref (player_preferences->name, MAX_NET_PLAYER_NAME_LENGTH);
1123- binders.insert<std::string> (m_nameWidget, &namePref);
1124- Int16Pref colourPref (player_preferences->color);
1125- binders.insert<int> (m_colourWidget, &colourPref);
1126- Int16Pref teamPref (player_preferences->team);
1127- binders.insert<int> (m_teamWidget, &teamPref);
1128-
1129- FilePref mapPref (environment_preferences->map_file);
1130- binders.insert<FileSpecifier> (m_mapWidget, &mapPref);
1131-
1132- LevelInt16Pref levelPref (active_network_preferences->entry_point, m_old_game_type);
1133- binders.insert<int> (m_levelWidget, &levelPref);
1134- GametypePref gameTypePref (active_network_preferences->game_type);
1135- binders.insert<int> (m_gameTypeWidget, &gameTypePref);
1136- Int16Pref difficultyPref (active_network_preferences->difficulty_level);
1137- binders.insert<int> (m_difficultyWidget, &difficultyPref);
1138-
1139- LimitTypePref limitTypePref (active_network_preferences->game_is_untimed, active_network_preferences->game_options, _game_has_kill_limit);
1140- binders.insert<int> (m_limitTypeWidget, &limitTypePref);
1141- TimerInt32Pref timeLimitPref (active_network_preferences->time_limit);
1142- binders.insert<int> (m_timeLimitWidget, &timeLimitPref);
1143- Int16Pref scoreLimitPref (active_network_preferences->kill_limit);
1144- binders.insert<int> (m_scoreLimitWidget, &scoreLimitPref);
1145-
1146- BitPref aliensPref (active_network_preferences->game_options, _monsters_replenish);
1147- binders.insert<bool> (m_aliensWidget, &aliensPref);
1148- BitPref allowTeamsPref (active_network_preferences->game_options, _force_unique_teams, true);
1149- binders.insert<bool> (m_allowTeamsWidget, &allowTeamsPref);
1150- BitPref deadPlayersDropItemsPref (active_network_preferences->game_options, _burn_items_on_death, true);
1151- binders.insert<bool> (m_deadPlayersDropItemsWidget, &deadPlayersDropItemsPref);
1152- BitPref penalizeDeathPref (active_network_preferences->game_options, _dying_is_penalized);
1153- binders.insert<bool> (m_penalizeDeathWidget, &penalizeDeathPref);
1154- BitPref penalizeSuicidePref (active_network_preferences->game_options, _suicide_is_penalized);
1155- binders.insert<bool> (m_penalizeSuicideWidget, &penalizeSuicidePref);
1156-
1157- BoolPref useMetaserverPref (active_network_preferences->advertise_on_metaserver);
1158- binders.insert<bool> (m_useMetaserverWidget, &useMetaserverPref);
1159-
1160- BoolPref allowMicPref (active_network_preferences->allow_microphone);
1161- binders.insert<bool> (m_allowMicWidget, &allowMicPref);
1162-
1163- BitPref liveCarnagePref (active_network_preferences->game_options, _live_network_stats);
1164- binders.insert<bool> (m_liveCarnageWidget, &liveCarnagePref);
1165- BitPref motionSensorPref (active_network_preferences->game_options, _motion_sensor_does_not_work);
1166- binders.insert<bool> (m_motionSensorWidget, &motionSensorPref);
1167-
1168- BitPref zoomPref (active_network_preferences->cheat_flags, _allow_tunnel_vision);
1169- binders.insert<bool> (m_zoomWidget, &zoomPref);
1170- BitPref crosshairPref (active_network_preferences->cheat_flags, _allow_crosshair);
1171- binders.insert<bool> (m_crosshairWidget, &crosshairPref);
1172- BitPref overlayPref (active_network_preferences->cheat_flags, _allow_overlay_map);
1173- binders.insert<bool> (m_overlayWidget, &overlayPref);
1174- BitPref laraCroftPref (active_network_preferences->cheat_flags, _allow_behindview);
1175- binders.insert<bool> (m_laraCroftWidget, &laraCroftPref);
1176- BitPref carnageMessagesPref (active_network_preferences->cheat_flags, _disable_carnage_messages, true);
1177- binders.insert<bool> (m_carnageMessagesWidget, &carnageMessagesPref);
1178- BitPref savingLevelPref (active_network_preferences->cheat_flags, _disable_saving_level, true);
1179- binders.insert<bool> (m_savingLevelWidget, &savingLevelPref);
1180-
1181- BoolPref useScriptPref (active_network_preferences->use_netscript);
1182- binders.insert<bool> (m_useScriptWidget, &useScriptPref);
1183- FilePref scriptPref (active_network_preferences->netscript_file);
1184- binders.insert<FileSpecifier> (m_scriptWidget, &scriptPref);
1185-
1186- BoolPref useUpnpPref (active_network_preferences->attempt_upnp);
1187- binders.insert<bool> (m_useUpnpWidget, &useUpnpPref);
1188-
1189- LatencyTolerancePref latencyTolerancePref (hub_get_minimum_send_period());
1190- binders.insert<int> (m_latencyToleranceWidget, &latencyTolerancePref);
1191-
1192- binders.migrate_all_second_to_first ();
1193-
1194- m_cancelWidget->set_callback (boost::bind (&SetupNetgameDialog::Stop, this, false));
1195- m_okWidget->set_callback (boost::bind (&SetupNetgameDialog::okHit, this));
1196- m_limitTypeWidget->set_callback (boost::bind (&SetupNetgameDialog::limitTypeHit, this));
1197- m_allowTeamsWidget->set_callback (boost::bind (&SetupNetgameDialog::teamsHit, this));
1198- m_gameTypeWidget->set_callback (boost::bind (&SetupNetgameDialog::gameTypeHit, this));
1199- m_mapWidget->set_callback (boost::bind (&SetupNetgameDialog::chooseMapHit, this));
1200-
1201- setupForGameType ();
1202-
1203- if (m_limitTypeWidget->get_value () == duration_kill_limit)
1204- setupForScoreGame ();
1205- else if (m_limitTypeWidget->get_value () == duration_no_time_limit)
1206- setupForUntimedGame ();
1207- else
1208- setupForTimedGame ();
1209-
1210- /* Setup the team popup.. */
1211- if (!m_allowTeamsWidget->get_value ())
1212- m_teamWidget->deactivate ();
1213-
1214- if (Run ()) {
1215-
1216- // migrate widget settings to preferences structure
1217- binders.migrate_all_first_to_second ();
1218-
1219- pstrcpy (player_information->name, player_preferences->name);
1220- player_information->color = player_preferences->color;
1221- player_information->team = player_preferences->team;
1222-
1223- game_information->server_is_playing = true;
1224- game_information->net_game_type = active_network_preferences->game_type;
1225-
1226- game_information->game_options = active_network_preferences->game_options;
1227- game_information->game_options |= (_ammo_replenishes | _weapons_replenish | _specials_replenish);
1228- if (active_network_preferences->game_type == _game_of_cooperative_play)
1229- game_information->game_options |= _overhead_map_is_omniscient;
1230-
1231- // ZZZ: don't screw with the limits if resuming.
1232- if (resuming_game)
1233- {
1234- game_information->time_limit = dynamic_world->game_information.game_time_remaining;
1235- game_information->kill_limit = dynamic_world->game_information.kill_limit;
1236- } else {
1237- if (!active_network_preferences->game_is_untimed)
1238- game_information->time_limit = m_timeLimitWidget->get_value () * TICKS_PER_SECOND * 60;
1239- else
1240- game_information->time_limit = INT32_MAX;
1241-
1242- game_information->kill_limit = active_network_preferences->kill_limit;
1243- }
1244-
1245- entry_point entry;
1246- menu_index_to_level_entry (active_network_preferences->entry_point, NONE, &entry);
1247- game_information->level_number = entry.level_number;
1248- strcpy (game_information->level_name, entry.level_name);
1249- game_information->parent_checksum = read_wad_file_checksum(get_map_file());
1250- game_information->difficulty_level = active_network_preferences->difficulty_level;
1251- game_information->allow_mic = active_network_preferences->allow_microphone;
1252-
1253- int updates_per_packet = 1;
1254- int update_latency = 0;
1255- vassert(updates_per_packet > 0 && update_latency >= 0 && updates_per_packet < 16,
1256- csprintf(temporary, "You idiot! updates_per_packet = %d, update_latency = %d", updates_per_packet, update_latency));
1257- game_information->initial_updates_per_packet = updates_per_packet;
1258- game_information->initial_update_latency = update_latency;
1259- NetSetInitialParameters(updates_per_packet, update_latency);
1260-
1261- game_information->initial_random_seed = resuming_game ? dynamic_world->random_seed : (uint16) machine_tick_count();
1262-
1263-#if mac
1264- FileSpecifier theNetscriptFile;
1265- theNetscriptFile.SetSpec (active_network_preferences->netscript_file);
1266-#else
1267- FileSpecifier theNetscriptFile (active_network_preferences->netscript_file);
1268-#endif
1269-
1270- // This will be set true below if appropriate
1271- SetNetscriptStatus(false);
1272-
1273- if (active_network_preferences->use_netscript)
1274- {
1275- OpenedFile script_file;
1276-
1277- if (theNetscriptFile.Open (script_file))
1278- {
1279- int32 script_length;
1280- script_file.GetLength (script_length);
1281-
1282- // DeferredScriptSend will delete this storage the *next time* we call it (!)
1283- byte* script_buffer = new byte [script_length];
1284-
1285- if (script_file.Read (script_length, script_buffer))
1286- {
1287- DeferredScriptSend (script_buffer, script_length);
1288- SetNetscriptStatus (true);
1289- }
1290-
1291- script_file.Close ();
1292- }
1293- else
1294- // hmm failing quietly is probably not the best course of action, but ...
1295- ;
1296- }
1297-
1298- game_information->cheat_flags = active_network_preferences->cheat_flags;
1299-
1300- outAdvertiseGameOnMetaserver = active_network_preferences->advertise_on_metaserver;
1301-
1302- //if(shouldUseNetscript)
1303- //{
1304- // Sorry, probably should use a FileSpecifier in the prefs,
1305- // but that means prefs reading/writing have to be reworked instead
1306-#ifdef SDL
1307- // strncpy(network_preferences->netscript_file, theNetscriptFile.GetPath(), sizeof(network_preferences->netscript_file));
1308-#else
1309- // network_preferences->netscript_file = theNetscriptFile.GetSpec();
1310-#endif
1311- //}
1312-
1313- return true;
1314-
1315- } else // dialog was cancelled
1316- return false;
1317-}
1318-
1319-void SetupNetgameDialog::setupForUntimedGame ()
1320-{
1321- m_timeLimitWidget->hide ();
1322- m_scoreLimitWidget->hide ();
1323-}
1324-
1325-void SetupNetgameDialog::setupForTimedGame ()
1326-{
1327- m_timeLimitWidget->show ();
1328- m_scoreLimitWidget->hide ();
1329-}
1330-
1331-void SetupNetgameDialog::setupForScoreGame ()
1332-{
1333- m_timeLimitWidget->hide ();
1334- m_scoreLimitWidget->show ();
1335-}
1336-
1337-void SetupNetgameDialog::limitTypeHit ()
1338-{
1339- switch(m_limitTypeWidget->get_value ())
1340- {
1341- case 0:
1342- setupForUntimedGame ();
1343- break;
1344-
1345- case 1:
1346- setupForTimedGame ();
1347- break;
1348-
1349- case 2:
1350- setupForScoreGame ();
1351- break;
1352- }
1353-}
1354-
1355-void SetupNetgameDialog::teamsHit ()
1356-{
1357- if (m_allowTeamsWidget->get_value ())
1358- m_teamWidget->activate ();
1359- else
1360- m_teamWidget->deactivate ();
1361-}
1362-
1363-void SetupNetgameDialog::setupForGameType ()
1364-{
1365- int raw_value = m_gameTypeWidget->get_value ();
1366- switch (raw_value < 5 ? raw_value : raw_value + 1)
1367- {
1368- case _game_of_cooperative_play:
1369- m_allowTeamsWidget->activate ();
1370- m_deadPlayersDropItemsWidget->deactivate ();
1371- m_aliensWidget->deactivate ();
1372-
1373- m_deadPlayersDropItemsWidget->set_value (true);
1374- m_aliensWidget->set_value (true);
1375-
1376- getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, killsString);
1377-// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1378- break;
1379-
1380- case _game_of_kill_monsters:
1381- case _game_of_king_of_the_hill:
1382- case _game_of_kill_man_with_ball:
1383- case _game_of_tag:
1384- case _game_of_custom:
1385- m_allowTeamsWidget->activate ();
1386- m_deadPlayersDropItemsWidget->activate ();
1387- m_aliensWidget->activate ();
1388-
1389- getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, killsString);
1390-// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1391- break;
1392-
1393- case _game_of_capture_the_flag:
1394- m_allowTeamsWidget->deactivate ();
1395- m_deadPlayersDropItemsWidget->activate ();
1396- m_aliensWidget->activate ();
1397-
1398- m_allowTeamsWidget->set_value (true);
1399- m_teamWidget->activate ();
1400-
1401- getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, flagsString);
1402-// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1403- break;
1404-
1405- case _game_of_rugby:
1406- m_allowTeamsWidget->deactivate ();
1407- m_deadPlayersDropItemsWidget->activate ();
1408- m_aliensWidget->activate ();
1409-
1410- m_allowTeamsWidget->set_value (true);
1411- m_teamWidget->activate ();
1412-
1413- getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, pointsString);
1414-// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1415- break;
1416-
1417- default:
1418- assert(false);
1419- break;
1420- }
1421-}
1422-
1423-void SetupNetgameDialog::gameTypeHit ()
1424-{
1425- int new_game_type= m_gameTypeWidget->get_value ();
1426- if (new_game_type >= 5)
1427- ++new_game_type;
1428-
1429- if (new_game_type != m_old_game_type) {
1430- int32 new_entry_flags, old_entry_flags;
1431- struct entry_point entry;
1432-
1433- if(m_allow_all_levels) {
1434- new_entry_flags= old_entry_flags= NONE;
1435- } else {
1436- new_entry_flags= get_entry_point_flags_for_game_type(new_game_type);
1437- old_entry_flags= get_entry_point_flags_for_game_type(m_old_game_type);
1438- }
1439- menu_index_to_level_entry (m_levelWidget->get_value (), old_entry_flags, &entry);
1440-
1441- /* Now reset entry points */
1442- m_levelWidget->set_labels (make_entry_vector (new_entry_flags));
1443- m_levelWidget->set_value (level_index_to_menu_index (entry.level_number, new_entry_flags));
1444- m_old_game_type= new_game_type;
1445-
1446- setupForGameType ();
1447- }
1448-}
1449-
1450-void SetupNetgameDialog::chooseMapHit ()
1451-{
1452- FileSpecifier mapFile = m_mapWidget->get_file ();
1453-
1454- environment_preferences->map_checksum = read_wad_file_checksum (mapFile);
1455-#ifdef SDL
1456- strcpy(environment_preferences->map_file, mapFile.GetPath());
1457-#else
1458- environment_preferences->map_file = mapFile.GetSpec();
1459-#endif
1460- load_environment_from_preferences();
1461-
1462- m_levelWidget->set_labels (make_entry_vector (get_entry_point_flags_for_game_type (m_old_game_type)));
1463- m_levelWidget->set_value (0);
1464-}
1465-
1466-bool SetupNetgameDialog::informationIsAcceptable ()
1467-{
1468- bool information_is_acceptable = true;
1469- short game_limit_type = m_limitTypeWidget->get_value ();
1470-
1471- if (information_is_acceptable)
1472- if (game_limit_type == duration_time_limit)
1473- {
1474- information_is_acceptable = m_timeLimitWidget->get_value () >= 1;
1475- }
1476-
1477- if (information_is_acceptable)
1478- if (game_limit_type == duration_kill_limit)
1479- {
1480- information_is_acceptable = m_scoreLimitWidget->get_value () >= 1;
1481- }
1482-
1483- if (information_is_acceptable)
1484- information_is_acceptable = !(m_nameWidget->get_text ().empty ());
1485-
1486- if (information_is_acceptable)
1487- information_is_acceptable = m_mapWidget->get_file ().Exists ();
1488-
1489- if (information_is_acceptable)
1490- {
1491- entry_point ep;
1492- short index = 0;
1493- information_is_acceptable = get_indexed_entry_point(&ep, &index, get_entry_point_flags_for_game_type(m_old_game_type));
1494- }
1495-
1496- return (information_is_acceptable);
1497-}
1498-
1499-void SetupNetgameDialog::okHit ()
1500-{
1501- if (informationIsAcceptable ())
1502- Stop (true);
1503- else
1504- unacceptableInfo ();
1505-
1506-}
1507-
1508-void menu_index_to_level_entry(
1509- short menu_index,
1510- int32 entry_flags,
1511- struct entry_point *entry)
1512-{
1513- short i, map_index;
1514-
1515- map_index= 0;
1516- for (i= 0; i<=menu_index; i++)
1517- {
1518- get_indexed_entry_point(entry, &map_index, entry_flags);
1519- }
1520-
1521- return;
1522-}
1523-
1524-int menu_index_to_level_index (int menu_index, int32 entry_flags)
1525-{
1526- entry_point entry;
1527-
1528- menu_index_to_level_entry (menu_index, entry_flags, &entry);
1529-
1530- return entry.level_number;
1531-}
1532-
1533-int level_index_to_menu_index (int level_index, int32 entry_flags)
1534-{
1535- entry_point entry;
1536- short map_index = 0;
1537-
1538- int result = 0;
1539- while (get_indexed_entry_point(&entry, &map_index, entry_flags)) {
1540- if (map_index == level_index + 1)
1541- return result;
1542- ++result;
1543- }
1544-
1545- return 0;
1546-}
1547-
1548-/*************************************************************************************************
1549- *
1550- * Function: reassign_player_colors
1551- * Purpose: This function used to reassign a player's color if it conflicted with another
1552- * player's color. Now it reassigns everyone's colors. for the old function, see the
1553- * obsoleted version (called check_player_info) at the end of this file.
1554- * (Woody note: check_player_info can be found in network_dialogs_macintosh.cpp.)
1555- *
1556- *************************************************************************************************/
1557-/* Note that we now only force unique colors across teams. */
1558-
1559-// ZZZ: moved here (from network_dialogs_macintosh.cpp) so it can be shared with SDL version
1560-
1561-void reassign_player_colors(
1562- short player_index,
1563- short num_players)
1564-{
1565- short actual_colors[MAXIMUM_NUMBER_OF_PLAYERS]; // indexed by player
1566- bool colors_taken[NUMBER_OF_TEAM_COLORS]; // as opposed to desired team. indexed by team
1567- game_info *game;
1568-
1569- (void)(player_index);
1570-
1571- assert(num_players<=MAXIMUM_NUMBER_OF_PLAYERS);
1572- game= (game_info *)NetGetGameData();
1573-
1574- objlist_set(colors_taken, false, NUMBER_OF_TEAM_COLORS);
1575- objlist_set(actual_colors, NONE, MAXIMUM_NUMBER_OF_PLAYERS);
1576-
1577- if(game->game_options & _force_unique_teams)
1578- {
1579- short index;
1580-
1581- for(index= 0; index<num_players; ++index)
1582- {
1583- player_info *player= (player_info *)NetGetPlayerData(index);
1584- if(!colors_taken[player->desired_color])
1585- {
1586- player->color= player->desired_color;
1587- player->team= player->color;
1588- colors_taken[player->color]= true;
1589- actual_colors[index]= player->color;
1590- }
1591- }
1592-
1593- /* Now give them a random color.. */
1594- for (index= 0; index<num_players; index++)
1595- {
1596- player_info *player= (player_info *)NetGetPlayerData(index);
1597-
1598- if (actual_colors[index]==NONE) // This player needs a team
1599- {
1600- short remap_index;
1601-
1602- for (remap_index= 0; remap_index<num_players; remap_index++)
1603- {
1604- if (!colors_taken[remap_index])
1605- {
1606- player->color= remap_index;
1607- player->team= remap_index;
1608- colors_taken[remap_index] = true;
1609- break;
1610- }
1611- }
1612- assert(remap_index<num_players);
1613- }
1614- }
1615- } else {
1616- short index;
1617- short team_color;
1618-
1619- /* Allow teams.. */
1620- for(team_color= 0; team_color<NUMBER_OF_TEAM_COLORS; ++team_color)
1621- {
1622- // let's mark everybody down for the teams that they can get without conflicts.
1623- for (index = 0; index < num_players; index++)
1624- {
1625- player_info *player= (player_info *)NetGetPlayerData(index);
1626-
1627- if (player->team==team_color && !colors_taken[player->desired_color])
1628- {
1629- player->color= player->desired_color;
1630- colors_taken[player->color] = true;
1631- actual_colors[index]= player->color;
1632- }
1633- }
1634-
1635- // ok, everyone remaining gets a team that we pick for them.
1636- for (index = 0; index < num_players; index++)
1637- {
1638- player_info *player= (player_info *)NetGetPlayerData(index);
1639-
1640- if (player->team==team_color && actual_colors[index]==NONE) // This player needs a team
1641- {
1642- short j;
1643-
1644- for (j = 0; j < num_players; j++)
1645- {
1646- if (!colors_taken[j])
1647- {
1648- player->color= j;
1649- colors_taken[j] = true;
1650- break;
1651- }
1652- }
1653- assert(j < num_players);
1654- }
1655- }
1656- }
1657- }
1658-}
1659-
1660-
1661-
1662-////////////////////////////////////////////////////////////////////////////////
1663-// Postgame Carnage Report stuff
1664-struct net_rank rankings[MAXIMUM_NUMBER_OF_PLAYERS];
1665-
1666-#if 0
1667-// These were used for an array-lookup-based find_graph_mode, which worked but was later
1668-// abandoned in favor of the original (very slightly modified to "add back in" separator indices).
1669-// Left here for the curious.
1670-
1671-// This should not conflict with the other _*_graph identifiers
1672-enum { _total_team_carnage_or_total_scores_graph = 4242 };
1673-
1674-// We lookup into a menu contents array now
1675-static int sMenuContents[] =
1676-#ifdef mac
1677-{
1678- NONE, // separator
1679- _total_carnage_graph,
1680- _total_team_carnage_or_total_scores_graph,
1681- NONE, // separator
1682- _total_team_carnage_graph,
1683- _total_team_scores_graph
1684-};
1685-#else // !mac
1686-{
1687- _total_carnage_graph,
1688- _total_team_carnage_or_total_scores_graph,
1689- _total_team_carnage_graph,
1690- _total_team_scores_graph
1691-};
1692-#endif // !mac
1693-#endif // 0
1694-
1695-// (ZZZ annotation:) Figure out which graph type the user wants to display based
1696-// on his selection from the popup/selection control. (See also draw_new_graph().)
1697-short
1698-find_graph_mode(
1699- NetgameOutcomeData &outcome,
1700- short *index)
1701-{
1702- short value;
1703- short graph_type = NONE;
1704- bool has_scores;
1705-
1706- has_scores= current_net_game_has_scores();
1707-
1708- /* Popups are 1 based */
1709-#ifdef USES_NIBS
1710- value = GetControl32BitValue(outcome.SelectCtrl) - 1;
1711-#else
1712- value = get_selection_control_value(outcome, iGRAPH_POPUP)-1;
1713-#endif
1714- if(value<dynamic_world->player_count)
1715- {
1716- if(index) *index= value;
1717- graph_type= _player_graph;
1718- }
1719- else
1720- {
1721-#if 0
1722- // alternate method based on array lookup, works but abandoned.
1723- // left here for the curious.
1724-
1725- // ZZZ change: lookup graph type from static array
1726- int theIndexAfterPlayers = value - dynamic_world->player_count;
1727- int theNumberOfMenuItemsAfterPlayers = (sizeof(sMenuContents) / sizeof(sMenuContents[0]));
1728-
1729- // Make sure the index is sane
1730- assert(theIndexAfterPlayers >= 0 && theIndexAfterPlayers < theNumberOfMenuItemsAfterPlayers);
1731-
1732- // Do the lookup
1733- graph_type = sMenuContents[theIndexAfterPlayers];
1734-
1735- // Make sure graph type is sane
1736- assert(graph_type != NONE);
1737-
1738- bool isTeamGame = ((GET_GAME_OPTIONS() & _force_unique_teams) ? false : true);
1739-
1740- // Disambiguate
1741- if(graph_type == _total_team_carnage_or_total_scores_graph)
1742- graph_type = has_scores ? _total_scores_graph : _total_team_carnage_graph;
1743-
1744- // Sanity check the graph type
1745- if(!isTeamGame) {
1746- assert(graph_type != _total_team_carnage_graph);
1747- assert(graph_type != _total_team_scores_graph);
1748- }
1749-
1750- if(!has_scores) {
1751- assert(graph_type != _total_scores_graph);
1752- assert(graph_type != _total_team_scores_graph);
1753- }
1754-
1755-#else // !0
1756-
1757- int theValueAfterPlayers = value-dynamic_world->player_count;
1758-#ifndef mac
1759- // ZZZ: Account for (lack of) separators
1760- if(theValueAfterPlayers >= 0) theValueAfterPlayers++;
1761- if(theValueAfterPlayers >= 3) theValueAfterPlayers++;
1762-#endif
1763-
1764- /* Different numbers of items based on game type. */
1765- switch(theValueAfterPlayers)
1766- {
1767- case 0:
1768- /* Separator line */
1769- assert(false);
1770- break;
1771-
1772- case 1: /* FIrst item after the players. */
1773- graph_type= _total_carnage_graph; /* Always.. */
1774- break;
1775-
1776- case 2: /* May be either: _total_scores or _total_team_carnage */
1777- if(has_scores)
1778- {
1779- graph_type= _total_scores_graph;
1780- } else {
1781- assert(!(GET_GAME_OPTIONS() & _force_unique_teams));
1782- graph_type= _total_team_carnage_graph;
1783- }
1784- break;
1785-
1786- case 3:
1787- /* Separator line */
1788- assert(false);
1789- break;
1790-
1791- case 4:
1792- assert(!(GET_GAME_OPTIONS() & _force_unique_teams));
1793- graph_type= _total_team_carnage_graph;
1794- break;
1795-
1796- case 5:
1797- assert(has_scores);
1798- graph_type= _total_team_scores_graph;
1799- break;
1800-
1801- default:
1802- assert(false);
1803- break;
1804- }
1805-#endif // !0
1806- }
1807-
1808- return graph_type;
1809-}
1810-
1811-
1812-
1813-// (ZZZ annotation:) Fill in array of net_rank with total carnage values, individual scores,
1814-// colors, etc. Note that team-by-team rankings (draw_team_*_graph()) and player vs.
1815-// player rankings (draw_player_graph()) use their own local ranks[] arrays instead.
1816-// The team-by-team rankings are computed from these; the player vs. player are not.
1817-void calculate_rankings(
1818- struct net_rank *ranks,
1819- short num_players)
1820-{
1821- short player_index;
1822-
1823- for(player_index= 0; player_index<num_players; ++player_index)
1824- {
1825- ranks[player_index].player_index= player_index;
1826- ranks[player_index].color= get_player_data(player_index)->color;
1827- ranks[player_index].game_ranking= get_player_net_ranking(player_index,
1828- &ranks[player_index].kills,
1829- &ranks[player_index].deaths, true);
1830- ranks[player_index].ranking= ranks[player_index].kills-ranks[player_index].deaths;
1831- }
1832-}
1833-
1834-
1835-// (ZZZ annotation:) Individual carnage totals comparison for sorting.
1836-int rank_compare(
1837- void const *r1,
1838- void const *r2)
1839-{
1840- struct net_rank const *rank1=(struct net_rank const *)r1;
1841- struct net_rank const *rank2=(struct net_rank const *)r2;
1842- int diff;
1843- struct player_data *p1, *p2;
1844-
1845- diff = rank2->ranking - rank1->ranking;
1846-
1847- // (ZZZ annotation:) Tiebreaker: which player did more killing (and thus dying)?
1848- if (diff == 0)
1849- {
1850- // i have to resort to looking here because the information may not be contained
1851- // in the rank structure if we're not displaying the totals graph.
1852- p1 = get_player_data(rank1->player_index);
1853- p2 = get_player_data(rank2->player_index);
1854- diff = p2->total_damage_given.kills - p1->total_damage_given.kills;
1855- }
1856-
1857- return diff;
1858-}
1859-
1860-// (ZZZ annotation:) Team carnage totals comparison for sorting.
1861-// Same as rank_compare(), but without tiebreaking.
1862-int team_rank_compare(
1863- void const *rank1,
1864- void const *rank2)
1865-{
1866- return ((struct net_rank const *)rank2)->ranking
1867- -((struct net_rank const *)rank1)->ranking;
1868-}
1869-
1870-// (ZZZ annotation:) Game-specific score comparison for sorting.
1871-int score_rank_compare(
1872- void const *rank1,
1873- void const *rank2)
1874-{
1875- return ((struct net_rank const *)rank2)->game_ranking
1876- -((struct net_rank const *)rank1)->game_ranking;
1877-}
1878-
1879-
1880-
1881-// (ZZZ annotation:) Graph of player's killing performance vs each other player
1882-void draw_player_graph(
1883- NetgameOutcomeData &outcome,
1884- short index)
1885-{
1886- short key_player_index= rankings[index].player_index;
1887- struct player_data *key_player= get_player_data(key_player_index);
1888- struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
1889- short loop;
1890-
1891- /* Copy in the total ranks. */
1892- for(loop= 0; loop<dynamic_world->player_count; ++loop)
1893- {
1894- short test_player_index= rankings[loop].player_index;
1895- struct player_data *player= get_player_data(test_player_index);
1896-
1897- /* Copy most of the data */
1898- ranks[loop]= rankings[loop];
1899-
1900- /* How many times did I kill this guy? */
1901- ranks[loop].kills= player->damage_taken[key_player_index].kills;
1902-
1903- /* How many times did this guy kill me? */
1904- ranks[loop].deaths= key_player->damage_taken[test_player_index].kills;
1905- }
1906-
1907- draw_names(outcome, ranks, dynamic_world->player_count, index);
1908- draw_kill_bars(outcome, ranks, dynamic_world->player_count, index, false, false);
1909-}
1910-
1911-
1912-// ZZZ: team vs team carnage (analogous to draw_player_graph's player vs player carnage)
1913-// THIS IS UNFINISHED (and thus unused at the moment :) )
1914-void draw_team_graph(
1915- NetgameOutcomeData &outcome,
1916- short team_index)
1917-{
1918- // ZZZZZZ this is where I add my team vs team ranking computation. Yay.
1919- // We'll just fill in the rank structures straight, then count the teams later.
1920- struct net_rank team_ranks[NUMBER_OF_TEAM_COLORS];
1921-
1922- objlist_clear(team_ranks, NUMBER_OF_TEAM_COLORS);
1923-
1924- /* Loop across players on the reference team */
1925- for(int ref_player_index = 0; ref_player_index < dynamic_world->player_count; ref_player_index++)
1926- {
1927-// short test_player_index= rankings[loop].player_index;
1928- struct player_data *ref_player= get_player_data(ref_player_index);
1929-
1930- if(ref_player->team != team_index)
1931- continue;
1932-
1933- /* Loop across all players */
1934- for(int player_index = 0; player_index < dynamic_world->player_count; player_index++)
1935- {
1936- // short test_player_index= rankings[loop].player_index;
1937- struct player_data *player= get_player_data(player_index);
1938-
1939- team_ranks[player->team].player_index = NONE;
1940- team_ranks[player->team].color = player->team;
1941- team_ranks[player->team].kills += player->damage_taken[ref_player_index].kills;
1942- team_ranks[player->team].deaths += ref_player->damage_taken[player_index].kills;
1943- } // all players
1944- } // players on reference team
1945-
1946- // Condense into the first group of slots in the rankings
1947- // NOTE ideally these will be ordered the same way the team_total_carnage rankings are.
1948-
1949- // Draw the bars
1950-// draw_names(dialog, team_ranks, dynamic_world->player_count, index);
1951-// draw_kill_bars(dialog, team_ranks, dynamic_world->player_count, index, false, false);
1952-}
1953-
1954-
1955-
1956-// (ZZZ annotation:) Total Carnage graph
1957-void draw_totals_graph(
1958- NetgameOutcomeData &outcome)
1959-{
1960- draw_names(outcome, rankings, dynamic_world->player_count, NONE);
1961- draw_kill_bars(outcome, rankings, dynamic_world->player_count, NONE, true, false);
1962-}
1963-
1964-
1965-// (ZZZ annotation:) Total Team Carnage graph
1966-void draw_team_totals_graph(
1967- NetgameOutcomeData &outcome)
1968-{
1969- short team_index, player_index, num_teams;
1970- bool found_team_of_current_color;
1971- struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
1972-
1973- objlist_clear(ranks, MAXIMUM_NUMBER_OF_PLAYERS);
1974- for (team_index = 0, num_teams = 0; team_index < NUMBER_OF_TEAM_COLORS; team_index++) {
1975- found_team_of_current_color = false;
1976- if (team_damage_given[team_index].kills ||
1977- (team_damage_taken[team_index].kills + team_monster_damage_taken[team_index].kills)) {
1978- found_team_of_current_color = true;
1979- } else {
1980- for (player_index = 0; player_index < dynamic_world->player_count; player_index++) {
1981- struct player_data *player = get_player_data(player_index);
1982- if (player->team == team_index) {
1983- found_team_of_current_color = true;
1984- break;
1985- }
1986- }
1987- }
1988- if (found_team_of_current_color) {
1989- ranks[num_teams].player_index = NONE;
1990- ranks[num_teams].color = team_index;
1991- ranks[num_teams].kills = team_damage_given[team_index].kills;
1992- ranks[num_teams].deaths = team_damage_taken[team_index].kills + team_monster_damage_taken[team_index].kills;
1993- ranks[num_teams].friendly_fire_kills = team_friendly_fire[team_index].kills;
1994- num_teams++;
1995- }
1996- }
1997-
1998- /* Setup the team rankings.. */
1999- for (team_index= 0; team_index<num_teams; team_index++)
2000- {
2001- ranks[team_index].ranking= ranks[team_index].kills - ranks[team_index].deaths;
2002- }
2003- qsort(ranks, num_teams, sizeof(struct net_rank), team_rank_compare);
2004-
2005- draw_names(outcome, ranks, num_teams, NONE);
2006- draw_kill_bars(outcome, ranks, num_teams, NONE, true, true);
2007-}
2008-
2009-
2010-
2011-// (ZZZ annotation:) Time on Hill, etc. graph
2012-void draw_total_scores_graph(
2013- NetgameOutcomeData &outcome)
2014-{
2015- struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
2016-
2017- /* Use a private copy to avoid boning things */
2018- objlist_copy(ranks, rankings, dynamic_world->player_count);
2019-
2020- /* First qsort the rankings arrray by game_ranking.. */
2021- qsort(ranks, dynamic_world->player_count, sizeof(struct net_rank), score_rank_compare);
2022-
2023- draw_names(outcome, ranks, dynamic_world->player_count, NONE);
2024- draw_score_bars(outcome, ranks, dynamic_world->player_count);
2025-}
2026-
2027-
2028-
2029-// (ZZZ annotation:) Team Time on Hill, etc. graph
2030-void draw_team_total_scores_graph(
2031- NetgameOutcomeData &outcome)
2032-{
2033- short team_index, team_count;
2034- struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
2035-
2036- objlist_clear(ranks, MAXIMUM_NUMBER_OF_PLAYERS);
2037- team_count = 0;
2038-
2039- for (team_index = 0; team_index < NUMBER_OF_TEAM_COLORS; ++team_index) {
2040- bool team_is_valid = false;
2041- short kills, deaths;
2042- int32 ranking = get_team_net_ranking(team_index, &kills, &deaths, true);
2043-
2044- if (kills || deaths || ranking) {
2045- team_is_valid = true;
2046- } else {
2047- for (short player_index = 0; player_index < dynamic_world->player_count; ++player_index) {
2048- struct player_data *player = get_player_data(player_index);
2049- if (player->team == team_index) {
2050- team_is_valid = true;
2051- break;
2052- }
2053- }
2054- }
2055-
2056- if (team_is_valid) {
2057- ranks[team_count].kills = kills;
2058- ranks[team_index].deaths = deaths;
2059- ranks[team_count].player_index = NONE;
2060- ranks[team_count].color = team_index;
2061- ranks[team_count].game_ranking = ranking;
2062- ranks[team_count].friendly_fire_kills = team_friendly_fire[NUMBER_OF_TEAM_COLORS].kills;
2063- team_count++;
2064- }
2065- }
2066-
2067- /* Now qsort our team stuff. */
2068- qsort(ranks, team_count, sizeof(struct net_rank), score_rank_compare);
2069-
2070- draw_names(outcome, ranks, team_count, NONE);
2071- draw_score_bars(outcome, ranks, team_count);
2072-}
2073-
2074-
2075-
2076-// (ZZZ) ripped this out of draw_kill_bars since we can share this bit but not the rest of draw_kill_bars().
2077-// (ZZZ annotation:) Update the "N deaths total (D dpm) including S suicides"-type text at the bottom.
2078-void update_carnage_summary(
2079- NetgameOutcomeData &outcome,
2080- struct net_rank *ranks,
2081- short num_players,
2082- short suicide_index,
2083- bool do_totals,
2084- bool friendly_fire)
2085-{
2086- short i;
2087- short num_suicides;
2088- float minutes;
2089- float kpm;
2090- float dpm;
2091- int32 total_kills = 0;
2092- int32 total_deaths = 0;
2093- char kill_string_format[65];
2094- char death_string_format[65];
2095- char suicide_string_format[65];
2096-
2097- for (i = 0; i < num_players; i++)
2098- {
2099- if (do_totals || i != suicide_index)
2100- total_kills += ranks[i].kills;
2101- total_deaths += ranks[i].deaths;
2102- }
2103- if (do_totals)
2104- {
2105- for (i = num_suicides = 0; i < num_players; i++)
2106- {
2107- if (friendly_fire)
2108- num_suicides += ranks[i].friendly_fire_kills;
2109- else
2110- num_suicides += (players+i)->damage_taken[i].kills;
2111- }
2112- }
2113- else
2114- num_suicides = ranks[suicide_index].kills;
2115-
2116-#ifdef mac
2117- TextFace(0); TextFont(0); TextSize(0);
2118-#endif
2119-
2120- minutes = ((float)dynamic_world->tick_count / TICKS_PER_SECOND) / 60.0F;
2121- if (minutes > 0) kpm = total_kills / minutes;
2122- else kpm = 0;
2123- getcstr(kill_string_format, strNET_STATS_STRINGS, strTOTAL_KILLS_STRING);
2124- psprintf(ptemporary, kill_string_format, total_kills, kpm);
2125-// GetDialogItem(dialog, iTOTAL_KILLS, &item_type, &item_handle, &item_rect);
2126-// SetDialogItemText(item_handle, ptemporary);
2127-
2128-#ifdef USES_NIBS
2129- SetStaticPascalText(outcome.KillsTextCtrl, ptemporary);
2130-#else
2131- copy_pstring_to_static_text(outcome, iTOTAL_KILLS, ptemporary);
2132-#endif
2133-
2134- if (minutes > 0) dpm = total_deaths / minutes;
2135- else dpm = 0;
2136- getcstr(death_string_format, strNET_STATS_STRINGS, strTOTAL_DEATHS_STRING);
2137-
2138- if (num_suicides)
2139- {
2140- if (friendly_fire)
2141- getcstr(suicide_string_format, strNET_STATS_STRINGS, strFRIENDLY_FIRE_STRING);
2142- else
2143- getcstr(suicide_string_format, strNET_STATS_STRINGS, strINCLUDING_SUICIDES_STRING);
2144- strcat(death_string_format, suicide_string_format);
2145- psprintf(ptemporary, death_string_format, total_deaths, dpm, num_suicides);
2146- }
2147- else
2148- psprintf(ptemporary, death_string_format, total_deaths, dpm);
2149-// GetDialogItem(dialog, iTOTAL_DEATHS, &item_type, &item_handle, &item_rect);
2150-// SetDialogItemText(item_handle, ptemporary);
2151-
2152-#ifdef USES_NIBS
2153- SetStaticPascalText(outcome.DeathsTextCtrl, ptemporary);
2154-#else
2155- copy_pstring_to_static_text(outcome, iTOTAL_DEATHS, ptemporary);
2156-#endif
2157-}
2158-
2159-
2160-
2161-// ZZZ: ripped out of update_damage_item
2162-// (ZZZ annotation:) Demultiplex to draw_X_graph() function based on find_graph_mode().
2163-void draw_new_graph(
2164- NetgameOutcomeData &outcome)
2165-{
2166- short graph_type;
2167- short index;
2168-
2169- graph_type= find_graph_mode(outcome, &index);
2170-
2171- switch(graph_type)
2172- {
2173- case _player_graph:
2174- draw_player_graph(outcome, index);
2175- break;
2176-
2177- case _total_carnage_graph:
2178- draw_totals_graph(outcome);
2179- break;
2180-
2181- case _total_scores_graph:
2182- draw_total_scores_graph(outcome);
2183- break;
2184-
2185- /* These two functions need to have the team colors. */
2186- case _total_team_carnage_graph:
2187- draw_team_totals_graph(outcome);
2188- break;
2189-
2190- case _total_team_scores_graph:
2191- draw_team_total_scores_graph(outcome);
2192- break;
2193-
2194- default:
2195- assert(false);
2196- break;
2197- }
2198-}
2199-
2200-// (ZZZ annotation:) Find the highest player vs. player kills
2201-// (so all player vs. player graphs can be at the same scale)
2202-short calculate_max_kills(
2203- size_t num_players)
2204-{
2205- short max_kills = 0;
2206- size_t i, j;
2207-
2208- for (i = 0; i < num_players; i++)
2209- {
2210- for (j = 0; j < num_players; j++)
2211- {
2212- struct player_data *player= get_player_data(i);
2213-
2214- if (player->damage_taken[j].kills > max_kills)
2215- {
2216- max_kills= player->damage_taken[j].kills;
2217- }
2218- }
2219- }
2220-
2221- return max_kills;
2222-}
2223-
2224-
2225-
2226-// (ZZZ annotation:) Get postgame bar color for _suicide_color, etc.
2227-/* If alain wasn't a tool, this would be in a resource.. */
2228-void get_net_color(
2229- short index,
2230- RGBColor *color)
2231-{
2232- switch(index)
2233- {
2234- case _suicide_color:
2235- color->red= color->green= USHRT_MAX;
2236- color->blue= 0;
2237- break;
2238- case _kill_color:
2239- color->red= USHRT_MAX;
2240- color->green= color->blue= 0;
2241- break;
2242- case _death_color:
2243- case _score_color:
2244- color->red= color->green= color->blue= 60000;
2245- break;
2246- default:
2247- assert(false);
2248- break;
2249- }
2250-}
2251-
2252-
2253-// Get player name from outside
2254-// ZZZ random note: I didn't do this part, and I'm not sure it's right. At least, the
2255-// documentation seems a bit inconsistent. The MML docs say that it determines the
2256-// default player name in multiplayer. This is true, but more importantly, it determines
2257-// the service type advertised/sought when trying to get together a game. I guess this
2258-// could be advantageous, in case MML is used to change the way a game works, in which case
2259-// you wouldn't want folks with their MML set up in different ways trying to play together
2260-// (instant sync problems - just add water.)
2261-#define PLAYER_TYPE GetPlayerName()
2262-
2263-// ZZZ: some features that may or may not be there - these are used to control what UI gets drawn.
2264-// (as of my initial submission, only pregame gatherer-to-joiner messaging works.)
2265-// Eventually, someone will make network microphone for SDL, at which point it should be extended
2266-// to work in pre/postgame also, so the text chat becomes less important.
2267-// NOTE - if you enable the POSTGAME_CHAT UI, you will need to edit kPostgameTopMargin in
2268-// w_players_in_game2's implementation to reduce the dialog height it uses. (network_dialog_widgets_sdl.cpp)
2269-#define NETWORK_PREGAME_CHAT // Gatherer can message joiners at pregame
2270-#undef NETWORK_POSTGAME_CHAT // Gatherer can message joiners at postgame
2271-#undef NETWORK_TWO_WAY_CHAT // Joiners can message whenever the gatherer can
2272-
2273-// Stuff that used to live in network_dialogs_sdl.cpp
2274-
2275-#ifdef DEBUG
2276-//#define NETWORK_TEST_POSTGAME_DIALOG // For testing without having to play a netgame
2277-
2278-// Note! If you use NETWORK_TEST_MICROPHONE_LOCALLY you'll also want to set the microphone
2279-// implementation to local loopback, else who knows what will happen...
2280-//#define NETWORK_TEST_MICROPHONE_LOCALLY // can use this OR test postgame dialog, not both at once.
2281-#endif
2282-
2283-#ifdef NETWORK_TEST_MICROPHONE_LOCALLY
2284-#include "network_speaker_sdl.h"
2285-#include "network_sound.h"
2286-#endif
2287-
2288-// ZZZ: graph types are a dynamically-generated StringSet (not loaded from MML)
2289-enum {
2290- kGraphTypesStringSetID = 3180
2291-};
2292-
2293-// limit types, 0-based, for w_select-compatible use. (see also set_limit_type())
2294-enum {
2295- kNoLimit = 0,
2296- kTimeLimit = 0x01,
2297- kScoreLimit = 0x02,
2298- kScoreAndTimeLimits = kScoreLimit | kTimeLimit // currently cannot be selected directly
2299-};
2300-
2301-// Some identifiers used only locally. Hope the numeric equivalents don't conflict!
2302-// (they shouldn't.)
2303-enum {
2304- iDONT_DO_THIS_USE_SHARED_SYMBOLS= 4242, // Score limit? Time limit? No limit?
2305- iCHAT_HISTORY, // Where chat text appears
2306- iCHAT_ENTRY, // Where chat text is entered
2307-
2308-};
2309-
2310-
2311-static bool sAdvertiseGameOnMetaserver = false;
2312-
2313-// ZZZ: this is based on the eponymous function on the Mac side
2314-static short create_graph_popup_menu(w_select* theMenu)
2315-{
2316- short index;
2317- bool has_scores;
2318-
2319- // Clear the graph types stringset
2320- TS_DeleteStringSet(kGraphTypesStringSetID);
2321-
2322- /* Setup the player names */
2323- for (index= 0; index<dynamic_world->player_count; index++)
2324- {
2325- struct player_data *player= get_player_data(rankings[index].player_index);
2326-
2327- TS_PutCString(kGraphTypesStringSetID, index, player->name);
2328- }
2329-
2330- /* Add in the total carnage.. */
2331- getcstr(temporary, strNET_STATS_STRINGS, strTOTALS_STRING);
2332- TS_PutCString(kGraphTypesStringSetID, index, temporary);
2333- index++;
2334-
2335- /* Add in the scores */
2336- has_scores= get_network_score_text_for_postgame(temporary, false);
2337- if(has_scores)
2338- {
2339- TS_PutCString(kGraphTypesStringSetID, index, temporary);
2340- index++;
2341- }
2342-
2343- /* If the game has teams, show the team stats. */
2344- if (!(dynamic_world->game_information.game_options & _force_unique_teams))
2345- {
2346- getcstr(temporary, strNET_STATS_STRINGS, strTEAM_TOTALS_STRING);
2347- TS_PutCString(kGraphTypesStringSetID, index, temporary);
2348- index++;
2349-
2350- if(has_scores)
2351- {
2352- get_network_score_text_for_postgame(temporary, true);
2353- TS_PutCString(kGraphTypesStringSetID, index, temporary);
2354- index++;
2355- }
2356- }
2357-
2358- // Place the newly-constructed StringSet into the graph selection widget.
2359- theMenu->set_labels_stringset(kGraphTypesStringSetID);
2360-
2361- // Change of behavior here: instead of choosing individual scores, or failing that, Total Carnage,
2362- // I select team scores, then failing that either team carnage or individual scores, then failing that,
2363- // Total Carnage. I think this better reflects what really happens - in a team game, it's *teams*
2364- // that win things. You can view the individual results, sure, but the first thing that pops up
2365- // (who won??) is a team stat.
2366- theMenu->set_selection(index - 1);
2367-
2368- return index;
2369-}
2370-
2371-
2372-void
2373-draw_names(DialogPtr &dialog, struct net_rank *ranks, short number_of_bars, short which_player) {
2374- // This does nothing here - draw_kill_bars or draw_score_bars is assumed to have enough data to work with,
2375- // and one of those is always called adjacent to a call to draw_names in practice.
2376-}
2377-
2378-void
2379-draw_kill_bars(DialogPtr &dialog, struct net_rank *ranks, short num_players,
2380- short suicide_index, bool do_totals, bool friendly_fire)
2381-{
2382- // We don't actually draw here - we just pass the data along to the widget, and it will take care of the rest.
2383- w_players_in_game2* wpig2 = dynamic_cast<w_players_in_game2*>(dialog->get_widget_by_id(iDAMAGE_STATS));
2384- wpig2->set_graph_data(ranks, num_players, suicide_index, (ranks[0].player_index == NONE) ? true : false, false);
2385-
2386- update_carnage_summary(dialog, ranks, num_players, suicide_index, do_totals, friendly_fire);
2387-}
2388-
2389-void
2390-draw_score_bars(DialogPtr &dialog, struct net_rank *ranks, short bar_count) {
2391- // We don't actually draw here - we just pass the data along to the widget, and it will take care of the rest.
2392- w_players_in_game2* wpig2 = dynamic_cast<w_players_in_game2*>(dialog->get_widget_by_id(iDAMAGE_STATS));
2393- wpig2->set_graph_data(ranks, bar_count, NONE, (ranks[0].player_index == NONE) ? true : false, true);
2394-
2395- // clear the summary text
2396- unsigned char theEmptyString = '\0';
2397- copy_pstring_to_static_text(dialog, iTOTAL_KILLS, &theEmptyString);
2398- copy_pstring_to_static_text(dialog, iTOTAL_DEATHS, &theEmptyString);
2399-}
2400-
2401-// User clicked on a postgame carnage report element. If it was a player and we're showing Total Carnage
2402-// or a player vs player graph, switch to showing a player vs player graph according to the player clicked.
2403-static void
2404-respond_to_element_clicked(w_players_in_game2* inWPIG2, bool inTeam, bool inGraph, bool inScore, size_t inDrawIndex,
2405- int inPlayerIndexOrTeamColor) {
2406- if(inGraph && !inTeam && !inScore) {
2407- w_select* theGraphMenu = dynamic_cast<w_select*>(inWPIG2->get_owning_dialog()->get_widget_by_id(iGRAPH_POPUP));
2408-
2409- if(theGraphMenu->get_selection() != inDrawIndex)
2410- theGraphMenu->set_selection(inDrawIndex, true);
2411- }
2412-}
2413-
2414-// User twiddled the iGRAPH_POPUP; draw a new kind of graph in response.
2415-static void
2416-respond_to_graph_type_change(w_select* inGraphMenu) {
2417- DialogPtr p = inGraphMenu->get_owning_dialog();
2418- draw_new_graph(p);
2419-}
2420-
2421-#ifdef NETWORK_TWO_WAY_CHAT
2422-// There's currently no underlying support for this, so we just do some fakery.
2423-static void
2424-send_text_fake(w_text_entry* te) {
2425- assert(te != NULL);
2426-
2427- dialog* d = te->get_owning_dialog();
2428-
2429- w_chat_history* ch = dynamic_cast<w_chat_history*>(d->get_widget_by_id(iCHAT_HISTORY));
2430- assert(ch != NULL);
2431-
2432- int netState = NetState();
2433-
2434- if(netState != netUninitialized && netState != netJoining && netState != netDown
2435- && !(netState == netGathering && NetGetNumberOfPlayers() <= 1))
2436- {
2437- ch->append_chat_entry(NULL, "This is not finished yet. Your text will not be seen by others.");
2438- player_info* info = (player_info*)NetGetPlayerData(NetGetLocalPlayerIndex());
2439- ch->append_chat_entry(info, te->get_text());
2440-
2441- te->set_text("");
2442- }
2443- else {
2444- ch->append_chat_entry(NULL, "There is nobody in the game to hear you yet.");
2445- }
2446-}
2447-#endif // NETWORK_TWO_WAY_CHAT
2448-
2449-// Here's the main entry point for thgametypee postgame carnage report.
2450-void display_net_game_stats(void)
2451-{
2452-//printf("display_net_game_stats\n");
2453-
2454- if (gMetaserverClient)
2455- {
2456- gMetaserverClient->announceGameDeleted();
2457- gMetaserverClient->pump();
2458- }
2459-
2460- dialog d;
2461-
2462- vertical_placer *placer = new vertical_placer;
2463- placer->dual_add(new w_title("POSTGAME CARNAGE REPORT"), d);
2464-
2465- horizontal_placer *graph_type_placer = new horizontal_placer;
2466- w_select* graph_type_w = new w_select(0, NULL);
2467- graph_type_w->set_identifier(iGRAPH_POPUP);
2468- graph_type_w->set_selection_changed_callback(respond_to_graph_type_change);
2469- graph_type_placer->dual_add(graph_type_w->label("Report on"), d);
2470- graph_type_placer->dual_add(graph_type_w, d);
2471-
2472- placer->add(graph_type_placer, true);
2473-
2474- w_players_in_game2* wpig2 = new w_players_in_game2(true); // "true": extra space for postgame labels etc.
2475- wpig2->set_identifier(iDAMAGE_STATS);
2476- wpig2->set_element_clicked_callback(respond_to_element_clicked);
2477- wpig2->update_display(true); // "true": widget gets data from dynamic_world, not topology
2478- placer->dual_add(wpig2, d);
2479-
2480- placer->add(new w_spacer(), true);
2481-
2482-#if 0
2483-// these conditionals don't do the right thing for network_postgame_chat && !network_two_way_chat - there's no
2484-// UI for the gatherer to send. oh well, since that combination seems unlikely at the moment, I'll leave it
2485-// as it; someone can easily fix it if the underlying functionality is added.
2486-#ifdef NETWORK_POSTGAME_CHAT
2487- w_chat_history* chat_history_w = new w_chat_history(600, 6);
2488- chat_history_w->set_identifier(iCHAT_HISTORY);
2489- d.add(chat_history_w);
2490-#ifdef NETWORK_TWO_WAY_CHAT
2491- w_text_entry* chatentry_w = new w_text_entry(240, "");
2492- chatentry_w->set_identifier(iCHAT_ENTRY);
2493- chatentry_w->set_enter_pressed_callback(send_text_fake);
2494- chatentry_w->set_alignment(widget::kAlignLeft);
2495- chatentry_w->set_full_width();
2496- d.add(chatentry_w);
2497-
2498- d.add(new w_spacer());
2499-#endif // NETWORK_TWO_WAY_CHAT
2500-#endif // NETWORK_POSTGAME_CHAT
2501-
2502-#endif
2503-
2504- horizontal_placer *carnage_and_ok_placer = new horizontal_placer;
2505- vertical_placer *carnage_placer = new vertical_placer;
2506-
2507- carnage_placer->add_flags((placeable::placement_flags) ((int) placeable::kAlignLeft | (int) placeable::kFill));
2508- // (total kills) and (total deaths) will be replaced by update_carnage_summary() or set to "".
2509- w_static_text* total_kills_w = new w_static_text("(total kills)");
2510- total_kills_w->set_identifier(iTOTAL_KILLS);
2511- carnage_placer->dual_add(total_kills_w, d);
2512-
2513- w_static_text* total_deaths_w = new w_static_text("(total deaths)");
2514- total_deaths_w->set_identifier(iTOTAL_DEATHS);
2515- carnage_placer->dual_add(total_deaths_w, d);
2516-
2517- carnage_and_ok_placer->add_flags(placeable::kFill);
2518- carnage_and_ok_placer->add(carnage_placer, true);
2519- carnage_and_ok_placer->add_flags();
2520- carnage_and_ok_placer->dual_add(new w_button("OK", dialog_ok, &d), d);
2521-
2522- placer->add_flags(placeable::kFill);
2523- placer->add(carnage_and_ok_placer, true);
2524-
2525- /* Calculate the rankings (once) for the entire graph */
2526- calculate_rankings(rankings, dynamic_world->player_count);
2527- qsort(rankings, dynamic_world->player_count, sizeof(struct net_rank), rank_compare);
2528-
2529- /* Create the graph popup menu */
2530- create_graph_popup_menu(graph_type_w);
2531-
2532- {
2533- DialogPtr p = &d;
2534- draw_new_graph(p);
2535- }
2536-
2537- d.set_widget_placer(placer);
2538- d.run();
2539-}
2540-
2541-class SdlGatherDialog : public GatherDialog
2542-{
2543-public:
2544- SdlGatherDialog()
2545- {
2546- vertical_placer *placer = new vertical_placer;
2547- placer->dual_add(new w_title("GATHER NETWORK GAME"), m_dialog);
2548- placer->add(new w_spacer());
2549-
2550- // m_dialog.add(new w_static_text("Players on Network"));
2551-
2552- w_joining_players_in_room* foundplayers_w = new w_joining_players_in_room(NULL, 320, 3);
2553- placer->dual_add(foundplayers_w, m_dialog);
2554-
2555- horizontal_placer *autogather_placer = new horizontal_placer(get_theme_space(ITEM_WIDGET), true);
2556- w_toggle* autogather_w = new w_toggle(false);
2557- autogather_placer->dual_add(autogather_w->label("Auto-Gather"), m_dialog);
2558- autogather_placer->dual_add(autogather_w, m_dialog);
2559-
2560- placer->add(autogather_placer, true);
2561- placer->add(new w_spacer(), true);
2562-
2563- w_players_in_game2* players_w = new w_players_in_game2(false);
2564- placer->dual_add(players_w, m_dialog);
2565-
2566- horizontal_placer *button_placer = new horizontal_placer;
2567- w_button* play_button_w = new w_button("PLAY");
2568- button_placer->dual_add(play_button_w, m_dialog);
2569-
2570- w_button* cancel_w = new w_button("CANCEL");
2571- button_placer->dual_add(cancel_w, m_dialog);
2572-
2573- placer->add(button_placer, true);
2574-
2575- horizontal_placer *chat_choice_placer = new horizontal_placer;
2576- w_select_popup* chat_choice_w = new w_select_popup();
2577- chat_choice_placer->dual_add(chat_choice_w->label("chat:"), m_dialog);
2578- chat_choice_placer->dual_add(chat_choice_w, m_dialog);
2579- placer->add(chat_choice_placer, true);
2580-
2581- w_colorful_chat* chat_history_w = new w_colorful_chat(600, 6);
2582- placer->dual_add(chat_history_w, m_dialog);
2583-
2584- w_chat_entry* chatentry_w = new w_chat_entry(240);
2585- chatentry_w->enable_mac_roman_input();
2586-
2587- horizontal_placer *say_placer = new horizontal_placer;
2588- say_placer->dual_add(chatentry_w->label("Say:"), m_dialog);
2589- say_placer->add_flags(placeable::kFill);
2590- say_placer->dual_add(chatentry_w, m_dialog);
2591-
2592- placer->add_flags(placeable::kFill);
2593- placer->add(say_placer, true);
2594-
2595- m_dialog.activate_widget(chatentry_w);
2596-
2597- m_dialog.set_widget_placer(placer);
2598-
2599- m_cancelWidget = new ButtonWidget (cancel_w);
2600- m_startWidget = new ButtonWidget (play_button_w);
2601-
2602- m_autogatherWidget = new ToggleWidget (autogather_w);
2603-
2604- m_ungatheredWidget = new JoiningPlayerListWidget (foundplayers_w);
2605- m_pigWidget = new PlayersInGameWidget (players_w);
2606-
2607- m_chatEntryWidget = new EditTextWidget (chatentry_w);
2608- m_chatWidget = new ColorfulChatWidget(new ColorfulChatWidgetImpl(chat_history_w));
2609- m_chatChoiceWidget = new PopupSelectorWidget (chat_choice_w);
2610- }
2611-
2612- virtual bool Run ()
2613- {
2614- m_dialog.set_processing_function (boost::bind(&SdlGatherDialog::idle, this));
2615- return (m_dialog.run() == 0);
2616- }
2617-
2618- virtual void Stop (bool result)
2619- {
2620- if (result)
2621- m_dialog.quit (0);
2622- else
2623- m_dialog.quit (-1);
2624- }
2625-
2626-private:
2627- dialog m_dialog;
2628-};
2629-
2630-auto_ptr<GatherDialog>
2631-GatherDialog::Create()
2632-{
2633- return auto_ptr<GatherDialog>(new SdlGatherDialog);
2634-}
2635-
2636-extern struct color_table *build_8bit_system_color_table(void);
2637-
2638-class SdlJoinDialog : public JoinDialog
2639-{
2640-public:
2641- SdlJoinDialog() : m_tabs(0)
2642- {
2643- SDLMod m = SDL_GetModState();
2644- if ((m & KMOD_ALT) || (m & KMOD_META)) skipToMetaserver = true;
2645-
2646- vertical_placer *placer = new vertical_placer;
2647- placer->dual_add(new w_title("JOIN NETWORK GAME"), m_dialog);
2648- placer->add(new w_spacer(), true);
2649-
2650- table_placer *table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2651- table->col_flags(0, placeable::kAlignRight);
2652-
2653- w_text_entry *name_w = new w_text_entry(PREFERENCES_NAME_LENGTH, "");
2654- name_w->enable_mac_roman_input();
2655- table->dual_add(name_w->label("Name"), m_dialog);
2656- table->dual_add(name_w, m_dialog);
2657-
2658- w_player_color *pcolor_w = new w_player_color(0);
2659- table->dual_add(pcolor_w->label("Color"), m_dialog);
2660- table->dual_add(pcolor_w, m_dialog);
2661-
2662- w_player_color *tcolor_w = new w_player_color(0);
2663- table->dual_add(tcolor_w->label("Team Color"), m_dialog);
2664- table->dual_add(tcolor_w, m_dialog);
2665-
2666- placer->add(table, true);
2667- placer->add(new w_spacer(), true);
2668-
2669- m_tabs = new tab_placer;
2670-
2671- vertical_placer *prejoin_placer = new vertical_placer;
2672- table_placer *prejoin_table = new table_placer(2, get_theme_space(ITEM_WIDGET), true);
2673- prejoin_table->col_flags(0, placeable::kAlignRight);
2674-
2675- w_toggle* hint_w = new w_toggle(false);
2676- prejoin_table->dual_add(hint_w->label("Join by address"), m_dialog);
2677- prejoin_table->dual_add(hint_w, m_dialog);
2678-
2679- w_text_entry* hint_address_w = new w_text_entry(kJoinHintingAddressLength, "");
2680- prejoin_table->dual_add(hint_address_w->label("Join address"), m_dialog);
2681- prejoin_table->dual_add(hint_address_w, m_dialog);
2682-
2683- prejoin_placer->add(prejoin_table, true);
2684- prejoin_placer->add(new w_spacer(), true);
2685-
2686- vertical_placer *postjoin_placer = new vertical_placer;
2687-
2688- w_static_text* join_messages_w = new w_static_text("");
2689-
2690- // jkvw: add it to dialog, but never show it.
2691- // Two things which we need don't work:
2692- // 1) w_static_text can't handle text longer than the dialog width
2693- // 2) widgets in dialog don't update layout position once dialog starts to run
2694- // If we get solutions to these issues, then we can show the join messages.
2695-
2696- prejoin_placer->add(new w_spacer(), true);
2697-
2698- w_button* join_by_metaserver_w = new w_button("FIND INTERNET GAME");
2699- prejoin_placer->dual_add(join_by_metaserver_w, m_dialog);
2700-
2701- w_players_in_game2* players_w = new w_players_in_game2(false);
2702- postjoin_placer->dual_add(players_w, m_dialog);
2703-
2704- horizontal_placer *chat_choice_placer = new horizontal_placer;
2705-
2706- w_select_popup* chat_choice_w = new w_select_popup();
2707- chat_choice_placer->dual_add(chat_choice_w->label("chat:"), m_dialog);
2708- chat_choice_placer->dual_add(chat_choice_w, m_dialog);
2709-
2710- postjoin_placer->add(chat_choice_placer, true);
2711-
2712- w_colorful_chat* chat_history_w = new w_colorful_chat(600, 7);
2713- postjoin_placer->dual_add(chat_history_w, m_dialog);
2714-
2715- chatentry_w = new w_chat_entry(240);
2716- chatentry_w->enable_mac_roman_input();
2717-
2718- horizontal_placer *say_placer = new horizontal_placer;
2719- say_placer->dual_add(chatentry_w->label("Say:"), m_dialog);
2720- say_placer->add_flags(placeable::kFill);
2721- say_placer->dual_add(chatentry_w, m_dialog);
2722-
2723- postjoin_placer->add_flags(placeable::kFill);
2724- postjoin_placer->add(say_placer, true);
2725- postjoin_placer->add_flags();
2726-
2727- m_tabs->add(prejoin_placer, true);
2728- m_tabs->add(postjoin_placer, true);
2729- m_tabs->dual_add(join_messages_w, m_dialog);
2730- placer->add(m_tabs, true);
2731-
2732- horizontal_placer *button_placer = new horizontal_placer;
2733-
2734- w_button* join_w = new w_button("JOIN");
2735- button_placer->dual_add(join_w, m_dialog);
2736-
2737- w_button* cancel_w = new w_button("CANCEL");
2738- button_placer->dual_add(cancel_w, m_dialog);
2739-
2740- placer->add(button_placer, true);
2741-
2742- m_dialog.set_widget_placer(placer);
2743-
2744- m_cancelWidget = new ButtonWidget (cancel_w);
2745- m_joinWidget = new ButtonWidget (join_w);
2746-
2747- m_joinMetaserverWidget = new ButtonWidget (join_by_metaserver_w);
2748- m_joinAddressWidget = new EditTextWidget (hint_address_w);
2749- m_joinByAddressWidget = new ToggleWidget (hint_w);
2750-
2751- m_nameWidget = new EditTextWidget (name_w);
2752- m_colourWidget = new ColourSelectorWidget (pcolor_w);
2753- m_teamWidget = new ColourSelectorWidget (tcolor_w);
2754-
2755- m_messagesWidget = new StaticTextWidget (join_messages_w);
2756-
2757- m_pigWidget = new PlayersInGameWidget (players_w);
2758-
2759- m_chatEntryWidget = new EditTextWidget (chatentry_w);
2760- m_chatWidget = new ColorfulChatWidget(new ColorfulChatWidgetImpl(chat_history_w));
2761- m_chatChoiceWidget = new PopupSelectorWidget (chat_choice_w);
2762- }
2763-
2764- virtual void Run ()
2765- {
2766- // Load sensible palette
2767- if (SDL_GetVideoSurface()->format->BitsPerPixel == 8) {
2768- struct color_table *system_colors = build_8bit_system_color_table();
2769- assert_world_color_table(system_colors, system_colors);
2770- delete system_colors;
2771- }
2772-
2773- m_dialog.set_processing_function (boost::bind(&SdlJoinDialog::gathererSearch, this));
2774- m_dialog.run();
2775- }
2776-
2777- virtual void Stop()
2778- {
2779- if (join_result == kNetworkJoinFailedUnjoined || join_result == kNetworkJoinFailedJoined)
2780- m_dialog.quit(-1);
2781- else
2782- m_dialog.quit(0);
2783- }
2784-
2785- virtual void respondToJoinHit()
2786- {
2787- play_dialog_sound(DIALOG_OK_SOUND);
2788- m_tabs->choose_tab(1);
2789- m_dialog.draw();
2790- JoinDialog::respondToJoinHit();
2791- m_dialog.activate_widget(chatentry_w);
2792- }
2793-
2794-private:
2795- enum {
2796- iJOIN_PREJOIN_TAB,
2797- iJOIN_POSTJOIN_TAB,
2798- iJOIN_NEVERSHOW_TAB
2799- };
2800-
2801- tab_placer *m_tabs;
2802- w_chat_entry *chatentry_w;
2803- dialog m_dialog;
2804-};
2805-
2806-auto_ptr<JoinDialog>
2807-JoinDialog::Create()
2808-{
2809- return auto_ptr<JoinDialog>(new SdlJoinDialog);
2810-}
2811-
2812-class SdlSetupNetgameDialog : public SetupNetgameDialog
2813-{
2814-public:
2815- SdlSetupNetgameDialog ()
2816- {
2817- vertical_placer *placer = new vertical_placer;
2818-
2819- placer->dual_add(new w_title("SETUP NETWORK GAME"), m_dialog);
2820- placer->add(new w_spacer(), true);
2821-
2822- table_placer *table = new table_placer(2, 10);
2823- vertical_placer *left_placer = new vertical_placer;
2824- vertical_placer *right_placer = new vertical_placer;
2825-
2826- table_placer *player_table = new table_placer(2, get_theme_space(ITEM_WIDGET));
2827- player_table->col_flags(0, placeable::kAlignRight);
2828- player_table->dual_add_row(new w_static_text("Appearance"), m_dialog);
2829- w_text_entry *name_w = new w_text_entry (PREFERENCES_NAME_LENGTH, "");
2830- name_w->enable_mac_roman_input();
2831-
2832- player_table->dual_add(name_w->label("Name"), m_dialog);
2833- player_table->dual_add(name_w, m_dialog);
2834-
2835- w_player_color *pcolor_w = new w_player_color (player_preferences->color);
2836- player_table->dual_add(pcolor_w->label("Color"), m_dialog);
2837- player_table->dual_add(pcolor_w, m_dialog);
2838-
2839- w_player_color *tcolor_w = new w_player_color (player_preferences->team);
2840- player_table->dual_add(tcolor_w->label("Team"), m_dialog);
2841- player_table->dual_add(tcolor_w, m_dialog);
2842-
2843- right_placer->dual_add(new w_static_text("Network"), m_dialog);
2844- table_placer *network_table = new table_placer(2, get_theme_space(ITEM_WIDGET));
2845- network_table->col_flags(1, placeable::kAlignLeft);
2846-
2847- w_toggle *advertise_on_metaserver_w = new w_toggle (sAdvertiseGameOnMetaserver);
2848- network_table->dual_add(advertise_on_metaserver_w, m_dialog);
2849- network_table->dual_add(advertise_on_metaserver_w->label("Advertise Game on Internet"), m_dialog);
2850-
2851- w_toggle *use_upnp_w = new w_toggle (true);
2852- network_table->dual_add(use_upnp_w, m_dialog);
2853- network_table->dual_add(use_upnp_w->label("Configure UPnP Router"), m_dialog);
2854-
2855- w_toggle* realtime_audio_w = new w_toggle(network_preferences->allow_microphone);
2856- network_table->dual_add(realtime_audio_w, m_dialog);
2857- network_table->dual_add(realtime_audio_w->label("Allow Microphone"), m_dialog);
2858-
2859- w_select_popup *latency_tolerance_w = new w_select_popup();
2860- horizontal_placer *latency_placer = new horizontal_placer(get_theme_space(ITEM_WIDGET));
2861- latency_placer->dual_add(latency_tolerance_w->label("Latency Tolerance"), m_dialog);
2862- latency_placer->dual_add(latency_tolerance_w, m_dialog);
2863-
2864- network_table->add(new w_spacer(), true);
2865- network_table->add(latency_placer, true);
2866-
2867- right_placer->add(network_table, true);
2868-
2869- player_table->add_row(new w_spacer(), true);
2870- player_table->dual_add_row(new w_static_text("Game"), m_dialog);
2871-
2872- // Could eventually store this path in network_preferences somewhere, so to have separate map file
2873- // prefs for single- and multi-player.
2874- w_file_chooser* map_w = new w_file_chooser ("Choose Map", _typecode_scenario);
2875- player_table->dual_add(map_w->label("Map"), m_dialog);
2876- player_table->dual_add(map_w, m_dialog);
2877-
2878- w_select_popup* entry_point_w = new w_select_popup ();
2879- player_table->dual_add(entry_point_w->label("Level"), m_dialog);
2880- player_table->dual_add(entry_point_w, m_dialog);
2881-
2882- w_select_popup* game_type_w = new w_select_popup ();
2883- player_table->dual_add(game_type_w->label("Game Type"), m_dialog);
2884- player_table->dual_add(game_type_w, m_dialog);
2885-
2886- w_select *diff_w = new w_select(network_preferences->difficulty_level, NULL);
2887- player_table->dual_add(diff_w->label("Difficulty"), m_dialog);
2888- player_table->dual_add(diff_w, m_dialog);
2889-
2890- left_placer->add(player_table, true);
2891-
2892- network_table->add_row(new w_spacer(), true);
2893- network_table->dual_add_row(new w_static_text("Net Script"), m_dialog);
2894- w_enabling_toggle* use_netscript_w = new w_enabling_toggle (network_preferences->use_netscript);
2895- network_table->dual_add(use_netscript_w, m_dialog);
2896- network_table->dual_add(use_netscript_w->label("Use Netscript"), m_dialog);
2897-
2898- w_file_chooser* choose_script_w = new w_file_chooser ("Choose Script", _typecode_netscript);
2899- network_table->add(new w_spacer(), true);
2900- network_table->dual_add(choose_script_w, m_dialog);
2901- use_netscript_w->add_dependent_widget(choose_script_w);
2902-
2903- left_placer->add(new w_spacer(), true);
2904- table_placer *options_table = new table_placer(2, get_theme_space(ITEM_WIDGET));
2905- options_table->col_flags(1, placeable::kAlignLeft);
2906- options_table->dual_add_row(new w_static_text("Options"), m_dialog);
2907-
2908- w_toggle *aliens_w = new w_toggle((network_preferences->game_options & _monsters_replenish) != 0);
2909- options_table->dual_add(aliens_w, m_dialog);
2910- options_table->dual_add(aliens_w->label("Aliens"), m_dialog);
2911-
2912- w_toggle *live_w = new w_toggle((network_preferences->game_options & _live_network_stats) != 0);
2913- options_table->dual_add(live_w, m_dialog);
2914- options_table->dual_add(live_w->label("Live Carnage Reporting"), m_dialog);
2915-
2916- w_toggle *teams_w = new w_toggle(!(network_preferences->game_options & _force_unique_teams));
2917- options_table->dual_add(teams_w, m_dialog);
2918- options_table->dual_add(teams_w->label("Teams"), m_dialog);
2919-
2920- w_toggle *drop_w = new w_toggle(!(network_preferences->game_options & _burn_items_on_death));
2921- options_table->dual_add(drop_w, m_dialog);
2922- options_table->dual_add(drop_w->label("Dead Players Drop Items"), m_dialog);
2923-
2924- w_toggle *sensor_w = new w_toggle((network_preferences->game_options & _motion_sensor_does_not_work) != 0);
2925- options_table->dual_add(sensor_w, m_dialog);
2926- options_table->dual_add(sensor_w->label("Disable Motion Sensor"), m_dialog);
2927-
2928- w_toggle *pen_die_w = new w_toggle((network_preferences->game_options & _dying_is_penalized) != 0);
2929- options_table->dual_add(pen_die_w, m_dialog);
2930- options_table->dual_add(pen_die_w->label("Penalize Dying (10 seconds)"), m_dialog);
2931-
2932- w_toggle *pen_sui_w = new w_toggle((network_preferences->game_options & _suicide_is_penalized) != 0);
2933- options_table->dual_add(pen_sui_w, m_dialog);
2934- options_table->dual_add(pen_sui_w->label("Penalize Suicide (15 seconds)"), m_dialog);
2935-
2936- left_placer->add(options_table, true);
2937-
2938- network_table->add_row(new w_spacer(), true);
2939- network_table->dual_add_row(new w_static_text("Cheats / Extras"), m_dialog);
2940- w_toggle *zoom_w = new w_toggle(true);
2941- network_table->dual_add(zoom_w, m_dialog);
2942- network_table->dual_add(zoom_w->label("Allow Zoom"), m_dialog);
2943-
2944- w_toggle *crosshairs_w = new w_toggle(true);
2945- network_table->dual_add(crosshairs_w, m_dialog);
2946- network_table->dual_add(crosshairs_w->label("Allow Crosshairs"), m_dialog);
2947-
2948- w_toggle *overlay_w = new w_toggle(true);
2949- network_table->dual_add(overlay_w, m_dialog);
2950- network_table->dual_add(overlay_w->label("Allow Overlay Map"), m_dialog);
2951-
2952- w_toggle *lara_croft_w = new w_toggle(true);
2953- network_table->dual_add(lara_croft_w, m_dialog);
2954- network_table->dual_add(lara_croft_w->label("Allow Chase Cam"), m_dialog);
2955-
2956- w_toggle *carnage_messages_w = new w_toggle(true);
2957- network_table->dual_add(carnage_messages_w, m_dialog);
2958- network_table->dual_add(carnage_messages_w->label("Allow Carnage Messages"), m_dialog);
2959-
2960- w_toggle *saving_level_w = new w_toggle(true);
2961- network_table->dual_add(saving_level_w, m_dialog);
2962- network_table->dual_add(saving_level_w->label("Allow .save level"), m_dialog);
2963-
2964- right_placer->add(new w_spacer(), true);
2965- right_placer->dual_add(new w_static_text("Duration"), m_dialog);
2966- table_placer *limits_table = new table_placer(2, get_theme_space(ITEM_WIDGET));
2967- limits_table->col_flags(0, placeable::kAlignRight);
2968-
2969- w_select* endcondition_w = new w_select(kTimeLimit, NULL);
2970- limits_table->dual_add(endcondition_w->label("Game Ends At"), m_dialog);
2971- limits_table->dual_add(endcondition_w, m_dialog);
2972-
2973- w_number_entry* timelimit_w = new w_number_entry (network_preferences->time_limit);
2974- limits_table->dual_add(timelimit_w->label("Time Limit (minutes)"), m_dialog);
2975- limits_table->dual_add(timelimit_w, m_dialog);
2976-
2977- // The name of this widget (score limit) will be replaced by Kill Limit, Flag Capture Limit, etc.
2978- w_number_entry* scorelimit_w = new w_number_entry (network_preferences->kill_limit);
2979- limits_table->dual_add(scorelimit_w->label("Kill / Score Limit"), m_dialog);
2980- limits_table->dual_add(scorelimit_w, m_dialog);
2981- right_placer->add(limits_table, true);
2982-
2983- table->add(left_placer, true);
2984- table->add(right_placer, true);
2985-
2986- placer->add(table, true);
2987-
2988- placer->add(new w_spacer(), true);
2989-
2990- horizontal_placer *button_placer = new horizontal_placer;
2991- w_button* ok_w = new w_button ("OK");
2992- button_placer->dual_add(ok_w, m_dialog);
2993- w_button* cancel_w = new w_button ("CANCEL");
2994- button_placer->dual_add(cancel_w, m_dialog);
2995- placer->add(button_placer, true);
2996-
2997- m_dialog.set_widget_placer(placer);
2998-
2999- m_cancelWidget = new ButtonWidget (cancel_w);
3000- m_okWidget = new ButtonWidget (ok_w);
3001-
3002- m_nameWidget = new EditTextWidget (name_w);
3003- m_colourWidget = new ColourSelectorWidget (pcolor_w);
3004- m_teamWidget = new ColourSelectorWidget (tcolor_w);
3005-
3006- m_mapWidget = new FileChooserWidget (map_w);
3007-
3008- m_levelWidget = new PopupSelectorWidget (entry_point_w);
3009- m_gameTypeWidget = new PopupSelectorWidget (game_type_w);
3010- m_difficultyWidget = new SelectSelectorWidget (diff_w);
3011-
3012- m_limitTypeWidget = new SelectSelectorWidget (endcondition_w);
3013- m_timeLimitWidget = new EditNumberWidget (timelimit_w);
3014- m_scoreLimitWidget = new EditNumberWidget (scorelimit_w);
3015-
3016- m_aliensWidget = new ToggleWidget (aliens_w);
3017- m_allowTeamsWidget = new ToggleWidget (teams_w);
3018- m_deadPlayersDropItemsWidget = new ToggleWidget (drop_w);
3019- m_penalizeDeathWidget = new ToggleWidget (pen_die_w);
3020- m_penalizeSuicideWidget = new ToggleWidget (pen_sui_w);
3021-
3022- m_useMetaserverWidget = new ToggleWidget (advertise_on_metaserver_w);
3023-
3024- m_useScriptWidget = new ToggleWidget (use_netscript_w);
3025- m_scriptWidget = new FileChooserWidget (choose_script_w);
3026-
3027- m_allowMicWidget = new ToggleWidget (realtime_audio_w);
3028-
3029- m_liveCarnageWidget = new ToggleWidget (live_w);
3030- m_motionSensorWidget = new ToggleWidget (sensor_w);
3031-
3032- m_zoomWidget = new ToggleWidget (zoom_w);
3033- m_crosshairWidget = new ToggleWidget (crosshairs_w);
3034- m_overlayWidget = new ToggleWidget (overlay_w);
3035- m_laraCroftWidget = new ToggleWidget (lara_croft_w);
3036- m_carnageMessagesWidget = new ToggleWidget (carnage_messages_w);
3037- m_savingLevelWidget = new ToggleWidget (saving_level_w);
3038-
3039- m_useUpnpWidget = new ToggleWidget (use_upnp_w);
3040- m_latencyToleranceWidget = new PopupSelectorWidget(latency_tolerance_w);
3041- }
3042-
3043- virtual bool Run ()
3044- {
3045- // Load sensible palette
3046- if (SDL_GetVideoSurface()->format->BitsPerPixel == 8) {
3047- struct color_table *system_colors = build_8bit_system_color_table();
3048- assert_world_color_table(system_colors, system_colors);
3049- delete system_colors;
3050- }
3051-
3052- return (m_dialog.run () == 0);
3053- }
3054-
3055- virtual void Stop (bool result)
3056- {
3057- if (result)
3058- m_dialog.quit (0);
3059- else
3060- m_dialog.quit (-1);
3061- }
3062-
3063- virtual bool allLevelsAllowed ()
3064- {
3065- return false;
3066- }
3067-
3068- virtual void unacceptableInfo ()
3069- {
3070- play_dialog_sound (DIALOG_ERROR_SOUND);
3071- }
3072-
3073-private:
3074- dialog m_dialog;
3075-};
3076-
3077-auto_ptr<SetupNetgameDialog>
3078-SetupNetgameDialog::Create ()
3079-{
3080- return auto_ptr<SetupNetgameDialog>(new SdlSetupNetgameDialog);
3081-}
3082-
3083-// This should really be done better, I guess, but most people will never see it long enough to read it.
3084-// Currently no actual bar is drawn (just a box with message), and no effort is made to make sure all messages
3085-// will physically fit into the box. (should probably somehow force the dialog to a width and set_full_width on
3086-// the text widget. Maybe alter its justification also.)
3087-dialog* sProgressDialog = NULL;
3088-w_static_text* sProgressMessage = NULL;
3089-w_progress_bar* sProgressBar = NULL;
3090-
3091-void open_progress_dialog(size_t message_id, bool show_progress_bar)
3092-{
3093-//printf("open_progress_dialog %d\n", message_id);
3094-
3095- assert(sProgressDialog == NULL);
3096-
3097- sProgressDialog = new dialog;
3098- sProgressMessage = new w_static_text(TS_GetCString(strPROGRESS_MESSAGES, message_id));
3099- if (show_progress_bar)
3100- sProgressBar = new w_progress_bar(200);
3101-
3102- vertical_placer *placer = new vertical_placer;
3103- placer->dual_add(sProgressMessage, *sProgressDialog);
3104- if (show_progress_bar)
3105- placer->dual_add(sProgressBar, *sProgressDialog);
3106-
3107- sProgressDialog->set_widget_placer(placer);
3108-
3109- sProgressDialog->start(false);
3110-
3111-// bool done = sProgressDialog->process_events();
3112-// assert(!done);
3113-}
3114-
3115-
3116-void set_progress_dialog_message(size_t message_id)
3117-{
3118-//printf("set_progress_dialog_message %d\n", message_id);
3119- assert(sProgressMessage != NULL);
3120-
3121- sProgressMessage->set_text(TS_GetCString(strPROGRESS_MESSAGES, message_id));
3122-
3123-// bool done = sProgressDialog->process_events();
3124-
3125-// assert(!done);
3126-}
3127-
3128-void close_progress_dialog(void)
3129-{
3130-//printf("close_progress_dialog\n");
3131-
3132- assert(sProgressDialog != NULL);
3133-
3134- sProgressDialog->quit(0);
3135-
3136-// bool done = sProgressDialog->process_events();
3137-
3138-// assert(done);
3139-
3140- int result = sProgressDialog->finish(false);
3141-
3142- assert(result == 0);
3143-
3144- delete sProgressDialog;
3145-
3146- sProgressDialog = NULL;
3147- sProgressMessage = NULL;
3148- sProgressBar = NULL;
3149-}
3150-
3151-void draw_progress_bar(size_t sent, size_t total)
3152-{
3153- if (!sProgressBar) return;
3154- sProgressBar->set_progress(sent, total);
3155- sProgressDialog->draw();
3156-}
3157-
3158-void reset_progress_bar(void)
3159-{
3160- if (!sProgressBar) return;
3161- sProgressBar->set_progress(0, 1);
3162- sProgressDialog->draw();
3163-}
3164-
3165-
3166-#ifdef NETWORK_TEST_POSTGAME_DIALOG
3167-static const char* sTestingNames[] = {
3168- "Doctor Burrito",
3169- "Carnage Asada",
3170- "Bongo Bob",
3171- "The Napalm Man",
3172- "Kissy Monster",
3173- "lala",
3174- "Prof. Windsurf",
3175- "-ZED-"
3176-};
3177-
3178-// THIS ONE IS FAKE - used to test postgame report dialog without going through a game.
3179-bool network_gather(void) {
3180- short i, j;
3181- player_info thePlayerInfo;
3182- game_info theGameInfo;
3183-
3184- if(network_game_setup(&thePlayerInfo, &theGameInfo)) {
3185-
3186- for (i = 0; i < MAXIMUM_NUMBER_OF_PLAYERS; i++)
3187- {
3188- // make up a name
3189- /*int theNameLength = (local_random() % MAXIMUM_PLAYER_NAME_LENGTH) + 1;
3190- for(int n = 0; n < theNameLength; n++)
3191- players[i].name[n] = 'a' + (local_random() % ('z' - 'a'));
3192-
3193- players[i].name[theNameLength] = '\0';
3194-*/
3195- strcpy(players[i].name, sTestingNames[i]);
3196-
3197- // make up a team and color
3198- players[i].color = local_random() % 8;
3199- int theNumberOfTeams = 2 + (local_random() % 3);
3200- players[i].team = local_random() % theNumberOfTeams;
3201-
3202- (players+i)->monster_damage_taken.damage = abs(local_random()%200);
3203- (players+i)->monster_damage_taken.kills = abs(local_random()%30);
3204- (players+i)->monster_damage_given.damage = abs(local_random()%200);
3205- (players+i)->monster_damage_given.kills = abs(local_random()%30);
3206-
3207- players[i].netgame_parameters[0] = local_random() % 200;
3208- players[i].netgame_parameters[1] = local_random() % 200;
3209-
3210- for (j = 0; j < MAXIMUM_NUMBER_OF_PLAYERS; j++)
3211- {
3212- (players+i)->damage_taken[j].damage = abs(local_random()%200);
3213- (players+i)->damage_taken[j].kills = abs(local_random()%6);
3214- }
3215- }
3216-
3217- dynamic_world->player_count = MAXIMUM_NUMBER_OF_PLAYERS;
3218-
3219- game_data& game_information = dynamic_world->game_information;
3220- game_info* network_game_info = &theGameInfo;
3221-
3222- game_information.game_time_remaining= network_game_info->time_limit;
3223- game_information.kill_limit= network_game_info->kill_limit;
3224- game_information.game_type= network_game_info->net_game_type;
3225- game_information.game_options= network_game_info->game_options;
3226- game_information.initial_random_seed= network_game_info->initial_random_seed;
3227- game_information.difficulty_level= network_game_info->difficulty_level;
3228-
3229- display_net_game_stats();
3230- } // if setup box was OK'd
3231- return false;
3232-}
3233-#endif // NETWORK_TEST_POSTGAME_DIALOG
3234-
3235-
3236-
3237-
3238-
3239-#ifdef NETWORK_TEST_MICROPHONE_LOCALLY
3240-static void
3241-respond_to_microphone_toggle(w_select* inWidget) {
3242- set_network_microphone_state(inWidget->get_selection() != 0);
3243-}
3244-
3245-bool
3246-network_gather(bool) {
3247- open_network_speaker();
3248- open_network_microphone();
3249-
3250- dialog d;
3251-
3252- d.add(new w_title("TEST MICROPHONE"));
3253-
3254- w_toggle* onoff_w = new w_toggle("Active", 0);
3255- onoff_w->set_selection_changed_callback(respond_to_microphone_toggle);
3256- d.add(onoff_w);
3257-
3258- d.add(new w_button("DONE", dialog_ok, &d));
3259-
3260- d.run();
3261-
3262- close_network_microphone();
3263- close_network_speaker();
3264-
3265- return false;
3266-}
3267-#endif
3268-
3269-#endif // !defined(DISABLE_NETWORKING)
3270-
1+/*
2+NETWORK_DIALOGS.C (network_dialogs.cpp)
3+
4+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5+ and the "Aleph One" developers.
6+
7+ This program is free software; you can redistribute it and/or modify
8+ it under the terms of the GNU General Public License as published by
9+ the Free Software Foundation; either version 2 of the License, or
10+ (at your option) any later version.
11+
12+ This program is distributed in the hope that it will be useful,
13+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ GNU General Public License for more details.
16+
17+ This license is contained in the file "COPYING",
18+ which is included with this source code; it is available online at
19+ http://www.gnu.org/licenses/gpl.html
20+
21+Monday, June 20, 1994 12:36:39 PM
22+Thursday, June 30, 1994 6:27:43 PM (ajr)
23+ Made some UPPs for the dialogs
24+Tuesday, July 19, 1994 7:16:54 PM (ajr)
25+ fixed up dialogs. added dialog for net game stats
26+Tuesday, September 6, 1994 3:50:01 PM (ajr)
27+ recently, the net game stats dialog has been rewritten to be a graph and some other
28+ stuff has been cleaned up a bit.
29+
30+Jan 30, 2000 (Loren Petrich):
31+ Added some typecasts
32+
33+Feb. 4, 2000 (Loren Petrich):
34+ Changed halt() to assert(false) for better debugging
35+
36+Apr 30, 2000 (Loren Petrich):
37+ Did change for getting default player name from outside
38+
39+Jul 1, 2000 (Loren Petrich):
40+ Added Benad's netgame stuff
41+
42+Sept-Nov 2001 (Woody Zenfell):
43+ This file was split into Mac-specific code (in network_dialogs_macintosh.cpp) and
44+ shared code (this file, network_dialogs.cpp).
45+
46+Feb 27, 2002 (Br'fin (Jeremy Parsons)):
47+ Moved shared SDL hint address info here from network_dialogs_sdl.cpp
48+ Reworked #ifdef mac to #if !defined(HAVE_SDL_NET)
49+
50+Mar 1, 2002 (Woody Zenfell):
51+ Reworked SDL dialog-box level-choosing code; interface is different now (can't use
52+ get_selection_control_value(), gives and takes level numbers instead). SDL prefs now store
53+ level number instead of menu index. Using #ifdef mac to decide which interface to use.
54+
55+Mar 8, 2002 (Woody Zenfell):
56+ Network microphone UI is now handled for SDL version as well (since SDL has net-audio now)
57+
58+Feb 12, 2003 (Woody Zenfell):
59+ Support for resuming netgames (optionally get game options from saved-game not prefs, optionally don't save options into prefs)
60+
61+Apr 10, 2003 (Woody Zenfell):
62+ Join hinting and autogathering have Preferences entries now
63+
64+ August 27, 2003 (Woody Zenfell):
65+ Reworked netscript selection stuff to use Preferences and to be more cross-platform
66+ and more consistent with other dialog code
67+*/
68+
69+#if !defined(DISABLE_NETWORKING)
70+
71+#include "cseries.h"
72+#include "map.h"
73+#include "shell.h"
74+#include "preferences.h"
75+#include "network.h"
76+#include "network_dialogs.h"
77+#include "network_games.h"
78+#include "player.h" // ZZZ: for MAXIMUM_NUMBER_OF_PLAYERS, for reassign_player_colors
79+#include "metaserver_dialogs.h" // GameAvailableMetaserverAnnouncer
80+#include "wad.h" // jkvw: for read_wad_file_checksum
81+#include "game_wad.h" // get_map_file
82+
83+#include <map>
84+
85+// For LAN netgame location services
86+#include <sstream>
87+#include "network_private.h" // actually just need "network_dialogs_private.h"
88+#include "SSLP_API.h"
89+
90+#ifdef USES_NIBS
91+ #include "NibsUiHelpers.h"
92+#endif
93+
94+// for game types...
95+#include "network_dialogs.h"
96+#include "TextStrings.h"
97+
98+#include "network_dialog_widgets_sdl.h"
99+#include "screen.h"
100+#include "SoundManager.h"
101+#include "progress.h"
102+
103+
104+extern void NetRetargetJoinAttempts(const IPaddress* inAddress);
105+
106+
107+// Metaserver Globals
108+// We can't construct a MetaserverClient until MetaserverClient::s_instances is initialised.
109+MetaserverClient* gMetaserverClient = NULL;
110+
111+// Chat History Globals
112+ChatHistory gMetaserverChatHistory;
113+ChatHistory gPregameChatHistory;
114+
115+
116+
117+
118+////////////////////////////////////////////////////////////////////////////////
119+// LAN game-location services support
120+
121+static const string
122+get_sslp_service_type()
123+{
124+ return kNetworkSetupProtocolID;
125+}
126+
127+
128+GathererAvailableAnnouncer::GathererAvailableAnnouncer()
129+{
130+ strncpy(mServiceInstance.sslps_type, get_sslp_service_type().c_str(), SSLP_MAX_TYPE_LENGTH);
131+ strncpy(mServiceInstance.sslps_name, "Boomer", SSLP_MAX_NAME_LENGTH);
132+ memset(&(mServiceInstance.sslps_address), '\0', sizeof(mServiceInstance.sslps_address));
133+ SSLP_Allow_Service_Discovery(&mServiceInstance);
134+}
135+
136+GathererAvailableAnnouncer::~GathererAvailableAnnouncer()
137+{
138+ SSLP_Disallow_Service_Discovery(&mServiceInstance);
139+}
140+
141+void // static
142+GathererAvailableAnnouncer::pump()
143+{
144+ SSLP_Pump();
145+}
146+
147+
148+JoinerSeekingGathererAnnouncer::JoinerSeekingGathererAnnouncer(bool shouldSeek) : mShouldSeek(shouldSeek)
149+{
150+ if(mShouldSeek)
151+ SSLP_Locate_Service_Instances(
152+ get_sslp_service_type().c_str(),
153+ found_gatherer_callback,
154+ lost_gatherer_callback,
155+ found_gatherer_callback
156+ );
157+}
158+
159+JoinerSeekingGathererAnnouncer::~JoinerSeekingGathererAnnouncer()
160+{
161+ if(mShouldSeek)
162+ SSLP_Stop_Locating_Service_Instances(get_sslp_service_type().c_str());
163+}
164+
165+void // static
166+JoinerSeekingGathererAnnouncer::pump()
167+{
168+ SSLP_Pump();
169+}
170+
171+void // static
172+JoinerSeekingGathererAnnouncer::found_gatherer_callback(const SSLP_ServiceInstance* instance)
173+{
174+ NetRetargetJoinAttempts(&instance->sslps_address);
175+}
176+
177+void // static
178+JoinerSeekingGathererAnnouncer::lost_gatherer_callback(const SSLP_ServiceInstance* instance)
179+{
180+ NetRetargetJoinAttempts(NULL);
181+}
182+
183+
184+/****************************************************
185+ *
186+ * Shared Network Gather Dialog Code
187+ *
188+ ****************************************************/
189+
190+bool network_gather(bool inResumingGame)
191+{
192+ bool successful= false;
193+ game_info myGameInfo;
194+ player_info myPlayerInfo;
195+ bool advertiseOnMetaserver = false;
196+
197+ show_cursor(); // JTP: Hidden one way or another
198+ if (network_game_setup(&myPlayerInfo, &myGameInfo, inResumingGame, advertiseOnMetaserver))
199+ {
200+ myPlayerInfo.desired_color= myPlayerInfo.color;
201+ memcpy(myPlayerInfo.long_serial_number, serial_preferences->long_serial_number, LONG_SERIAL_NUMBER_LENGTH);
202+
203+ auto_ptr<GameAvailableMetaserverAnnouncer> metaserverAnnouncer;
204+ if(NetEnter())
205+ {
206+ bool gather_dialog_result;
207+
208+ if(NetGather(&myGameInfo, sizeof(game_info), (void*) &myPlayerInfo,
209+ sizeof(myPlayerInfo), inResumingGame))
210+ {
211+ GathererAvailableAnnouncer announcer;
212+
213+ if (!gMetaserverClient) gMetaserverClient = new MetaserverClient ();
214+
215+ if(advertiseOnMetaserver)
216+ {
217+ try
218+ {
219+ metaserverAnnouncer.reset(new GameAvailableMetaserverAnnouncer(myGameInfo));
220+ }
221+ catch (const MetaserverClient::LoginDeniedException& e)
222+ {
223+ char message[1024];
224+ if (e.code() == MetaserverClient::LoginDeniedException::BadUserOrPassword)
225+ {
226+ strcpy(message, "ログイン拒否:ユーザー名もしくは、パスワードが間違っています。インターネット上にゲームの募集広告を出すことができませんでした。");
227+ }
228+ else if (e.code() == MetaserverClient::LoginDeniedException::UserAlreadyLoggedIn)
229+ {
230+ strcpy(message, "ログイン拒否:そのユーザは、すでにログインしています。インターネット上にゲームの募集広告を出すことができませんでした。");
231+ }
232+ else if (e.code() == MetaserverClient::LoginDeniedException::AccountAlreadyLoggedIn)
233+ {
234+ strcpy(message, "ログイン拒否:そのアカウントは、すでにログインしています。インターネット上にゲームの募集広告を出すことができませんでした。");
235+ }
236+ else if (e.code() == MetaserverClient::LoginDeniedException::RoomFull)
237+ {
238+ strcpy(message, "ログイン拒否:ルームが満室です!インターネット上にゲームの募集広告を出すことができませんでした。");
239+ }
240+ else if (e.code() == MetaserverClient::LoginDeniedException::AccountLocked)
241+ {
242+ strcpy(message, "ログイン拒否:そのアカウントはロックされています。インターネット上にゲームの募集広告を出すことができませんでした。");
243+ }
244+ else
245+ {
246+ sprintf(message, "インターネット上のサーバーへの接続時に問題が発生しました:%s お手数ですが、しばらくたってからもう一度やり直してください。", e.what());
247+ }
248+
249+ alert_user(message, 0);
250+ }
251+ catch (const MetaserverClient::ServerConnectException&)
252+ {
253+ alert_user(infoError, strNETWORK_ERRORS, netWarnCouldNotAdvertiseOnMetaserver, 0);
254+ }
255+ }
256+
257+ gather_dialog_result = GatherDialog::Create()->GatherNetworkGameByRunning();
258+
259+ } else {
260+ gather_dialog_result = false;
261+ }
262+
263+ if (gather_dialog_result) {
264+ NetDoneGathering();
265+ if (advertiseOnMetaserver)
266+ {
267+ metaserverAnnouncer->Start(myGameInfo.time_limit);
268+ gMetaserverClient->setMode(1);
269+ gMetaserverClient->pump();
270+ }
271+ successful= true;
272+ }
273+ else
274+ {
275+ delete gMetaserverClient;
276+ gMetaserverClient = new MetaserverClient();
277+ NetCancelGather();
278+ NetExit();
279+ }
280+ } else {
281+ /* error correction handled in the network code now.. */
282+ }
283+ }
284+
285+ hide_cursor();
286+ return successful;
287+}
288+
289+GatherDialog::GatherDialog() { }
290+
291+GatherDialog::~GatherDialog()
292+{
293+ delete m_cancelWidget;
294+ delete m_startWidget;
295+ delete m_autogatherWidget;
296+ delete m_ungatheredWidget;
297+ delete m_pigWidget;
298+ delete m_chatEntryWidget;
299+ delete m_chatWidget;
300+ delete m_chatChoiceWidget;
301+
302+ gMetaserverClient->associateNotificationAdapter(0);
303+
304+}
305+
306+bool GatherDialog::GatherNetworkGameByRunning ()
307+{
308+ vector<string> chat_choice_labels;
309+ chat_choice_labels.push_back ("参加者と");
310+ chat_choice_labels.push_back ("インターネットのプレイヤーと");
311+ m_chatChoiceWidget->set_labels (chat_choice_labels);
312+
313+ m_cancelWidget->set_callback(boost::bind(&GatherDialog::Stop, this, false));
314+ m_startWidget->set_callback(boost::bind(&GatherDialog::StartGameHit, this));
315+ m_ungatheredWidget->SetItemSelectedCallback(boost::bind(&GatherDialog::gathered_player, this, _1));
316+
317+ m_startWidget->deactivate ();
318+
319+ NetSetGatherCallbacks(this);
320+
321+ m_chatChoiceWidget->set_callback(boost::bind(&GatherDialog::chatChoiceHit, this));
322+ m_chatEntryWidget->set_callback(boost::bind(&GatherDialog::chatTextEntered, this, _1));
323+
324+ gPregameChatHistory.clear ();
325+ NetSetChatCallbacks(this);
326+
327+ BoolPref autoGatherPref (network_preferences->autogather);
328+ Binder<bool> binder (m_autogatherWidget, &autoGatherPref);
329+ binder.migrate_second_to_first ();
330+
331+ if (gMetaserverClient->isConnected ()) {
332+ gMetaserverClient->associateNotificationAdapter(this);
333+ m_chatChoiceWidget->set_value (kMetaserverChat);
334+ gMetaserverChatHistory.clear ();
335+ m_chatWidget->attachHistory (&gMetaserverChatHistory);
336+ } else {
337+ m_chatChoiceWidget->deactivate ();
338+ m_chatChoiceWidget->set_value (kPregameChat);
339+ gMetaserverChatHistory.clear ();
340+ m_chatWidget->attachHistory (&gPregameChatHistory);
341+ }
342+
343+ bool result = Run ();
344+
345+ binder.migrate_first_to_second ();
346+
347+ // Save autogather setting, even if we cancel the dialog
348+ write_preferences ();
349+
350+ return result;
351+}
352+
353+void GatherDialog::idle ()
354+{
355+ MetaserverClient::pumpAll();
356+
357+ prospective_joiner_info info;
358+ if (player_search(info)) {
359+ m_ungathered_players[info.stream_id] = info;
360+ update_ungathered_widget ();
361+ }
362+
363+ if (m_autogatherWidget->get_value ()) {
364+ map<int, prospective_joiner_info>::iterator it;
365+ it = m_ungathered_players.begin ();
366+ while (it != m_ungathered_players.end () && NetGetNumberOfPlayers() < MAXIMUM_NUMBER_OF_PLAYERS) {
367+ gathered_player ((it++)->second);
368+ }
369+ }
370+}
371+
372+void GatherDialog::update_ungathered_widget ()
373+{
374+ vector<prospective_joiner_info> temp;
375+
376+ for (map<int, prospective_joiner_info>::iterator it = m_ungathered_players.begin (); it != m_ungathered_players.end (); ++it)
377+ temp.push_back ((*it).second);
378+
379+ m_ungatheredWidget->SetItems (temp);
380+}
381+
382+bool GatherDialog::player_search (prospective_joiner_info& player)
383+{
384+ GathererAvailableAnnouncer::pump();
385+
386+ if (NetCheckForNewJoiner(player)) {
387+ m_ungathered_players[player.stream_id] = player;
388+ update_ungathered_widget ();
389+ return true;
390+ } else
391+ return false;
392+}
393+
394+bool GatherDialog::gathered_player (const prospective_joiner_info& player)
395+{
396+ if (NetGetNumberOfPlayers() >= MAXIMUM_NUMBER_OF_PLAYERS) return false;
397+ int theGatherPlayerResult = NetGatherPlayer(player, reassign_player_colors);
398+
399+ if (theGatherPlayerResult != kGatherPlayerFailed) {
400+ m_ungathered_players.erase (m_ungathered_players.find (player.stream_id));
401+ update_ungathered_widget ();
402+ return true;
403+ } else
404+ return false;
405+}
406+
407+void GatherDialog::StartGameHit ()
408+{
409+ for (map<int, prospective_joiner_info>::iterator it = m_ungathered_players.begin (); it != m_ungathered_players.end (); ++it)
410+ NetHandleUngatheredPlayer ((*it).second);
411+
412+ Stop (true);
413+}
414+
415+void GatherDialog::JoinSucceeded(const prospective_joiner_info* player)
416+{
417+ if (NetGetNumberOfPlayers () > 1)
418+ m_startWidget->activate ();
419+
420+ m_pigWidget->redraw ();
421+
422+ if (gMetaserverClient->isConnected())
423+ {
424+ gMetaserverClient->announcePlayersInGame(NetGetNumberOfPlayers());
425+ }
426+}
427+
428+void GatherDialog::JoiningPlayerDropped(const prospective_joiner_info* player)
429+{
430+ map<int, prospective_joiner_info>::iterator it = m_ungathered_players.find (player->stream_id);
431+ if (it != m_ungathered_players.end ())
432+ m_ungathered_players.erase (it);
433+
434+ update_ungathered_widget ();
435+}
436+
437+void GatherDialog::JoinedPlayerDropped(const prospective_joiner_info* player)
438+{
439+ if (NetGetNumberOfPlayers () < 2)
440+ m_startWidget->deactivate ();
441+
442+ m_pigWidget->redraw ();
443+
444+ if (gMetaserverClient->isConnected())
445+ {
446+ gMetaserverClient->announcePlayersInGame(NetGetNumberOfPlayers());
447+ }
448+}
449+
450+void GatherDialog::JoinedPlayerChanged(const prospective_joiner_info* player)
451+{
452+ m_pigWidget->redraw ();
453+}
454+
455+void GatherDialog::sendChat ()
456+{
457+ string message = m_chatEntryWidget->get_text();
458+
459+#ifndef SDL
460+ // It's just a little semantic difference, really. :)
461+ message = string(message.data (), message.length () - 1); // lose the last character, i.e. '\r'.
462+#endif
463+
464+ if (m_chatChoiceWidget->get_value () == kMetaserverChat)
465+ gMetaserverClient->sendChatMessage(message);
466+ else
467+ SendChatMessage(message);
468+
469+ m_chatEntryWidget->set_text(string());
470+}
471+
472+void GatherDialog::chatTextEntered (char character)
473+{
474+ if (character == '\r')
475+ sendChat();
476+}
477+
478+void GatherDialog::chatChoiceHit ()
479+{
480+ if (m_chatChoiceWidget->get_value () == kPregameChat)
481+ m_chatWidget->attachHistory (&gPregameChatHistory);
482+ else
483+ m_chatWidget->attachHistory (&gMetaserverChatHistory);
484+}
485+
486+void GatherDialog::ReceivedMessageFromPlayer(
487+ const char *player_name,
488+ const char *message)
489+{
490+ ColoredChatEntry e;
491+ e.type = ColoredChatEntry::ChatMessage;
492+ e.sender = player_name;
493+ e.message = message;
494+
495+ gPregameChatHistory.append(e);
496+}
497+
498+/****************************************************
499+ *
500+ * Shared Network Join Dialog Code
501+ *
502+ ****************************************************/
503+
504+int network_join(void)
505+{
506+ int join_dialog_result;
507+
508+ show_cursor(); // Hidden one way or another
509+
510+ /* If we can enter the network... */
511+ if(NetEnter())
512+ {
513+
514+ join_dialog_result = JoinDialog::Create()->JoinNetworkGameByRunning();
515+
516+ if (join_dialog_result == kNetworkJoinedNewGame || join_dialog_result == kNetworkJoinedResumeGame)
517+ {
518+ write_preferences ();
519+
520+ game_info* myGameInfo= (game_info *)NetGetGameData();
521+ NetSetInitialParameters(myGameInfo->initial_updates_per_packet, myGameInfo->initial_update_latency);
522+ if (gMetaserverClient && gMetaserverClient->isConnected())
523+ {
524+ gMetaserverClient->setMode(1);
525+ gMetaserverClient->pump();
526+ }
527+ }
528+ else
529+ {
530+ read_preferences ();
531+
532+ if (join_dialog_result == kNetworkJoinFailedJoined)
533+ NetCancelJoin();
534+
535+ NetExit();
536+ }
537+ } else { // Failed NetEnter
538+ join_dialog_result = kNetworkJoinFailedUnjoined;
539+ }
540+
541+ hide_cursor();
542+ return join_dialog_result;
543+}
544+
545+JoinDialog::JoinDialog() : got_gathered(false), skipToMetaserver(false)
546+ { if (!gMetaserverClient) gMetaserverClient = new MetaserverClient (); }
547+
548+JoinDialog::~JoinDialog ()
549+{
550+ gMetaserverClient->associateNotificationAdapter(0);
551+
552+ delete m_cancelWidget;
553+ delete m_joinWidget;
554+ delete m_joinMetaserverWidget;
555+ delete m_joinAddressWidget;
556+ delete m_joinByAddressWidget;
557+ delete m_nameWidget;
558+ delete m_colourWidget;
559+ delete m_teamWidget;
560+ delete m_messagesWidget;
561+ delete m_pigWidget;
562+ delete m_chatEntryWidget;
563+ delete m_chatChoiceWidget;
564+ delete m_chatWidget;
565+}
566+
567+const int JoinDialog::JoinNetworkGameByRunning ()
568+{
569+ join_result = kNetworkJoinFailedUnjoined;
570+
571+ vector<string> chat_choice_labels;
572+ chat_choice_labels.push_back ("参加者/募集者と");
573+ chat_choice_labels.push_back ("インターネットのプレイヤーと");
574+ m_chatChoiceWidget->set_labels (chat_choice_labels);
575+
576+ m_colourWidget->set_labels (kTeamColorsStringSetID);
577+ m_teamWidget->set_labels (kTeamColorsStringSetID);
578+
579+ m_cancelWidget->set_callback(boost::bind(&JoinDialog::Stop, this));
580+ m_joinWidget->set_callback(boost::bind(&JoinDialog::attemptJoin, this));
581+ m_joinMetaserverWidget->set_callback(boost::bind(&JoinDialog::getJoinAddressFromMetaserver, this));
582+
583+ m_chatChoiceWidget->set_value (kPregameChat);
584+ m_chatChoiceWidget->deactivate ();
585+ m_chatEntryWidget->deactivate ();
586+ m_chatChoiceWidget->set_callback(boost::bind(&JoinDialog::chatChoiceHit, this));
587+ m_chatEntryWidget->set_callback(boost::bind(&JoinDialog::chatTextEntered, this, _1));
588+
589+ getpstr(ptemporary, strJOIN_DIALOG_MESSAGES, _join_dialog_welcome_string);
590+ m_messagesWidget->set_text(pstring_to_string(ptemporary));
591+
592+ CStringPref joinAddressPref (network_preferences->join_address, 255);
593+ binders.insert<std::string> (m_joinAddressWidget, &joinAddressPref);
594+ BoolPref joinByAddressPref (network_preferences->join_by_address);
595+ binders.insert<bool> (m_joinByAddressWidget, &joinByAddressPref);
596+
597+ PStringPref namePref (player_preferences->name, MAX_NET_PLAYER_NAME_LENGTH);
598+ binders.insert<std::string> (m_nameWidget, &namePref);
599+ Int16Pref colourPref (player_preferences->color);
600+ binders.insert<int> (m_colourWidget, &colourPref);
601+ Int16Pref teamPref (player_preferences->team);
602+ binders.insert<int> (m_teamWidget, &teamPref);
603+
604+ binders.migrate_all_second_to_first ();
605+
606+ Run ();
607+
608+ binders.migrate_all_first_to_second ();
609+
610+ return join_result;
611+
612+ // We'll choose to use old prefs or new changes when we return into network_join
613+}
614+
615+void JoinDialog::respondToJoinHit()
616+{
617+ gPregameChatHistory.clear ();
618+ if (gMetaserverClient->isConnected ()) {
619+ m_chatChoiceWidget->activate ();
620+ m_chatChoiceWidget->set_value (kMetaserverChat);
621+ gMetaserverClient->associateNotificationAdapter(this);
622+ m_chatWidget->attachHistory (&gMetaserverChatHistory);
623+ } else {
624+ m_chatWidget->attachHistory (&gPregameChatHistory);
625+ }
626+ m_chatEntryWidget->activate ();
627+ NetSetChatCallbacks(this);
628+}
629+
630+void JoinDialog::attemptJoin ()
631+{
632+ char* hintString = NULL;
633+
634+ if(m_joinByAddressWidget->get_value()) {
635+ hintString = new char[256];
636+ copy_string_to_cstring (m_joinAddressWidget->get_text (), hintString);
637+ }
638+
639+ player_info myPlayerInfo;
640+ copy_string_to_pstring (m_nameWidget->get_text (), myPlayerInfo.name, MAX_NET_PLAYER_NAME_LENGTH);
641+ myPlayerInfo.team = m_teamWidget->get_value ();
642+ myPlayerInfo.desired_color = m_colourWidget->get_value ();
643+
644+ // jkvw: It may look like we're passing our player name into NetGameJoin,
645+ // but network code will later draw the name directly from prefs.
646+ binders.migrate_all_first_to_second ();
647+ bool result = NetGameJoin((void *) &myPlayerInfo, sizeof(myPlayerInfo), hintString);
648+
649+ if (hintString)
650+ delete [] hintString;
651+
652+ if (result) {
653+ m_nameWidget->deactivate ();
654+ m_teamWidget->deactivate ();
655+ m_colourWidget->deactivate ();
656+
657+ m_joinAddressWidget->deactivate ();
658+ m_joinByAddressWidget->deactivate ();
659+ m_joinWidget->deactivate ();
660+ m_joinMetaserverWidget->deactivate ();
661+
662+ getpstr(ptemporary, strJOIN_DIALOG_MESSAGES, _join_dialog_waiting_string);
663+ m_messagesWidget->set_text(pstring_to_string(ptemporary));
664+
665+ if (!m_joinByAddressWidget->get_value()) {
666+ join_announcer.reset(new JoinerSeekingGathererAnnouncer(true));
667+ }
668+
669+ respondToJoinHit ();
670+ }
671+}
672+
673+void JoinDialog::gathererSearch ()
674+{
675+ if (skipToMetaserver)
676+ {
677+ skipToMetaserver = false;
678+ getJoinAddressFromMetaserver();
679+ }
680+
681+ JoinerSeekingGathererAnnouncer::pump();
682+ MetaserverClient::pumpAll();
683+
684+ switch (NetUpdateJoinState())
685+ {
686+ case NONE: // haven't Joined yet.
687+ join_result = kNetworkJoinFailedUnjoined;
688+ break;
689+
690+ case netConnecting:
691+ case netJoining:
692+ join_result = kNetworkJoinFailedJoined;
693+ break;
694+
695+ case netCancelled: // the server cancelled the game; force bail
696+ join_result = kNetworkJoinFailedJoined;
697+ Stop ();
698+ break;
699+
700+ case netWaiting:
701+ join_result = kNetworkJoinFailedJoined;
702+ break;
703+
704+ case netStartingUp: // the game is starting up (we have the network topography)
705+ join_result = kNetworkJoinedNewGame;
706+ Stop ();
707+ break;
708+
709+ case netStartingResumeGame: // the game is starting up a resume game (we have the network topography)
710+ join_result = kNetworkJoinedResumeGame;
711+ Stop ();
712+ break;
713+
714+ case netPlayerChanged:
715+ case netPlayerAdded:
716+ case netPlayerDropped:
717+ if (!got_gathered) {
718+ // Do this stuff only once - when we become gathered
719+ got_gathered = true;
720+ char joinMessage[256];
721+ game_info *info= (game_info *)NetGetGameData();
722+ get_network_joined_message(joinMessage, info->net_game_type);
723+ m_messagesWidget->set_text(std::string(joinMessage));
724+ m_teamWidget->activate ();
725+ m_colourWidget->activate ();
726+ m_colourWidget->set_callback(boost::bind(&JoinDialog::changeColours, this));
727+ m_teamWidget->set_callback(boost::bind(&JoinDialog::changeColours, this));
728+ }
729+ m_pigWidget->redraw ();
730+ join_result = kNetworkJoinFailedJoined;
731+ break;
732+
733+ case netJoinErrorOccurred:
734+ join_result = kNetworkJoinFailedJoined;
735+ Stop ();
736+ break;
737+
738+ case netChatMessageReceived:
739+ // Show chat message
740+ join_result = kNetworkJoinFailedJoined;
741+ break;
742+
743+ default:
744+ assert(false);
745+ }
746+}
747+
748+void JoinDialog::changeColours ()
749+{
750+ int requested_colour = m_colourWidget->get_value ();
751+ int requested_team = m_teamWidget->get_value ();
752+ NetChangeColors(requested_colour, requested_team);
753+}
754+
755+void JoinDialog::getJoinAddressFromMetaserver ()
756+{
757+ // jkvw: The network metaserver code will draw our name and colour info directly from prefs.
758+ binders.migrate_all_first_to_second ();
759+
760+ try
761+ {
762+ IPaddress result = run_network_metaserver_ui();
763+ if(result.host != 0)
764+ {
765+ uint8* hostBytes = reinterpret_cast<uint8*>(&(result.host));
766+ char buffer[16];
767+ snprintf(buffer, sizeof(buffer), "%u.%u.%u.%u", hostBytes[0], hostBytes[1], hostBytes[2], hostBytes[3]);
768+ m_joinByAddressWidget->set_value (true);
769+ m_joinAddressWidget->set_text (string (buffer));
770+ m_joinWidget->push ();
771+ }
772+ }
773+ catch (const MetaserverClient::LoginDeniedException& e)
774+ {
775+ char message[1024];
776+ if (e.code() == MetaserverClient::LoginDeniedException::BadUserOrPassword)
777+ {
778+ strcpy(message, "ログイン拒否:不正なユーザ名もしくは、パスワードです。");
779+ }
780+ else if (e.code() == MetaserverClient::LoginDeniedException::UserAlreadyLoggedIn)
781+ {
782+ strcpy(message, "ログイン拒否:そのユーザは、すでにログインしています。");
783+ }
784+ else if (e.code() == MetaserverClient::LoginDeniedException::AccountAlreadyLoggedIn)
785+ {
786+ strcpy(message, "ログイン拒否:そのアカウントは、すでにログインしています。");
787+ }
788+ else if (e.code() == MetaserverClient::LoginDeniedException::RoomFull)
789+ {
790+ strcpy(message, "ログイン拒否:ルームが満席!?");
791+ }
792+ else if (e.code() == MetaserverClient::LoginDeniedException::AccountLocked)
793+ {
794+ strcpy(message, "ログイン拒否:あなたのアカウントはロックされています。");
795+ }
796+ else
797+ {
798+ sprintf(message, "インターネット上のサーバーへの接続時に問題が発生しました:%s お手数ですが、しばらくたってからもう一度やり直してください。", e.what());
799+ }
800+
801+ alert_user(message, 0);
802+ }
803+ catch (const MetaserverClient::ServerConnectException&)
804+ {
805+ alert_user(infoError, strNETWORK_ERRORS, netErrMetaserverConnectionFailure, 0);
806+ }
807+}
808+
809+void JoinDialog::sendChat ()
810+{
811+ string message = m_chatEntryWidget->get_text();
812+
813+ // jkvw: Why don't we need NIBs to strip a trailing \r here, like in
814+ // GatherDialog::sendChat and MetaserverClientUi::sendChat?
815+ // Good question, burrito.
816+
817+ if (m_chatChoiceWidget->get_value () == kMetaserverChat)
818+ gMetaserverClient->sendChatMessage(message);
819+ else
820+ SendChatMessage(message);
821+
822+ m_chatEntryWidget->set_text(string());
823+}
824+
825+void JoinDialog::chatTextEntered (char character)
826+{
827+ if (character == '\r')
828+ sendChat();
829+}
830+
831+void JoinDialog::chatChoiceHit ()
832+{
833+ if (m_chatChoiceWidget->get_value () == kPregameChat)
834+ m_chatWidget->attachHistory (&gPregameChatHistory);
835+ else
836+ m_chatWidget->attachHistory (&gMetaserverChatHistory);
837+}
838+
839+void JoinDialog::ReceivedMessageFromPlayer(const char *player_name, const char *message)
840+{
841+ ColoredChatEntry e;
842+ e.type = ColoredChatEntry::ChatMessage;
843+ e.sender = player_name;
844+ e.message = message;
845+
846+ gPregameChatHistory.append(e);
847+}
848+
849+/****************************************************
850+ *
851+ * Shared Network Game Setup Code
852+ *
853+ ****************************************************/
854+
855+bool network_game_setup(
856+ player_info *player_information,
857+ game_info *game_information,
858+ bool ResumingGame,
859+ bool& outAdvertiseGameOnMetaserver)
860+{
861+ if (SetupNetgameDialog::Create ()->SetupNetworkGameByRunning (player_information, game_information, ResumingGame, outAdvertiseGameOnMetaserver)) {
862+ write_preferences ();
863+ return true;
864+ } else {
865+ read_preferences ();
866+ load_environment_from_preferences(); // In case user changed map
867+ return false;
868+ }
869+}
870+
871+// converts menu index <---> level index
872+class LevelInt16Pref : public Bindable<int>
873+{
874+public:
875+ LevelInt16Pref (int16& pref, int& gametype) : m_pref (pref), m_gametype (gametype) {}
876+
877+ virtual int bind_export ()
878+ {
879+ int32 entry_flags = get_entry_point_flags_for_game_type (m_gametype);
880+ return level_index_to_menu_index (m_pref, entry_flags);
881+ }
882+
883+ virtual void bind_import (int value)
884+ {
885+ int32 entry_flags = get_entry_point_flags_for_game_type (m_gametype);
886+ m_pref = menu_index_to_level_index (value, entry_flags);
887+ }
888+
889+protected:
890+ int16& m_pref;
891+ int& m_gametype;
892+};
893+
894+class TimerInt32Pref : public Bindable<int>
895+{
896+public:
897+ TimerInt32Pref (int32& pref) : m_pref (pref) {}
898+
899+ virtual int bind_export ()
900+ {
901+ return m_pref / (60 * TICKS_PER_SECOND);
902+ }
903+
904+ virtual void bind_import (int value)
905+ {
906+ m_pref = static_cast<int32>(value) * (60 * TICKS_PER_SECOND);
907+ }
908+
909+protected:
910+ int32& m_pref;
911+};
912+
913+// limit type is represented by a bool and a bit in game options
914+class LimitTypePref : public Bindable<int>
915+{
916+public:
917+ LimitTypePref (bool& untimed_pref, uint16& options_pref, uint16 kill_limit_mask)
918+ : m_untimed (untimed_pref)
919+ , m_kill_limited (options_pref, kill_limit_mask)
920+ {}
921+
922+ virtual int bind_export ()
923+ {
924+ if (!m_untimed.bind_export ())
925+ return duration_time_limit;
926+ else if (m_kill_limited.bind_export ())
927+ return duration_kill_limit;
928+ else
929+ return duration_no_time_limit;
930+ }
931+
932+ virtual void bind_import (int value)
933+ {
934+ if (value == duration_no_time_limit) {
935+ m_untimed.bind_import (true);
936+ m_kill_limited.bind_import (false);
937+ } else if (value == duration_time_limit) {
938+ m_untimed.bind_import (false);
939+ m_kill_limited.bind_import (false);
940+ } else {
941+ m_untimed.bind_import (true);
942+ m_kill_limited.bind_import (true);
943+ }
944+ }
945+
946+protected:
947+ BoolPref m_untimed;
948+ BitPref m_kill_limited;
949+};
950+
951+class GametypePref : public Bindable<int>
952+{
953+public:
954+ GametypePref (int16& pref) : m_pref (pref) {}
955+
956+ virtual int bind_export ()
957+ {
958+ return ((m_pref < 5) ? m_pref : m_pref - 1);
959+ }
960+
961+ virtual void bind_import (int value)
962+ {
963+ m_pref = ((value < 5) ? value : value + 1);
964+ }
965+
966+protected:
967+ int16& m_pref;
968+};
969+
970+class LatencyTolerancePref : public Bindable<int>
971+{
972+public:
973+ LatencyTolerancePref (int32& pref) : m_pref(pref) { }
974+
975+ virtual int bind_export () {
976+ return (m_pref == 0) ? 5 : (m_pref - 1);
977+ }
978+
979+ virtual void bind_import(int value) {
980+ m_pref = (value == 5) ? 0 : (value + 1);
981+ }
982+protected:
983+ int32& m_pref;
984+};
985+
986+static const vector<string> make_entry_vector (int32 entry_flags)
987+{
988+ vector<string> result;
989+
990+ entry_point ep;
991+ short index = 0;
992+
993+ while (get_indexed_entry_point (&ep, &index, entry_flags))
994+ result.push_back (string (ep.level_name));
995+
996+ return result;
997+}
998+
999+SetupNetgameDialog::SetupNetgameDialog()
1000+{ }
1001+
1002+SetupNetgameDialog::~SetupNetgameDialog ()
1003+{
1004+ delete m_cancelWidget;
1005+ delete m_okWidget;
1006+
1007+ delete m_nameWidget;
1008+ delete m_colourWidget;
1009+ delete m_teamWidget;
1010+
1011+ delete m_mapWidget;
1012+ delete m_levelWidget;
1013+ delete m_gameTypeWidget;
1014+ delete m_difficultyWidget;
1015+
1016+ delete m_timeLimitWidget;
1017+ delete m_scoreLimitWidget;
1018+
1019+ delete m_aliensWidget;
1020+ delete m_allowTeamsWidget;
1021+ delete m_deadPlayersDropItemsWidget;
1022+ delete m_penalizeDeathWidget;
1023+ delete m_penalizeSuicideWidget;
1024+
1025+ delete m_useMetaserverWidget;
1026+
1027+ delete m_useScriptWidget;
1028+ delete m_scriptWidget;
1029+
1030+ delete m_allowMicWidget;
1031+
1032+ delete m_liveCarnageWidget;
1033+ delete m_motionSensorWidget;
1034+
1035+ delete m_zoomWidget;
1036+ delete m_crosshairWidget;
1037+ delete m_overlayWidget;
1038+ delete m_laraCroftWidget;
1039+ delete m_carnageMessagesWidget;
1040+
1041+ delete m_useUpnpWidget;
1042+}
1043+
1044+extern int32& hub_get_minimum_send_period();
1045+
1046+bool SetupNetgameDialog::SetupNetworkGameByRunning (
1047+ player_info *player_information,
1048+ game_info *game_information,
1049+ bool resuming_game,
1050+ bool& outAdvertiseGameOnMetaserver)
1051+{
1052+ int32 entry_flags;
1053+
1054+ m_allow_all_levels = allLevelsAllowed ();
1055+
1056+ // We use a temporary structure so that we can change things without messing with the real preferences
1057+ network_preferences_data theAdjustedPreferences = *network_preferences;
1058+ if (resuming_game)
1059+ {
1060+ // Adjust the apparent preferences to get values from the loaded game (dynamic_world)
1061+ // rather than from the actual network_preferences.
1062+ theAdjustedPreferences.game_type = GET_GAME_TYPE();
1063+ theAdjustedPreferences.difficulty_level = dynamic_world->game_information.difficulty_level;
1064+ theAdjustedPreferences.entry_point = dynamic_world->current_level_number;
1065+ theAdjustedPreferences.kill_limit = dynamic_world->game_information.kill_limit;
1066+ theAdjustedPreferences.time_limit = dynamic_world->game_information.game_time_remaining;
1067+ theAdjustedPreferences.game_options = GET_GAME_OPTIONS();
1068+ // If the time limit is longer than a week, we figure it's untimed ( ;)
1069+ theAdjustedPreferences.game_is_untimed = (dynamic_world->game_information.game_time_remaining > 7 * 24 * 3600 * TICKS_PER_SECOND);
1070+ // If they are resuming a single-player game, assume they want cooperative play now.
1071+ if (dynamic_world->player_count == 1 && GET_GAME_TYPE() == _game_of_kill_monsters)
1072+ {
1073+ theAdjustedPreferences.game_type = _game_of_cooperative_play;
1074+ theAdjustedPreferences.game_options |= _live_network_stats; // single-player game doesn't, and they probably want it
1075+ }
1076+ m_allow_all_levels = true;
1077+
1078+ // If resuming an untimed game, show the "time limit" from the prefs in the grayed-out widget
1079+ // rather than some ridiculously large number
1080+ if (theAdjustedPreferences.game_is_untimed)
1081+ theAdjustedPreferences.time_limit = theAdjustedPreferences.time_limit/TICKS_PER_SECOND/60;
1082+
1083+ // Disable certain elements when resuming a game
1084+ m_gameTypeWidget->deactivate ();
1085+ m_levelWidget->deactivate ();
1086+ m_scoreLimitWidget->deactivate ();
1087+ m_timeLimitWidget->deactivate ();
1088+ m_limitTypeWidget->deactivate ();
1089+ m_mapWidget->deactivate ();
1090+ }
1091+
1092+ // if we're resuming, use the temporary prefs structure, otherwise use the prefs as usual
1093+ network_preferences_data* active_network_preferences = resuming_game ? &theAdjustedPreferences : network_preferences;
1094+
1095+ m_old_game_type = active_network_preferences->game_type;
1096+
1097+ /* Fill in the entry points */
1098+ if(m_allow_all_levels)
1099+ {
1100+ entry_flags= NONE;
1101+ } else {
1102+ entry_flags= get_entry_point_flags_for_game_type(active_network_preferences->game_type);
1103+ }
1104+ m_levelWidget->set_labels (make_entry_vector (entry_flags));
1105+ m_gameTypeWidget->set_labels (kNetworkGameTypesStringSetID);
1106+ m_colourWidget->set_labels (kTeamColorsStringSetID);
1107+ m_teamWidget->set_labels (kTeamColorsStringSetID);
1108+ m_difficultyWidget->set_labels (kDifficultyLevelsStringSetID);
1109+ m_limitTypeWidget->set_labels (kEndConditionTypeStringSetID);
1110+
1111+ vector<string> toleranceLabels;
1112+ toleranceLabels.push_back("33 ms");
1113+ toleranceLabels.push_back("66 ms");
1114+ toleranceLabels.push_back("100 ms");
1115+ toleranceLabels.push_back("133 ms");
1116+ toleranceLabels.push_back("166 ms");
1117+ toleranceLabels.push_back("2 sec");
1118+ m_latencyToleranceWidget->set_labels (toleranceLabels);
1119+
1120+ BinderSet binders;
1121+
1122+ PStringPref namePref (player_preferences->name, MAX_NET_PLAYER_NAME_LENGTH);
1123+ binders.insert<std::string> (m_nameWidget, &namePref);
1124+ Int16Pref colourPref (player_preferences->color);
1125+ binders.insert<int> (m_colourWidget, &colourPref);
1126+ Int16Pref teamPref (player_preferences->team);
1127+ binders.insert<int> (m_teamWidget, &teamPref);
1128+
1129+ FilePref mapPref (environment_preferences->map_file);
1130+ binders.insert<FileSpecifier> (m_mapWidget, &mapPref);
1131+
1132+ LevelInt16Pref levelPref (active_network_preferences->entry_point, m_old_game_type);
1133+ binders.insert<int> (m_levelWidget, &levelPref);
1134+ GametypePref gameTypePref (active_network_preferences->game_type);
1135+ binders.insert<int> (m_gameTypeWidget, &gameTypePref);
1136+ Int16Pref difficultyPref (active_network_preferences->difficulty_level);
1137+ binders.insert<int> (m_difficultyWidget, &difficultyPref);
1138+
1139+ LimitTypePref limitTypePref (active_network_preferences->game_is_untimed, active_network_preferences->game_options, _game_has_kill_limit);
1140+ binders.insert<int> (m_limitTypeWidget, &limitTypePref);
1141+ TimerInt32Pref timeLimitPref (active_network_preferences->time_limit);
1142+ binders.insert<int> (m_timeLimitWidget, &timeLimitPref);
1143+ Int16Pref scoreLimitPref (active_network_preferences->kill_limit);
1144+ binders.insert<int> (m_scoreLimitWidget, &scoreLimitPref);
1145+
1146+ BitPref aliensPref (active_network_preferences->game_options, _monsters_replenish);
1147+ binders.insert<bool> (m_aliensWidget, &aliensPref);
1148+ BitPref allowTeamsPref (active_network_preferences->game_options, _force_unique_teams, true);
1149+ binders.insert<bool> (m_allowTeamsWidget, &allowTeamsPref);
1150+ BitPref deadPlayersDropItemsPref (active_network_preferences->game_options, _burn_items_on_death, true);
1151+ binders.insert<bool> (m_deadPlayersDropItemsWidget, &deadPlayersDropItemsPref);
1152+ BitPref penalizeDeathPref (active_network_preferences->game_options, _dying_is_penalized);
1153+ binders.insert<bool> (m_penalizeDeathWidget, &penalizeDeathPref);
1154+ BitPref penalizeSuicidePref (active_network_preferences->game_options, _suicide_is_penalized);
1155+ binders.insert<bool> (m_penalizeSuicideWidget, &penalizeSuicidePref);
1156+
1157+ BoolPref useMetaserverPref (active_network_preferences->advertise_on_metaserver);
1158+ binders.insert<bool> (m_useMetaserverWidget, &useMetaserverPref);
1159+
1160+ BoolPref allowMicPref (active_network_preferences->allow_microphone);
1161+ binders.insert<bool> (m_allowMicWidget, &allowMicPref);
1162+
1163+ BitPref liveCarnagePref (active_network_preferences->game_options, _live_network_stats);
1164+ binders.insert<bool> (m_liveCarnageWidget, &liveCarnagePref);
1165+ BitPref motionSensorPref (active_network_preferences->game_options, _motion_sensor_does_not_work);
1166+ binders.insert<bool> (m_motionSensorWidget, &motionSensorPref);
1167+
1168+ BitPref zoomPref (active_network_preferences->cheat_flags, _allow_tunnel_vision);
1169+ binders.insert<bool> (m_zoomWidget, &zoomPref);
1170+ BitPref crosshairPref (active_network_preferences->cheat_flags, _allow_crosshair);
1171+ binders.insert<bool> (m_crosshairWidget, &crosshairPref);
1172+ BitPref overlayPref (active_network_preferences->cheat_flags, _allow_overlay_map);
1173+ binders.insert<bool> (m_overlayWidget, &overlayPref);
1174+ BitPref laraCroftPref (active_network_preferences->cheat_flags, _allow_behindview);
1175+ binders.insert<bool> (m_laraCroftWidget, &laraCroftPref);
1176+ BitPref carnageMessagesPref (active_network_preferences->cheat_flags, _disable_carnage_messages, true);
1177+ binders.insert<bool> (m_carnageMessagesWidget, &carnageMessagesPref);
1178+ BitPref savingLevelPref (active_network_preferences->cheat_flags, _disable_saving_level, true);
1179+ binders.insert<bool> (m_savingLevelWidget, &savingLevelPref);
1180+
1181+ BoolPref useScriptPref (active_network_preferences->use_netscript);
1182+ binders.insert<bool> (m_useScriptWidget, &useScriptPref);
1183+ FilePref scriptPref (active_network_preferences->netscript_file);
1184+ binders.insert<FileSpecifier> (m_scriptWidget, &scriptPref);
1185+
1186+ BoolPref useUpnpPref (active_network_preferences->attempt_upnp);
1187+ binders.insert<bool> (m_useUpnpWidget, &useUpnpPref);
1188+
1189+ LatencyTolerancePref latencyTolerancePref (hub_get_minimum_send_period());
1190+ binders.insert<int> (m_latencyToleranceWidget, &latencyTolerancePref);
1191+
1192+ binders.migrate_all_second_to_first ();
1193+
1194+ m_cancelWidget->set_callback (boost::bind (&SetupNetgameDialog::Stop, this, false));
1195+ m_okWidget->set_callback (boost::bind (&SetupNetgameDialog::okHit, this));
1196+ m_limitTypeWidget->set_callback (boost::bind (&SetupNetgameDialog::limitTypeHit, this));
1197+ m_allowTeamsWidget->set_callback (boost::bind (&SetupNetgameDialog::teamsHit, this));
1198+ m_gameTypeWidget->set_callback (boost::bind (&SetupNetgameDialog::gameTypeHit, this));
1199+ m_mapWidget->set_callback (boost::bind (&SetupNetgameDialog::chooseMapHit, this));
1200+
1201+ setupForGameType ();
1202+
1203+ if (m_limitTypeWidget->get_value () == duration_kill_limit)
1204+ setupForScoreGame ();
1205+ else if (m_limitTypeWidget->get_value () == duration_no_time_limit)
1206+ setupForUntimedGame ();
1207+ else
1208+ setupForTimedGame ();
1209+
1210+ /* Setup the team popup.. */
1211+ if (!m_allowTeamsWidget->get_value ())
1212+ m_teamWidget->deactivate ();
1213+
1214+ if (Run ()) {
1215+
1216+ // migrate widget settings to preferences structure
1217+ binders.migrate_all_first_to_second ();
1218+
1219+ pstrcpy (player_information->name, player_preferences->name);
1220+ player_information->color = player_preferences->color;
1221+ player_information->team = player_preferences->team;
1222+
1223+ game_information->server_is_playing = true;
1224+ game_information->net_game_type = active_network_preferences->game_type;
1225+
1226+ game_information->game_options = active_network_preferences->game_options;
1227+ game_information->game_options |= (_ammo_replenishes | _weapons_replenish | _specials_replenish);
1228+ if (active_network_preferences->game_type == _game_of_cooperative_play)
1229+ game_information->game_options |= _overhead_map_is_omniscient;
1230+
1231+ // ZZZ: don't screw with the limits if resuming.
1232+ if (resuming_game)
1233+ {
1234+ game_information->time_limit = dynamic_world->game_information.game_time_remaining;
1235+ game_information->kill_limit = dynamic_world->game_information.kill_limit;
1236+ } else {
1237+ if (!active_network_preferences->game_is_untimed)
1238+ game_information->time_limit = m_timeLimitWidget->get_value () * TICKS_PER_SECOND * 60;
1239+ else
1240+ game_information->time_limit = INT32_MAX;
1241+
1242+ game_information->kill_limit = active_network_preferences->kill_limit;
1243+ }
1244+
1245+ entry_point entry;
1246+ menu_index_to_level_entry (active_network_preferences->entry_point, NONE, &entry);
1247+ game_information->level_number = entry.level_number;
1248+ strcpy (game_information->level_name, entry.level_name);
1249+ game_information->parent_checksum = read_wad_file_checksum(get_map_file());
1250+ game_information->difficulty_level = active_network_preferences->difficulty_level;
1251+ game_information->allow_mic = active_network_preferences->allow_microphone;
1252+
1253+ int updates_per_packet = 1;
1254+ int update_latency = 0;
1255+ vassert(updates_per_packet > 0 && update_latency >= 0 && updates_per_packet < 16,
1256+ csprintf(temporary, "You idiot! updates_per_packet = %d, update_latency = %d", updates_per_packet, update_latency));
1257+ game_information->initial_updates_per_packet = updates_per_packet;
1258+ game_information->initial_update_latency = update_latency;
1259+ NetSetInitialParameters(updates_per_packet, update_latency);
1260+
1261+ game_information->initial_random_seed = resuming_game ? dynamic_world->random_seed : (uint16) machine_tick_count();
1262+
1263+#if mac
1264+ FileSpecifier theNetscriptFile;
1265+ theNetscriptFile.SetSpec (active_network_preferences->netscript_file);
1266+#else
1267+ FileSpecifier theNetscriptFile (active_network_preferences->netscript_file);
1268+#endif
1269+
1270+ // This will be set true below if appropriate
1271+ SetNetscriptStatus(false);
1272+
1273+ if (active_network_preferences->use_netscript)
1274+ {
1275+ OpenedFile script_file;
1276+
1277+ if (theNetscriptFile.Open (script_file))
1278+ {
1279+ int32 script_length;
1280+ script_file.GetLength (script_length);
1281+
1282+ // DeferredScriptSend will delete this storage the *next time* we call it (!)
1283+ byte* script_buffer = new byte [script_length];
1284+
1285+ if (script_file.Read (script_length, script_buffer))
1286+ {
1287+ DeferredScriptSend (script_buffer, script_length);
1288+ SetNetscriptStatus (true);
1289+ }
1290+
1291+ script_file.Close ();
1292+ }
1293+ else
1294+ // hmm failing quietly is probably not the best course of action, but ...
1295+ ;
1296+ }
1297+
1298+ game_information->cheat_flags = active_network_preferences->cheat_flags;
1299+
1300+ outAdvertiseGameOnMetaserver = active_network_preferences->advertise_on_metaserver;
1301+
1302+ //if(shouldUseNetscript)
1303+ //{
1304+ // Sorry, probably should use a FileSpecifier in the prefs,
1305+ // but that means prefs reading/writing have to be reworked instead
1306+#ifdef SDL
1307+ // strncpy(network_preferences->netscript_file, theNetscriptFile.GetPath(), sizeof(network_preferences->netscript_file));
1308+#else
1309+ // network_preferences->netscript_file = theNetscriptFile.GetSpec();
1310+#endif
1311+ //}
1312+
1313+ return true;
1314+
1315+ } else // dialog was cancelled
1316+ return false;
1317+}
1318+
1319+void SetupNetgameDialog::setupForUntimedGame ()
1320+{
1321+ m_timeLimitWidget->hide ();
1322+ m_scoreLimitWidget->hide ();
1323+}
1324+
1325+void SetupNetgameDialog::setupForTimedGame ()
1326+{
1327+ m_timeLimitWidget->show ();
1328+ m_scoreLimitWidget->hide ();
1329+}
1330+
1331+void SetupNetgameDialog::setupForScoreGame ()
1332+{
1333+ m_timeLimitWidget->hide ();
1334+ m_scoreLimitWidget->show ();
1335+}
1336+
1337+void SetupNetgameDialog::limitTypeHit ()
1338+{
1339+ switch(m_limitTypeWidget->get_value ())
1340+ {
1341+ case 0:
1342+ setupForUntimedGame ();
1343+ break;
1344+
1345+ case 1:
1346+ setupForTimedGame ();
1347+ break;
1348+
1349+ case 2:
1350+ setupForScoreGame ();
1351+ break;
1352+ }
1353+}
1354+
1355+void SetupNetgameDialog::teamsHit ()
1356+{
1357+ if (m_allowTeamsWidget->get_value ())
1358+ m_teamWidget->activate ();
1359+ else
1360+ m_teamWidget->deactivate ();
1361+}
1362+
1363+void SetupNetgameDialog::setupForGameType ()
1364+{
1365+ int raw_value = m_gameTypeWidget->get_value ();
1366+ switch (raw_value < 5 ? raw_value : raw_value + 1)
1367+ {
1368+ case _game_of_cooperative_play:
1369+ m_allowTeamsWidget->activate ();
1370+ m_deadPlayersDropItemsWidget->deactivate ();
1371+ m_aliensWidget->deactivate ();
1372+
1373+ m_deadPlayersDropItemsWidget->set_value (true);
1374+ m_aliensWidget->set_value (true);
1375+
1376+ getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, killsString);
1377+// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1378+ break;
1379+
1380+ case _game_of_kill_monsters:
1381+ case _game_of_king_of_the_hill:
1382+ case _game_of_kill_man_with_ball:
1383+ case _game_of_tag:
1384+ case _game_of_custom:
1385+ m_allowTeamsWidget->activate ();
1386+ m_deadPlayersDropItemsWidget->activate ();
1387+ m_aliensWidget->activate ();
1388+
1389+ getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, killsString);
1390+// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1391+ break;
1392+
1393+ case _game_of_capture_the_flag:
1394+ m_allowTeamsWidget->deactivate ();
1395+ m_deadPlayersDropItemsWidget->activate ();
1396+ m_aliensWidget->activate ();
1397+
1398+ m_allowTeamsWidget->set_value (true);
1399+ m_teamWidget->activate ();
1400+
1401+ getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, flagsString);
1402+// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1403+ break;
1404+
1405+ case _game_of_rugby:
1406+ m_allowTeamsWidget->deactivate ();
1407+ m_deadPlayersDropItemsWidget->activate ();
1408+ m_aliensWidget->activate ();
1409+
1410+ m_allowTeamsWidget->set_value (true);
1411+ m_teamWidget->activate ();
1412+
1413+ getpstr (ptemporary, strSETUP_NET_GAME_MESSAGES, pointsString);
1414+// m_scoreLimitWidget->set_label (pstring_to_string (ptemporary));
1415+ break;
1416+
1417+ default:
1418+ assert(false);
1419+ break;
1420+ }
1421+}
1422+
1423+void SetupNetgameDialog::gameTypeHit ()
1424+{
1425+ int new_game_type= m_gameTypeWidget->get_value ();
1426+ if (new_game_type >= 5)
1427+ ++new_game_type;
1428+
1429+ if (new_game_type != m_old_game_type) {
1430+ int32 new_entry_flags, old_entry_flags;
1431+ struct entry_point entry;
1432+
1433+ if(m_allow_all_levels) {
1434+ new_entry_flags= old_entry_flags= NONE;
1435+ } else {
1436+ new_entry_flags= get_entry_point_flags_for_game_type(new_game_type);
1437+ old_entry_flags= get_entry_point_flags_for_game_type(m_old_game_type);
1438+ }
1439+ menu_index_to_level_entry (m_levelWidget->get_value (), old_entry_flags, &entry);
1440+
1441+ /* Now reset entry points */
1442+ m_levelWidget->set_labels (make_entry_vector (new_entry_flags));
1443+ m_levelWidget->set_value (level_index_to_menu_index (entry.level_number, new_entry_flags));
1444+ m_old_game_type= new_game_type;
1445+
1446+ setupForGameType ();
1447+ }
1448+}
1449+
1450+void SetupNetgameDialog::chooseMapHit ()
1451+{
1452+ FileSpecifier mapFile = m_mapWidget->get_file ();
1453+
1454+ environment_preferences->map_checksum = read_wad_file_checksum (mapFile);
1455+#ifdef SDL
1456+ strcpy(environment_preferences->map_file, mapFile.GetPath());
1457+#else
1458+ environment_preferences->map_file = mapFile.GetSpec();
1459+#endif
1460+ load_environment_from_preferences();
1461+
1462+ m_levelWidget->set_labels (make_entry_vector (get_entry_point_flags_for_game_type (m_old_game_type)));
1463+ m_levelWidget->set_value (0);
1464+}
1465+
1466+bool SetupNetgameDialog::informationIsAcceptable ()
1467+{
1468+ bool information_is_acceptable = true;
1469+ short game_limit_type = m_limitTypeWidget->get_value ();
1470+
1471+ if (information_is_acceptable)
1472+ if (game_limit_type == duration_time_limit)
1473+ {
1474+ information_is_acceptable = m_timeLimitWidget->get_value () >= 1;
1475+ }
1476+
1477+ if (information_is_acceptable)
1478+ if (game_limit_type == duration_kill_limit)
1479+ {
1480+ information_is_acceptable = m_scoreLimitWidget->get_value () >= 1;
1481+ }
1482+
1483+ if (information_is_acceptable)
1484+ information_is_acceptable = !(m_nameWidget->get_text ().empty ());
1485+
1486+ if (information_is_acceptable)
1487+ information_is_acceptable = m_mapWidget->get_file ().Exists ();
1488+
1489+ if (information_is_acceptable)
1490+ {
1491+ entry_point ep;
1492+ short index = 0;
1493+ information_is_acceptable = get_indexed_entry_point(&ep, &index, get_entry_point_flags_for_game_type(m_old_game_type));
1494+ }
1495+
1496+ return (information_is_acceptable);
1497+}
1498+
1499+void SetupNetgameDialog::okHit ()
1500+{
1501+ if (informationIsAcceptable ())
1502+ Stop (true);
1503+ else
1504+ unacceptableInfo ();
1505+
1506+}
1507+
1508+void menu_index_to_level_entry(
1509+ short menu_index,
1510+ int32 entry_flags,
1511+ struct entry_point *entry)
1512+{
1513+ short i, map_index;
1514+
1515+ map_index= 0;
1516+ for (i= 0; i<=menu_index; i++)
1517+ {
1518+ get_indexed_entry_point(entry, &map_index, entry_flags);
1519+ }
1520+
1521+ return;
1522+}
1523+
1524+int menu_index_to_level_index (int menu_index, int32 entry_flags)
1525+{
1526+ entry_point entry;
1527+
1528+ menu_index_to_level_entry (menu_index, entry_flags, &entry);
1529+
1530+ return entry.level_number;
1531+}
1532+
1533+int level_index_to_menu_index (int level_index, int32 entry_flags)
1534+{
1535+ entry_point entry;
1536+ short map_index = 0;
1537+
1538+ int result = 0;
1539+ while (get_indexed_entry_point(&entry, &map_index, entry_flags)) {
1540+ if (map_index == level_index + 1)
1541+ return result;
1542+ ++result;
1543+ }
1544+
1545+ return 0;
1546+}
1547+
1548+/*************************************************************************************************
1549+ *
1550+ * Function: reassign_player_colors
1551+ * Purpose: This function used to reassign a player's color if it conflicted with another
1552+ * player's color. Now it reassigns everyone's colors. for the old function, see the
1553+ * obsoleted version (called check_player_info) at the end of this file.
1554+ * (Woody note: check_player_info can be found in network_dialogs_macintosh.cpp.)
1555+ *
1556+ *************************************************************************************************/
1557+/* Note that we now only force unique colors across teams. */
1558+
1559+// ZZZ: moved here (from network_dialogs_macintosh.cpp) so it can be shared with SDL version
1560+
1561+void reassign_player_colors(
1562+ short player_index,
1563+ short num_players)
1564+{
1565+ short actual_colors[MAXIMUM_NUMBER_OF_PLAYERS]; // indexed by player
1566+ bool colors_taken[NUMBER_OF_TEAM_COLORS]; // as opposed to desired team. indexed by team
1567+ game_info *game;
1568+
1569+ (void)(player_index);
1570+
1571+ assert(num_players<=MAXIMUM_NUMBER_OF_PLAYERS);
1572+ game= (game_info *)NetGetGameData();
1573+
1574+ objlist_set(colors_taken, false, NUMBER_OF_TEAM_COLORS);
1575+ objlist_set(actual_colors, NONE, MAXIMUM_NUMBER_OF_PLAYERS);
1576+
1577+ if(game->game_options & _force_unique_teams)
1578+ {
1579+ short index;
1580+
1581+ for(index= 0; index<num_players; ++index)
1582+ {
1583+ player_info *player= (player_info *)NetGetPlayerData(index);
1584+ if(!colors_taken[player->desired_color])
1585+ {
1586+ player->color= player->desired_color;
1587+ player->team= player->color;
1588+ colors_taken[player->color]= true;
1589+ actual_colors[index]= player->color;
1590+ }
1591+ }
1592+
1593+ /* Now give them a random color.. */
1594+ for (index= 0; index<num_players; index++)
1595+ {
1596+ player_info *player= (player_info *)NetGetPlayerData(index);
1597+
1598+ if (actual_colors[index]==NONE) // This player needs a team
1599+ {
1600+ short remap_index;
1601+
1602+ for (remap_index= 0; remap_index<num_players; remap_index++)
1603+ {
1604+ if (!colors_taken[remap_index])
1605+ {
1606+ player->color= remap_index;
1607+ player->team= remap_index;
1608+ colors_taken[remap_index] = true;
1609+ break;
1610+ }
1611+ }
1612+ assert(remap_index<num_players);
1613+ }
1614+ }
1615+ } else {
1616+ short index;
1617+ short team_color;
1618+
1619+ /* Allow teams.. */
1620+ for(team_color= 0; team_color<NUMBER_OF_TEAM_COLORS; ++team_color)
1621+ {
1622+ // let's mark everybody down for the teams that they can get without conflicts.
1623+ for (index = 0; index < num_players; index++)
1624+ {
1625+ player_info *player= (player_info *)NetGetPlayerData(index);
1626+
1627+ if (player->team==team_color && !colors_taken[player->desired_color])
1628+ {
1629+ player->color= player->desired_color;
1630+ colors_taken[player->color] = true;
1631+ actual_colors[index]= player->color;
1632+ }
1633+ }
1634+
1635+ // ok, everyone remaining gets a team that we pick for them.
1636+ for (index = 0; index < num_players; index++)
1637+ {
1638+ player_info *player= (player_info *)NetGetPlayerData(index);
1639+
1640+ if (player->team==team_color && actual_colors[index]==NONE) // This player needs a team
1641+ {
1642+ short j;
1643+
1644+ for (j = 0; j < num_players; j++)
1645+ {
1646+ if (!colors_taken[j])
1647+ {
1648+ player->color= j;
1649+ colors_taken[j] = true;
1650+ break;
1651+ }
1652+ }
1653+ assert(j < num_players);
1654+ }
1655+ }
1656+ }
1657+ }
1658+}
1659+
1660+
1661+
1662+////////////////////////////////////////////////////////////////////////////////
1663+// Postgame Carnage Report stuff
1664+struct net_rank rankings[MAXIMUM_NUMBER_OF_PLAYERS];
1665+
1666+#if 0
1667+// These were used for an array-lookup-based find_graph_mode, which worked but was later
1668+// abandoned in favor of the original (very slightly modified to "add back in" separator indices).
1669+// Left here for the curious.
1670+
1671+// This should not conflict with the other _*_graph identifiers
1672+enum { _total_team_carnage_or_total_scores_graph = 4242 };
1673+
1674+// We lookup into a menu contents array now
1675+static int sMenuContents[] =
1676+#ifdef mac
1677+{
1678+ NONE, // separator
1679+ _total_carnage_graph,
1680+ _total_team_carnage_or_total_scores_graph,
1681+ NONE, // separator
1682+ _total_team_carnage_graph,
1683+ _total_team_scores_graph
1684+};
1685+#else // !mac
1686+{
1687+ _total_carnage_graph,
1688+ _total_team_carnage_or_total_scores_graph,
1689+ _total_team_carnage_graph,
1690+ _total_team_scores_graph
1691+};
1692+#endif // !mac
1693+#endif // 0
1694+
1695+// (ZZZ annotation:) Figure out which graph type the user wants to display based
1696+// on his selection from the popup/selection control. (See also draw_new_graph().)
1697+short
1698+find_graph_mode(
1699+ NetgameOutcomeData &outcome,
1700+ short *index)
1701+{
1702+ short value;
1703+ short graph_type = NONE;
1704+ bool has_scores;
1705+
1706+ has_scores= current_net_game_has_scores();
1707+
1708+ /* Popups are 1 based */
1709+#ifdef USES_NIBS
1710+ value = GetControl32BitValue(outcome.SelectCtrl) - 1;
1711+#else
1712+ value = get_selection_control_value(outcome, iGRAPH_POPUP)-1;
1713+#endif
1714+ if(value<dynamic_world->player_count)
1715+ {
1716+ if(index) *index= value;
1717+ graph_type= _player_graph;
1718+ }
1719+ else
1720+ {
1721+#if 0
1722+ // alternate method based on array lookup, works but abandoned.
1723+ // left here for the curious.
1724+
1725+ // ZZZ change: lookup graph type from static array
1726+ int theIndexAfterPlayers = value - dynamic_world->player_count;
1727+ int theNumberOfMenuItemsAfterPlayers = (sizeof(sMenuContents) / sizeof(sMenuContents[0]));
1728+
1729+ // Make sure the index is sane
1730+ assert(theIndexAfterPlayers >= 0 && theIndexAfterPlayers < theNumberOfMenuItemsAfterPlayers);
1731+
1732+ // Do the lookup
1733+ graph_type = sMenuContents[theIndexAfterPlayers];
1734+
1735+ // Make sure graph type is sane
1736+ assert(graph_type != NONE);
1737+
1738+ bool isTeamGame = ((GET_GAME_OPTIONS() & _force_unique_teams) ? false : true);
1739+
1740+ // Disambiguate
1741+ if(graph_type == _total_team_carnage_or_total_scores_graph)
1742+ graph_type = has_scores ? _total_scores_graph : _total_team_carnage_graph;
1743+
1744+ // Sanity check the graph type
1745+ if(!isTeamGame) {
1746+ assert(graph_type != _total_team_carnage_graph);
1747+ assert(graph_type != _total_team_scores_graph);
1748+ }
1749+
1750+ if(!has_scores) {
1751+ assert(graph_type != _total_scores_graph);
1752+ assert(graph_type != _total_team_scores_graph);
1753+ }
1754+
1755+#else // !0
1756+
1757+ int theValueAfterPlayers = value-dynamic_world->player_count;
1758+#ifndef mac
1759+ // ZZZ: Account for (lack of) separators
1760+ if(theValueAfterPlayers >= 0) theValueAfterPlayers++;
1761+ if(theValueAfterPlayers >= 3) theValueAfterPlayers++;
1762+#endif
1763+
1764+ /* Different numbers of items based on game type. */
1765+ switch(theValueAfterPlayers)
1766+ {
1767+ case 0:
1768+ /* Separator line */
1769+ assert(false);
1770+ break;
1771+
1772+ case 1: /* FIrst item after the players. */
1773+ graph_type= _total_carnage_graph; /* Always.. */
1774+ break;
1775+
1776+ case 2: /* May be either: _total_scores or _total_team_carnage */
1777+ if(has_scores)
1778+ {
1779+ graph_type= _total_scores_graph;
1780+ } else {
1781+ assert(!(GET_GAME_OPTIONS() & _force_unique_teams));
1782+ graph_type= _total_team_carnage_graph;
1783+ }
1784+ break;
1785+
1786+ case 3:
1787+ /* Separator line */
1788+ assert(false);
1789+ break;
1790+
1791+ case 4:
1792+ assert(!(GET_GAME_OPTIONS() & _force_unique_teams));
1793+ graph_type= _total_team_carnage_graph;
1794+ break;
1795+
1796+ case 5:
1797+ assert(has_scores);
1798+ graph_type= _total_team_scores_graph;
1799+ break;
1800+
1801+ default:
1802+ assert(false);
1803+ break;
1804+ }
1805+#endif // !0
1806+ }
1807+
1808+ return graph_type;
1809+}
1810+
1811+
1812+
1813+// (ZZZ annotation:) Fill in array of net_rank with total carnage values, individual scores,
1814+// colors, etc. Note that team-by-team rankings (draw_team_*_graph()) and player vs.
1815+// player rankings (draw_player_graph()) use their own local ranks[] arrays instead.
1816+// The team-by-team rankings are computed from these; the player vs. player are not.
1817+void calculate_rankings(
1818+ struct net_rank *ranks,
1819+ short num_players)
1820+{
1821+ short player_index;
1822+
1823+ for(player_index= 0; player_index<num_players; ++player_index)
1824+ {
1825+ ranks[player_index].player_index= player_index;
1826+ ranks[player_index].color= get_player_data(player_index)->color;
1827+ ranks[player_index].game_ranking= get_player_net_ranking(player_index,
1828+ &ranks[player_index].kills,
1829+ &ranks[player_index].deaths, true);
1830+ ranks[player_index].ranking= ranks[player_index].kills-ranks[player_index].deaths;
1831+ }
1832+}
1833+
1834+
1835+// (ZZZ annotation:) Individual carnage totals comparison for sorting.
1836+int rank_compare(
1837+ void const *r1,
1838+ void const *r2)
1839+{
1840+ struct net_rank const *rank1=(struct net_rank const *)r1;
1841+ struct net_rank const *rank2=(struct net_rank const *)r2;
1842+ int diff;
1843+ struct player_data *p1, *p2;
1844+
1845+ diff = rank2->ranking - rank1->ranking;
1846+
1847+ // (ZZZ annotation:) Tiebreaker: which player did more killing (and thus dying)?
1848+ if (diff == 0)
1849+ {
1850+ // i have to resort to looking here because the information may not be contained
1851+ // in the rank structure if we're not displaying the totals graph.
1852+ p1 = get_player_data(rank1->player_index);
1853+ p2 = get_player_data(rank2->player_index);
1854+ diff = p2->total_damage_given.kills - p1->total_damage_given.kills;
1855+ }
1856+
1857+ return diff;
1858+}
1859+
1860+// (ZZZ annotation:) Team carnage totals comparison for sorting.
1861+// Same as rank_compare(), but without tiebreaking.
1862+int team_rank_compare(
1863+ void const *rank1,
1864+ void const *rank2)
1865+{
1866+ return ((struct net_rank const *)rank2)->ranking
1867+ -((struct net_rank const *)rank1)->ranking;
1868+}
1869+
1870+// (ZZZ annotation:) Game-specific score comparison for sorting.
1871+int score_rank_compare(
1872+ void const *rank1,
1873+ void const *rank2)
1874+{
1875+ return ((struct net_rank const *)rank2)->game_ranking
1876+ -((struct net_rank const *)rank1)->game_ranking;
1877+}
1878+
1879+
1880+
1881+// (ZZZ annotation:) Graph of player's killing performance vs each other player
1882+void draw_player_graph(
1883+ NetgameOutcomeData &outcome,
1884+ short index)
1885+{
1886+ short key_player_index= rankings[index].player_index;
1887+ struct player_data *key_player= get_player_data(key_player_index);
1888+ struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
1889+ short loop;
1890+
1891+ /* Copy in the total ranks. */
1892+ for(loop= 0; loop<dynamic_world->player_count; ++loop)
1893+ {
1894+ short test_player_index= rankings[loop].player_index;
1895+ struct player_data *player= get_player_data(test_player_index);
1896+
1897+ /* Copy most of the data */
1898+ ranks[loop]= rankings[loop];
1899+
1900+ /* How many times did I kill this guy? */
1901+ ranks[loop].kills= player->damage_taken[key_player_index].kills;
1902+
1903+ /* How many times did this guy kill me? */
1904+ ranks[loop].deaths= key_player->damage_taken[test_player_index].kills;
1905+ }
1906+
1907+ draw_names(outcome, ranks, dynamic_world->player_count, index);
1908+ draw_kill_bars(outcome, ranks, dynamic_world->player_count, index, false, false);
1909+}
1910+
1911+
1912+// ZZZ: team vs team carnage (analogous to draw_player_graph's player vs player carnage)
1913+// THIS IS UNFINISHED (and thus unused at the moment :) )
1914+void draw_team_graph(
1915+ NetgameOutcomeData &outcome,
1916+ short team_index)
1917+{
1918+ // ZZZZZZ this is where I add my team vs team ranking computation. Yay.
1919+ // We'll just fill in the rank structures straight, then count the teams later.
1920+ struct net_rank team_ranks[NUMBER_OF_TEAM_COLORS];
1921+
1922+ objlist_clear(team_ranks, NUMBER_OF_TEAM_COLORS);
1923+
1924+ /* Loop across players on the reference team */
1925+ for(int ref_player_index = 0; ref_player_index < dynamic_world->player_count; ref_player_index++)
1926+ {
1927+// short test_player_index= rankings[loop].player_index;
1928+ struct player_data *ref_player= get_player_data(ref_player_index);
1929+
1930+ if(ref_player->team != team_index)
1931+ continue;
1932+
1933+ /* Loop across all players */
1934+ for(int player_index = 0; player_index < dynamic_world->player_count; player_index++)
1935+ {
1936+ // short test_player_index= rankings[loop].player_index;
1937+ struct player_data *player= get_player_data(player_index);
1938+
1939+ team_ranks[player->team].player_index = NONE;
1940+ team_ranks[player->team].color = player->team;
1941+ team_ranks[player->team].kills += player->damage_taken[ref_player_index].kills;
1942+ team_ranks[player->team].deaths += ref_player->damage_taken[player_index].kills;
1943+ } // all players
1944+ } // players on reference team
1945+
1946+ // Condense into the first group of slots in the rankings
1947+ // NOTE ideally these will be ordered the same way the team_total_carnage rankings are.
1948+
1949+ // Draw the bars
1950+// draw_names(dialog, team_ranks, dynamic_world->player_count, index);
1951+// draw_kill_bars(dialog, team_ranks, dynamic_world->player_count, index, false, false);
1952+}
1953+
1954+
1955+
1956+// (ZZZ annotation:) Total Carnage graph
1957+void draw_totals_graph(
1958+ NetgameOutcomeData &outcome)
1959+{
1960+ draw_names(outcome, rankings, dynamic_world->player_count, NONE);
1961+ draw_kill_bars(outcome, rankings, dynamic_world->player_count, NONE, true, false);
1962+}
1963+
1964+
1965+// (ZZZ annotation:) Total Team Carnage graph
1966+void draw_team_totals_graph(
1967+ NetgameOutcomeData &outcome)
1968+{
1969+ short team_index, player_index, num_teams;
1970+ bool found_team_of_current_color;
1971+ struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
1972+
1973+ objlist_clear(ranks, MAXIMUM_NUMBER_OF_PLAYERS);
1974+ for (team_index = 0, num_teams = 0; team_index < NUMBER_OF_TEAM_COLORS; team_index++) {
1975+ found_team_of_current_color = false;
1976+ if (team_damage_given[team_index].kills ||
1977+ (team_damage_taken[team_index].kills + team_monster_damage_taken[team_index].kills)) {
1978+ found_team_of_current_color = true;
1979+ } else {
1980+ for (player_index = 0; player_index < dynamic_world->player_count; player_index++) {
1981+ struct player_data *player = get_player_data(player_index);
1982+ if (player->team == team_index) {
1983+ found_team_of_current_color = true;
1984+ break;
1985+ }
1986+ }
1987+ }
1988+ if (found_team_of_current_color) {
1989+ ranks[num_teams].player_index = NONE;
1990+ ranks[num_teams].color = team_index;
1991+ ranks[num_teams].kills = team_damage_given[team_index].kills;
1992+ ranks[num_teams].deaths = team_damage_taken[team_index].kills + team_monster_damage_taken[team_index].kills;
1993+ ranks[num_teams].friendly_fire_kills = team_friendly_fire[team_index].kills;
1994+ num_teams++;
1995+ }
1996+ }
1997+
1998+ /* Setup the team rankings.. */
1999+ for (team_index= 0; team_index<num_teams; team_index++)
2000+ {
2001+ ranks[team_index].ranking= ranks[team_index].kills - ranks[team_index].deaths;
2002+ }
2003+ qsort(ranks, num_teams, sizeof(struct net_rank), team_rank_compare);
2004+
2005+ draw_names(outcome, ranks, num_teams, NONE);
2006+ draw_kill_bars(outcome, ranks, num_teams, NONE, true, true);
2007+}
2008+
2009+
2010+
2011+// (ZZZ annotation:) Time on Hill, etc. graph
2012+void draw_total_scores_graph(
2013+ NetgameOutcomeData &outcome)
2014+{
2015+ struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
2016+
2017+ /* Use a private copy to avoid boning things */
2018+ objlist_copy(ranks, rankings, dynamic_world->player_count);
2019+
2020+ /* First qsort the rankings arrray by game_ranking.. */
2021+ qsort(ranks, dynamic_world->player_count, sizeof(struct net_rank), score_rank_compare);
2022+
2023+ draw_names(outcome, ranks, dynamic_world->player_count, NONE);
2024+ draw_score_bars(outcome, ranks, dynamic_world->player_count);
2025+}
2026+
2027+
2028+
2029+// (ZZZ annotation:) Team Time on Hill, etc. graph
2030+void draw_team_total_scores_graph(
2031+ NetgameOutcomeData &outcome)
2032+{
2033+ short team_index, team_count;
2034+ struct net_rank ranks[MAXIMUM_NUMBER_OF_PLAYERS];
2035+
2036+ objlist_clear(ranks, MAXIMUM_NUMBER_OF_PLAYERS);
2037+ team_count = 0;
2038+
2039+ for (team_index = 0; team_index < NUMBER_OF_TEAM_COLORS; ++team_index) {
2040+ bool team_is_valid = false;
2041+ short kills, deaths;
2042+ int32 ranking = get_team_net_ranking(team_index, &kills, &deaths, true);
2043+
2044+ if (kills || deaths || ranking) {
2045+ team_is_valid = true;
2046+ } else {
2047+ for (short player_index = 0; player_index < dynamic_world->player_count; ++player_index) {
2048+ struct player_data *player = get_player_data(player_index);
2049+ if (player->team == team_index) {
2050+ team_is_valid = true;
2051+ break;
2052+ }
2053+ }
2054+ }
2055+
2056+ if (team_is_valid) {
2057+ ranks[team_count].kills = kills;
2058+ ranks[team_index].deaths = deaths;
2059+ ranks[team_count].player_index = NONE;
2060+ ranks[team_count].color = team_index;
2061+ ranks[team_count].game_ranking = ranking;
2062+ ranks[team_count].friendly_fire_kills = team_friendly_fire[NUMBER_OF_TEAM_COLORS].kills;
2063+ team_count++;
2064+ }
2065+ }
2066+
2067+ /* Now qsort our team stuff. */
2068+ qsort(ranks, team_count, sizeof(struct net_rank), score_rank_compare);
2069+
2070+ draw_names(outcome, ranks, team_count, NONE);
2071+ draw_score_bars(outcome, ranks, team_count);
2072+}
2073+
2074+
2075+
2076+// (ZZZ) ripped this out of draw_kill_bars since we can share this bit but not the rest of draw_kill_bars().
2077+// (ZZZ annotation:) Update the "N deaths total (D dpm) including S suicides"-type text at the bottom.
2078+void update_carnage_summary(
2079+ NetgameOutcomeData &outcome,
2080+ struct net_rank *ranks,
2081+ short num_players,
2082+ short suicide_index,
2083+ bool do_totals,
2084+ bool friendly_fire)
2085+{
2086+ short i;
2087+ short num_suicides;
2088+ float minutes;
2089+ float kpm;
2090+ float dpm;
2091+ int32 total_kills = 0;
2092+ int32 total_deaths = 0;
2093+ char kill_string_format[65];
2094+ char death_string_format[65];
2095+ char suicide_string_format[65];
2096+
2097+ for (i = 0; i < num_players; i++)
2098+ {
2099+ if (do_totals || i != suicide_index)
2100+ total_kills += ranks[i].kills;
2101+ total_deaths += ranks[i].deaths;
2102+ }
2103+ if (do_totals)
2104+ {
2105+ for (i = num_suicides = 0; i < num_players; i++)
2106+ {
2107+ if (friendly_fire)
2108+ num_suicides += ranks[i].friendly_fire_kills;
2109+ else
2110+ num_suicides += (players+i)->damage_taken[i].kills;
2111+ }
2112+ }
2113+ else
2114+ num_suicides = ranks[suicide_index].kills;
2115+
2116+#ifdef mac
2117+ TextFace(0); TextFont(0); TextSize(0);
2118+#endif
2119+
2120+ minutes = ((float)dynamic_world->tick_count / TICKS_PER_SECOND) / 60.0F;
2121+ if (minutes > 0) kpm = total_kills / minutes;
2122+ else kpm = 0;
2123+ getcstr(kill_string_format, strNET_STATS_STRINGS, strTOTAL_KILLS_STRING);
2124+ psprintf(ptemporary, kill_string_format, total_kills, kpm);
2125+// GetDialogItem(dialog, iTOTAL_KILLS, &item_type, &item_handle, &item_rect);
2126+// SetDialogItemText(item_handle, ptemporary);
2127+
2128+#ifdef USES_NIBS
2129+ SetStaticPascalText(outcome.KillsTextCtrl, ptemporary);
2130+#else
2131+ copy_pstring_to_static_text(outcome, iTOTAL_KILLS, ptemporary);
2132+#endif
2133+
2134+ if (minutes > 0) dpm = total_deaths / minutes;
2135+ else dpm = 0;
2136+ getcstr(death_string_format, strNET_STATS_STRINGS, strTOTAL_DEATHS_STRING);
2137+
2138+ if (num_suicides)
2139+ {
2140+ if (friendly_fire)
2141+ getcstr(suicide_string_format, strNET_STATS_STRINGS, strFRIENDLY_FIRE_STRING);
2142+ else
2143+ getcstr(suicide_string_format, strNET_STATS_STRINGS, strINCLUDING_SUICIDES_STRING);
2144+ strcat(death_string_format, suicide_string_format);
2145+ psprintf(ptemporary, death_string_format, total_deaths, dpm, num_suicides);
2146+ }
2147+ else
2148+ psprintf(ptemporary, death_string_format, total_deaths, dpm);
2149+// GetDialogItem(dialog, iTOTAL_DEATHS, &item_type, &item_handle, &item_rect);
2150+// SetDialogItemText(item_handle, ptemporary);
2151+
2152+#ifdef USES_NIBS
2153+ SetStaticPascalText(outcome.DeathsTextCtrl, ptemporary);
2154+#else
2155+ copy_pstring_to_static_text(outcome, iTOTAL_DEATHS, ptemporary);
2156+#endif
2157+}
2158+
2159+
2160+
2161+// ZZZ: ripped out of update_damage_item
2162+// (ZZZ annotation:) Demultiplex to draw_X_graph() function based on find_graph_mode().
2163+void draw_new_graph(
2164+ NetgameOutcomeData &outcome)
2165+{
2166+ short graph_type;
2167+ short index;
2168+
2169+ graph_type= find_graph_mode(outcome, &index);
2170+
2171+ switch(graph_type)
2172+ {
2173+ case _player_graph:
2174+ draw_player_graph(outcome, index);
2175+ break;
2176+
2177+ case _total_carnage_graph:
2178+ draw_totals_graph(outcome);
2179+ break;
2180+
2181+ case _total_scores_graph:
2182+ draw_total_scores_graph(outcome);
2183+ break;
2184+
2185+ /* These two functions need to have the team colors. */
2186+ case _total_team_carnage_graph:
2187+ draw_team_totals_graph(outcome);
2188+ break;
2189+
2190+ case _total_team_scores_graph:
2191+ draw_team_total_scores_graph(outcome);
2192+ break;
2193+
2194+ default:
2195+ assert(false);
2196+ break;
2197+ }
2198+}
2199+
2200+// (ZZZ annotation:) Find the highest player vs. player kills
2201+// (so all player vs. player graphs can be at the same scale)
2202+short calculate_max_kills(
2203+ size_t num_players)
2204+{
2205+ short max_kills = 0;
2206+ size_t i, j;
2207+
2208+ for (i = 0; i < num_players; i++)
2209+ {
2210+ for (j = 0; j < num_players; j++)
2211+ {
2212+ struct player_data *player= get_player_data(i);
2213+
2214+ if (player->damage_taken[j].kills > max_kills)
2215+ {
2216+ max_kills= player->damage_taken[j].kills;
2217+ }
2218+ }
2219+ }
2220+
2221+ return max_kills;
2222+}
2223+
2224+
2225+
2226+// (ZZZ annotation:) Get postgame bar color for _suicide_color, etc.
2227+/* If alain wasn't a tool, this would be in a resource.. */
2228+void get_net_color(
2229+ short index,
2230+ RGBColor *color)
2231+{
2232+ switch(index)
2233+ {
2234+ case _suicide_color:
2235+ color->red= color->green= USHRT_MAX;
2236+ color->blue= 0;
2237+ break;
2238+ case _kill_color:
2239+ color->red= USHRT_MAX;
2240+ color->green= color->blue= 0;
2241+ break;
2242+ case _death_color:
2243+ case _score_color:
2244+ color->red= color->green= color->blue= 60000;
2245+ break;
2246+ default:
2247+ assert(false);
2248+ break;
2249+ }
2250+}
2251+
2252+
2253+// Get player name from outside
2254+// ZZZ random note: I didn't do this part, and I'm not sure it's right. At least, the
2255+// documentation seems a bit inconsistent. The MML docs say that it determines the
2256+// default player name in multiplayer. This is true, but more importantly, it determines
2257