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    : OTOWNIF.CPP
22 //Description : Town interface routines
23 
24 #include <OINFO.h>
25 #include <OBOX.h>
26 #include <OVGA.h>
27 #include <vga_util.h>
28 #include <OSYS.h>
29 #include <OHELP.h>
30 #include <OSPY.h>
31 #include <OSTR.h>
32 #include <OFONT.h>
33 #include <OMOUSE.h>
34 #include <OVBROWIF.h>
35 #include <OGAME.h>
36 #include <ONATION.h>
37 #include <OBUTT3D.h>
38 #include <OIMGRES.h>
39 #include <ORAWRES.h>
40 #include <ORACERES.h>
41 #include <OWORLD.h>
42 #include <OUNIT.h>
43 #include <OTOWN.h>
44 #include <OREMOTE.h>
45 #include <OSE.h>
46 #include <OSERES.h>
47 #include <OBUTTCUS.h>
48 #include "gettext.h"
49 
50 
51 //------------- Define coordinations -----------//
52 
53 enum { RACE_BROWSE_X1 = INFO_X1,
54 		 RACE_BROWSE_Y1 = INFO_Y1+48,
55 		 RACE_BROWSE_X2 = INFO_X2,
56 		 RACE_BROWSE_Y2 = RACE_BROWSE_Y1+130,
57 	  };
58 
59 enum { BUTTON_X1 = INFO_X1,
60 		 BUTTON_Y1 = RACE_BROWSE_Y2+28,
61 		 BUTTON_X2 = INFO_X2,
62 		 BUTTON_Y2 = BUTTON_Y1+50,
63      };
64 
65 //---------- Define constant ------------//
66 
67 enum { TOWN_MENU_MAIN,
68 		 TOWN_MENU_TRAIN,
69 		 TOWN_MENU_SPY,
70 		 TOWN_MENU_VIEW_SECRET,
71 		 TOWN_MENU_SET_AUTO_COLLECT_TAX,
72 		 TOWN_MENU_SET_AUTO_GRANT,
73 	  };
74 
75 #define BUTTON_LOYALTY_COUNT	8
76 #define COUNT_BUTTON_OFFSET_X	165
77 #define COUNT_BUTTON_OFFSET_Y 5
78 #define COUNT_BUTTON_WIDTH		32
79 #define COUNT_BUTTON_HEIGHT	32
80 
81 //----------- Define static variables ----------//
82 
83 static VBrowseIF  browse_race, browse_spy;
84 static Button3D   button_recruit, button_train, button_tax, button_grant;
85 static Button3D   button_spy, button_cancel, button_spy_mobilize,
86 						button_spy_reward, button_spy_action, button_spy_view_secret;
87 static Button3D	button_cancel_training;
88 static ButtonCustom  button_cancel3;
89 static Button		button_loyalty_array[BUTTON_LOYALTY_COUNT];
90 static Button		button_loyalty_disabled;
91 static Button		button_cancel2;
92 static ButtonCustom button_skill[MAX_TRAINABLE_SKILL];
93 static ButtonCustom button_queue_skill[MAX_TRAINABLE_SKILL];
94 static int        queue_train_selected;
95 static short      browse_race_recno=1, browse_race_town_recno=0;		// the town which the browse_race displays its info
96 static short      recruit_race_count;
97 static short		spy_count;
98 static short      last_town_recno=0, last_rebel_recno=0;
99 static char 		last_has_linked_own_camp;
100 static char       town_menu_mode=TOWN_MENU_MAIN;
101 static char       disable_refresh=0;
102 static short 		action_spy_recno;	// recno of the spy that is doing the bribing or viewing secret reports of other nations
103 
104 //-------- Define static class member var ------//
105 
106 short  Town::if_town_recno = 0;
107 
108 //----------- Define static functions ----------//
109 
110 static int  race_filter(int recNo=0);
111 static int  spy_filter(int recNo=0);
112 static void put_race_rec(int recNo, int x, int y, int refreshFlag);
113 static void put_spy_rec(int recNo, int x, int y, int refreshFlag);
114 // ###### begin Gilbert 12/9 ########//
115 static void i_disp_skill_button(ButtonCustom *button, int);
116 static void i_disp_queue_skill_button(ButtonCustom *button, int);
117 // ###### end Gilbert 12/9 ########//
118 
119 
120 //--------- Begin of function Town::disp_info ---------//
121 //
disp_info(int refreshFlag)122 void Town::disp_info(int refreshFlag)
123 {
124 	if_town_recno = town_recno;
125 
126 	if( town_recno != last_town_recno ||
127 		 (refreshFlag==INFO_REPAINT && !disable_refresh) )
128 	{
129 		if( town_recno != last_town_recno )
130 			browse_race_recno = 1;
131 
132 		town_menu_mode  = TOWN_MENU_MAIN;
133 		last_town_recno = town_recno;
134 	}
135 
136 	//-----------------------------------------//
137 
138 	int needRepaint=0;
139 
140 	if( last_rebel_recno != rebel_recno )
141 	{
142 		last_rebel_recno = rebel_recno;
143 		needRepaint = 1;
144 	}
145 
146 	if( last_has_linked_own_camp != has_linked_own_camp )
147 	{
148 		last_has_linked_own_camp = has_linked_own_camp;
149 		needRepaint = 1;
150 	}
151 
152 	if( needRepaint && refreshFlag == INFO_UPDATE )
153 	{
154 		info.disp();
155 		return;
156 	}
157 
158 	//-----------------------------------//
159 
160 	switch( town_menu_mode )
161 	{
162 		case TOWN_MENU_MAIN:
163 			disp_main_menu(refreshFlag);
164 			break;
165 
166 		case TOWN_MENU_TRAIN:
167 			disp_train_menu(refreshFlag);
168 			break;
169 
170 		case TOWN_MENU_SPY:
171 			disp_spy_menu(refreshFlag);
172 			break;
173 
174 		case TOWN_MENU_VIEW_SECRET:
175 			spy_array.disp_view_secret_menu(action_spy_recno, refreshFlag);
176 			break;
177 
178 		case TOWN_MENU_SET_AUTO_COLLECT_TAX:
179 			if( refreshFlag == INFO_REPAINT )
180 				disp_auto_menu(1);
181 			break;
182 
183 		case TOWN_MENU_SET_AUTO_GRANT:
184 			if( refreshFlag == INFO_REPAINT )
185 				disp_auto_menu(0);
186 			break;
187 	}
188 }
189 //----------- End of function Town::disp_info -----------//
190 
191 
192 //--------- Begin of function Town::detect_info ---------//
193 //
detect_info()194 void Town::detect_info()
195 {
196 	if_town_recno = town_recno;
197 
198 	switch( town_menu_mode )
199 	{
200 		case TOWN_MENU_MAIN:
201 			if( detect_main_menu() )
202 				return;
203 			break;
204 
205 		case TOWN_MENU_TRAIN:
206 			if( detect_train_menu() )
207 				return;
208 			break;
209 
210 		case TOWN_MENU_SPY:
211 			if( detect_spy_menu() )
212 				return;
213 			break;
214 
215 		case TOWN_MENU_VIEW_SECRET:
216 			if( spy_array.detect_view_secret_menu(action_spy_recno, nation_recno) )
217 			{
218 				town_menu_mode = TOWN_MENU_MAIN;
219 				info.disp();
220 				return;
221 			}
222 			break;
223 
224 		case TOWN_MENU_SET_AUTO_COLLECT_TAX:
225 			if( detect_auto_menu(1) )
226 				return;
227 			break;
228 
229 		case TOWN_MENU_SET_AUTO_GRANT:
230 			if( detect_auto_menu(0) )
231 				return;
232 			break;
233 	}
234 
235 	if( ISKEY(KEYEVENT_OBJECT_PREV) )
236 	{
237 		town_array.disp_next(-1, 0);    // previous same object type of any nation
238 		return;
239 	}
240 
241 	if( ISKEY(KEYEVENT_OBJECT_NEXT) )
242 	{
243 		town_array.disp_next(1, 0);     // next same object type of any nation
244 		return;
245 	}
246 
247 	if( ISKEY(KEYEVENT_NATION_OBJECT_PREV) )
248 	{
249 		town_array.disp_next(-1, 1);    // prevous same object type of the same nation
250 		return;
251 	}
252 
253 	if( ISKEY(KEYEVENT_NATION_OBJECT_NEXT) )
254 	{
255 		town_array.disp_next(1, 1);     // next same object type of the same nation
256 		return;
257 	}
258 }
259 //----------- End of function Town::detect_info -----------//
260 
261 
262 //--------- Begin of function Town::disp_main_menu ---------//
263 //
disp_main_menu(int refreshFlag)264 void Town::disp_main_menu(int refreshFlag)
265 {
266 	static short lastTownNationRecno;
267 
268 	//--- if the town's owner nation has just been changed ---//
269 
270 	if( lastTownNationRecno != nation_recno )
271 	{
272 		lastTownNationRecno = nation_recno;
273 		info.disp();
274 		return;
275 	}
276 
277 	//--------- display basic info --------//
278 
279 	disp_basic_info(refreshFlag);
280 
281 	//---------- paint controls -----------//
282 
283 	if( refreshFlag == INFO_REPAINT )
284 	{
285 		recruit_race_count = race_filter();
286 
287 		//------ display browser field description -------//
288 
289 		int x=RACE_BROWSE_X1+2;
290 		int y=RACE_BROWSE_Y1-23;
291 
292 		vga_util.d3_panel_up( RACE_BROWSE_X1, y, RACE_BROWSE_X2, RACE_BROWSE_Y1-3 );
293 
294 		font_san.put( x+2  , y+4, _("Population") );
295 		font_san.put( x+70 , y+4, _("Peasants") );
296 
297 		if( nation_recno )      // only display loyalty if this town is controlled by a nation
298 			font_san.put( x+132, y+4, _("Loyalty") );
299 		else
300 		{
301 			#ifdef GERMAN
302 				font_san.put( x+128, y+4, "Resistance" );
303 			#else
304 				font_san.put( x+132, y+4, _("Resistance") );
305 			#endif
306 		}
307 
308 		//------------ create browser ------------//
309 
310 		browse_race.init( RACE_BROWSE_X1, RACE_BROWSE_Y1, RACE_BROWSE_X2, RACE_BROWSE_Y2,
311 								0, 25, recruit_race_count, put_race_rec );
312 
313 		browse_race.open(browse_race_recno);
314 
315 		browse_race_town_recno = town_recno;		// the town which browse_race displays
316 
317 		//---------- paint total section ----------//
318 
319 		vga_util.d3_panel_up( RACE_BROWSE_X1, RACE_BROWSE_Y2+3, RACE_BROWSE_X2, RACE_BROWSE_Y2+23 );
320 
321 		font_san.put( RACE_BROWSE_X1+5, RACE_BROWSE_Y2+7, _("Total") );
322 		font_san.put( RACE_BROWSE_X1+128, RACE_BROWSE_Y2+7, _("Avg") );
323 	}
324 	else
325 	{
326 		//---------- update controls -----------//
327 
328 		if( recruit_race_count != race_filter() )
329 		{
330 			info.disp();
331 			return;
332 		}
333 
334 		browse_race.update();
335 	}
336 
337 	browse_race_recno = browse_race.recno();
338 
339 	//----------- display total -----------//
340 
341 	font_mid.put( RACE_BROWSE_X1+52, RACE_BROWSE_Y2+6, population, 1 );
342 	font_mid.put( RACE_BROWSE_X1+94, RACE_BROWSE_Y2+6, jobless_population, 1 );
343 
344 	if( nation_recno )
345 		font_mid.put( RACE_BROWSE_X1+165, RACE_BROWSE_Y2+6, average_loyalty(), 1 );
346 	else
347 		font_mid.put( RACE_BROWSE_X1+165, RACE_BROWSE_Y2+6, average_resistance(nation_array.player_recno), 1 );
348 
349 	//------ if this town is controlled by a rebel group -----//
350 
351 	int x=BUTTON_X1, y=BUTTON_Y1;
352 
353 	if( rebel_recno )
354 	{
355 		if( refreshFlag == INFO_REPAINT )
356 			font_san.d3_put( BUTTON_X1, y-1, BUTTON_X2, y+19, _("Controlled by Rebels") );
357 
358 		y+=23;
359 	}
360 
361 	//----------- create the paint button ----------//
362 
363 	if( nation_recno==nation_array.player_recno )
364 	{
365 		if( refreshFlag == INFO_REPAINT )
366 		{
367 			button_recruit.paint( BUTTON_X1, y, 'A', "RECRUIT" );
368 
369 			if( has_linked_own_camp )
370 			{
371 				button_train.paint( BUTTON_X1+BUTTON_ACTION_WIDTH, y, 'A', "TRAIN" );
372 				button_tax.paint( BUTTON_X1+BUTTON_ACTION_WIDTH*2, y, 'A', "COLLTAX" );
373 				button_grant.paint( BUTTON_X1+BUTTON_ACTION_WIDTH*3, y, 'A', "GRANT" );
374 
375 				disp_auto_loyalty();
376 			}
377 			else
378 			{
379 				button_train.reset();
380 				button_tax.reset();
381 				button_grant.reset();
382 			}
383 
384 			#ifdef DEBUG
385 			if(debug2_enable_flag)
386 			{
387 				font_san.d3_put( INFO_X1, INFO_Y2-30, INFO_X2, INFO_Y2, "" );
388 				font_san.field( INFO_X1+10, INFO_Y2-20, " ", INFO_X1+20, town_recno, 1, INFO_X2-10, refreshFlag);
389 				font_san.field( INFO_X1+40, INFO_Y2-20, " ", INFO_X1+50, loc_x1, 1, INFO_X2-10, refreshFlag);
390 				font_san.field( INFO_X1+70, INFO_Y2-20, " ", INFO_X1+80, loc_y1, 1, INFO_X2-10, refreshFlag);
391 				font_san.field( INFO_X1+100, INFO_Y2-20, " ", INFO_X1+110, ai_link_checked, 1, INFO_X2-10, refreshFlag);
392 			}
393 			#endif
394 		}
395 
396 		if( has_linked_own_camp )		 // a whole row is used for displaying buttons, so additional buttons will be displayed in the next row
397 			y += BUTTON_ACTION_HEIGHT;
398 		else
399 			x += BUTTON_ACTION_WIDTH;	 // only one button "Recruit", new button displayed next to it.
400 
401 		//-------- enable/disable the train button -----------//
402 
403 		int raceId = race_filter(browse_race.recno());
404 
405 		if( can_recruit(raceId) )
406 			button_recruit.enable();
407 		else
408 			button_recruit.disable();
409 
410 		if( button_train.init_flag )
411 		{
412 			if( can_train(raceId) )
413 				button_train.enable();
414 			else
415 				button_train.disable();
416 		}
417 
418 		if( button_tax.init_flag )
419 		{
420 			// ###### patch begin Gilbert 5/8 ######//
421 			// if( average_loyalty() >= 1 )
422 			if( average_loyalty() > COLLECT_TAX_LOYALTY_DECREASE )
423 			// ###### end begin Gilbert 5/8 ######//
424 				button_tax.enable();
425 			else
426 				button_tax.disable();
427 		}
428 
429 		if( button_grant.init_flag )
430 		{
431 			if( nation_recno && nation_array[nation_recno]->cash > 0 )
432 				button_grant.enable();
433 			else
434 				button_grant.disable();
435 		}
436 
437 		//--------- display train info --------//
438 
439 		if( train_unit_recno )			// display the progress of the current training process
440 			disp_train_info(refreshFlag);
441 	}
442 
443 	//------ grant to an independent town ------//
444 
445 	else if( nation_array.player_recno &&
446 				can_grant_to_non_own_town(nation_array.player_recno) )
447 	{
448 		if( refreshFlag == INFO_REPAINT )
449 			button_grant.paint( BUTTON_X1, y, 'A', "GRANT2" );
450 
451 		if( button_grant.init_flag )
452 		{
453 			if( nation_array[nation_array.player_recno]->cash > 0 )
454 				button_grant.enable();
455 			else
456 				button_grant.disable();
457 		}
458 
459 		x += BUTTON_ACTION_WIDTH;
460 	}
461 
462 	//---------- display the spy button ----------//
463 
464 	int spyFlag = spy_filter() > 0;
465 
466 	if( refreshFlag == INFO_REPAINT )
467 	{
468 		if( spyFlag )				// only display the spy button for non-player towns
469 			button_spy.paint( x, y, 'A', "SPYMENU" );
470 		else
471 			button_spy.reset();
472 	}
473 	else
474 	{
475 		if( spyFlag != button_spy.init_flag )		// if the button availability has just changed
476 		{
477 			if(spyFlag)		// only display the spy button for non-player towns
478 				button_spy.paint( x, y, 'A', "SPYMENU" );
479 			else				// remove the button from the screen
480 				button_spy.hide();
481 		}
482 	}
483 
484 	//-------- display debug info ----------//
485 
486 	if( sys.debug_session || sys.testing_session )
487 		disp_debug_resistance(refreshFlag);
488 }
489 //----------- End of function Town::disp_main_menu -----------//
490 
491 
492 //--------- Begin of function Town::disp_auto_loyalty ---------//
493 //
disp_auto_loyalty()494 void Town::disp_auto_loyalty()
495 {
496 	if( auto_collect_tax_loyalty )
497 	{
498 		vga_front.bar( button_tax.x1+8, button_tax.y1+10, button_tax.x2-12, button_tax.y2-15, V_WHITE );
499 		vga_front.rect( button_tax.x1+8, button_tax.y1+10, button_tax.x2-12, button_tax.y2-15, 1, V_BLACK );
500 
501 		font_mid.center_put( button_tax.x1+8, button_tax.y1+10, button_tax.x2-12, button_tax.y2-15,
502 									misc.format(auto_collect_tax_loyalty) );
503 	}
504 
505 	if( auto_grant_loyalty )
506 	{
507 		vga_front.bar( button_grant.x1+8, button_grant.y1+10, button_grant.x2-12, button_grant.y2-15, V_WHITE );
508 		vga_front.rect( button_grant.x1+8, button_grant.y1+10, button_grant.x2-12, button_grant.y2-15, 1, V_BLACK );
509 
510 		font_mid.center_put( button_grant.x1+8, button_grant.y1+10, button_grant.x2-12, button_grant.y2-15,
511 									misc.format(auto_grant_loyalty) );
512 	}
513 }
514 //----------- End of function Town::disp_auto_loyalty -----------//
515 
516 
517 //--------- Begin of function Town::detect_main_menu ---------//
518 //
detect_main_menu()519 int Town::detect_main_menu()
520 {
521 	//--- detect clicking on the name area to center the map on it ---//
522 
523 	if( mouse.single_click(INFO_X1, INFO_Y1, INFO_X2, INFO_Y1+21) )
524 	{
525 		world.go_loc( center_x, center_y );
526 		return 1;
527 	}
528 
529 	//-------- detect browsers ---------//
530 
531 	if( browse_race.detect() )
532 	{
533 		browse_race_recno = browse_race.recno();
534 		// ##### begin patch Gilbert 21/1 #######//
535 		if( sys.debug_session || sys.testing_session )
536 			disp_debug_resistance(INFO_UPDATE);
537 		// ##### end patch Gilbert 21/1 #######//
538 		return 1;
539 	}
540 
541 	if( button_spy.detect() )	// switch to the spy menu
542 	{
543 		town_menu_mode  = TOWN_MENU_SPY;
544 		disable_refresh = 1;    // static var for disp_info() only
545 		info.disp();
546 		disable_refresh = 0;
547 		return 1;
548 	}
549 
550 	//----- detect granting to an independent town ---//
551 
552 	if( nation_array.player_recno &&
553 		 can_grant_to_non_own_town(nation_array.player_recno) )
554 	{
555 		if( button_grant.detect() )
556 		{
557 			se_ctrl.immediate_sound("TURN_ON");
558 
559 			grant_to_non_own_town(nation_array.player_recno, COMMAND_PLAYER);
560 			return 1;
561 		}
562 	}
563 
564 	//---------- buttons for player town only --------//
565 
566 	if( nation_recno!=nation_array.player_recno )
567 		return 0;
568 
569 	//------ update button status ------//
570 
571 	if( browse_race.recno() > race_filter() )
572 		return 0;
573 
574 	int raceId = race_filter(browse_race.recno());
575 
576 	if( can_recruit(raceId) )
577 		button_recruit.enable();
578 	else
579 		button_recruit.disable();
580 
581 	if( can_train(raceId) )
582 		button_train.enable();
583 	else
584 		button_train.disable();
585 
586 	//------- detect buttons --------//
587 
588 	if( button_recruit.detect(GETKEY(KEYEVENT_TOWN_RECRUIT)) )
589 	{
590 		recruit(-1, 0, COMMAND_PLAYER);
591 		return 1;
592 	}
593 
594 	if( button_train.detect(GETKEY(KEYEVENT_TOWN_TRAIN)) )
595 	{
596 		town_menu_mode = TOWN_MENU_TRAIN;
597 		disable_refresh = 1;    // static var for disp_info() only
598 		info.disp();
599 		disable_refresh = 0;
600 		return 1;
601 	}
602 
603 	int rc;
604 
605 	if( (rc=button_tax.detect(0, 0, 1)) > 0 )		// 1-detect right-clicking
606 	{
607 		disp_auto_loyalty();
608 
609 		// ##### begin Gilbert 26/9 ########//
610 		se_ctrl.immediate_sound("TURN_ON");
611 		// ##### end Gilbert 26/9 ########//
612 
613 		if( rc==1 )
614 		{
615 			town_array[town_recno]->collect_tax(COMMAND_PLAYER);
616 		}
617 		else if( rc==2 )		// right click
618 		{
619 			town_menu_mode  = TOWN_MENU_SET_AUTO_COLLECT_TAX;
620 			disable_refresh = 1;    // static var for disp_info() only
621 			info.disp();
622 			disable_refresh = 0;
623 		}
624 		return 1;
625 	}
626 
627 	if( (rc=button_grant.detect(0, 0, 1)) > 0 )
628 	{
629 		disp_auto_loyalty();
630 
631 		// ##### begin Gilbert 26/9 ########//
632 		se_ctrl.immediate_sound("TURN_ON");
633 		// ##### end Gilbert 26/9 ########//
634 
635 		if( rc==1 )
636 		{
637 			town_array[town_recno]->reward(COMMAND_PLAYER);
638 		}
639 		else if( rc==2 ) 		// right click
640 		{
641 			town_menu_mode  = TOWN_MENU_SET_AUTO_GRANT;
642 			disable_refresh = 1;    // static var for disp_info() only
643 			info.disp();
644 			disable_refresh = 0;
645 		}
646 		return 1;
647 	}
648 
649 	if(train_unit_recno)
650 	{
651 		if((rc = button_cancel_training.detect()))
652 		{
653 			if( !remote.is_enable() )
654 			{
655 				cancel_train_unit();
656 				info.disp();
657 			}
658 			else
659 			{
660 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_SKIP_RECRUIT, sizeof(short));
661 				shortPtr[0] = town_recno;
662 			}
663 			return 1;
664 		}
665 	}
666 
667 	return 0;
668 }
669 //----------- End of function Town::detect_main_menu -----------//
670 
671 
672 //--------- Begin of function Town::disp_basic_info ---------//
673 //
disp_basic_info(int refreshFlag)674 void Town::disp_basic_info(int refreshFlag)
675 {
676 	if( refreshFlag == INFO_REPAINT )
677 	{
678 		vga_util.d3_panel_up( INFO_X1, INFO_Y1, INFO_X2, INFO_Y1+21 );
679 
680 		if( nation_recno )
681 		{
682 			font_san.center_put( INFO_X1+21, INFO_Y1, INFO_X2-2, INFO_Y1+21, town_name() );
683 
684 			char *nationPict = image_button.get_ptr("V_COLCOD");
685 
686 			vga_front.put_bitmap_trans_remap_decompress(INFO_X1+3, INFO_Y1+2, nationPict, game.get_color_remap_table(nation_recno, 0) );
687 		}
688 		else
689 		{
690 			font_san.center_put( INFO_X1, INFO_Y1, INFO_X2-2, INFO_Y1+21, town_name() );
691 		}
692 	}
693 }
694 //----------- End of function Town::disp_basic_info -----------//
695 
696 
697 //--------- Begin of function Town::disp_train_info ---------//
698 //
disp_train_info(int refreshFlag)699 void Town::disp_train_info(int refreshFlag)
700 {
701 	if( !train_unit_recno || nation_recno!=nation_array.player_recno )
702 		return;
703 
704 	int dispY1 = INFO_Y1+26;
705 
706 	Unit* unitPtr = unit_array[train_unit_recno];
707 	int	x=MSG_X1+4, y=MSG_Y1+4;
708 
709 	if( refreshFlag == INFO_REPAINT )
710 	{
711 		vga_util.d3_panel_up( MSG_X1, MSG_Y1, MSG_X2, MSG_Y2 );
712 
713 		vga_util.d3_panel_down(x, y, x+RACE_ICON_WIDTH+3, y+RACE_ICON_HEIGHT+3 );
714 		vga_front.put_bitmap(x+2, y+2, race_res[unitPtr->race_id]->icon_bitmap_ptr );
715 
716 		// vga_util.d3_panel_down(x+RACE_ICON_WIDTH+6, y, MSG_X2-4, MSG_Y2-4 );
717 	}
718 
719 	int totalDays;
720 
721 	if( config.fast_build && nation_recno==nation_array.player_recno )
722 		totalDays = TOTAL_TRAIN_DAYS/2;
723 	else
724 		totalDays = TOTAL_TRAIN_DAYS;
725 
726 	vga_front.indicator( 0, x+RACE_ICON_WIDTH+6, y, float(sys.frame_count-start_train_frame_no),
727 		float(totalDays * FRAMES_PER_DAY), VGA_GRAY );
728 
729 	button_cancel_training.paint(MSG_X2-27, MSG_Y1+2, "V_X-U", "V_X-D");
730 	button_cancel_training.set_help_code( "CANCELTRA" );
731 }
732 //----------- End of function Town::disp_train_info -----------//
733 
734 
735 //------ Begin of function Town::browse_selected_race_id ------//
736 //
737 // Return the id. of the race selected in the town's race browser.
738 //
browse_selected_race_id()739 int Town::browse_selected_race_id()
740 {
741 	err_when( town_recno != town_array.selected_recno );		// the current town must be the selected one when this function is called
742 
743 	if( browse_race_town_recno != town_recno )		// the browser still hasn't displayed this town yet. This happens when the selected town is just changed, and this function is called before the town interface is refreshed
744 		return 0;
745 
746 	if( browse_race.none_record )
747 		return 0;
748 
749 	int raceCount = race_filter();
750 
751 	if( raceCount != browse_race.total_rec() ||
752 		 raceCount != recruit_race_count )
753 	{
754 		info.disp();
755 	}
756 
757 	err_when( browse_race.recno() < 1 );
758 	err_when( browse_race.recno() > raceCount );
759 	err_when( browse_race.recno() > browse_race.total_rec() );
760 
761 	return race_filter(browse_race.recno());
762 }
763 //------ End of function Town::browse_selected_race_id ------//
764 
765 
766 //--------- Begin of function race_filter ---------//
767 //
768 // Filter those races that are in the town and can be trained.
769 //
race_filter(int recNo)770 static int race_filter(int recNo)
771 {
772 	err_when( recNo && recNo < 1 );
773 	err_when( recNo && recNo > recruit_race_count );
774 	err_when( recNo && recNo > browse_race.total_rec() );
775 
776 	int 	i, recCount=0;
777 	Town* townPtr = town_array[Town::if_town_recno];
778 
779 	for( i=0 ; i<MAX_RACE ; i++ )
780 	{
781 		if( townPtr->race_pop_array[i] > 0 )
782 			recCount++;
783 
784 		if( recNo && recCount==recNo )
785 			return i+1;
786 	}
787 
788 	err_when( recNo );
789 
790 	return recCount;
791 }
792 //----------- End of function race_filter -----------//
793 
794 
795 //-------- Begin of static function put_race_rec --------//
796 //
put_race_rec(int recNo,int x,int y,int refreshFlag)797 static void put_race_rec(int recNo, int x, int y, int refreshFlag)
798 {
799 	//-------- display race icon -------//
800 
801 	int       raceId   = race_filter(recNo);
802 	RaceInfo* raceInfo = race_res[raceId];
803 
804 	if( refreshFlag == INFO_REPAINT )
805 	{
806 		vga_util.d3_panel_down(x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4 );
807 		vga_front.put_bitmap(x+3, y+3, raceInfo->icon_bitmap_ptr);
808 	}
809 
810 	//--------- set help parameters --------//
811 
812 	if( mouse.in_area(x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4) )
813 		help.set_unit_help( raceInfo->basic_unit_id, 0, x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4 );
814 
815 	//-------- display race name --------//
816 
817 	Town* townPtr = town_array[Town::if_town_recno];
818 
819 	font_mid.put( x+46, y+6, townPtr->race_pop_array[raceId-1],1, x+87 );
820 	font_mid.put( x+88, y+6, townPtr->jobless_race_pop_array[raceId-1], 1, x+129 );
821 
822 	//---- only display loyalty if this town is controlled by a nation ----//
823 
824 	int curLoyalty, targetLoyalty;
825 	int x2 = x+browse_race.rec_width-1;
826 
827 	if( townPtr->nation_recno )
828 	{
829 		curLoyalty    = (int) townPtr->race_loyalty_array[raceId-1];
830 		targetLoyalty = (int) townPtr->race_target_loyalty_array[raceId-1];
831 	}
832 	else
833 	{
834 		curLoyalty    = (int) townPtr->race_resistance_array[raceId-1][nation_array.player_recno-1];
835 		targetLoyalty = (int) townPtr->race_target_resistance_array[raceId-1][nation_array.player_recno-1];
836 
837 		if( targetLoyalty > curLoyalty )		// resistance only decrease, won't increase
838 			targetLoyalty = -1;					// don't display the decrease target
839 	}
840 
841 	//---------- display loyalty/resistance ------------//
842 
843 	int    dispArrow=0;
844 	String str;
845 
846 	if( curLoyalty == targetLoyalty || targetLoyalty == -1 )					// only display up and down arrow for independent town's resistance
847 	{
848 		str = curLoyalty;
849 	}
850 	else
851 	{
852 		str  = curLoyalty;
853 		str += " ";
854 		str += targetLoyalty;
855 
856 		dispArrow=1;
857 	}
858 
859 	x2 = font_mid.center_put( x+110, y+6, x2, y+5+font_mid.height(), str, 1 );
860 
861 	//--------- display up/down arrow -----------//
862 
863 	if( dispArrow )
864 	{
865 		x = x2-font_mid.text_width( misc.format(targetLoyalty) ) - 8;
866 
867 		if( (int) targetLoyalty > (int) curLoyalty )
868 			image_icon.put_join( x+1, y+9, "ARROWUP" );
869 
870 		else if( (int) targetLoyalty < (int) curLoyalty )
871 			image_icon.put_join( x+1, y+9, "ARROWDWN" );
872 	}
873 }
874 //----------- End of static function put_race_rec -----------//
875 
876 
877 //--------- Begin of function Town::disp_train_menu ---------//
878 //
disp_train_menu(int refreshFlag)879 void Town::disp_train_menu(int refreshFlag)
880 {
881 	// ####### begin Gilbert 13/9 ########//
882 	if( refreshFlag == INFO_UPDATE )
883 	{
884 		for( int i=1; i<=MAX_TRAINABLE_SKILL; i++)
885 		{
886 			button_skill[i-1].paint(-1,0);
887 			// button_queue_skill[i] is called by automatically
888 		}
889 	}
890 	else if( refreshFlag == INFO_REPAINT )
891 	{
892 		font_san.d3_put( INFO_X1, INFO_Y1, INFO_X2, INFO_Y1+18, _("Train (Cost: $30, Skill: 20)") );
893 		int x=INFO_X1, y=INFO_Y1+24;
894 
895 		for(int i=1; i<=MAX_TRAINABLE_SKILL; i++)
896 		{
897 			button_queue_skill[i-1].create(INFO_X1+COUNT_BUTTON_OFFSET_X,
898 			y+COUNT_BUTTON_OFFSET_Y,
899 			INFO_X1+COUNT_BUTTON_OFFSET_X+COUNT_BUTTON_WIDTH-1,
900 			y+COUNT_BUTTON_OFFSET_Y+COUNT_BUTTON_HEIGHT-1,
901 			i_disp_queue_skill_button, ButtonCustomPara(this,i));
902 
903 			button_skill[i-1].paint(INFO_X1, y,
904 			INFO_X2, y+BUTTON_ACTION_HEIGHT-1,
905 			i_disp_skill_button, ButtonCustomPara(&button_queue_skill[i-1],i) );
906 
907 			y += BUTTON_ACTION_HEIGHT;
908 		}
909 
910 		button_cancel3.paint( INFO_X1, y, INFO_X2, y+BUTTON_ACTION_HEIGHT*3/4-1,
911 		ButtonCustom::disp_text_button_func, ButtonCustomPara((void*)_("Done"),0) );
912 	}
913 	// ####### end Gilbert 13/9 ########//
914 }
915 //----------- End of function Town::disp_train_menu -----------//
916 
917 // ######### begin Gilbert 13/9 #########//
918 
919 //-------- Begin of function i_disp_skill_button --------//
920 //
i_disp_skill_button(ButtonCustom * button,int repaintBody)921 static void i_disp_skill_button(ButtonCustom *button, int repaintBody)
922 {
923 	int x1 = button->x1;
924 	int y1 = button->y1;
925 	int x2 = button->x2;
926 	int y2 = button->y2;
927 	if( !button->pushed_flag )
928 	{
929 		if( repaintBody )
930 		{
931 			vga_util.blt_buf(x1, y1, x2, y2, 0);
932 			vga_util.d3_panel2_up( x1, y1, x2, y2, 1 );
933 		}
934 		x2--;
935 		y2--;
936 	}
937 	else
938 	{
939 		if( repaintBody )
940 		{
941 			vga_util.blt_buf(x1, y1, x2, y2, 0);
942 			vga_util.d3_panel2_down( x1, y1, x2, y2, 1 );
943 		}
944 		x1++;
945 		y1++;
946 	}
947 
948 	ButtonCustom *queueButton = (ButtonCustom *)button->custom_para.ptr;
949 	if( repaintBody)
950 	{
951 		// display skill large icon
952 		short skillId = button->custom_para.value;
953 		char str[9] = "U_";
954 		strcat( str, Skill::skill_code_array[skillId-1] );
955 		char *bitmapPtr = image_button.get_ptr(str);
956 
957 		vga_front.put_bitmap_trans_decompress(x1, y1+4, bitmapPtr);
958 
959 		// put name
960 		String str2;
961 
962 		str2 = "";
963 
964 		if( skillId == queue_train_selected )
965 			str2 += ">";
966 
967 		if( skillId == SKILL_MFT )
968 			str2 += _("Manufacturing");		// the string in skill_str_array[] is "Manufacture".
969 		else
970 			str2 += _(Skill::skill_str_array[skillId-1]);
971 
972 		if( skillId == queue_train_selected )
973 			str2 += "<";
974 
975 		font_bible.put(x1+50, y1+11, str2);
976 	}
977 
978 	// display small button
979 	queueButton->paint(-1, repaintBody);
980 }
981 //--------- End of static function i_disp_skill_button ---------//
982 
983 //-------- Begin of static function i_disp_queue_skill_button --------//
984 //
i_disp_queue_skill_button(ButtonCustom * button,int repaintBody)985 static void i_disp_queue_skill_button(ButtonCustom *button, int repaintBody)
986 {
987 	Town *townPtr= (Town *)button->custom_para.ptr;
988 
989 	int x1 = button->x1;
990 	int y1 = button->y1;
991 	int x2 = button->x2;
992 	int y2 = button->y2;
993 	if( !button->pushed_flag )
994 	{
995 		if( repaintBody )
996 		{
997 			vga_util.blt_buf(x1, y1, x2, y2, 0);
998 			vga_util.d3_panel2_up( x1, y1, x2, y2, 1, 1);
999 		}
1000 		x2--;
1001 		y2--;
1002 	}
1003 	else
1004 	{
1005 		if( repaintBody )
1006 		{
1007 			vga_util.blt_buf(x1, y1, x2, y2, 0);
1008 			vga_util.d3_panel2_down( x1, y1, x2, y2, 1, 1);
1009 		}
1010 		x1++;
1011 		y1++;
1012 	}
1013 
1014 	//----- count the no. of units queued for this skill ------//
1015 
1016 	short skillId = button->custom_para.value;
1017 	int queuedCount=0;
1018 	for( int i=0 ; i<townPtr->train_queue_count ; i++ )
1019 	{
1020 		if( townPtr->train_queue_skill_array[i] == skillId )
1021 			queuedCount++;
1022 	}
1023 	if(townPtr->train_unit_recno)
1024 	{
1025 		Unit *unitPtr = unit_array[townPtr->train_unit_recno];
1026 		// ##### begin Gilbert 10/10 #######//
1027 		if(unitPtr->skill.skill_id==skillId
1028 			//### begin alex 17/3 ###//
1029 			//|| (unitPtr->spy_recno && skillId == SKILL_SPYING) )
1030 			|| (skillId == SKILL_SPYING && unitPtr->spy_recno && unitPtr->skill.skill_id == 0) ) // 0 for spying-training
1031 			 //#### end alex 17/3 ####//
1032 			queuedCount++;
1033 		// ##### end Gilbert 10/10 #######//
1034 	}
1035 
1036 	font_mid.center_put( x1+3, y1+3, x2-3, y2-3, misc.format(queuedCount), 1);
1037 }
1038 //--------- End of static function i_disp_queue_skill_button ---------//
1039 // ######### end Gilbert 13/9 #########//
1040 
1041 //--------- Begin of function Town::detect_train_menu ---------//
1042 //
detect_train_menu()1043 int Town::detect_train_menu()
1044 {
1045 	int	x=INFO_X1+2, y=INFO_Y1+24, rc, quitFlag, waitFlag;
1046 
1047 	waitFlag = 0;
1048 	for(int b=1; b<=MAX_TRAINABLE_SKILL; ++b)
1049 	{
1050 		// ###### begin Gilbert 10/9 ########//
1051 		//------ detect pressing on the small queue count button -------//
1052 		rc = 0;
1053 		if( (rc = button_queue_skill[b-1].detect(0,0,2)) != 0)
1054 		{
1055 			quitFlag = 0;		// don't quit the menu right after pressing the button
1056 		}
1057 		//------ detect pressing on the big button -------//
1058 		// but defer to the queue button if clicked over that
1059 		else if( !button_queue_skill[b-1].button_wait && ((rc= button_skill[b-1].detect(0,0,2)) != 0) )
1060 		{
1061 			quitFlag = 1;		// quit the menu right after pressing the button
1062 		}
1063 		// ###### end Gilbert 10/9 ########//
1064 
1065 		if( button_queue_skill[b-1].button_wait || button_skill[b-1].button_wait )
1066 			waitFlag = 1;
1067 
1068 		int shiftPressed = mouse.event_skey_state & SHIFT_KEY_MASK;
1069 
1070 		//------- process the action --------//
1071 		if( rc > 0 )
1072 		{
1073 			// Holding shift will use batches of FIRMWAR_BUILD_BATCH_COUNT
1074 			int trainCancelAmount = shiftPressed ? TOWN_TRAIN_BATCH_COUNT : 1;
1075 
1076 			if( rc==1 )		// left button
1077 			{
1078 				if( remote.is_enable() )
1079 				{
1080 					// packet structure : <town recno> <skill id> <race id> <amount>
1081 					short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1082 					shortPtr[0] = town_recno;
1083 					shortPtr[1] = b;
1084 					shortPtr[2] = race_filter(browse_race.recno());
1085 					shortPtr[3] = (short)trainCancelAmount;
1086 				}
1087 				else
1088 					add_queue(b, race_filter(browse_race.recno()), trainCancelAmount);
1089 				// ##### begin Gilbert 26/9 ########//
1090 				se_ctrl.immediate_sound("TURN_ON");
1091 				// ##### end Gilbert 26/9 ########//
1092 			}
1093 			else 				// right button - remove queue
1094 			{
1095 				if( remote.is_enable() )
1096 				{
1097 					// packet structure : <town recno> <skill id> <race id> <amount>
1098 					short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1099 					shortPtr[0] = town_recno;
1100 					shortPtr[1] = b;
1101 					shortPtr[2] = -1;		// -1 race_id represent remove queue
1102 					shortPtr[3] = (short)trainCancelAmount;
1103 				}
1104 				else
1105 					remove_queue(b, trainCancelAmount);
1106 				// ##### begin Gilbert 26/9 ########//
1107 				se_ctrl.immediate_sound("TURN_OFF");
1108 				// ##### end Gilbert 26/9 ########//
1109 			}
1110 
1111 			if( quitFlag )
1112 				info.disp();		// info.disp() will call put_info() which will switch mode back to the main menu mode
1113 			// ####### begin Gilbert 10/9 ######//
1114 			else
1115 				info.update();
1116 			// ####### end Gilbert 10/9 ######//
1117 
1118 			return 1;
1119 		}
1120 
1121 		y += BUTTON_ACTION_HEIGHT;
1122 	}
1123 	//------ detect the cancel button --------//
1124 
1125 	if( button_cancel3.detect() || (!waitFlag && mouse.any_click(1)) )		// press the cancel button or right click
1126 	{
1127 		// ##### begin Gilbert 26/9 ########//
1128 		se_ctrl.immediate_sound("TURN_OFF");
1129 		// ##### end Gilbert 26/9 ########//
1130       town_menu_mode = TOWN_MENU_MAIN;
1131 		info.disp();
1132 		return 1;
1133 	}
1134 
1135 	//------ detect production selecting hotkeys --------//
1136 
1137 	if( ISKEY(KEYEVENT_MANUF_QUEUE_UP) )
1138 	{
1139 		queue_train_selected--;
1140 		if( queue_train_selected <= 0 )
1141 			queue_train_selected = MAX_TRAINABLE_SKILL;
1142 		disp_train_menu(INFO_REPAINT);
1143 		return 1;
1144 	}
1145 
1146 	if( ISKEY(KEYEVENT_MANUF_QUEUE_DOWN) )
1147 	{
1148 		queue_train_selected++;
1149 		if( queue_train_selected > MAX_TRAINABLE_SKILL )
1150 			queue_train_selected = 1;
1151 		disp_train_menu(INFO_REPAINT);
1152 		return 1;
1153 	}
1154 
1155 	if( queue_train_selected && ISKEY(KEYEVENT_MANUF_QUEUE_ADD) )
1156 	{
1157 		if( remote.is_enable() )
1158 		{
1159 			// packet structure : <town recno> <skill id> <race id> <amount>
1160 			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1161 			shortPtr[0] = town_recno;
1162 			shortPtr[1] = queue_train_selected;
1163 			shortPtr[2] = race_filter(browse_race.recno());
1164 			shortPtr[3] = 1;
1165 		}
1166 		else
1167 			add_queue(queue_train_selected, race_filter(browse_race.recno()), 1);
1168 		se_ctrl.immediate_sound("TURN_ON");
1169 		return 1;
1170 	}
1171 
1172 	if( queue_train_selected && ISKEY(KEYEVENT_MANUF_QUEUE_ADD_BATCH) )
1173 	{
1174 		if( remote.is_enable() )
1175 		{
1176 			// packet structure : <town recno> <skill id> <race id> <amount>
1177 			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1178 			shortPtr[0] = town_recno;
1179 			shortPtr[1] = queue_train_selected;
1180 			shortPtr[2] = race_filter(browse_race.recno());
1181 			shortPtr[3] = TOWN_TRAIN_BATCH_COUNT;
1182 		}
1183 		else
1184 			add_queue(queue_train_selected, race_filter(browse_race.recno()), TOWN_TRAIN_BATCH_COUNT);
1185 		se_ctrl.immediate_sound("TURN_ON");
1186 		return 1;
1187 	}
1188 
1189 	if( queue_train_selected && ISKEY(KEYEVENT_MANUF_QUEUE_REMOVE) )
1190 	{
1191 		if( remote.is_enable() )
1192 		{
1193 			// packet structure : <town recno> <skill id> <race id> <amount>
1194 			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1195 			shortPtr[0] = town_recno;
1196 			shortPtr[1] = queue_train_selected;
1197 			shortPtr[2] = -1;		// -1 race_id represent remove queue
1198 			shortPtr[3] = 1;
1199 		}
1200 		else
1201 			remove_queue(queue_train_selected, 1);
1202 		se_ctrl.immediate_sound("TURN_OFF");
1203 		return 1;
1204 	}
1205 
1206 	if( queue_train_selected && ISKEY(KEYEVENT_MANUF_QUEUE_REMOVE_BATCH) )
1207 	{
1208 		if( remote.is_enable() )
1209 		{
1210 			// packet structure : <town recno> <skill id> <race id> <amount>
1211 			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 4*sizeof(short) );
1212 			shortPtr[0] = town_recno;
1213 			shortPtr[1] = queue_train_selected;
1214 			shortPtr[2] = -1;		// -1 race_id represent remove queue
1215 			shortPtr[3] = TOWN_TRAIN_BATCH_COUNT;
1216 		}
1217 		else
1218 			remove_queue(queue_train_selected, TOWN_TRAIN_BATCH_COUNT);
1219 		se_ctrl.immediate_sound("TURN_OFF");
1220 		return 1;
1221 	}
1222 
1223 	return 0;
1224 }
1225 //----------- End of function Town::detect_train_menu -----------//
1226 
1227 
1228 //--------- Begin of function Town::disp_auto_menu ---------//
1229 //
disp_auto_menu(int modeCollectTax)1230 void Town::disp_auto_menu(int modeCollectTax)
1231 {
1232 	int curAutoLoyalty;
1233 
1234 	Nation* nationPtr = nation_array[nation_recno];
1235 
1236 	if( modeCollectTax )
1237 		curAutoLoyalty =	auto_collect_tax_loyalty;
1238 	else
1239 		curAutoLoyalty =	auto_grant_loyalty;
1240 
1241 	//---------- paint buttons ------------//
1242 
1243 	String headingStr;
1244 
1245 	if( modeCollectTax )
1246 		headingStr = _("Automatically Collect Tax from Villagers when their Loyalty reaches:");
1247 	else
1248 		headingStr = _("Automatically Grant Money to Villagers when their Loyalty drops below:");
1249 
1250 	headingStr += "\n";
1251 	headingStr += _("(Left-click below to apply to this village. Right-click below to apply to all your villages.)");
1252 
1253 	vga_util.d3_panel_up( INFO_X1, INFO_Y1, INFO_X2, INFO_Y1+110 );
1254 
1255 	font_san.put_paragraph( INFO_X1+7, INFO_Y1+8, INFO_X2-7, INFO_Y2-5, headingStr );
1256 
1257 	int i, loyaltyLevel, y=INFO_Y1+114;
1258 
1259 	for( i=0, loyaltyLevel=30 ; i<BUTTON_LOYALTY_COUNT ; loyaltyLevel+=10, i++, y+=20 )
1260 		button_loyalty_array[i].paint_text( INFO_X1, y, INFO_X2, y+18, misc.format(loyaltyLevel), 0, loyaltyLevel==curAutoLoyalty );
1261 
1262 	button_loyalty_disabled.paint_text( INFO_X1, y, INFO_X2, y+18, _("Disabled"), 0, curAutoLoyalty==0 );
1263 	y+=20;
1264 
1265 	button_cancel2.paint_text( INFO_X1, y, INFO_X2, y+18, _("Cancel") );
1266 }
1267 //----------- End of function Town::disp_auto_menu -----------//
1268 
1269 
1270 //--------- Begin of function Town::detect_auto_menu ---------//
1271 //
detect_auto_menu(int modeCollectTax)1272 int Town::detect_auto_menu(int modeCollectTax)
1273 {
1274 	int i, rc=0, loyaltyLevel;
1275 
1276 	for( i=0, loyaltyLevel=30 ; i<BUTTON_LOYALTY_COUNT ; loyaltyLevel+=10, i++ )
1277 	{
1278 		rc = button_loyalty_array[i].detect(0, 0, 1);
1279 
1280 		if( rc )
1281 			break;
1282 	}
1283 
1284 	if( !rc )
1285 	{
1286 		rc = button_loyalty_disabled.detect(0, 0, 1);
1287 		loyaltyLevel = 0;
1288 	}
1289 
1290 	//------ set new settings now -------//
1291 
1292 	if( rc==1 )
1293 	{
1294 		if( modeCollectTax )
1295 		{
1296 			if( !remote.is_enable() )
1297 			{
1298 				set_auto_collect_tax_loyalty(loyaltyLevel);
1299 			}
1300 			else
1301 			{
1302 				// packet structure <town recno> <loyalty level>
1303 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_AUTO_TAX, 2*sizeof(short) );
1304 				*shortPtr = town_recno;
1305 				shortPtr[1] = loyaltyLevel;
1306 			}
1307 		}
1308 		else
1309 		{
1310 			if( !remote.is_enable() )
1311 			{
1312 				set_auto_grant_loyalty(loyaltyLevel);
1313 			}
1314 			else
1315 			{
1316 				// packet structure <town recno> <loyalty level>
1317 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_AUTO_GRANT, 2*sizeof(short) );
1318 				*shortPtr = town_recno;
1319 				shortPtr[1] = loyaltyLevel;
1320 			}
1321 		}
1322 	}
1323 	else if( rc==2 )
1324 	{
1325 		// ####### begin Gilbert 11/9 ########//
1326 		//----- set the national policy -----//
1327 		if( !remote.is_enable() )
1328 		{
1329 			Nation* nationPtr = nation_array[nation_recno];
1330 
1331 			if( modeCollectTax )
1332 				nationPtr->set_auto_collect_tax_loyalty(loyaltyLevel);
1333 			else
1334 				nationPtr->set_auto_grant_loyalty(loyaltyLevel);
1335 
1336 			//----- update individual towns -----//
1337 
1338 			Town* townPtr;
1339 
1340 			for( i=town_array.size() ; i>0 ; i-- )
1341 			{
1342 				if( town_array.is_deleted(i) )
1343 					continue;
1344 
1345 				townPtr = town_array[i];
1346 				if( townPtr->nation_recno == nation_recno )
1347 				{
1348 					if( modeCollectTax )
1349 						townPtr->set_auto_collect_tax_loyalty(loyaltyLevel);
1350 					else
1351 						townPtr->set_auto_grant_loyalty(loyaltyLevel);
1352 				}
1353 			}
1354 		}
1355 		else
1356 		{
1357 			err_when(!nation_recno);
1358 			if( modeCollectTax )
1359 			{
1360 				// packet structure <-nation recno> <loyalty level>
1361 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_AUTO_TAX, 2*sizeof(short) );
1362 				*shortPtr = -nation_recno;
1363 				shortPtr[1] = loyaltyLevel;
1364 			}
1365 			else
1366 			{
1367 				// packet structure <-nation recno> <loyalty level>
1368 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_AUTO_GRANT, 2*sizeof(short) );
1369 				*shortPtr = -nation_recno;
1370 				shortPtr[1] = loyaltyLevel;
1371 			}
1372 		}
1373 		// ####### end Gilbert 11/9 ########//
1374 	}
1375 
1376 	//--------------------------------------//
1377 
1378 	if( button_cancel2.detect() || rc )
1379 	{
1380 		// ##### begin Gilbert 26/9 ########//
1381 		se_ctrl.immediate_sound("TURN_OFF");
1382 		// ##### begin Gilbert 26/9 ########//
1383 		town_menu_mode = TOWN_MENU_MAIN;
1384 		info.disp();
1385 		return 1;
1386 	}
1387 
1388 	return 0;
1389 }
1390 //----------- End of function Town::detect_auto_menu -----------//
1391 
1392 
1393 //--------- Begin of function Town::disp_spy_menu ---------//
1394 //
disp_spy_menu(int refreshFlag)1395 void Town::disp_spy_menu(int refreshFlag)
1396 {
1397 	disp_basic_info(refreshFlag);
1398 
1399 	//---------- paint controls -----------//
1400 
1401 	if( refreshFlag == INFO_REPAINT )
1402 	{
1403 		spy_count = spy_filter();
1404 
1405 		//------ display browser field description -------//
1406 
1407 		int x=RACE_BROWSE_X1+2;
1408 		int y=RACE_BROWSE_Y1-23;
1409 
1410 		vga_util.d3_panel_up( RACE_BROWSE_X1, y, RACE_BROWSE_X2, RACE_BROWSE_Y1-3 );
1411 
1412 		font_san.put( x+4  , y+4, _("Spy Skill") );
1413 		font_san.put( x+70 , y+4, _("Loyalty") );
1414 		font_san.put( x+130, y+4, _("Action") );
1415 
1416 		//------------ create browser ------------//
1417 
1418 		browse_spy.init( RACE_BROWSE_X1, RACE_BROWSE_Y1, RACE_BROWSE_X2, RACE_BROWSE_Y2,
1419 								0, 25, spy_count, put_spy_rec );
1420 
1421 		browse_spy.open(1);
1422 	}
1423 	else
1424 	{
1425 		//---------- update controls -----------//
1426 
1427 		if( spy_count != spy_filter() )
1428 		{
1429 			spy_count = spy_filter();
1430 
1431 			if( spy_count>0 )
1432 			{
1433 				disable_refresh = 1;		// stay in the spy menu mode if disable_refresh is 1
1434 				info.disp();
1435 				disable_refresh = 0;
1436 			}
1437 			else
1438 				info.disp();				// reset to the main menu mode if disable_refresh is 0
1439 
1440 			return;
1441 		}
1442 		else
1443 			browse_spy.update();
1444 	}
1445 
1446 	//----------- create the paint button ----------//
1447 
1448 	if( refreshFlag == INFO_REPAINT )
1449 	{
1450 		int x=BUTTON_X1, y=RACE_BROWSE_Y2+5;
1451 
1452 		button_spy_mobilize.paint( x, y, 'A', "MOBILSPY" );
1453 		x+=BUTTON_ACTION_WIDTH;
1454 
1455 		//--------- reward spy button --------//
1456 
1457 		button_spy_reward.paint( x, y, 'A', "REWARD" );
1458 		x+=BUTTON_ACTION_WIDTH;
1459 
1460 		if( nation_recno != nation_array.player_recno )		// if the spy is in another nation's town
1461 		{
1462 			button_spy_action.paint( x, y, 'A', "SPYCHACT" );
1463 			x+=BUTTON_ACTION_WIDTH;
1464 		}
1465 
1466 		if( nation_recno && nation_recno != nation_array.player_recno )
1467 		{
1468 			button_spy_view_secret.paint( x, y, 'A', "VSECRET" );
1469 			x+=BUTTON_ACTION_WIDTH;
1470 
1471 			if( x+BUTTON_ACTION_WIDTH-5 > INFO_X2 )
1472 			{
1473 				x  = BUTTON_X1;
1474 				y += BUTTON_ACTION_HEIGHT;
1475 			}
1476 		}
1477 
1478 		button_cancel.paint( x, y, 'A', "PREVMENU" );
1479 	}
1480 }
1481 //----------- End of function Town::disp_spy_menu -----------//
1482 
1483 
1484 //--------- Begin of function Town::detect_spy_menu ---------//
1485 //
detect_spy_menu()1486 int Town::detect_spy_menu()
1487 {
1488 	browse_spy.detect();
1489 
1490 	Spy* spyPtr = spy_array[ spy_filter( browse_spy.recno() ) ];
1491 
1492 	//------- mobilize spy --------//
1493 
1494 	if( button_spy_mobilize.detect() )
1495 	{
1496 		if( !remote.is_enable() )
1497 		{
1498 			if( spyPtr->mobilize_town_spy() )
1499 			{
1500 				spyPtr->notify_cloaked_nation_flag = 0;		// reset it so the player can control it
1501 				disp_spy_menu( INFO_UPDATE );
1502 			}
1503 		}
1504 		else
1505 		{
1506 			// packet structure <spy recno>
1507 			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_SPY_LEAVE_TOWN, sizeof(short) );
1508 			*shortPtr = spyPtr->spy_recno;
1509 		}
1510 		return 1;
1511 	}
1512 
1513 	//------ reward spy ---------//
1514 
1515 	else if( button_spy_reward.detect() )
1516 	{
1517 		spyPtr->reward(COMMAND_PLAYER);
1518 		return 1;
1519 	}
1520 
1521 	//----- change spy action --------//
1522 
1523 	if( nation_recno != nation_array.player_recno )		// if the spy is in another nation's town
1524 	{
1525 		if( button_spy_action.detect() )		// set action mode
1526 		{
1527 			if( !remote.is_enable() )
1528 			{
1529 				spyPtr->set_next_action_mode();
1530 				disp_spy_menu( INFO_UPDATE );
1531 			}
1532 			else
1533 			{
1534 				// packet structure <spy recno>
1535 				short *shortPtr = (short *)remote.new_send_queue_msg(MSG_SPY_CYCLE_ACTION, sizeof(short) );
1536 				*shortPtr = spyPtr->spy_recno;
1537 			}
1538 			return 1;
1539 		}
1540 	}
1541 
1542 	//----- view secret report ------/
1543 
1544 	if( nation_recno && nation_recno != nation_array.player_recno )
1545 	{
1546 		if( button_spy_view_secret.detect() )
1547 		{
1548 			action_spy_recno = spyPtr->spy_recno;
1549 			town_menu_mode   = TOWN_MENU_VIEW_SECRET;
1550 			disable_refresh = 1;
1551 			info.disp();
1552 			disable_refresh = 0;
1553 			return 1;
1554 		}
1555 	}
1556 
1557 	//--------- cancel -----------//
1558 
1559 	if( button_cancel.detect() || mouse.any_click(1) )		// right click to cancel
1560 	{
1561 		info.disp();
1562 		return 1;
1563 	}
1564 
1565 	return 0;
1566 }
1567 //----------- End of function Town::detect_spy_menu -----------//
1568 
1569 
1570 //--------- Begin of function Town::has_player_spy ---------//
1571 //
1572 // Whether this town has any player spies.
1573 //
has_player_spy()1574 int Town::has_player_spy()
1575 {
1576 	int i;
1577 	for( i=0 ; i<MAX_RACE ; i++ )
1578 	{
1579 		if( race_spy_count_array[i] > 0 )
1580 			break;
1581 	}
1582 
1583 	if( i==MAX_RACE )		// no spies in this nation
1584 		return 0;
1585 
1586 	//----- look for player spy in the spy_array -----//
1587 
1588 	Spy* spyPtr;
1589 
1590 	for( i=spy_array.size() ; i>=1 ; i-- )
1591 	{
1592 		if( spy_array.is_deleted(i) )
1593 			continue;
1594 
1595 		spyPtr = spy_array[i];
1596 
1597 		if( spyPtr->spy_place==SPY_TOWN &&
1598 			 spyPtr->spy_place_para==town_recno &&
1599 			 spyPtr->true_nation_recno==nation_array.player_recno )
1600 		{
1601 			return 1;
1602 		}
1603 	}
1604 
1605 	return 0;
1606 }
1607 //----------- End of function Town::has_player_spy -----------//
1608 
1609 
1610 //--------- Begin of function spy_filter ---------//
1611 //
spy_filter(int recNo)1612 static int spy_filter(int recNo)
1613 {
1614 	Spy* spyPtr;
1615 	int  recCount=0;
1616 
1617 	for( int i=spy_array.size() ; i>=1 ; i-- )
1618 	{
1619 		if( spy_array.is_deleted(i) )
1620 			continue;
1621 
1622 		spyPtr = spy_array[i];
1623 
1624 		if( spyPtr->spy_place==SPY_TOWN &&
1625 			 spyPtr->spy_place_para==Town::if_town_recno &&
1626 			 spyPtr->true_nation_recno==nation_array.player_recno )
1627 		{
1628 			recCount++;
1629 		}
1630 
1631 		if( recNo && recCount==recNo )
1632 			return i;
1633 	}
1634 
1635 	err_when( recNo );
1636 
1637 	return recCount;
1638 }
1639 //----------- End of function spy_filter -----------//
1640 
1641 
1642 //-------- Begin of static function put_spy_rec --------//
1643 //
put_spy_rec(int recNo,int x,int y,int refreshFlag)1644 static void put_spy_rec(int recNo, int x, int y, int refreshFlag)
1645 {
1646 	int x2 = x+browse_spy.rec_width-1;
1647 
1648 	//-------- display icon of the spy unit -----//
1649 
1650 	Spy* spyPtr = spy_array[ spy_filter(recNo) ];
1651 
1652 	if( refreshFlag == INFO_REPAINT )
1653 	{
1654 		vga_util.d3_panel_down(x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4 );
1655 		vga_front.put_bitmap(x+3, y+3, race_res[spyPtr->race_id]->icon_bitmap_ptr);
1656 	}
1657 
1658 	//--------- set help parameters --------//
1659 
1660 	if( mouse.in_area(x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4) )
1661 	{
1662 		int unitId = race_res[spyPtr->race_id]->basic_unit_id;
1663 
1664 		help.set_unit_help( unitId, 0, x+1, y+1, x+RACE_ICON_WIDTH+4, y+RACE_ICON_HEIGHT+4 );
1665 	}
1666 
1667 	//-------- display spy skill -------//
1668 
1669 	font_san.put( x+40, y+6, spyPtr->spy_skill, 1, x+66 );
1670 
1671 	//-------- display spy loyalty -------//
1672 
1673 	font_san.put( x+67, y+6, spyPtr->spy_loyalty, 1, x+94 );
1674 
1675 	//------ display the action mode of the spy ------//
1676 
1677 	vga_util.blt_buf( x+95, y+6, x2, y+5+font_san.height(), 0 );
1678 
1679 	font_san.center_put( x+95, y+6, x2, y+5+font_san.height(), spyPtr->action_str() );
1680 }
1681 //----------- End of static function put_spy_rec -----------//
1682 
1683 
1684 //--------- Begin of function Town::recruit ---------//
1685 //
1686 // <int> trainSkillId =  -1 - non-trained unit
1687 //                      >=1 - skill id. of the unit to be trained.
1688 //
1689 // [int] raceId       = the race id. of the unit to be recruited
1690 //                      (default: the currently selected race)
1691 //
1692 // return: <int> recno of the recruited unit
1693 //
recruit(int trainSkillId,int raceId,char remoteAction)1694 int Town::recruit(int trainSkillId, int raceId, char remoteAction)
1695 {
1696 	//---- we can't train a new one when there is one currently under training ---//
1697 
1698 	if( trainSkillId >= 1 && train_unit_recno )
1699 		return 0;
1700 
1701 	//--------------------------------------------//
1702 
1703 	if( !raceId )
1704 	{
1705 		if( browse_race.recno() > race_filter() )
1706 			return 0;
1707 
1708 		raceId = race_filter(browse_race.recno());
1709 	}
1710 
1711 	if( !remoteAction && remote.is_enable() )
1712 	{
1713 		// packet structure : <town recno> <skill id> <race id>
1714 		short *shortPtr = (short *)remote.new_send_queue_msg(MSG_TOWN_RECRUIT, 3*sizeof(short));
1715 		shortPtr[0] = town_recno;
1716 		shortPtr[1] = trainSkillId;
1717 		shortPtr[2] = raceId;
1718 		return 0;
1719 	}
1720 
1721 	//---- check if there are units of the race ready for training ----//
1722 
1723 	int recruitableCount = recruitable_race_pop(raceId, 1);
1724 
1725 	if( recruitableCount == 0 )
1726 		return 0;
1727 
1728 	err_when( recruitableCount < 0 );		// 1-allow recruiting spies
1729 
1730 	//-------- create an unit ------//
1731 
1732 	int townRecno = town_recno;
1733 	int nationRecno = nation_recno;        // save this town's info that is needed as promote_pop() will delete Town if all population of the town are promoted
1734 
1735 	//--- if there are spies in this town, chances are that they will be mobilized ---//
1736 
1737 	int shouldTrainSpy = race_spy_count_array[raceId-1] >= misc.random(recruitableCount)+1;		// 1-allow recruiting spies
1738 
1739 	//---- if we are trying to train an enemy to our spy, then... -----//
1740 
1741 	if( shouldTrainSpy && trainSkillId == SKILL_SPYING )
1742 	{
1743 		//-- if there are other non-spy units in the town, then train the other and don't train the spy --//
1744 
1745 		if( recruitableCount > race_spy_count_array[raceId-1] )
1746 		{
1747 			shouldTrainSpy = 0;
1748 		}
1749 		//--- if all remaining units are spies, when you try to train one, all of them will become mobilized ---//
1750 
1751 		else
1752 		{
1753 			int spyRecno = spy_array.find_town_spy(town_recno, raceId, 1);
1754 
1755 			err_when( !spyRecno );
1756 
1757 			Spy* spyPtr = spy_array[spyRecno];
1758 
1759 			if( !spyPtr->mobilize_town_spy() )
1760 				return 0;
1761 
1762 			spyPtr->change_cloaked_nation( spyPtr->true_nation_recno );
1763 
1764 			return 0;
1765 		}
1766 	}
1767 
1768 	//------- if we should train a spy --------//
1769 
1770 	int unitRecno=0;
1771 
1772 	if( shouldTrainSpy )
1773 	{
1774 		int  spyCount = spy_array.size();
1775 		int  spyRecno = misc.random(spyCount)+1;
1776 		Spy* spyPtr;
1777 
1778 		//-----------------------------------------------------//
1779 		// Spies from other nations will first be mobilized,
1780 		// when all peasants and spies are mobilized and
1781 		// the only ones left in the town are spies from our
1782 		// nation, then mobilize them finally.
1783 		//-----------------------------------------------------//
1784 
1785 		for( int mobileNationType=1 ; unitRecno==0 && mobileNationType<=2 ; mobileNationType++ )
1786 		{
1787 			if( mobileNationType==2 )	// only mobilize our own spies are there are the only ones in the town
1788 			{
1789 				if( recruitable_race_pop(raceId,1) > race_spy_count_array[raceId-1] )		// 1-allow recruiting spies
1790 					break;
1791 			}
1792 
1793 			for( int i=0 ; i<spyCount ; i++ )
1794 			{
1795 				if( ++spyRecno > spyCount )
1796 					spyRecno = 1;
1797 
1798 				if( spy_array.is_deleted(spyRecno) )
1799 					continue;
1800 
1801 				spyPtr = spy_array[spyRecno];
1802 
1803 				if( spyPtr->spy_place == SPY_TOWN
1804 					 && spyPtr->spy_place_para == town_recno
1805 					 // ##### patch begin Gilbert 9/4 ######//
1806 					 && spyPtr->race_id == raceId
1807 					 // ##### patch end Gilbert 9/4 ######//
1808 					 )
1809 				{
1810 					if( mobileNationType==1 )		// only mobilize spies from other nations, don't mobilize spies of our own nation
1811 					{
1812 						if( spyPtr->true_nation_recno == nation_recno )
1813 								continue;
1814 					}
1815 
1816 					unitRecno = spyPtr->mobilize_town_spy(trainSkillId== -1);	// the parameter is whether decreasing the population immediately, if decrease immediately in recruit mode, not in training mode, 1-mobilize spies
1817 					break;
1818 				}
1819 			}
1820 		}
1821 	}
1822 
1823 	//-------- mobilize normal peasant units -------//
1824 
1825 	if( !unitRecno )
1826 		unitRecno = mobilize_town_people(raceId, trainSkillId== -1, 0 ); 	// the 2nd parameter is whether decreasing the population immediately, if decrease immediately in recruit mode, not in training mode, 2nd para 0-don't mobilize spies
1827 
1828 	if( !unitRecno )
1829 		return 0;
1830 
1831 	err_when(unitRecno<=0 || unit_array.is_deleted(unitRecno));
1832 
1833 	if( !unitRecno )
1834 		return 0;
1835 
1836 	Unit* unitPtr = unit_array[unitRecno];
1837 
1838 	//-------- training skill -----------//
1839 
1840 	if( trainSkillId > 0 )
1841 	{
1842 		if( trainSkillId == SKILL_SPYING )
1843 		{
1844 			unitPtr->spy_recno = spy_array.add_spy(unitRecno, TRAIN_SKILL_LEVEL);
1845 		}
1846 		else
1847 		{
1848 			if( trainSkillId == SKILL_LEADING )		// also increase the combat level for leadership skill training
1849 				unitPtr->set_combat_level(TRAIN_SKILL_LEVEL);
1850 
1851 			unitPtr->skill.skill_id    = trainSkillId;
1852 			unitPtr->skill.skill_level = TRAIN_SKILL_LEVEL;
1853 		}
1854 
1855 		nation_array[nationRecno]->add_expense( EXPENSE_TRAIN_UNIT, (float) TRAIN_SKILL_COST );
1856 	}
1857 	else
1858 	{
1859 		//------ recruitment without training decreases loyalty --------//
1860 
1861 		recruit_dec_loyalty(raceId);
1862 
1863 		if( unitPtr->is_own() )
1864 		{
1865 			se_res.far_sound(unitPtr->cur_x_loc(), unitPtr->cur_y_loc(), 1,
1866 				'S', unitPtr->sprite_id, "RDY" );
1867 		}
1868 	}
1869 
1870 	//---- training solider or skilled unit takes time ----//
1871 
1872 	if( trainSkillId >= 0 )
1873 	{
1874 		err_when(unitRecno<=0 || unit_array.is_deleted(unitRecno));
1875 
1876 		err_when( train_unit_recno );		// if there is already a unit under training
1877 
1878 		train_unit_recno = unitRecno;
1879 		start_train_frame_no = sys.frame_count;	// as an offset for displaying the progress bar correctly
1880 
1881 		unitPtr->deinit_sprite();
1882 		unitPtr->unit_mode = UNIT_MODE_UNDER_TRAINING;
1883       unitPtr->unit_mode_para = town_recno;
1884 	}
1885 
1886 	//--- mobilize_pop() will delete the current Town if population goes down to 0 ---//
1887 
1888 	if( town_recno == town_array.selected_recno )
1889 	{
1890 		if( town_array.is_deleted(townRecno) )
1891 			info.disp();
1892 	}
1893 
1894 	return unitRecno;
1895 }
1896 //----------- End of function Town::recruit -----------//
1897 
1898 
1899 //--------- Begin of function Town::recruit_dec_loyalty ---------//
1900 //
1901 // Decrease loyalty when an unit is recruited.
1902 // This function is called by recruit() and Firm::pull_town_people()
1903 //
1904 // <int> raceId - the race to be recruited
1905 // <int> decNow - decrease now, if it is 0, just return the
1906 //						loyalty to be decreased without actual decreasing.
1907 //						(default: 1)
1908 //
1909 // return: <int> - the loyalty decreased or to be decreased.
1910 //
recruit_dec_loyalty(int raceId,int decNow)1911 int Town::recruit_dec_loyalty(int raceId, int decNow)
1912 {
1913 	float loyaltyDec = MIN( 5, (float) MAX_TOWN_POPULATION / race_pop_array[raceId-1] );
1914 
1915 	//------ recruitment without training decreases loyalty --------//
1916 
1917 	if( decNow )
1918 	{
1919 		loyaltyDec += accumulated_recruit_penalty/5;
1920 
1921 		loyaltyDec = MIN(loyaltyDec, 10);
1922 
1923 		accumulated_recruit_penalty += 5;
1924 
1925 		//-------------------------------------//
1926 
1927 		race_loyalty_array[raceId-1] -= loyaltyDec;
1928 
1929 		if( race_loyalty_array[raceId-1] < 0 )
1930 			race_loyalty_array[raceId-1] = (float) 0;
1931 	}
1932 
1933 	return (int) loyaltyDec;
1934 }
1935 //----------- End of function Town::recruit_dec_loyalty -----------//
1936 
1937 
1938 //--------- Begin of function Town::process_train ---------//
process_train()1939 void Town::process_train()
1940 {
1941 	err_when( !train_unit_recno );
1942 
1943 	Unit* unitPtr = unit_array[train_unit_recno];
1944 	int   raceId  = unitPtr->race_id;
1945 
1946 	//---- if the unit being trained was killed -----//
1947 
1948 	int cancelFlag = 0;
1949 
1950 	err_when( jobless_race_pop_array[raceId-1] < 0 );
1951 
1952 	if( jobless_race_pop_array[raceId-1]==0 )		// the unit being trained was killed
1953 	{
1954 		cancelFlag = 1;
1955 	}
1956 
1957 	//-----------------------------------------------------------------//
1958 	//
1959 	// If after start training the unit (non-spy unit), a unit has been
1960 	// mobilized, resulting that the spy count >= jobless_race,
1961 	// we must cancel the training, otherwise when training finishes,
1962 	// and dec_pop is called, spy count will > jobless count and cause error.
1963 	//
1964 	//-----------------------------------------------------------------//
1965 
1966 	err_when( race_spy_count_array[raceId-1] > jobless_race_pop_array[raceId-1] );
1967 
1968 	if( race_spy_count_array[raceId-1] == jobless_race_pop_array[raceId-1] )
1969 		cancelFlag = 1;
1970 
1971 	if( cancelFlag )
1972 	{
1973 		unit_array.disappear_in_town(train_unit_recno, town_recno);
1974 		train_unit_recno = 0;
1975 		return;
1976 	}
1977 
1978 	//------------- process training ---------------//
1979 
1980 	int totalDays;
1981 
1982 	if( config.fast_build && nation_recno==nation_array.player_recno )
1983 		totalDays = TOTAL_TRAIN_DAYS/2;
1984 	else
1985 		totalDays = TOTAL_TRAIN_DAYS;
1986 
1987 	if( (int)(sys.frame_count-start_train_frame_no) / FRAMES_PER_DAY >= totalDays )
1988 	{
1989 		finish_train(unitPtr);
1990 	}
1991 }
1992 //----------- End of function Town::process_train -----------//
1993 
1994 
1995 //--------- Begin of function Town::finish_train ---------//
1996 
finish_train(Unit * unitPtr)1997 void Town::finish_train(Unit* unitPtr)
1998 {
1999 	err_when(train_unit_recno<=0 || unit_array.is_deleted(train_unit_recno));
2000 	SpriteInfo*	spriteInfo = unitPtr->sprite_info;
2001 	int 			xLoc=loc_x1; // xLoc & yLoc are used for returning results
2002 	int 			yLoc=loc_y1;
2003 
2004 	if( !world.locate_space(&xLoc, &yLoc, loc_x2, loc_y2, spriteInfo->loc_width, spriteInfo->loc_height) )
2005 		return;
2006 
2007 	unitPtr->init_sprite(xLoc, yLoc);
2008 
2009 	if( unitPtr->is_own() )
2010 		se_res.far_sound( xLoc, yLoc, 1, 'S', unitPtr->sprite_id, "RDY");
2011 
2012 	unitPtr->unit_mode = 0;		// reset it to 0 from UNIT_MODE_UNDER_TRAINING
2013 	train_unit_recno   = 0;
2014 
2015 	int townRecno = town_recno;		// save the recno as it can be deleted in dec_pop()
2016 
2017 	dec_pop(unitPtr->race_id, 0);		// decrease the population now as the recruit() does do so
2018 
2019 	//---- if this trained unit is tied to an AI action ----//
2020 
2021 	if( train_unit_action_id )
2022 	{
2023 		nation_array[nation_recno]->process_action_id(train_unit_action_id);
2024 		train_unit_action_id = 0;
2025 	}
2026 
2027 	//----- refresh if this town is currently selected ------//
2028 
2029 	if(townRecno==town_array.selected_recno)
2030 	{
2031 		if(town_menu_mode==TOWN_MENU_MAIN)
2032 		{
2033 			info.disp();
2034 		}
2035 		else
2036 		{
2037 			disable_refresh = 1;
2038 			info.disp();
2039 			disable_refresh = 0;
2040 		}
2041 	}
2042 }
2043 //----------- End of function Town::finish_train -----------//
2044 
2045 
2046 //--------- Begin of function Town::process_queue ---------//
process_queue()2047 void Town::process_queue()
2048 {
2049 	if(train_queue_count==0)
2050 		return;
2051 
2052 	if(jobless_population==0)
2053 		return;
2054 
2055 	err_when(train_queue_count > MAX_TRAIN_QUEUE);
2056 
2057 	char queueCount = train_queue_count;
2058 	char skillId, raceId;
2059 	char i;
2060 	for(i=0; i<queueCount; ++i)
2061 	{
2062 		if(can_train(train_queue_race_array[i]))
2063 		{
2064 			skillId = train_queue_skill_array[i];
2065 			raceId = train_queue_race_array[i];
2066 			err_when(train_queue_count-i-1 < 0 || train_queue_count-i-1 > MAX_TRAIN_QUEUE);
2067 			memmove(train_queue_skill_array, train_queue_skill_array+i+1,
2068 						sizeof(train_queue_skill_array[0])*(train_queue_count-i-1));
2069 			memmove(train_queue_race_array, train_queue_race_array+i+1,
2070 						sizeof(train_queue_race_array[0])*(train_queue_count-i-1));
2071 			train_queue_count -= i+1;
2072 			recruit(skillId, raceId, COMMAND_AUTO);
2073 			break;
2074 		}
2075 	}
2076 
2077 	if(i==queueCount)
2078 		train_queue_count = 0;
2079 
2080 	if(town_menu_mode==TOWN_MENU_MAIN)
2081 		info.disp();
2082 }
2083 //----------- End of function Town::process_queue -----------//
2084 
2085 
2086 //--------- Begin of function Town::add_queue ---------//
add_queue(char skillId,char raceId,int amount)2087 void Town::add_queue(char skillId, char raceId, int amount)
2088 {
2089 	err_when(amount < 0);
2090 	if (amount < 0) return;
2091 
2092 	int queueSpace = MAX_TRAIN_QUEUE - train_queue_count - (train_unit_recno>0);
2093 	int enqueueAmount = MIN(queueSpace, amount);
2094 
2095 	for (int i = 0; i < enqueueAmount; ++i)
2096 	{
2097 		train_queue_skill_array[train_queue_count] = skillId;
2098 		train_queue_race_array[train_queue_count++] = raceId;
2099 	}
2100 
2101 	if( !train_unit_recno )
2102 		process_queue();
2103 }
2104 //----------- End of function Town::add_queue -----------//
2105 
2106 
2107 //--------- Begin of function Town::remove_queue ---------//
remove_queue(char skillId,int amount)2108 void Town::remove_queue(char skillId, int amount)
2109 {
2110 	err_when(amount < 1);
2111 	if (amount < 1) return;
2112 
2113 	for(int i=train_queue_count-1; i>=0; i--)
2114 	{
2115 		if(train_queue_skill_array[i] == skillId)
2116 		{
2117 			err_when(train_queue_count > MAX_TRAIN_QUEUE);
2118 
2119 			misc.del_array_rec(train_queue_skill_array, train_queue_count, sizeof(train_queue_skill_array[0]), i+1);
2120 			misc.del_array_rec(train_queue_race_array, train_queue_count, sizeof(train_queue_race_array[0]), i+1);
2121 			train_queue_count--;
2122 			amount--;
2123 
2124 			if (amount <= 0) return;
2125 		}
2126 	}
2127 
2128 	// If there were less trained of skillId in the queue than were requested to be removed then
2129 	// also cancel currently trained unit
2130 	if(train_unit_recno)
2131 	{
2132 		Unit *unitPtr = unit_array[train_unit_recno];
2133 		if((unitPtr->skill).skill_id==skillId)
2134 			cancel_train_unit();
2135 	}
2136 }
2137 //----------- End of function Town::remove_queue -----------//
2138 
2139 
2140 //--------- Begin of function Town::disp_debug_resistance ---------//
2141 //
disp_debug_resistance(int refreshFlag)2142 void Town::disp_debug_resistance(int refreshFlag)
2143 {
2144 	if( nation_recno == nation_array.player_recno )		// not for player's own town
2145 		return;
2146 
2147 	if( refreshFlag==INFO_REPAINT )
2148 		vga_util.d3_panel_up( INFO_X1, INFO_Y2-50, INFO_X2, INFO_Y2 );
2149 
2150 	//------ display resistance (only for independent town) -----//
2151 
2152 	int x=INFO_X1+10, y=INFO_Y2-47;
2153 
2154 	if( nation_recno ==0 )
2155 	{
2156 		int raceId = race_filter(browse_race.recno());
2157 
2158 		for( int i=1 ; i<=nation_array.size() ; i++, x+=28 )
2159 		{
2160 			if( nation_array.is_deleted(i) )
2161 				continue;
2162 
2163 			if( refreshFlag==INFO_REPAINT )
2164 				vga_front.bar( x, y, x+18, y+16, nation_array[i]->nation_color );
2165 
2166 			font_san.put( x, y+18, (int) race_resistance_array[raceId-1][i-1], 1, x+19 );
2167 			font_san.put( x, y+32, (int) race_target_resistance_array[raceId-1][i-1], 1, x+19 );
2168 		}
2169 	}
2170 	else
2171 	{
2172 		//------ if this town is the nation's base town -----//
2173 
2174 		String str;
2175 
2176 		str = "Base: ";
2177 		str += is_base_town;
2178 
2179 		font_san.put( INFO_X1+10, y, str );
2180 
2181 		str = "Town recno: ";
2182 		str += town_recno;
2183 
2184 		font_san.put( INFO_X1+70, y, str );
2185 
2186 		str = "no_neighbor_space: ";
2187 		str += no_neighbor_space;
2188 
2189 		font_san.put( INFO_X1+10, y+16, str );
2190 
2191 		str = "quality of life: ";
2192 		str += quality_of_life;
2193 
2194 		font_san.disp( INFO_X1+10, y+32, str, INFO_X2-10 );
2195 	}
2196 }
2197 //----------- End of function Town::disp_debug_resistance -----------//
2198 
2199 
2200 //--------- Begin of function Town::get_elected_race ---------//
get_selected_race()2201 int Town::get_selected_race()
2202 {
2203 	if(browse_race.recno() > race_filter())
2204 		return 0;
2205 
2206 	return race_filter(browse_race.recno());
2207 }
2208 //----------- End of function Town::get_elected_race -----------//
2209