Logo Search packages:      
Sourcecode: 7kaa version File versions  Download package

OUNITAT.cpp

/*
 * Seven Kingdoms: Ancient Adversaries
 *
 * Copyright 1997,1998 Enlight Software Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

//Filename    : OUNITAT.CPP
//Description : Object Unit attack supporting functions
//Owner             : Alex

#include <ALL.h>
#include <ONEWS.h>
#include <OWORLD.h>
#include <ONATION.h>
#include <OU_MARI.h>
#include <OSPY.h>
#include <OGAME.h>
#include <OTOWN.h>
#include <OF_CAMP.h>
#include <OF_MONS.h>
#include <OEFFECT.h>
#include <OMONSRES.h>
#include <OREBEL.h>
#include <OSERES.h>
#include <OSYS.h>
#include <OWARPT.h>

#ifdef NO_DEBUG_UNIT
#undef err_when
#undef err_here
#undef err_if
#undef err_else
#undef err_now
#define err_when(cond)
#define err_here()
#define err_if(cond)
#define err_else
#define err_now(msg)
#undef DEBUG
#endif

//----------------- Begin of function Unit::update_attack_path_dist -----------------//
// return 1 if it is time to update result path
// return 0 otherwise
//
int Unit::update_attack_path_dist()
{
      if(result_path_dist<=6) //1-6
      {
            return 1;
      }
      else if(result_path_dist<=10) // 8, 10
      {
            return !((result_path_dist-6)%2);
      }
      else if(result_path_dist<=20) // 15, 20
      {
            return !((result_path_dist-10)%5);
      }
      else if(result_path_dist<=60) // 28, 36, 44, 52, 60
      {
            return !((result_path_dist-20)%8);
      }
      else if(result_path_dist<=90) // 75, 90
      {
            return !((result_path_dist-60)%15);
      }
      else // 110, 130, 150, etc
      {
            return !((result_path_dist-90)%20);
      }

      err_here();
      return 0;
}
//---------- End of function Unit::update_attack_path_dist ----------//

//##### begin trevor 15/8 #######//

//----------------- Begin of function Unit::hit_target -----------------//
//    The function can be called in by class Bullet since parent and target
//    are specified.
//
// [Unit*]  parentUnit is the unit attacking
//    [Unit*]     taregtUnit is the unit being attacked
// <int>          attackDamage is the damage done to the target Unit
//
// ****************************** Warning ***********************************
// don't use any member variables of this unit. This unit may not be involved
// in the attack event
// **************************************************************************
//
void Unit::hit_target(Unit* parentUnit, Unit* targetUnit, float attackDamage)
{
      short parentNationRecno = (parentUnit) ? parentUnit->nation_recno : 0;
      short targetNationRecno = targetUnit->nation_recno;

      //------------------------------------------------------------//
      // if the attacked unit is in defense mode, order other available
      // unit in the same camp to help this unit
      // Note : checking for nation_recno since one unit can attack units
      //                 in same nation by bullet accidentally
      //------------------------------------------------------------//
      if(parentUnit && parentUnit->cur_action!=SPRITE_DIE && parentUnit->is_visible() &&
            parentNationRecno!=targetNationRecno && parentUnit->nation_can_attack(targetNationRecno) &&
            targetUnit->in_auto_defense_mode())
      {
            err_when(targetUnit->action_misc!=ACTION_MISC_DEFENSE_CAMP_RECNO || !targetUnit->action_misc_para);
            if(!firm_array.is_deleted(targetUnit->action_misc_para))
            {
                  Firm *firmPtr = firm_array[targetUnit->action_misc_para];
                  if(firmPtr->firm_id==FIRM_CAMP)
                  {
                        err_when(firmPtr->firm_id!=FIRM_CAMP);
                        FirmCamp *campPtr = firmPtr->cast_to_FirmCamp();
                        campPtr->defense(parentUnit->sprite_recno);
                  }
            }
            else
                  targetUnit->clear_unit_defense_mode();
      }

      // ---------- add indicator on the map ----------//
      if( nation_array.player_recno && targetUnit->is_own() )
            war_point_array.add_point(targetUnit->next_x_loc(),targetUnit->next_y_loc());

      //-----------------------------------------------------------------------//
      // decrease the hit points of the target Unit
      //-----------------------------------------------------------------------//
      #define DEFAULT_ARMOR         4
      #define DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN     (float) DEFAULT_ARMOR / ATTACK_SLOW_DOWN
      #define ONE_OVER_ATTACK_SLOW_DOWN                     (float) 1/ATTACK_SLOW_DOWN
      #define COMPARE_POINT                                             DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN + ONE_OVER_ATTACK_SLOW_DOWN

      //-*********** simulate aat ************-//
#ifdef DEBUG
            if(!debug_sim_game_type)
            {
#endif
                  if( attackDamage >= COMPARE_POINT )
                        targetUnit->hit_points -= attackDamage - DEFAULT_ARMOR_OVER_ATTACK_SLOW_DOWN;
                  else
                        targetUnit->hit_points -= MIN(attackDamage,ONE_OVER_ATTACK_SLOW_DOWN);  // in case attackDamage = 0, no hit_point is reduced
#ifdef DEBUG
            }
#endif
      //-*********** simulate aat ************-//

      Nation *parentNationPtr = parentNationRecno ? nation_array[parentNationRecno] : NULL;
      Nation *targetNationPtr = targetNationRecno ? nation_array[targetNationRecno] : NULL;
      char targetUnitClass = unit_res[targetUnit->unit_id]->unit_class;

      if( targetUnit->hit_points <= 0 )
      {
            targetUnit->hit_points = (float) 0;

            //---- if the unit killed is a human unit -----//

            if( targetUnit->race_id )
            {
                  //---- if the unit killed is a civilian unit -----//

                  if( targetUnit->is_civilian() )
                  {
                        if( targetNationRecno )
                        {
                              targetNationPtr->civilian_killed(targetUnit->race_id, 0);
                              targetNationPtr->own_civilian_killed++;
                        }

                        if(parentUnit && parentNationRecno )
                        {
                              parentNationPtr->civilian_killed(targetUnit->race_id, 1);
                              parentNationPtr->enemy_civilian_killed++;
                        }
                  }
                  else  //---- if the unit killed is a soldier -----//
                  {
                        if( targetNationRecno )
                              targetNationPtr->own_soldier_killed++;

                        if(parentUnit && parentNationRecno )
                              parentNationPtr->enemy_soldier_killed++;
                  }
            }

            //--------- if it's a non-human unit ---------//

            else
            {
                  switch( unit_res[targetUnit->unit_id]->unit_class )
                  {
                        case UNIT_CLASS_WEAPON:
                              if(parentUnit && parentNationRecno )
                                    parentNationPtr->enemy_weapon_destroyed++;

                              if( targetNationRecno )
                                    targetNationPtr->own_weapon_destroyed++;
                              break;


                        case UNIT_CLASS_SHIP:
                              if(parentUnit && parentNationRecno )
                                    parentNationPtr->enemy_ship_destroyed++;

                              if( targetNationRecno )
                                    targetNationPtr->own_ship_destroyed++;
                              break;
                  }

                  //---- if the unit destroyed is a trader or caravan -----//

                  if( targetUnit->unit_id == UNIT_CARAVAN ||      // whoever kills a caravan decrease, who will be resented by all races
                         targetUnit->unit_id == UNIT_VESSEL )
                  {
                        if( targetNationRecno )
                              targetNationPtr->civilian_killed(0, 0);         // 0-for all races

                        if(parentUnit && parentNationRecno )
                              parentNationPtr->civilian_killed(0, 1);         // 1-is the nation is the attacking one
                  }
            }

            return;
      }

      if(parentUnit!=NULL && parentNationRecno!=targetNationRecno)
            parentUnit->gain_experience(); // gain experience to increase combat level

      //-----------------------------------------------------------------------//
      // action of the target to take
      //-----------------------------------------------------------------------//
      if( !parentUnit ) // do nothing if parent is dead
            return;

      if( parentUnit->cur_action==SPRITE_DIE ) // skip for explosive cart
      {
            err_when(parentUnit->unit_id!=UNIT_EXPLOSIVE_CART);
            return;
      }

      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)
            return;

      //------- two nations at war ---------//

      if( parentNationRecno && targetNationRecno )
      {
            parentNationPtr->set_at_war_today();
            targetNationPtr->set_at_war_today(parentUnit->sprite_recno);
      }

      //-------- increase battling fryhtan score --------//

      if( targetUnitClass==UNIT_CLASS_MONSTER && parentNationRecno )
      {
            parentNationPtr->kill_monster_score += (float) 0.1;
      }

      //------ call target unit being attack functions -------//

      if( targetNationRecno )
      {
            targetNationPtr->being_attacked(parentNationRecno);

            if( targetUnit->ai_unit )
            {
                  if( targetUnit->rank_id >= RANK_GENERAL )
                        targetUnit->ai_leader_being_attacked(parentUnit->sprite_recno);

                  if( unit_res[targetUnit->unit_id]->unit_class == UNIT_CLASS_SHIP )
                        ((UnitMarine*)targetUnit)->ai_ship_being_attacked(parentUnit->sprite_recno);
            }

            //--- if a member in a troop is under attack, ask for other troop members to help ---//

            if( info.game_date%2 == sprite_recno%2 )
            {
                  if( targetUnit->leader_unit_recno || 
                         (targetUnit->team_info && targetUnit->team_info->member_count > 1) )
                  {
                        if( !unit_array.is_deleted(parentUnit->sprite_recno) )            // it is possible that parentUnit is dying right now 
                        {
                              targetUnit->ask_team_help_attack(parentUnit);
                        }
                  }
            }
      }

      //--- increase reputation of the nation that attacks monsters ---//

      else if( targetUnitClass == UNIT_CLASS_MONSTER )
      {
            if( parentNationRecno )
                  parentNationPtr->change_reputation(REPUTATION_INCREASE_PER_ATTACK_MONSTER);
      }

      //------------------------------------------//

      if(!targetUnit->can_attack()) // no action if the target unit is unable to attack
            return;

      err_when(!targetUnit->can_attack());

      targetUnit->unit_auto_guarding(parentUnit);
}
//---------- End of function Unit::hit_target ----------//

//##### end trevor 15/8 #######//


//----------- Begin of function Unit::unit_auto_guarding ---------------//
// the unit attacks the unit attacking it
//
// <Unit*> attackUnit   - the unit attacking this unit
//
void Unit::unit_auto_guarding(Unit *attackUnit)
{
      if( force_move_flag )
            return;

      //##### begin trevor 9/10 ########//

      //---------------------------------------//
      //
      // If the aggressive_mode is off, then don't
      // fight back when the unit is moving, only
      // fight back when the unit is already fighting
      // or is idle.
      //
      //---------------------------------------//

      if( !aggressive_mode && cur_action != SPRITE_ATTACK &&
             cur_action != SPRITE_IDLE )
      {
            return;
      }

      //##### begin trevor 9/10 ########//

      //--------------------------------------------------------------------//
      // decide attack or not
      //--------------------------------------------------------------------//

      int changeToAttack=0;
      if(cur_action==SPRITE_ATTACK || (sprite_info->need_turning && cur_action==SPRITE_TURN &&
            (abs(next_x_loc()-action_x_loc)<attack_range || abs(next_y_loc()-action_y_loc)<attack_range)))
      {
            err_when(cur_x!=next_x || cur_y!=next_y);
            if(action_mode!=ACTION_ATTACK_UNIT)
                  changeToAttack++;  //else continue to attack the target unit
            else
            {
                  err_when(!action_para);
                  if(unit_array.is_deleted(action_para))
                        changeToAttack++; // attack new target
            }
      }
      else if(cur_action!=SPRITE_DIE)// && abs(cur_x-next_x)<spriteInfo->speed && abs(cur_y-next_y)<spriteInfo->speed)
      {
            changeToAttack++;
            /*if(!ai_unit) // player unit
            {
                  if(action_mode!=ACTION_ATTACK_UNIT)
                        changeToAttack++;  //else continue to attack the target unit
                  else
                  {
                        err_when(!action_para);
                        if(unit_array.is_deleted(action_para))
                              changeToAttack++; // attack new target
                  }
            }
            else
                  changeToAttack++;*/
      }

      if(!changeToAttack)
      {
            if(ai_unit) // allow ai unit to select target to attack
            {
                  //------------------------------------------------------------//
                  // conditions to let the unit escape
                  //------------------------------------------------------------//
                  //-************* codes here ************-//

                  //------------------------------------------------------------//
                  // select the weaker target to attack first, if more than one
                  // unit attack this unit
                  //------------------------------------------------------------//
                  int attackXLoc = attackUnit->next_x_loc();
                  int attackYLoc = attackUnit->next_y_loc();

                  int attackDistance = cal_distance(attackXLoc, attackYLoc, attackUnit->sprite_info->loc_width,
                                                                                     attackUnit->sprite_info->loc_height);
                  if(attackDistance==1) // only consider close attack
                  {
                        err_when(!action_para || unit_array.is_deleted(action_para));
                        Unit *targetUnit = unit_array[action_para];
                        if(targetUnit->hit_points > attackUnit->hit_points) // the new attacker is weaker
                              attack_unit(attackUnit->sprite_recno);
                  }
            }

            return;
      }

      //--------------------------------------------------------------------//
      // cancel AI actions
      //--------------------------------------------------------------------//
      if( ai_action_id )
            nation_array[nation_recno]->action_failure(ai_action_id, sprite_recno);

      if(in_auto_defense_mode())
            set_search_tries(AUTO_DEFENSE_SEARCH_TRIES);

      if(!attackUnit->is_visible())
            return;

      //--------------------------------------------------------------------------------//
      // checking for ship processing trading
      //--------------------------------------------------------------------------------//
      if(sprite_info->sprite_sub_type=='M') //**** BUGHERE, is sprite_sub_type really representing UNIT_MARINE???
      {
            UnitInfo* unitInfo = unit_res[unit_id];
            if(unitInfo->carry_goods_capacity)
            {
                  UnitMarine *shipPtr = (UnitMarine*) this;
                  if(shipPtr->auto_mode && shipPtr->stop_defined_num>1)
                  {
                        int targetXLoc = attackUnit->next_x_loc();
                        int targetYLoc = attackUnit->next_y_loc();
                        SpriteInfo *targetSpriteInfo = attackUnit->sprite_info;
                        int attackDistance = cal_distance(targetXLoc, targetYLoc, targetSpriteInfo->loc_width, targetSpriteInfo->loc_height);
                        int maxAttackRange = max_attack_range();
                        if(maxAttackRange<attackDistance)
                              return; // can't attack the target
                  }
            }
      }

      switch(action_mode2)
      {
            case ACTION_AUTO_DEFENSE_DETECT_TARGET: case ACTION_AUTO_DEFENSE_BACK_CAMP:
                        action_mode2 = ACTION_AUTO_DEFENSE_ATTACK_TARGET;
                        break;

            case ACTION_DEFEND_TOWN_DETECT_TARGET:    case ACTION_DEFEND_TOWN_BACK_TOWN:
                        action_mode2 = ACTION_DEFEND_TOWN_ATTACK_TARGET;
                        break;

            case ACTION_MONSTER_DEFEND_DETECT_TARGET: case ACTION_MONSTER_DEFEND_BACK_FIRM:
                        action_mode2 = ACTION_MONSTER_DEFEND_ATTACK_TARGET;
                        break;
      }

      //##### trevor 9/10 #######//

      save_original_action();

      //----------------------------------------------------------//
      // set the original location of the attacking target when
      // the attack() function is called, action_x_loc2 & action_y_loc2
      // will change when the unit move, but these two will not.
      //----------------------------------------------------------//

      original_target_x_loc = attackUnit->next_x_loc();
      original_target_y_loc = attackUnit->next_y_loc();

      //##### trevor 9/10 #######//

      if(!unit_array.is_deleted(attackUnit->sprite_recno))
            attack_unit(attackUnit->sprite_recno);
}
//---------- End of function Unit::unit_auto_guarding ----------//


