/* * 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 . * */ //Filename : OUNITAT.CPP //Description : Object Unit attack supporting functions //Owner : Alex #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 // 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 // // 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)speed && abs(cur_y-next_y)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(maxAttackRangenext_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. // // parentUnit - the attacking unit // target?Loc - the target building location // 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. // // attackUnit - the attacking unit // target?Loc - the target building location // 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. // // attackUnit - the attacking unit // target?Loc - the target building location // 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 -----------// // // attackUnit - the attacking unit // target?Loc - the targeted wall location // 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) // // targetXLoc - x location of the target // targetYLoc - y location of the target // targetWidth - target width // 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(curXLocloc_width) + 1; else if((dispX=curXLoc-targetXLoc-targetWidth+1)<0) dispX = 0; err_when(dispX<0 || dispX>MAX_WORLD_X_LOC); if(curYLocloc_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 // // 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 // // 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 // // 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; iattack_range>=attackDistance) { //-------------------- able to attack ----------------------// canAttack = 1; if(attackInfoSelected->attack_rangeattack_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_rangeattack_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 // // curX - x location of the unit // curY - y location of the unit // targetX - x location of the target // 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; iattack_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 -------//