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 : OSPY2.CPP
22 //Description : Spy AI functions
23
24 #include <OINFO.h>
25 #include <OFIRM.h>
26 #include <OTOWN.h>
27 #include <ONATION.h>
28 #include <OSPY.h>
29
30 #define THIS ptr()
31
32 //--------- Begin of function SpyProcess::process_ai ----------//
33 //
process_ai()34 void SpyProcess::process_ai()
35 {
36 if( spy_recno%30 == info.game_date%30 ) // think about changing actions once 30 days
37 think_reward();
38
39 switch( THIS->spy_place )
40 {
41 case SPY_TOWN:
42 if( spy_recno%30 == info.game_date%30 )
43 think_town_spy();
44 break;
45
46 case SPY_FIRM:
47 if( spy_recno%30 == info.game_date%30 )
48 think_firm_spy();
49 break;
50
51 case SPY_MOBILE:
52 if( spy_recno%5 == info.game_date%5 )
53 think_mobile_spy();
54 break;
55 }
56 }
57 //---------- End of function SpyProcess::process_ai ----------//
58
59
60 //--------- Begin of function SpyProcess::think_town_spy ----------//
61 //
think_town_spy()62 void SpyProcess::think_town_spy()
63 {
64 Town* townPtr = town_array[THIS->spy_place_para];
65
66 if( townPtr->nation_recno == THIS->true_nation_recno ) // anti-spy
67 return;
68
69 //------ if it's an independent town ------//
70
71 if( townPtr->nation_recno == 0 )
72 {
73 THIS->set_action_mode(SPY_SOW_DISSENT);
74
75 //--- if the resistance has already drop low enough, the spy no longer needs to be in the town ---//
76
77 if( townPtr->race_loyalty_array[THIS->race_id-1] < MIN_INDEPENDENT_DEFEND_LOYALTY )
78 {
79 THIS->mobilize_town_spy();
80 }
81 }
82 else
83 {
84 //-------------- if it's a nation town -------------//
85 //
86 // Set to sleep mode in most time so the spying skill can increase
87 // gradually, when the loyalty level of the village falls to near
88 // rebel level, set all of your spies in the village to sow dissent
89 // mode and cause rebellion in the enemy village.
90 //
91 //--------------------------------------------------//
92
93 Nation* trueNation = nation_array[THIS->true_nation_recno];
94
95 if( townPtr->average_loyalty() < 50 - trueNation->pref_loyalty_concern/10 ) // pref_loyalty_concern actually does apply to here, we just use a preference var so that the decision making process will vary between nations
96 {
97 THIS->set_action_mode(SPY_SOW_DISSENT);
98 }
99 else
100 {
101 if( misc.random(5)==0 ) // 20% chance of sowing dissents.
102 THIS->set_action_mode(SPY_SOW_DISSENT);
103 else
104 THIS->set_action_mode(SPY_IDLE);
105 }
106 }
107 }
108 //---------- End of function SpyProcess::think_town_spy ----------//
109
110
111 //--------- Begin of function SpyProcess::think_firm_spy ----------//
112 //
think_firm_spy()113 void SpyProcess::think_firm_spy()
114 {
115 Firm* firmPtr = firm_array[THIS->spy_place_para];
116
117 if( firmPtr->nation_recno == THIS->true_nation_recno ) // anti-spy
118 return;
119
120 //-------- try to capturing the firm --------//
121
122 if( THIS->capture_firm() )
123 return;
124
125 //-------- think about bribing ---------//
126
127 if( think_bribe() )
128 return;
129
130 //-------- think about assassinating ---------//
131
132 if( think_assassinate() )
133 return;
134
135 //------ think about changing spy mode ----//
136
137 else if( misc.random(3)==0 ) // 1/10 chance to set it to idle to prevent from being caught
138 {
139 THIS->set_action_mode(SPY_IDLE);
140 }
141 else if( misc.random(2)==0 &&
142 THIS->can_sabotage() && firmPtr->is_operating() && firmPtr->productivity >= 20 )
143 {
144 THIS->set_action_mode(SPY_SABOTAGE);
145 }
146 else
147 {
148 THIS->set_action_mode(SPY_SOW_DISSENT);
149 }
150 }
151 //---------- End of function SpyProcess::think_firm_spy ----------//
152
153
154 //--------- Begin of function SpyProcess::think_bribe ----------//
155 //
think_bribe()156 int SpyProcess::think_bribe()
157 {
158 Firm* firmPtr = firm_array[THIS->spy_place_para];
159
160 //--- only bribe enemies in military camps ---//
161
162 if( firmPtr->firm_id != FIRM_CAMP )
163 return 0;
164
165 //----- only if there is an overseer in the camp -----//
166
167 if( !firmPtr->overseer_recno )
168 return 0;
169
170 //---- see if the overseer can be bribe (kings and your own spies can't be bribed) ----//
171
172 if( !firmPtr->can_spy_bribe(0, THIS->true_nation_recno ) ) // 0-bribe the overseer
173 return 0;
174
175 //------ first check our financial status ------//
176
177 Nation* ownNation = nation_array[THIS->true_nation_recno];
178 Unit* overseerUnit = unit_array[firmPtr->overseer_recno];
179
180 if( THIS->spy_skill < MIN(50, overseerUnit->skill.skill_level) ||
181 !ownNation->ai_should_spend(30) )
182 {
183 return 0;
184 }
185
186 //----- think about how important is this firm -----//
187
188 int firmImportance = 0;
189 Town* townPtr;
190
191 for( int i=firmPtr->linked_town_count-1 ; i>=0 ; i-- )
192 {
193 townPtr = town_array[ firmPtr->linked_town_array[i] ];
194
195 if( townPtr->nation_recno == firmPtr->nation_recno )
196 firmImportance += townPtr->population * 2;
197 else
198 firmImportance += townPtr->population;
199 }
200
201 //------- think about which one to bribe -------//
202
203 //-- first get the succeedChange if the bribe amount is zero --//
204
205 int succeedChange = firmPtr->spy_bribe_succeed_chance( 0, spy_recno, 0 ); // first 0 - $0 bribe amount, 3rd 0 - bribe the overseer
206
207 //-- then based on it, figure out how much we have to offer to bribe successfully --//
208
209 int bribeAmount = MAX_BRIBE_AMOUNT * (100-succeedChange) / 100;
210
211 bribeAmount = MAX(100, bribeAmount);
212
213 //--- only bribe when the nation has enough money ---//
214
215 if( !ownNation->ai_should_spend(30, (float)bribeAmount) )
216 return 0;
217
218 //------- try to bribe the commander ----//
219
220 int newSpyRecno = firmPtr->spy_bribe(bribeAmount, spy_recno, 0);
221
222 if( !newSpyRecno ) // bribing failed
223 return 1; // return 1 as the spy has been killed
224
225 Spy* newSpy = spy_array[newSpyRecno];
226
227 err_when( newSpy->true_nation_recno != THIS->true_nation_recno );
228 err_when( newSpy->spy_place != SPY_FIRM );
229
230 if( newSpy->capture_firm() ) // try to capture the firm now
231 {
232 err_when( firm_array[newSpy->spy_place_para]->nation_recno != THIS->true_nation_recno );
233
234 newSpy->drop_spy_identity(); // drop the spy identity of the newly bribed spy if the capture is successful, this will save the spying costs
235 }
236
237 return 1;
238 }
239 //---------- End of function SpyProcess::think_bribe ----------//
240
241
242 //--------- Begin of function SpyProcess::think_reward ----------//
243 //
244 // Think about rewarding this spy.
245 //
think_reward()246 int SpyProcess::think_reward()
247 {
248 Nation* ownNation = nation_array[THIS->true_nation_recno];
249
250 //----------------------------------------------------------//
251 // The need to secure high loyalty on this unit is based on:
252 // -its skill
253 // -its combat level
254 // -soldiers commanded by this unit
255 //----------------------------------------------------------//
256
257 int neededLoyalty = THIS->spy_skill * (100+ownNation->pref_loyalty_concern) / 100;
258
259 neededLoyalty = MAX( UNIT_BETRAY_LOYALTY+10, neededLoyalty ); // 10 points above the betray loyalty level to prevent betrayal
260 neededLoyalty = MIN( 100, neededLoyalty );
261
262 //------- if the loyalty is already high enough ------//
263
264 if( THIS->spy_loyalty >= neededLoyalty )
265 return 0;
266
267 //---------- see how many cash & profit we have now ---------//
268
269 int rewardNeedRating = neededLoyalty - THIS->spy_loyalty;
270
271 if( THIS->spy_loyalty < UNIT_BETRAY_LOYALTY+5 )
272 rewardNeedRating += 50;
273
274 if( ownNation->ai_should_spend(rewardNeedRating) )
275 {
276 THIS->reward(COMMAND_AI);
277 return 1;
278 }
279
280 return 0;
281 }
282 //---------- End of function SpyProcess::think_reward ----------//
283
284
285 //--------- Begin of function SpyProcess::think_mobile_spy ----------//
286 //
think_mobile_spy()287 int SpyProcess::think_mobile_spy()
288 {
289 Unit* unitPtr = unit_array[THIS->spy_place_para];
290
291 //--- if the spy is on the ship, nothing can be done ---//
292
293 if( !unitPtr->is_visible() )
294 return 0;
295
296 //---- if the spy has stopped and there is no new action ----//
297
298 if( unitPtr->is_ai_all_stop() &&
299 (!THIS->notify_cloaked_nation_flag || THIS->cloaked_nation_recno==0) )
300 {
301 return think_mobile_spy_new_action();
302 }
303
304 return 0;
305 }
306 //---------- End of function SpyProcess::think_mobile_spy ----------//
307
308
309 //-------- Begin of function SpyProcess::think_mobile_spy_new_action --------//
310 //
think_mobile_spy_new_action()311 int SpyProcess::think_mobile_spy_new_action()
312 {
313 Nation* trueNation = nation_array[THIS->true_nation_recno];
314
315 err_when( THIS->spy_place != SPY_MOBILE );
316
317 int spyRegionId = unit_array[THIS->spy_place_para]->region_id();
318
319 //----- try to sneak into an enemy camp ------//
320
321 int firmRecno = trueNation->think_assign_spy_target_camp(THIS->race_id, spyRegionId);
322
323 if( firmRecno )
324 {
325 Firm* firmPtr = firm_array[firmRecno];
326
327 return add_assign_spy_action( firmPtr->loc_x1, firmPtr->loc_y1, firmPtr->nation_recno );
328 }
329
330 //--- try to sneak into an enemy town or an independent town ---//
331
332 int townRecno = trueNation->think_assign_spy_target_town(THIS->race_id, spyRegionId);
333
334 if( townRecno )
335 {
336 Town* townPtr = town_array[townRecno];
337
338 return add_assign_spy_action( townPtr->loc_x1, townPtr->loc_y1, townPtr->nation_recno );
339 }
340
341 //------ think if we should drop the spy identity -------//
342
343 int dropIdentity = 0;
344
345 //-------- if we already have too many spies --------//
346
347 if( trueNation->total_spy_count > trueNation->total_population * (10+trueNation->pref_spy/5) / 100 ) // 10% to 30%
348 {
349 dropIdentity = 1;
350 }
351
352 //--- the expense of spies should not be too large ---//
353
354 else if( trueNation->expense_365days(EXPENSE_SPY) >
355 trueNation->expense_365days() * (50+trueNation->pref_counter_spy) / 400 )
356 {
357 dropIdentity = 1;
358 }
359
360 else //------- try to assign to one of our own towns -------//
361 {
362 int townRecno = trueNation->think_assign_spy_own_town(THIS->race_id, spyRegionId);
363
364 if( townRecno )
365 {
366 Town* townPtr = town_array[townRecno];
367
368 return add_assign_spy_action( townPtr->loc_x1, townPtr->loc_y1, townPtr->nation_recno );
369 }
370 else
371 {
372 dropIdentity = 1;
373 }
374 }
375
376 //---------- drop spy identity now --------//
377
378 if( dropIdentity )
379 {
380 THIS->drop_spy_identity();
381 return 1;
382 }
383
384 return 0;
385 }
386 //---------- End of function SpyProcess::think_mobile_spy_new_action --------//
387
388
389 //-------- Begin of function SpyProcess::add_assign_spy_action --------//
390 //
add_assign_spy_action(int destXLoc,int destYLoc,int cloakedNationRecno)391 int SpyProcess::add_assign_spy_action(int destXLoc, int destYLoc, int cloakedNationRecno)
392 {
393 err_when( THIS->spy_place != SPY_MOBILE );
394 err_when( unit_array.is_deleted(THIS->spy_place_para) );
395
396 return nation_array[THIS->true_nation_recno]->add_action( destXLoc, destYLoc,
397 -1, -1, ACTION_AI_ASSIGN_SPY, cloakedNationRecno, 1, THIS->spy_place_para );
398 }
399 //---------- End of function SpyProcess::add_assign_spy_action --------//
400
401
402 //--------- Begin of function Spy::ai_spy_being_attacked ----------//
403 //
404 // This function is called when this spy is under attack.
405 //
406 // <int> attackerUnitRecno - recno of the attacker unit.
407 //
ai_spy_being_attacked(int attackerUnitRecno)408 int Spy::ai_spy_being_attacked(int attackerUnitRecno)
409 {
410 err_when( spy_place != SPY_MOBILE );
411
412 Unit* attackerUnit = unit_array[attackerUnitRecno];
413 Unit* spyUnit = unit_array[spy_place_para];
414 Nation* trueNation = nation_array[true_nation_recno];
415
416 //----- if we are attacking our own units -----//
417
418 if( attackerUnit->true_nation_recno() == true_nation_recno )
419 {
420 if( spy_skill > 50-trueNation->pref_spy/10 ||
421 spyUnit->hit_points < spyUnit->max_hit_points * (100-trueNation->pref_military_courage/2) / 100 )
422 {
423 change_cloaked_nation( true_nation_recno );
424 return 1;
425 }
426 }
427 else
428 {
429 //---- if this unit is attacking units of other nations -----//
430 //
431 // If the nation this spy cloaked into is at war with the spy's
432 // true nation and the nation which the spy is currently attacking
433 // is not at war with the spy's true nation, then change
434 // the spy's cloak to the non-hostile nation.
435 //
436 //-----------------------------------------------------------//
437
438 if( trueNation->get_relation_status(attackerUnit->nation_recno) != NATION_HOSTILE &&
439 trueNation->get_relation_status(cloaked_nation_recno) == NATION_HOSTILE )
440 {
441 if( spy_skill > 50-trueNation->pref_spy/10 ||
442 spyUnit->hit_points < spyUnit->max_hit_points * (100-trueNation->pref_military_courage/2) / 100 )
443 {
444 change_cloaked_nation( true_nation_recno );
445 return 1;
446 }
447 }
448 }
449
450 return 0;
451 }
452 //---------- End of function Spy::ai_spy_being_attacked ----------//
453
454
455 //--------- Begin of function SpyProcess::think_assassinate ----------//
456 //
think_assassinate()457 int SpyProcess::think_assassinate()
458 {
459 Firm* firmPtr = firm_array[THIS->spy_place_para];
460
461 //--- only bribe enemies in military camps ---//
462
463 if( firmPtr->firm_id != FIRM_CAMP )
464 return 0;
465
466 //----- only if there is an overseer in the camp -----//
467
468 if( !firmPtr->overseer_recno )
469 return 0;
470
471 //---- get the attack and defense rating ----//
472
473 int attackRating, defenseRating, otherDefenderCount;
474
475 if( !THIS->get_assassinate_rating(firmPtr->overseer_recno, attackRating, defenseRating, otherDefenderCount) ) // return 0 if assassination is not possible
476 return 0;
477
478 Nation* trueNation = nation_array[THIS->true_nation_recno];
479
480 if( attackRating + misc.random(20+trueNation->pref_spy/2) > defenseRating ) // the random number is to increase the chance of attempting assassination
481 {
482 THIS->assassinate(firmPtr->overseer_recno, COMMAND_AI);
483 return 1;
484 }
485
486 return 0;
487 }
488 //---------- End of function SpyProcess::think_assassinate ----------//
489
490