//----------- Begin of function Unit::hit_building ---------------//
//
// note:    If parentUnit==NULL, the attacking unit is already dead.
//                In range attack, the unit calling this function may not be the
//                attacking unit.
//
// <Unit*> parentUnit   - the attacking unit
// <int>      target?Loc   - the target building location
// <int>   attackDamage - the actual damage made
//
// ****************************** Warning ***********************************
// don't use any member variables of this unit. This unit may not be involved
// in the attack event
// **************************************************************************
//
void Unit::hit_building(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage)
{
      Location* locPtr = world.get_loc(targetXLoc, targetYLoc);

      if(locPtr->is_firm())
            hit_firm( attackUnit, targetXLoc, targetYLoc, attackDamage);
      else if( locPtr->is_town() )
            hit_town( attackUnit, targetXLoc, targetYLoc, attackDamage);
}
//---------- End of function Unit::hit_building ----------//

//##### begin trevor 15/8 #######//


//------------ Begin of function Unit::hit_firm --------------//
//
// note:    If parentUnit==NULL, the attacking unit is already dead.
//                In range attack, the unit calling this function may not be the
//                attacking unit.
//
// <Unit*> attackUnit   - the attacking unit
// <int>      target?Loc   - the target building location
// <int>   attackDamage - the actual damage made
//
// ****************************** Warning ***********************************
// don't use any member variables of this unit. This unit may not be involved
// in the attack event
// **************************************************************************
//
void Unit::hit_firm(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage)
{
      Location* locPtr = world.get_loc(targetXLoc, targetYLoc);
      if(!locPtr->is_firm())
            return;     // do nothing if no firm there

      //----------- attack firm ------------//
      err_when(!locPtr->firm_recno());
      Firm *targetFirm = firm_array[locPtr->firm_recno()];
      err_when(!targetFirm);

      //------------------------------------------------------------------------------//
      // change relation to hostile
      // check for NULL to skip unhandled case by bullets
      // check for SPRITE_DIE to skip the case by EXPLOSIVE_CART
      //------------------------------------------------------------------------------//
      if( attackUnit!=NULL && attackUnit->cur_action!=SPRITE_DIE &&
             targetFirm->nation_recno != attackUnit->nation_recno )           // 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)
      {
            if( attackUnit->nation_recno && targetFirm->nation_recno )
            {
                  //### trevor 29/9 ###//
                  nation_array[attackUnit->nation_recno]->set_at_war_today();
                  nation_array[targetFirm->nation_recno]->set_at_war_today(attackUnit->sprite_recno);
                  //### trevor 29/9 ###//
            }

            if( targetFirm->nation_recno )
                  nation_array[targetFirm->nation_recno]->being_attacked(attackUnit->nation_recno);

            //------------ auto defense -----------------//
            if(attackUnit->is_visible())
                  targetFirm->auto_defense(attackUnit->sprite_recno);

            if(attackUnit->nation_recno!=targetFirm->nation_recno)
                  attackUnit->gain_experience(); // gain experience to increase combat level

            targetFirm->being_attacked(attackUnit->sprite_recno);

            //------ increase battling fryhtan score -------//

            if( targetFirm->firm_id == FIRM_MONSTER && attackUnit->nation_recno )
                  nation_array[attackUnit->nation_recno]->kill_monster_score += (float) 0.01;
      }

      //---------- add indicator on the map ----------//

      // ###### begin Gilbert 6/10 #######//
      if( nation_array.player_recno && targetFirm->own_firm() )
            war_point_array.add_point(targetFirm->center_x, targetFirm->center_y);
      // ###### end Gilbert 6/10 #######//

      //---------- damage to the firm ------------//

      targetFirm->hit_points -= attackDamage/3;       // /3 so that it takes longer to destroy a firm

      //######## begin trevor 25/8 ##########//

      if(targetFirm->hit_points <= 0)
      {
            targetFirm->hit_points = (float) 0;

            se_res.sound(targetFirm->center_x, targetFirm->center_y, 1,
                  'F', targetFirm->firm_id, "DIE" );

            if( targetFirm->nation_recno == nation_array.player_recno )
                  news_array.firm_destroyed(targetFirm->firm_recno, attackUnit);

            if( targetFirm->nation_recno )
            {
                  if( attackUnit->nation_recno )
                        nation_array[attackUnit->nation_recno]->enemy_firm_destroyed++;

                  if( targetFirm->nation_recno )
                        nation_array[targetFirm->nation_recno]->own_firm_destroyed++;
            }

            else if( targetFirm->firm_id == FIRM_MONSTER )
            {
                  news_array.monster_firm_destroyed( ((FirmMonster*)targetFirm)->monster_id, targetFirm->center_x, targetFirm->center_y );
            }

            firm_array.del_firm(targetFirm->firm_recno);
      }

      //######## end trevor 25/8 ##########//
}
//---------- End of function Unit::hit_firm ----------//


