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: OUNIT2.CPP
22 //Description: Unit functions
23
24 #include <OWORLD.h>
25 #include <ONATION.h>
26 #include <OFIRM.h>
27 #include <OTOWN.h>
28 #include <OUNIT.h>
29
30
31 //------- Begin of function Unit::think_aggressive_action --------//
32 //
33 // This function is called when the unit is in aggressive mode.
34 //
think_aggressive_action()35 int Unit::think_aggressive_action()
36 {
37 //------ think about resuming the original action -----//
38
39 if( original_action_mode && cur_action!=SPRITE_ATTACK ) // if it is currently attacking somebody, don't think about resuming the original action
40 {
41 return think_resume_original_action();
42 }
43
44 //---- think about attacking nearby units if this unit is attacking a town or a firm ---//
45
46 if( aggressive_mode && unit_mode==0 && cur_action==SPRITE_ATTACK ) // only in attack mode, as if the unit is still moving the target may be far away from the current position
47 {
48 //--- only when the unit is currently attacking a firm or a town ---//
49
50 if( action_mode2 == ACTION_ATTACK_FIRM ||
51 action_mode2 == ACTION_ATTACK_TOWN )
52 {
53 if( info.game_date%5==0 ) // check once every 5 days
54 return think_change_attack_target();
55 }
56 }
57
58 return 0;
59 }
60 //-------- End of function Unit::think_aggressive_action --------//
61
62
63 //--------- Begin of function Unit::think_resume_original_action --------//
64 //
65 // If this unit is chasing a target to attack. Stop the chase
66 // if the target is being far away from its original location.
67 //
think_resume_original_action()68 int Unit::think_resume_original_action()
69 {
70 if( !is_visible() ) // if the unit is no longer visible, cancel the saved orignal action
71 {
72 original_action_mode = 0;
73 return 0;
74 }
75
76 //---- if the unit is in defense mode now, don't do anything ----//
77
78 if( in_any_defense_mode() )
79 return 0;
80
81 //----------------------------------------------------//
82 //
83 // If the action has been changed or the target unit has been deleted,
84 // stop the chase right and move back to the original position
85 // before the auto guard attack.
86 //
87 //----------------------------------------------------//
88
89 if(action_mode2!=ACTION_ATTACK_UNIT || unit_array.is_deleted(action_para2))
90 {
91 resume_original_action();
92 return 1;
93 }
94
95 //-----------------------------------------------------//
96 //
97 // Stop the chase if the target is being far away from
98 // its original location and move back to its original
99 // position before the auto guard attack.
100 //
101 //-----------------------------------------------------//
102
103 #define AUTO_GUARD_CHASE_ATTACK_DISTANCE 5
104
105 Unit* targetUnit = unit_array[action_para2];
106
107 int curDistance = misc.points_distance( targetUnit->next_x_loc(), targetUnit->next_y_loc(),
108 original_target_x_loc, original_target_y_loc );
109
110 if( curDistance > AUTO_GUARD_CHASE_ATTACK_DISTANCE )
111 {
112 resume_original_action();
113 return 1;
114 }
115
116 return 0;
117 }
118 //---------- End of function Unit::think_resume_original_action --------//
119
120
121 //------- Begin of function Unit::think_change_attack_target -------//
122 //
123 // When the unit is attacking a firm or town, look out for enemy units
124 // to attack. Enemy units should be attacked first.
125 //
think_change_attack_target()126 int Unit::think_change_attack_target()
127 {
128 err_when( !nation_recno ); // only for nation units
129
130 //----------------------------------------------//
131
132 int attackRange = MAX(attack_range, 8);
133 int attackScanRange = attackRange*2+1;
134
135 int xOffset, yOffset;
136 int xLoc, yLoc;
137 Location* locPtr;
138 int curXLoc = next_x_loc(), curYLoc = next_y_loc();
139 int regionId = world.get_region_id(curXLoc, curYLoc);
140
141 for( int i=2 ; i<attackScanRange*attackScanRange ; i++ )
142 {
143 misc.cal_move_around_a_point(i, attackScanRange, attackScanRange, xOffset, yOffset);
144
145 xLoc = curXLoc + xOffset;
146 yLoc = curYLoc + yOffset;
147
148 xLoc = MAX(0, xLoc);
149 xLoc = MIN(MAX_WORLD_X_LOC-1, xLoc);
150
151 yLoc = MAX(0, yLoc);
152 yLoc = MIN(MAX_WORLD_Y_LOC-1, yLoc);
153
154 locPtr = world.get_loc(xLoc, yLoc);
155
156 if( locPtr->region_id != regionId )
157 continue;
158
159 //----- if there is a unit on the location ------//
160
161 if( locPtr->has_unit(UNIT_LAND) )
162 {
163 int unitRecno = locPtr->unit_recno(UNIT_LAND);
164
165 if( unit_array.is_deleted(unitRecno) )
166 continue;
167
168 if( unit_array[unitRecno]->nation_recno != nation_recno &&
169 idle_detect_unit_checking(unitRecno) )
170 {
171 save_original_action();
172
173 original_target_x_loc = xLoc;
174 original_target_y_loc = yLoc;
175
176 attack_unit(xLoc, yLoc);
177 return 1;
178 }
179 }
180 }
181
182 return 0;
183 }
184 //---------- End of function Unit::think_change_attack_target ------//
185
186
187 //----------- Begin of function Unit::save_original_action -------//
save_original_action()188 void Unit::save_original_action()
189 {
190 if( original_action_mode==0 )
191 {
192 original_action_mode = action_mode2;
193 original_action_para = action_para2;
194 original_action_x_loc = action_x_loc2;
195 original_action_y_loc = action_y_loc2;
196 }
197 }
198 //----------- End of function Unit::save_original_action -------//
199
200
201 //----------- Begin of function Unit::resume_original_action -------//
202
resume_original_action()203 void Unit::resume_original_action()
204 {
205 if( !original_action_mode )
206 return;
207
208 //--------- If it is an attack action ---------//
209
210 if( original_action_mode == ACTION_ATTACK_UNIT ||
211 original_action_mode == ACTION_ATTACK_FIRM ||
212 original_action_mode == ACTION_ATTACK_TOWN )
213 {
214 resume_original_attack_action();
215 return;
216 }
217
218 //--------------------------------------------//
219
220 if( original_action_x_loc<0 || original_action_x_loc>=MAX_WORLD_X_LOC ||
221 original_action_y_loc<0 || original_action_y_loc>=MAX_WORLD_Y_LOC )
222 {
223 original_action_mode = 0;
224 return;
225 }
226
227 short selectedArray[1];
228 selectedArray[0] = sprite_recno; // use unit_array.attack() instead of unit.attack_???() as we are unsure about what type of object the target is.
229
230 Location* locPtr = world.get_loc(original_action_x_loc, original_action_y_loc);
231
232 //--------- resume assign to town -----------//
233
234 if( original_action_mode == ACTION_ASSIGN_TO_TOWN && locPtr->is_town() )
235 {
236 if( locPtr->town_recno() == original_action_para &&
237 town_array[original_action_para]->nation_recno == nation_recno )
238 {
239 unit_array.assign( original_action_x_loc, original_action_y_loc, 0,
240 COMMAND_AUTO, selectedArray, 1 );
241 }
242 }
243
244 //--------- resume assign to firm ----------//
245
246 else if( original_action_mode == ACTION_ASSIGN_TO_FIRM && locPtr->is_firm() )
247 {
248 if( locPtr->firm_recno() == original_action_para &&
249 firm_array[original_action_para]->nation_recno == nation_recno )
250 {
251 unit_array.assign( original_action_x_loc, original_action_y_loc, 0,
252 COMMAND_AUTO, selectedArray, 1 );
253 }
254 }
255
256 //--------- resume build firm ---------//
257
258 else if( original_action_mode == ACTION_BUILD_FIRM )
259 {
260 if( world.can_build_firm( original_action_x_loc, original_action_y_loc,
261 original_action_para, sprite_recno ) )
262 {
263 build_firm( original_action_x_loc, original_action_y_loc,
264 original_action_para, COMMAND_AUTO );
265 }
266 }
267
268 //--------- resume settle ---------//
269
270 else if( original_action_mode == ACTION_SETTLE )
271 {
272 if( world.can_build_town( original_action_x_loc, original_action_y_loc, sprite_recno ) )
273 {
274 unit_array.settle( original_action_x_loc, original_action_y_loc,
275 0, COMMAND_AUTO, selectedArray, 1 );
276 }
277 }
278
279 //--------- resume move ----------//
280
281 else if( original_action_mode == ACTION_MOVE )
282 {
283 unit_array.move_to( original_action_x_loc, original_action_y_loc, 0,
284 selectedArray, 1, COMMAND_AUTO );
285 }
286
287 original_action_mode = 0;
288 }
289 //----------- End of function Unit::resume_original_action -------//
290
291
292 //----------- Begin of function Unit::resume_original_attack_action -------//
293 //
resume_original_attack_action()294 void Unit::resume_original_attack_action()
295 {
296 if( !original_action_mode )
297 return;
298
299 if( original_action_mode != ACTION_ATTACK_UNIT &&
300 original_action_mode != ACTION_ATTACK_FIRM &&
301 original_action_mode != ACTION_ATTACK_TOWN )
302 {
303 original_action_mode = 0;
304 return;
305 }
306
307 //--------------------------------------------//
308
309 err_when( original_action_x_loc<0 || original_action_x_loc>=MAX_WORLD_X_LOC );
310 err_when( original_action_y_loc<0 || original_action_y_loc>=MAX_WORLD_Y_LOC );
311
312 Location* locPtr = world.get_loc(original_action_x_loc, original_action_y_loc);
313 int targetNationRecno = -1;
314
315 if( original_action_mode == ACTION_ATTACK_UNIT && locPtr->has_unit(UNIT_LAND) )
316 {
317 int unitRecno = locPtr->unit_recno(UNIT_LAND);
318
319 if( unitRecno == original_action_para )
320 targetNationRecno = unit_array[unitRecno]->nation_recno;
321 }
322 else if( original_action_mode == ACTION_ATTACK_FIRM && locPtr->is_firm() )
323 {
324 int firmRecno = locPtr->firm_recno();
325
326 if( firmRecno == original_action_para )
327 targetNationRecno = firm_array[firmRecno]->nation_recno;
328 }
329 else if( original_action_mode == ACTION_ATTACK_TOWN && locPtr->is_town() )
330 {
331 int townRecno = locPtr->town_recno();
332
333 if( townRecno == original_action_para )
334 targetNationRecno = town_array[townRecno]->nation_recno;
335 }
336
337 //----- the original target is no longer valid ----//
338
339 if( targetNationRecno == -1 )
340 {
341 original_action_mode = 0;
342 return;
343 }
344
345 //---- only resume attacking the target if the target nation is at war with us currently ---//
346
347 if( !targetNationRecno || (targetNationRecno &&
348 targetNationRecno != nation_recno &&
349 nation_array[nation_recno]->get_relation_status(targetNationRecno) == NATION_HOSTILE ))
350 {
351 short selectedArray[1];
352 selectedArray[0] = sprite_recno; // use unit_array.attack() instead of unit.attack_???() as we are unsure about what type of object the target is.
353
354 // #### begin Gilbert 5/8 ########//
355 unit_array.attack(original_action_x_loc, original_action_y_loc, 0, selectedArray, 1, COMMAND_AI, 0 );
356 // #### end Gilbert 5/8 ########//
357 }
358
359 original_action_mode = 0;
360 }
361 //----------- End of function Unit::resume_original_attack_action -------//
362
363 //------- Begin of function Unit::ask_team_help_attack --------//
364 //
365 // It returns whether any of the co-member of this unit in a troop
366 // is under attack.
367 //
368 // <Unit*> attackerUnit - the unit pointer of the attacker
369 //
ask_team_help_attack(Unit * attackerUnit)370 void Unit::ask_team_help_attack(Unit* attackerUnit)
371 {
372 //--- if the attacking unit is our unit (this can happen if the unit is porcupine) ---//
373
374 if( attackerUnit->nation_recno == nation_recno )
375 return;
376
377 //-----------------------------------------//
378
379 int leaderUnitRecno=0;
380
381 if( leader_unit_recno ) // if the current unit is a soldier, get its leader's recno
382 leaderUnitRecno = leader_unit_recno;
383
384 else if( team_info ) // this unit is the commander
385 leaderUnitRecno = sprite_recno;
386
387 if( leaderUnitRecno )
388 {
389 TeamInfo* teamInfo = unit_array[leaderUnitRecno]->team_info;
390
391 err_when( !teamInfo );
392
393 for( int i=teamInfo->member_count-1 ; i>=0 ; i-- )
394 {
395 int unitRecno = teamInfo->member_unit_array[i];
396
397 if( unit_array.is_deleted(unitRecno) )
398 continue;
399
400 Unit* unitPtr = unit_array[ unitRecno ];
401
402 if( unitPtr->cur_action==SPRITE_IDLE && unitPtr->is_visible() )
403 {
404 unitPtr->attack_unit(attackerUnit->sprite_recno);
405 return;
406 }
407 }
408 }
409 }
410 //-------- End of function Unit::ask_team_help_attack --------//
411
412