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 : OHELP.CPP
22 //Description : Object Help
23
24 #include <ALL.h>
25 #include <OSYS.h>
26 #include <OSTR.h>
27 #include <OVGA.h>
28 #include <OMUSIC.h>
29 #include <OFONT.h>
30 #include <OINFO.h>
31 #include <OGAME.h>
32 #include <OUNIT.h>
33 #include <OBOX.h>
34 #include <OMOUSE.h>
35 #include <OVBROWSE.h>
36 #include <OFILETXT.h>
37 #include <OHELP.h>
38 #include "gettext.h"
39
40
41 //---------- Define constant -------------//
42
43 #define HELP_BOX_COLOR (VGA_GRAY+5)
44 #define HELP_INACTIVE_TIME ((float) 0.8) // when the mouse is inactive for one second, display help
45
46 enum { HELP_SCR_BUF_WIDTH = 400, // 400
47 HELP_SCR_BUF_HEIGHT = 200, // 300
48 HELP_SCR_BUF_SIZE = HELP_SCR_BUF_WIDTH * HELP_SCR_BUF_HEIGHT };
49
50 enum { MSG_LINE_SPACE = 4 };
51
52 enum { X_MARGIN = 10,
53 Y_MARGIN = 6 };
54
55 enum { MSG_WIN_WIDTH = 390,
56 MSG_HEAD_HEIGHT = 16 };
57
58
59 //------- Begin of function Help::Help ----------//
60
Help()61 Help::Help()
62 {
63 memset( this, 0, sizeof(Help) );
64
65 save_scr_x1 = -1;
66 }
67 //------- Begin of function Help::Help ----------//
68
69
70 //------- Begin of function Help::~Help ----------//
71
~Help()72 Help::~Help()
73 {
74 deinit();
75 }
76 //------- Begin of function Help::~Help ----------//
77
78
79 //------- Begin of function Help::init ----------//
80
init(const char * resName)81 void Help::init(const char* resName)
82 {
83 help_info_array = (HelpInfo*) mem_add( sizeof(HelpInfo) * MAX_HELP_INFO );
84
85 String str;
86 str = DIR_RES;
87 str += resName;
88
89 load( str );
90
91 save_scr_buf = mem_add(HELP_SCR_BUF_SIZE);
92 }
93 //------- Begin of function Help::init ----------//
94
95
96 //------- Begin of function Help::deinit ----------//
97
deinit()98 void Help::deinit()
99 {
100 if( save_scr_buf )
101 {
102 mem_del( save_scr_buf );
103 save_scr_buf = NULL;
104 }
105
106 if( help_info_array )
107 {
108 mem_del( help_info_array );
109 help_info_array = NULL;
110 }
111
112 if( help_text_buf )
113 {
114 mem_del( help_text_buf );
115 help_text_buf = NULL;
116 help_text_buf_size = 0;
117 }
118 }
119 //------- Begin of function Help::deinit ----------//
120
121
122 //------- Begin of function Help::load ----------//
123 //
124 // <char*> helpFileName = name of the help file.
125 //
load(char * helpFileName)126 void Help::load(char* helpFileName)
127 {
128 //------ Open the file and allocate buffer -----//
129
130 FileTxt fileTxt( helpFileName );
131
132 int dataSize = fileTxt.file_size();
133
134 if( dataSize > help_text_buf_size )
135 {
136 help_text_buf = mem_resize( help_text_buf, dataSize ); // allocate a buffer larger than we need for the largest size possible
137 help_text_buf_size = dataSize;
138 }
139
140 //-------- read in help info one by one --------//
141
142 HelpInfo* iptr = help_info_array;
143 char* textPtr = help_text_buf;
144 int readLen, totalReadLen=0; // total length of text read into the help_text_buf
145 int loopCount=0;
146 char* tokenStr;
147
148 help_info_count=0;
149
150 while( !fileTxt.is_eof() )
151 {
152 err_when( loopCount++ > 10000 );
153
154 tokenStr = fileTxt.get_token(0); // don't advance the pointer
155
156 if( !tokenStr )
157 break;
158
159 //--------- if it's a help code ----------//
160
161 if( tokenStr[0] >= 'A' && tokenStr[0] <= 'Z' )
162 {
163 strncpy( iptr->help_code, tokenStr, iptr->HELP_CODE_LEN );
164 iptr->help_code[iptr->HELP_CODE_LEN] = '\0';
165 }
166
167 //------ if it's a help area position ------//
168
169 else if( tokenStr[0] >= '0' && tokenStr[0] <= '9' )
170 {
171 iptr->area_x1 = (short) fileTxt.get_num();
172 iptr->area_y1 = (short) fileTxt.get_num();
173 iptr->area_x2 = (short) fileTxt.get_num();
174 iptr->area_y2 = (short) fileTxt.get_num();
175 }
176 else
177 err_here();
178
179 //---------- next line -----------//
180
181 fileTxt.next_line();
182
183 if( fileTxt.is_eof() )
184 break;
185
186 //--------------------------------------------//
187 // get help title
188 //--------------------------------------------//
189
190 fileTxt.read_line( iptr->help_title, iptr->HELP_TITLE_LEN );
191
192 //---------------------------------------------------------//
193 // get help description
194 //---------------------------------------------------------//
195
196 readLen = fileTxt.read_paragraph(textPtr, help_text_buf_size-totalReadLen);
197
198 iptr->help_text_ptr = textPtr;
199 iptr->help_text_len = readLen;
200
201 textPtr += readLen;
202 totalReadLen += readLen;
203
204 err_when( totalReadLen>help_text_buf_size );
205
206 //----------- next help block -------------//
207
208 fileTxt.next_line(); // pass the page break line
209
210 help_info_count++;
211 iptr++;
212
213 err_when( help_info_count >= MAX_HELP_INFO );
214 }
215 }
216 //--------- End of function Help::load ----------//
217
218
219 //---------- Begin of function Help::save_scr ---------//
220 //
221 // Save the specified porton of the screen
222 //
223 // <int> x1,y1,x2,y2 = the area of the screen
224 //
save_scr(int x1,int y1,int x2,int y2)225 void Help::save_scr(int x1, int y1, int x2, int y2)
226 {
227 if( save_scr_x1 >= 0 ) // there is already a screen saved
228 return;
229
230 err_when( x1>x2 || y1>y2 || x1<0 || y1<0 || x2>=VGA_WIDTH || y2>=VGA_HEIGHT );
231
232 long saveSize = (long)(x2-x1+1) * (y2-y1+1);
233
234 err_when( saveSize > HELP_SCR_BUF_SIZE );
235
236 save_scr_x1 = x1;
237 save_scr_y1 = y1;
238 save_scr_x2 = x2;
239 save_scr_y2 = y2;
240
241 mouse.hide_area( x1, y1, x2, y2 );
242
243 vga_front.read_bitmap( x1, y1, x2, y2, save_scr_buf );
244
245 mouse.show_area();
246 }
247 //------------ End of function Help::save_scr ---------//
248
249
250 //----------- Begin of function Help::rest_scr --------//
251 //
252 // Restore previously saved screen
253 //
rest_scr()254 void Help::rest_scr()
255 {
256 if( save_scr_x1 < 0 ) // already restored, or not saved yet
257 return;
258
259 err_when( save_scr_x1>save_scr_x2 || save_scr_y1>save_scr_y2 ||
260 save_scr_x1<0 || save_scr_y1<0 || save_scr_x2>=VGA_WIDTH || save_scr_y2>=VGA_HEIGHT );
261
262 // mouse.hide_area( save_scr_x1, save_scr_y1, save_scr_x2, save_scr_y2 );
263
264 vga_front.put_bitmap( save_scr_x1, save_scr_y1, save_scr_buf );
265 sys.blt_virtual_buf();
266
267 // mouse.show_area();
268
269 mouse.show();
270
271 save_scr_x1 = -1; // state that it has been restored.
272 }
273 //------------ End of function Help::rest_scr ----------//
274
275
276 //----------- Begin of function Help::disp --------//
277 //
278 // Display help message on the given screen location.
279 //
disp()280 void Help::disp()
281 {
282 //---- first check if we should disp the help now ------//
283
284 if( !should_disp() )
285 {
286 help_code[0] = '\0'; // reset it everytime after displaying, if the mouse is still on the button, help_code will be set again.
287 custom_help_title[0] = '\0';
288 return;
289 }
290
291 //-------- button help ---------//
292
293 if( help_code[0] )
294 {
295 //--------- locate the help and display it ----------//
296
297 int i;
298 HelpInfo* helpInfo = help_info_array;
299
300 for( i=0 ; i<help_info_count ; i++, helpInfo++ )
301 {
302 if( helpInfo->help_code[0] == help_code[0] &&
303 strcmp( helpInfo->help_code, help_code )==0 )
304 {
305 const char *text_ptr = *helpInfo->help_text_ptr ? _(helpInfo->help_text_ptr) : helpInfo->help_text_ptr;
306 disp_help( help_x, help_y,
307 _(helpInfo->help_title), text_ptr );
308 break;
309 }
310 }
311
312 help_code[0] = '\0'; // reset it everytime after displaying, if the mouse is still on the button, help_code will be set again.
313 }
314
315 //-------- custom help ---------//
316
317 else if( custom_help_title[0] )
318 {
319 disp_help(help_x, help_y, custom_help_title, custom_help_detail);
320 custom_help_title[0] = '\0';
321 }
322
323 //-------- other interface help ---------//
324
325 else
326 {
327 //--------- locate the help and display it ----------//
328
329 int i;
330 HelpInfo* helpInfo = help_info_array;
331
332 int spotX = mouse.cur_x;
333 int spotY = mouse.cur_y;
334
335 for( i=0 ; i<help_info_count ; i++, helpInfo++ )
336 {
337 if( spotX >= helpInfo->area_x1 && spotY >= helpInfo->area_y1 &&
338 spotX <= helpInfo->area_x2 && spotY <= helpInfo->area_y2 )
339 {
340 const char *text_ptr = *helpInfo->help_text_ptr ? _(helpInfo->help_text_ptr) : helpInfo->help_text_ptr;
341 disp_help( (helpInfo->area_x1+helpInfo->area_x2)/2,
342 (helpInfo->area_y1+helpInfo->area_y2)/2,
343 _(helpInfo->help_title), text_ptr );
344 break;
345 }
346 }
347 }
348 }
349 //------------ End of function Help::disp ----------//
350
351
352 //---------- Begin of function Help::disp_help ----------//
353 //
354 // <int> centerX, centerY - the center position of the help area.
355 // <char*> helpTitle - title of the help
356 // [char*] helpDetail - detail of the help.
357 //
disp_help(int centerX,int centerY,const char * helpTitle,const char * helpDetail)358 void Help::disp_help(int centerX, int centerY, const char* helpTitle, const char* helpDetail)
359 {
360 if( config.help_mode == NO_HELP )
361 return;
362
363 mouse.hide();
364
365 //------ calculate the position of the help box ------//
366
367 int winWidth, winHeight, dispHelpDetail=0;
368
369 if( helpDetail && helpDetail[0] && // with detailed help
370 config.help_mode == DETAIL_HELP ) // Detailed Help
371 {
372 winWidth = font_san.text_width(helpDetail, -1, MSG_WIN_WIDTH-X_MARGIN*2) + X_MARGIN*2;
373 winHeight = Y_MARGIN*2 + font_san.height() + 8 + font_san.text_height(MSG_LINE_SPACE); // text_width() must be called before calling text_height()
374
375 dispHelpDetail = 1;
376 }
377 else // Help title only
378 {
379 winWidth = font_san.text_width(helpTitle, -1, MSG_WIN_WIDTH-X_MARGIN*2) + X_MARGIN*2;
380 winHeight = Y_MARGIN*2 + font_san.height();
381 }
382
383 //--- if the text is bigger than one text box can hold, use a scrollable text box ---//
384
385 int x1, y1, x2, y2;
386
387 if( winWidth * winHeight > HELP_SCR_BUF_SIZE )
388 {
389 x1 = MAX( 2, centerX - HELP_SCR_BUF_WIDTH / 2 );
390 y1 = MAX( 2, centerY - HELP_SCR_BUF_HEIGHT / 2 );
391
392 x2 = x1 + HELP_SCR_BUF_WIDTH - 1;
393 y2 = y1 + HELP_SCR_BUF_HEIGHT - 1;
394 }
395 else
396 {
397 x1 = MAX( 2, centerX - winWidth / 2 );
398 y1 = MAX( 2, centerY - winHeight / 2 );
399
400 x2 = x1 + winWidth - 1;
401 y2 = y1 + winHeight - 1;
402 }
403
404 if( x2 >= VGA_WIDTH )
405 {
406 x2 = VGA_WIDTH-10;
407 x1 = x2-winWidth+1;
408 }
409
410 if( y2 >= VGA_HEIGHT )
411 {
412 y2 = VGA_HEIGHT-3;
413 y1 = y2-winHeight+1;
414 }
415
416 //------------- save the area --------------//
417
418 help.save_scr( x1, y1, x2, y2 ); // save the screen to the private buffer in Help
419
420 //------- Draw box (and arrow if specified object) ------//
421
422 vga_front.bar( x1, y1, x2, y2, V_WHITE );
423
424 vga_front.bar( x1, y1, x2, y1+1, HELP_BOX_COLOR ); // Top border
425 vga_front.bar( x1, y2-1, x2, y2, HELP_BOX_COLOR ); // Bottom border
426 vga_front.bar( x1, y1, x1+1, y2, HELP_BOX_COLOR ); // Left border
427 vga_front.bar( x2-1, y1, x2, y2, HELP_BOX_COLOR ); // Right border
428
429 //--------- disp help detail -----------//
430
431 font_san.put( x1+X_MARGIN, y1+Y_MARGIN, helpTitle );
432
433 if( dispHelpDetail )
434 {
435 int y = y1 + Y_MARGIN + font_san.height() + 4;
436
437 vga_front.bar( x1, y, x2, y+1, HELP_BOX_COLOR ); // line between description and help text
438
439 font_san.put_paragraph( x1+X_MARGIN, y+4, x2-X_MARGIN, y2-Y_MARGIN, helpDetail, MSG_LINE_SPACE );
440 }
441
442 if( sys.debug_session )
443 sys.blt_virtual_buf();
444
445 //--- in a single player game, pause the game when a help message is disp_helplayed ---//
446
447 while( help.should_disp() )
448 {
449 sys.yield();
450 vga.flip();
451 music.yield();
452
453 mouse.get_event();
454 }
455
456 help.rest_scr();
457 }
458 //--------- End of function Help::disp_help ----------//
459
460
461 //--------- Begin of function Help::should_disp --------//
462 //
should_disp()463 int Help::should_disp()
464 {
465 if( config.help_mode == NO_HELP )
466 return 0;
467
468 if( last_mouse_x==mouse.cur_x && last_mouse_y==mouse.cur_y &&
469 !mouse.left_press && !mouse.right_press && !mouse.any_click(2) )
470 {
471 if( misc.get_time() >= mouse_still_time + HELP_INACTIVE_TIME * 1000 )
472 {
473 return 1;
474 }
475 }
476 else
477 {
478 last_mouse_x = mouse.cur_x;
479 last_mouse_y = mouse.cur_y;
480 mouse_still_time = misc.get_time();
481 }
482
483 return 0;
484 }
485 //---------- End of function Help::should_disp ---------//
486
487
488 //--------- Begin of function Help::set_help --------//
489 //
set_help(int x1,int y1,int x2,int y2,const char * helpCode)490 void Help::set_help(int x1, int y1, int x2, int y2, const char* helpCode)
491 {
492 err_when( strlen(helpCode) > 8 );
493
494 if( !mouse.in_area(x1, y1, x2, y2) )
495 return;
496
497 strcpy( help_code, helpCode );
498
499 help_x = (x1+x2)/2;
500 help_y = (y1+y2)/2;
501 }
502 //---------- End of function Help::set_help ---------//
503
504
505 const char *unit_help_general[MAX_UNIT_TYPE+1] =
506 {
507 N_("Norman General"),
508 N_("Maya General"),
509 N_("Greek General"),
510 N_("Viking General"),
511 N_("Persian General"),
512 N_("Chinese General"),
513 N_("Japanese General"),
514 N_("Caravan"),
515 N_("Catapult"),
516 N_("Ballista"),
517 N_("Spitfire"),
518 N_("Cannon"),
519 N_("Porcupine"),
520 N_("Trader"),
521 N_("Transport"),
522 N_("Caravel"),
523 N_("Galleon"),
524 N_("Dragon"),
525 N_("Jing Nung"),
526 N_("Lord of Healing"),
527 N_("Thor"),
528 N_("Phoenix"),
529 N_("Kukulcan"),
530 N_("Mind Turner"),
531 // TRANSLATORS: Unit icon help popup with rank title. In the original game, fryhtans used the human ranks in the help message. Use General or Ordo whichever makes sense.
532 N_("Deezboanz General"),
533 N_("Rattus General"),
534 N_("Broosken General"),
535 N_("Haubudam General"),
536 N_("Pfith General"),
537 N_("Rokken General"),
538 N_("Doink General"),
539 N_("Wyrm General"),
540 N_("Droog General"),
541 N_("Ick General"),
542 N_("Sauroid General"),
543 N_("Karrotten General"),
544 N_("Holgh General"),
545 N_("Egyptian General"),
546 N_("Mughul General"),
547 N_("Zulu General"),
548 N_("Isis"),
549 N_("Djini"),
550 N_("uNkulunkulu"),
551 N_("Unicorn"),
552 };
553 const char *unit_help_king[MAX_UNIT_TYPE+1] =
554 {
555 N_("Norman King"),
556 N_("Maya King"),
557 N_("Greek King"),
558 N_("Viking King"),
559 N_("Persian King"),
560 N_("Chinese King"),
561 N_("Japanese King"),
562 N_("Caravan"),
563 N_("Catapult"),
564 N_("Ballista"),
565 N_("Spitfire"),
566 N_("Cannon"),
567 N_("Porcupine"),
568 N_("Trader"),
569 N_("Transport"),
570 N_("Caravel"),
571 N_("Galleon"),
572 N_("Dragon"),
573 N_("Jing Nung"),
574 N_("Lord of Healing"),
575 N_("Thor"),
576 N_("Phoenix"),
577 N_("Kukulcan"),
578 N_("Mind Turner"),
579 // TRANSLATORS: Unit icon help popup with rank title. In the original game, fryhtans used the human ranks in the help message. Use King or All High whichever makes sense.
580 N_("Deezboanz King"),
581 N_("Rattus King"),
582 N_("Broosken King"),
583 N_("Haubudam King"),
584 N_("Pfith King"),
585 N_("Rokken King"),
586 N_("Doink King"),
587 N_("Wyrm King"),
588 N_("Droog King"),
589 N_("Ick King"),
590 N_("Sauroid King"),
591 N_("Karrotten King"),
592 N_("Holgh King"),
593 N_("Egyptian King"),
594 N_("Mughul King"),
595 N_("Zulu King"),
596 N_("Isis"),
597 N_("Djini"),
598 N_("uNkulunkulu"),
599 N_("Unicorn"),
600 };
601 //--------- Begin of function Help::set_unit_help --------//
602 //
set_unit_help(int unitId,int rankId,int x1,int y1,int x2,int y2)603 void Help::set_unit_help(int unitId, int rankId, int x1, int y1, int x2, int y2)
604 {
605 if( !mouse.in_area(x1, y1, x2, y2) )
606 return;
607
608 //-------- compose the help string --------//
609
610 static String str;
611
612 if( rankId==RANK_KING )
613 str = _(unit_help_king[unitId-1]);
614 else if( rankId==RANK_GENERAL )
615 str = _(unit_help_general[unitId-1]);
616 else
617 str = _(unit_res[unitId]->name);
618
619 set_custom_help( x1, y1, x2, y2, str );
620 }
621 //---------- End of function Help::set_unit_help ---------//
622
623
624 //--------- Begin of function Help::set_custom_help --------//
625 //
626 // <int> x1, y1, x2, y2 - the coordination of the help
627 // <char*> helpTitle - the title of the help
628 // [char*] helpDetail - the detailed text of the help
629 //
set_custom_help(int x1,int y1,int x2,int y2,const char * helpTitle,const char * helpDetail)630 void Help::set_custom_help(int x1, int y1, int x2, int y2, const char* helpTitle, const char* helpDetail)
631 {
632 if( !mouse.in_area(x1, y1, x2, y2) )
633 return;
634
635 help_x = (x1+x2)/2;
636 help_y = (y1+y2)/2;
637
638 strncpy( custom_help_title, helpTitle, CUSTOM_HELP_TITLE_LEN );
639 custom_help_title[CUSTOM_HELP_TITLE_LEN] = '\0';
640
641 if( helpDetail )
642 {
643 strncpy( custom_help_detail, helpDetail, CUSTOM_HELP_DETAIL_LEN );
644 custom_help_detail[CUSTOM_HELP_DETAIL_LEN] = '\0';
645 }
646 else
647 {
648 custom_help_detail[0] = '\0';
649 }
650 }
651 //---------- End of function Help::set_custom_help ---------//
652
653
654