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_SPY.CPP
22 //Description: AI - Spy activities
23 
24 #include <ALL.h>
25 #include <OFIRM.h>
26 #include <OTOWN.h>
27 #include <OUNIT.h>
28 #include <ONATION.h>
29 #include <OSPY.h>
30 
31 //--------- Begin of function Nation::think_spy --------//
32 
think_spy()33 void Nation::think_spy()
34 {
35 
36 
37 }
38 //---------- End of function Nation::think_spy --------//
39 
40 
41 //--------- Begin of function Nation::ai_assign_spy_to_town --------//
42 //
43 // Think about sending spies to the specific town.
44 //
45 // <int> townRecno - recno of the town
46 // [int] raceId 	 - race id. of the spy
47 //							(default: majority_race() of the tonw)
48 //
49 // return: <int> 1 - a spy is assigned successfully.
50 // 				  0 - failure.
51 //
ai_assign_spy_to_town(int townRecno,int raceId)52 int Nation::ai_assign_spy_to_town(int townRecno, int raceId)
53 {
54 	Town* townPtr = town_array[townRecno];
55 
56 	if( townPtr->population >= MAX_TOWN_POPULATION )
57 		return 0;
58 
59 	if( !raceId )
60 		raceId = townPtr->majority_race();
61 
62 	int mobileOnly = townPtr->nation_recno == nation_recno;   // if assign to own towns/firms, only get mobile spies, don't get spies from existing towns/firms as that will result in a loop effect
63 
64 	return ai_assign_spy( townPtr->loc_x1, townPtr->loc_y1, raceId, mobileOnly );
65 }
66 //---------- End of function Nation::ai_assign_spy_to_town --------//
67 
68 
69 //--------- Begin of function Nation::ai_assign_spy_to_firm --------//
70 //
71 // Think about sending spies to the specific firm.
72 //
73 // return: <int> 1 - a spy is assigned successfully.
74 // 				  0 - failure.
75 //
ai_assign_spy_to_firm(int firmRecno)76 int Nation::ai_assign_spy_to_firm(int firmRecno)
77 {
78 	Firm* firmPtr = firm_array[firmRecno];
79 
80 	err_when( !firmPtr->worker_array );
81 
82 	//---- check if the firm is full or not -----//
83 
84 	if( firmPtr->nation_recno == nation_recno )	// if it's our own firm
85 	{
86 		if( firmPtr->is_worker_full() )	// use is_worker_full() for own firms as it take into account of units patrolling outside
87 			return 0;
88 	}
89 	else
90 	{
91 		if( firmPtr->worker_count == MAX_WORKER )
92 			return 0;
93 	}
94 
95 	//------ look for an existing spy -------//
96 
97 	int raceId	   = firmPtr->majority_race();
98 	int mobileOnly = firmPtr->nation_recno == nation_recno;   // if assign to own firms/firms, only get mobile spies, don't get spies from existing firms/firms as that will result in a loop effect
99 
100 	return ai_assign_spy( firmPtr->loc_x1, firmPtr->loc_y1, raceId, mobileOnly );
101 }
102 //---------- End of function Nation::ai_assign_spy_to_firm --------//
103 
104 
105 //--------- Begin of function Nation::ai_assign_spy --------//
106 //
107 // Try to locate an existing spy for use.
108 //
109 // <int> targetXLoc, targetYLoc - the target location
110 // [int] spyRaceId			  - if specified, only spies of this race
111 //											 will be located. (default:0)
112 // [int] mobileOnly 				  - get mobile spies only. (default:0)
113 //
ai_assign_spy(int targetXLoc,int targetYLoc,int spyRaceId,int mobileOnly)114 int Nation::ai_assign_spy(int targetXLoc, int targetYLoc, int spyRaceId, int mobileOnly)
115 {
116 	int unitRecno=0;
117 
118 	//---- try to find an existing spy ----//
119 
120 	Spy* spyPtr = ai_find_spy( targetXLoc, targetYLoc, spyRaceId, mobileOnly );
121 
122 	if( spyPtr )
123 		unitRecno = spyPtr->mobilize_spy();
124 
125 	//--- if not successful, then try to hire one ---//
126 
127 	if( !unitRecno )
128 		unitRecno = hire_unit(SKILL_SPYING, spyRaceId, targetXLoc, targetYLoc);
129 
130 	//--- if cannot hire one, try to train one ----//
131 
132 	int trainTownRecno=0;
133 
134 	if( !unitRecno )
135 		unitRecno = train_unit(SKILL_SPYING, spyRaceId, targetXLoc, targetYLoc, trainTownRecno);
136 
137 	if( !unitRecno )
138 		return 0;
139 
140 	//------ get the spy object of the unit ------//
141 
142 	Unit* unitPtr = unit_array[unitRecno];
143 
144 	err_when( !unitPtr->spy_recno );
145 
146 	spyPtr = spy_array[unitPtr->spy_recno];
147 
148 	//------- get the nation of the assign destination -----//
149 
150 	Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
151 	int 		 cloakedNationRecno;
152 
153 	if( locPtr->is_firm() )
154 	{
155 		Firm* firmPtr = firm_array[locPtr->firm_recno()];
156 
157 		err_when( firmPtr->nation_recno==0 );		// cannot assign to a monster firm
158 
159 		cloakedNationRecno = firmPtr->nation_recno;
160 	}
161 	else if( locPtr->is_town() )
162 	{
163 		Town* townPtr = town_array[locPtr->town_recno()];
164 
165 		cloakedNationRecno = townPtr->nation_recno;
166 	}
167 	else
168 	{
169 		return 0;		// the original firm or town has been destroyed or sold
170 	}
171 
172 	//------- Add the assign spy action --------//
173 
174 	int actionRecno = add_action( targetXLoc, targetYLoc,
175 							-1, -1, ACTION_AI_ASSIGN_SPY, cloakedNationRecno, 1, unitRecno );
176 
177 	if( !actionRecno )
178 		return 0;
179 
180 	//------ if the unit is under training ------//
181 
182 	if( trainTownRecno )
183 		town_array[trainTownRecno]->train_unit_action_id = get_action(actionRecno)->action_id;
184 
185 	return 1;
186 }
187 //---------- End of function Nation::ai_assign_spy --------//
188 
189 
190 //--------- Begin of function Nation::ai_find_spy --------//
191 //
192 // Try to locate an existing spy for use.
193 //
194 // <int> targetXLoc, targetYLoc - the target location
195 // [int] spyRaceId			  - if specified, only spies of this race
196 //											 will be located. (default:0)
197 // [int] mobileOnly 				  - get mobile spies only. (default:0)
198 //
ai_find_spy(int targetXLoc,int targetYLoc,int spyRaceId,int mobileOnly)199 Spy* Nation::ai_find_spy(int targetXLoc, int targetYLoc, int spyRaceId, int mobileOnly)
200 {
201 	//--- first check if we have an existing spy ready for the mission ---//
202 
203 	Spy  *spyPtr, *bestSpy=NULL;
204 	int  curRating, bestRating=0;
205 	int  spyXLoc, spyYLoc;
206 	int  targetRegionId = world.get_region_id(targetXLoc, targetYLoc);
207 
208 	for(int i=spy_array.size(); i>0; i--)
209 	{
210 		if(spy_array.is_deleted(i))
211 			continue;
212 
213 		spyPtr = spy_array[i];
214 
215 		if( spyPtr->true_nation_recno != nation_recno )
216 			continue;
217 
218 		if( spyRaceId && spyRaceId != race_id )
219 			continue;
220 
221 		if( spyPtr->spy_place == SPY_MOBILE )
222 		{
223 			Unit* unitPtr = unit_array[spyPtr->spy_place_para];
224 
225 			// Don't 'find' units that are dying, under training by a town, or under AI orders
226 			if( unitPtr->is_unit_dead() || !unitPtr->is_visible() || !unitPtr->is_ai_all_stop() )
227 				continue;
228 
229 			spyXLoc = unitPtr->next_x_loc();
230 			spyYLoc = unitPtr->next_y_loc();
231 		}
232 		else
233 		{
234 			if( mobileOnly )
235 				continue;
236 
237 			if( spyPtr->spy_place == SPY_FIRM )
238 			{
239 				Firm* firmPtr = firm_array[spyPtr->spy_place_para];
240 
241 				if( firmPtr->nation_recno != nation_recno )		// only get spies from our own firms
242 					continue;
243 
244 				spyXLoc = firmPtr->center_x;
245 				spyYLoc = firmPtr->center_y;
246 			}
247 			else if( spyPtr->spy_place == SPY_TOWN )
248 			{
249 				Town* townPtr = town_array[spyPtr->spy_place_para];
250 
251 				if( townPtr->nation_recno != nation_recno )		// only get spies from our own towns
252 					continue;
253 
254 				spyXLoc = townPtr->center_x;
255 				spyYLoc = townPtr->center_y;
256 			}
257 			else
258 				continue;		// in ships or undefined
259 		}
260 
261 		//--- check if the region ids are matched ---//
262 
263 		if( world.get_region_id(spyXLoc, spyYLoc) != targetRegionId )
264 			continue;
265 
266 		//----------------------------------------//
267 
268 		curRating  = world.distance_rating(targetXLoc, targetYLoc, spyXLoc, spyYLoc);
269 		curRating += spyPtr->spy_skill + spyPtr->spy_loyalty/2;
270 
271 		if( curRating > bestRating )
272 		{
273 			bestRating = curRating;
274 			bestSpy    = spyPtr;
275 		}
276 	}
277 
278 	return bestSpy;
279 }
280 //---------- End of function Nation::ai_find_spy --------//
281 
282 
283 //----- Begin of function Nation::ai_assign_spy -----//
284 //
285 // action_x_loc, action_y_loc - location of the target firm or town
286 // ref_x_loc,    ref_y_loc    - not used
287 // unit_recno 					   - unit recno of the spy
288 // action_para						- the cloak nation recno the spy should set to.
289 //
ai_assign_spy(ActionNode * actionNode)290 int Nation::ai_assign_spy(ActionNode* actionNode)
291 {
292 	if(!seek_path.total_node_avail)
293 		return 0;
294 
295 	if( unit_array.is_deleted(actionNode->unit_recno) )
296 		return -1;
297 
298 	Unit* spyUnit = unit_array[actionNode->unit_recno];
299 
300 	if( !spyUnit->is_visible() )		// it's still under training, not available yet
301 		return -1;
302 
303 	if( !spyUnit->spy_recno || spyUnit->true_nation_recno() != nation_recno )
304 		return -1;
305 
306 	//------ change the cloak of the spy ------//
307 
308 	int  newFlag;
309 	Spy* spyPtr = spy_array[spyUnit->spy_recno];
310 
311 	if( reputation < 0 )		// if the nation's reputation is negative, use sneak mode to avoid chance of being uncovered and further damage the reputation
312 		newFlag = misc.random( 2+(-(int)reputation)/5 )==0;	// 2 to 22
313 	else
314 		newFlag = misc.random(2)==0;		// 50% chance of being 1
315 
316 	spyPtr->notify_cloaked_nation_flag = newFlag;
317 
318 	if( !spyUnit->can_spy_change_nation() )		// if the spy can't change nation recno now
319 	{
320 		int destXLoc = spyUnit->next_x_loc() + misc.random(20) - 10;
321 		int destYLoc = spyUnit->next_y_loc() + misc.random(20) - 10;
322 
323 		destXLoc = MAX(0, destXLoc);
324 		destXLoc = MIN(MAX_WORLD_X_LOC-1, destXLoc);
325 
326 		destYLoc = MAX(0, destYLoc);
327 		destYLoc = MIN(MAX_WORLD_Y_LOC-1, destXLoc);
328 
329 		spyUnit->move_to( destXLoc, destYLoc );
330 
331 		actionNode->retry_count++;			// never give up
332 		return 0;								// return now and try again later
333 	}
334 
335 	spyUnit->spy_change_nation(actionNode->action_para,COMMAND_AI);
336 
337 	//------- assign the spy to the target -------//
338 
339 	spyUnit->assign(actionNode->action_x_loc, actionNode->action_y_loc);
340 
341 	//----------------------------------------------------------------//
342 	// Since the spy has already changed its cloaked nation recno
343 	// we cannot set the ai_action_id of the unit as when it needs
344 	// to call action_finished() or action_failure() it will
345 	// use the cloaked nation recno, which is incorrect.
346 	// So we just return -1, noting that the action has been completed.
347 	//----------------------------------------------------------------//
348 
349 	return -1;
350 }
351 //----- End of function Nation::ai_assign_spy -----//
352 
353 
354 //-------- Begin of function Nation::think_assign_spy_target_camp --------//
355 //
356 // Think about planting spies into enemy buildings.
357 //
think_assign_spy_target_camp(int raceId,int regionId)358 int Nation::think_assign_spy_target_camp(int raceId, int regionId)
359 {
360 	Firm  *firmPtr;
361 	int	curRating, bestRating=0, bestFirmRecno=0;
362 
363 	for( int firmRecno=firm_array.size() ; firmRecno>0 ; firmRecno-- )
364 	{
365 		if( firm_array.is_deleted(firmRecno) )
366 			continue;
367 
368 		firmPtr = firm_array[firmRecno];
369 
370 		if( firmPtr->nation_recno == nation_recno )		// don't assign to own firm
371 			continue;
372 
373 		if( firmPtr->region_id != regionId )
374 			continue;
375 
376 		if( firmPtr->overseer_recno == 0 ||
377 			 firmPtr->worker_count == MAX_WORKER )
378 		{
379 			continue;
380 		}
381 
382 		if( firmPtr->majority_race() != raceId )
383 			continue;
384 
385 		//---------------------------------//
386 
387 		Unit* overseerUnit = unit_array[firmPtr->overseer_recno];
388 
389 		if( overseerUnit->spy_recno )		// if the overseer is already a spy
390 			continue;
391 
392 		curRating = 100 - overseerUnit->loyalty;
393 
394 		if( curRating > bestRating )
395 		{
396 			bestRating 	  = curRating;
397 			bestFirmRecno = firmRecno;
398 		}
399 	}
400 
401 	return bestFirmRecno;
402 }
403 //-------- End of function Nation::think_assign_spy_target_camp --------//
404 
405 
406 //-------- Begin of function Nation::think_assign_spy_target_town --------//
407 //
408 // Think about planting spies into independent towns and enemy towns.
409 //
think_assign_spy_target_town(int raceId,int regionId)410 int Nation::think_assign_spy_target_town(int raceId, int regionId)
411 {
412 	Town  *townPtr;
413 	int   townCount = town_array.size();
414 	int   townRecno = misc.random(townCount)+1;
415 
416 	for( int i=town_array.size() ; i>0 ; i-- )
417 	{
418 		if( ++townRecno > townCount )
419 			townRecno = 1;
420 
421 		if( town_array.is_deleted(townRecno) )
422 			continue;
423 
424 		townPtr = town_array[townRecno];
425 
426 		if( townPtr->nation_recno == nation_recno )		// don't assign to own firm
427 			continue;
428 
429 		if( townPtr->region_id != regionId )
430 			continue;
431 
432 		if( townPtr->population > MAX_TOWN_POPULATION-5 )		// -5 so that even if we assign too many spies to a town at the same time, there will still room for them
433 			continue;
434 
435 		//---- for player towns, don't assign too frequently ----//
436 
437 		if( !townPtr->ai_town )
438 		{
439 			if( misc.random(3) != 0 )
440 				continue;
441 		}
442 
443 		//----------------------------------------//
444 
445 		if( townPtr->nation_recno )
446 		{
447 			if( townPtr->race_loyalty_array[raceId-1] < MIN_NATION_DEFEND_LOYALTY )		// no need to assign spies to these towns as they are already very low
448 				continue;
449 		}
450 		else
451 		{
452 			if( townPtr->race_resistance_array[raceId-1][nation_recno-1] < MIN_INDEPENDENT_DEFEND_LOYALTY )		// no need to assign spies to these towns as they are already very low
453 				continue;
454 		}
455 
456 		if( townPtr->majority_race() != raceId )
457 			continue;
458 
459 		return townRecno;
460 	}
461 
462 	return 0;
463 }
464 //-------- End of function Nation::think_assign_spy_target_town --------//
465 
466 
467 //-------- Begin of function Nation::think_assign_spy_own_town --------//
468 //
469 // Think about planting spies into own towns.
470 //
think_assign_spy_own_town(int raceId,int regionId)471 int Nation::think_assign_spy_own_town(int raceId, int regionId)
472 {
473 	Town  *townPtr;
474 	int   townCount = town_array.size();
475 	int   townRecno = misc.random(townCount)+1;
476 	int   spyCount;
477 
478 	for( int i=town_array.size() ; i>0 ; i-- )
479 	{
480 		if( ++townRecno > townCount )
481 			townRecno = 1;
482 
483 		if( town_array.is_deleted(townRecno) )
484 			continue;
485 
486 		townPtr = town_array[townRecno];
487 
488 		if( townPtr->nation_recno != nation_recno )		// only assign to own firm
489 			continue;
490 
491 		if( townPtr->region_id != regionId )
492 			continue;
493 
494 		if( townPtr->population > MAX_TOWN_POPULATION-5 )
495 			continue;
496 
497 		if( townPtr->majority_race() != raceId )
498 			continue;
499 
500 		int curSpyLevel    = spy_array.total_spy_skill_level( SPY_TOWN, townRecno, nation_recno, spyCount );
501 		int neededSpyLevel = townPtr->needed_anti_spy_level();
502 
503 		if( neededSpyLevel > curSpyLevel + 30 )
504 			return townRecno;
505 	}
506 
507 	return 0;
508 }
509 //-------- End of function Nation::think_assign_spy_own_town --------//
510