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 : OUNITATM.CPP
22 //Description : Object Unit attack functions to handle blocked movement and group attacking
23 //Owner : Alex
24
25 #include <OWORLD.h>
26 #include <OFIRM.h>
27 #include <OTOWN.h>
28 #include <OUNIT.h>
29 #include <OTERRAIN.h>
30
31 #ifdef NO_DEBUG_UNIT
32 #undef err_when
33 #undef err_here
34 #undef err_if
35 #undef err_else
36 #undef err_now
37 #define err_when(cond)
38 #define err_here()
39 #define err_if(cond)
40 #define err_else
41 #define err_now(msg)
42 #undef DEBUG
43 #endif
44
45 //--------- Begin of function Unit::search_or_stop ---------//
46 // process searching or stop the units
47 //
48 // <int> destX - x location to move to
49 // <int> destY - y location to move to
50 // <int> preserveAction - preserve action when calling to stop
51 // <short> searchMode - search mode for searching
52 // <short> miscNo - miscenllaneous information
53 //
search_or_stop(int destX,int destY,int preserveAction,short searchMode,short miscNo)54 void Unit::search_or_stop(int destX, int destY, int preserveAction, short searchMode, short miscNo)
55 {
56 Location *locPtr = world.get_loc(destX, destY);
57 if(!locPtr->can_move(mobile_type))
58 {
59 stop(KEEP_PRESERVE_ACTION); // let reactivate..() call searching later
60 //waiting_term = MAX_SEARCH_OT_STOP_WAIT_TERM;
61 }
62 else
63 {
64 search(destX, destY, preserveAction, searchMode, miscNo);
65 /*if(mobile_type==UNIT_LAND)
66 search(destX, destY, preserveAction, searchMode, miscNo);
67 else
68 waiting_term = 0;*/
69 }
70 }
71 //----------- End of function Unit::search_or_stop -----------//
72
73
74 //------ Begin of function Unit::search_or_wait ---------//
75 // call searching or wait
76 //
search_or_wait()77 void Unit::search_or_wait()
78 {
79 #define SQUARE1 9
80 #define SQUARE2 25
81 #define SQUARE3 49
82 #define DIMENSION 7
83
84 int curXLoc = next_x_loc(), curYLoc = next_y_loc();
85 short surrArray[SQUARE3];
86 int xShift, yShift, checkXLoc, checkYLoc, hasFree, i, shouldWait;
87 short unitRecno;
88 Unit *unitPtr;
89 Location *locPtr;
90
91 //----------------------------------------------------------------------------//
92 // wait if the unit is totally blocked. Otherwise, call searching
93 //----------------------------------------------------------------------------//
94 memset(surrArray, 0, sizeof(short)*SQUARE3);
95 for(shouldWait=0, hasFree=0, i=2; i<=SQUARE3; i++)
96 {
97 if(i==SQUARE1 || i==SQUARE2 || i==SQUARE3)
98 {
99 if(!hasFree)
100 {
101 shouldWait++;
102 break;
103 }
104 else
105 hasFree = 0;
106 }
107
108 misc.cal_move_around_a_point(i, DIMENSION, DIMENSION, xShift, yShift);
109 checkXLoc = curXLoc+xShift;
110 checkYLoc = curYLoc+yShift;
111 if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
112 continue;
113
114 locPtr = world.get_loc(checkXLoc, checkYLoc);
115 if(!locPtr->has_unit(mobile_type))
116 {
117 hasFree++;
118 continue;
119 }
120
121 unitRecno = locPtr->unit_recno(mobile_type);
122 err_when(!unitRecno);
123 if(unit_array.is_deleted(unitRecno))
124 continue;
125
126 unitPtr = unit_array[unitRecno];
127 if(unitPtr->nation_recno==nation_recno && unitPtr->unit_group_id==unit_group_id &&
128 ((unitPtr->cur_action==SPRITE_WAIT && unitPtr->waiting_term>1) || unitPtr->cur_action==SPRITE_TURN ||
129 unitPtr->cur_action==SPRITE_MOVE))
130 {
131 surrArray[i-2] = unitRecno;
132 unitPtr->unit_group_id++;
133 }
134 }
135
136 //------------------- call searching if should not wait --------------------//
137 if(!shouldWait)
138 search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_IN_A_GROUP);
139 //search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_IN_A_GROUP);
140
141 for(i=0; i<SQUARE3; i++)
142 {
143 if(surrArray[i])
144 {
145 err_when(unit_array.is_deleted(surrArray[i]));
146 unitPtr = unit_array[surrArray[i]];
147 unitPtr->unit_group_id--;
148 }
149 }
150
151 if(shouldWait)
152 set_wait();
153 }
154 //-------- End of function Unit::search_or_wait ---------//
155
156
157 //------ Begin of function Unit::handle_blocked_move_s11 -------//
158 // both blocked and blocking are size 1
159 //
handle_blocked_move_s11(Unit * unitPtr)160 void Unit::handle_blocked_move_s11(Unit *unitPtr)
161 {
162 err_when( world.get_unit_recno(next_x_loc(), next_y_loc(), mobile_type) != sprite_recno );
163 err_when( world.get_unit_recno(unitPtr->next_x_loc(), unitPtr->next_y_loc(), unitPtr->mobile_type) != unitPtr->sprite_recno );
164 err_when(cur_x!=next_x || cur_y!=next_y);
165
166 int waitTerm;
167 int moveStep = move_step_magn();
168
169 switch(unitPtr->cur_action)
170 {
171 //------------------------------------------------------------------------------------//
172 // handle blocked for units belonging to the same nation. For those belonging to other
173 // nations, wait for it moving to other locations or search for another path.
174 //------------------------------------------------------------------------------------//
175 case SPRITE_WAIT: // the blocking unit is waiting
176 err_when(unitPtr->go_x==unitPtr->cur_x && unitPtr->go_y==unitPtr->cur_y);
177
178 case SPRITE_TURN:
179 if(unitPtr->nation_recno==nation_recno)
180 handle_blocked_wait(unitPtr); // check for cycle wait for our nation
181 else if(waiting_term>=MAX_WAITING_TERM_DIFF)
182 {
183 search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall searching
184 waiting_term = 0;
185 }
186 else // wait
187 set_wait();
188 return;
189
190 //------------------------------------------------------------------------------------//
191 // We know from the cur_action of the blocking unit it is moving to another locations,
192 // the blocked unit wait for a number of terms or search again.
193 //------------------------------------------------------------------------------------//
194 case SPRITE_MOVE:
195 case SPRITE_READY_TO_MOVE:
196 case SPRITE_SHIP_EXTRA_MOVE:
197 if(unit_id!=UNIT_CARAVAN && unitPtr->unit_id==UNIT_CARAVAN) // don't wait for caravans, and caravans don't wait for other units
198 {
199 search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP);
200 }
201 else
202 {
203 waitTerm = (nation_recno==unitPtr->nation_recno) ? MAX_WAITING_TERM_SAME : MAX_WAITING_TERM_DIFF;
204 if(waiting_term>=waitTerm)
205 {
206 search_or_wait();
207 waiting_term = 0;
208 }
209 else
210 set_wait();
211 }
212 return;
213
214 //------------------------------------------------------------------------------------//
215 // handling blocked for idle unit
216 //------------------------------------------------------------------------------------//
217 case SPRITE_IDLE:
218 err_when(unitPtr->result_node_array!=NULL);
219 if(unitPtr->action_mode==ACTION_SHIP_TO_BEACH)
220 {
221 //----------------------------------------------------------------------//
222 // the blocking unit is trying to move to beach, so wait a number of terms,
223 // or call searching again
224 //----------------------------------------------------------------------//
225 if(abs(unitPtr->next_x_loc()-unitPtr->action_x_loc2)<=moveStep &&
226 abs(unitPtr->next_y_loc()-unitPtr->action_y_loc2)<=moveStep &&
227 terrain_res[world.get_loc(unitPtr->action_x_loc2, unitPtr->action_y_loc2)->terrain_id]->average_type
228 !=TERRAIN_OCEAN)
229 {
230 if(action_mode2==ACTION_SHIP_TO_BEACH && action_x_loc2==unitPtr->action_x_loc2 &&
231 action_y_loc2==unitPtr->action_y_loc2)
232 {
233 int tempX, tempY;
234 ship_to_beach(action_x_loc2, action_y_loc2, tempX, tempY);
235 }
236 else
237 {
238 waitTerm = (nation_recno==unitPtr->nation_recno) ? MAX_WAITING_TERM_SAME : MAX_WAITING_TERM_DIFF;
239 if(waiting_term>=waitTerm)
240 stop2();
241 else
242 set_wait();
243 }
244 return;
245 }
246 }
247
248 if(unitPtr->nation_recno==nation_recno) //-------- same nation
249 {
250 //------------------------------------------------------------------------------------//
251 // units from our nation
252 //------------------------------------------------------------------------------------//
253 if(unitPtr->unit_group_id==unit_group_id)
254 {
255 //--------------- from the same group -----------------//
256 if(way_point_count && !unitPtr->way_point_count)
257 {
258 //------------ reset way point --------------//
259 stop2();
260 reset_way_point_array();
261 }
262 else if((unitPtr->next_x_loc() != move_to_x_loc || unitPtr->next_y_loc() != move_to_y_loc) &&
263 (unitPtr->cur_action==SPRITE_IDLE && unitPtr->action_mode2==ACTION_STOP))
264 move_to_my_loc(unitPtr); // push the blocking unit and exchange their destination
265 else if(unitPtr->action_mode == ACTION_SETTLE)
266 set_wait(); // wait for the settler
267 else if(waiting_term>MAX_WAITING_TERM_SAME)
268 {
269 //---------- stop if wait too long ----------//
270 terminate_move();
271 waiting_term = 0;
272 }
273 else
274 set_wait();
275 }
276 else if(unitPtr->action_mode2==ACTION_STOP)
277 handle_blocked_by_idle_unit(unitPtr);
278 else if(way_point_count && !unitPtr->way_point_count)
279 {
280 stop2();
281 reset_way_point_array();
282 }
283 else
284 search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall A* algorithm by default mode
285 }
286 else // different nation
287 {
288 //------------------------------------------------------------------------------------//
289 // units from other nations
290 //------------------------------------------------------------------------------------//
291 if(unitPtr->next_x_loc() == move_to_x_loc && unitPtr->next_y_loc() == move_to_y_loc)
292 {
293 terminate_move(); // destination occupied by other unit
294
295 if(action_mode==ACTION_ATTACK_UNIT && unitPtr->nation_recno!=nation_recno && unitPtr->sprite_recno==action_para)
296 {
297 err_when(action_x_loc!=unitPtr->next_x_loc() || action_y_loc!=unitPtr->next_y_loc());
298 err_when(action_mode2!=ACTION_ATTACK_UNIT && action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET &&
299 action_mode2!=ACTION_DEFEND_TOWN_ATTACK_TARGET && action_mode2!=ACTION_MONSTER_DEFEND_ATTACK_TARGET);
300 err_when(action_para2!=action_para);
301 err_when(action_x_loc!=action_x_loc2 || action_y_loc!=action_y_loc2);
302
303 set_dir(next_x, next_y, unitPtr->next_x, unitPtr->next_y);
304 if(is_dir_correct())
305 attack_unit(action_para);
306 else
307 set_turn();
308 cur_frame = 1;
309 }
310 }
311 else
312 search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall A* algorithm by default mode
313 }
314 return;
315
316 //------------------------------------------------------------------------------------//
317 // don't wait for attackers from other nations, search for another path.
318 //------------------------------------------------------------------------------------//
319 case SPRITE_ATTACK:
320 //----------------------------------------------------------------//
321 // don't wait for other nation unit, call searching again
322 //----------------------------------------------------------------//
323 if(nation_recno!=unitPtr->nation_recno)
324 {
325 search_or_stop(move_to_x_loc, move_to_y_loc, 1);
326 return;
327 }
328
329 //------------------------------------------------------------------------------------//
330 // for attackers owned by our commander, handled blocked case by case as follows.
331 //------------------------------------------------------------------------------------//
332 switch(unitPtr->action_mode)
333 {
334 case ACTION_ATTACK_UNIT:
335 if(action_para && !unit_array.is_deleted(action_para))
336 {
337 Unit *targetPtr = unit_array[action_para];
338 handle_blocked_attack_unit(unitPtr, targetPtr);
339 }
340 else
341 search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP);
342 break;
343
344 case ACTION_ATTACK_FIRM:
345 if(!unitPtr->action_para || firm_array.is_deleted(unitPtr->action_para))
346 set_wait();
347 else
348 handle_blocked_attack_firm(unitPtr);
349 break;
350
351 case ACTION_ATTACK_TOWN:
352 if(!unitPtr->action_para || town_array.is_deleted(unitPtr->action_para))
353 set_wait();
354 else
355 handle_blocked_attack_town(unitPtr);
356 break;
357
358 case ACTION_ATTACK_WALL:
359 if(unitPtr->action_para)
360 set_wait();
361 else
362 handle_blocked_attack_wall(unitPtr);
363 break;
364
365 case ACTION_GO_CAST_POWER:
366 set_wait();
367 break;
368
369 default: err_here();
370 break;
371 }
372 return;
373
374 //------------------------------------------------------------------------------------//
375 // the blocked unit can pass after the blocking unit disappears in air.
376 //------------------------------------------------------------------------------------//
377 case SPRITE_DIE:
378 set_wait(); // assume this unit will not wait too long
379 return;
380
381 default:
382 err_here();
383 break;
384 }
385
386 err_when(cur_x==go_x && cur_y==go_y && (cur_x!=next_x || cur_y!=next_y));
387 }
388 //------- End of function Unit::handle_blocked_move_s11 --------//
389
390
391 //------ Begin of function Unit::handle_blocked_same_target_attack ---------//
392 // the target of the blocked unit and this unit are same
393 //
394 // <Unit*> unitPtr - pointer to blocking uit
395 // <Unit*> targetPtr - pointer to target unit
396 //
handle_blocked_same_target_attack(Unit * unitPtr,Unit * targetPtr)397 void Unit::handle_blocked_same_target_attack(Unit* unitPtr, Unit* targetPtr)
398 {
399 //----------------------------------------------------------//
400 // this unit is now waiting and the unit pointed by unitPtr
401 // is attacking the unit pointed by targetPtr
402 //----------------------------------------------------------//
403 err_when(cur_x%ZOOM_LOC_WIDTH || cur_y%ZOOM_LOC_HEIGHT);
404 err_when(cur_x!=next_x || cur_y!=next_y);
405 err_when(cur_x==go_x && cur_y==go_y);
406
407 if(space_for_attack(action_x_loc, action_y_loc, targetPtr->mobile_type, targetPtr->sprite_info->loc_width, targetPtr->sprite_info->loc_height))
408 {
409 err_when(action_x_loc!=action_x_loc2 || action_y_loc!=action_y_loc2);
410 search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_TO_ATTACK, targetPtr->sprite_recno);
411 //search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_TO_ATTACK, targetPtr->sprite_recno);
412 }
413 else if(in_any_defense_mode())
414 {
415 err_when(action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET && action_mode2!=ACTION_DEFEND_TOWN_ATTACK_TARGET &&
416 action_mode2!=ACTION_MONSTER_DEFEND_ATTACK_TARGET);
417
418 general_defend_mode_detect_target();
419 }
420 else if(misc.points_distance(next_x_loc(), next_y_loc(), action_x_loc, action_y_loc)<ATTACK_DETECT_DISTANCE)
421 {
422 //------------------------------------------------------------------------//
423 // if the target is within the detect range, stop the unit's action to detect
424 // another target if any exist. In case, there is no other target, the unit
425 // will still attack the original target since it is the only target in the
426 // detect range
427 //------------------------------------------------------------------------//
428 stop2(KEEP_DEFENSE_MODE);
429 }
430 else
431 set_wait(); // set wait to stop the movement
432 }
433 //------- End of function Unit::handle_blocked_same_target_attack --------//
434
435
436 //--------- Begin of function Unit::handle_blocked_attack_unit ---------//
437 // the blocking unit is attacking against other unit
438 //
439 // <Unit*> unitPtr - pointer to blocking uit
440 // <Unit*> targetPtr - pointer to target unit
441 //
handle_blocked_attack_unit(Unit * unitPtr,Unit * targetPtr)442 void Unit::handle_blocked_attack_unit(Unit* unitPtr, Unit* targetPtr)
443 {
444 if(action_para==targetPtr->sprite_recno && unitPtr->action_para==targetPtr->sprite_recno &&
445 action_mode==unitPtr->action_mode)
446 { //----------------- both attack the same target --------------------//
447 err_when(unit_array.is_deleted(action_para));
448 handle_blocked_same_target_attack(unitPtr, targetPtr);
449 }
450 else
451 search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); // recall A* algorithm
452 //search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); // recall A* algorithm
453 }
454 //----------- End of function Unit::handle_blocked_attack_unit -----------//
455
456
457 //--------- Begin of function Unit::handle_blocked_attack_firm ---------//
458 // handle the case that the way of this unit to the target firm is blocked by
459 // another unit
460 //
461 // <Unit*> unitPtr - the blocking unit
462 //
handle_blocked_attack_firm(Unit * unitPtr)463 void Unit::handle_blocked_attack_firm(Unit *unitPtr)
464 {
465 if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_para==unitPtr->action_para &&
466 action_mode==unitPtr->action_mode)
467 {
468 //------------- both attacks the same firm ------------//
469 Location *locPtr = world.get_loc(action_x_loc, action_y_loc);
470 if(!locPtr->is_firm())
471 stop2(KEEP_DEFENSE_MODE); // stop since firm is deleted
472 else
473 {
474 Firm *firmPtr = firm_array[action_para];
475 FirmInfo *firmInfo = firm_res[firmPtr->firm_id];
476
477 if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, firmInfo->loc_width, firmInfo->loc_height))
478 {
479 //------------ found surrounding place to attack the firm -------------//
480 if(mobile_type==UNIT_LAND)
481 set_move_to_surround(firmPtr->loc_x1, firmPtr->loc_y1, firmInfo->loc_width, firmInfo->loc_height, BUILDING_TYPE_FIRM_MOVE_TO);
482 else
483 attack_firm(firmPtr->loc_x1, firmPtr->loc_y1);
484 }
485 else // no surrounding place found, stop now
486 stop(KEEP_PRESERVE_ACTION);
487 }
488 }
489 else // let process_idle() handle it
490 stop();
491 }
492 //----------- End of function Unit::handle_blocked_attack_firm -----------//
493
494
495 //--------- Begin of function Unit::handle_blocked_attack_town ---------//
496 // handle the case that the way of this unit to the target town is blocked by
497 // another unit
498 //
499 // <Unit*> unitPtr - the blocking unit
500 //
handle_blocked_attack_town(Unit * unitPtr)501 void Unit::handle_blocked_attack_town(Unit *unitPtr)
502 {
503 if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_para==unitPtr->action_para &&
504 action_mode==unitPtr->action_mode)
505 {
506 //---------------- both attacks the same town ----------------------//
507 Location *locPtr = world.get_loc(action_x_loc, action_y_loc);
508 if(!locPtr->is_town())
509 stop2(KEEP_DEFENSE_MODE); // stop since town is deleted
510 else if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT))
511 {
512 //------------ found surrounding place to attack the town -------------//
513 Town *townPtr = town_array[action_para];
514 {
515 if(mobile_type==UNIT_LAND)
516 set_move_to_surround(townPtr->loc_x1, townPtr->loc_y1, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT, BUILDING_TYPE_TOWN_MOVE_TO);
517 else
518 attack_town(townPtr->loc_x1, townPtr->loc_y1);
519 }
520 }
521 else // no surrounding place found, stop now
522 stop(KEEP_PRESERVE_ACTION);
523 }
524 else
525 stop();
526 }
527 //----------- End of function Unit::handle_blocked_attack_town -----------//
528
529
530 //--------- Begin of function Unit::handle_blocked_attack_wall ---------//
531 // handle the case that the way of this unit to the target wall is blocked by
532 // another unit
533 //
534 // <Unit*> unitPtr - the blocking unit
535 //
handle_blocked_attack_wall(Unit * unitPtr)536 void Unit::handle_blocked_attack_wall(Unit *unitPtr)
537 {
538 if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_mode==unitPtr->action_mode)
539 {
540 //------------- both attacks the same wall ------------//
541 Location *locPtr = world.get_loc(action_x_loc, action_y_loc);
542 if(!locPtr->is_wall())
543 stop2(KEEP_DEFENSE_MODE); // stop since wall is deleted
544 else if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, 1, 1))
545 {
546 //------------ found surrounding place to attack the wall -------------//
547 if(mobile_type==UNIT_LAND)
548 set_move_to_surround(action_x_loc, action_y_loc, 1, 1, BUILDING_TYPE_WALL); // search for a unit only, not for a group
549 else
550 attack_wall(action_x_loc, action_y_loc);
551 }
552 else // no surrounding place found, stop now
553 stop(KEEP_PRESERVE_ACTION); // no space available, so stop to wait for space to attack the wall
554 }
555 else
556 {
557 if(action_x_loc==-1 || action_y_loc==-1)
558 stop();
559 else
560 set_wait();
561 }
562 }
563 //----------- End of function Unit::handle_blocked_attack_wall -----------//
564