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