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