//--------- Begin of function Unit::hit_town ---------//
//
// note:    If attackUnit==NULL, the attacking unit is already dead.
//                In range attack, the unit calling this function may not be the
//                attacking unit.
//
// <Unit*> attackUnit   - the attacking unit
// <int>      target?Loc   - the target building location
// <int>   attackDamage - the actual damage made
//
// ****************************** Warning ***********************************
// don't use any member variables of this unit. This unit may not be involved
// in the attack event
// **************************************************************************
//
void Unit::hit_town(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage)
{
      Location *locPtr = world.get_loc(targetXLoc, targetYLoc);

      if(!locPtr->is_town())
            return;     // do nothing if no town there

      //----------- attack town ----------//

      err_when(!locPtr->town_recno());

      Town *targetTown = town_array[locPtr->town_recno()];
      int   targetTownRecno = targetTown->town_recno;
      int   targetTownNameId = targetTown->town_name_id;
      int   targetTownXLoc = targetTown->center_x;
      int   targetTownYLoc = targetTown->center_y;

      // ---------- add indicator on the map ----------//
      // ###### begin Gilbert 6/10 #######//
      if( nation_array.player_recno && targetTown->nation_recno == nation_array.player_recno )
            war_point_array.add_point(targetTown->center_x, targetTown->center_y);
      // ###### end Gilbert 6/10 #######//

      //------------------------------------------------------------------------------//
      // change relation to hostile
      // check for NULL to skip unhandled case by bullets
      // check for SPRITE_DIE to skip the case by EXPLOSIVE_CART
      //------------------------------------------------------------------------------//
      if( attackUnit!=NULL && attackUnit->cur_action!=SPRITE_DIE &&
             targetTown->nation_recno != attackUnit->nation_recno )           // 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)
      {
            int townNationRecno = targetTown->nation_recno;

            //------- change to hostile relation -------//

            if( attackUnit->nation_recno && targetTown->nation_recno )
            {
                  //### trevor 29/9 ###//
                  nation_array[attackUnit->nation_recno]->set_at_war_today();
                  nation_array[targetTown->nation_recno]->set_at_war_today(attackUnit->sprite_recno);
                  //### trevor 29/9 ###//
            }

            if( targetTown->nation_recno)
            {
                  nation_array[targetTown->nation_recno]->being_attacked(attackUnit->nation_recno);
            }

            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

            targetTown->being_attacked(attackUnit->sprite_recno, attackDamage);

            news_array.enable();

            //------ if the town is destroyed, add a news --------//

            if( town_array.is_deleted(targetTownRecno) &&
                   townNationRecno == nation_array.player_recno )
            {
                  news_array.town_destroyed(targetTownNameId, targetTownXLoc, targetTownYLoc, attackUnit);
            }

            //---------- gain experience --------//

            if(attackUnit->nation_recno!=targetTown->nation_recno)
                  attackUnit->gain_experience(); // gain experience to increase combat level

            //------------ auto defense -----------------//

            if( !firm_array.is_deleted(targetTownRecno) )
                  targetTown->auto_defense(attackUnit->sprite_recno);
      }
}
//---------- End of function Unit::hit_town ----------//

