1 /*
2  * Seven Kingdoms: Ancient Adversaries
3  *
4  * Copyright 1997,1998 Enlight Software Ltd.
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  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 //Filename    : OSYS.CPP
22 //Description : System resource management object
23 
24 #include <cstdio>
25 #include <cstdlib>
26 #include <limits.h>
27 #include <stdint.h>
28 #include <string>
29 
30 #ifdef HAVE__NSGETEXECUTABLEPATH
31 # include <mach-o/dyld.h>
32 #endif
33 
34 #include <RESOURCE.h>
35 #include <ALL.h>
36 #include <OAUDIO.h>
37 #include <ODATE.h>
38 #include <OBOX.h>
39 #include <OFONT.h>
40 #include <OSTR.h>
41 #include <OVGA.h>
42 #include <OGAME.h>
43 #include <ONEWS.h>
44 #include <OGAMESET.h>
45 #include <OSaveGameArray.h>
46 #include <OSaveGameProvider.h>
47 #include <OGAMHALL.h>
48 #include <OINFO.h>
49 #include <OVBROWSE.h>
50 #include <OIMGRES.h>
51 #include <OMOUSE.h>
52 #include <OMOUSE2.h>
53 #include <KEY.h>
54 #include <OMOUSECR.h>
55 #include <OUNIT.h>
56 #include <OSITE.h>
57 #include <OSPATH.h>
58 #include <OSPREUSE.h>
59 #include <OSPY.h>
60 #include <OSYS.h>
61 #include <OREMOTE.h>
62 #include <OTECHRES.h>
63 #include <OTALKRES.h>
64 #include <OGODRES.h>
65 #include <OHELP.h>
66 #include <OTUTOR.h>
67 #include <OF_BASE.h>
68 #include <OTOWN.h>
69 #include <OBULLET.h>
70 #include <ONATION.h>
71 #include <OFLAME.h>
72 #include <OPOWER.h>
73 #include <OTERRAIN.h>
74 #include <OWORLD.h>
75 #include <OANLINE.h>
76 #include <OSE.h>
77 #include <OLOG.h>
78 #include <OERRCTRL.h>
79 #include <OMUSIC.h>
80 #include <OLZW.h>
81 #include <OLONGLOG.h>
82 #include <OVGALOCK.h>
83 #include <OGRPSEL.h>
84 #include <OCRC_STO.h>
85 #include <OF_HARB.h>
86 // ##### begin Gilbert 23/10 ######//
87 #include <OOPTMENU.h>
88 #include <OINGMENU.h>
89 // ##### end Gilbert 23/10 ######//
90 #include <LocaleRes.h>
91 #include <CmdLine.h>
92 #include <FilePath.h>
93 #include <ConfigAdv.h>
94 
95 #include <dbglog.h>
96 #ifdef USE_WINDOWS
97 #include <direct.h>
98 #define chdir _chdir
99 #else
100 #include <unistd.h>
101 #endif
102 #include "gettext.h"
103 
104 DBGLOG_DEFAULT_CHANNEL(Sys);
105 
106 //----------- Declare static functions -----------//
107 
108 static void test_lzw();
109 
110 static void locate_king_general(int rankId);
111 static void locate_spy();
112 static void locate_ship();
113 static void locate_camp();
114 static int  locate_ship_in_harbor();
115 static int  locate_visible_ship();
116 static int  detect_scenario_cheat_key(unsigned scanCode, unsigned skeyState);
117 static int  get_mouse_loc_in_zoom_map(int &x, int &y);
118 static char random_race();
119 
120 //----------- Define static variables ------------//
121 
122 static unsigned long last_frame_time=0, last_resend_time=0;
123 static char          remote_send_success_flag=1;
124 static char          scenario_cheat_flag=0;
125 static short         last_frame_speed=0;
126 
127 static KeyEventType cheat_str[] = {
128    KEYEVENT_CHEAT_ENABLE1,
129    KEYEVENT_CHEAT_ENABLE1,
130    KEYEVENT_CHEAT_ENABLE1,
131    KEYEVENT_CHEAT_ENABLE2,
132    KEYEVENT_CHEAT_ENABLE2,
133    KEYEVENT_CHEAT_ENABLE2,
134    KEYEVENT_CHEAT_ENABLE3,
135    KEYEVENT_CHEAT_ENABLE3,
136    KEYEVENT_CHEAT_ENABLE3,
137    KEYEVENT_MAX
138 };
139 
get_bundle_resources_path(void)140 static std::string get_bundle_resources_path(void)
141 {
142 #ifdef HAVE__NSGETEXECUTABLEPATH
143    char path[PATH_MAX];
144    char real_path[PATH_MAX];
145    uint32_t buf_size = sizeof(path);
146 
147    int rv = _NSGetExecutablePath(path, &buf_size);
148 
149    if (rv != 0)
150       return "";
151 
152    if (realpath(path, real_path) == NULL)
153       return "";
154 
155    std::string res_path = real_path;
156    size_t sep = res_path.rfind('/');
157    if (sep == std::string::npos)
158       return "";
159 
160    res_path = res_path.substr(0, sep + 1);
161    res_path = res_path + "../Resources";
162 
163    return res_path;
164 #else
165    return "";
166 #endif
167 }
168 
169 //----------- Begin of function Sys::Sys -----------//
170 
Sys()171 Sys::Sys()
172 {
173    memset(this, 0, sizeof(Sys) );
174 
175    common_data_buf = mem_add( COMMON_DATA_BUF_SIZE );
176 
177    view_mode = MODE_NORMAL;         // the animation mode
178    sys_flag = SYS_PREGAME;
179 
180    is_mp_game = 0;
181    toggle_full_screen_flag = 0;
182    user_pause_flag = 0;
183    disp_fps_flag = 0;
184 }
185 //----------- End of function Sys::Sys -----------//
186 
187 
188 //----------- Begin of function Sys::~Sys -----------//
189 
~Sys()190 Sys::~Sys()
191 {
192    mem_del(common_data_buf);
193 
194    deinit();
195 }
196 //----------- End of function Sys::~Sys -----------//
197 
198 
199 //------------ Begin of function Sys::init ----------//
200 //
init()201 int Sys::init()
202 {
203    err_when( init_flag );
204 
205    //------- initialize basic vars --------//
206 
207 	#ifdef BETA
208 		debug_session       = misc.is_file_exist("DEBUG.SYS");
209 		testing_session     = misc.is_file_exist("TESTING.SYS");
210 		scenario_cheat_flag = misc.is_file_exist("CHEAT.SYS");
211 	#endif
212 
213 	#ifdef DEBUG
214 		debug_session       = misc.is_file_exist("DEBUG.SYS");
215 		testing_session     = misc.is_file_exist("TESTING.SYS");
216 		scenario_cheat_flag = misc.is_file_exist("CHEAT.SYS");
217 	#endif
218 
219 //	debug_session       = misc.is_file_exist("DEBUG.SYS");
220 
221    // set game directory paths and game version
222    if ( !set_game_dir() )
223       return 0;
224 
225    //------- initialize more stuff ---------//
226 
227    if( !init_directx() )
228       return 0;
229 
230    if( !init_objects() )   // initialize system objects which do not change from games to games.
231       return 0;
232 
233    init_flag = 1;
234 
235    return 1;
236 }
237 //------------ End of function Sys::init ----------//
238 
239 
240 //-------- Begin of function Sys::deinit --------//
241 //
242 // Finished with all objects we use; release them
243 //
deinit()244 void Sys::deinit()
245 {
246    if( !init_flag )
247       return;
248 
249    game.deinit();    // actually game.deinit() will be called by main_win_proc() and calling it here will have no effect
250 
251    deinit_objects();
252 
253    //-------------------------------------//
254 
255    if( vga_back.buf_locked )
256       vga_back.unlock_buf();
257 
258    if( vga_front.buf_locked )
259       vga_front.unlock_buf();
260 
261    //-------------------------------------//
262 
263    deinit_directx();
264 
265    init_flag = 0;
266 }
267 //--------- End of function Sys::deinit ---------//
268 
269 
270 //-------- Begin of function Sys::init_directx --------//
271 //
init_directx()272 int Sys::init_directx()
273 {
274 
275    //-------- initialize DirectDraw --------//
276 
277    DEBUG_LOG("Attempt vga.init()");
278    if( cmd_line.enable_if && !vga.init() )
279       return 0;
280    DEBUG_LOG("vga.init() ok");
281 
282    DEBUG_LOG("Attempt vga.load_pal()");
283    vga.load_pal(DIR_RES"PAL_STD.RES");
284    DEBUG_LOG("vga.load_pal() finish");
285 
286    if( sys.debug_session )                // if we are currently in a debug session, don't lock the front buffer otherwise the system will hang up
287    {
288       DEBUG_LOG("Attempt vga_front.init_back()");
289       vga_front.init(1);
290       DEBUG_LOG("Attempt vga_true_front.init_front()");
291       vga_true_front.init(1);
292       DEBUG_LOG("Attempt vga.activate_pal()");
293       vga.activate_pal (&vga_front);
294       vga.activate_pal(&vga_true_front);
295       DEBUG_LOG("vga.activate_pal() finish");
296    }
297    else
298    {
299       vga_front.init(1);
300       vga.activate_pal(&vga_front);
301    }
302 
303    DEBUG_LOG("Attempt vga_back.init_back()");
304    vga_back.init(0);
305    DEBUG_LOG("vga_back.init_back() finish");
306 
307    DEBUG_LOG("Attempt vga_front.lock_buf()");
308    vga_front.lock_buf();
309    DEBUG_LOG("vga_front.lock_buf() finish");
310 
311    DEBUG_LOG("Attempt vga_back.lock_buf()");
312    vga_back.lock_buf();
313    DEBUG_LOG("vga_back.lock_buf() finish");
314 
315    //---------- Initialize Audio ----------//
316 
317    DEBUG_LOG("Attempt audio.init()");
318    if( cmd_line.enable_if )
319       audio.init();
320    DEBUG_LOG(audio.wav_init_flag);
321    music.init();
322    se_ctrl.init();
323 
324    return 1;
325 }
326 //-------- End of function Sys::init_directx --------//
327 
328 
329 //-------- Begin of function Sys::deinit_directx --------//
330 //
deinit_directx()331 void Sys::deinit_directx()
332 {
333    se_ctrl.deinit();
334    music.deinit();
335    DEBUG_LOG("Attempt audio.deinit()");
336    audio.deinit();
337    DEBUG_LOG("audio.deinit() finish");
338 
339    //------------------------------//
340 
341    vga.save_status_report();
342    DEBUG_LOG("Attempt vga.deinit()");
343    vga.deinit();
344    DEBUG_LOG("vga.deinit() finish");
345 
346 }
347 //--------- End of function Sys::deinit_directx ---------//
348 
349 
350 //------- Begin of function Sys::init_objects -----------//
351 //
352 // Initialize system objects which do not change from games to games.
353 //
init_objects()354 int Sys::init_objects()
355 {
356    //--------- init system class ----------//
357 
358    mouse_cursor.init();
359    mouse_cursor.set_frame_border(ZOOM_X1,ZOOM_Y1,ZOOM_X2,ZOOM_Y2);
360 
361    mouse.init();
362 
363    //------- init resource class ----------//
364 
365    if( locale_res.fontset[0] )
366    {
367       String font;
368 
369       font = "STD_";
370       font += locale_res.fontset;
371       font_std.init(font, 1);
372 
373       font = "SAN_";
374       font += locale_res.fontset;
375       font_san.init(font, 0);
376 
377       font = "MID_";
378       font += locale_res.fontset;
379       font_mid.init(font);
380 
381       font = "SMAL_";
382       font += locale_res.fontset;
383       font_small.init(font);
384 
385       font = "NEWS_";
386       font += locale_res.fontset;
387       font_news.init(font);
388 
389       font = "CASA_";
390       font += locale_res.fontset;
391       font_bible.init(font, 1, 1);
392       font_bard.init(font, 0);
393    }
394    else
395    {
396       // fall back to original fonts
397       font_std.init("STD", 2);
398       font_san.init("SAN", 0);      // 0-zero inter-character space
399       font_mid.init("MID");
400       font_small.init("SMAL");
401       font_news.init("NEWS");
402       font_bible.init("CASA", 1, 3);
403       font_bard.init("CASA", 0);
404    }
405 
406    // non-localized fonts
407    font_hitpoint.init("HITP");
408 
409    #ifdef ENABLE_NLS
410       // use correct conversion for non-localized fonts
411       font_hitpoint.cd = locale_res.cd_latin;
412    #endif
413 
414    image_icon.init(DIR_RES"I_ICON.RES",1,0);       // 1-read into buffer
415    image_interface.init(DIR_RES"I_IF.RES",0,0);    // 0-don't read into the buffer, don't use common buffer
416 
417    #ifndef DEMO         // do not load these in the demo verison
418       image_menu.init(DIR_RES"I_MENU.RES",0,0);       // 0-don't read into the buffer, don't use common buffer
419       image_encyc.init(DIR_RES"I_ENCYC.RES",0,0); // 0-don't read into the buffer, don't use common buffer
420    #endif
421 
422    image_button.init(DIR_RES"I_BUTTON.RES",1,0);
423    image_spict.init(DIR_RES"I_SPICT.RES",1,0);
424    image_tutorial.init(DIR_RES"TUT_PICT.RES",0,0);
425 
426 		#ifndef DEMO         // do not load these in the demo verison
427 			image_menu_plus.init(DIR_RES"I_MENU2.RES",0,0);       // 0-don't read into the buffer, don't use common buffer
428 		#endif
429 
430    seek_path.init(MAX_BACKGROUND_NODE);
431    seek_path_reuse.init(MAX_BACKGROUND_NODE);
432    group_select.init();
433 
434    //------------ init flame ------------//
435 
436    for(int i = 0; i < FLAME_GROW_STEP; ++i)
437       flame[i].init(Flame::default_width(i), Flame::default_height(i), Flame::base_width(i), FLAME_WIDE);
438 
439    //------------ init animated line drawer -------//
440 
441    anim_line.init(ZOOM_X1, ZOOM_Y1, ZOOM_X2, ZOOM_Y2);
442 
443    //---------- init other objects ----------//
444 
445    game_set.init();     // this must be called before game.init() as game.init() assume game_set has been initialized
446    help.init("HELP.RES");
447 
448    tutor.init();
449    // Need to init hall_of_fame *before* save_game_array to persist the last savegame filename
450    hall_of_fame.init();
451    save_game_array.init("*.SAV");
452 
453    //---------- init game_set -----------//
454 
455    DEBUG_LOG("Sys::init_objects finish");
456 
457    return 1;
458 }
459 //------- End of function Sys::init_objects -----------//
460 
461 
462 //------- Begin of function Sys::deinit_objects -----------//
463 
deinit_objects()464 void Sys::deinit_objects()
465 {
466    //--------- deinit system class ----------//
467 
468    mouse.deinit();            // mouse must be deinitialized first
469    mouse_cursor.deinit();
470 
471    //------- deinit resource class ----------//
472 
473    font_std.deinit();
474    font_san.deinit();
475    font_mid.deinit();
476    font_small.deinit();
477    font_news.deinit();
478    font_hitpoint.deinit();
479    font_bible.deinit();
480 	font_bard.deinit();
481 
482    image_icon.deinit();
483    image_interface.deinit();
484    image_menu.deinit();
485    image_button.deinit();
486    image_spict.deinit();
487    image_encyc.deinit();
488    image_tutorial.deinit();
489 
490    seek_path.deinit();
491    seek_path_reuse.deinit();
492    group_select.deinit();
493 
494    for(int i = 0; i < FLAME_GROW_STEP; ++i)
495       flame[i].deinit();
496 
497    //--------- deinit other objects ----------//
498 
499    game_set.deinit();
500    help.deinit();
501 
502    tutor.deinit();
503    config.deinit();
504    // Need to deinit hall_of_fame *after* save_game_array to persist the last savegame filename
505    save_game_array.deinit();
506    hall_of_fame.deinit();
507 }
508 //------- End of function Sys::deinit_objects -----------//
509 
510 
511 //-------- Begin of function Sys::set_config_dir --------//
512 //
set_config_dir()513 int Sys::set_config_dir()
514 {
515    // Get the path for the config directory from SDL. Guaranteed to end with a path separator
516    char *home;
517 
518    home = getenv("SKCONFIG");
519    if( home )
520    {
521       strcpy(dir_config, home);
522       strcat(dir_config, PATH_DELIM);
523    }
524    else
525    {
526       home = SDL_GetPrefPath(CONFIG_ORGANIZATION_NAME, CONFIG_APPLICATION_NAME);
527       strcpy(dir_config, home);
528       SDL_free(home);
529    }
530 
531    MSG("Game config dir path: %s\n", dir_config);
532 
533    // create the config directory
534    if (!misc.mkpath(dir_config))
535    {
536       show_error_dialog(_("Unable to determine a location for storing the game configuration"));
537       dir_config[0] = 0;
538       return 0;
539    }
540 
541    return 1;
542 }
543 //------- End of function Sys::set_config_dir -----------//
544 
545 
546 //-------- Begin of function Sys::run --------//
547 //
run(int isLoadedGame)548 void Sys::run(int isLoadedGame)
549 {
550    //-*********** simulate aat ************-//
551    #ifdef DEBUG
552       //--------- enable only when simulation    -------//
553       debug_sim_game_type = (misc.is_file_exist("sim.sys")) ? 2 : 0;
554    #endif
555    //-*********** simulate aat ************-//
556 
557    sys_flag  = SYS_RUN;
558    view_mode = MODE_NORMAL;
559 
560    //------- test LZW compression ---------//
561 
562 #ifdef DEBUG_LZW
563    test_lzw();
564 #endif
565 
566    //------ reset mouse ---------//
567 
568    mouse.reset_click();
569    mouse_cursor.set_frame(0);
570 
571    //-- enable power after the game objets has been initialized --//
572 
573    power.enable();      // enable power, which handle mouse inputs
574 
575    //----- sys::disp_frame() will redraw everything when this flag is set to 1 ----//
576 
577    sys.need_redraw_flag = 1;
578    user_pause_flag = 0;
579 
580    option_menu.active_flag = 0;
581    in_game_menu.active_flag = 0;
582 
583    sys.disp_frame();
584    disp_view_mode();
585 
586    //----------- run the main loop -----------//
587 
588    main_loop(isLoadedGame);
589 
590    //-----------------------------------------//
591 
592    //------ reset mouse ---------//
593 
594    mouse.reset_click();
595    mouse.reset_boundary();
596    mouse_cursor.set_frame(0);
597 
598    misc.unlock_seed();
599 
600    sys_flag = SYS_PREGAME;
601 }
602 //--------- End of function Sys::run --------//
603 
604 
605 //-------- Begin of static function test_lzw --------//
606 //
test_lzw()607 static void test_lzw()
608 {
609    // test lzw compress
610    if( misc.is_file_exist("NORMAL.SAV")) // BUGHERE: Should use a full path, using sys.dir_config
611    {
612       File f,g;
613       Lzw lzw_c, lzw_d;    // one for compress, the other for decompress
614       f.file_open("NORMAL.SAV"); // BUGHERE: <same as above>
615 
616       // read into buffer
617       long fileSize = f.file_size();
618       unsigned char *srcPtr = (unsigned char *) mem_add(fileSize);
619       f.file_read(srcPtr, fileSize);
620 
621       // find compressed size to allocate space
622       long compSize = lzw_c.compress(srcPtr, fileSize);
623       unsigned char *destPtr = (unsigned char *) mem_add( compSize/8+ 4 );       // alloc 4 bytes more
624       if( compSize != lzw_c.compress(srcPtr, fileSize, destPtr) )
625       {
626          err_here();
627       }
628 
629       // decompress again
630       long backSize = lzw_d.expand(destPtr, compSize, NULL);
631       err_when(backSize != fileSize);
632       unsigned char *backPtr = (unsigned char *) mem_add( backSize+4 );
633       if( backSize != lzw_d.expand(destPtr, compSize, backPtr) )
634       {
635          err_here();
636       }
637 
638       // finally compare srcPtr and backPtr
639       err_when( memcmp(srcPtr, backPtr, fileSize) );
640 
641       f.file_close();
642 
643       // write it to a file
644       {
645          unsigned char *writePtr = destPtr;
646          long writeSize = (compSize +7) / 8;
647          g.file_create("NORMAL.LZ1");
648          for( ; writeSize > 0; writeSize -= 0x4000)
649          {
650             g.file_write(writePtr, writeSize > 0x4000 ? 0x4000 : writeSize);
651             writePtr += 0x4000;
652          }
653          g.file_close();
654       }
655 
656       // test two, compress to a file
657       g.file_create("NORMAL.LZW");
658       compSize = lzw_c.compress(srcPtr, fileSize, &g);
659       g.file_close();
660 
661       g.file_open("NORMAL.LZW");
662       backSize = lzw_d.expand(&g, NULL);
663       err_when(backSize != fileSize);
664       backPtr = (unsigned char *) mem_resize(backPtr, backSize+4 );
665       g.file_close();
666 
667       g.file_open("NORMAL.LZW");
668       if( backSize != lzw_d.expand(&g, backPtr))
669       {
670          err_here();
671       }
672       err_when( memcmp(srcPtr,backPtr, fileSize) );
673 
674       mem_del(destPtr);
675       mem_del(backPtr);
676       mem_del(srcPtr);
677    }
678 }
679 //--------- End of static function test_lzw --------//
680 
681 
682 //-------- Begin of function Sys::main_loop --------//
683 //
main_loop(int isLoadedGame)684 void Sys::main_loop(int isLoadedGame)
685 {
686    // #### begin Gilbert 31/10 #####//
687    // int rc;
688    // #### end Gilbert 31/10 #####//
689 
690    //-------- reset day_frame_count -------//
691 
692    if( !isLoadedGame )
693    {
694       day_frame_count = 0;       // for determining when the day counter should be increased.
695       frame_count = 1;
696    }
697 
698    //----- initialize these vars for every game -----//
699 
700    for( int i=nation_array.size() ; i>0 ; i-- )
701    {
702       if( !nation_array.is_deleted(i) )
703          nation_array[i]->next_frame_ready = 0;
704    }
705 
706    remote.packet_send_count    = 0;
707    remote.packet_receive_count = 0;
708 
709    last_frame_time = misc.get_time()+60000;     // plus 60 seconds buffer for game loading/starting time
710    //frame_count     = 1;
711    is_sync_frame   = 0;
712 
713    //----------------------------------------------//
714    mp_clear_request_save();
715    remote.enable_poll_msg();
716    remote.enable_process_queue();
717    remote_send_success_flag = 1;
718 
719 #ifdef DEBUG_LONG_LOG
720    char longLogSuffix = 'A';
721    if( remote.is_enable() )
722    {
723       if(long_log)
724          delete long_log;
725       long_log = new LongLog(longLogSuffix);
726    }
727 #endif
728 
729    //-*********** syn game test ***********-//
730    #ifdef DEBUG
731    if(debug_seed_status_flag==DEBUG_SYN_LOAD_AND_COMPARE_ONCE)
732       sp_load_seed_file();
733    #endif
734    //-*********** syn game test ***********-//
735 
736    vga_front.unlock_buf();
737 
738    // ------- establish_contact again --------//
739    // if the game saved after NationRelation::contact_msg_flag set, but
740    // remote players may have not receive MSG_NATION_CONTACT
741    //
742    // send MSG_NATION_CONTACT now
743    if( !config.explore_whole_map && nation_array.player_recno &&
744       !nation_array.is_deleted(nation_array.player_recno) )
745    {
746       for(short nationRecno = 1; nationRecno <= nation_array.size(); ++nationRecno )
747       {
748          if( nationRecno == nation_array.player_recno ||
749             nation_array.is_deleted(nationRecno) )
750             continue;
751 
752          NationRelation *relation = (~nation_array)->get_relation(nationRecno);
753          if( relation->contact_msg_flag && !relation->has_contact)
754          {
755             // packet structure : <player nation> <explored nation>
756             int16_t *shortPtr = (int16_t *)remote.new_send_queue_msg(MSG_NATION_CONTACT, 2*sizeof(int16_t));
757             *shortPtr = nation_array.player_recno;
758             shortPtr[1] = nationRecno;
759          }
760       }
761    }
762 
763    // #### begin Gilbert 23/10 #######//
764    option_menu.active_flag = 0;
765    in_game_menu.active_flag = 0;
766    // #### end Gilbert 23/10 #######//
767 
768    // ##### begin Gilbert 4/11 ######//
769    // time the screen was redrawn last time
770    // (despite the name, this time may differ from last frame's time)
771    uint32_t lastDispFrameTime = misc.get_time();
772    // ##### end Gilbert 4/11 ######//
773 
774 	// ##### patch begin Gilbert 17/11 #######//
775 	// used to determine, if there are players who are
776 	// unable to send their frames for a too long time
777 	uint32_t firstUnreadyTime = 0;
778 	// ##### patch end Gilbert 17/11 #######//
779 
780    while( 1 )
781    {
782          // #### begin Gilbert 31/10 ######//
783          int rc = 0;
784          // #### end Gilbert 31/10 ######//
785          if( sys.signal_exit_flag )
786             break;
787 
788          vga_front.lock_buf();
789 
790          yield();       // could be improved, give back the control to Windows, so it can do some OS management. Maybe call WaitMessage() here and set up a timer to get messages regularly.
791          if( cmd_line.enable_if )
792             vga.flip();
793 
794          detect();
795 
796          //--------------------------------//
797 
798          // ###### begin Gilbert 4/11 ######//
799          // Time taken at the beginning of new loop iteration. It's important,
800          // that it's taken _before_ 'should_next_frame'. This variable is
801          // further used to determine how many time passed since screen was
802          // redrawn last time.
803          uint32_t markTime = misc.get_time();
804          // ###### end Gilbert 4/11 ######//
805 
806 			// ##### patch begin Gilbert 17/11 #######//
807 			int unreadyPlayerFlag = 0;
808 			// ##### patch end Gilbert 17/11 #######//
809 
810 	//------- play sound effect ------//
811 
812 	se_ctrl.flush();
813 
814          if( config.frame_speed>0 )              // 0-frozen
815          {
816             if( remote.is_enable() )      // && is_sync_frame )
817             {
818                remote.poll_msg();
819                misc.unlock_seed();
820                rc = is_mp_sync(&unreadyPlayerFlag);         // if all players are synchronized
821                misc.lock_seed();
822             }
823             else
824                rc = should_next_frame();
825 
826             if( rc )
827             {
828                LOG_BEGIN;
829                misc.unlock_seed();
830 
831                if( remote.is_replay() )
832                   remote.process_receive_queue();
833 
834 #ifdef DEBUG_LONG_LOG
835                if( remote.is_enable() )
836                {
837                   long_log->printf("begin process frame %d\n", frame_count);
838                }
839 #endif
840 
841                process(); // also calls 'disp_frame()'
842 
843                if(remote.is_enable() )
844                   misc.lock_seed();    // such that random seed is unchanged outside sys::process()
845                LOG_END;
846 
847                // -------- compare objects' crc --------- //
848                // ###### patch begin Gilbert 20/1 ######//
849                if( (remote.is_enable() || remote.is_replay()) && (remote.sync_test_level & 2) && (frame_count % (remote.get_process_frame_delay()+3)) == 0 )
850                {
851                   // cannot compare every frame, as PROCESS_FRAME_DELAY >= 1
852                   crc_store.record_all();
853                   if( !remote.is_replay() )
854                      crc_store.send_all();
855                }
856                // ###### patch end Gilbert 20/1 ######//
857 
858             }
859          }
860 
861          // ###### begin Gilbert 4/11 #######//
862          // ------- display gradually, keep on displaying --------- //
863          if( rc )
864          {
865             lastDispFrameTime = misc.get_time();
866 				// ####### patch begin Gilbert 17/11 ######//
867 				// reset firstUnreadyTime
868 				firstUnreadyTime = 0;
869 				// ####### patch end Gilbert 17/11 ######//
870          }
871          else
872          {
873 				// ####### patch begin Gilbert 17/11 ######//
874 				// set firstUnreadyTime, begin of a delay
875 				if( !firstUnreadyTime )
876 					firstUnreadyTime = misc.get_time();
877 				// ####### patch end Gilbert 17/11 ######//
878 
879             // although it's not time for new frame, check
880             // if we still need to redraw the screen
881             if( config.frame_speed == 0 || markTime-lastDispFrameTime >= uint32_t(1000/config.frame_speed)
882 					|| zoom_need_redraw || map_need_redraw
883 					)
884             {
885                // second condition (markTime-lastDispFrameTime >= DWORD(1000/config.frame_speed) )
886                // may happen in multiplayer, where 'should_next_frame' would pass (what means it's time
887                // to process new frame according to config.frame_speed), but 'is_mp_sync' still failed.
888                if( cmd_line.enable_if )
889                   disp_frame();
890                lastDispFrameTime = markTime;
891 
892 					// ####### patch begin Gilbert 17/11 ######//
893 					// If somebody is not ready for more than five seconds
894 					// (may happen in multiplayer), display info message
895 					if( firstUnreadyTime && misc.get_time() - firstUnreadyTime > 5000 )
896 					{
897 						int y = ZOOM_Y1 + 10;
898 						int x = ZOOM_X1 + 10;
899 						for( int nationRecno = 1; nationRecno <= MAX_NATION; ++nationRecno )
900 						{
901 							if( unreadyPlayerFlag & (1 << (nationRecno-1)) )
902 							{
903 								if( !nation_array.is_deleted(nationRecno) )
904 								{
905 									String newsStr;
906 									// TRANSLATORS: Waiting for <King>'s Kingdom
907 									snprintf( newsStr, MAX_STR_LEN+1, _("Waiting for %s's Kingdom"), nation_array[nationRecno]->king_name(1) );
908 									int x2 = font_news.put( x, y, newsStr );
909 									y += font_news.height() + 5;
910 								}
911 							}
912 						}
913 					}
914 					// ####### patch end Gilbert 17/11 ######//
915             }
916          }
917          // ###### end Gilbert 4/11 #######//
918 
919          // ----------- detect if song has ended, play another -----------//
920 
921          if( config.frame_speed == 0 || day_frame_count == 0)
922             music.yield();
923 
924 #ifdef DEBUG_LONG_LOG
925          if( rc && remote.is_enable() && day_frame_count == 0 )
926          {
927             if( long_log)
928                delete long_log;
929 
930             if( ++longLogSuffix > 'Z' )
931             {
932                longLogSuffix = 'A';
933             }
934             long_log = new LongLog(longLogSuffix);
935             long_log->printf("Game Date : %d/%d/%d\n", info.game_month, info.game_day, info.game_year);
936          }
937 #endif
938 
939          if(rc)
940          {
941             //-*********** syn game test ***********-//
942             //-------------------------------------------------------------//
943             // record random seed for comparison
944             //-------------------------------------------------------------//
945             #ifdef DEBUG
946                if(debug_seed_status_flag==DEBUG_SYN_LOAD_AND_COMPARE_ONCE ||
947                   debug_seed_status_flag==DEBUG_SYN_AUTO_LOAD)
948                   sp_compare_seed();
949                else if(debug_seed_status_flag==DEBUG_SYN_AUTO_SAVE)
950                   sp_record_seed();
951             #endif
952             //-*********** syn game test ***********-//
953 
954             //------ auto save -------//
955 
956             auto_save();
957          }
958 
959          //------ detect save game triggered by remote player ------//
960 
961          if( mp_save_flag && mp_save_frame == frame_count )
962          {
963             mp_clear_request_save();            // clear request first before save game
964 
965             if( nation_array.player_recno )     // only save when the player is still in the game
966             {
967                SaveGameProvider::save_game(remote.save_file_name);
968 
969                // ####### begin Gilbert 24/10 ######//
970                //static String str;
971                //str  = "The current game has been saved to ";
972                //str += remote.save_file_name;
973                //str += ".";
974                //box.msg( str );
975                news_array.multi_save_game();
976                // ####### end Gilbert 24/10 ######//
977             }
978          }
979 
980          vga_front.unlock_buf();
981    }
982 
983    // #### begin Gilbert 23/10 #######//
984    in_game_menu.active_flag = 0;
985    option_menu.active_flag = 0;
986    // #### end Gilbert 23/10 #######//
987 
988    vga_front.lock_buf();
989 
990 #ifdef DEBUG_LONG_LOG
991    if(remote.is_enable())
992    {
993       if(long_log)
994          delete long_log;
995       long_log = NULL;
996    }
997 #endif
998 
999    music.stop();
1000    remote.disable_process_queue();
1001    remote.disable_poll_msg();
1002    mp_clear_request_save();
1003 }
1004 //--------- End of function Sys::main_loop --------//
1005 
1006 
1007 //-------- Begin of function Sys::auto_save --------//
1008 //
auto_save()1009 void Sys::auto_save()
1010 {
1011    if( nation_array.player_recno == 0 )
1012       return;
1013 
1014    //---------- single player auto save ----------//
1015 
1016    if( !remote.is_enable() &&          // no auto save in a multiplayer game
1017        info.game_month%2==0 && info.game_day==1 && day_frame_count==0)
1018    {
1019       #ifdef DEBUG2
1020       if(1)
1021       #else
1022       if( sys.debug_session || sys.testing_session )
1023       #endif
1024       {
1025          static int saveCount = 0;
1026          switch(saveCount)
1027          {
1028             case 0:  SaveGameProvider::save_game("DEBUG1.SAV");
1029                      break;
1030 			case 1:  SaveGameProvider::save_game("DEBUG2.SAV");
1031                      break;
1032 			case 2:  SaveGameProvider::save_game("DEBUG3.SAV");
1033                      break;
1034          }
1035          if( ++saveCount>=3 )
1036             saveCount = 0;
1037       }
1038       else
1039       {
1040          //---------- get path to savegames ----------//
1041 
1042          FilePath auto1_path(dir_config);
1043          FilePath auto2_path(dir_config);
1044 
1045          auto1_path += "AUTO.SAV";
1046          auto2_path += "AUTO2.SAV";
1047          if( auto1_path.error_flag || auto2_path.error_flag )
1048             return;
1049 
1050          //--- rename the existing AUTO.SAV to AUTO2.SAV and save a new game ---//
1051 
1052          if( misc.is_file_exist( auto1_path ) )
1053          {
1054             if( misc.is_file_exist( auto2_path ) )      // if there is already an AUTO2.SAV, delete it
1055                remove( auto2_path );
1056 
1057             rename( auto1_path, auto2_path );
1058          }
1059 
1060          SaveGameProvider::save_game("AUTO.SAV");
1061       }
1062 
1063       //-*********** syn game test ***********-//
1064       #ifdef DEBUG
1065          if(debug_seed_status_flag==DEBUG_SYN_AUTO_SAVE)
1066          {
1067             sp_write_seed();
1068             sp_close_seed_file();
1069 
1070             debug_seed_status_flag = NO_DEBUG_SYN;
1071             // DIK_BACKSLASH = 0x2B
1072             mouse.add_key_event(0x2B, misc.get_time()); // load file for comparison
1073          }
1074 
1075          //debug_seed_status_flag = 2;
1076          //sp_seed_pos_reset();
1077          //sp_record_match_seed();
1078       #endif
1079       //-*********** syn game test ***********-//
1080    }
1081 
1082    // --------- multiplayer autosave game --------//
1083 
1084 	// ###### patch begin Gilbert 23/1 #######//
1085    if( remote.is_enable() && remote.sync_test_level >= 0 &&			// disable autosave after un-sync
1086       day_frame_count==0 && info.game_day==1 && info.game_month%2==0 )
1087 	// ###### patch end Gilbert 23/1 #######//
1088    {
1089       //---------- get path to savegames ----------//
1090 
1091       FilePath auto1_path(dir_config);
1092       FilePath auto2_path(dir_config);
1093 
1094       auto1_path += "AUTO.SVM";
1095       auto2_path += "AUTO2.SVM";
1096       if( auto1_path.error_flag || auto2_path.error_flag )
1097          return;
1098 
1099       //--- rename the existing AUTO.SVM to AUTO2.SVM and save a new game ---//
1100 
1101       if( misc.is_file_exist( auto1_path ) )
1102       {
1103          if( misc.is_file_exist( auto2_path ) )      // if there is already an AUTO2.SVM, delete it
1104             remove( auto2_path );
1105 
1106          rename( auto1_path, auto2_path );
1107       }
1108 
1109       SaveGameProvider::save_game("AUTO.SVM");
1110    }
1111 }
1112 //-------- End of function Sys::auto_save --------//
1113 
1114 
1115 //-------- Begin of function Sys::pause --------//
1116 //
1117 // If the game is running, pause the game. For the window manager to pause
1118 // the game when focus is lost.
1119 //
pause()1120 void Sys::pause()
1121 {
1122    if( config.frame_speed && sys_flag == SYS_RUN )
1123    {
1124       set_speed( 0 );
1125    }
1126 }
1127 //--------- End of function Sys::pause ---------//
1128 
1129 
1130 //-------- Begin of function Sys::unpause --------//
1131 //
1132 // If the game is not running, unpause the game. For the window manager to
1133 // unpause the game when focus is gained. Will not unpause if the user actually
1134 // paused the game first.
1135 //
unpause()1136 void Sys::unpause()
1137 {
1138    if( !config.frame_speed && sys_flag == SYS_RUN && !user_pause_flag )
1139    {
1140       set_speed( 0 );
1141    }
1142 }
1143 //--------- End of function Sys::unpause ---------//
1144 
1145 
1146 //-------- Begin of function Sys::show_error_dialog ----------//
1147 //
1148 // Show SDL error dialog that does not depend on video init. This is a blocking
1149 // routine, and never should be used after Sys::init.
1150 //
show_error_dialog(const char * formatStr,...)1151 void Sys::show_error_dialog(const char *formatStr, ...)
1152 {
1153    enum { RESULT_STR_LEN=200 };
1154 
1155    static char resultStr[RESULT_STR_LEN+1];
1156 
1157    va_list argPtr;
1158 
1159    va_start( argPtr, formatStr );
1160    vsnprintf( resultStr, RESULT_STR_LEN, formatStr, argPtr );
1161    va_end( argPtr );
1162 
1163    deinit_directx(); // in case vga is full screen, destroy game window to ensure dialog is visible
1164    SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Seven Kingdoms", resultStr, NULL );
1165 }
1166 //----------- End of function Sys::show_error_dialog ----------//
1167 
1168 
1169 //-------- Begin of function Sys::yield --------//
1170 //
yield()1171 void Sys::yield()
1172 {
1173    static int isYielding=0;
1174 
1175    if( isYielding )
1176       return;
1177 
1178    isYielding=1;
1179 
1180    vga.handle_messages();
1181 
1182    if (toggle_full_screen_flag)
1183    {
1184       toggle_full_screen_flag = 0;
1185       vga.set_full_screen_mode(-1);
1186    }
1187 
1188    mouse.poll_event();
1189 
1190    audio.yield();
1191 
1192    if( remote.is_enable() )
1193    {
1194       //yield_wsock_msg();
1195       remote.poll_msg();
1196 
1197       remote.process_specific_msg(MSG_SET_SPEED);        // need to test it here for restoring the speed from frozen to normal
1198 
1199       if( config.frame_speed > 0 )
1200       {
1201          remote.process_specific_msg(MSG_TELL_SEND_TIME);
1202          remote.process_specific_msg(MSG_REQUEST_RESEND);
1203       }
1204 
1205       //-------- display debug info -----------//
1206 
1207       if( power.enable_flag && (testing_session || debug_session) )
1208       {
1209          String str;
1210 
1211          str  = "Player: ";
1212          str += nation_array.player_recno;
1213          str += "/";
1214          str += nation_array.size();
1215 
1216          str += " Send:";
1217          str += remote.packet_send_count;
1218          str += " Recv:";
1219          str += remote.packet_receive_count;
1220          str += " Frame:";
1221          str += frame_count;
1222 
1223          font_san.disp( ZOOM_X1, 4, str, ZOOM_X1+300);
1224       }
1225    }
1226 
1227    isYielding=0;
1228 }
1229 //--------- End of function Sys::yield ---------//
1230 
1231 
1232 //-------- Begin of function Sys::yield_wsock_msg --------//
1233 //
yield_wsock_msg()1234 void Sys::yield_wsock_msg()
1235 {
1236    // MSG msg;
1237 
1238    //------ only get WinSock messages (WSA_ACCEPT & WSA_READ) ------//
1239 
1240    // if( PeekMessage(&msg, NULL, WSA_ACCEPT, WSA_READ, PM_NOREMOVE) )
1241    // {
1242    //   if (!GetMessage( &msg, NULL, WSA_ACCEPT, WSA_READ))
1243    //       return;
1244 
1245    //   TranslateMessage(&msg);
1246    //   DispatchMessage(&msg);
1247    //}
1248 }
1249 //--------- End of function Sys::yield_wsock_msg ---------//
1250 
1251 
1252 //-------- Begin of function Sys::is_mp_sync --------//
1253 //
1254 // Multiplayer synchronization.
1255 //
1256 // Check all players are ready to proceed to the next frame.
1257 //
is_mp_sync(int * unreadyPlayerFlag)1258 int Sys::is_mp_sync(int *unreadyPlayerFlag)
1259 {
1260    #define RESEND_TIME_OUT            2000    // if the other machines still aren't ready after 2 seconds, send the notification again
1261    #define RESEND_AGAIN_TIME_OUT      1000    // keep resending if no responses
1262    #define CONNECTION_LOST_TIME_OUT  20000    // ask for connection lost handling aftering waiting for 5 seconds.
1263 
1264    //---- if we haven't been ready for the next frame yet ----//
1265 
1266 #ifdef DEBUG
1267    int n;
1268    DEBUG_LOG("begin nation's next_frame_ready");
1269    for (n = 1; n <= nation_array.size(); ++n)
1270    {
1271       DEBUG_LOG(nation_array[n]->next_frame_ready);
1272    }
1273    DEBUG_LOG("end nation's next_frame_ready");
1274 #endif
1275 
1276 	// ####### patch begin Gilbert 17/11 ######//
1277 	if( unreadyPlayerFlag )
1278 		*unreadyPlayerFlag = 0;
1279 	// ####### end begin Gilbert 17/11 ######//
1280 
1281    // if last remote.send was fail, attempt to send it again
1282    if( !nation_array.player_recno )
1283    {
1284       // observation mode
1285       if( !should_next_frame() )
1286          return 0;
1287       remote_send_success_flag = 1;
1288    }
1289    else if( remote_send_success_flag
1290 		&& remote.has_send_frame(nation_array.player_recno, frame_count)
1291 		&& (~nation_array)->next_frame_ready==0 )
1292    {
1293       //DEBUG_LOG("Local player not ready");
1294       if( !should_next_frame() )    // not ready to proceed yet
1295          return 0;
1296 
1297       //------------ queue MSG_NEXT_FRAME ----------//
1298 
1299       short* shortPtr = (short*) remote.new_send_queue_msg(MSG_NEXT_FRAME, sizeof(short));
1300 
1301       shortPtr[0] = nation_array.player_recno;  // short_para1 is the nation recno of the current player
1302 
1303       //------------ queue MSG_QUEUE_TRAILER ----------//
1304 
1305       shortPtr = (short*) remote.new_send_queue_msg(MSG_QUEUE_TRAILER, sizeof(short));
1306       shortPtr[0] = nation_array.player_recno;  // short_para1 is the nation recno of the current player
1307 
1308       //------ copy all queued action to our own receive buffer to merge with other player's action ----//
1309 
1310       remote.append_send_to_receive();
1311 
1312       //--- copy the whole queue to a buffer in case of resend request from other players ---//
1313 
1314       remote.copy_send_to_backup();
1315 
1316       //----------- queue MSG_TELL_SEND_TIME ----------//
1317 /*
1318       unsigned long* longPtr = (unsigned long*) remote.new_send_queue_msg(MSG_TELL_SEND_TIME, sizeof(unsigned long));
1319 
1320       longPtr[0] = misc.get_time();
1321 */
1322       //---------- send out all messages in the queue ---------//
1323 
1324       remote_send_success_flag = remote.send_queue_now();               // if not sent successfully, try again next time
1325 
1326       if( remote_send_success_flag )      // still failed, try again next time
1327       {
1328          DEBUG_LOG("First send success" );
1329          remote.init_send_queue(frame_count+1, nation_array.player_recno);    // frame_count, initialize for next frame's send queue
1330          // sent random seed
1331          char *p = (char *)remote.new_send_queue_msg(MSG_TELL_RANDOM_SEED, sizeof(short)+sizeof(int32_t));
1332          *(short *)p = nation_array.player_recno;
1333          p += sizeof(short);
1334          *(int32_t *)p = misc.get_random_seed();
1335       }
1336       else
1337       {
1338          // re_transmit as quickly as possible
1339          ec_remote.re_transmit(5);
1340       }
1341    }
1342    else
1343    {
1344       DEBUG_LOG("Local player nation ready");
1345    }
1346 
1347    //----- if previous sending was not successful, send again now -----//
1348 
1349    if( !remote_send_success_flag )
1350    {
1351       remote_send_success_flag = remote.send_queue_now();               // if not sent successfully, try again next time
1352 
1353       if( remote_send_success_flag )      // still failed, try again next time
1354       {
1355          DEBUG_LOG("resending ok");
1356          remote.init_send_queue(frame_count+1, nation_array.player_recno);    // frame_count, initialize for next frame's send queue
1357          // sent random seed
1358          char *p = (char *)remote.new_send_queue_msg(MSG_TELL_RANDOM_SEED, sizeof(short)+sizeof(int32_t));
1359          *(short *)p = nation_array.player_recno;
1360          p += sizeof(short);
1361          *(int32_t *)p = misc.get_random_seed();
1362       }
1363       else
1364       {
1365          // re_transmit as quickly as possible
1366          ec_remote.re_transmit(5);
1367          DEBUG_LOG("resending not ok");
1368          return 0;
1369       }
1370    }
1371 
1372    //------ pre_process MSG_NEXT_FRAME in the queue -----//
1373 
1374    remote.process_specific_msg(MSG_NEXT_FRAME);
1375 
1376 #ifdef DEBUG
1377    DEBUG_LOG("begin nation's next_frame_ready");
1378    for (n = 1; n <= nation_array.size(); ++n)
1379    {
1380       DEBUG_LOG(nation_array[n]->next_frame_ready);
1381    }
1382    DEBUG_LOG("end nation's next_frame_ready");
1383 #endif
1384 
1385    //------ check if all remote players are ready to proceed -----//
1386 
1387    int     nationRecno;
1388    Nation* nationPtr;
1389 
1390    for( nationRecno=nation_array.size() ; nationRecno>0 ; nationRecno-- )
1391    {
1392       if( nation_array.is_deleted(nationRecno) )
1393          continue;
1394 
1395       nationPtr = nation_array[nationRecno];
1396 
1397       //------- if some remote machines are not ready yet -------//
1398 
1399 		// ###### patch begin Gilbert 17/11 ######//
1400       if( nationPtr->is_remote() &&
1401 			(remote.has_send_frame(nationRecno, frame_count) && !nationPtr->next_frame_ready) )
1402 		{
1403 			if( unreadyPlayerFlag )
1404 				*unreadyPlayerFlag |= ( 1 << (nationRecno-1) );
1405          break;
1406 		}
1407 		// ###### end begin Gilbert 17/11 ######//
1408    }
1409 
1410    //------- if some remote machines are not ready yet -------//
1411 
1412    if( nationRecno>0 )
1413    {
1414       if ( !ec_remote.is_player_valid(nationRecno) )
1415       {
1416          DEBUG_LOG("Connection Lost");
1417          DEBUG_LOG(nationRecno);
1418 
1419          //---- the connection was lost with a remote player, let the ai take over ----//
1420          news_array.multi_connection_lost(nationRecno);
1421          nationPtr->nation_type = NATION_AI;
1422          nation_array.ai_nation_count++;
1423       }
1424 
1425       return 0;
1426    }
1427 
1428    //--------------------------------------------------------//
1429    //
1430    // When all players are ready to proceed to the next frame
1431    //
1432    // As we have already know all players are ready, we can
1433    // reset the next_frame_ready flag for all nations.
1434    //
1435    //--------------------------------------------------------//
1436 
1437    DEBUG_LOG("all nation ready");
1438    for( int i=nation_array.size() ; i>0 ; i-- )
1439    {
1440       if( nation_array.is_deleted(i) )
1441          continue;
1442 
1443       nation_array[i]->next_frame_ready=0;      // -- instead of set to 0, set it may be 2 if it has just received an notifying signal for the further next frame from a player as it also sent out a next frame ready msg to all other players
1444    }
1445 
1446    //--------- process msgs in the receive queue ----------//
1447 
1448    remote.process_receive_queue();
1449 
1450 #ifdef DEBUG
1451    DEBUG_LOG("begin nation's next_frame_ready");
1452    for (n = 1; n <= nation_array.size(); ++n)
1453    {
1454       DEBUG_LOG(nation_array[n]->next_frame_ready);
1455    }
1456    DEBUG_LOG("end nation's next_frame_ready");
1457 #endif
1458 
1459    //-------- record this frame's time -------//
1460 
1461    last_frame_time  = misc.get_time();
1462    last_resend_time = 0;
1463 
1464    return 1;
1465 }
1466 //---------- End of function Sys::is_mp_sync --------//
1467 
1468 
1469 //-------- Begin of function Sys::should_next_frame --------//
1470 //
1471 // Check if it's now the time for processing the next frame.
1472 //
should_next_frame()1473 int Sys::should_next_frame()
1474 {
1475    //----- special modes: 0-frozen, 9-fastest possible -----//
1476 
1477    if( config.frame_speed==99 )
1478       return 1;
1479 
1480    if( config.frame_speed==0 )
1481       return 0;
1482 
1483    //---- check if it's now the time for processing the next frame ----//
1484 
1485    uint32_t curTime = misc.get_time();
1486 
1487    if( next_frame_time )      // if next_frame_time==0, it's the first frame of the game
1488    {
1489       if( next_frame_time < 1000 )  // the uint32_t variable has been overflow
1490       {
1491          if( curTime < next_frame_time || curTime >= 1000 )    // >= 1000 if the curTime has been overflow yet, wait for it to overflow so we can compare it when next_frame_time
1492             return 0;
1493       }
1494       else     // normal non-overflow case
1495       {
1496          if( curTime < next_frame_time )
1497             return 0;
1498       }
1499    }
1500 
1501    //--- Time between frames = 1000 milliseconds / frames per second ---//
1502 
1503    next_frame_time = curTime + 1000 / config.frame_speed;
1504 
1505    return 1;
1506 }
1507 //--------- End of function Sys::should_next_frame ---------//
1508 
1509 
1510 //-------- Begin of function Sys::process_key --------//
1511 //
process_key(unsigned scanCode,unsigned skeyState)1512 void Sys::process_key(unsigned scanCode, unsigned skeyState)
1513 {
1514    detect_function_key(scanCode, skeyState);
1515 
1516    //----- don't detect letter keys when in chat mode ----//
1517 
1518    if( !(view_mode == MODE_NATION &&
1519          info.nation_report_mode == NATION_REPORT_CHAT) )
1520    {
1521       if( sys.debug_session || sys.testing_session || scenario_cheat_flag )
1522       {
1523          detect_cheat_key(scanCode, skeyState);
1524 
1525          if( detect_debug_cheat_key(scanCode, skeyState) || detect_scenario_cheat_key(scanCode, skeyState) )
1526             return;
1527       }
1528       else
1529       {
1530          if( nation_array.player_recno && !remote.is_enable() )      // not allowed in multiplayer mode
1531          {
1532             if( (~nation_array)->cheat_enabled_flag )
1533             {
1534                detect_cheat_key(scanCode, skeyState);
1535             }
1536             else
1537             {
1538                if( detect_key_str(1, cheat_str) )
1539                {
1540                   box.msg( _("Cheat Mode Enabled.") );
1541                   (~nation_array)->cheat_enabled_flag = 1;
1542                }
1543             }
1544          }
1545       }
1546 
1547       detect_letter_key(scanCode, skeyState);
1548 
1549       detect_set_speed(scanCode, skeyState);                     // set the speed of the game
1550    }
1551 }
1552 //--------- End of function Sys::process_key ---------//
1553 
1554 
1555 //-------- Begin of function Sys::detect_letter_key --------//
1556 //
detect_letter_key(unsigned scanCode,unsigned skeyState)1557 void Sys::detect_letter_key(unsigned scanCode, unsigned skeyState)
1558 {
1559    int keyCode;
1560 
1561    if((keyCode = mouse.is_key(scanCode, skeyState, (unsigned short) 0, K_IS_CTRL)))
1562    {
1563       int groupId;
1564       switch(keyCode)
1565       {
1566          case '1': case '2': case '3': case '4': case '5':
1567          case '6': case '7': case '8': case '9':
1568             groupId = keyCode-'0';
1569             group_select.group_units(groupId);
1570             break;
1571       }
1572    }
1573 
1574    if((keyCode = mouse.is_key(scanCode, skeyState, (unsigned short) 0, K_IS_ALT)))
1575    {
1576       int groupId;
1577       switch(keyCode)
1578       {
1579          case '1': case '2': case '3': case '4': case '5':
1580          case '6': case '7': case '8': case '9':
1581             groupId = keyCode-'0';
1582             group_select.select_grouped_units(groupId);
1583             break;
1584       }
1585    }
1586 
1587    //---- keys for toggling map mode ----//
1588 
1589    if( ISKEY(KEYEVENT_MAP_MODE_CYCLE) )
1590    {
1591       world.map_matrix->cycle_map_mode();
1592    }
1593 
1594    else if( ISKEY(KEYEVENT_MAP_MODE0) )
1595    {
1596       world.map_matrix->toggle_map_mode(0);
1597    }
1598 
1599    else if( ISKEY(KEYEVENT_MAP_MODE1) )
1600    {
1601       world.map_matrix->toggle_map_mode(1);
1602    }
1603 
1604    else if( ISKEY(KEYEVENT_MAP_MODE2) )
1605    {
1606       world.map_matrix->toggle_map_mode(2);
1607    }
1608 
1609    //--------- opaque report mode --------//
1610 
1611    else if( ISKEY(KEYEVENT_REPORT_OPAQUE_TOGGLE) )
1612    {
1613       config.opaque_report = !config.opaque_report;
1614 
1615       if( config.opaque_report )
1616          box.msg( _("Opaque report mode") );
1617       else
1618          box.msg( _("Transparent report mode") );
1619    }
1620 
1621    //------ clear news messages ------//
1622 
1623    else if( ISKEY(KEYEVENT_CLEAR_NEWS) )
1624    {
1625       news_array.clear_news_disp();
1626    }
1627 
1628       //------ open oldest open diplomatic message  ------//
1629 
1630    else if( ISKEY(KEYEVENT_OPEN_DIPLOMATIC_MSG) )
1631    {
1632       news_array.view_first_diplomatic();
1633    }
1634 
1635    //------ jump to a location with natural resource ---//
1636 
1637    else if( ISKEY(KEYEVENT_GOTO_RAW) )
1638    {
1639       site_array.go_to_a_raw_site();
1640    }
1641 
1642    //--------- bring up the option menu  ----------//
1643 
1644    else if( ISKEY(KEYEVENT_OPEN_OPTION_MENU) )
1645    {
1646       // ##### begin Gilbert 5/11 #######//
1647       // game.in_game_option_menu();
1648       option_menu.enter(!remote.is_enable());
1649       // ##### end Gilbert 5/11 #######//
1650    }
1651 
1652    //--------- forward/backward tutorial text block --------//
1653 
1654    else if( ISKEY(KEYEVENT_TUTOR_PREV) )
1655    {
1656       if( game.game_mode == GAME_TUTORIAL )
1657          tutor.prev_text_block();
1658    }
1659 
1660    else if( ISKEY(KEYEVENT_TUTOR_NEXT) )
1661    {
1662       if( game.game_mode == GAME_TUTORIAL )
1663          tutor.next_text_block();
1664    }
1665 
1666    //---- keys for saving and loading game -----//
1667 
1668    else if( ISKEY(KEYEVENT_SAVE_GAME) )
1669    {
1670       save_game();
1671    }
1672 
1673    else if( ISKEY(KEYEVENT_LOAD_GAME) )
1674    {
1675       load_game();
1676    }
1677 
1678    //---- key for quick locate -----//
1679 
1680    else if( ISKEY(KEYEVENT_GOTO_KING) )
1681    {
1682       locate_king_general(RANK_KING);
1683    }
1684 
1685    else if( ISKEY(KEYEVENT_GOTO_GENERAL) )
1686    {
1687       locate_king_general(RANK_GENERAL);
1688    }
1689 
1690    else if( ISKEY(KEYEVENT_GOTO_SPY) )
1691    {
1692       locate_spy();
1693    }
1694 
1695    else if( ISKEY(KEYEVENT_GOTO_SHIP) )
1696    {
1697       locate_ship();
1698    }
1699 
1700    else if( ISKEY(KEYEVENT_GOTO_CAMP) )
1701    {
1702       locate_camp();
1703    }
1704 }
1705 //--------- End of function Sys::detect_letter_key ---------//
1706 
1707 
1708 //-------- Begin of function Sys::detect_function_key --------//
1709 //
detect_function_key(unsigned scanCode,unsigned skeyState)1710 void Sys::detect_function_key(unsigned scanCode, unsigned skeyState)
1711 {
1712    int keyCode;
1713 
1714    if( (keyCode = mouse.is_key(scanCode, skeyState, (unsigned short) 0, K_UNIQUE_KEY)) )
1715    {
1716       switch(keyCode)
1717       {
1718       case KEY_ESC:
1719          set_view_mode(MODE_NORMAL);
1720          break;
1721 
1722       case KEY_F1:
1723          if( view_mode==MODE_NATION )
1724             set_view_mode(MODE_NORMAL);
1725          else
1726             set_view_mode(MODE_NATION);
1727          break;
1728       case KEY_F2:
1729          if( view_mode==MODE_TOWN )
1730             set_view_mode(MODE_NORMAL);
1731          else
1732             set_view_mode(MODE_TOWN);
1733          break;
1734       case KEY_F3:
1735          if( view_mode==MODE_ECONOMY )
1736             set_view_mode(MODE_NORMAL);
1737          else
1738             set_view_mode(MODE_ECONOMY);
1739          break;
1740       case KEY_F4:
1741          if( view_mode==MODE_TRADE )
1742             set_view_mode(MODE_NORMAL);
1743          else
1744             set_view_mode(MODE_TRADE);
1745          break;
1746       case KEY_F5:
1747          if( view_mode==MODE_MILITARY )
1748             set_view_mode(MODE_NORMAL);
1749          else
1750             set_view_mode(MODE_MILITARY);
1751          break;
1752       case KEY_F6:
1753          if( view_mode==MODE_TECH )
1754             set_view_mode(MODE_NORMAL);
1755          else
1756             set_view_mode(MODE_TECH);
1757          break;
1758       case KEY_F7:
1759          if( view_mode==MODE_SPY )
1760             set_view_mode(MODE_NORMAL);
1761          else
1762             set_view_mode(MODE_SPY);
1763          break;
1764       case KEY_F8:
1765          if( view_mode==MODE_RANK )
1766             set_view_mode(MODE_NORMAL);
1767          else
1768             set_view_mode(MODE_RANK);
1769          break;
1770       case KEY_F9:
1771          if( view_mode==MODE_NEWS_LOG )
1772             set_view_mode(MODE_NORMAL);
1773          else
1774             set_view_mode(MODE_NEWS_LOG);
1775          break;
1776 
1777       case KEY_F10:
1778          // ##### begin Gilbert 5/11 ######//
1779          //game.in_game_menu();
1780          in_game_menu.enter(!remote.is_enable());
1781          // ##### end Gilbert 5/11 ######//
1782          break;
1783 
1784       case KEY_F11:
1785          capture_screen();
1786          break;
1787       }
1788    }
1789 }
1790 //--------- End of function Sys::detect_function_key ---------//
1791 
1792 
1793 //-------- Begin of function Sys::detect_cheat_key --------//
1794 //
detect_cheat_key(unsigned scanCode,unsigned skeyState)1795 void Sys::detect_cheat_key(unsigned scanCode, unsigned skeyState)
1796 {
1797    if( remote.is_enable() )      // no cheat keys in multiplayer games
1798       return;
1799 
1800    int keyCode = mouse.is_key( scanCode, skeyState, (unsigned short) 0, K_CHAR_KEY );
1801 
1802    if( !keyCode )    // since all keys concern are printable
1803       return;
1804 
1805    keyCode = misc.lower(keyCode);
1806 
1807    switch( keyCode )
1808    {
1809       //-------- cheat keys ---------//
1810 
1811       case 'c':      // add cash
1812          if( nation_array.player_recno )
1813             (~nation_array)->add_cheat((float)1000);
1814          break;
1815 
1816       case '\\':     // add food
1817          if( nation_array.player_recno )
1818             (~nation_array)->add_food((float)1000);
1819          break;
1820 
1821       case 'n':
1822          tech_res.inc_all_tech_level(nation_array.player_recno);
1823          god_res.enable_know_all(nation_array.player_recno);
1824          box.msg( _("Your technology has advanced.\nYou can now invoke all Greater Beings.") );
1825          break;
1826 
1827       case '/':
1828          world.unveil(0, 0, MAX_WORLD_X_LOC-1, MAX_WORLD_Y_LOC-1);
1829          world.visit(0, 0, MAX_WORLD_X_LOC-1, MAX_WORLD_Y_LOC-1, 0, 0);
1830          break;
1831 
1832       case ';':   // increase town population
1833          if( town_array.selected_recno )
1834          {
1835             Town* townPtr = town_array[town_array.selected_recno];
1836             #ifdef DEBUG2
1837                for(int di=0; di<MAX_RACE; di++)
1838                {
1839                   if(townPtr->race_pop_array[di])
1840                   {
1841                      townPtr->init_pop(di+1, 10, 100);
1842                      break;
1843                   }
1844                }
1845             #else
1846                townPtr->init_pop( random_race(), 10, 100 );
1847             #endif
1848             townPtr->auto_set_layout();
1849          }
1850          break;
1851 
1852       case 'u':
1853          config.king_undie_flag = !config.king_undie_flag;
1854 
1855          if( config.king_undie_flag )
1856             box.msg( _("Your king is now immortal.") );
1857          else
1858             box.msg( _("King immortal mode is now disabled.") );
1859          break;
1860 
1861       case '=':
1862          if( firm_array.selected_recno )
1863          {
1864             Firm* firmPtr = firm_array[firm_array.selected_recno];
1865 
1866             if( firmPtr->firm_id == FIRM_BASE )
1867             {
1868                ((FirmBase*)firmPtr)->pray_points = (float) MAX_PRAY_POINTS;
1869                info.disp();
1870             }
1871          }
1872          break;
1873 
1874       case '-':      // finish building a firm instantly or increase the hit points of a firm to its MAX
1875          if( firm_array.selected_recno )
1876          {
1877             Firm* firmPtr = firm_array[firm_array.selected_recno];
1878             firmPtr->hit_points = firmPtr->max_hit_points;
1879          }
1880          break;
1881 
1882       case 'z':      // toggle fast_build
1883          config.fast_build = !config.fast_build;
1884 
1885          if( !config.fast_build )
1886             box.msg( _("Fast build is now disabled.") );
1887          else
1888             box.msg( _("Fast build is now enabled.") );
1889          break;
1890 
1891       //----- increase the combat level -------//
1892 
1893       case '[':
1894          if( unit_array.selected_recno )
1895          {
1896             Unit* unitPtr = unit_array[unit_array.selected_recno];
1897 
1898             unitPtr->set_combat_level( MIN(100, unitPtr->skill.combat_level+20) );
1899          }
1900          break;
1901 
1902       //----- increase the skill level of the unit -------//
1903 
1904       case ']':
1905          if( unit_array.selected_recno )
1906          {
1907             Unit* unitPtr = unit_array[unit_array.selected_recno];
1908 
1909             if( unitPtr->skill.skill_id )
1910                unitPtr->skill.skill_level = MIN(100, unitPtr->skill.skill_level+20);
1911          }
1912          break;
1913 
1914       //----- increase the spying skill -------//
1915 
1916       case '\'':
1917          if( unit_array.selected_recno )
1918          {
1919             Unit* unitPtr = unit_array[unit_array.selected_recno];
1920 
1921             if( unitPtr->spy_recno )
1922             {
1923                Spy* spyPtr = spy_array[unitPtr->spy_recno];
1924 
1925                spyPtr->spy_skill = MIN(100, spyPtr->spy_skill+20);
1926             }
1927          }
1928          break;
1929    }
1930 }
1931 //--------- End of function Sys::detect_cheat_key ---------//
1932 
1933 
1934 //-------- Begin of function Sys::detect_debug_cheat_key --------//
1935 //
detect_debug_cheat_key(unsigned scanCode,unsigned skeyState)1936 int Sys::detect_debug_cheat_key(unsigned scanCode, unsigned skeyState)
1937 {
1938    int keyProcessed = 0;
1939 
1940    if( remote.is_enable() )      // no cheat keys in multiplayer games
1941       return keyProcessed;
1942 
1943    int keyCode = mouse.is_key( scanCode, skeyState, (unsigned short) 0, K_IS_CTRL );
1944 
1945    if( !keyCode )    // since all keys concerned are printable
1946       return keyProcessed;
1947 
1948    switch( keyCode )
1949    {
1950 /*
1951       case 'j':      // allow all nations to have all god creatures
1952          for( i=1; i<=nation_array.size() ; i++ )
1953          {
1954             if( !nation_array.is_deleted(i) )
1955                god_res.enable_know_all(i);
1956          }
1957          box.msg( "Now knowledge of seat of power is available to all nations." );
1958          break;
1959 */
1960       case 'n':
1961          config.blacken_map = !config.blacken_map;
1962          config.fog_of_war  = config.blacken_map;
1963          ++keyProcessed;
1964          break;
1965 
1966       case 'f':      // set default report nation
1967          if( firm_array.selected_recno )
1968          {
1969             info.default_viewing_nation_recno = firm_array[firm_array.selected_recno]->nation_recno;
1970          }
1971          else if( town_array.selected_recno )
1972          {
1973             int nationRecno = town_array[town_array.selected_recno]->nation_recno;
1974 
1975             if( nationRecno )
1976                info.default_viewing_nation_recno = nationRecno;
1977          }
1978          else if( unit_array.selected_recno )
1979          {
1980             int nationRecno = unit_array[unit_array.selected_recno]->nation_recno;
1981 
1982             if( nationRecno )
1983                info.default_viewing_nation_recno = nationRecno;
1984          }
1985          ++keyProcessed;
1986          break;
1987 
1988       case 'a':
1989          if( view_mode==MODE_AI_ACTION )
1990             set_view_mode(MODE_NORMAL);
1991          else
1992             set_view_mode(MODE_AI_ACTION);
1993          ++keyProcessed;
1994          break;
1995 
1996       //-----------------------------------//
1997 /*
1998       case 't':   // next town layout
1999          if( town_array.selected_recno )
2000             town_array[town_array.selected_recno]->auto_set_layout();
2001          ++keyProcessed;
2002          break;
2003 */
2004       //-------------------------------//
2005 
2006       case 'i':
2007          config.disable_ai_flag = !config.disable_ai_flag;
2008 
2009          if( config.disable_ai_flag )
2010             box.msg( "AI is now disabled." );
2011          else
2012             box.msg( "AI is now enabled." );
2013          ++keyProcessed;
2014          break;
2015 
2016       case 'd':
2017          config.show_ai_info = !config.show_ai_info;
2018          info.disp();
2019 
2020          if( config.show_ai_info )
2021             box.msg( "Now AI info will be displayed." );
2022          else
2023             box.msg( "Now AI info will not be displayed." );
2024          ++keyProcessed;
2025          break;
2026 
2027       case '/':
2028          config.show_all_unit_icon = !config.show_all_unit_icon;
2029 
2030          if( config.show_all_unit_icon )
2031             box.msg( "Now all unit icons will be displayed." );
2032          else
2033             box.msg( "Now all unit icons will not be displayed." );
2034          ++keyProcessed;
2035          break;
2036 
2037 #ifdef DEBUG
2038       case '~':
2039          sys.testing_session = !sys.testing_session;
2040 
2041          if( sys.testing_session )
2042             box.msg( "sys.testing_session is now 1." );
2043          else
2044             box.msg( "sys.testing_session is now 0." );
2045          ++keyProcessed;
2046          break;
2047 
2048       case '\\':
2049          if(debug2_enable_flag)
2050             debug2_enable_flag = 0;
2051          else
2052             debug2_enable_flag = 1;
2053          ++keyProcessed;
2054          break;
2055 
2056 /*    //-*********** syn game test ***********-//
2057       case '\'':
2058          //if(debug2_enable_flag && debug_sim_game_type)
2059          //save_game_array[0]->load_game("syn.sav");
2060          game_file.load_game("syn.sav");
2061          sp_load_seed_file();
2062          debug_seed_status_flag = DEBUG_SYN_AUTO_LOAD;
2063          ++keyProcessed;
2064          break;
2065 
2066       case '[':
2067          if(misc.is_file_exist("SYN.SYS"))
2068          {
2069             debug_seed_status_flag = DEBUG_SYN_AUTO_SAVE;
2070             sp_seed_pos_reset();
2071             sp_record_match_seed();
2072             sp_create_seed_file("nseed.rs");
2073 
2074             game_file.save_game("syn.sav");
2075          }
2076          ++keyProcessed;
2077          break;
2078 
2079       case ']':
2080          if(debug_seed_status_flag==NO_DEBUG_SYN)
2081          {
2082             if(misc.is_file_exist("SYN.SYS"))
2083             {
2084                debug_seed_status_flag = DEBUG_SYN_LOAD_AND_COMPARE_ONCE;
2085                game_file.load_game("syn.sav");
2086                sp_load_seed_file();
2087             }
2088             else
2089                debug_seed_status_flag = NO_DEBUG_SYN;
2090          }
2091          ++keyProcessed;
2092          break;
2093 */       //-*********** syn game test ***********-//
2094 #endif
2095    }
2096 
2097    return keyProcessed;
2098 }
2099 //--------- End of function Sys::detect_debug_cheat_key ---------//
2100 
2101 
2102 //-------- Start of function detect_scenario_cheat_key -------------//
2103 
detect_scenario_cheat_key(unsigned scanCode,unsigned skeyState)2104 static int detect_scenario_cheat_key(unsigned scanCode, unsigned skeyState)
2105 {
2106    if( remote.is_enable() )      // no cheat keys in multiplayer games
2107       return 0;
2108 
2109    int keyCode = mouse.is_key(scanCode, skeyState, (unsigned short) 0, K_IS_CTRL);
2110 
2111    if( !keyCode )
2112       return 0;
2113 
2114    //------------------------------------------//
2115 
2116    int keyProcessed = 0;
2117 
2118    Firm *firmPtr;
2119    Unit *unitPtr;
2120    Town *townPtr;
2121    Nation *nationPtr;
2122    Site *sitePtr;
2123    Spy *spyPtr;
2124    Location *locPtr;
2125    int i, j, curXLoc, curYLoc;
2126 
2127    switch(keyCode)
2128    {
2129       case 'p': //-------- get scroll of power for the race of selected unit --------//
2130          if(unit_array.selected_recno)
2131          {
2132             unitPtr = unit_array[unit_array.selected_recno];
2133             if(unitPtr->nation_recno==nation_array.player_recno && unitPtr->race_id)
2134             {
2135                god_res[unitPtr->race_id]->enable_know(unitPtr->nation_recno);
2136                //box.msg( "Get Scroll of Power of selected unit for his race" );
2137                keyProcessed++;
2138             }
2139          }
2140          keyProcessed++;
2141          break;
2142 
2143       case 't': //-------- get all technology except scrolls of power -------//
2144          tech_res.inc_all_tech_level(nation_array.player_recno);
2145          //box.msg( "Your technology has advanced." );
2146          keyProcessed++;
2147          break;
2148 
2149       case 'g': //------- get galleon and cannon technologies -------//
2150          err_when(tech_res[4]->unit_id != UNIT_CANNON);
2151          err_when(tech_res[7]->unit_id != UNIT_GALLEON);
2152 
2153          i = tech_res[4]->get_nation_tech_level(nation_array.player_recno);
2154          if(i < tech_res[4]->max_tech_level)
2155             tech_res[4]->set_nation_tech_level(nation_array.player_recno, i+1);
2156 
2157          i = tech_res[7]->get_nation_tech_level(nation_array.player_recno);
2158          if(i < tech_res[7]->max_tech_level)
2159             tech_res[7]->set_nation_tech_level(nation_array.player_recno, i+1);
2160 
2161          //box.msg( "Get technologies of Galleon and Cannon." );
2162          keyProcessed++;
2163          break;
2164 
2165       case 'q': //-- decrease population of a selected race in a selected village by 10 --//
2166          if(town_array.selected_recno)
2167          {
2168             townPtr = town_array[town_array.selected_recno];
2169             if(townPtr->nation_recno == nation_array.player_recno)
2170             {
2171                i = townPtr->get_selected_race();
2172                if(i && townPtr->race_pop_array[i-1])
2173                {
2174                   for(j=10; j>0 && !town_array.is_deleted(townPtr->town_recno); --j)
2175                      townPtr->kill_town_people(i);
2176 
2177                   //box.msg( "Population decrease by 10." );
2178                   keyProcessed++;
2179                }
2180                townPtr->auto_set_layout();
2181             }
2182          }
2183          keyProcessed++;
2184          break;
2185 
2186       case 'w': //-- increase population of a selected race in a selected village by 10 --//
2187          if(town_array.selected_recno)
2188          {
2189             townPtr = town_array[town_array.selected_recno];
2190             if(townPtr->nation_recno == nation_array.player_recno)
2191             {
2192                i = townPtr->get_selected_race();
2193                if(i && townPtr->race_pop_array[i-1])
2194                {
2195                   townPtr->init_pop(i, 10, 100);
2196                   //box.msg( "Population increase by 10." );
2197                   keyProcessed++;
2198                }
2199                townPtr->auto_set_layout();
2200             }
2201          }
2202          keyProcessed++;
2203          break;
2204 
2205       case 'e': //-------- decrease the reputation by 10 -----------//
2206          nationPtr = nation_array[nation_array.player_recno];
2207          nationPtr->reputation -= 10;
2208          if(nationPtr->reputation < -100)
2209             nationPtr->reputation = (float) -100;
2210 
2211          //box.msg( "Reputation decrease by 10." );
2212          keyProcessed++;
2213          break;
2214 
2215       case 'r': //-------- increase the reputation by 10 -----------//
2216          nationPtr = nation_array[nation_array.player_recno];
2217          nationPtr->reputation += 10;
2218          if(nationPtr->reputation > 100)
2219             nationPtr->reputation = (float) 100;
2220 
2221          //box.msg( "Reputation increase by 10." );
2222          keyProcessed++;
2223          break;
2224 
2225       case 'j': //--------- damage a building by 20 pt -----------//
2226          if(firm_array.selected_recno)
2227          {
2228             firmPtr = firm_array[firm_array.selected_recno];
2229             if(firmPtr->nation_recno==nation_array.player_recno)
2230             {
2231                firmPtr->hit_points -= 20;
2232                if(firmPtr->hit_points < 1)
2233                   firmPtr->hit_points = (float) 1;
2234                //box.msg( "damage firm by 20 points." );
2235                keyProcessed++;
2236             }
2237          }
2238          keyProcessed++;
2239          break;
2240 
2241       case 'k': //--------- repair a building by 20 pt -----------//
2242          if(firm_array.selected_recno)
2243          {
2244             firmPtr = firm_array[firm_array.selected_recno];
2245             if(firmPtr->nation_recno==nation_array.player_recno)
2246             {
2247                firmPtr->hit_points += 20;
2248                if(firmPtr->hit_points > firmPtr->max_hit_points)
2249                   firmPtr->hit_points = firmPtr->max_hit_points;
2250                //box.msg( "Repair firm by 20 points." );
2251                keyProcessed++;
2252             }
2253          }
2254          keyProcessed++;
2255          break;
2256 
2257       case 'x': //------ decrease cash by 1000 --------//
2258          nationPtr = nation_array[nation_array.player_recno];
2259          nationPtr->cash -= 1000;
2260          if(nationPtr->cash < 0)
2261             nationPtr->cash = (float) 0;
2262          //box.msg( "Decrease cash by 1000." );
2263          keyProcessed++;
2264          break;
2265 
2266       case 'c': //------ decrease food by 1000 --------//
2267          nationPtr = nation_array[nation_array.player_recno];
2268          nationPtr->food -= 1000;
2269          if(nationPtr->food < 0)
2270             nationPtr->food = (float) 0;
2271          //box.msg( "Decrease food by 1000." );
2272          keyProcessed++;
2273          break;
2274 
2275       case 'm': //----- add natural resource to cursor pos / remove existing resource ------//
2276          if(get_mouse_loc_in_zoom_map(curXLoc, curYLoc))
2277          {
2278             locPtr = world.get_loc(curXLoc, curYLoc);
2279             if(locPtr->has_site()) // remove site
2280             {
2281                i = locPtr->site_recno();
2282                sitePtr = site_array[i];
2283                if(!sitePtr->has_mine)
2284                {
2285                   site_array.del_site(i);
2286                   //box.msg( "Site deleted." );
2287                }
2288             }
2289             else if(locPtr->can_build_site(1) && !locPtr->is_power_off()) // add site
2290             {
2291                i = MAX_RAW_RESERVE_QTY * (50 + misc.random(50)) / 100;
2292                site_array.add_site(curXLoc, curYLoc, SITE_RAW, misc.random(MAX_RAW)+1, i);
2293                //box.msg( "Site added." );
2294             }
2295          }
2296          keyProcessed++;
2297          break;
2298 
2299       case 'b': //------------ add reserve of natural resource by 100 ----------//
2300          if(get_mouse_loc_in_zoom_map(curXLoc, curYLoc))
2301          {
2302             locPtr = world.get_loc(curXLoc, curYLoc);
2303             if(locPtr->has_site())
2304             {
2305                i = locPtr->site_recno();
2306                sitePtr = site_array[i];
2307                if(!sitePtr->has_mine)
2308                {
2309                   sitePtr->reserve_qty += 100;
2310                   //box.msg( "increase reserve by 100." );
2311                   info.disp();
2312                }
2313             }
2314          }
2315          keyProcessed++;
2316          break;
2317 
2318       case 'v': //------------ reduce reserve of natural resource by 100 ----------//
2319          if(get_mouse_loc_in_zoom_map(curXLoc, curYLoc))
2320          {
2321             locPtr = world.get_loc(curXLoc, curYLoc);
2322             if(locPtr->has_site())
2323             {
2324                i = locPtr->site_recno();
2325                sitePtr = site_array[i];
2326                if(!sitePtr->has_mine && sitePtr->reserve_qty>100)
2327                {
2328                   sitePtr->reserve_qty -= 100;
2329                   //box.msg( "reduce reserve by 100." );
2330                   info.disp();
2331                }
2332             }
2333          }
2334          keyProcessed++;
2335          break;
2336 
2337       case 'h': //-------- hide map except for areas around your village, people --------//
2338          if( config.explore_whole_map )      // no action if the setting of the map is explored
2339          {
2340             keyProcessed++;
2341             break;
2342          }
2343 
2344          vga_back.bar(MAP_X1, MAP_Y1, MAP_X2, MAP_Y2, UNEXPLORED_COLOR);
2345          for(j=0; j<MAX_WORLD_Y_LOC; ++j)
2346          {
2347             locPtr = world.get_loc(0, j);
2348             for(i=0; i<MAX_WORLD_X_LOC; ++i, locPtr++)
2349                locPtr->explored_off();
2350          }
2351 
2352          for(i=town_array.size(); i>0; --i)
2353          {
2354             if(town_array.is_deleted(i))
2355                continue;
2356 
2357             townPtr = town_array[i];
2358             if(townPtr->nation_recno == nation_array.player_recno)
2359                world.unveil(townPtr->loc_x1, townPtr->loc_y1, townPtr->loc_x2, townPtr->loc_y2);
2360          }
2361 
2362          for(i=firm_array.size(); i>0; --i)
2363          {
2364             if(firm_array.is_deleted(i))
2365                continue;
2366 
2367             firmPtr = firm_array[i];
2368             if(firmPtr->nation_recno == nation_array.player_recno)
2369                world.unveil(firmPtr->loc_x1, firmPtr->loc_y1, firmPtr->loc_x2, firmPtr->loc_y2);
2370          }
2371 
2372          for(i=unit_array.size(); i>0; --i)
2373          {
2374             if(unit_array.is_deleted(i))
2375                continue;
2376 
2377             unitPtr = unit_array[i];
2378             if(unitPtr->nation_recno == nation_array.player_recno)
2379                world.unveil(unitPtr->next_x_loc(), unitPtr->next_y_loc(), unitPtr->next_x_loc(), unitPtr->next_y_loc());
2380          }
2381 
2382          for(i=spy_array.size(); i>0; --i)
2383          {
2384             if(spy_array.is_deleted(i))
2385                continue;
2386 
2387             spyPtr = spy_array[i];
2388             if(spyPtr->true_nation_recno!=nation_array.player_recno)
2389                continue;
2390 
2391             if(spyPtr->spy_place == SPY_FIRM)
2392             {
2393                if(!firm_array.is_deleted(spyPtr->spy_place_para))
2394                {
2395                   firmPtr = firm_array[spyPtr->spy_place_para];
2396                   world.unveil(firmPtr->loc_x1, firmPtr->loc_y1, firmPtr->loc_x2, firmPtr->loc_y2);
2397                }
2398             }
2399             else if(spyPtr->spy_place == SPY_TOWN)
2400             {
2401                if(!town_array.is_deleted(spyPtr->spy_place_para))
2402                {
2403                   townPtr = town_array[spyPtr->spy_place_para];
2404                   world.unveil(townPtr->loc_x1, townPtr->loc_y1, townPtr->loc_x2, townPtr->loc_y2);
2405                }
2406             }
2407          }
2408 
2409          for(i=2; i<=nation_array.size(); ++i) // assume player_nation_recno = 1
2410          {
2411             if( nation_array.is_deleted(i) )
2412                continue;
2413 
2414             (~nation_array)->init_relation(i);
2415             nation_array[i]->init_relation(1);
2416          }
2417 
2418          keyProcessed++;
2419          break;
2420 
2421       case 'z': //------------ put the selected unit to the cursor position ------------//
2422          if(unit_array.selected_recno)
2423          {
2424             unitPtr = unit_array[unit_array.selected_recno];
2425             if(get_mouse_loc_in_zoom_map(curXLoc, curYLoc))
2426             {
2427                if(unitPtr->mobile_type!=UNIT_LAND)
2428                {
2429                   curXLoc = (curXLoc/2) * 2;
2430                   curYLoc = (curYLoc/2) * 2;
2431                }
2432 
2433                locPtr = world.get_loc(curXLoc, curYLoc);
2434                if(locPtr->can_move(unitPtr->mobile_type))
2435                {
2436                   world.set_unit_recno(unitPtr->next_x_loc(), unitPtr->next_y_loc(), unitPtr->mobile_type, 0);
2437                   unitPtr->stop2();
2438                   unitPtr->next_x = curXLoc << ZOOM_X_SHIFT_COUNT;
2439                   unitPtr->next_y = curYLoc << ZOOM_Y_SHIFT_COUNT;
2440                   unitPtr->cur_x = unitPtr->go_x = unitPtr->next_x;
2441                   unitPtr->cur_y = unitPtr->go_y = unitPtr->next_y;
2442                   unitPtr->move_to_x_loc = curXLoc;
2443                   unitPtr->move_to_y_loc = curYLoc;
2444                   world.set_unit_recno(curXLoc, curYLoc, unitPtr->mobile_type, unitPtr->sprite_recno);
2445                   //box.msg( "move unit." );
2446                }
2447             }
2448          }
2449          keyProcessed++;
2450          break;
2451 
2452       case 's': //------------ force current-default-view nation (CTRL+F) to surrender to player ------------//
2453          if( info.default_viewing_nation_recno &&
2454              nation_array.player_recno &&
2455              nation_array[info.default_viewing_nation_recno]->is_ai() )
2456          {
2457             nation_array[info.default_viewing_nation_recno]->surrender(nation_array.player_recno);
2458          }
2459          keyProcessed++;
2460          break;
2461 
2462       case 'y': //-- cause independent/rebel town to found a new nation --//
2463           if( town_array.selected_recno )
2464           {
2465               townPtr = town_array[town_array.selected_recno];
2466               if( townPtr->nation_recno == 0 && nation_array.nation_count < MAX_NATION )
2467               {
2468                   townPtr->form_new_nation();
2469               }
2470           }
2471           keyProcessed++;
2472           break;
2473    }
2474 
2475    return keyProcessed;
2476 }
2477 //--------- End of function detect_scenario_cheat_key ---------------//
2478 
2479 
2480 //-------- Begin of function Sys::detect_set_speed --------//
2481 //
detect_set_speed(unsigned scanCode,unsigned skeyState)2482 int Sys::detect_set_speed(unsigned scanCode, unsigned skeyState)
2483 {
2484    int keyCode = mouse.is_key( scanCode, skeyState, (unsigned short) 0, K_CHAR_KEY );
2485 
2486    if( !keyCode )    // since all keys concerned are printable
2487       return 0;
2488 
2489    //------- determine the speed to set of the key pressed -------//
2490 
2491    if( keyCode >= '1' && keyCode <= '8' )
2492    {
2493       set_speed( (keyCode-'0') * 3 );
2494       user_pause_flag = 0;
2495       return 1;
2496    }
2497 
2498    else if( keyCode == '9' )
2499    {
2500       set_speed( 99 ); // highest possible speed
2501       user_pause_flag = 0;
2502       return 1;
2503    }
2504 
2505    else if( keyCode == ' ' || keyCode == '0' )
2506    {
2507       // toggle pausing
2508       set_speed( 0 );
2509       user_pause_flag = !config.frame_speed;
2510       return 1;
2511    }
2512 
2513    return 0;
2514 }
2515 //--------- End of function Sys::detect_set_speed ---------//
2516 
2517 
2518 //--------- Begin of function Sys::detect_key_str --------//
2519 //
2520 // Detect for continous input of a string from the keyboard
2521 //
2522 // <int>   keyStrId = the id. of the key string
2523 //                    each id has its individual key_str_pos
2524 // <char*> keyStr   = the string to detect
2525 //
2526 // return : <int> 1 - complete string detected
2527 //                0 - not detected
2528 //
detect_key_str(int keyStrId,const KeyEventType * keyStr)2529 int Sys::detect_key_str(int keyStrId, const KeyEventType* keyStr)
2530 {
2531    err_when( keyStrId < 0 || keyStrId >= MAX_KEY_STR );
2532 
2533    //const KeyEventType *keyStr = cheat_str;
2534 
2535    if( ISKEY(keyStr[key_str_pos[keyStrId]]) )
2536       key_str_pos[keyStrId]++;
2537    else
2538       key_str_pos[keyStrId]=0;    // when one key unmatched, reset the counter
2539 
2540    if( keyStr[key_str_pos[keyStrId]] == KEYEVENT_MAX )
2541    {
2542       key_str_pos[keyStrId]=0;    // the full string has been entered successfully without any mistakes
2543       return 1;
2544    }
2545 
2546    return 0;
2547 }
2548 //----------- End of function Sys::detect_key_str --------//
2549 
2550 
2551 //-------- Begin of function Sys::set_speed --------//
2552 //
2553 // Set the speed if frameSpeed is greater than zero, and
2554 // toggle the last speed if it is zero.
2555 //
set_speed(int frameSpeed,int remoteCall)2556 void Sys::set_speed(int frameSpeed, int remoteCall)
2557 {
2558    short requested_speed;
2559 
2560    if( frameSpeed > 0 )
2561    {
2562       // set the game speed
2563       requested_speed = frameSpeed;
2564       last_frame_speed = 0;
2565    }
2566    else
2567    {
2568       // toggle last game speed
2569       requested_speed = last_frame_speed;
2570       last_frame_speed = config.frame_speed;
2571    }
2572 
2573    //--------- if multiplayer, update remote players setting -------//
2574 
2575    if( remote.is_enable() && !remoteCall )
2576    {
2577       RemoteMsg *remoteMsg = remote.new_msg(MSG_SET_SPEED, sizeof(short));
2578 
2579       *((short*)remoteMsg->data_buf) = requested_speed;
2580 
2581       remote.send_free_msg( remoteMsg );     // send out the message and free it after finishing sending
2582    }
2583 
2584    //---------- set the speed now ----------//
2585 
2586    if( config.frame_speed==0 )
2587    {
2588       // if it's currently frozen, set last_frame_time to avoid incorrect timeout
2589       last_frame_time = misc.get_time();
2590    }
2591 
2592    config.frame_speed = requested_speed;
2593 }
2594 //--------- End of function Sys::set_speed ---------//
2595 
2596 
2597 //-------- Begin of function Sys::capture_screen --------//
2598 //
capture_screen()2599 void Sys::capture_screen()
2600 {
2601    FilePath full_path(dir_config);
2602    const char filename_template[] = "7KXX.BMP";
2603 
2604    full_path += filename_template; // template for screenshot filename
2605    if( full_path.error_flag )
2606       return;
2607 
2608    char *filename = (char*)full_path+strlen(dir_config);
2609    String str("7K");
2610 
2611    int i;
2612    for( i=0 ; i<=99 ; i++ )
2613    {
2614       str  = "7K";
2615 
2616       if( i<10 )
2617          str += "0";
2618 
2619       str += i;
2620       str += ".BMP";
2621 
2622       memcpy(filename, str, strlen(filename_template));
2623 
2624       if( !misc.is_file_exist(full_path) )
2625          break;
2626    }
2627 
2628    if( i>99 )        // all file names from DWORLD00 to DWORLD99 have been occupied
2629       return;
2630 
2631    if( sys.debug_session )    // in debug session, the buffer is not locked, we need to lock it for capturing the screen
2632    {
2633       vga_true_front.lock_buf();
2634       vga_true_front.write_bmp_file(full_path);
2635       vga_true_front.unlock_buf();
2636    }
2637    else
2638    {
2639       vga_front.write_bmp_file(full_path);
2640    }
2641 
2642    //------ display msg --------//
2643 
2644    String str2;
2645    const char *file_name = str;
2646 
2647    snprintf( str2, MAX_STR_LEN+1, _("The current screen has been written to file %s."), file_name );
2648 
2649    box.msg( str2 );
2650 }
2651 //--------- End of function Sys::capture_screen ---------//
2652 
2653 
2654 //-------- Begin of function Sys::load_game --------//
2655 //
load_game()2656 void Sys::load_game()
2657 {
2658    //--- load game not enabled in multiplayer game ---//
2659 
2660    if( remote.is_enable() )
2661       return;
2662 
2663    signal_exit_flag=2;     // for deinit functions to recognize that this is an end game deinitialization instead of a normal deinitialization
2664 
2665    int rc=0;
2666 
2667    save_game_array.init("*.SAV");                  // reload any save game file
2668    save_game_array.menu(-2);               // save screen area to back buffer
2669    switch( save_game_array.load_game() )
2670    {
2671       case 1:
2672          rc = 1;                 // fall through to case 0
2673 
2674       case 0:
2675          signal_exit_flag = 0;
2676          break;
2677 
2678 	  default:
2679 		 // case -1 and otherwise, set sys.signal_exit_flag to 1 to exit the game
2680 		 sys.signal_exit_flag = 1;
2681    }
2682 
2683    save_game_array.menu(-1);               // restore screen area from back buffer
2684 
2685    //-----------------------------------//
2686    if( rc == -1)
2687    {
2688       box.msg( _("Failed Loading Game") );
2689       return;
2690    }
2691 
2692    if( rc )    // if rc==0, leave signal_exit_flag 1, which the game will then quit
2693    {
2694       need_redraw_flag = 1;
2695       disp_frame();
2696       // #### begin Gilbert 22/10 ######//
2697       disp_view_mode();
2698       // #### end Gilbert 22/10 ######//
2699       box.msg( _("Game Loaded Successfully") );
2700       signal_exit_flag=0;
2701       user_pause_flag = !config.frame_speed;
2702       if( user_pause_flag )
2703          last_frame_speed = 9;
2704       else
2705          last_frame_speed = 0;
2706       info.disp();
2707    }
2708 }
2709 //--------- End of function Sys::load_game ---------//
2710 
2711 
2712 //-------- Begin of function Sys::save_game --------//
2713 //
save_game()2714 void Sys::save_game()
2715 {
2716    if( nation_array.player_recno==0 )     // cannot save game when the player's kingdom has been destroyed
2717       return;
2718 
2719    if( remote.is_enable() )
2720    {
2721       uint32_t *dwordPtr = (uint32_t *)remote.new_send_queue_msg( MSG_REQUEST_SAVE, sizeof(uint32_t) );
2722       *dwordPtr = remote.next_send_frame(nation_array.player_recno, sys.frame_count+remote.process_frame_delay)+2;
2723       return;
2724    }
2725 
2726    save_game_array.init("*.SAV");                  // reload any save game file
2727    save_game_array.menu(-2);               // save screen area to back buffer
2728 
2729    if( save_game_array.menu(1) == 1 )
2730    {
2731       box.msg( _("Game Saved Successfully") );
2732    }
2733 
2734    save_game_array.menu(-1);               // restore screen area from back buffer
2735 
2736 	// ##### patch begin Gilbert 16/3 #######//
2737 	info.disp();
2738 	// ##### patch end Gilbert 16/3 #######//
2739 }
2740 //-------- End of function Sys::save_game --------//
2741 
2742 
2743 // --------- begin of function Sys::mp_request_save ----------//
mp_request_save(uint32_t frame)2744 void Sys::mp_request_save(uint32_t frame)
2745 {
2746    if( !mp_save_flag )
2747    {
2748       mp_save_flag = 1;
2749       mp_save_frame = frame;
2750    }
2751 }
2752 // --------- end of function Sys::mp_request_save ----------//
2753 
2754 
2755 // --------- begin of function Sys::mp_clear_request_save ----------//
mp_clear_request_save()2756 void Sys::mp_clear_request_save()
2757 {
2758    mp_save_flag = 0;
2759    mp_save_frame = 0;
2760 }
2761 // --------- end of function Sys::mp_clear_request_save ----------//
2762 
2763 
2764 //-------- Begin of function Sys::chdir_to_game_dir ----------//
2765 //
2766 // Returns true if we have successfully switched to the game data dir.
2767 //
chdir_to_game_dir()2768 int Sys::chdir_to_game_dir()
2769 {
2770    const char *env_data_path;
2771    const char *test_file;
2772 
2773    // test current directory
2774    test_file = "IMAGE" PATH_DELIM "HALLFAME.ICN";
2775    if (misc.is_file_exist(test_file))
2776       return 1;
2777 
2778    // test environment variable SKDATA for the path
2779    env_data_path = getenv("SKDATA");
2780    if (env_data_path)
2781    {
2782       chdir(env_data_path);
2783       if (misc.is_file_exist(test_file))
2784          return 1;
2785    }
2786 
2787    /* on Mac OS X, test the bundle resources directory */
2788    std::string bundle_resources_path = get_bundle_resources_path();
2789    if (!bundle_resources_path.empty())
2790    {
2791       chdir(bundle_resources_path.c_str());
2792       if (misc.is_file_exist(test_file))
2793 	 return 1;
2794    }
2795 
2796    // test compile time path
2797 #ifdef PACKAGE_DATA_DIR
2798    chdir(PACKAGE_DATA_DIR);
2799    if (misc.is_file_exist(test_file))
2800       return 1;
2801 #endif
2802 
2803    show_error_dialog(_("Unable to locate the game resources"));
2804 
2805    return 0;
2806 }
2807 //----------- End of function Sys::chdir_to_game_dir ----------//
2808 
2809 
2810 //-------- Begin of function Sys::set_game_dir ----------//
2811 //
2812 // Set all game directories. Return true on success.
2813 //
2814 #define P PATH_DELIM
set_game_dir()2815 int Sys::set_game_dir()
2816 {
2817    if (!chdir_to_game_dir())
2818       return 0;
2819 
2820    set_one_dir( "HALLFAME.ICN"         , "IMAGE" P   , dir_image );
2821    set_one_dir( "SEAT" P "NORMAN.ICN"  , "ENCYC" P   , dir_encyc );
2822 //#ifdef AMPLUS
2823    set_one_dir( "SEAT" P "EGYPTIAN.ICN", "ENCYC2" P  , dir_encyc2 );
2824 //#endif
2825    set_one_dir( "INTRO.AVI"            , "MOVIE" P   , dir_movie );
2826 
2827 #ifdef DEMO
2828    set_one_dir( "DEMO.WAV"             , "MUSIC" P   , dir_music );
2829    set_one_dir( "STANDARD.TUT"         , "TUTORIAL" P, dir_tutorial );
2830    set_one_dir( "DEMO.SCN"             , "SCENARIO" P, dir_scenario );
2831 #else
2832    set_one_dir( "NORMAN.WAV"           , "MUSIC" P   , dir_music );
2833    set_one_dir( "1BAS_MIL.TUT"         , "TUTORIAL" P, dir_tutorial );
2834    set_one_dir( "7FOR7.SCN"            , "SCENARIO" P, dir_scenario );
2835 #endif
2836 
2837 #if(MAX_SCENARIO_PATH >= 2)
2838    set_one_dir( "SCN_01.SCN"           , "SCENARI2" P, dir_scenario_path[1] );
2839 #endif
2840 
2841    //-------- set game version ---------//
2842 
2843    game_version = VERSION_FULL;
2844 
2845    return 1;
2846 }
2847 //----------- End of function Sys::set_game_dir ----------//
2848 #undef P
2849 
2850 
2851 //-------- Begin of function Sys::set_one_dir ----------//
2852 //
set_one_dir(const char * checkFileName,const char * defaultDir,char * trueDir)2853 int Sys::set_one_dir(const char* checkFileName, const char* defaultDir, char* trueDir)
2854 {
2855    FilePath full_path(defaultDir);
2856    full_path += checkFileName;
2857 
2858    if( !full_path.error_flag && misc.is_file_exist(full_path) )
2859    {
2860       strcpy(trueDir, defaultDir);
2861    }
2862    else
2863    {
2864       trueDir[0] = 0;
2865       return 0;
2866    }
2867 
2868    return 1;
2869 }
2870 //----------- End of function Sys::set_one_dir ----------//
2871 
2872 
2873 //-------- Start of function locate_king_general -------------//
2874 //
locate_king_general(int rankId)2875 static void locate_king_general(int rankId)
2876 {
2877    if( !nation_array.player_recno )
2878       return;
2879 
2880    int unitRecno = 0;
2881    if(unit_array.selected_recno)
2882       unitRecno = unit_array.selected_recno;
2883    else if(rankId!=RANK_KING && firm_array.selected_recno)
2884    {
2885       Firm *firmPtr = firm_array[firm_array.selected_recno];
2886       if((firmPtr->firm_id==FIRM_CAMP || firmPtr->firm_id==FIRM_BASE) && firmPtr->overseer_recno)
2887          unitRecno = firmPtr->overseer_recno;
2888    }
2889 
2890    for( int i=unit_array.size() ; i>0 ; i-- )
2891    {
2892       if( ++unitRecno > unit_array.size() )
2893          unitRecno = 1;
2894 
2895       if( unit_array.is_deleted(unitRecno) )
2896          continue;
2897 
2898       Unit* unitPtr = unit_array[unitRecno];
2899 
2900       if( unitPtr->nation_recno == nation_array.player_recno &&
2901           unitPtr->rank_id == rankId )
2902       {
2903          short xLoc, yLoc;
2904 
2905          if( unitPtr->get_cur_loc(xLoc, yLoc) )
2906          {
2907             world.go_loc(xLoc, yLoc, 1);
2908             return;
2909          }
2910       }
2911    }
2912 }
2913 //--------- End of function locate_king_general ---------------//
2914 
2915 
2916 //-------- Start of function locate_spy -------------//
2917 //
locate_spy()2918 static void locate_spy()
2919 {
2920    if( !nation_array.player_recno )
2921       return;
2922 
2923    int unitRecno = unit_array.selected_recno;
2924 
2925    for( int i=unit_array.size() ; i>0 ; i-- )
2926    {
2927       if( ++unitRecno > unit_array.size() )
2928          unitRecno = 1;
2929 
2930       if( unit_array.is_deleted(unitRecno) )
2931          continue;
2932 
2933       Unit* unitPtr = unit_array[unitRecno];
2934 
2935       if( unitPtr->true_nation_recno() == nation_array.player_recno &&
2936           unitPtr->spy_recno && unitPtr->is_visible() )
2937       {
2938          short xLoc, yLoc;
2939 
2940          if( unitPtr->get_cur_loc(xLoc, yLoc) )
2941          {
2942             world.go_loc(xLoc, yLoc, 1);
2943             return;
2944          }
2945       }
2946    }
2947 }
2948 //--------- End of function locate_spy ---------------//
2949 
2950 
2951 //-------- Start of function locate_ship -------------//
2952 //
locate_ship()2953 static void locate_ship()
2954 {
2955    if( !nation_array.player_recno )
2956       return;
2957 
2958    //----------------------------------------------------------------------------//
2959    // The order is
2960    // 1) visible ships by their sprite_recno in ascending order
2961    // 2) FirmHarbors with ships (num of ships >= 1) by their firm_recno in ascending
2962    //    order
2963    //
2964    // Start the scaning in one of the following cases
2965    // 1) a unit is selected
2966    // 2) a firm is selected
2967    // 3) neither unit or firm is selected
2968    //----------------------------------------------------------------------------//
2969 
2970    if(firm_array.selected_recno)
2971    {
2972       if(!locate_ship_in_harbor()) // harbor first, then unit
2973          locate_visible_ship();
2974    }
2975    else // if(unit_array.selected_recno) or neither of them is selected
2976    {
2977       if(!locate_visible_ship()) // unit first, then harbor
2978          locate_ship_in_harbor();
2979    }
2980 }
2981 //--------- End of function locate_ship ---------------//
2982 
2983 
2984 //-------- Start of function locate_ship_in_harbor -------------//
2985 // return 1 if found
2986 // return 0 otherwise
2987 //
locate_ship_in_harbor()2988 static int locate_ship_in_harbor()
2989 {
2990    int firmRecno = firm_array.selected_recno;
2991    int checkSize = firm_array.size();
2992    if(firmRecno)
2993       checkSize--; // not include the selected firm
2994 
2995    for( int i=checkSize ; i>0 ; i-- )
2996    {
2997       if( ++firmRecno > firm_array.size() )
2998          firmRecno = 1;
2999 
3000       if( firm_array.is_deleted(firmRecno) )
3001          continue;
3002 
3003       Firm* firmPtr = firm_array[firmRecno];
3004       if(firmPtr->firm_id!=FIRM_HARBOR ||
3005          firmPtr->nation_recno != nation_array.player_recno)
3006          continue;
3007 
3008       if(((FirmHarbor*)firmPtr)->ship_count==0)
3009          continue; // not interested
3010 
3011       world.go_loc(firmPtr->center_x, firmPtr->center_y, 1);
3012       return 1;
3013    }
3014    return 0;
3015 }
3016 //--------- End of function locate_ship_in_harbor ---------------//
3017 
3018 
3019 //-------- Start of function locate_visible_ship -------------//
3020 // return 1 if found
3021 // return 0 otherwise
3022 //
locate_visible_ship()3023 static int locate_visible_ship()
3024 {
3025    int unitRecno = unit_array.selected_recno;
3026    int checkSize = unit_array.size();
3027    if(unitRecno)
3028       checkSize--; // not include the selected unit
3029 
3030    for( int i=checkSize ; i>0 ; i-- )
3031    {
3032       if( ++unitRecno > unit_array.size() )
3033          unitRecno = 1;
3034 
3035       if( unit_array.is_deleted(unitRecno) )
3036          continue;
3037 
3038       Unit* unitPtr = unit_array[unitRecno];
3039       if(!unitPtr->is_visible()) // skip the case unit_mode==UNIT_MODE_IN_HARBOR in calling Unit::get_cur_loc()
3040          continue;
3041 
3042       if( unitPtr->nation_recno == nation_array.player_recno &&
3043           unit_res[unitPtr->unit_id]->unit_class == UNIT_CLASS_SHIP )
3044       {
3045          short xLoc, yLoc;
3046 
3047          if( unitPtr->get_cur_loc(xLoc, yLoc) )
3048          {
3049             world.go_loc(xLoc, yLoc, 1);
3050             return 1;
3051          }
3052       }
3053    }
3054 
3055    return 0;
3056 }
3057 //--------- End of function locate_visible_ship ---------------//
3058 
3059 
3060 //-------- Start of function locate_camp -------------//
3061 //
locate_camp()3062 static void locate_camp()
3063 {
3064    if( !nation_array.player_recno )
3065       return;
3066 
3067    int firmRecno = firm_array.selected_recno;
3068 
3069    for( int i=firm_array.size() ; i>0 ; i-- )
3070    {
3071       if( ++firmRecno > firm_array.size() )
3072          firmRecno = 1;
3073 
3074       if( firm_array.is_deleted(firmRecno) )
3075          continue;
3076 
3077       Firm* firmPtr = firm_array[firmRecno];
3078 
3079       if( firmPtr->nation_recno == nation_array.player_recno &&
3080           firmPtr->firm_id == FIRM_CAMP )
3081       {
3082          world.go_loc(firmPtr->center_x, firmPtr->center_y, 1);
3083          return;
3084       }
3085    }
3086 }
3087 //--------- End of function locate_camp ---------------//
3088 
3089 
3090 //-------- Start of function get_mouse_loc_in_zoom_map -------------//
get_mouse_loc_in_zoom_map(int & x,int & y)3091 static int get_mouse_loc_in_zoom_map(int &x, int &y)
3092 {
3093    int mouseX = mouse.cur_x;
3094    int mouseY = mouse.cur_y;
3095    if(mouseX >= ZOOM_X1 && mouseX <= ZOOM_X2 && mouseY >= ZOOM_Y1 && mouseY <= ZOOM_Y2)
3096    {
3097       x = world.zoom_matrix->top_x_loc + (mouseX-ZOOM_X1)/ZOOM_LOC_WIDTH;
3098       y = world.zoom_matrix->top_y_loc + (mouseY-ZOOM_Y1)/ZOOM_LOC_HEIGHT;
3099       return 1;
3100    }
3101 
3102    return 0; // out of zoom map boundary
3103 }
3104 //--------- End of function get_mouse_loc_in_zoom_map ---------------//
3105 
3106 
3107 //-------- Begin of static function random_race --------//
3108 //
3109 // Uses misc.random() for random race
3110 //
random_race()3111 static char random_race()
3112 {
3113 	int num = misc.random(config_adv.race_random_list_max);
3114 	return config_adv.race_random_list[num];
3115 }
3116 //--------- End of static function random_race ---------//
3117