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   : OAI_ATTK.CPP
22 //Description: AI - attacking
23 
24 #include <stdlib.h>
25 #include <ALL.h>
26 #include <OUNIT.h>
27 #include <OCONFIG.h>
28 #include <OFIRMALL.h>
29 #include <OTALKRES.h>
30 #include <ONATION.h>
31 
32 
33 //------ Declare static functions --------//
34 
35 static int get_target_nation_recno(int targetXLoc, int targetYLoc);
36 static int sort_attack_camp_function( const void *a, const void *b );
37 
38 
39 //--------- Begin of function Nation::ai_attack_target --------//
40 //
41 // Think about attacking a specific target.
42 //
43 // <int> targetXLoc, targetYLoc - location of the target
44 // <int> targetCombatLevel      - the combat level of the target, will
45 //                                only attack the target if the attacker's
46 //                                force is larger than it.
47 // [int] defenseMode            - whether the attack is basically for
48 //                                defending against an attack
49 //                                (default: 0)
50 // [int] justMoveToFlag         - whether just all move there and wait for
51 //                                the units to attack the enemies automatically
52 //                                (default: 0)
53 // [int] attackerMinCombatLevel - the minimum combat level of the attacker,
54 //											 do not send troops whose combat level
55 //											 is lower than this.
56 //                                (default: 0)
57 // [int] leadAttackCampRecno	  - if this is given, this camp will be
58 //											 included in the attacker list by passing
59 //											 checking on it.
60 //                                (default: 0)
61 // [int] useAllCamp 				  - use all camps to attack even if defenseMode is 0
62 //											 (default: defenseMode, which is default to 0)
63 //
64 // return: <int> 0  - no attack action
65 //					  >0 - the total combat level of attacking force.
66 //
ai_attack_target(int targetXLoc,int targetYLoc,int targetCombatLevel,int defenseMode,int justMoveToFlag,int attackerMinCombatLevel,int leadAttackCampRecno,int useAllCamp)67 int Nation::ai_attack_target(int targetXLoc, int targetYLoc, int targetCombatLevel, int defenseMode, int justMoveToFlag, int attackerMinCombatLevel, int leadAttackCampRecno, int useAllCamp)
68 {
69 /*					// this will be called when the AI tries to capture the town and attack the town's defense.
70 #ifdef DEBUG	//----- check for attacking own objects error ------//
71 	{
72 		int targetNationRecno = get_target_nation_recno(targetXLoc, targetYLoc);
73 
74 		if( targetNationRecno )
75 		{
76 			err_when( get_relation(targetNationRecno)->status >= NATION_FRIENDLY );
77 		}
78 	}
79 #endif
80 */
81 	//--- order nearby mobile units who are on their way to home camps to join this attack mission. ---//
82 
83 	if( defenseMode )
84 		useAllCamp = 1;
85 
86 	if( defenseMode )		// only for defense mode, for attack mission, we should plan and organize it better
87 	{
88 		int originalTargetCombatLevel = targetCombatLevel;
89 
90 		targetCombatLevel = ai_attack_order_nearby_mobile(targetXLoc, targetYLoc, targetCombatLevel);
91 
92 		if( targetCombatLevel < 0 )		// the mobile force alone can finish all the enemies
93 			return originalTargetCombatLevel - targetCombatLevel;
94 	}
95 
96 	//--- try to send troop with maxTargetCombatLevel, and don't send troop if available combat level < minTargetCombatLevel ---//
97 
98 	int maxTargetCombatLevel = targetCombatLevel * (150+pref_force_projection/2) / 100;		// 150% to 200%
99 	int minTargetCombatLevel;
100 
101 	if( defenseMode )
102 		minTargetCombatLevel = targetCombatLevel * (100-pref_military_courage/2) / 100;		// 50% to 100%
103 	else
104 		minTargetCombatLevel = targetCombatLevel * (125+pref_force_projection/4) / 100;		// 125% to 150%
105 
106 	//--- if the AI is already on an attack mission ---//
107 
108 	if( attack_camp_count )
109 		return 0;
110 
111 	//---- first locate for camps that do not need to protect any towns ---//
112 
113 	#define MAX_SUITABLE_TOWN_CAMP      10    // no. of camps in a town
114 
115 	FirmCamp* firmCamp;
116 	int   i, j;
117 	int   targetRegionId = world.get_loc(targetXLoc, targetYLoc)->region_id;
118 
119 	err_when( targetXLoc < 0 || targetXLoc >= MAX_WORLD_X_LOC );
120 	err_when( targetYLoc < 0 || targetYLoc >= MAX_WORLD_Y_LOC );
121 
122 	ai_attack_target_x_loc = targetXLoc;
123 	ai_attack_target_y_loc = targetYLoc;
124 	ai_attack_target_nation_recno = get_target_nation_recno(targetXLoc, targetYLoc);
125 
126 	attack_camp_count=0;
127 
128 	AttackCamp townCampArray[MAX_SUITABLE_TOWN_CAMP];
129 	short townCampCount;
130 
131 	//------- if there is a pre-selected camp -------//
132 
133 	lead_attack_camp_recno = leadAttackCampRecno;
134 
135 	if( leadAttackCampRecno )
136 	{
137 		err_when( firm_array[leadAttackCampRecno]->nation_recno != nation_recno );
138 		err_when( firm_array[leadAttackCampRecno]->firm_id != FIRM_CAMP );
139 
140 		attack_camp_array[attack_camp_count].firm_recno   = leadAttackCampRecno;
141 		attack_camp_array[attack_camp_count].combat_level = ((FirmCamp*)firm_array[leadAttackCampRecno])->total_combat_level();
142 
143 		err_when( attack_camp_array[attack_camp_count].combat_level < 0 );
144 
145 		attack_camp_count++;
146 	}
147 
148 	//---- if the military courage is low or the king is injured, don't send the king out to battles ---//
149 
150 	Nation* ownNation = nation_array[nation_recno];
151 	int	  kingFirmRecno=0;
152 
153 	if( king_unit_recno )
154 	{
155 		Unit* kingUnit = unit_array[king_unit_recno];
156 
157 		if( kingUnit->unit_mode == UNIT_MODE_OVERSEE )
158 		{
159 			Firm* kingFirm = firm_array[kingUnit->unit_mode_para];
160 			int   rc = 0;
161 
162 			if( ai_camp_count > 3 + (100-ownNation->pref_military_courage)/20 ) 		// don't use the king if we have other generals, the king won't be used if we have 3 to 8 camps. The higher the military courage is, the smaller will be the number of camps
163 				rc = 1;
164 
165 			//--- if the military courage is low or the king is injured ---//
166 
167 			else if( kingUnit->hit_points < 230-ownNation->pref_military_courage )		// 130 to 230, if over 200, the king will not fight
168 				rc = 1;
169 
170 			//--- if the King does have a full troop ----//
171 
172 			else if( kingFirm->worker_count < MAX_WORKER )
173 				rc = 1;
174 
175 			//-------------------------------------------//
176 
177 			if( rc )
178 			{
179 				kingFirmRecno = kingUnit->unit_mode_para;
180 
181 				//--- if the king is very close to the target, ask him to attack also ---//
182 
183 				if( kingFirmRecno &&
184 					 kingUnit->hit_points >= 150-ownNation->pref_military_courage/4 )		// if the king is not heavily injured
185 				{
186 					firmCamp = (FirmCamp*) firm_array[kingFirmRecno];
187 
188 					if( firmCamp->worker_count == MAX_WORKER )		// the king shouldn't go out alone
189 					{
190 						if( misc.points_distance(firmCamp->center_x, firmCamp->center_y,
191 							 targetXLoc, targetYLoc) <= EFFECTIVE_FIRM_TOWN_DISTANCE )
192 						{
193 							kingFirmRecno = 0;
194 						}
195 					}
196 				}
197 			}
198 		}
199 	}
200 
201 	//--------- locate for camps that are not linked to towns ---------//
202 
203 	int rc;
204 
205 	for( i=0 ; i<ai_camp_count ; i++ )
206 	{
207 		firmCamp = (FirmCamp*) firm_array[ ai_camp_array[i] ];
208 
209 		err_when( firmCamp->firm_id != FIRM_CAMP );
210 
211 		if( firmCamp->region_id != targetRegionId )
212 			continue;
213 
214 		if( !firmCamp->overseer_recno || !firmCamp->worker_count )
215 			continue;
216 
217 		if( firmCamp->patrol_unit_count > 0 )     // if there are units patrolling out
218 			continue;
219 
220 		if( firmCamp->ai_capture_town_recno )     // the base is trying to capture an independent town
221 			continue;
222 
223 		if( firmCamp->is_attack_camp )
224 			continue;
225 
226 		if( firmCamp->firm_recno == kingFirmRecno )
227 			continue;
228 
229 		if( leadAttackCampRecno && firmCamp->firm_recno == leadAttackCampRecno )
230 			continue; // don't double count
231 
232 		//---- don't order this camp if the overseer is injured ----//
233 
234 		Unit* overseerUnit = unit_array[firmCamp->overseer_recno];
235 
236 		if( overseerUnit->hit_points < overseerUnit->max_hit_points &&
237 			 overseerUnit->hit_points < 100-ownNation->pref_military_courage/2 )		// 50 to 100
238 		{
239 			continue;
240 		}
241 
242 		//----------------------------------------------------------//
243 
244 		if( attackerMinCombatLevel )
245 		{
246 			if( firmCamp->average_combat_level() < attackerMinCombatLevel )
247 				continue;
248 		}
249 
250 		//-------------------------------------//
251 		//
252 		// Add this camp if:
253 		// 1. we are in defense mode, and have to get all the forces available to defend against the attack.
254 		// 2. this camp isn't linked to any of our towns.
255 		//
256 		//-------------------------------------//
257 
258 		if( useAllCamp )
259 			rc = 1;
260 		else
261 		{
262 			rc = firmCamp->linked_town_count==0;		// don't use this camp as it may be in the process of capturing an indepdendent town or an enemy town
263 /*
264 			for( int j=firmCamp->linked_town_count-1 ; j>=0 ; j-- )
265 			{
266 				if( town_array[firmCamp->linked_town_array[j]]->nation_recno == nation_recno )
267 					break;
268 			}
269 
270 			rc = j<0;		// j<0 means not linked to any of our towns.
271 */
272 		}
273 
274 		if( rc )
275 		{
276 			//--- if this camp into the list of suitable attacker firm ---//
277 
278 			if( attack_camp_count < MAX_SUITABLE_ATTACK_CAMP )
279 			{
280 				err_when( firmCamp->nation_recno != nation_recno );
281 
282 				attack_camp_array[attack_camp_count].firm_recno   = firmCamp->firm_recno;
283 				attack_camp_array[attack_camp_count].combat_level = firmCamp->total_combat_level();
284 
285 				err_when( attack_camp_array[attack_camp_count].combat_level < 0 );
286 
287 				attack_camp_count++;
288 			}
289 		}
290 	}
291 
292 	//---- locate for camps that are extra for protecting towns (there are basic ones doing the protection job only) ----//
293 
294 	int   totalCombatLevel, protectionNeeded;
295 	Town* townPtr;
296 	Firm* firmPtr;
297 
298 	if( !useAllCamp )         // in defense mode, every camp has been already counted
299 	{
300 		for( i=0 ; i<ai_town_count ; i++ )
301 		{
302 			townPtr = town_array[ ai_town_array[i] ];
303 
304 			if( townPtr->region_id != targetRegionId )
305 				continue;
306 
307 			err_when( townPtr->nation_recno != nation_recno );
308 
309 			//----- calculate the protection needed for this town ----//
310 
311 			protectionNeeded = townPtr->protection_needed();
312 
313 			townCampCount =0;
314 			totalCombatLevel=0;
315 
316 			for( j=townPtr->linked_firm_count-1 ; j>=0 ; j-- )
317 			{
318 				firmPtr = firm_array[ townPtr->linked_firm_array[j] ];
319 
320 				if( firmPtr->nation_recno != nation_recno )
321 					continue;
322 
323 				if( firmPtr->firm_recno == kingFirmRecno )
324 					continue;
325 
326 				//----- if this is a camp, add combat level points -----//
327 
328 				if( firmPtr->firm_id == FIRM_CAMP )
329 				{
330 					if( !firmPtr->overseer_recno && !firmPtr->worker_count )
331 						continue;
332 
333 					firmCamp = (FirmCamp*) firmPtr;
334 
335 					if( firmCamp->patrol_unit_count > 0 )     // if there are units patrolling out
336 						continue;
337 
338 					if( firmCamp->ai_capture_town_recno )     // the base is trying to capture an independent town
339 						continue;
340 
341 					if( firmCamp->is_attack_camp )
342 						continue;
343 
344 					if( leadAttackCampRecno && firmCamp->firm_recno == leadAttackCampRecno )
345 						continue; // don't double count
346 
347 					totalCombatLevel += firmCamp->total_combat_level();
348 
349 					if( townCampCount < MAX_SUITABLE_TOWN_CAMP )
350 					{
351 						err_when( firmCamp->nation_recno != nation_recno );
352 
353 						townCampArray[townCampCount].firm_recno   = firmCamp->firm_recno;
354 						townCampArray[townCampCount].combat_level = firmCamp->total_combat_level();
355 
356 						err_when( townCampArray[townCampCount].combat_level < 0 );
357 
358 						townCampCount++;
359 					}
360 				}
361 
362 				//--- if this is a civilian firm, add needed protection points ---//
363 
364 				else
365 				{
366 					if( firmPtr->firm_id == FIRM_MARKET )
367 						protectionNeeded += ((FirmMarket*)firmPtr)->stock_value_index();
368 					else
369 						protectionNeeded += (int) firmPtr->productivity;
370 				}
371 			}
372 
373 			//--- see if the current combat level is larger than the protection needed ---//
374 
375 			if( totalCombatLevel > protectionNeeded )
376 			{
377 				//--- see if the protection is still enough if we put one of the camps into the upcoming battle ---//
378 
379 				for( int j=0 ; j<townCampCount ; j++ )
380 				{
381 					if( totalCombatLevel - townCampArray[j].combat_level > protectionNeeded )
382 					{
383 						//--- if so, add this camp to the suitable camp list ---//
384 
385 						if( attack_camp_count < MAX_SUITABLE_ATTACK_CAMP )
386 						{
387 							//--- this camp can be linked to a town previously processed already (in this case, two towns linked to the same camp) ---//
388 
389 							int k;
390 							for( k=0 ; k<attack_camp_count ; k++ )
391 							{
392 								if( attack_camp_array[k].firm_recno == townCampArray[j].firm_recno )
393 									break;
394 							}
395 
396 							//---- if this camp hasn't been added yet ----//
397 
398 							if( k==attack_camp_count )
399 							{
400 								err_when( firm_array[townCampArray[j].firm_recno]->nation_recno != nation_recno );
401 
402 								attack_camp_array[attack_camp_count] = townCampArray[j];
403 								attack_camp_count++;
404 
405 								totalCombatLevel -= townCampArray[j].combat_level;    // reduce it from the total combat level as its combat level has just been used, and is no longer available
406 							}
407 						}
408 					}
409 				}
410 			}
411 		}
412 	}
413 
414 	//---- now we get all suitable camps in the list, it's time to attack ---//
415 
416 	//----- think about which ones in the list should be used -----//
417 
418 	//--- first calculate the total combat level of these camps ---//
419 
420 	totalCombatLevel = 0;
421 
422 	for( i=0 ; i<attack_camp_count ; i++ )
423 		totalCombatLevel += attack_camp_array[i].combat_level;
424 
425 	//--- see if we are not strong enough to attack yet -----//
426 
427 	if( totalCombatLevel < minTargetCombatLevel )		// if we are not strong enough to attack yet
428 	{
429 		attack_camp_count=0;
430 		return 0;
431 	}
432 
433 	//----- build an array of the distance data first -----//
434 
435 	for( i=0 ; i<attack_camp_count ; i++ )
436 	{
437 		firmPtr = firm_array[ attack_camp_array[i].firm_recno ];
438 
439 		err_when( firmPtr->nation_recno != nation_recno );
440 
441 		attack_camp_array[i].distance = misc.points_distance( firmPtr->center_x, firmPtr->center_y,
442 												targetXLoc, targetYLoc );
443 
444 		err_when( attack_camp_array[i].distance < 0 );
445 	}
446 
447 	//---- now sort the camps based on their distances & combat levels ----//
448 
449 	qsort( &attack_camp_array, attack_camp_count, sizeof(attack_camp_array[0]), sort_attack_camp_function );
450 
451 	//----- now take out the lowest rating ones -----//
452 
453 	for( i=attack_camp_count-1 ; i>=0 ; i-- )
454 	{
455 		if( totalCombatLevel - attack_camp_array[i].combat_level > maxTargetCombatLevel )
456 		{
457 			totalCombatLevel -= attack_camp_array[i].combat_level;
458 			attack_camp_count--;
459 		}
460 	}
461 
462 	err_when( attack_camp_count < 0 );
463 
464 	//------- synchronize the attack date for different camps ----//
465 
466 	ai_attack_target_sync();
467 
468 	ai_attack_target_execute(!justMoveToFlag);
469 
470 	return totalCombatLevel;
471 }
472 //---------- End of function Nation::ai_attack_target --------//
473 
474 
475 //--------- Begin of function Nation::ai_attack_order_nearby_mobile --------//
476 //
477 // Order nearby mobile units who are on their way to home camps to
478 // join this attack mission.
479 //
480 // <int> targetXLoc, targetYLoc - location of the target
481 // <int> targetCombatLevel      - the combat level of the target, will
482 //                                only attack the target if the attacker's
483 //                                force is larger than it.
484 //
485 // return: <int> the remaining target combat level of the target
486 //					  after ordering the mobile units to deal with some of them.
487 //
ai_attack_order_nearby_mobile(int targetXLoc,int targetYLoc,int targetCombatLevel)488 int Nation::ai_attack_order_nearby_mobile(int targetXLoc, int targetYLoc, int targetCombatLevel)
489 {
490 	int		 scanRange = 15+pref_military_development/20;		// 15 to 20
491 	int		 xOffset, yOffset;
492 	int		 xLoc, yLoc;
493 	int		 targetRegionId = world.get_region_id(targetXLoc, targetYLoc);
494 	Location* locPtr;
495 
496 	for( int i=2 ; i<scanRange*scanRange ; i++ )
497 	{
498 		misc.cal_move_around_a_point(i, scanRange, scanRange, xOffset, yOffset);
499 
500 		xLoc = targetXLoc + xOffset;
501 		yLoc = targetYLoc + yOffset;
502 
503 		xLoc = MAX(0, xLoc);
504 		xLoc = MIN(MAX_WORLD_X_LOC-1, xLoc);
505 
506 		yLoc = MAX(0, yLoc);
507 		yLoc = MIN(MAX_WORLD_Y_LOC-1, yLoc);
508 
509 		locPtr = world.get_loc(xLoc, yLoc);
510 
511 		if( locPtr->region_id != targetRegionId )
512 			continue;
513 
514 		if( !locPtr->has_unit(UNIT_LAND) )
515 			continue;
516 
517 		//----- if there is a unit on the location ------//
518 
519 		int unitRecno = locPtr->unit_recno(UNIT_LAND);
520 
521 		if( unit_array.is_deleted(unitRecno) )		// the unit is dying
522 			continue;
523 
524 		Unit* unitPtr = unit_array[unitRecno];
525 
526 		//--- if if this is our own military unit ----//
527 
528 		if( unitPtr->nation_recno != nation_recno ||
529 			 unitPtr->skill.skill_id != SKILL_LEADING )
530 		{
531 			continue;
532 		}
533 
534 		//--------- if this unit is injured ----------//
535 
536 		if( unitPtr->hit_points <
537 			 unitPtr->max_hit_points * (150-pref_military_courage/2) / 200 )
538 		{
539 			continue;
540 		}
541 
542 		//---- only if this is not assigned to an action ---//
543 
544 		if( unitPtr->ai_action_id )
545 			continue;
546 
547 		//---- if this unit is stop or assigning to a firm ----//
548 
549 		if( unitPtr->action_mode2 == ACTION_STOP ||
550 			 unitPtr->action_mode2 == ACTION_ASSIGN_TO_FIRM )
551 		{
552 			//-------- set should_attack on the target to 1 --------//
553 
554 			enable_should_attack_on_target(targetXLoc, targetYLoc);
555 
556 			//---------- attack now -----------//
557 
558 			unitPtr->attack_unit(targetXLoc, targetYLoc);
559 
560 			targetCombatLevel -= (int) unitPtr->hit_points;		// reduce the target combat level
561 
562 			if( targetCombatLevel <= 0 )
563 				break;
564 		}
565 	}
566 
567 	return targetCombatLevel;
568 }
569 //--------- End of function Nation::ai_attack_order_nearby_mobile --------//
570 //
571 
572 //--------- Begin of function Nation::ai_attack_target_sync --------//
573 //
574 // Synchronize the timing of attacking a target. Camps that are further
575 // away from the target will move first while camps that are closer
576 // to the target will move later.
577 //
ai_attack_target_sync()578 void Nation::ai_attack_target_sync()
579 {
580 	//---- find the distance of the camp that is farest to the target ----//
581 
582 	int maxDistance=0;
583 
584 	int i;
585 	for( i=0 ; i<attack_camp_count ; i++ )
586 	{
587 		err_when( attack_camp_array[i].distance < 0 );
588 
589 		if( attack_camp_array[i].distance > maxDistance )
590 			maxDistance = attack_camp_array[i].distance;
591 	}
592 
593 	int maxTravelDays = sprite_res[ unit_res[UNIT_NORMAN]->sprite_id ]->travel_days(maxDistance);
594 
595 	//------ set the date which the troop should start moving -----//
596 
597 	int travelDays;
598 
599 	for( i=0 ; i<attack_camp_count ; i++ )
600 	{
601 		travelDays = maxTravelDays * attack_camp_array[i].distance / maxDistance;
602 
603 		attack_camp_array[i].patrol_date = info.game_date + (maxTravelDays-travelDays);
604 	}
605 
606 	//----- set the is_attack_camp flag of the camps ------//
607 
608 	for( i=0 ; i<attack_camp_count ; i++ )
609 	{
610 		Firm* firmPtr = firm_array[ attack_camp_array[i].firm_recno ];
611 
612 		err_when( firmPtr->firm_id != FIRM_CAMP );
613 		err_when( firmPtr->nation_recno != nation_recno );
614 
615 		((FirmCamp*)firmPtr)->is_attack_camp = 1;
616 	}
617 }
618 //---------- End of function Nation::ai_attack_target_sync --------//
619 
620 
621 //--------- Begin of function Nation::ai_attack_target_execute --------//
622 //
623 // Synchronize the timing of attacking a target. Camps that are further
624 // away from the target will move first while camps that are closer
625 // to the target will move later.
626 //
627 // <int> directAttack - whether directly attack the target or
628 //								just move close to the target.
629 //
ai_attack_target_execute(int directAttack)630 void Nation::ai_attack_target_execute(int directAttack)
631 {
632 	FirmCamp* firmCamp;
633 	int		 firmRecno;
634 
635 	err_when( ai_attack_target_x_loc < 0 || ai_attack_target_x_loc >= MAX_WORLD_X_LOC );
636 	err_when( ai_attack_target_y_loc < 0 || ai_attack_target_y_loc >= MAX_WORLD_Y_LOC );
637 
638 	//---- if the target no longer exist -----//
639 
640 	if( ai_attack_target_nation_recno != get_target_nation_recno(ai_attack_target_x_loc, ai_attack_target_y_loc) )
641 	{
642 		reset_ai_attack_target();
643 	}
644 
645 	//----------------------------------------//
646 
647 	for( int i=attack_camp_count-1 ; i>=0 ; i-- )
648 	{
649 		//----- if it's still not the date to move to attack ----//
650 
651 		if( info.game_date < attack_camp_array[i].patrol_date )
652 			continue;
653 
654 		//-------------------------------------------------------//
655 
656 		firmRecno = attack_camp_array[i].firm_recno;
657 
658 		firmCamp = (FirmCamp*) firm_array[firmRecno];
659 
660 		if( firmCamp && (firmCamp->overseer_recno || firmCamp->worker_count) )
661 		{
662 			//--- if this is the lead attack camp, don't mobilize the overseer ---//
663 
664 			if( lead_attack_camp_recno == firmRecno )
665 				firmCamp->patrol_all_soldier(); 	// don't mobilize the overseer
666 			else
667 				firmCamp->patrol();    // mobilize the overseer and the soldiers
668 
669 			//----------------------------------------//
670 
671 			if( firmCamp->patrol_unit_count > 0 )     // there could be chances that there are no some for mobilizing the units
672 			{
673 				//------- declare war with the target nation -------//
674 
675 				if( ai_attack_target_nation_recno )
676 					talk_res.ai_send_talk_msg(ai_attack_target_nation_recno, nation_recno, TALK_DECLARE_WAR);
677 
678 				//--- in defense mode, just move close to the target, the unit will start attacking themselves as their relationship is hostile already ---//
679 
680 				if( !directAttack )
681 				{
682 					unit_array.move_to(ai_attack_target_x_loc, ai_attack_target_y_loc, 0, firmCamp->patrol_unit_array,
683 											 firmCamp->patrol_unit_count, COMMAND_AI);
684 				}
685 				else
686 				{
687 					//-------- set should_attack on the target to 1 --------//
688 
689 					enable_should_attack_on_target(ai_attack_target_x_loc, ai_attack_target_y_loc);
690 
691 					//---------- attack now -----------//
692 
693 					// ##### patch begin Gilbert 5/8 ######//
694 					unit_array.attack(ai_attack_target_x_loc, ai_attack_target_y_loc, 0, firmCamp->patrol_unit_array,
695 											firmCamp->patrol_unit_count, COMMAND_AI, 0);
696 					// ##### patch end Gilbert 5/8 ######//
697 				}
698 			}
699 		}
700 
701 		//--------- reset FirmCamp::is_attack_camp ---------//
702 
703 		if( firmCamp )
704 			firmCamp->is_attack_camp = 0;
705 
706 		//------- remove this from attack_camp_array -------//
707 
708 		misc.del_array_rec(attack_camp_array, attack_camp_count, sizeof(AttackCamp), i+1 );
709 		attack_camp_count--;
710 	}
711 }
712 //---------- End of function Nation::ai_attack_target_execute --------//
713 
714 
715 //--------- Begin of function Nation::reset_ai_attack_target --------//
716 //
reset_ai_attack_target()717 void Nation::reset_ai_attack_target()
718 {
719 	//------ reset all is_attack_camp -------//
720 
721 	for( int i=0 ; i<attack_camp_count ; i++ )
722 	{
723 		Firm* firmPtr = firm_array[ attack_camp_array[i].firm_recno ];
724 
725 		err_when( firmPtr->firm_id != FIRM_CAMP ||
726 					 firmPtr->nation_recno != nation_recno );
727 
728 		((FirmCamp*)firmPtr)->is_attack_camp = 0;
729 	}
730 
731 	//--------------------------------------//
732 
733 	attack_camp_count = 0;
734 }
735 //---------- End of function Nation::reset_ai_attack_target --------//
736 
737 
738 //--------- Begin of function Nation::enable_should_attack_on_target --------//
739 //
enable_should_attack_on_target(int targetXLoc,int targetYLoc)740 void Nation::enable_should_attack_on_target(int targetXLoc, int targetYLoc)
741 {
742 	//------ set should attack to 1 --------//
743 
744 	int targetNationRecno = 0;
745 
746 	Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
747 
748 	if( locPtr->has_unit(UNIT_LAND) )
749 		targetNationRecno = unit_array[ locPtr->unit_recno(UNIT_LAND) ]->nation_recno;
750 
751 	else if( locPtr->is_firm() )
752 		targetNationRecno = firm_array[locPtr->firm_recno()]->nation_recno;
753 
754 	else if( locPtr->is_town() )
755 		targetNationRecno = town_array[locPtr->town_recno()]->nation_recno;
756 
757 	if( targetNationRecno )
758 	{
759 		set_relation_should_attack(targetNationRecno, 1, COMMAND_AI);
760 	}
761 }
762 //--------- End of function Nation::enable_should_attack_on_target --------//
763 
764 
765 //--------- Begin of static function get_target_nation_recno --------//
766 //
767 // Return the nation recno of the target.
768 //
get_target_nation_recno(int targetXLoc,int targetYLoc)769 static int get_target_nation_recno(int targetXLoc, int targetYLoc)
770 {
771    Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
772 
773    if( locPtr->is_firm() )
774    {
775 		return firm_array[locPtr->firm_recno()]->nation_recno;
776    }
777    else if( locPtr->is_town() )
778    {
779 		return town_array[locPtr->town_recno()]->nation_recno;
780 	}
781    else if( locPtr->has_unit(UNIT_LAND) )
782    {
783       return unit_array[locPtr->unit_recno(UNIT_LAND)]->nation_recno;
784    }
785 
786    return 0;
787 }
788 //---------- End of static function get_target_nation_recno --------//
789 
790 
791 //------ Begin of function sort_attack_camp_function ------//
792 //
sort_attack_camp_function(const void * a,const void * b)793 static int sort_attack_camp_function( const void *a, const void *b )
794 {
795 	int ratingA = ((AttackCamp*)a)->combat_level - ((AttackCamp*)a)->distance;
796 	int ratingB = ((AttackCamp*)b)->combat_level - ((AttackCamp*)b)->distance;
797 	int rc = ratingB - ratingA;
798 	if( rc )
799 		return rc;
800 	return ((AttackCamp*)b)->firm_recno - ((AttackCamp*)a)->firm_recno;
801 }
802 //------- End of function sort_attack_camp_function ------//
803 
804 
805 //--------- Begin of function Nation::think_secret_attack --------//
806 //
807 // Think about secret assault plans.
808 //
think_secret_attack()809 int Nation::think_secret_attack()
810 {
811 	//--- never secret attack if its peacefulness >= 80 ---//
812 
813 	if( pref_peacefulness >= 80 )
814 		return 0;
815 
816 	//--- don't try to get new enemies if we already have many ---//
817 
818 	int totalEnemyMilitary = total_enemy_military();
819 
820 	if( totalEnemyMilitary > 20+pref_military_courage-pref_peacefulness )
821 		return 0;
822 
823 	//---------------------------------------------//
824 
825 	int bestRating = 0;
826 	int bestNationRecno = 0;
827 	int     ourMilitary = military_rank_rating();
828 	Nation* nationPtr;
829 
830 	for( int i=1 ; i<=nation_array.size() ; i++ )
831 	{
832 		int tradeRating;
833 		int curRating;
834 		NationRelation *nationRelation;
835 
836 		if( nation_array.is_deleted(i) || nation_recno == i )
837 			continue;
838 
839 		nationPtr = nation_array[i];
840 
841 		nationRelation = get_relation(i);
842 
843 		tradeRating = trade_rating(i)/2 +        // existing trade
844 			      ai_trade_with_rating(i)/2; // possible trade
845 
846 		//---- if the secret attack flag is not enabled yet ----//
847 
848 		if( !nationRelation->ai_secret_attack )
849 		{
850 			int relationStatus = nationRelation->status;
851 
852 			//---- if we have a friendly treaty with this nation ----//
853 
854 			if( relationStatus == NATION_FRIENDLY )
855 			{
856 				if( totalEnemyMilitary > 0 )     // do not attack if we still have enemies
857 					continue;
858 			}
859 
860 			//-------- never attacks an ally ---------//
861 
862 			else if( relationStatus == NATION_ALLIANCE )
863 			{
864 				continue;
865 			}
866 
867 			//---- don't attack if we have a big trade volume with the nation ---//
868 
869 			if( tradeRating > (50-pref_trading_tendency/2) )	// 0 to 50, 0 if trade tendency is 100, it is 0
870 			{
871 				continue;
872 			}
873 		}
874 
875 		//--------- calculate the rating ----------//
876 
877 		curRating = (ourMilitary - nationPtr->military_rank_rating()) * 2
878 						 + (overall_rank_rating() - 50) 		// if <50 negative, if >50 positive
879 						 - tradeRating*2
880 						 - get_relation(i)->ai_relation_level/2
881 						 - pref_peacefulness/2;
882 
883 		//------- if aggressiveness config is medium or high ----//
884 
885 		if( !nationPtr->is_ai() )		// more aggressive towards human players
886 		{
887 			switch( config.ai_aggressiveness )
888 			{
889 				case OPTION_MODERATE:
890 					curRating += 100;
891 					break;
892 
893 				case OPTION_HIGH:
894 					curRating += 300;
895 					break;
896 
897 				case OPTION_VERY_HIGH:
898 					curRating += 500;
899 					break;
900 			}
901 		}
902 
903 		//----- if the secret attack is already on -----//
904 
905 		if( nationRelation->ai_secret_attack )
906 		{
907 			//--- cancel secret attack if the situation has changed ---//
908 
909 			if( curRating < 0 )
910 			{
911 				nationRelation->ai_secret_attack = 0;
912 				continue;
913 			}
914 		}
915 
916 		//--------- compare ratings -----------//
917 
918 		if( curRating > bestRating )
919       {
920          bestRating = curRating;
921          bestNationRecno = i;
922       }
923    }
924 
925    //-------------------------------//
926 
927    if( bestNationRecno )
928    {
929 		get_relation(bestNationRecno)->ai_secret_attack = 1;
930 		return 1;
931    }
932 
933    return 0;
934 }
935 //---------- End of function Nation::think_secret_attack --------//
936 
937