//##### end trevor 15/8 #######//


//--------- Begin of function Unit::hit_wall -----------//
//
// <Unit*> attackUnit   - the attacking unit
// <int>      target?Loc   - the targeted wall location
// <int>   attackDamage - the actual damage made
//
// ****************************** Warning ***********************************
// don't use any member variables of this unit. This unit may not be involved
// in the attack event
// **************************************************************************
//
void Unit::hit_wall(Unit* attackUnit, int targetXLoc, int targetYLoc, float attackDamage)
{
      Location *locPtr = world.get_loc(targetXLoc, targetYLoc);
      err_when(!locPtr->is_wall());

      //######## begin trevor 25/6 #########//
/*
      if(attackUnit!=NULL)
            attackUnit->change_relation(attackUnit->nation_recno, locPtr->wall_nation_recno(), NATION_HOSTILE);
*/
      //######## end trevor 25/6 #########//

      if( !locPtr->attack_wall((int)attackDamage) )
            world.correct_wall(targetXLoc, targetYLoc);
}
//---------- End of function Unit::hit_wall ----------//


//--------- Begin of function Unit::cal_distance ---------//
// calculate the distance from this unit to the target
// (assume the size of this unit is a square)
//
// <int>    targetXLoc        - x location of the target
// <int>    targetYLoc        - y location of the target
// <int> targetWidth          - target width
// <int> targetHeight   - target height
//
int Unit::cal_distance(int targetXLoc, int targetYLoc, int targetWidth, int targetHeight)
{
      int curXLoc = next_x_loc();
      int curYLoc = next_y_loc();
      int dispX=0, dispY=0;

      if(curXLoc<targetXLoc)
            dispX = (targetXLoc - curXLoc - sprite_info->loc_width) + 1;
      else if((dispX=curXLoc-targetXLoc-targetWidth+1)<0)
            dispX  = 0;
      err_when(dispX<0 || dispX>MAX_WORLD_X_LOC);

      if(curYLoc<targetYLoc)
            dispY = (targetYLoc - curYLoc - sprite_info->loc_height) + 1;
      else if((dispY=curYLoc-targetYLoc-targetHeight+1)<0)
      {
            err_when(mobile_type!=UNIT_AIR && !dispX); // inside the target
            return dispX;
      }
      err_when(dispY<0 || dispY>MAX_WORLD_Y_LOC);

      return (dispX>=dispY)? dispX : dispY;
}
//----------- End of function Unit::cal_distance -----------//


