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