/* * 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 : OUNIT.CPP //Description : Object Unit #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if(GAME_FRAMES_PER_DAY!=FRAMES_PER_DAY) #error #endif #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::Unit ---------// // Unit::Unit() { // ##### patch begin Gilbert 21/1 ######// // unit_id = 0; memset( sizeof(Sprite) + (char *)this, 0, sizeof(Unit) - sizeof(Sprite)); // ##### patch end Gilbert 21/1 ######// } //----------- End of function Unit::Unit -----------// //--------- Begin of function Unit::~Unit ---------// // Unit::~Unit() { deinit(); } //----------- End of function Unit::~Unit -----------// //--------- Begin of function Unit::init ---------// // // unitId - the id. of the unit // nationRecno - the recno of nation // [int] rankId - rank id. of the unit (none for non-human unit) // [int] unitLoyalty - loyalty of the unit (none for non-human unit) // [int] startXLoc, startYLoc - the starting location of the unit // (if startXLoc < 0, this is a unit for hire, and is not a unit of the game yet. init_sprite() won't be called for this unit) // (default: -1, -1) // // Note: sprite_recno must be initialized first before calling Unit::init() // void Unit::init(int unitId, int nationRecno, int rankId, int unitLoyalty, int startXLoc, int startYLoc) { //------------ set basic vars -------------// nation_recno = (char) nationRecno; rank_id = rankId; // rank_id must be initialized before init_unit_id() as init_unit_id() may overwrite it nation_contribution = 0; // nation_contribution must be initialized before init_unit_id() as init_unit_id() may overwrite it if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) { team_info = (TeamInfo*) mem_add( sizeof(TeamInfo) ); team_info->member_count = 0; } else team_info = NULL; init_unit_id(unitId); group_select_id = 0; unit_group_id = unit_array.cur_group_id++; race_id = (char) unit_res[unit_id]->race_id; //------- init unit name ---------// if( race_id ) { name_id = race_res[race_id]->get_new_name_id(); } else //---- init non-human unit series no. ----// { if( nation_recno ) name_id = ++nation_array[nation_recno]->last_unit_name_id_array[unit_id-1]; else name_id = 0; } //------- init ai_unit ----------// if( nation_recno ) ai_unit = nation_array[nation_recno]->nation_type == NATION_AI; else ai_unit = 0; err_when( unitLoyalty < 0 || unitLoyalty > 100 ); //----------------------------------------------// ai_action_id = 0; action_misc = ACTION_MISC_STOP; action_misc_para = 0; action_mode2 = action_mode = ACTION_STOP; action_para2 = action_para = 0; action_x_loc2 = action_y_loc2 = action_x_loc = action_y_loc = -1; memset(blocked_edge, 0, sizeof(char)*4); attack_range = 0; //store the attack range of the current attack mode if the unit is ordered to attack leader_unit_recno= 0; team_id = 0; selected_flag = 0; waiting_term = 0; swapping = 0; // indicate whether swapping is processed. spy_recno = 0; range_attack_x_loc = -1; range_attack_y_loc = -1; //------- initialize path seek vars -------// result_node_array = NULL; result_node_count = result_node_recno = result_path_dist = 0; //------- initialize way point vars -------// way_point_array = NULL; way_point_array_size = 0; way_point_count = 0; //---------- initialize game vars ----------// unit_mode = 0; unit_mode_para = 0; max_hit_points = unit_res[unit_id]->hit_points; hit_points = max_hit_points; loyalty = unitLoyalty; can_guard_flag = 0; can_attack_flag = 1; force_move_flag = 0; ai_no_suitable_action = 0; cur_power = 0; max_power = 0; total_reward = 0; home_camp_firm_recno = 0; seek_path_fail_count = 0; ignore_power_nation = 0; aggressive_mode = 1; // the default mode is 1 err_when( loyalty<0 || loyalty>100 ); //--------- init skill potential ---------// if( m.random(10)==0 ) // 1 out of 10 has a higher than normal potential in this skill { skill.skill_potential = 50+m.random(51); // 50 to 100 potential } //------ initialize the base Sprite class -------// if( startXLoc >= 0 ) init_sprite( startXLoc, startYLoc ); else { cur_x = -1; } //------------- set attack_dir ------------// attack_dir = final_dir; //-------------- update loyalty -------------// update_loyalty(); //--------------- init AI info -------------// if( ai_unit ) { Nation* nationPtr = nation_array[nation_recno]; if( rank_id==RANK_GENERAL || rank_id==RANK_KING ) nationPtr->add_general_info(sprite_recno); switch( unit_res[unit_id]->unit_class ) { case UNIT_CLASS_CARAVAN: nationPtr->add_caravan_info(sprite_recno); break; case UNIT_CLASS_SHIP: nationPtr->add_ship_info(sprite_recno); break; } } //----------- init derived class ----------// init_derived(); } //----------- End of function Unit::init -----------// //--------- Begin of function Unit::init_unit_id ---------// void Unit::init_unit_id(int unitId) { unit_id = unitId; UnitInfo* unitInfo = unit_res[unit_id]; sprite_id = unitInfo->sprite_id; sprite_info = sprite_res[sprite_id]; mobile_type = unitInfo->mobile_type; //--- if this unit is a weapon unit with multiple versions ---// set_combat_level(100); // set combat level default to 100, for human units, it will be adjusted later by individual functions int techLevel; if( nation_recno && unitInfo->unit_class == UNIT_CLASS_WEAPON && (techLevel=unitInfo->nation_tech_level_array[nation_recno-1]) > 0 ) { set_weapon_version(techLevel); } fix_attack_info(); //-------- set unit count ----------// if( nation_recno ) { if( rank_id != RANK_KING ) unitInfo->inc_nation_unit_count(nation_recno); if( rank_id == RANK_GENERAL ) unitInfo->inc_nation_general_count(nation_recno); } //--------- increase monster count ----------// if( unit_res[unit_id]->unit_class == UNIT_CLASS_MONSTER ) unit_res.mobile_monster_count++; } //----------- End of function Unit::init_unit_id -----------// //--------- Begin of function Unit::deinit_unit_id ---------// void Unit::deinit_unit_id() { if( sys.signal_exit_flag ) return; //-----------------------------------------// UnitInfo *unitInfo = unit_res[unit_id]; if( nation_recno ) { if( rank_id != RANK_KING ) unitInfo->dec_nation_unit_count(nation_recno); if( rank_id == RANK_GENERAL ) unitInfo->dec_nation_general_count(nation_recno); } //--------- if the unit is a spy ----------// // // A spy has double identity and is counted // by both the true controlling nation and // the deceiving nation. // //-----------------------------------------// if( spy_recno && true_nation_recno() != nation_recno ) { err_when( !race_id ); int trueNationRecno = true_nation_recno(); if( rank_id != RANK_KING ) unitInfo->dec_nation_unit_count(trueNationRecno); if( rank_id == RANK_GENERAL ) unitInfo->dec_nation_general_count(trueNationRecno); } //--------- decrease monster count ----------// if( unit_res[unit_id]->unit_class == UNIT_CLASS_MONSTER ) { unit_res.mobile_monster_count--; err_when( unit_res.mobile_monster_count < 0 ); } } //----------- End of function Unit::deinit_unit_id -----------// //--------- Begin of function Unit::set_spy ---------// void Unit::set_spy(int spyRecno) { spy_recno = spyRecno; } //---------- End of function Unit::set_spy ---------// //--------- Begin of function Unit::init_sprite ---------// // // startXLoc, startYLoc - the starting location of the unit // void Unit::init_sprite(int startXLoc, int startYLoc) { err_when( !world.get_loc(startXLoc, startYLoc)->can_move(unit_res[unit_id]->mobile_type) ); err_when(unit_res[unit_id]->unit_class==UNIT_CLASS_SHIP && (startXLoc%2 || startYLoc%2)); Sprite::init(unit_res[unit_id]->sprite_id, startXLoc, startYLoc); //--------------------------------------------------------------------// // move_to_?_loc is always the current location of the unit as // cur_action == SPRITE_IDLE //--------------------------------------------------------------------// original_action_mode = 0; ai_original_target_x_loc = -1; attack_range = 0; move_to_x_loc = next_x_loc(); move_to_y_loc = next_y_loc(); go_x = next_x; go_y = next_y; //-------- set the cargo_recno -------------// char w, h; short x, y; err_if(!sprite_recno) // sprite_recno must be initialized first before calling Unit::init() err_here(); for(h=0, y=startYLoc; hloc_height; h++, y++) { for(w=0, x=startXLoc; wloc_width; w++, x++) { err_if( world.get_unit_recno(x, y, mobile_type) ) // it must be 0 to put the sprite in this location err_here(); world.set_unit_recno(x, y, mobile_type, sprite_recno); } } if( is_own() || (nation_recno && nation_array[nation_recno]->is_allied_with_player) ) { world.unveil(startXLoc, startYLoc, startXLoc+sprite_info->loc_width-1, startYLoc+sprite_info->loc_height-1 ); world.visit(startXLoc, startYLoc, startXLoc+sprite_info->loc_width-1, startYLoc+sprite_info->loc_height-1, unit_res[unit_id]->visual_range, unit_res[unit_id]->visual_extend); } err_when(result_node_array || result_path_dist); } //----------- End of function Unit::init_sprite -----------// //--------- Begin of function Unit::deinit ---------// void Unit::deinit() { err_when( unit_array.is_truly_deleted(sprite_recno) ); if( !unit_id ) return; //-------- if this is a king --------// if( !sys.signal_exit_flag && nation_recno ) { if( rank_id == RANK_KING ) // check nation_recno because monster kings will die also. { king_die(); err_when( unit_array.is_truly_deleted(sprite_recno) ); } else if( rank_id == RANK_GENERAL ) { general_die(); err_when( unit_array.is_truly_deleted(sprite_recno) ); } } //------------ free up team_info -----------// if( team_info ) { mem_del(team_info); team_info = NULL; } //---- if this is a general, deinit its link with its soldiers ----// // // We do not use team_info because monsters and rebels also use // leader_unit_recno and they do not use keep the member info // in team_info. // //-----------------------------------------------------------------// if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) { for( int i=unit_array.size() ; i>0 ; i-- ) { if( unit_array.is_deleted(i) ) continue; if( unit_array[i]->leader_unit_recno == sprite_recno ) unit_array[i]->leader_unit_recno = 0; } } //----- if this is a unit on a ship ------// if( unit_mode == UNIT_MODE_ON_SHIP ) { if( !unit_array.is_deleted(unit_mode_para) ) // the ship may have been destroyed at the same time. Actually when the ship is destroyed, all units onboard are killed and this function is called. { Unit* unitPtr = unit_array[unit_mode_para]; err_when( unit_res[unitPtr->unit_id]->unit_class != UNIT_CLASS_SHIP ); ((UnitMarine*)unitPtr)->del_unit(sprite_recno); } } //----- if this is a ship in the harbor -----// else if( unit_mode == UNIT_MODE_IN_HARBOR ) { if( !firm_array.is_deleted(unit_mode_para) ) // the ship may have been destroyed at the same time. Actually when the ship is destroyed, all firms onboard are killed and this function is called. { Firm* firmPtr = firm_array[unit_mode_para]; err_when( firmPtr->firm_id != FIRM_HARBOR ); ((FirmHarbor*)firmPtr)->del_hosted_ship(sprite_recno); } } //----- if this unit is a constructor in a firm -------// else if( unit_mode == UNIT_MODE_CONSTRUCT ) { err_when( firm_array[unit_mode_para]->builder_recno != sprite_recno ); firm_array[unit_mode_para]->builder_recno = 0; } //-------- if this is a spy ---------// if( spy_recno ) { spy_array.del_spy( spy_recno ); spy_recno = 0; } //---------- reset command ----------// if( power.command_unit_recno == sprite_recno ) power.reset_command(); //-----------------------------------// deinit_unit_id(); //-------- reset seek path ----------// reset_path(); //----- if cur_x == -1, the unit has not yet been hired -----// if( cur_x >= 0 ) deinit_sprite(); //------------------------------------------------// // // Prime rule: // // world.get_loc(next_x_loc() and next_y_loc())->cargo_recno // is always = sprite_recno // // no matter what cur_action is. // //------------------------------------------------// // // Relationship between (next_x, next_y) and (cur_x, cur_y) // // when SPRITE_WAIT, SPRITE_IDLE, SPRITE_READY_TO_MOVE, // SPRITE_ATTACK, SPRITE_DIE: // // (next_x, next_y) == (cur_x, cur_y), it's the location of the sprite. // // when SPRITE_MOVE: // // (next_x, next_y) != (cur_x, cur_y) // (next_x, next_y) is where the sprite is moving towards. // (cur_x , cur_y ) is the location of the sprite. // //------------------------------------------------// //--------------- deinit AI info -------------// if( ai_unit ) { if( !nation_array.is_deleted(nation_recno) ) { Nation* nationPtr = nation_array[nation_recno]; if( rank_id==RANK_GENERAL || rank_id==RANK_KING ) nationPtr->del_general_info(sprite_recno); switch( unit_res[unit_id]->unit_class ) { case UNIT_CLASS_CARAVAN: nationPtr->del_caravan_info(sprite_recno); break; case UNIT_CLASS_SHIP: nationPtr->del_ship_info(sprite_recno); break; } } } //-------------- reset unit_id ---------------// unit_id = 0; } //----------- End of function Unit::deinit -----------// //--------- Begin of function Unit::deinit_sprite ---------// // // [int] keepSelected - keep it selected if it is current selected. // (default: 0) // void Unit::deinit_sprite(int keepSelected) { err_when(result_node_array!=NULL); if( cur_x == -1 ) return; //---- if this unit is led by a leader, only mobile units has leader_unit_recno assigned to a leader -----// if( leader_unit_recno ) { if( !unit_array.is_deleted(leader_unit_recno) ) // the leader unit may have been killed at the same time unit_array[leader_unit_recno]->del_team_member(sprite_recno); leader_unit_recno = 0; } //-------- clear the cargo_recno ----------// short w, h; short x, y; for(h=0, y=next_y_loc(); hloc_height; h++, y++) { for(w=0, x=next_x_loc(); wloc_width; w++, x++) { err_if( world.get_unit_recno(x, y, mobile_type) != sprite_recno ) // it must be 0 to put the sprite in this location err_here(); world.set_unit_recno(x, y, mobile_type, 0); } } cur_x = -1; //---- reset other parameters related to this unit ----// if( !keepSelected ) { if( unit_array.selected_recno == sprite_recno ) { unit_array.selected_recno = 0; info.disp(); } if( power.command_unit_recno == sprite_recno ) power.command_id = 0; } //------- deinit unit mode -------// deinit_unit_mode(); } //----------- End of function Unit::deinit_sprite -----------// //--------- Begin of function Unit::deinit_unit_mode ---------// // void Unit::deinit_unit_mode() { if( sys.signal_exit_flag ) return; //----- this unit was defending the town before it gets killed ----// if(unit_mode==UNIT_MODE_DEFEND_TOWN) { if(!town_array.is_deleted(unit_mode_para)) { Town *townPtr = town_array[unit_mode_para]; if(nation_recno == townPtr->nation_recno) townPtr->reduce_defender_count(); } set_mode(0); // reset mode } //----- this is a monster unit defending its town ------// else if( unit_mode==UNIT_MODE_MONSTER && unit_mode_para ) { if(((UnitMonster*)this)->monster_action_mode!=MONSTER_ACTION_DEFENSE) return; FirmMonster* firmMonster = (FirmMonster*) firm_array[unit_mode_para]; err_when( firmMonster->firm_id != FIRM_MONSTER ); firmMonster->reduce_defender_count(rank_id); } } //----------- End of function Unit::deinit_unit_mode -----------// //--------- Begin of function Unit::king_die ---------// // void Unit::king_die() { //--------- add news ---------// news_array.king_die(nation_recno); //--- see if the units, firms and towns of the nation are all destroyed ---// Nation* nationPtr = nation_array[nation_recno]; nationPtr->king_unit_recno = 0; } //----------- End of function Unit::king_die -----------// //--------- Begin of function Unit::general_die ---------// // void Unit::general_die() { //--------- add news ---------// if( nation_recno == nation_array.player_recno ) news_array.general_die(sprite_recno); } //----------- End of function Unit::general_die -----------// //--------- Begin of function Unit::unit_name ---------// // // [int] withTitle - whether return a string with the title of the unit // or not. (default: 1) // char* Unit::unit_name(int withTitle) { static String str; UnitInfo* unitInfo = unit_res[unit_id]; //------------------------------------// if( race_id ) { str = ""; if( withTitle ) { if( unit_mode == UNIT_MODE_REBEL ) { if( rank_id == RANK_GENERAL ) str = "Rebel Leader "; } else { if( rank_id == RANK_KING ) str = "King "; else if( rank_id == RANK_GENERAL ) str = "General "; } } str = translate.process(str); if( rank_id == RANK_KING ) // use the player name str += nation_array[nation_recno]->king_name(); else str += race_res[race_id]->get_name(name_id); } else { str = unitInfo->name; //--- for weapons, the rank_id is used to store the version of the weapon ---// if( unitInfo->unit_class == UNIT_CLASS_WEAPON && get_weapon_version() > 1 ) { str += " "; str += m.roman_number(get_weapon_version()); } if( unitInfo->unit_class != UNIT_CLASS_GOD ) // God doesn't have any series no. { str += " "; str += name_id; // name id is the series no. of the unit } } return str; } //----------- End of function Unit::unit_name ---------// //--------- Begin of function Unit::set_name ---------// // // Set the name id. of this unit. // void Unit::set_name(WORD newNameId) { //------- free up the existing name id. ------// race_res[race_id]->free_name_id(name_id); //------- set the new name id. ---------// name_id = newNameId; //-------- register usage of the new name id. ------// race_res[race_id]->use_name_id(name_id); } //----------- End of function Unit::set_name ---------// //--------- Begin of function Unit::is_own ---------// // int Unit::is_own() { return is_nation(nation_array.player_recno); } //----------- End of function Unit::is_own ---------// //--------- Begin of function Unit::is_own_spy ---------// // int Unit::is_own_spy() { return spy_recno && spy_array[spy_recno]->true_nation_recno == nation_array.player_recno; } //----------- End of function Unit::is_own_spy ---------// //--------- Begin of function Unit::is_nation ---------// // // Whether the unit belongs to the specific nation. // int Unit::is_nation(int nationRecno) { if( nation_recno == nationRecno ) return 1; if( spy_recno && spy_array[spy_recno]->true_nation_recno == nationRecno ) return 1; return 0; } //----------- End of function Unit::is_nation ---------// //--------- Begin of function Unit::is_civilian ---------// // int Unit::is_civilian() { return race_id>0 && skill.combat_level<20 && skill.skill_id != SKILL_LEADING && unit_mode != UNIT_MODE_DEFEND_TOWN && unit_mode != UNIT_MODE_REBEL; } //----------- End of function Unit::is_civilian ---------// //--------- Begin of function Unit::true_nation_recno ---------// // // The true nation recno of the unit, taking care of the // situation where the unit is a spy. // int Unit::true_nation_recno() { if( spy_recno ) return spy_array[spy_recno]->true_nation_recno; else return nation_recno; } //----------- End of function Unit::true_nation_recno ---------// //--------- Begin of function Unit::next_day ---------// // void Unit::next_day() { int unitRecno = sprite_recno; err_when( unit_array.is_deleted(unitRecno) ); err_when( race_id && !is_visible() && unit_mode==0 ); #ifdef DEBUG if( unit_mode == UNIT_MODE_UNDER_TRAINING ) { Town* townPtr =town_array[unit_mode_para]; err_when( townPtr->train_unit_recno != sprite_recno ); err_when( townPtr->nation_recno != nation_recno ); } #endif //------- functions for non-independent nations only ------// if( nation_recno ) { pay_expense(); if( unit_array.is_deleted(unitRecno) ) // if its hit points go down to 0, is_deleted() will return 1. return; //------- update loyalty -------------// if( info.game_date%30 == sprite_recno%30 ) { update_loyalty(); err_when( unit_array.is_deleted(unitRecno) ); } //------- think about rebeling -------------// if( info.game_date%15 == sprite_recno%15 ) { if( think_betray() ) return; } } //------- recover from damage -------// if( info.game_date%15 == sprite_recno%15 ) // recover one point per two weeks { process_recover(); err_when( unit_array.is_deleted(unitRecno) ); } //------- restore cur_power --------// cur_power += 5; if( cur_power > max_power) cur_power = max_power; //------- king undie flag (for testing games only) --------// if( config.king_undie_flag && rank_id == RANK_KING && nation_recno && !nation_array[nation_recno]->is_ai() ) { hit_points = max_hit_points; } //-------- if aggresive_mode is 1 --------// if( nation_recno && is_visible() ) think_aggressive_action(); //---------- debug ------------// #ifdef DEBUG err_when( unit_res[unit_id]->unit_class != UNIT_CLASS_HUMAN && race_id ); if( spy_recno ) { err_when( spy_array.is_deleted(spy_recno) ); Spy* spyPtr = spy_array[spy_recno]; err_when( nation_recno != spyPtr->cloaked_nation_recno ); if( unit_mode == UNIT_MODE_OVERSEE ) { err_when( spyPtr->spy_place != SPY_FIRM ); err_when( spyPtr->spy_place_para != unit_mode_para ); } else { err_when( spyPtr->spy_place != SPY_MOBILE ); err_when( spyPtr->spy_place_para != sprite_recno ); } } if( leader_unit_recno ) { Unit* unitPtr = unit_array[leader_unit_recno]; err_when( unitPtr->rank_id != RANK_GENERAL && unitPtr->rank_id != RANK_KING ); } err_when( (rank_id == RANK_GENERAL || rank_id == RANK_KING) && !team_info ); if( leader_unit_recno ) { err_when( unit_array.is_truly_deleted(leader_unit_recno) ); err_when( unit_array[leader_unit_recno]->nation_recno != nation_recno ); // err_when( unit_array[leader_unit_recno]->team_id != team_id ); } err_when( hit_points > max_hit_points ); err_when( max_hit_points == 0 ); err_when( skill.combat_level<=0 ); err_when( skill.combat_level>100 ); err_when( unit_mode==UNIT_MODE_REBEL && spy_recno ); // no rebel spies err_when( unit_mode==UNIT_MODE_REBEL && nation_recno ); // all rebels must be independent units err_when( unit_mode==UNIT_MODE_DEFEND_TOWN && spy_recno ); // no rebel spies err_when( loyalty < 0 || loyalty > 100 ); err_when( skill.skill_id == SKILL_SPYING ); // skill.skill_id should never be SKILL_SPYING, it will be shown in spy_recno if it's a spy err_when( nation_contribution < 0 ); err_when( nation_contribution > MAX_NATION_CONTRIBUTION ); err_when( ai_unit && ( !nation_recno || !nation_array[nation_recno]->is_ai() ) ); #else // fix bug on fly in the release version if( skill.combat_level > 100 ) skill.combat_level = 100; #endif } //----------- End of function Unit::next_day -----------// //--------- Begin of function Unit::process_recover ---------// // void Unit::process_recover() { if( hit_points==0 || hit_points == max_hit_points ) // this unit is dead already return; err_when( hit_points > max_hit_points ); //---- overseers in firms and ships in harbors recover faster ----// int hitPointsInc; if( unit_mode == UNIT_MODE_OVERSEE || unit_mode == UNIT_MODE_IN_HARBOR ) { hitPointsInc = 2; } //------ for units on ships --------// else if( unit_mode == UNIT_MODE_ON_SHIP ) { //--- if the ship where the unit is on is in the harbor, the unit recovers faster ---// if( unit_array[unit_mode_para]->unit_mode == UNIT_MODE_IN_HARBOR ) hitPointsInc = 2; else hitPointsInc = 1; } //----- only recover when the unit is not moving -----// else if( cur_action == SPRITE_IDLE ) { hitPointsInc = 1; } else return; //---------- recover now -----------// hit_points += hitPointsInc; if( hit_points > max_hit_points ) hit_points = max_hit_points; } //----------- End of function Unit::process_recover -----------// //--------- Begin of function Unit::update_loyalty ---------// // // How loyalty of units are updated: // // General: in a military camp - updated in FirmCamp::update_loyalty() // mobile - no update // // Soldiers led by a general: in a military camp - updated in FirmCamp::update_loyalty() // mobile - updated here // // Other units: no update. // void Unit::update_loyalty() { if( !nation_recno || rank_id==RANK_KING || !unit_res[unit_id]->race_id ) return; if( unit_mode == UNIT_MODE_CONSTRUCT ) // constructor worker will not change their loyalty when they are in a building return; //----- if this unit is a spy, set its fake loyalty ------// if( spy_recno ) // a spy's loyalty is always >= 70 { if( loyalty < 70 ) loyalty = 70+m.random(20); // initialize it to be a number between 70 and 90 target_loyalty = loyalty; return; } //-------- if this is a general ---------// Nation* ownNation = nation_array[nation_recno]; int rc=0; if( rank_id==RANK_GENERAL ) { //----- the general's power affect his loyalty ----// int targetLoyalty = commander_power(); //----- the king's race affects the general's loyalty ----// if( ownNation->race_id == race_id ) targetLoyalty += 20; //----- the kingdom's reputation affects the general's loyalty ----// targetLoyalty += (int)ownNation->reputation/4; //--- the king's leadership also affect the general's loyalty -----// if( ownNation->king_unit_recno ) targetLoyalty += unit_array[ownNation->king_unit_recno]->skill.skill_level / 4; //-- if the unit is rewarded less than the amount of contribution he made, he will become unhappy --// if( nation_contribution > total_reward*2 ) { int decLoyalty = (nation_contribution - total_reward*2)/2; targetLoyalty -= min(50, decLoyalty); // this affect 50 points at maximum } targetLoyalty = min( targetLoyalty, 100 ); target_loyalty = max( targetLoyalty, 0 ); } //-------- if this is a soldier ---------// else if( rank_id==RANK_SOLDIER ) { if( leader_unit_recno ) { //----------------------------------------// // // If this soldier is led by a general, // the targeted loyalty // // = race friendliness between the unit and the general / 2 // + the leader unit's leadership / 2 // //----------------------------------------// if( unit_array.is_deleted(leader_unit_recno) ) { leader_unit_recno = 0; return; } Unit* leaderUnit = unit_array[leader_unit_recno]; int targetLoyalty = 30 + leaderUnit->skill.get_skill(SKILL_LEADING); //---------------------------------------------------// // // Soldiers with higher combat and leadership skill // will get discontented if they are led by a general // with low leadership. // //---------------------------------------------------// targetLoyalty -= skill.combat_level/2; targetLoyalty -= skill.skill_level; if( leaderUnit->rank_id == RANK_KING ) targetLoyalty += 20; if( race_res.is_same_race(race_id, leaderUnit->race_id) ) targetLoyalty += 20; if( targetLoyalty < 0 ) targetLoyalty = 0; targetLoyalty = min( targetLoyalty, 100 ); target_loyalty = max( targetLoyalty, 0 ); } else { target_loyalty = 0; } } //--------- update loyalty ---------// err_when( target_loyalty < 0 || target_loyalty > 100 ); if( target_loyalty > loyalty ) // only increase, no decrease. Decrease are caused by events. Increases are made gradually { int incValue = (target_loyalty - loyalty)/10; int newLoyalty = (int) loyalty + max(1, incValue); if( newLoyalty > target_loyalty ) newLoyalty = target_loyalty; loyalty = newLoyalty; } else if( target_loyalty < loyalty ) // only increase, no decrease. Decrease are caused by events. Increases are made gradually { loyalty--; } err_when( loyalty < 0 || loyalty > 100 ); } //-------- End of function Unit::update_loyalty -----------// //--------- Begin of function Unit::commander_power ---------// // // A commander's power is determined: // // -Population of the towns he controls // -The employment rate of the towns he controls, the higher the // employment rate, the higher his power is // -If there are any other commanders controls the towns at the same time. // -the no. of soldiers led by the commander and their combat levels. // int Unit::commander_power() { //---- if the commander is in a military camp -----// int commanderPower=0; if( unit_mode == UNIT_MODE_OVERSEE ) { Firm* firmPtr = firm_array[unit_mode_para]; if( firmPtr->firm_id == FIRM_CAMP ) { Town* townPtr; for( int i=firmPtr->linked_town_count-1 ; i>=0 ; i-- ) { if( firmPtr->linked_town_enable_array[i] == LINK_EE ) { townPtr = town_array[firmPtr->linked_town_array[i]]; commanderPower += townPtr->population / townPtr->linked_active_camp_count(); } } commanderPower += firmPtr->worker_count*3; // 0 to 24 } else if( firmPtr->firm_id == FIRM_BASE ) { commanderPower = 60; } } else { commanderPower = team_info->member_count*3; // 0 to 24 } return commanderPower; } //----------- End of function Unit::commander_power -----------// //--------- Begin of function Unit::think_betray ---------// // int Unit::think_betray() { int unitRecno = sprite_recno; err_when( unit_array.is_deleted(unitRecno) ); if( spy_recno ) // spies do not betray here, spy has its own functions for betrayal return 0; //----- if the unit is in training or is constructing a building, do not rebel -------// if( !is_visible() && unit_mode != UNIT_MODE_OVERSEE ) return 0; if( loyalty >= UNIT_BETRAY_LOYALTY ) // you when unit is return 0; if( !unit_res[unit_id]->race_id || !nation_recno || rank_id==RANK_KING || spy_recno ) { return 0; } err_when(unit_res[unit_id]->unit_class == UNIT_CLASS_GOD); err_when(unit_id==UNIT_CARAVAN); //------ turn towards other nation --------// int i, bestNationRecno=0, nationScore, bestScore=loyalty; // the score must be larger than the current loyalty Nation *curNation, *nationPtr; int unitRegionId = region_id(); if( loyalty==0 ) // if the loyalty is 0, it will definitely betray bestScore = -100; curNation = nation_array[nation_recno]; for( i=nation_array.size() ; i>0 ; i-- ) { if( nation_array.is_deleted(i) ) continue; if( !curNation->get_relation(i)->has_contact || i==nation_recno ) continue; nationPtr = nation_array[i]; //--- only if the nation has a base town in the region where the unit stands ---// if( !region_array.nation_has_base_town(unitRegionId, i) ) continue; //------------------------------------------------// nationScore = (int) nationPtr->reputation + (nationPtr->overall_rating - curNation->overall_rating); if( race_res.is_same_race(nationPtr->race_id, race_id) ) nationScore += 30; if( nationScore > bestScore ) { bestScore = nationScore; bestNationRecno = i; } } err_when( unit_array.is_deleted(unitRecno) ); if( bestNationRecno ) { return betray(bestNationRecno); } else if( loyalty==0 ) { //----------------------------------------------// // If there is no good nation to turn towards to and // the loyalty has dropped to 0, resign itself and // leave the nation. // // However, if the unit is spy, it will stay with the // nation as it has never been really loyal to the nation. //---------------------------------------------// if( rank_id != RANK_KING && is_visible() && !spy_recno ) { resign(COMMAND_AUTO); return 1; } } return 0; } //-------- End of function Unit::think_betray -----------// //--------- Begin of function Unit::betray ---------// // // If this unit is a spy, this function betray() will be // called by Unit::spy_change_nation() or Firm::capture_firm(). // // If this is not a spy, this function will only be called // by think_betray() and other nation deinit functions. // int Unit::betray(int newNationRecno) { int unitRecno = sprite_recno; //### begin alex 18/3 ###// //err_when( unit_array.is_deleted(unitRecno) ); err_when( unit_array.is_truly_deleted(unitRecno) ); //#### end alex 18/3 ####// err_when( rank_id == RANK_KING ); if( nation_recno == newNationRecno ) return 0; if( unit_mode == UNIT_MODE_CONSTRUCT || // don't change nation when the unit is constructing a firm unit_mode == UNIT_MODE_ON_SHIP ) // don't change nation when the unit is constructing a firm { return 0; } //---------- add news -----------// if( nation_recno == nation_array.player_recno || newNationRecno == nation_array.player_recno ) { //--- if this is a spy, don't display news message for betrayal as it is already displayed in Unit::spy_change_nation() ---// if( !spy_recno ) news_array.unit_betray(sprite_recno, newNationRecno); } //------ change nation now ------// //### begin alex 18/3 ###// //err_when( unit_array.is_deleted(unitRecno) ); err_when( unit_array.is_truly_deleted(unitRecno) ); //#### end alex 18/3 ####// change_nation(newNationRecno); //### begin alex 18/3 ###// //err_when( unit_array.is_deleted(unitRecno) ); err_when( unit_array.is_truly_deleted(unitRecno) ); //#### end alex 18/3 ####// //-------- set the loyalty of the unit -------// if( nation_recno ) { Nation* nationPtr = nation_array[nation_recno]; loyalty = UNIT_BETRAY_LOYALTY + m.random(5); if( nationPtr->reputation > 0 ) change_loyalty( (int) nationPtr->reputation ); if( race_res.is_same_race( nationPtr->race_id, race_id ) ) change_loyalty( 30 ); err_when( loyalty < 0 || loyalty > 100 ); update_loyalty(); // update target loyalty } else //------ if change to independent rebel -------// { loyalty = 0; // no loyalty needed } //--- if this unit is a general, change nation for the units he commands ---// DWORD newTeamId = unit_array.cur_team_id++; if( rank_id==RANK_GENERAL ) { Unit* unitPtr; int i, nationReputation = (int) nation_array[nation_recno]->reputation; for( i=unit_array.size() ; i>0 ; i-- ) { if( unit_array.is_deleted(i) ) continue; unitPtr = unit_array[i]; //---- capture the troop this general commands -----// if( unitPtr->leader_unit_recno == sprite_recno && unitPtr->rank_id == RANK_SOLDIER && unitPtr->is_visible() ) { if( unitPtr->spy_recno ) // if the unit is a spy unitPtr->spy_change_nation(newNationRecno, COMMAND_AUTO); else unitPtr->change_nation(newNationRecno); unitPtr->team_id = newTeamId; // assign new team_id or checking for nation_recno } } } team_id = newTeamId; //### begin alex 18/3 ###// //err_when( unit_array.is_deleted(unitRecno) ); err_when( unit_array.is_truly_deleted(unitRecno) ); //#### end alex 18/3 ####// //------ go to meet the new master -------// if( is_visible() && nation_recno ) { if( !spy_recno || spy_array[spy_recno]->notify_cloaked_nation_flag ) { if( rank_id == RANK_GENERAL ) // generals shouldn't automatically be assigned to camps, they should just move near your villages ai_move_to_nearby_town(); else think_normal_human_action(); // this is an AI function in OUNITAI.CPP } } //### begin alex 18/3 ###// //err_when( unit_array.is_deleted(unitRecno) ); err_when( unit_array.is_truly_deleted(unitRecno) ); //#### end alex 18/3 ####// return 1; } //-------- End of function Unit::betray -----------// //--------- Begin of function Unit::change_nation ---------// // // This function is called when a unit change nation. // It is not necessarily a result of betray, when a spy // hands over his new nation to his parent nation, this // function will also be called. // // newNationRecno - change the nation of the unit. // void Unit::change_nation(int newNationRecno) { err_when( newNationRecno && nation_array.is_deleted(newNationRecno) ); err_when( unit_mode == UNIT_MODE_REBEL ); // rebels do not change nation //---------------------------------// int oldAiUnit = ai_unit; int oldNationRecno = nation_recno; group_select_id = 0; // clear group select id if(way_point_count) reset_way_point_array(); //-- if the player is giving a command to this unit, cancel the command --// if( nation_recno == nation_array.player_recno && sprite_recno == unit_array.selected_recno && power.command_id ) { power.command_id = 0; } //---------- stop all action to attack this unit ------------// unit_array.stop_attack_unit(sprite_recno); //---- update nation_unit_count_array[] ----// unit_res[unit_id]->unit_change_nation(newNationRecno, nation_recno, rank_id); //------- if the nation has an AI action -------// stop2(); // clear the existing order //---------------- update vars ----------------// unit_group_id = unit_array.cur_group_id++; // separate from the current group nation_recno = newNationRecno; home_camp_firm_recno = 0; // reset it original_action_mode = 0; if( race_id ) { nation_contribution = 0; // contribution to the nation total_reward = 0; } //-------- if change to one of the existing nations ------// ai_unit = nation_recno && nation_array[nation_recno]->is_ai(); //------------ update AI info --------------// if( oldAiUnit ) { Nation* nationPtr = nation_array[oldNationRecno]; if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) nationPtr->del_general_info(sprite_recno); else if( unit_res[unit_id]->unit_class == UNIT_CLASS_CARAVAN ) nationPtr->del_caravan_info(sprite_recno); else if( unit_res[unit_id]->unit_class == UNIT_CLASS_SHIP ) nationPtr->del_ship_info(sprite_recno); } if( ai_unit && nation_recno != 0 ) { Nation* nationPtr = nation_array[nation_recno]; if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) nationPtr->add_general_info(sprite_recno); else if( unit_res[unit_id]->unit_class == UNIT_CLASS_CARAVAN ) nationPtr->add_caravan_info(sprite_recno); else if( unit_res[unit_id]->unit_class == UNIT_CLASS_SHIP ) nationPtr->add_ship_info(sprite_recno); } //------ if this unit oversees a firm -----// if( unit_mode==UNIT_MODE_OVERSEE ) firm_array[unit_mode_para]->change_nation(newNationRecno); //----- this unit was defending the town before it gets killed ----// else if( unit_mode==UNIT_MODE_DEFEND_TOWN ) { if( !town_array.is_deleted(unit_mode_para) ) town_array[unit_mode_para]->reduce_defender_count(); set_mode(0); // reset unit mode } //---- if the unit is no longer the same nation as the leader ----// if( leader_unit_recno ) { Unit* leaderUnit = unit_array[leader_unit_recno]; if( leaderUnit->nation_recno != nation_recno ) { leaderUnit->del_team_member(sprite_recno); leader_unit_recno = 0; team_id = 0; } } //------ if it is currently selected -------// if( selected_flag ) info.disp(); } //----------- End of function Unit::change_nation -----------// //--------- Begin of function Unit::pay_expense ---------// // void Unit::pay_expense() { if( game.game_mode == GAME_TEST ) // no deduction in testing game return; if( !nation_recno ) return; //--- if it's a mobile spy or the spy is in its own firm, no need to pay salary here as Spy::pay_expense() will do that ---// // // -If your spies are mobile: // >your nation pays them 1 food and $5 dollars per month // // -If your spies are in an enemy's town or firm: // >the enemy pays them 1 food and the normal salary of their jobs. // // >your nation pays them $5 dollars per month. (your nation pays them no food) // // -If your spies are in your own town or firm: // >your nation pays them 1 food and $5 dollars per month // //------------------------------------------------------// if( spy_recno ) { if( is_visible() ) // the cost will be deducted in spy_array return; if( unit_mode == UNIT_MODE_OVERSEE && firm_array[unit_mode_para]->nation_recno == true_nation_recno() ) { return; } } //---------- if it's a human unit -----------// // // The unit is paid even during its training period in a town // //-------------------------------------------// Nation* nationPtr = nation_array[nation_recno]; if( unit_res[unit_id]->race_id ) { //---------- reduce cash -----------// if( nationPtr->cash > 0 ) { if( rank_id == RANK_SOLDIER ) nationPtr->add_expense( EXPENSE_MOBILE_UNIT, (float) SOLDIER_YEAR_SALARY / 365, 1 ); if( rank_id == RANK_GENERAL ) nationPtr->add_expense( EXPENSE_GENERAL, (float) GENERAL_YEAR_SALARY / 365, 1 ); } else // decrease loyalty if the nation cannot pay the unit { change_loyalty(-1); } //---------- reduce food -----------// if( unit_res[unit_id]->race_id ) // if it's a human unit { if( nationPtr->food > 0 ) nationPtr->consume_food((float) PERSON_FOOD_YEAR_CONSUMPTION / 365); else { if( info.game_date%NO_FOOD_LOYALTY_DECREASE_INTERVAL == 0 ) // decrease 1 loyalty point every 2 days change_loyalty(-1); } } } else //----- it's a non-human unit ------// { if( nationPtr->cash > 0 ) { int expenseType; switch(unit_res[unit_id]->unit_class) { case UNIT_CLASS_WEAPON: expenseType = EXPENSE_WEAPON; break; case UNIT_CLASS_SHIP: expenseType = EXPENSE_SHIP; break; case UNIT_CLASS_CARAVAN: expenseType = EXPENSE_CARAVAN; break; default: expenseType = EXPENSE_MOBILE_UNIT; } nationPtr->add_expense( expenseType, (float) unit_res[unit_id]->year_cost / 365, 1 ); } else // decrease hit points if the nation cannot pay the unit { if( unit_res[unit_id]->unit_class != UNIT_CLASS_CARAVAN ) // Even when caravans are not paid, they still stay in your service. { if( hit_points > 0 ) { hit_points--; if( hit_points < 0 ) hit_points = (float) 0; //--- when the hit points drop to zero and the unit is destroyed ---// if( hit_points==0 ) { if( nation_recno == nation_array.player_recno ) { int unitClass = unit_res[unit_id]->unit_class; if( unitClass==UNIT_CLASS_WEAPON ) news_array.weapon_ship_worn_out(unit_id, get_weapon_version()); else if( unitClass==UNIT_CLASS_SHIP ) news_array.weapon_ship_worn_out(unit_id, 0); } } } } } } } //----------- End of function Unit::pay_expense -----------// //--------- Begin of function Unit::change_hit_points ---------// // void Unit::change_hit_points(float changePoints) { hit_points += changePoints; if( hit_points < 0 ) hit_points = (float) 0; if( hit_points > max_hit_points ) hit_points = max_hit_points; } //-------- End of function Unit::change_hit_points -----------// //--------- Begin of function Unit::change_loyalty ---------// // // changeAmt - amount of loyalty to be changed. // void Unit::change_loyalty(int changeAmt) { int newLoyalty = loyalty + changeAmt; newLoyalty = max(0, newLoyalty); loyalty = min(100, newLoyalty); } //----------- End of function Unit::change_loyalty -----------// //------- Begin of function Unit::inc_minor_combat_level --------// // void Unit::inc_minor_combat_level(int incLevel) { err_when( incLevel<0 || incLevel>100 ); // it cannot be larger than 100, because the current code can't handle it skill.combat_level_minor += incLevel; if( skill.combat_level_minor > 100 ) { if( skill.combat_level < 100 ) set_combat_level(skill.combat_level+1); skill.combat_level_minor -= 100; } } //-------- End of function Unit::inc_minor_combat_level ---------// //------- Begin of function Unit::inc_minor_skill_level --------// // void Unit::inc_minor_skill_level(int incLevel) { err_when( incLevel<0 || incLevel>100 ); skill.skill_level_minor += incLevel; if( skill.skill_level_minor > 100 ) { if( skill.skill_level < 100 ) skill.skill_level++; skill.skill_level_minor -= 100; } } //-------- End of function Unit::inc_minor_skill_level ---------// //--------- Begin of function Unit::set_combat_level ---------// // void Unit::set_combat_level(int combatLevel) { err_when( combatLevel<=0 || combatLevel>100 ); skill.combat_level = combatLevel; UnitInfo* unitInfo = unit_res[unit_id]; int oldMaxHitPoints = max_hit_points; max_hit_points = unitInfo->hit_points * combatLevel / 100; hit_points = hit_points * max_hit_points / oldMaxHitPoints; hit_points = min(hit_points, max_hit_points); // --------- update can_guard_flag -------// if( combatLevel >= unitInfo->guard_combat_level) { can_guard_flag = sprite_info->can_guard_flag; #ifdef AMPLUS if( unit_id == UNIT_ZULU ) can_guard_flag |= 4; // shield during attack delay #endif } else { can_guard_flag = 0; } max_power = skill.combat_level + 50; cur_power = min(cur_power, max_power); } //-------- End of function Unit::set_combat_level -----------// //--------- Begin of function Unit::set_rank ---------// // // Only if the unit has leadership skill, it can be a general or king. // void Unit::set_rank(int rankId) { err_when( !race_id ); #ifdef DEBUG if( !is_visible() ) { err_when( rank_id==RANK_GENERAL && rankId==RANK_SOLDIER ); err_when( rank_id==RANK_SOLDIER && rankId==RANK_GENERAL ); } #endif if( rank_id == rankId ) return; //------- promote --------// if( rankId > rank_id ) change_loyalty(PROMOTE_LOYALTY_INCREASE); //------- demote -----------// else if( rankId < rank_id && rank_id != RANK_KING ) // no decrease in loyalty if a spy king hands his nation to his parent nation and become a general again change_loyalty(-DEMOTE_LOYALTY_DECREASE); //---- update nation_general_count_array[] ----// if( nation_recno ) { UnitInfo* unitInfo = unit_res[unit_id]; if( rank_id == RANK_GENERAL ) // if it was a general originally unitInfo->dec_nation_general_count(nation_recno); if( rankId == RANK_GENERAL ) // if the new rank is general unitInfo->inc_nation_general_count(nation_recno); //------ if demote a king to a unit ------// if( rank_id == RANK_KING && rankId != RANK_KING ) unitInfo->inc_nation_unit_count(nation_recno); // since kings are not included in nation_unit_count, when it is no longer a king, we need to re-increase it. //------ if promote a unit to a king ------// if( rank_id != RANK_KING && rankId == RANK_KING ) unitInfo->dec_nation_unit_count(nation_recno); // since kings are not included in nation_unit_count, we need to decrease it } //----- reset leader_unit_recno if demote a general to soldier ----// if( rank_id == RANK_GENERAL && rankId == RANK_SOLDIER ) { //----- reset leader_unit_recno of the units he commands ----// for( int i=unit_array.size() ; i>0 ; i-- ) { Unit* unitPtr = (Unit*) unit_array.get_ptr(i); // don't use is_deleted() as it filters out units that are currently dying if( unitPtr && unitPtr->leader_unit_recno == sprite_recno ) { unitPtr->leader_unit_recno = 0; unitPtr->team_id = 0; } } //--------- deinit team_info ---------// err_when( !team_info ); mem_del(team_info); team_info = NULL; team_id = 0; } //----- if this is a soldier being promoted to a general -----// else if( rank_id == RANK_SOLDIER && rankId == RANK_GENERAL ) { //-- if this soldier is formerly commanded by a general, detech it ---// if( leader_unit_recno ) { if( !unit_array.is_deleted(leader_unit_recno) ) // the leader unit may have been killed at the same time unit_array[leader_unit_recno]->del_team_member(sprite_recno); leader_unit_recno = 0; } } //-------------- update AI info --------------// if( ai_unit ) { if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) nation_array[nation_recno]->del_general_info(sprite_recno); rank_id = rankId; if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) nation_array[nation_recno]->add_general_info(sprite_recno); } else { rank_id = rankId; } //----- if this is a general/king ------// if( rank_id == RANK_GENERAL || rank_id == RANK_KING ) { //--------- init team_info -------// if( !team_info ) { team_info = (TeamInfo*) mem_add( sizeof(TeamInfo) ); team_info->member_count = 0; } //--- set leadership if this unit does not have any now ----// if( skill.skill_id != SKILL_LEADING ) { skill.skill_id = SKILL_LEADING; skill.skill_level = 10 + m.random(40); } } //------ refresh if the current unit is selected -----// if( unit_array.selected_recno == sprite_recno ) info.disp(); } //-------- End of function Unit::set_rank -----------// //--------- Begin of function Unit::embark ---------// // // Order this unit to embark an vehicle // // vehicleRecno - recno of the vehicle. // void Unit::embark(int vehicleRecno) { err_here(); // this function is no longer functional Unit* vehiclePtr = unit_array[vehicleRecno]; if( unit_res[unit_id]->vehicle_id == vehiclePtr->unit_id ) // not the right vehicle return; int vehicleUnitId = unit_res[unit_id]->vehicle_unit_id; float vehicleHitPoints = vehiclePtr->hit_points; int xLoc = vehiclePtr->cur_x_loc(); int yLoc = vehiclePtr->cur_y_loc(); //------- delete the vehicle unit --------// unit_array.del(vehicleRecno); // delete the vehicle (e.g. horse) //--------- add the combined unit -------// int newUnitRecno = unit_array.add_unit(vehicleUnitId, nation_recno, rank_id, loyalty, xLoc, yLoc); // add the combined unit (e.g. cavalry) UnitVehicle* unitVehicle = (UnitVehicle*) unit_array[newUnitRecno]; unitVehicle->skill = skill; unitVehicle->set_combat_level(skill.combat_level); unitVehicle->solider_hit_points = (int) hit_points; unitVehicle->vehicle_hit_points = (int) vehicleHitPoints; unitVehicle->hit_points = (float) unitVehicle->solider_hit_points + unitVehicle->vehicle_hit_points; //-------- delete the solider unit ---------// unit_array.del(sprite_recno); // delete the embarker (e.g. knight) } //-------- End of function Unit::embark -----------// //--------- Begin of function Unit::reward ---------// // // rewardNationRecno - the nation which does this reward. // void Unit::reward(int rewardNationRecno) { // ###### patch begin Gilbert 24/9 ########// if( nation_array[rewardNationRecno]->cash < REWARD_COST ) return; // ###### patch end Gilbert 24/9 ########// //--------- if this is a spy ---------// if( spy_recno && true_nation_recno() == rewardNationRecno ) // if the spy's owning nation rewards the spy { spy_array[spy_recno]->change_loyalty(REWARD_LOYALTY_INCREASE); } //--- if this spy's nation_recno & true_nation_recno() are both == rewardNationRecno, it's true loyalty and cloaked loyalty will both be increased ---// if( nation_recno == rewardNationRecno ) { total_reward += REWARD_COST; change_loyalty(REWARD_LOYALTY_INCREASE); } nation_array[rewardNationRecno]->add_expense(EXPENSE_REWARD_UNIT, (float)REWARD_COST); } //----------- End of function Unit::reward -----------// //------- Begin of function Unit::overseer_migrate ---------// // // Order the overseer migrate to a new town but still keeps // working for the same firm. // // destTownRecno - the recno of the town the worker should // migrate to. // void Unit::overseer_migrate(int destTownRecno) { err_when( unit_mode!=UNIT_MODE_OVERSEE ); int curTownRecno = firm_array[unit_mode_para]->overseer_town_recno; //------- decrease the population of the unit's home town ------// town_array[curTownRecno]->dec_pop(race_id, 1); //--------- increase the population of the target town ------// town_array[destTownRecno]->inc_pop(race_id, 1, loyalty ); } //-------- End of function Unit::overseer_migrate ---------// //--------- Begin of function Unit::group_transform ---------// void Unit::group_transform(char remoteAction, short *selectedArray, short selectedCount) { } //----------- End of function Unit::group_transform -----------// //--------- Begin of function Unit::transform ---------// // // Transform the unit into another unit type. // void Unit::transform() { /* UnitInfo* unitInfo = unit_res[unit_id]; if( unitInfo->transform_unit_id==0 ) return; UnitInfo* newUnitInfo = unit_res[unitInfo->transform_unit_id]; //--- check if the unit has the required combat level ---// if( skill.combat_level < unitInfo->transform_combat_level ) return; //------ free up space on the map for the new unit -----// int xLoc=next_x_loc(), yLoc=next_y_loc(); stop(); deinit_sprite(1); // 1-keep the unit selected if it is currently selected //---- locate the space for the new unit as their size, mobile type may be different ----// SpriteInfo* spriteInfo = sprite_res[newUnitInfo->sprite_id]; int xLoc2 = xLoc + spriteInfo->loc_width-1; int yLoc2 = yLoc + spriteInfo->loc_height-1; if( !world.check_unit_space(xLoc, yLoc, xLoc2, yLoc2, newUnitInfo->mobile_type) ) // first check if if is free to create unit on the same location { if( !world.locate_space( xLoc, yLoc, xLoc+spriteInfo->loc_width-1, yLoc+spriteInfo->loc_height-1, spriteInfo->loc_width, spriteInfo->loc_height, newUnitInfo->mobile_type) ) { init_sprite( xLoc, yLoc ); // not able to find space for the new unit, transformation cancelled. return; } } //--------- transform now ------------// deinit_unit_id(); init_unit_id(unitInfo->transform_unit_id); init_sprite( xLoc, yLoc ); */ } //----------- End of function Unit::transform -----------// //--------- Begin of function Unit::spy_change_nation ---------// // // Change the deceiving nation recno of a spy unit which you control. // // newNationRecno - the new nation the spy changes its cloack to // remoteAction - remote action type // void Unit::spy_change_nation(int newNationRecno, char remoteAction) { if( newNationRecno == nation_recno ) return; if( newNationRecno && nation_array.is_deleted(newNationRecno) ) // this can happen in a multiplayer message return; //------- if this is a remote action -------// if( !remoteAction && remote.is_enable() ) { // packet structure short *shortPtr = (short *)remote.new_send_queue_msg(MSG_UNIT_SPY_NATION, 2*sizeof(short) ); *shortPtr = sprite_recno; shortPtr[1] = newNationRecno; return; } //----- update the var in Spy ------// Spy* spyPtr = spy_array[spy_recno]; //--- when a spy change cloak to another nation, he can't cloak as a general, he must become a soldier first ---// if( is_visible() && // if the spy is a commander in a camp, don't set its rank to soldier rank_id == RANK_GENERAL && newNationRecno != spyPtr->true_nation_recno ) { set_rank(RANK_SOLDIER); } //---------------------------------------------------// // // If this spy unit is a general or an overseer of the // cloaked nation, when he changes nation, that will // inevitably be noticed by the cloaked nation. // //---------------------------------------------------// if( spyPtr->true_nation_recno != nation_array.player_recno ) // only send news message if he is not the player's own spy { if( rank_id == RANK_GENERAL || unit_mode == UNIT_MODE_OVERSEE || spyPtr->notify_cloaked_nation_flag ) { //-- if this spy's cloaked nation is the player's nation, the player will be notified --// if( nation_recno == nation_array.player_recno ) news_array.unit_betray(sprite_recno, newNationRecno); } //---- send news to the cloaked nation if notify flag is on ---// if( spyPtr->notify_cloaked_nation_flag ) { if( newNationRecno == nation_array.player_recno ) // cloaked as the player's nation news_array.unit_betray(sprite_recno, newNationRecno); } } //--------- change nation recno now --------// spyPtr->cloaked_nation_recno = newNationRecno; betray(newNationRecno); // call the betray function to change natino. There is no difference between a spy changing nation and a unit truly betrays } //----------- End of function Unit::spy_change_nation -----------// //--------- Begin of function Unit::can_spy_change_nation ---------// // // Whether the spy unit can change its spy cloak now or not. // // If there are enemy nearby, the unit cannot change its cloak. // int Unit::can_spy_change_nation() { if( !spy_recno ) return 0; //--------------------------------------------// int xLoc1=cur_x_loc()-SPY_ENEMY_RANGE, yLoc1=cur_y_loc()-SPY_ENEMY_RANGE; int xLoc2=cur_x_loc()+SPY_ENEMY_RANGE, yLoc2=cur_y_loc()+SPY_ENEMY_RANGE; xLoc1 = max(0, xLoc1); yLoc1 = max(0, yLoc1); xLoc2 = min(MAX_WORLD_X_LOC-1, xLoc2); yLoc2 = min(MAX_WORLD_Y_LOC-1, yLoc2); int xLoc, yLoc; int unitRecno, trueNationRecno = true_nation_recno(); Location* locPtr; for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ ) { locPtr = world.get_loc(xLoc1, yLoc); for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ ) { if( locPtr->has_unit(UNIT_LAND) ) unitRecno = locPtr->unit_recno(UNIT_LAND); else if( locPtr->has_unit(UNIT_SEA) ) unitRecno = locPtr->unit_recno(UNIT_SEA); else if( locPtr->has_unit(UNIT_AIR) ) unitRecno = locPtr->unit_recno(UNIT_AIR); else continue; if( unit_array.is_deleted(unitRecno) ) // the unit is dying, its recno is still in the location continue; if( unit_array[unitRecno]->true_nation_recno() != trueNationRecno ) return 0; } } return 1; } //----------- End of function Unit::can_spy_change_nation -----------// //--------- Begin of function Unit::resign ---------// // // Resign the unit. // void Unit::resign(int remoteAction) { if( !remoteAction && remote.is_enable() ) { // packet structure : short *shortPtr = (short *)remote.new_send_queue_msg(MSG_UNIT_RESIGN, 2*sizeof(short)); *shortPtr = sprite_recno; shortPtr[1] = nation_array.player_recno; return; } //---- increase the wandering count when a unit is disbanded ----// if( race_id ) town_array.race_wander_pop_array[race_id-1] += 2; // disbanding one resulted in two wandering units to make the effect more significant //--- if the unit is visible, call stop2() so if it has an AI action queue, that will be reset ---// if( is_visible() ) stop2(); //--- if the spy is resigned by an enemy, display a message ---// if( spy_recno && true_nation_recno() != nation_recno ) // the spy is cloaked in an enemy nation when it is resigned { //------ decrease reputation ------// nation_array[true_nation_recno()]->change_reputation((float)-SPY_KILLED_REPUTATION_DECREASE); //------- add news message -------// if( true_nation_recno() == nation_array.player_recno || // display when the player's spy is revealed or the player has revealed an enemy spy nation_recno == nation_array.player_recno ) { //--- If a spy is caught, the spy's nation's reputation wil decrease ---// news_array.spy_killed(spy_recno); } } //----------------------------------------------// if( rank_id == RANK_GENERAL ) // if this is a general, news_array.general_die() will be called, set news_add_flag to 0 to suppress the display of thew news news_array.news_add_flag=0; unit_array.del( sprite_recno ); news_array.news_add_flag=1; } //----------- End of function Unit::resign -----------// //--------- Begin of function Unit::region_id ---------// // // Return the region id. of this unit. // BYTE Unit::region_id() { if( is_visible() ) { return world.get_region_id( next_x_loc(), next_y_loc() ); } else { if( unit_mode == UNIT_MODE_OVERSEE ) return firm_array[unit_mode_para]->region_id; } return 0; } //----------- End of function Unit::region_id -----------// //--------- Begin of function Unit::del_team_member ---------// // // Delete a specific member of the team led by this leader. // void Unit::del_team_member(int unitRecno) { err_when( !team_info ); for( int i=0 ; imember_count ; i++ ) { if( team_info->member_unit_array[i] == unitRecno ) { err_when( team_info->member_count > MAX_TEAM_MEMBER ); m.del_array_rec( team_info->member_unit_array, team_info->member_count, sizeof( team_info->member_unit_array[0] ), i+1 ); team_info->member_count--; return; } } //-------------------------------------------------------// // // Note: for rebels and monsters, although they have // leader_unit_recno, their team_info is not used. // So del_team_member() won't be able to match the // unit in its member_unit_array[]. // //-------------------------------------------------------// } //----------- End of function Unit::del_team_member -----------// //--------- Begin of function Unit::validate_team ---------// // // Validate the member in this commander's team. If there // are any units with hit_points <= 0, delete them. // // Those unit may just be killed, so soon that the Unit's set_die() // function hsa been called yet. validate_team() function must // be called before all issunig any new team actions. // void Unit::validate_team() { err_when( !team_info ); int unitRecno; for( int i=team_info->member_count-1 ; i>=0 ; i-- ) { unitRecno = team_info->member_unit_array[i]; if( unit_array.is_deleted(unitRecno) ) { err_when( team_info->member_count > MAX_TEAM_MEMBER ); m.del_array_rec( team_info->member_unit_array, team_info->member_count, sizeof( team_info->member_unit_array[0] ), i+1 ); team_info->member_count--; } } } //----------- End of function Unit::validate_team -----------// //--------- Begin of function Unit::commanded_soldier_count ---------// // // Return the no. of soldiers commanded by this unit. // int Unit::commanded_soldier_count() { if( rank_id != RANK_GENERAL && rank_id != RANK_KING ) return 0; //--------------------------------------// err_when( !team_info ); int soldierCount=0; if( is_visible() ) { soldierCount = team_info->member_count-1; if( soldierCount < 0 ) // member_count can be 0 soldierCount = 0; } else { if( unit_mode == UNIT_MODE_OVERSEE ) { Firm* firmPtr = firm_array[unit_mode_para]; if( firmPtr->firm_id == FIRM_CAMP ) // it can be an overseer of a seat of powre soldierCount = firmPtr->worker_count; } } return soldierCount; } //----------- End of function Unit::commanded_soldier_count -----------// //----------- Begin of function Unit::fix_attack_info -----------// void Unit::fix_attack_info() { int techLevel; UnitInfo *unitInfo = unit_res[unit_id]; attack_count = unitInfo->attack_count; if( attack_count > 0 && unitInfo->first_attack > 0) attack_info_array = unit_res.attack_info_array+unitInfo->first_attack-1; else attack_info_array = NULL; if( unitInfo->unit_class == UNIT_CLASS_WEAPON && (techLevel=get_weapon_version()) > 0 ) { switch( unit_id ) { case UNIT_BALLISTA: #ifdef AMPLUS case UNIT_F_BALLISTA: #endif attack_count = 2; break; case UNIT_EXPLOSIVE_CART: attack_count = 0; break; default: attack_count = 1; } if( attack_count > 0) { attack_info_array += (techLevel-1) * attack_count; } else { // no attack like explosive cart attack_info_array = NULL; } } } //----------- End of function Unit::fix_attack_info -----------// //----------- Begin of function Unit::return_camp -----------// // // Order this unit to return to the camp. For ordering many // units to return to a camp, UnitArray::return_camp() should // be called instead. // int Unit::return_camp() { if( !home_camp_firm_recno ) return 0; err_when( firm_array.is_deleted(home_camp_firm_recno) ); Firm* firmPtr = firm_array[home_camp_firm_recno]; if( firmPtr->region_id != region_id() ) return 0; err_when( firmPtr->firm_id != FIRM_CAMP ); err_when( firmPtr->nation_recno != nation_recno ); //--------- assign now ---------// assign(firmPtr->loc_x1, firmPtr->loc_y1); force_move_flag = 1; return cur_action != SPRITE_IDLE; } //----------- End of function Unit::return_camp -----------// //----------- Begin of function Unit::unit_power -----------// // // Return a power index of the weapon, this is for calculating // the total combat level of a target. // int Unit::unit_power() { UnitInfo* unitInfo = unit_res[unit_id]; if( unitInfo->unit_class == UNIT_CLASS_WEAPON ) { return (int) hit_points + (unitInfo->weapon_power + get_weapon_version() - 1) * 15; } else { return (int) hit_points; } } //----------- End of function Unit::unit_power -----------// //----------- Begin of function Unit::get_cur_loc -----------// // // xLoc, yLoc - reference vars for returning the // location of this unit // // return : 0 - if this unit is invisible // 1 - if a location has been returned. // int Unit::get_cur_loc(short& xLoc, short& yLoc) { if( is_visible() ) { xLoc = next_x_loc(); // update location yLoc = next_y_loc(); } else if( unit_mode == UNIT_MODE_OVERSEE || unit_mode==UNIT_MODE_CONSTRUCT || unit_mode == UNIT_MODE_IN_HARBOR ) { Firm* firmPtr = firm_array[unit_mode_para]; xLoc = firmPtr->center_x; yLoc = firmPtr->center_y; } else if( unit_mode == UNIT_MODE_ON_SHIP ) { Unit* unitPtr = unit_array[unit_mode_para]; //### begin alex 22/10 ###// //xLoc = unitPtr->next_x_loc(); //yLoc = unitPtr->next_y_loc(); if(unitPtr->is_visible()) { xLoc = unitPtr->next_x_loc(); yLoc = unitPtr->next_y_loc(); } else { err_when(unitPtr->unit_mode!=UNIT_MODE_IN_HARBOR); Firm *firmPtr = firm_array[unitPtr->unit_mode_para]; xLoc = firmPtr->center_x; yLoc = firmPtr->center_y; } //#### end alex 22/10 ####// } else return 0; return 1; } //----------- End of function Unit::get_cur_loc -----------// //----------- Begin of function Unit::add_way_point -----------// // Add the point to the way_point_array if it is not in the array. // Otherwise, remove the point from the way_point_array. // // x - x coordinate of the point // y - y coordinate of the point // void Unit::add_way_point(short x, short y) { if(way_point_count>=100) return; // too many way point if(way_point_count>1) // don't allow to remove the 1st node, since the unit is moving there { ResultNode *nodePtr = way_point_array + 1; for(int i=1; inode_x == x && nodePtr->node_y == y) // remove this node { m.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), i+1); // remove 1st node way_point_count--; return; // there should be one and only one node with the same value } } } //-------------- add new node -----------------// if(way_point_count>=way_point_array_size) // buffer full { way_point_array_size += WAY_POINT_ADJUST_SIZE; if(way_point_count) way_point_array = (ResultNode*) mem_resize(way_point_array, way_point_array_size*sizeof(ResultNode)); else way_point_array = (ResultNode*) mem_add(sizeof(ResultNode)*WAY_POINT_ADJUST_SIZE); } ResultNode *nodePtr = way_point_array + way_point_count; nodePtr->node_x = x; nodePtr->node_y = y; way_point_count++; if(way_point_count==1) move_to(x, y); } //----------- End of function Unit::add_way_point -----------// //----------- Begin of function Unit::reset_way_point_array -----------// void Unit::reset_way_point_array() { //------------------------------------------------------------------------------------// // There are nly two conditions to reset the way_point_array // 1) action_mode2!=ACTION_MOVE in Unit::stop() // 2) dest? != node_? in the first node of way_point_array in calling Unit::move_to() //------------------------------------------------------------------------------------// if(way_point_array_size) { mem_del(way_point_array); way_point_array = NULL; way_point_array_size = 0; way_point_count = 0; } } //----------- End of function Unit::reset_way_point_array -----------// //----------- Begin of function Unit::process_way_point -----------// // move to the next way point and remove this point from the // way_point_array // void Unit::process_way_point() { err_when(action_mode!=ACTION_STOP || action_mode2!=ACTION_STOP || cur_action!=SPRITE_IDLE); int destX, destY; if(way_point_count>1) { ResultNode *nodePtr = way_point_array+1; destX = nodePtr->node_x; destY = nodePtr->node_y; m.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), 1); // remove 1st node way_point_count--; } else // only one unprocessed node { ResultNode *nodePtr = way_point_array; destX = nodePtr->node_x; destY = nodePtr->node_y; //m.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), 1); // remove 1st node //way_point_count--; } move_to(destX, destY); } //----------- End of function Unit::process_way_point -----------// //----------- Begin of function TeamInfo::TeamInfo ---------// TeamInfo::TeamInfo() { memset( this, 0, sizeof(TeamInfo) ); } //----------- End of function TeamInfo::TeamInfo ---------//