//------------ Begin of function Unit::actual_damage --------------//
//
// return: return the actual hit damage this unit can do to a target.
//
float Unit::actual_damage()
{
      AttackInfo *attackInfo = attack_info_array+cur_attack;

      int attackDamage = attackInfo->attack_damage;

      //-------- pierce damage --------//

      attackDamage += m.random(3) + attackInfo->pierce_damage
                                     * m.random(skill.combat_level-attackInfo->combat_level)
                                     / (100-attackInfo->combat_level);

      //--- if this unit is led by a general, its attacking ability is influenced by the general ---//
      //
      // The unit's attacking ability is increased by a percentage equivalent to
      // the leader unit's leadership.
      //
      //------------------------------------------------------------------------//

      if( leader_unit_recno )
      {
            if( unit_array.is_deleted(leader_unit_recno) )
            {
                  leader_unit_recno = 0;
            }
            else
            {
                  Unit* leaderUnit = unit_array[leader_unit_recno];
                  int   leaderXLoc, leaderYLoc;

                  if( leaderUnit->is_visible() )
                  {
                        leaderXLoc = cur_x_loc();
                        leaderYLoc = cur_y_loc();
                  }
                  else if( leaderUnit->unit_mode == UNIT_MODE_OVERSEE )
                  {
                        Firm* firmPtr = firm_array[leaderUnit->unit_mode_para];

                        leaderXLoc = firmPtr->center_x;
                        leaderYLoc = firmPtr->center_y;
                  }
                  else
                        leaderXLoc = -1;

                  if( leaderXLoc >= 0 &&
                         m.points_distance(cur_x_loc(), cur_y_loc(), leaderXLoc, leaderYLoc) <= EFFECTIVE_LEADING_DISTANCE )
                  {
                        attackDamage += attackDamage * leaderUnit->skill.skill_level / 100;
                  }
            }
      }

      return (float) attackDamage / ATTACK_SLOW_DOWN;       // lessen all attacking damages, thus slowing down all battles.
}
//------------ End of function Unit::actual_damage --------------//


//--------- Begin of function Unit::gain_experience ---------//
void Unit::gain_experience()
{
      if(unit_res[unit_id]->unit_class != UNIT_CLASS_HUMAN)
            return; // no experience gain if unit is not human

      //---- increase the unit's contribution to the nation ----//

      if( nation_contribution < MAX_NATION_CONTRIBUTION )
      {
            nation_contribution++;

            err_when( nation_contribution < 0 );            // overflow
      }

      //------ increase combat skill -------//

      err_when(skill.combat_level<0 || skill.combat_level>100);

      inc_minor_combat_level(6);

      //--- if this is a soldier led by a commander, increase the leadership of its commander -----//

      if( leader_unit_recno )
      {
            Unit* leaderUnit = unit_array[leader_unit_recno];
            int   leaderXLoc= -1, leaderYLoc;

            if( leaderUnit->is_visible() )
            {
                  leaderXLoc = cur_x_loc();
                  leaderYLoc = cur_y_loc();
            }
            else if( leaderUnit->unit_mode == UNIT_MODE_OVERSEE )
            {
                  Firm* firmPtr = firm_array[leaderUnit->unit_mode_para];

                  leaderXLoc = firmPtr->center_x;
                  leaderYLoc = firmPtr->center_y;
            }
            else
                  leaderXLoc = -1;

            if( leaderXLoc >= 0 &&
                   m.points_distance( cur_x_loc(), cur_y_loc(), leaderXLoc, leaderYLoc ) <= EFFECTIVE_LEADING_DISTANCE )
            {
                  leaderUnit->inc_minor_skill_level(1);

                  //-- give additional increase if the leader has skill potential on leadership --//

                  if( leaderUnit->skill.skill_potential > 0 )
                  {
                        if( m.random(10-leaderUnit->skill.skill_potential/10)==0 )
                              leaderUnit->inc_minor_skill_level(5);
                  }
            }

            //--- if this soldier has leadership potential and is led by a commander ---//
            //--- he learns leadership by watching how the commander commands the troop --//

            if( skill.skill_potential > 0 )
            {
                  if( m.random(10-skill.skill_potential/10)==0 )
                        inc_minor_skill_level(5);
            }
      }
}
//------------ End of function Unit::gain_experience --------------//


