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 : OINFO.CPP
22 //Description : Info class
23
24 #include <time.h>
25 #include <OVGA.h>
26 #include <OIMGRES.h>
27 #include <OSPY.h>
28 #include <OSTR.h>
29 #include <OBUTTON.h>
30 #include <OMOUSE.h>
31 #include <OFIRM.h>
32 #include <OSTR.h>
33 #include <ONEWS.h>
34 #include <ONATION.h>
35 #include <OFONT.h>
36 #include <ODATE.h>
37 #include <OTOWN.h>
38 #include <ODATE.h>
39 #include <OGAME.h>
40 #include <OPOWER.h>
41 #include <OSITE.h>
42 #include <OWALLRES.h>
43 #include <OSYS.h>
44 #include <OUNIT.h>
45 #include <OCOLTBL.h>
46 #include <OINFO.h>
47 #include <OOPTMENU.h>
48 #include "gettext.h"
49
50 //------------ Define static vars ----------------//
51
52 static const char* skill_name_array[] =
53 {
54 "Combat Skill",
55 "Construction Skill",
56 "Production Skill"
57 };
58
59 //------ define static vars --------//
60
61 static char *save_buf_1 = NULL;
62 static char *save_buf_1b = NULL;
63 static char *save_buf_2 = NULL;
64 static char *save_buf_3 = NULL;
65 static char *save_buf_4 = NULL;
66
67
68 //-------- Begin of function Info::Info --------//
69 //
Info()70 Info::Info() : report_array(sizeof(short), 50),
71 report_array2(sizeof(short), 50),
72 talk_msg_disp_array(sizeof(TalkMsgDisp), 50)
73 {
74 info_background_bitmap = NULL;
75 }
76 //--------- End of function Info::Info ---------//
77
78
79 //-------- Begin of function Info::~Info --------//
80 //
~Info()81 Info::~Info()
82 {
83 deinit();
84
85 err_when( save_buf_1 );
86 }
87 //--------- End of function Info::~Info ---------//
88
89
90 //-------- Begin of function Info::init --------//
91 //
init()92 void Info::init()
93 {
94 deinit();
95
96 game_day = 1;
97 game_month = 6;
98 game_year = 1000;
99
100 game_start_date = date.julian( game_year, game_month, game_day );
101
102 game_date = game_start_date;
103
104 year_day = date.day_year( game_year, game_month, game_day );
105
106 year_passed= 0; // no. of years has been passed since the game begins
107
108 goal_deadline = date.julian( date.year(info.game_start_date)+config.goal_year_limit,
109 date.month(info.game_start_date),
110 date.day(info.game_start_date) );
111
112 goal_difficulty = 0;
113 goal_score_bonus = 0;
114
115 //------ reset report browsers recno -----//
116
117 browse_nation_recno = 0;
118 browse_race_recno = 0;
119 browse_firm_recno = 0;
120 browse_income_recno = 0;
121 browse_expense_recno = 0;
122 browse_troop_recno = 0;
123 browse_unit_recno = 0;
124 browse_tech_recno = 0;
125 browse_god_recno = 0;
126 browse_town_recno = 0;
127 browse_spy_recno = 0;
128 browse_caravan_recno = 0;
129 browse_ship_recno = 0;
130 browse_talk_msg_recno= 0;
131 browse_news_recno = 0;
132 browse_ai_action_recno = 0;
133 browse_ai_attack_recno = 0;
134
135 //------ vars of the nation report ------//
136
137 nation_report_mode = NATION_REPORT_INFO;
138 player_reply_mode = 0;
139 chat_receiver_type = CHAT_RECEIVER_CURRENT;
140
141 //----------------------------------//
142
143 start_play_time = misc.get_time(); // the time player start playing the game
144 total_play_time = 0; // total time the player has played in all saved games
145
146 // these will be updated during loading game and saving game
147
148 err_when( MAX_REMOTE_CHAT_STR < DISP_NEWS_COUNT ); // it must not fewer then the maximum number of news that can be displayed on the screen at a time.
149 }
150 //--------- End of function Info::init ---------//
151
152
153 //-------- Begin of function Info::deinit --------//
154 //
deinit()155 void Info::deinit()
156 {
157 if( info_background_bitmap )
158 {
159 mem_del( info_background_bitmap );
160 info_background_bitmap = NULL;
161 }
162 }
163 //--------- End of function Info::deinit ---------//
164
165
166 //------- Begin of function Info::init_random_seed ------//
167 //
168 // [unsigned] randomSeed - if given, it will be the random seed of the game.
169 // random seed. otherwise a random seed will be
170 // picked.
171 // (default:0)
172 //
init_random_seed(int randomSeed)173 void Info::init_random_seed(int randomSeed)
174 {
175 if( randomSeed )
176 random_seed = randomSeed;
177 else
178 {
179 randomSeed = time(NULL);
180 randomSeed = randomSeed << 4 | randomSeed >> (32-4); // _rotr( randomSeed, 4 )
181 if( randomSeed < 0 )
182 randomSeed = ~randomSeed;
183 if( randomSeed == 0 )
184 randomSeed = 1;
185 random_seed = randomSeed;
186 }
187
188 misc.set_random_seed(random_seed);
189
190 //------ write random seed --------//
191
192 if( sys.testing_session )
193 {
194 File fileMapSeed;
195
196 fileMapSeed.file_create( "MAP.RS" );
197
198 String str(misc.format(random_seed,1));
199
200 fileMapSeed.file_write(str, str.len());
201 }
202 }
203 //------- End of function Info::init_random_seed ------//
204
205
206 //-------- Begin of function Info::next_day ---------//
207 //
next_day()208 void Info::next_day()
209 {
210 if( ++game_day > 30 )
211 game_day = 30; // game_day is limited to 1-30 for
212 // calculation of e.g. revenue_30days()
213 game_date++;
214
215 week_day=game_date%7;
216
217 //-----------------------------------------//
218
219 if( date.month(game_date) != game_month )
220 {
221 game_day = 1;
222 game_month = date.month(game_date);
223
224 firm_array.next_month();
225 nation_array.next_month();
226 }
227
228 if( date.year(game_date) != game_year )
229 {
230 game_month = 1;
231 game_year = date.year(game_date);
232 year_passed++;
233
234 firm_array.next_year();
235 nation_array.next_year();
236 }
237
238 //-------- set year_day ----------//
239
240 year_day = date.day_year( game_year, game_month, game_day );
241
242 //--- if a spy is viewing secret reports of other nations ---//
243
244 if( viewing_spy_recno )
245 process_viewing_spy();
246
247 //-------- deadline approaching message -------//
248
249 if( !game.game_has_ended && config.goal_year_limit_flag )
250 {
251 int dayLeft = goal_deadline-game_date;
252 int yearLeft = dayLeft/365;
253
254 if( dayLeft%365==0 && yearLeft>=1 && yearLeft<=5 )
255 news_array.goal_deadline( yearLeft, 0 );
256
257 if( dayLeft==0 ) // deadline arrives, everybody loses the game
258 game.game_end(0, 0);
259 }
260 }
261 //---------- End of function Info::next_day --------//
262
263
264 //-------- Begin of function Info::disp_panel --------//
265 //
disp_panel()266 void Info::disp_panel()
267 {
268 image_interface.put_to_buf( &vga_back, "MAINSCR" );
269
270 //------ keep a copy of bitmap of the panel texture -----//
271
272 if( !info_background_bitmap )
273 info_background_bitmap = mem_add( 4 + (INFO_X2-INFO_X1+1)*(INFO_Y2-INFO_Y1+1) );
274
275 vga_back.read_bitmap( INFO_X1, INFO_Y1, INFO_X2, INFO_Y2, info_background_bitmap );
276 }
277 //--------- End of function Info::disp_panel ---------//
278
279
280 //-------- Begin of function Info::disp --------//
281 //
282 // Display the side info area.
283 //
disp()284 void Info::disp()
285 {
286 // mouse.handle_flicking = 0; // since it will be called by Firm detect functions directly which may have set mouse.handle_flicking to 1 first, so we need to cancel it here
287
288 if( !power.enable_flag )
289 return;
290
291 if( sys.signal_exit_flag )
292 return;
293
294 if( option_menu.is_active() )
295 return;
296
297 vga_back.put_bitmap( INFO_X1, INFO_Y1, info_background_bitmap );
298 vga_front.put_bitmap( INFO_X1, INFO_Y1, info_background_bitmap );
299
300 //------- use front buffer -------//
301
302 int saveUseBackBuf = vga.use_back_buf;
303
304 vga.use_front();
305
306 //------ if units/firm selected, display info --------//
307
308 if( firm_array.selected_recno )
309 {
310 firm_array[firm_array.selected_recno]->disp_info_both(INFO_REPAINT);
311 }
312 else if( town_array.selected_recno )
313 {
314 town_array[town_array.selected_recno]->disp_info(INFO_REPAINT);
315 }
316 else if( site_array.selected_recno )
317 {
318 site_array[site_array.selected_recno]->disp_info(INFO_REPAINT);
319 }
320 else if( unit_array.selected_recno )
321 {
322 unit_array[unit_array.selected_recno]->disp_info(INFO_REPAINT);
323 }
324 else if( wall_res.selected_x_loc >= 0 )
325 {
326 wall_res.disp_info(INFO_REPAINT);
327 }
328
329 //----- restore use back buffer if it was ----//
330
331 if( saveUseBackBuf )
332 vga.use_back();
333 }
334 //-------- End of function Info::disp --------//
335
336
337 //-------- Begin of function Info::update --------//
338
update()339 void Info::update()
340 {
341 if( !power.enable_flag )
342 return;
343
344 if( option_menu.is_active() )
345 return;
346
347 //-------------------------------------------//
348
349 disp_heading();
350
351 //------- use front buffer -------//
352
353 int saveUseBackBuf = vga.use_back_buf;
354
355 vga.use_front();
356
357 //-------------------------------------------//
358
359 if( firm_array.selected_recno )
360 {
361 firm_array[firm_array.selected_recno]->disp_info_both(INFO_UPDATE);
362 }
363 else if( town_array.selected_recno )
364 {
365 town_array[town_array.selected_recno]->disp_info(INFO_UPDATE);
366 }
367 else if( site_array.selected_recno )
368 {
369 site_array[site_array.selected_recno]->disp_info(INFO_UPDATE);
370 }
371 else if( unit_array.selected_recno )
372 {
373 unit_array[unit_array.selected_recno]->disp_info(INFO_UPDATE);
374 }
375 else if( wall_res.selected_x_loc >= 0 )
376 {
377 wall_res.disp_info(INFO_UPDATE);
378 }
379
380 //----- restore use back buffer if it was ----//
381
382 if( saveUseBackBuf )
383 vga.use_back();
384 }
385 //-------- End of function Info::update --------//
386
387
388 //-------- Begin of function Info::disp_heading --------//
389
disp_heading()390 void Info::disp_heading()
391 {
392 //---- display info on the top menu area ----//
393
394 int x=TOP_MENU_X2-250;
395
396 //---------- display date -----------//
397
398 font_mid.use_max_height();
399 font_mid.disp( 460, 10, date.date_str(game_date,1), 575);
400
401 if( !nation_array.player_recno ) // the player has lost the game
402 {
403 font_mid.disp( 307, 10, "", 445); // clear the display
404 font_mid.disp( 305, 30, "", 445);
405 // ##### begin Gilbert 4/11 #######//
406 image_icon.put_front(447,26, "REPU_DW" );
407 // ##### end Gilbert 4/11 #######//
408 font_mid.disp( 476, 30, "", 575);
409 font_mid.use_std_height();
410 return;
411 }
412
413 String str;
414 Nation* nationPtr = ~nation_array;
415
416 //------- display food and net food change --------//
417
418 err_when( vga.use_back_buf );
419
420 char* strPtr = nationPtr->food_str();
421
422 font_mid.disp( 307, 10, strPtr, 445);
423
424 //------- display cash and profit --------//
425
426 strPtr = nationPtr->cash_str();
427
428 font_mid.disp( 305, 30, strPtr, 445);
429
430 //------- display reputation ---------//
431
432 if( nationPtr->reputation >= 0 )
433 {
434 str = misc.format( (int)nationPtr->reputation, 4 ); // format type 4 - no thousand separators
435 }
436 else
437 {
438 str = "-";
439 str += misc.format( (int)-nationPtr->reputation, 4 ); // format type 4 - no thousand separators
440 }
441
442 int reputationChange = (int) nationPtr->reputation_change_365days();
443
444 if( reputationChange )
445 {
446 str += " (";
447
448 if( reputationChange > 0 )
449 str += "+";
450 else
451 str += "-";
452
453 str += abs(reputationChange);
454 str += ")";
455 }
456
457 image_icon.put_front(447,26, nationPtr->reputation_change_365days() >= (float)0.0 ? (char*)"REPU_UP" : (char*)"REPU_DW" );
458 font_mid.disp( 476, 30, str, 575);
459 font_mid.use_std_height();
460 }
461 //-------- End of function Info::disp_heading --------//
462
463
464 //-------- Begin of function Info::detect --------//
465
detect()466 int Info::detect()
467 {
468 //------------ detect objects ------------//
469
470 if( firm_array.selected_recno )
471 {
472 firm_array[firm_array.selected_recno]->detect_info_both();
473 }
474 else if( town_array.selected_recno )
475 {
476 town_array[town_array.selected_recno]->detect_info();
477 }
478 else if( site_array.selected_recno )
479 {
480 site_array[site_array.selected_recno]->detect_info();
481 }
482 else if( unit_array.selected_recno )
483 {
484 unit_array[unit_array.selected_recno]->detect_info();
485 }
486
487 return 0;
488 }
489 //-------- End of function Info::detect --------//
490
491
492 //-------- Begin of function Info::is_unit_build_menu_opened --------//
is_unit_build_menu_opened()493 bool Info::is_unit_build_menu_opened()
494 {
495 //--- Use exactly the same logic as Info::detect for determining if a unit is actively selected ---//
496 if( !firm_array.selected_recno && !town_array.selected_recno && !site_array.selected_recno
497 && unit_array.selected_recno )
498 {
499 return unit_array[unit_array.selected_recno]->is_in_build_menu();
500 }
501 return false;
502 }
503 //-------- End of function Info::is_unit_build_menu_opened --------//
504
505
506 //-------- Begin of function Info::draw_selected --------//
507
draw_selected()508 void Info::draw_selected()
509 {
510 if( firm_array.selected_recno )
511 firm_array[firm_array.selected_recno]->draw_selected();
512
513 else if( town_array.selected_recno )
514 town_array[town_array.selected_recno]->draw_selected();
515
516 else if( site_array.selected_recno )
517 site_array[site_array.selected_recno]->draw_selected();
518
519 else if( wall_res.selected_x_loc >= 0 )
520 wall_res.draw_selected();
521 }
522 //-------- End of function Info::draw_selected --------//
523
524
525 //-------- Begin of function Info::get_report_data --------//
526
get_report_data(int recNo)527 short Info::get_report_data(int recNo)
528 {
529 err_when( recNo<1 || recNo>report_array.size() );
530
531 return *((short*)report_array.get(recNo));
532 }
533 //-------- End of function Info::get_report_data --------//
534
535
536 //-------- Begin of function Info::get_report_data2 --------//
537
get_report_data2(int recNo)538 short Info::get_report_data2(int recNo)
539 {
540 err_when( recNo<1 || recNo>report_array2.size() );
541
542 return *((short*)report_array2.get(recNo));
543 }
544 //-------- End of function Info::get_report_data2 --------//
545
546
547 //-------- Begin of function Info::process_viewing_spy --------//
548
process_viewing_spy()549 void Info::process_viewing_spy()
550 {
551 //---- check if the viewing spy is still valid ----//
552
553 int isValid=1;
554
555 if( spy_array.is_deleted(viewing_spy_recno) ) // the spy is dead
556 {
557 isValid = 0;
558 }
559 else
560 {
561 Spy* spyPtr = spy_array[viewing_spy_recno];
562
563 //-- check if the spy still stay in the same place --//
564
565 if( spyPtr->spy_place_nation_recno() != info.viewing_nation_recno )
566 {
567 isValid = 0;
568 }
569 else
570 {
571 /*
572 //--- on average, a spy will get caught in 5 to 15 days when viewing the secret of its enemy ---//
573
574 if( misc2.random(5+spyPtr->spy_skill/5) ) // use m2 to avoid multiplayer sync problem
575 {
576 spyPtr->set_exposed(COMMAND_PLAYER);
577 isValid = 0;
578 }
579 */
580 }
581 }
582
583 if( !isValid ) //-- if not valid, set the mode back to normal viewing mode
584 sys.set_view_mode(MODE_NORMAL);
585 }
586 //-------- End of function Info::process_viewing_spy --------//
587
588
589 //------- Begin of function Info::play_time_str --------//
590 //
play_time_str()591 char* Info::play_time_str()
592 {
593 int totalMin = total_play_time / (60*1000);
594 int playHour = totalMin / 60;
595 int playMin = totalMin - playHour*60;
596
597 static String str;
598
599 str = "";
600
601 if( playHour )
602 {
603 // TRANSLATORS: Part of "%d hour(s) and %d minute(s)"
604 str.catf(ngettext("%d hour and", "%d hours and", playHour), playHour);
605 str += " ";
606 }
607
608 str.catf(ngettext("%d minute", "%d minutes", playMin), playMin);
609
610 return str;
611 }
612 //---------- End of function Info::play_time_str ---------//
613
614
615 //------- Begin of function Info::game_duration_str --------//
616 //
game_duration_str()617 char* Info::game_duration_str()
618 {
619 //------- get the true game start date --------//
620
621 int gameStartDate;
622
623 //-- For scenarios (whose goal_difficulty are > 0 ), the actual game start date is no info.game_start_date, we must calculate it
624
625 if( info.goal_difficulty )
626 {
627 gameStartDate = date.julian( date.year(info.goal_deadline)-config.goal_year_limit,
628 date.month(info.goal_deadline),
629 date.day(info.goal_deadline) );
630 }
631 else
632 {
633 gameStartDate = info.game_start_date;
634 }
635
636 //---------------------------------------------//
637
638 int totalDay = info.game_date - gameStartDate;
639 int playYear = totalDay / 365;
640 int playDay = totalDay - playYear*365;
641
642 static String str;
643
644 str = "";
645
646 if( playYear )
647 {
648 // TRANSLATORS: Part of "%d year(s) and %d day(s)"
649 str.catf(ngettext("%d year and", "%d years and", playYear), playYear);
650 str += " ";
651 }
652
653 str.catf(ngettext("%d day", "%d days", playDay), playDay);
654
655 return str;
656 }
657 //---------- End of function Info::game_duration_str ---------//
658
659
660 //------- Begin of function Info::save_game_scr --------//
661 //
save_game_scr()662 void Info::save_game_scr()
663 {
664 err_when( save_buf_1 );
665
666 // top and buttom
667 if( 0 < ZOOM_Y1 )
668 {
669 save_buf_1 = vga_front.save_area(0, 0, VGA_WIDTH-1, ZOOM_Y1-1);
670 save_buf_1b = vga_back.save_area(0, 0, VGA_WIDTH-1, ZOOM_Y1-1); // save the back buffer also as the top area of the back buf is used for font display
671 }
672
673 if( ZOOM_Y2 < VGA_HEIGHT-1 )
674 save_buf_2 = vga_front.save_area(0, ZOOM_Y2+1, VGA_WIDTH-1, VGA_HEIGHT-1);
675
676 // left and right
677 if( 0 < ZOOM_X1 )
678 save_buf_3 = vga_front.save_area(0, ZOOM_Y1, ZOOM_X1-1, ZOOM_Y2);
679
680 if( ZOOM_X2 < VGA_WIDTH-1 )
681 save_buf_4 = vga_front.save_area(ZOOM_X2+1, ZOOM_Y1, VGA_WIDTH-1, ZOOM_Y2);
682 }
683 //---------- End of function Info::save_game_scr ---------//
684
685
686 //------- Begin of function Info::rest_game_scr --------//
687 //
rest_game_scr()688 void Info::rest_game_scr()
689 {
690 // restore area outside front buffer
691 if(save_buf_4)
692 vga_front.rest_area(save_buf_4, 1);
693 if(save_buf_3)
694 vga_front.rest_area(save_buf_3, 1);
695 if(save_buf_2)
696 vga_front.rest_area(save_buf_2, 1);
697 if(save_buf_1)
698 vga_front.rest_area(save_buf_1, 1);
699 if(save_buf_1b)
700 vga_back.rest_area(save_buf_1b, 1);
701
702 save_buf_1 = NULL;
703
704 info.disp();
705 sys.blt_virtual_buf(); // blt the virtual front buffer to the screen
706 }
707 //---------- End of function Info::rest_game_scr ---------//
708
709
710 //------- Begin of function Info::free_game_scr --------//
711 //
free_game_scr()712 void Info::free_game_scr()
713 {
714 if(save_buf_4)
715 mem_del(save_buf_4);
716
717 if(save_buf_3)
718 mem_del(save_buf_3);
719
720 if(save_buf_2)
721 mem_del(save_buf_2);
722
723 if(save_buf_1)
724 mem_del(save_buf_1);
725
726 if(save_buf_1b)
727 mem_del(save_buf_1b);
728
729 save_buf_1 = NULL;
730 }
731 //---------- End of function Info::free_game_scr ---------//
732
733
734 //------- Begin of function Info::disp_loyalty --------//
735 //
736 // return: <int> x2 - the ending x position of the loyalty string.
737 //
disp_loyalty(int x,int y,int x2,int curLoyalty,int targetLoyalty,int nationRecno,int refreshFlag)738 int Info::disp_loyalty(int x, int y, int x2, int curLoyalty, int targetLoyalty, int nationRecno, int refreshFlag)
739 {
740 int endX;
741
742 if( x != x2 ) // if x==x2, don't display the field name.
743 {
744 font_san.field( x, y, _("Loyalty"), x2, curLoyalty, 1, INFO_X2-2, refreshFlag);
745 endX = x2 + 4 + font_san.text_width( misc.format(curLoyalty) );
746 }
747 else
748 {
749 endX = font_san.put( x2+4, y+2, curLoyalty, 1 );
750 }
751
752 if( nation_array[nationRecno]->cash <= 0 ) // if the nation no longer has money to pay the unit
753 targetLoyalty = 0;
754
755 if( curLoyalty != targetLoyalty ) // only increase, no decrease. Decrease are caused by events. Increases are made gradually
756 {
757 int tx = x2+6+font_san.text_width( misc.format(curLoyalty) );
758
759 if( targetLoyalty > curLoyalty )
760 image_icon.put_front( tx, y+2, "ARROWUP" );
761
762 else if( targetLoyalty < curLoyalty )
763 image_icon.put_front( tx, y+2, "ARROWDWN" );
764
765 endX = font_san.put( tx+10, y+2, targetLoyalty, 1 );
766 }
767
768 return endX;
769 }
770 //---------- End of function Info::disp_loyalty ---------//
771
772
773