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 : OUNITAT.CPP
22 //Description : Object Unit attack supporting functions
23 //Owner : Alex
24
25 #include <ALL.h>
26 #include <ONEWS.h>
27 #include <OWORLD.h>
28 #include <ONATION.h>
29 #include <OU_MARI.h>
30 #include <OSPY.h>
31 #include <OGAME.h>
32 #include <OTOWN.h>
33 #include <OF_CAMP.h>
34 #include <OF_MONS.h>
35 #include <OEFFECT.h>
36 #include <OMONSRES.h>
37 #include <OREBEL.h>
38 #include <OSERES.h>
39 #include <OSYS.h>
40 #include <OWARPT.h>
41
42 #ifdef NO_DEBUG_UNIT
43 #undef err_when
44 #undef err_here
45 #undef err_if
46 #undef err_else
47 #undef err_now
48 #define err_when(cond)
49 #define err_here()
50 #define err_if(cond)
51 #define err_else
52 #define err_now(msg)
53 #undef DEBUG
54 #endif
55
56 //----------------- Begin of function Unit::update_attack_path_dist -----------------//
57 // return 1 if it is time to update result path
58 // return 0 otherwise
59 //
update_attack_path_dist()60 int Unit::update_attack_path_dist()
61 {
62 if(result_path_dist<=6) //1-6
63 {
64 return 1;
65 }
66 else if(result_path_dist<=10) // 8, 10
67 {
68 return !((result_path_dist-6)%2);
69 }
70 else if(result_path_dist<=20) // 15, 20
71 {
72 return !((result_path_dist-10)%5);
73 }
74 else if(result_path_dist<=60) // 28, 36, 44, 52, 60
75 {
76 return !((result_path_dist-20)%8);
77 }
78 else if(result_path_dist<=90) // 75, 90
79 {
80 return !((result_path_dist-60)%15);
81 }
82 else // 110, 130, 150, etc
83 {
84 return !((result_path_dist-90)%20);
85 }
86
87 err_here();
88 return 0;
89 }
90 //---------- End of function Unit::update_attack_path_dist ----------//
91
92 //##### begin trevor 15/8 #######//
93
94 //----------------- Begin of function Unit::hit_target -----------------//
95 // The function can be called in by class Bullet since parent and target
96 // are specified.
97 //
98 // <Unit*> parentUnit is the unit attacking
99 // <Unit*> targetUnit is the unit being attacked
100 // <int> attackDamage is the damage done to the target Unit
101 // <short> parentNationRecno - the nation that ordered the hit
102 //
103 // ****************************** Warning ***********************************
104 // don't use any member variables of this unit. This unit may not be involved
105 // in the attack event
106 // **************************************************************************
107 //
hit_target(Unit * parentUnit,Unit * targetUnit,float attackDamage,short parentNationRecno)108 void Unit::hit_target(Unit* parentUnit, Unit* targetUnit, float attackDamage, short parentNationRecno)
109 {
110 short targetNationRecno = targetUnit->nation_recno;
111
112 //------------------------------------------------------------//
113 // if the attacked unit is in defense mode, order other available
114 // unit in the same camp to help this unit
115 // Note : checking for nation_recno since one unit can attack units
116 // in same nation by bullet accidentally
117 //------------------------------------------------------------//
118 if(parentUnit && parentUnit->cur_action!=SPRITE_DIE && parentUnit->is_visible() &&
119 parentNationRecno!=targetNationRecno && parentUnit->nation_can_attack(targetNationRecno) &&
120 targetUnit->in_auto_defense_mode())
121 {
122 err_when(targetUnit->action_misc!=ACTION_MISC_DEFENSE_CAMP_RECNO || !targetUnit->action_misc_para);
123 if(!firm_array.is_deleted(targetUnit->action_misc_para))
124 {
125 Firm *firmPtr = firm_array[targetUnit->action_misc_para];
126 if(firmPtr->firm_id==FIRM_CAMP)
127 {
128 err_when(firmPtr->firm_id!=FIRM_CAMP);
129 FirmCamp *campPtr = firmPtr->cast_to_FirmCamp();
130 campPtr->defense(parentUnit->sprite_recno);
131 }
132 }
133 else
134 targetUnit->clear_unit_defense_mode();
135 }
136
137 // ---------- add indicator on the map ----------//
138 if( nation_array.player_recno && targetUnit->is_own() )
139 war_point_array.add_point(targetUnit->next_x_loc(),targetUnit->next_y_loc());
140
141 //-----------------------------------------------------------------------//
142 // decrease the hit points of the target Unit
143 //-----------------------------------------------------------------------//
144 #define DEFAULT_ARMOR 4
145 #define DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN (float) DEFAULT_ARMOR / ATTACK_SLOW_DOWN
146 #define ONE_OVER_ATTACK_SLOW_DOWN (float) 1/ATTACK_SLOW_DOWN
147 #define COMPARE_POINT DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN + ONE_OVER_ATTACK_SLOW_DOWN
148
149 //-*********** simulate aat ************-//
150 #ifdef DEBUG
151 if(!debug_sim_game_type)
152 {
153 #endif
154 if( attackDamage >= COMPARE_POINT )
155 targetUnit->hit_points -= attackDamage - DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN;
156 else
157 targetUnit->hit_points -= MIN(attackDamage,ONE_OVER_ATTACK_SLOW_DOWN); // in case attackDamage = 0, no hit_point is reduced
158 #ifdef DEBUG
159 }
160 #endif
161 //-*********** simulate aat ************-//
162
163 Nation *parentNationPtr = parentNationRecno ? nation_array[parentNationRecno] : NULL;
164 Nation *targetNationPtr = targetNationRecno ? nation_array[targetNationRecno] : NULL;
165 char targetUnitClass = unit_res[targetUnit->unit_id]->unit_class;
166
167 if( targetUnit->hit_points <= 0 )
168 {
169 targetUnit->hit_points = (float) 0;
170
171 //---- if the unit killed is a human unit -----//
172
173 if( targetUnit->race_id )
174 {
175 //---- if the unit killed is a town defender unit -----//
176
177 if( targetUnit->is_civilian() && targetUnit->in_defend_town_mode() )
178 {
179 if( targetNationRecno )
180 {
181 targetNationPtr->civilian_killed(targetUnit->race_id, 0);
182 targetNationPtr->own_civilian_killed++;
183 }
184
185 if( parentNationPtr )
186 {
187 parentNationPtr->civilian_killed(targetUnit->race_id, 1);
188 parentNationPtr->enemy_civilian_killed++;
189 }
190 }
191 else if( targetUnit->is_civilian() && targetUnit->skill.combat_level<20 ) //--- mobile civilian ---//
192 {
193 if( targetNationRecno )
194 {
195 targetNationPtr->civilian_killed(targetUnit->race_id, 0);
196 targetNationPtr->own_civilian_killed++;
197 }
198
199 if( parentNationPtr )
200 {
201 parentNationPtr->civilian_killed(targetUnit->race_id, 0);
202 parentNationPtr->enemy_civilian_killed++;
203 }
204 }
205 else //---- if the unit killed is a soldier -----//
206 {
207 if( targetNationRecno )
208 targetNationPtr->own_soldier_killed++;
209
210 if( parentNationPtr )
211 parentNationPtr->enemy_soldier_killed++;
212 }
213 }
214
215 //--------- if it's a non-human unit ---------//
216
217 else
218 {
219 switch( unit_res[targetUnit->unit_id]->unit_class )
220 {
221 case UNIT_CLASS_WEAPON:
222 if( parentNationPtr )
223 parentNationPtr->enemy_weapon_destroyed++;
224
225 if( targetNationRecno )
226 targetNationPtr->own_weapon_destroyed++;
227 break;
228
229
230 case UNIT_CLASS_SHIP:
231 if( parentNationPtr )
232 parentNationPtr->enemy_ship_destroyed++;
233
234 if( targetNationRecno )
235 targetNationPtr->own_ship_destroyed++;
236 break;
237 }
238
239 //---- if the unit destroyed is a trader or caravan -----//
240
241 if( targetUnit->unit_id == UNIT_CARAVAN || // killing a caravan is resented by all races
242 targetUnit->unit_id == UNIT_VESSEL )
243 {
244 // Race-Id of 0 means a loyalty penalty applied for all races
245 if( targetNationRecno )
246 targetNationPtr->civilian_killed(0, -1);
247
248 if( parentNationPtr )
249 parentNationPtr->civilian_killed(0, 3);
250 }
251 }
252
253 return;
254 }
255
256 if(parentUnit!=NULL && parentNationRecno!=targetNationRecno)
257 parentUnit->gain_experience(); // gain experience to increase combat level
258
259 //-----------------------------------------------------------------------//
260 // action of the target to take
261 //-----------------------------------------------------------------------//
262 if( !parentUnit ) // do nothing if parent is dead
263 return;
264
265 if( parentUnit->cur_action==SPRITE_DIE ) // skip for explosive cart
266 {
267 err_when(parentUnit->unit_id!=UNIT_EXPLOSIVE_CART);
268 return;
269 }
270
271 if( targetNationRecno == parentNationRecno ) // the target and the attacker's nations are different (it's possible that when a unit who has just changed nation has its bullet hitting its own nation)
272 return;
273
274 //------- two nations at war ---------//
275
276 if( parentNationPtr && targetNationRecno )
277 {
278 parentNationPtr->set_at_war_today();
279 targetNationPtr->set_at_war_today(parentUnit->sprite_recno);
280 }
281
282 //-------- increase battling fryhtan score --------//
283
284 if( parentNationPtr && targetUnitClass==UNIT_CLASS_MONSTER )
285 {
286 parentNationPtr->kill_monster_score += (float) 0.1;
287 }
288
289 //------ call target unit being attack functions -------//
290
291 if( targetNationRecno )
292 {
293 targetNationPtr->being_attacked(parentNationRecno);
294
295 if( targetUnit->ai_unit )
296 {
297 if( targetUnit->rank_id >= RANK_GENERAL )
298 targetUnit->ai_leader_being_attacked(parentUnit->sprite_recno);
299
300 if( unit_res[targetUnit->unit_id]->unit_class == UNIT_CLASS_SHIP )
301 ((UnitMarine*)targetUnit)->ai_ship_being_attacked(parentUnit->sprite_recno);
302 }
303
304 //--- if a member in a troop is under attack, ask for other troop members to help ---//
305
306 if( info.game_date%2 == sprite_recno%2 )
307 {
308 if( targetUnit->leader_unit_recno ||
309 (targetUnit->team_info && targetUnit->team_info->member_count > 1) )
310 {
311 if( !unit_array.is_deleted(parentUnit->sprite_recno) ) // it is possible that parentUnit is dying right now
312 {
313 targetUnit->ask_team_help_attack(parentUnit);
314 }
315 }
316 }
317 }
318
319 //--- increase reputation of the nation that attacks monsters ---//
320
321 else if( targetUnitClass == UNIT_CLASS_MONSTER )
322 {
323 if( parentNationPtr )
324 parentNationPtr->change_reputation(REPUTATION_INCREASE_PER_ATTACK_MONSTER);
325 }
326
327 //------------------------------------------//
328
329 if(!targetUnit->can_attack()) // no action if the target unit is unable to attack
330 return;
331
332 err_when(!targetUnit->can_attack());
333
334 targetUnit->unit_auto_guarding(parentUnit);
335 }
336 //---------- End of function Unit::hit_target ----------//
337
338 //##### end trevor 15/8 #######//
339
340
341 //----------- Begin of function Unit::unit_auto_guarding ---------------//
342 // the unit attacks the unit attacking it
343 //
344 // <Unit*> attackUnit - the unit attacking this unit
345 //
unit_auto_guarding(Unit * attackUnit)346 void Unit::unit_auto_guarding(Unit *attackUnit)
347 {
348 if( force_move_flag )
349 return;
350
351 //##### begin trevor 9/10 ########//
352
353 //---------------------------------------//
354 //
355 // If the aggressive_mode is off, then don't
356 // fight back when the unit is moving, only
357 // fight back when the unit is already fighting
358 // or is idle.
359 //
360 //---------------------------------------//
361
362 if( !aggressive_mode && cur_action != SPRITE_ATTACK &&
363 cur_action != SPRITE_IDLE )
364 {
365 return;
366 }
367
368 //##### begin trevor 9/10 ########//
369
370 //--------------------------------------------------------------------//
371 // decide attack or not
372 //--------------------------------------------------------------------//
373
374 int changeToAttack=0;
375 if(cur_action==SPRITE_ATTACK || (sprite_info->need_turning && cur_action==SPRITE_TURN &&
376 (abs(next_x_loc()-action_x_loc)<attack_range || abs(next_y_loc()-action_y_loc)<attack_range)))
377 {
378 err_when(cur_x!=next_x || cur_y!=next_y);
379 if(action_mode!=ACTION_ATTACK_UNIT)
380 changeToAttack++; //else continue to attack the target unit
381 else
382 {
383 err_when(!action_para);
384 if(unit_array.is_deleted(action_para))
385 changeToAttack++; // attack new target
386 }
387 }
388 else if(cur_action!=SPRITE_DIE)// && abs(cur_x-next_x)<spriteInfo->speed && abs(cur_y-next_y)<spriteInfo->speed)
389 {
390 changeToAttack++;
391 /*if(!ai_unit) // player unit
392 {
393 if(action_mode!=ACTION_ATTACK_UNIT)
394 changeToAttack++; //else continue to attack the target unit
395 else
396 {
397 err_when(!action_para);
398 if(unit_array.is_deleted(action_para))
399 changeToAttack++; // attack new target
400 }
401 }
402 else
403 changeToAttack++;*/
404 }
405
406 if(!changeToAttack)
407 {
408 if(ai_unit) // allow ai unit to select target to attack
409 {
410 //------------------------------------------------------------//
411 // conditions to let the unit escape
412 //------------------------------------------------------------//
413 //-************* codes here ************-//
414
415 //------------------------------------------------------------//
416 // select the weaker target to attack first, if more than one
417 // unit attack this unit
418 //------------------------------------------------------------//
419 int attackXLoc = attackUnit->next_x_loc();
420 int attackYLoc = attackUnit->next_y_loc();
421
422 int attackDistance = cal_distance(attackXLoc, attackYLoc, attackUnit->sprite_info->loc_width,
423 attackUnit->sprite_info->loc_height);
424 if(attackDistance==1) // only consider close attack
425 {
426 err_when(!action_para || unit_array.is_deleted(action_para));
427 Unit *targetUnit = unit_array[action_para];
428 if(targetUnit->hit_points > attackUnit->hit_points) // the new attacker is weaker
429 attack_unit(attackUnit->sprite_recno);
430 }
431 }
432
433 return;
434 }
435
436 //--------------------------------------------------------------------//
437 // cancel AI actions
438 //--------------------------------------------------------------------//
439 if( ai_action_id )
440 nation_array[nation_recno]->action_failure(ai_action_id, sprite_recno);
441
442 if(in_auto_defense_mode())
443 set_search_tries(AUTO_DEFENSE_SEARCH_TRIES);
444
445 if(!attackUnit->is_visible())
446 return;
447
448 //--------------------------------------------------------------------------------//
449 // checking for ship processing trading
450 //--------------------------------------------------------------------------------//
451 if(sprite_info->sprite_sub_type=='M') //**** BUGHERE, is sprite_sub_type really representing UNIT_MARINE???
452 {
453 UnitInfo* unitInfo = unit_res[unit_id];
454 if(unitInfo->carry_goods_capacity)
455 {
456 UnitMarine *shipPtr = (UnitMarine*) this;
457 if(shipPtr->auto_mode && shipPtr->stop_defined_num>1)
458 {
459 int targetXLoc = attackUnit->next_x_loc();
460 int targetYLoc = attackUnit->next_y_loc();
461 SpriteInfo *targetSpriteInfo = attackUnit->sprite_info;
462 int attackDistance = cal_distance(targetXLoc, targetYLoc, targetSpriteInfo->loc_width, targetSpriteInfo->loc_height);
463 int maxAttackRange = max_attack_range();
464 if(maxAttackRange<attackDistance)
465 return; // can't attack the target
466 }
467 }
468 }
469
470 switch(action_mode2)
471 {
472 case ACTION_AUTO_DEFENSE_DETECT_TARGET: case ACTION_AUTO_DEFENSE_BACK_CAMP:
473 action_mode2 = ACTION_AUTO_DEFENSE_ATTACK_TARGET;
474 break;
475
476 case ACTION_DEFEND_TOWN_DETECT_TARGET: case ACTION_DEFEND_TOWN_BACK_TOWN:
477 action_mode2 = ACTION_DEFEND_TOWN_ATTACK_TARGET;
478 break;
479
480 case ACTION_MONSTER_DEFEND_DETECT_TARGET: case ACTION_MONSTER_DEFEND_BACK_FIRM:
481 action_mode2 = ACTION_MONSTER_DEFEND_ATTACK_TARGET;
482 break;
483 }
484
485 //##### trevor 9/10 #######//
486
487 save_original_action();
488
489 //----------------------------------------------------------//
490 // set the original location of the attacking target when
491 // the attack() function is called, action_x_loc2 & action_y_loc2
492 // will change when the unit move, but these two will not.
493 //----------------------------------------------------------//
494
495 original_target_x_loc = attackUnit->next_x_loc();
496 original_target_y_loc = attackUnit->next_y_loc();
497
498 //##### trevor 9/10 #######//
499
500 if(!unit_array.is_deleted(attackUnit->sprite_recno))
501 attack_unit(attackUnit->sprite_recno);
502 }
503 //---------- End of function Unit::unit_auto_guarding ----------//
504
505
506 //----------- Begin of function Unit::hit_building ---------------//
507 //
508 // note: If parentUnit==NULL, the attacking unit is already dead.
509 // In range attack, the unit calling this function may not be the
510 // attacking unit.
511 //
512 // <Unit*> attackUnit - the attacking unit
513 // <int> target?Loc - the target building location
514 // <int> attackDamage - the actual damage made
515 // <short> attackNationRecno - the nation that ordered the hit
516 //
517 // ****************************** Warning ***********************************
518 // don't use any member variables of this unit. This unit may not be involved
519 // in the attack event
520 // **************************************************************************
521 //
hit_building(Unit * attackUnit,int targetXLoc,int targetYLoc,float attackDamage,short attackNationRecno)522 void Unit::hit_building(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage, short attackNationRecno)
523 {
524 Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
525
526 if(locPtr->is_firm())
527 hit_firm(attackUnit, targetXLoc, targetYLoc, attackDamage, attackNationRecno);
528 else if( locPtr->is_town() )
529 hit_town(attackUnit, targetXLoc, targetYLoc, attackDamage, attackNationRecno);
530 }
531 //---------- End of function Unit::hit_building ----------//
532
533 //##### begin trevor 15/8 #######//
534
535
536 //------------ Begin of function Unit::hit_firm --------------//
537 //
538 // note: If parentUnit==NULL, the attacking unit is already dead.
539 // In range attack, the unit calling this function may not be the
540 // attacking unit.
541 //
542 // <Unit*> attackUnit - the attacking unit
543 // <int> target?Loc - the target building location
544 // <int> attackDamage - the actual damage made
545 // <short> attackNationRecno - the nation that ordered the hit
546 //
547 // ****************************** Warning ***********************************
548 // don't use any member variables of this unit. This unit may not be involved
549 // in the attack event
550 // **************************************************************************
551 //
hit_firm(Unit * attackUnit,int targetXLoc,int targetYLoc,float attackDamage,short attackNationRecno)552 void Unit::hit_firm(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage, short attackNationRecno)
553 {
554 Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
555 if(!locPtr->is_firm())
556 return; // do nothing if no firm there
557
558 //----------- attack firm ------------//
559 err_when(!locPtr->firm_recno());
560 Firm *targetFirm = firm_array[locPtr->firm_recno()];
561 err_when(!targetFirm);
562
563 //------------------------------------------------------------------------------//
564 // change relation to hostile
565 // check for NULL to skip unhandled case by bullets
566 // check for SPRITE_DIE to skip the case by EXPLOSIVE_CART
567 //------------------------------------------------------------------------------//
568 if( attackUnit!=NULL && attackUnit->cur_action!=SPRITE_DIE &&
569 targetFirm->nation_recno != attackNationRecno ) // the target and the attacker's nations are different (it's possible that when a unit who has just changed nation has its bullet hitting its own nation)
570 {
571 if( attackNationRecno && targetFirm->nation_recno )
572 {
573 //### trevor 29/9 ###//
574 nation_array[attackNationRecno]->set_at_war_today();
575 nation_array[targetFirm->nation_recno]->set_at_war_today(attackUnit->sprite_recno);
576 //### trevor 29/9 ###//
577 }
578
579 if( targetFirm->nation_recno )
580 nation_array[targetFirm->nation_recno]->being_attacked(attackNationRecno);
581
582 //------------ auto defense -----------------//
583 if(attackUnit->is_visible())
584 targetFirm->auto_defense(attackUnit->sprite_recno);
585
586 if( attackNationRecno != targetFirm->nation_recno )
587 attackUnit->gain_experience(); // gain experience to increase combat level
588
589 targetFirm->being_attacked(attackUnit->sprite_recno);
590
591 //------ increase battling fryhtan score -------//
592
593 if( attackNationRecno && targetFirm->firm_id == FIRM_MONSTER )
594 nation_array[attackNationRecno]->kill_monster_score += (float) 0.01;
595 }
596
597 //---------- add indicator on the map ----------//
598
599 // ###### begin Gilbert 6/10 #######//
600 if( nation_array.player_recno && targetFirm->own_firm() )
601 war_point_array.add_point(targetFirm->center_x, targetFirm->center_y);
602 // ###### end Gilbert 6/10 #######//
603
604 //---------- damage to the firm ------------//
605
606 targetFirm->hit_points -= attackDamage/3; // /3 so that it takes longer to destroy a firm
607
608 //######## begin trevor 25/8 ##########//
609
610 if(targetFirm->hit_points <= 0)
611 {
612 targetFirm->hit_points = (float) 0;
613
614 se_res.sound(targetFirm->center_x, targetFirm->center_y, 1,
615 'F', targetFirm->firm_id, "DIE" );
616
617 if( targetFirm->nation_recno == nation_array.player_recno )
618 news_array.firm_destroyed(targetFirm->firm_recno, attackUnit, attackNationRecno);
619
620 if( targetFirm->nation_recno )
621 {
622 if( attackNationRecno )
623 nation_array[attackNationRecno]->enemy_firm_destroyed++;
624
625 nation_array[targetFirm->nation_recno]->own_firm_destroyed++;
626 }
627
628 else if( targetFirm->firm_id == FIRM_MONSTER )
629 {
630 news_array.monster_firm_destroyed( ((FirmMonster*)targetFirm)->monster_id, targetFirm->center_x, targetFirm->center_y );
631 }
632
633 firm_array.del_firm(targetFirm->firm_recno);
634 }
635
636 //######## end trevor 25/8 ##########//
637 }
638 //---------- End of function Unit::hit_firm ----------//
639
640
641 //--------- Begin of function Unit::hit_town ---------//
642 //
643 // note: If attackUnit==NULL, the attacking unit is already dead.
644 // In range attack, the unit calling this function may not be the
645 // attacking unit.
646 //
647 // <Unit*> attackUnit - the attacking unit
648 // <int> target?Loc - the target building location
649 // <int> attackDamage - the actual damage made
650 // <short> attackNationRecno - the nation that ordered the hit
651 //
652 // ****************************** Warning ***********************************
653 // don't use any member variables of this unit. This unit may not be involved
654 // in the attack event
655 // **************************************************************************
656 //
hit_town(Unit * attackUnit,int targetXLoc,int targetYLoc,float attackDamage,short attackNationRecno)657 void Unit::hit_town(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage, short attackNationRecno)
658 {
659 Location *locPtr = world.get_loc(targetXLoc, targetYLoc);
660
661 if(!locPtr->is_town())
662 return; // do nothing if no town there
663
664 //----------- attack town ----------//
665
666 err_when(!locPtr->town_recno());
667
668 Town *targetTown = town_array[locPtr->town_recno()];
669 int targetTownRecno = targetTown->town_recno;
670 int targetTownNameId = targetTown->town_name_id;
671 int targetTownXLoc = targetTown->center_x;
672 int targetTownYLoc = targetTown->center_y;
673
674 // ---------- add indicator on the map ----------//
675 // ###### begin Gilbert 6/10 #######//
676 if( nation_array.player_recno && targetTown->nation_recno == nation_array.player_recno )
677 war_point_array.add_point(targetTown->center_x, targetTown->center_y);
678 // ###### end Gilbert 6/10 #######//
679
680 //------------------------------------------------------------------------------//
681 // change relation to hostile
682 // check for NULL to skip unhandled case by bullets
683 // check for SPRITE_DIE to skip the case by EXPLOSIVE_CART
684 //------------------------------------------------------------------------------//
685 if( attackUnit!=NULL && attackUnit->cur_action!=SPRITE_DIE &&
686 targetTown->nation_recno != attackNationRecno ) // the target and the attacker's nations are different (it's possible that when a unit who has just changed nation has its bullet hitting its own nation)
687 {
688 int townNationRecno = targetTown->nation_recno;
689
690 //------- change to hostile relation -------//
691
692 if( attackNationRecno && targetTown->nation_recno )
693 {
694 //### trevor 29/9 ###//
695 nation_array[attackNationRecno]->set_at_war_today();
696 nation_array[targetTown->nation_recno]->set_at_war_today(attackUnit->sprite_recno);
697 //### trevor 29/9 ###//
698 }
699
700 if( targetTown->nation_recno)
701 {
702 nation_array[targetTown->nation_recno]->being_attacked(attackNationRecno);
703 }
704
705 news_array.disable(); // don't add the town abandon news that might be called by Town::dec_pop() as the town is actually destroyed not abandoned
706
707 targetTown->being_attacked(attackUnit->sprite_recno, attackDamage);
708
709 news_array.enable();
710
711 //------ if the town is destroyed, add a news --------//
712
713 if( town_array.is_deleted(targetTownRecno) &&
714 townNationRecno == nation_array.player_recno )
715 {
716 news_array.town_destroyed(targetTownNameId, targetTownXLoc, targetTownYLoc, attackUnit, attackNationRecno);
717 }
718
719 //---------- gain experience --------//
720
721 if( attackNationRecno != targetTown->nation_recno )
722 attackUnit->gain_experience(); // gain experience to increase combat level
723
724 //------------ auto defense -----------------//
725
726 if( !firm_array.is_deleted(targetTownRecno) )
727 targetTown->auto_defense(attackUnit->sprite_recno);
728 }
729 }
730 //---------- End of function Unit::hit_town ----------//
731
732 //##### end trevor 15/8 #######//
733
734
735 //--------- Begin of function Unit::hit_wall -----------//
736 //
737 // <Unit*> attackUnit - the attacking unit
738 // <int> target?Loc - the targeted wall location
739 // <int> attackDamage - the actual damage made
740 // <short> attackNationRecno - the nation that ordered the hit
741 //
742 // ****************************** Warning ***********************************
743 // don't use any member variables of this unit. This unit may not be involved
744 // in the attack event
745 // **************************************************************************
746 //
hit_wall(Unit * attackUnit,int targetXLoc,int targetYLoc,float attackDamage,short attackNationRecno)747 void Unit::hit_wall(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage, short attackNationRecno)
748 {
749 Location *locPtr = world.get_loc(targetXLoc, targetYLoc);
750 err_when(!locPtr->is_wall());
751
752 //######## begin trevor 25/6 #########//
753 /*
754 if(attackUnit!=NULL)
755 attackUnit->change_relation(attackNationRecno, locPtr->wall_nation_recno(), NATION_HOSTILE);
756 */
757 //######## end trevor 25/6 #########//
758
759 if( !locPtr->attack_wall((int)attackDamage) )
760 world.correct_wall(targetXLoc, targetYLoc);
761 }
762 //---------- End of function Unit::hit_wall ----------//
763
764
765 //--------- Begin of function Unit::cal_distance ---------//
766 // calculate the distance from this unit to the target
767 // (assume the size of this unit is a square)
768 //
769 // <int> targetXLoc - x location of the target
770 // <int> targetYLoc - y location of the target
771 // <int> targetWidth - target width
772 // <int> targetHeight - target height
773 //
cal_distance(int targetXLoc,int targetYLoc,int targetWidth,int targetHeight)774 int Unit::cal_distance(int targetXLoc, int targetYLoc, int targetWidth, int targetHeight)
775 {
776 int curXLoc = next_x_loc();
777 int curYLoc = next_y_loc();
778 int dispX=0, dispY=0;
779
780 if(curXLoc<targetXLoc)
781 dispX = (targetXLoc - curXLoc - sprite_info->loc_width) + 1;
782 else if((dispX=curXLoc-targetXLoc-targetWidth+1)<0)
783 dispX = 0;
784 err_when(dispX<0 || dispX>MAX_WORLD_X_LOC);
785
786 if(curYLoc<targetYLoc)
787 dispY = (targetYLoc - curYLoc - sprite_info->loc_height) + 1;
788 else if((dispY=curYLoc-targetYLoc-targetHeight+1)<0)
789 {
790 err_when(mobile_type!=UNIT_AIR && !dispX); // inside the target
791 return dispX;
792 }
793 err_when(dispY<0 || dispY>MAX_WORLD_Y_LOC);
794
795 return (dispX>=dispY)? dispX : dispY;
796 }
797 //----------- End of function Unit::cal_distance -----------//
798
799
800 //------------ Begin of function Unit::actual_damage --------------//
801 //
802 // return: return the actual hit damage this unit can do to a target.
803 //
actual_damage()804 float Unit::actual_damage()
805 {
806 AttackInfo *attackInfo = attack_info_array+cur_attack;
807
808 int attackDamage = attackInfo->attack_damage;
809
810 //-------- pierce damage --------//
811
812 attackDamage += misc.random(3) + attackInfo->pierce_damage
813 * misc.random(skill.combat_level-attackInfo->combat_level)
814 / (100-attackInfo->combat_level);
815
816 //--- if this unit is led by a general, its attacking ability is influenced by the general ---//
817 //
818 // The unit's attacking ability is increased by a percentage equivalent to
819 // the leader unit's leadership.
820 //
821 //------------------------------------------------------------------------//
822
823 if( is_leader_in_range() )
824 {
825 Unit *leaderUnit = unit_array[leader_unit_recno];
826 attackDamage += attackDamage * leaderUnit->skill.skill_level / 100;
827 }
828
829 return (float) attackDamage / ATTACK_SLOW_DOWN; // lessen all attacking damages, thus slowing down all battles.
830 }
831 //------------ End of function Unit::actual_damage --------------//
832
833
834 //--------- Begin of function Unit::gain_experience ---------//
gain_experience()835 void Unit::gain_experience()
836 {
837 if(unit_res[unit_id]->unit_class != UNIT_CLASS_HUMAN)
838 return; // no experience gain if unit is not human
839
840 //---- increase the unit's contribution to the nation ----//
841
842 if( nation_contribution < MAX_NATION_CONTRIBUTION )
843 {
844 nation_contribution++;
845
846 err_when( nation_contribution < 0 ); // overflow
847 }
848
849 //------ increase combat skill -------//
850
851 err_when(skill.combat_level<0 || skill.combat_level>100);
852
853 inc_minor_combat_level(6);
854
855 //--- if this is a soldier led by a commander, increase the leadership of its commander -----//
856
857 if( leader_unit_recno )
858 {
859 Unit* leaderUnit = unit_array[leader_unit_recno];
860 int leaderXLoc= -1, leaderYLoc;
861
862 if( leaderUnit->is_visible() )
863 {
864 leaderXLoc = cur_x_loc();
865 leaderYLoc = cur_y_loc();
866 }
867 else if( leaderUnit->unit_mode == UNIT_MODE_OVERSEE )
868 {
869 Firm* firmPtr = firm_array[leaderUnit->unit_mode_para];
870
871 leaderXLoc = firmPtr->center_x;
872 leaderYLoc = firmPtr->center_y;
873 }
874 else
875 leaderXLoc = -1;
876
877 if( leaderXLoc >= 0 &&
878 misc.points_distance( cur_x_loc(), cur_y_loc(), leaderXLoc, leaderYLoc ) <= EFFECTIVE_LEADING_DISTANCE )
879 {
880 leaderUnit->inc_minor_skill_level(1);
881
882 //-- give additional increase if the leader has skill potential on leadership --//
883
884 if( leaderUnit->skill.skill_potential > 0 )
885 {
886 if( misc.random(10-leaderUnit->skill.skill_potential/10)==0 )
887 leaderUnit->inc_minor_skill_level(5);
888 }
889 }
890
891 //--- if this soldier has leadership potential and is led by a commander ---//
892 //--- he learns leadership by watching how the commander commands the troop --//
893
894 if( skill.skill_potential > 0 )
895 {
896 if( misc.random(10-skill.skill_potential/10)==0 )
897 inc_minor_skill_level(5);
898 }
899 }
900 }
901 //------------ End of function Unit::gain_experience --------------//
902
903
904 //--------- Begin of function Unit::can_attack ---------//
905 // return 1 if can attack
906 // return 0 otherwise
907 //
908 /*int Unit::can_attack()
909 {
910 return (can_attack_flag && attack_count);
911 }*/
912 //------------ End of function Unit::can_attack --------------//
913
914
915 //--------- Begin of function Unit::nation_can_attack ---------//
916 // return 1 for able to attack this nation i.e. relation is not
917 // friendly or alliance
918 // return 0 otherwise
919 //
920 // Whether this unit can attack others with the specified nation recno
921 //
922 // <short> nationRecno - nation recno to be checked
923 //
nation_can_attack(short nationRecno)924 int Unit::nation_can_attack(short nationRecno)
925 {
926 if(!ai_unit)
927 {
928 //return 1;
929 if( game.game_mode == GAME_TEST ) // in testing games, player units can attack their own units
930 return 1;
931 else
932 return nationRecno!=nation_recno; // able to attack all nation except our own nation
933 }
934 else if(nation_recno == nationRecno)
935 return 0; // ai unit don't attack its own nation, except special order
936
937 if(!nation_recno || !nationRecno)
938 return 1; // true if either nation is independent
939
940 Nation *nationPtr = nation_array[nation_recno];
941
942 char relatinStatus = nationPtr->get_relation_status(nationRecno);
943 if(relatinStatus==NATION_FRIENDLY || relatinStatus==NATION_ALLIANCE)
944 return 0;
945
946 return 1;
947 }
948 //------------ End of function Unit::nation_can_attack --------------//
949
950
951 //--------- Begin of function Unit::independent_nation_can_attack ---------//
952 // Note: this unit should be an independent unit
953 //
954 // Should this independent unit attack other units with specified nation recno
955 //
956 // return 1 if decision is attacking
957 // return 0 otherwise
958 //
959 // <short> nationRecno - nation recno to be checked
960 //
independent_nation_can_attack(short nationRecno)961 int Unit::independent_nation_can_attack(short nationRecno)
962 {
963 err_when(nation_recno);
964
965 Town *townPtr;
966 FirmMonster *firmMonster;
967 Rebel *rebelPtr;
968
969 switch(unit_mode)
970 {
971 case UNIT_MODE_DEFEND_TOWN:
972 err_when(unit_mode_para<=0);
973 if(town_array.is_deleted(unit_mode_para))
974 return 0; // don't attack independent unit with no town
975
976 townPtr = town_array[unit_mode_para];
977
978 if( !townPtr->is_hostile_nation(nationRecno) )
979 return 0; // false if the indepentent unit don't want to attack us
980
981 break;
982
983 case UNIT_MODE_REBEL:
984 if(rebel_array.is_deleted(unit_mode_para))
985 return 0;
986
987 rebelPtr = rebel_array[unit_mode_para];
988
989 if( !rebelPtr->is_hostile_nation(nationRecno) )
990 return 0;
991
992 break;
993
994 //######## begin trevor 22/8 ##########//
995 case UNIT_MODE_MONSTER:
996 if( unit_mode_para==0 )
997 return nationRecno; // attack anything that is not independent
998
999 firmMonster = (FirmMonster*) firm_array[unit_mode_para];
1000
1001 err_when( firmMonster->firm_id != FIRM_MONSTER );
1002
1003 if( !firmMonster->is_hostile_nation(nationRecno) )
1004 return 0; // false if the indepentent unit don't want to attack us
1005
1006 break;
1007 //######## end trevor 22/8 ##########//
1008
1009 default:
1010 return 0;
1011 }
1012
1013 return 1;
1014 }
1015 //------------ End of function Unit::independent_nation_can_attack --------------//
1016
1017
1018 //--------- Begin of function Unit::choose_best_attack_mode ---------//
1019 // if the unit has more than one attack mode, select the suitable mode
1020 // to attack the target
1021 //
1022 // <int> attackDistance - the distance from the target
1023 // [char] targetMobileType - the target's mobile_type (default: UNIT_LAND)
1024 //
choose_best_attack_mode(int attackDistance,char targetMobileType)1025 void Unit::choose_best_attack_mode(int attackDistance, char targetMobileType)
1026 {
1027 //------------ enable/disable range attack -----------//
1028 //cur_attack = 0;
1029 //return;
1030
1031 //-------------------- define parameters -----------------------//
1032 uint8_t attackModeBeingUsed = cur_attack;
1033 err_when(attackModeBeingUsed<0 || attackModeBeingUsed>MAX_UNIT_ATTACK_TYPE);
1034 //UCHAR maxAttackRangeMode = 0;
1035 uint8_t maxAttackRangeMode = cur_attack;
1036 AttackInfo* attackInfoMaxRange = attack_info_array;
1037 AttackInfo* attackInfoChecking;
1038 AttackInfo* attackInfoSelected = attack_info_array+cur_attack;
1039
1040 //--------------------------------------------------------------//
1041 // If targetMobileType==UNIT_AIR or mobile_type==UNIT_AIR,
1042 // force to use range_attack.
1043 // If there is no range_attack, return 0, i.e. cur_attack=0
1044 //--------------------------------------------------------------//
1045 if(attack_count>1)
1046 {
1047 int canAttack = 0;
1048 int checkingDamageWeight, selectedDamageWeight;
1049
1050 for(uint8_t i=0; i<attack_count; i++)
1051 {
1052 if(attackModeBeingUsed==i)
1053 continue; // it is the mode already used
1054
1055 attackInfoChecking = attack_info_array+i;
1056 if(can_attack_with(attackInfoChecking) && attackInfoChecking->attack_range>=attackDistance)
1057 {
1058 //-------------------- able to attack ----------------------//
1059 canAttack = 1;
1060
1061 if(attackInfoSelected->attack_range<attackDistance)
1062 {
1063 attackModeBeingUsed = i;
1064 attackInfoSelected = attackInfoChecking;
1065 continue;
1066 }
1067
1068 checkingDamageWeight = attackInfoChecking->attack_damage;
1069 selectedDamageWeight = attackInfoSelected->attack_damage;
1070
1071 if(attackDistance==1 && (targetMobileType!=UNIT_AIR && mobile_type!=UNIT_AIR))
1072 {
1073 //------------ force to use close attack if possible -----------//
1074 if(attackInfoSelected->attack_range==attackDistance)
1075 {
1076 if(attackInfoChecking->attack_range==attackDistance && checkingDamageWeight>selectedDamageWeight)
1077 {
1078 attackModeBeingUsed = i; // choose the one with strongest damage
1079 attackInfoSelected = attackInfoChecking;
1080 }
1081 continue;
1082 }
1083 else if(attackInfoChecking->attack_range==1)
1084 {
1085 attackModeBeingUsed = i;
1086 attackInfoSelected = attackInfoChecking;
1087 continue;
1088 }
1089 }
1090
1091 //----------------------------------------------------------------------//
1092 // further selection
1093 //----------------------------------------------------------------------//
1094 if(checkingDamageWeight == selectedDamageWeight)
1095 {
1096 if(attackInfoChecking->attack_range<attackInfoSelected->attack_range)
1097 {
1098 if(attackInfoChecking->attack_range>1 || (targetMobileType!=UNIT_AIR && mobile_type!=UNIT_AIR))
1099 {
1100 //--------------------------------------------------------------------------//
1101 // select one with shortest attack_range
1102 //--------------------------------------------------------------------------//
1103 attackModeBeingUsed = i;
1104 attackInfoSelected = attackInfoChecking;
1105 }
1106 }
1107 }
1108 else
1109 {
1110 //--------------------------------------------------------------------------//
1111 // select one that can do the attacking immediately with the strongest damage point
1112 //--------------------------------------------------------------------------//
1113 attackModeBeingUsed = i;
1114 attackInfoSelected = attackInfoChecking;
1115 }
1116 }
1117
1118 if(!canAttack)
1119 {
1120 //------------------------------------------------------------------------------//
1121 // if unable to attack the target, choose the mode with longer attack_range and
1122 // heavier damage
1123 //------------------------------------------------------------------------------//
1124 if(can_attack_with(attackInfoChecking) &&
1125 (attackInfoChecking->attack_range>attackInfoMaxRange->attack_range ||
1126 (attackInfoChecking->attack_range==attackInfoMaxRange->attack_range &&
1127 attackInfoChecking->attack_damage>attackInfoMaxRange->attack_damage)))
1128 {
1129 maxAttackRangeMode = i;
1130 attackInfoMaxRange = attackInfoChecking;
1131 }
1132 }
1133 }
1134
1135 if(canAttack)
1136 cur_attack = attackModeBeingUsed; // choose the strongest damage mode if able to attack
1137 else
1138 cur_attack = maxAttackRangeMode; // choose the longest attack range if unable to attack
1139
1140 attack_range = attack_info_array[cur_attack].attack_range;
1141 err_when(final_dir<0 || final_dir>=MAX_SPRITE_DIR_TYPE);
1142 err_when(cur_attack>=attack_count || cur_attack<0);
1143 }
1144 else
1145 {
1146 cur_attack = 0; // only one mode is supported
1147 attack_range = attack_info_array[0].attack_range;
1148 return;
1149 }
1150 }
1151 //---------- End of function Unit::choose_best_attack_mode ----------//
1152
1153
1154 //------ Begin of function Unit::set_attack_dir ---------//
1155 // set direction for attacking
1156 //
1157 // <short> curX - x location of the unit
1158 // <short> curY - y location of the unit
1159 // <short> targetX - x location of the target
1160 // <short> targetY - y location of the target
1161 //
set_attack_dir(short curX,short curY,short targetX,short targetY)1162 void Unit::set_attack_dir(short curX, short curY, short targetX, short targetY)
1163 {
1164 int targetDir = get_dir(curX, curY, targetX, targetY);
1165 if(unit_res[unit_id]->unit_class==UNIT_CLASS_SHIP)
1166 {
1167 int attackDir1, attackDir2;
1168
1169 attackDir1 = (targetDir+2)%MAX_SPRITE_DIR_TYPE;
1170 attackDir2 = (targetDir+6)%MAX_SPRITE_DIR_TYPE;
1171
1172 if((attackDir1+8-final_dir)%MAX_SPRITE_DIR_TYPE <= (attackDir2+8-final_dir)%MAX_SPRITE_DIR_TYPE)
1173 final_dir = attackDir1;
1174 else
1175 final_dir = attackDir2;
1176
1177 attack_dir = targetDir;
1178 err_when(attack_dir<0 || attack_dir>=MAX_SPRITE_DIR_TYPE);
1179 }
1180 else
1181 {
1182 attack_dir = targetDir;
1183 set_dir(targetDir);
1184 }
1185 }
1186 //---------- End of function Unit::set_attack_dir ----------//
1187
1188
1189 //--------- Begin of function Unit::set_unreachable_location ---------//
1190 // used to set the bit in the unreachable_flag (16 bits)
1191 //
1192 // The 16 bits of the flag are used to represent the 16 location of a unit
1193 // as follows:
1194 //
1195 // 1 2 3 4 where x is the upper left corner of the unit.
1196 // 5 x 6 7 For 1x1 unit, 1,2,3,5,6,8,9,10 are meaningful
1197 // 8 9 10 11 For 2x2 unit, 1,2,3,4,5,7,8,11,12,13,14,15 are meaningful
1198 // 12 13 14 15
1199 //
set_unreachable_location(int xLoc,int yLoc)1200 void Unit::set_unreachable_location(int xLoc, int yLoc)
1201 {
1202 static unsigned short bitFlag[16] = {0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
1203 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000};
1204 /*int curXLoc = next_x_loc();
1205 int curYLoc = next_y_loc();
1206 int xDist = xLoc-curXLoc+1;
1207 int yDist = yLoc-curYLoc+1;
1208
1209 err_when(xDist<0 || xDist>3 || yDist<0 || yDist>3);
1210 char bitNo = yDist*4 + xDist;
1211
1212 err_when(bitNo==5);
1213 if(bitNo>5)
1214 bitNo--;
1215
1216 unreachable_flag |= bitFlag[bitNo];*/
1217 }
1218 //----------- End of function Unit::set_unreachable_location -----------//
1219
1220 //--------- Begin of function Unit::check_self_surround ---------//
1221 // Note : mobile_type used is this unit's mobile_type
1222 //
check_self_surround()1223 void Unit::check_self_surround()
1224 {
1225 /*err_when(sprite_info->loc_height!=sprite_info->loc_width);
1226 err_when(sprite_info->loc_width<1 || sprite_info->loc_width>2);
1227
1228 Location *locPtr;
1229 int width = sprite_info->loc_width;
1230 int curXLoc = next_x_loc();
1231 int curYLoc = next_y_loc();
1232 int startCount = (width==1) ? 2 : 5;
1233 int endCount = (width==1) ? 9 : 16;
1234 int xShift, yShift;
1235
1236 self_surround_flag = 0;
1237
1238 for(int i=startCount; i<=endCount; i++)
1239 {
1240 misc.cal_move_around_a_point(i, width, width, xShift, yShift);
1241 locPtr = world.get_loc(curXLoc+xShift, curYLoc+yShift);
1242
1243 if(!locPtr->can_move(mobile_type))
1244 misc.set_surround_bit(self_surround_flag, i-startCount);
1245 }*/
1246 }
1247 //----------- End of function Unit::check_self_surround -----------//
1248
1249
1250 //----------- Begin of function Unit::cycle_eqv_attack -----------//
cycle_eqv_attack()1251 void Unit::cycle_eqv_attack()
1252 {
1253 int trial = MAX_UNIT_ATTACK_TYPE+2;
1254 if( attack_info_array[cur_attack].eqv_attack_next > 0)
1255 {
1256 do
1257 {
1258 cur_attack = attack_info_array[cur_attack].eqv_attack_next-1;
1259 err_when(--trial == 0);
1260 } while( !can_attack_with(cur_attack) );
1261 }
1262 else
1263 {
1264 if( !can_attack_with(cur_attack) )
1265 {
1266 err_here();
1267 // force to search again
1268 char attackRange = char(attack_info_array[cur_attack].attack_range);
1269 err_when(attackRange != attack_range); // redundant check
1270 AttackInfo *attackInfo = attack_info_array;
1271 for(int i = 0; i < attack_count; ++i, ++attackInfo)
1272 {
1273 if( attackInfo->attack_range >= attackRange &&
1274 can_attack_with(attackInfo))
1275 {
1276 cur_attack = i;
1277 break;
1278 }
1279 err_when(i >= attack_count); // not found
1280 }
1281 }
1282 }
1283 err_when(cur_attack < 0 || cur_attack >= attack_count);
1284 }
1285 //----------- End of function Unit::cycle_eqv_attack -----------//
1286
1287
1288 //----------- Begin of function Unit::max_attack_range -----------//
1289 // return the maximum attack range the unit can make
1290 //
1291 //
1292 //
max_attack_range()1293 int Unit::max_attack_range()
1294 {
1295 int maxRange=0;
1296
1297 AttackInfo *attackInfo = attack_info_array;
1298 for(int i=0; i<attack_count; i++, attackInfo++)
1299 {
1300 if(can_attack_with(attackInfo) &&
1301 attackInfo->attack_range>maxRange)
1302 maxRange = attackInfo->attack_range;
1303 }
1304
1305 return maxRange;
1306 }
1307 //----------- End of function Unit::max_attack_range -----------//
1308
1309
1310 //----------- Begin of function Unit::can_attack_with -------//
can_attack_with(int i)1311 int Unit::can_attack_with(int i)
1312 {
1313 err_when( i< 0 || i >= attack_count);
1314 AttackInfo *attackInfo = attack_info_array+i;
1315 return( skill.combat_level >= attackInfo->combat_level &&
1316 cur_power >= attackInfo->min_power);
1317
1318 }
1319
1320
can_attack_with(AttackInfo * attackInfo)1321 int Unit::can_attack_with(AttackInfo *attackInfo)
1322 {
1323 return( skill.combat_level >= attackInfo->combat_level &&
1324 cur_power >= attackInfo->min_power);
1325 }
1326 //----------- End of function Unit::can_attack_with -------//
1327
1328
1329 //----------- Begin of function Unit::get_hit_x_y -------//
get_hit_x_y(short * xPtr,short * yPtr)1330 void Unit::get_hit_x_y(short *xPtr, short *yPtr)
1331 {
1332 switch(cur_dir)
1333 {
1334 case 0: // north
1335 *xPtr = cur_x;
1336 *yPtr = cur_y - ZOOM_LOC_HEIGHT;
1337 break;
1338 case 1: // north east
1339 *xPtr = cur_x + ZOOM_LOC_WIDTH;
1340 *yPtr = cur_y - ZOOM_LOC_HEIGHT;
1341 break;
1342 case 2: // east
1343 *xPtr = cur_x + ZOOM_LOC_WIDTH;
1344 *yPtr = cur_y;
1345 break;
1346 case 3: // south east
1347 *xPtr = cur_x + ZOOM_LOC_WIDTH;
1348 *yPtr = cur_y + ZOOM_LOC_HEIGHT;
1349 break;
1350 case 4: // south
1351 *xPtr = cur_x;
1352 *yPtr = cur_y + ZOOM_LOC_HEIGHT;
1353 break;
1354 case 5: // south west
1355 *xPtr = cur_x - ZOOM_LOC_WIDTH;
1356 *yPtr = cur_y + ZOOM_LOC_HEIGHT;
1357 break;
1358 case 6: // west
1359 *xPtr = cur_x - ZOOM_LOC_WIDTH;
1360 *yPtr = cur_y;
1361 break;
1362 case 7: // north west
1363 *xPtr = cur_x - ZOOM_LOC_WIDTH;
1364 *yPtr = cur_y - ZOOM_LOC_HEIGHT;
1365 break;
1366 default:
1367 err_here();
1368 *xPtr = cur_x;
1369 *yPtr = cur_y;
1370 }
1371 }
1372 //----------- End of function Unit::get_hit_x_y -------//
1373
1374
1375 //----------- Begin of function Unit::add_close_attack_effect -------//
add_close_attack_effect()1376 void Unit::add_close_attack_effect()
1377 {
1378 short effectId = (attack_info_array+cur_attack)->effect_id;
1379 if( effectId )
1380 {
1381 short x,y;
1382 get_hit_x_y(&x, &y);
1383 Effect::create(effectId, x, y, SPRITE_IDLE, cur_dir, mobile_type == UNIT_AIR ? 8 : 2, 0);
1384 }
1385 }
1386 //----------- End of function Unit::add_close_attack_effect -------//
1387
1388
1389 //----------- Begin of function Unit::is_action_attack -------//
1390 // check whether the unit carrys on attacking action
1391 //
is_action_attack()1392 int Unit::is_action_attack()
1393 {
1394 switch(action_mode2)
1395 {
1396 case ACTION_ATTACK_UNIT:
1397 case ACTION_ATTACK_FIRM:
1398 case ACTION_ATTACK_TOWN:
1399 case ACTION_ATTACK_WALL:
1400 case ACTION_AUTO_DEFENSE_ATTACK_TARGET:
1401 case ACTION_AUTO_DEFENSE_DETECT_TARGET:
1402 case ACTION_AUTO_DEFENSE_BACK_CAMP:
1403 case ACTION_DEFEND_TOWN_ATTACK_TARGET:
1404 case ACTION_DEFEND_TOWN_DETECT_TARGET:
1405 case ACTION_DEFEND_TOWN_BACK_TOWN:
1406 case ACTION_MONSTER_DEFEND_ATTACK_TARGET:
1407 case ACTION_MONSTER_DEFEND_DETECT_TARGET:
1408 case ACTION_MONSTER_DEFEND_BACK_FIRM:
1409 return 1;
1410
1411 default: return 0;
1412 }
1413
1414 return 0;
1415 }
1416 //----------- End of function Unit::is_action_attack -------//
1417
1418