//--------- Begin of function Unit::can_attack ---------//
// return 1 if can attack
// return 0 otherwise
//
/*int Unit::can_attack()
{
      return (can_attack_flag && attack_count);
}*/
//------------ End of function Unit::can_attack --------------//


//--------- Begin of function Unit::nation_can_attack ---------//
// return 1 for able to attack this nation i.e. relation is not
//                      friendly or alliance
// return 0 otherwise
//
// Whether this unit can attack others with the specified nation recno
//
// <short> nationRecno  -     nation recno to be checked
//
int Unit::nation_can_attack(short nationRecno)
{
      if(!ai_unit)
      {
            //return 1;
            if( game.game_mode == GAME_TEST )   // in testing games, player units can attack their own units
                  return 1;
            else
                  return nationRecno!=nation_recno; // able to attack all nation except our own nation
      }
      else if(nation_recno == nationRecno)
            return 0; // ai unit don't attack its own nation, except special order

      if(!nation_recno || !nationRecno)
            return 1; // true if either nation is independent

      Nation *nationPtr = nation_array[nation_recno];

      char relatinStatus = nationPtr->get_relation_status(nationRecno);
      if(relatinStatus==NATION_FRIENDLY || relatinStatus==NATION_ALLIANCE)
            return 0;

      return 1;
}
//------------ End of function Unit::nation_can_attack --------------//


//--------- Begin of function Unit::independent_nation_can_attack ---------//
// Note: this unit should be an independent unit
//
// Should this independent unit attack other units with specified nation recno
//
// return 1 if decision is attacking
// return 0 otherwise
//
// <short> nationRecno  -     nation recno to be checked
//
int Unit::independent_nation_can_attack(short nationRecno)
{
      err_when(nation_recno);

      Town *townPtr;
      FirmMonster *firmMonster;
      Rebel *rebelPtr;

      switch(unit_mode)
      {
            case UNIT_MODE_DEFEND_TOWN:
                        err_when(unit_mode_para<=0);
                        if(town_array.is_deleted(unit_mode_para))
                              return 0; // don't attack independent unit with no town

                        townPtr = town_array[unit_mode_para];

                        if( !townPtr->is_hostile_nation(nationRecno) )
                              return 0; // false if the indepentent unit don't want to attack us

                        break;

            case UNIT_MODE_REBEL:
                        if(rebel_array.is_deleted(unit_mode_para))
                              return 0;

                        rebelPtr = rebel_array[unit_mode_para];

                        if( !rebelPtr->is_hostile_nation(nationRecno) )
                              return 0;

                        break;

//######## begin trevor 22/8 ##########//
            case UNIT_MODE_MONSTER:
                        if( unit_mode_para==0 )
                              return nationRecno; // attack anything that is not independent

                        firmMonster = (FirmMonster*) firm_array[unit_mode_para];

                        err_when( firmMonster->firm_id != FIRM_MONSTER );

                        if( !firmMonster->is_hostile_nation(nationRecno) )
                              return 0;               // false if the indepentent unit don't want to attack us

                        break;
//######## end trevor 22/8 ##########//

            default:
                        return 0;
      }

      return 1;
}
//------------ End of function Unit::independent_nation_can_attack --------------//


//--------- Begin of function Unit::choose_best_attack_mode ---------//
// if the unit has more than one attack mode, select the suitable mode
// to attack the target
//
// <int>  attackDistance      - the distance from the target
// [char] targetMobileType    - the target's mobile_type (default: UNIT_LAND)
//
void Unit::choose_best_attack_mode(int attackDistance, char targetMobileType)
{
      //------------ enable/disable range attack -----------//
      //cur_attack = 0;
      //return;

      //-------------------- define parameters -----------------------//
      UCHAR attackModeBeingUsed = cur_attack;
      err_when(attackModeBeingUsed<0 || attackModeBeingUsed>MAX_UNIT_ATTACK_TYPE);
      //UCHAR maxAttackRangeMode = 0;
      UCHAR maxAttackRangeMode = cur_attack;
      AttackInfo* attackInfoMaxRange = attack_info_array;
      AttackInfo* attackInfoChecking;
      AttackInfo* attackInfoSelected = attack_info_array+cur_attack;

      //--------------------------------------------------------------//
      // If targetMobileType==UNIT_AIR or mobile_type==UNIT_AIR,
      //    force to use range_attack.
      // If there is no range_attack, return 0, i.e. cur_attack=0
      //--------------------------------------------------------------//
      if(attack_count>1)
      {
            int canAttack = 0;
            int checkingDamageWeight, selectedDamageWeight;

            for(UCHAR i=0; i<attack_count; i++)
            {
                  if(attackModeBeingUsed==i)
                        continue; // it is the mode already used

                  attackInfoChecking = attack_info_array+i;
                  if(can_attack_with(attackInfoChecking) && attackInfoChecking->attack_range>=attackDistance)
                  {
                        //-------------------- able to attack ----------------------//
                        canAttack = 1;

                        if(attackInfoSelected->attack_range<attackDistance)
                        {
                              attackModeBeingUsed = UCHAR(i);
                              attackInfoSelected = attackInfoChecking;
                              continue;
                        }
                        
                        checkingDamageWeight = attackInfoChecking->attack_damage;
                        selectedDamageWeight = attackInfoSelected->attack_damage;

                        if(attackDistance==1 && (targetMobileType!=UNIT_AIR && mobile_type!=UNIT_AIR))
                        {
                              //------------ force to use close attack if possible -----------//
                              if(attackInfoSelected->attack_range==attackDistance)
                              {
                                    if(attackInfoChecking->attack_range==attackDistance && checkingDamageWeight>selectedDamageWeight)
                                    {
                                          attackModeBeingUsed = UCHAR(i); // choose the one with strongest damage
                                          attackInfoSelected = attackInfoChecking;
                                    }
                                    continue;
                              }
                              else if(attackInfoChecking->attack_range==1)
                              {
                                    attackModeBeingUsed = UCHAR(i);
                                    attackInfoSelected = attackInfoChecking;
                                    continue;
                              }
                        }
                        
                        //----------------------------------------------------------------------//
                        // further selection
                        //----------------------------------------------------------------------//
                        if(checkingDamageWeight == selectedDamageWeight)
                        {
                              if(attackInfoChecking->attack_range<attackInfoSelected->attack_range)
                              {
                                    if(attackInfoChecking->attack_range>1 || (targetMobileType!=UNIT_AIR && mobile_type!=UNIT_AIR))
                                    {
                                          //--------------------------------------------------------------------------//
                                          // select one with shortest attack_range
                                          //--------------------------------------------------------------------------//
                                          attackModeBeingUsed = UCHAR(i);
                                          attackInfoSelected = attackInfoChecking;
                                    }
                              }
                        }
                        else
                        {
                              //--------------------------------------------------------------------------//
                              // select one that can do the attacking immediately with the strongest damage point
                              //--------------------------------------------------------------------------//
                              attackModeBeingUsed = UCHAR(i);
                              attackInfoSelected = attackInfoChecking;
                        }
                  }
                  
                  if(!canAttack)
                  {
                        //------------------------------------------------------------------------------//
                        // if unable to attack the target, choose the mode with longer attack_range and
                        // heavier damage
                        //------------------------------------------------------------------------------//
                        if(can_attack_with(attackInfoChecking) &&
                              (attackInfoChecking->attack_range>attackInfoMaxRange->attack_range ||
                              (attackInfoChecking->attack_range==attackInfoMaxRange->attack_range &&
                               attackInfoChecking->attack_damage>attackInfoMaxRange->attack_damage)))
                        {
                              maxAttackRangeMode = UCHAR(i);
                              attackInfoMaxRange = attackInfoChecking;
                        }
                  }
            }

            if(canAttack)
                  cur_attack = attackModeBeingUsed;   // choose the strongest damage mode if able to attack
            else
                  cur_attack = maxAttackRangeMode;          //    choose the longest attack range if unable to attack

            attack_range = attack_info_array[cur_attack].attack_range;
            err_when(final_dir<0 || final_dir>=MAX_SPRITE_DIR_TYPE);
            err_when(cur_attack>=attack_count || cur_attack<0);
      }
      else
      {
            cur_attack = 0;   // only one mode is supported
            attack_range = attack_info_array[0].attack_range;
            return;
      }
}
//---------- End of function Unit::choose_best_attack_mode ----------//


//------ Begin of function Unit::set_attack_dir ---------//
// set direction for attacking
//
//    <short>     curX        -     x location of the unit
// <short>  curY        -     y location of the unit
// <short>  targetX     -     x location of the target
// <short>  targetY     -     y location of the target
//
void Unit::set_attack_dir(short curX, short curY, short targetX, short targetY)
{
      UCHAR targetDir = get_dir(curX, curY, targetX, targetY);
      if(unit_res[unit_id]->unit_class==UNIT_CLASS_SHIP)
      {
            UCHAR attackDir1, attackDir2;

            attackDir1 = (targetDir+2)%MAX_SPRITE_DIR_TYPE;
            attackDir2 = (targetDir+6)%MAX_SPRITE_DIR_TYPE;

            if((attackDir1+8-final_dir)%MAX_SPRITE_DIR_TYPE <= (attackDir2+8-final_dir)%MAX_SPRITE_DIR_TYPE)
                  final_dir = attackDir1;
            else
                  final_dir = attackDir2;

            attack_dir = targetDir;
            err_when(attack_dir<0 || attack_dir>=MAX_SPRITE_DIR_TYPE);
      }
      else
      {
            attack_dir = targetDir;
            set_dir(targetDir);
      }
}
//---------- End of function Unit::set_attack_dir ----------//


//--------- Begin of function Unit::set_unreachable_location ---------//
// used to set the bit in the unreachable_flag (16 bits)
//
//    The 16 bits of the flag are used to represent the 16 location of a unit
// as follows:
//
//    1     2     3     4                 where x is the upper left corner of the unit.
//    5     x     6     7                 For 1x1 unit, 1,2,3,5,6,8,9,10 are meaningful
//    8     9     10    11                For 2x2 unit, 1,2,3,4,5,7,8,11,12,13,14,15 are meaningful
//    12    13    14    15
//
void Unit::set_unreachable_location(int xLoc, int yLoc)
{
      static unsigned short bitFlag[16] = {0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
                                                                               0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000};
      /*int curXLoc = next_x_loc();
      int curYLoc = next_y_loc();
      int xDist = xLoc-curXLoc+1;
      int yDist = yLoc-curYLoc+1;

      err_when(xDist<0 || xDist>3 || yDist<0 || yDist>3);
      char bitNo = yDist*4 + xDist;

      err_when(bitNo==5);
      if(bitNo>5)
            bitNo--;

      unreachable_flag |= bitFlag[bitNo];*/
}
//----------- End of function Unit::set_unreachable_location -----------//

//--------- Begin of function Unit::check_self_surround ---------//
// Note : mobile_type used is this unit's mobile_type
//
void Unit::check_self_surround()
{
      /*err_when(sprite_info->loc_height!=sprite_info->loc_width);
      err_when(sprite_info->loc_width<1 || sprite_info->loc_width>2);
      
      Location *locPtr;
      int width = sprite_info->loc_width;
      int curXLoc = next_x_loc();
      int curYLoc = next_y_loc();
      int startCount = (width==1) ? 2 : 5;
      int endCount = (width==1) ? 9 : 16;
      int xShift, yShift;

      self_surround_flag = 0;

      for(int i=startCount; i<=endCount; i++)
      {
            m.cal_move_around_a_point(i, width, width, xShift, yShift);
            locPtr = world.get_loc(curXLoc+xShift, curYLoc+yShift);
            
            if(!locPtr->can_move(mobile_type))
                  m.set_surround_bit(self_surround_flag, i-startCount);
      }*/
}
//----------- End of function Unit::check_self_surround -----------//


//----------- Begin of function Unit::cycle_eqv_attack -----------//
void Unit::cycle_eqv_attack()
{
      int trial = MAX_UNIT_ATTACK_TYPE+2;
      if( attack_info_array[cur_attack].eqv_attack_next > 0)
      {
            do
            {
                  cur_attack = attack_info_array[cur_attack].eqv_attack_next-1;
                  err_when(--trial == 0);
            } while( !can_attack_with(cur_attack) );
      }
      else
      {
            if( !can_attack_with(cur_attack) )
            {
                  err_here();
                  // force to search again
                  char attackRange = char(attack_info_array[cur_attack].attack_range);
                  err_when(attackRange != attack_range);                // redundant check
                  AttackInfo *attackInfo = attack_info_array;
                  for(int i = 0; i < attack_count; ++i, ++attackInfo)
                  {
                        if( attackInfo->attack_range >= attackRange && 
                              can_attack_with(attackInfo))
                        {
                              cur_attack = i;
                              break;
                        }
                        err_when(i >= attack_count);        // not found
                  }
            }
      }
      err_when(cur_attack < 0 || cur_attack >= attack_count);
}
//----------- End of function Unit::cycle_eqv_attack -----------//


//----------- Begin of function Unit::max_attack_range -----------//
// return the maximum attack range the unit can make
// 
//
//
int Unit::max_attack_range()
{
      int maxRange=0;

      AttackInfo *attackInfo = attack_info_array;
      for(int i=0; i<attack_count; i++, attackInfo++)
      {
            if(can_attack_with(attackInfo) &&
                  attackInfo->attack_range>maxRange)
                  maxRange = attackInfo->attack_range;
      }
      
      return maxRange;
}
//----------- End of function Unit::max_attack_range -----------//


//----------- Begin of function Unit::can_attack_with -------//
int Unit::can_attack_with(int i)
{
      err_when( i< 0 || i >= attack_count);
      AttackInfo *attackInfo = attack_info_array+i;
      return( skill.combat_level >= attackInfo->combat_level &&
            cur_power >= attackInfo->min_power);
      
}


int Unit::can_attack_with(AttackInfo *attackInfo)
{
      return( skill.combat_level >= attackInfo->combat_level &&
            cur_power >= attackInfo->min_power);
}
//----------- End of function Unit::can_attack_with -------//


//----------- Begin of function Unit::get_hit_x_y -------//
void Unit::get_hit_x_y(short *xPtr, short *yPtr)
{
      switch(cur_dir)
      {
      case 0:     // north
            *xPtr = cur_x;
            *yPtr = cur_y - ZOOM_LOC_HEIGHT;
            break;
      case 1:     // north east
            *xPtr = cur_x + ZOOM_LOC_WIDTH;
            *yPtr = cur_y - ZOOM_LOC_HEIGHT;
            break;
      case 2:     // east
            *xPtr = cur_x + ZOOM_LOC_WIDTH;
            *yPtr = cur_y;
            break;
      case 3:     // south east
            *xPtr = cur_x + ZOOM_LOC_WIDTH;
            *yPtr = cur_y + ZOOM_LOC_HEIGHT;
            break;
      case 4:     // south
            *xPtr = cur_x;
            *yPtr = cur_y + ZOOM_LOC_HEIGHT;
            break;
      case 5:     // south west
            *xPtr = cur_x - ZOOM_LOC_WIDTH;
            *yPtr = cur_y + ZOOM_LOC_HEIGHT;
            break;
      case 6:     // west
            *xPtr = cur_x - ZOOM_LOC_WIDTH;
            *yPtr = cur_y;
            break;
      case 7:     // north west
            *xPtr = cur_x - ZOOM_LOC_WIDTH;
            *yPtr = cur_y - ZOOM_LOC_HEIGHT;
            break;
      default:
            err_here();
            *xPtr = cur_x;
            *yPtr = cur_y;
      }
}
//----------- End of function Unit::get_hit_x_y -------//


//----------- Begin of function Unit::add_close_attack_effect -------//
void Unit::add_close_attack_effect()
{
      short effectId = (attack_info_array+cur_attack)->effect_id;
      if( effectId )
      {
            short x,y;
            get_hit_x_y(&x, &y);
            Effect::create(effectId, x, y, SPRITE_IDLE, cur_dir, mobile_type == UNIT_AIR ? 8 : 2, 0);
      }
}
//----------- End of function Unit::add_close_attack_effect -------//


//----------- Begin of function Unit::is_action_attack -------//
// check whether the unit carrys on attacking action
//
int Unit::is_action_attack()
{
      switch(action_mode2)
      {
            case ACTION_ATTACK_UNIT:
            case ACTION_ATTACK_FIRM:
            case ACTION_ATTACK_TOWN:
            case ACTION_ATTACK_WALL:
            case ACTION_AUTO_DEFENSE_ATTACK_TARGET:
            case ACTION_AUTO_DEFENSE_DETECT_TARGET:
            case ACTION_AUTO_DEFENSE_BACK_CAMP:
            case ACTION_DEFEND_TOWN_ATTACK_TARGET:
            case ACTION_DEFEND_TOWN_DETECT_TARGET:
            case ACTION_DEFEND_TOWN_BACK_TOWN:
            case ACTION_MONSTER_DEFEND_ATTACK_TARGET:
            case ACTION_MONSTER_DEFEND_DETECT_TARGET:
            case ACTION_MONSTER_DEFEND_BACK_FIRM:
                        return 1;
      
            default: return 0;
      }

      return 0;
}
//----------- End of function Unit::is_action_attack -------//


Generated by  Doxygen 1.6.0   Back to index