/* * 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 : OUNITATM.CPP //Description : Object Unit attack functions to handle blocked movement and group attacking //Owner : Alex #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::search_or_stop ---------// // process searching or stop the units // // destX - x location to move to // destY - y location to move to // preserveAction - preserve action when calling to stop // searchMode - search mode for searching // miscNo - miscenllaneous information // void Unit::search_or_stop(int destX, int destY, int preserveAction, short searchMode, short miscNo) { Location *locPtr = world.get_loc(destX, destY); if(!locPtr->can_move(mobile_type)) { stop(KEEP_PRESERVE_ACTION); // let reactivate..() call searching later //waiting_term = MAX_SEARCH_OT_STOP_WAIT_TERM; } else { search(destX, destY, preserveAction, searchMode, miscNo); /*if(mobile_type==UNIT_LAND) search(destX, destY, preserveAction, searchMode, miscNo); else waiting_term = 0;*/ } } //----------- End of function Unit::search_or_stop -----------// //------ Begin of function Unit::search_or_wait ---------// // call searching or wait // void Unit::search_or_wait() { #define SQUARE1 9 #define SQUARE2 25 #define SQUARE3 49 #define DIMENSION 7 int curXLoc = next_x_loc(), curYLoc = next_y_loc(); short surrArray[SQUARE3]; int xShift, yShift, checkXLoc, checkYLoc, hasFree, i, shouldWait; short unitRecno; Unit *unitPtr; Location *locPtr; //----------------------------------------------------------------------------// // wait if the unit is totally blocked. Otherwise, call searching //----------------------------------------------------------------------------// memset(surrArray, 0, sizeof(short)*SQUARE3); for(shouldWait=0, hasFree=0, i=2; i<=SQUARE3; i++) { if(i==SQUARE1 || i==SQUARE2 || i==SQUARE3) { if(!hasFree) { shouldWait++; break; } else hasFree = 0; } m.cal_move_around_a_point(i, DIMENSION, DIMENSION, xShift, yShift); checkXLoc = curXLoc+xShift; checkYLoc = curYLoc+yShift; if(checkXLoc<0 || checkXLoc>=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC) continue; locPtr = world.get_loc(checkXLoc, checkYLoc); if(!locPtr->has_unit(mobile_type)) { hasFree++; continue; } unitRecno = locPtr->unit_recno(mobile_type); err_when(!unitRecno); if(unit_array.is_deleted(unitRecno)) continue; unitPtr = unit_array[unitRecno]; if(unitPtr->nation_recno==nation_recno && unitPtr->unit_group_id==unit_group_id && ((unitPtr->cur_action==SPRITE_WAIT && unitPtr->waiting_term>1) || unitPtr->cur_action==SPRITE_TURN || unitPtr->cur_action==SPRITE_MOVE)) { surrArray[i-2] = unitRecno; unitPtr->unit_group_id++; } } //------------------- call searching if should not wait --------------------// if(!shouldWait) search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_IN_A_GROUP); //search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_IN_A_GROUP); for(i=0; iunit_group_id--; } } if(shouldWait) set_wait(); } //-------- End of function Unit::search_or_wait ---------// //------ Begin of function Unit::handle_blocked_move_s11 -------// // both blocked and blocking are size 1 // void Unit::handle_blocked_move_s11(Unit *unitPtr) { err_when( world.get_unit_recno(next_x_loc(), next_y_loc(), mobile_type) != sprite_recno ); err_when( world.get_unit_recno(unitPtr->next_x_loc(), unitPtr->next_y_loc(), unitPtr->mobile_type) != unitPtr->sprite_recno ); err_when(cur_x!=next_x || cur_y!=next_y); int waitTerm; int moveStep = move_step_magn(); switch(unitPtr->cur_action) { //------------------------------------------------------------------------------------// // handle blocked for units belonging to the same nation. For those belonging to other // nations, wait for it moving to other locations or search for another path. //------------------------------------------------------------------------------------// case SPRITE_WAIT: // the blocking unit is waiting err_when(unitPtr->go_x==unitPtr->cur_x && unitPtr->go_y==unitPtr->cur_y); case SPRITE_TURN: if(unitPtr->nation_recno==nation_recno) handle_blocked_wait(unitPtr); // check for cycle wait for our nation else if(waiting_term>=MAX_WAITING_TERM_DIFF) { search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall searching waiting_term = 0; } else // wait set_wait(); return; //------------------------------------------------------------------------------------// // We know from the cur_action of the blocking unit it is moving to another locations, // the blocked unit wait for a number of terms or search again. //------------------------------------------------------------------------------------// case SPRITE_MOVE: case SPRITE_READY_TO_MOVE: case SPRITE_SHIP_EXTRA_MOVE: if(unit_id!=UNIT_CARAVAN && unitPtr->unit_id==UNIT_CARAVAN) // don't wait for caravans, and caravans don't wait for other units { search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); } else { waitTerm = (nation_recno==unitPtr->nation_recno) ? MAX_WAITING_TERM_SAME : MAX_WAITING_TERM_DIFF; if(waiting_term>=waitTerm) { search_or_wait(); waiting_term = 0; } else set_wait(); } return; //------------------------------------------------------------------------------------// // handling blocked for idle unit //------------------------------------------------------------------------------------// case SPRITE_IDLE: err_when(unitPtr->result_node_array!=NULL); if(unitPtr->action_mode==ACTION_SHIP_TO_BEACH) { //----------------------------------------------------------------------// // the blocking unit is trying to move to beach, so wait a number of terms, // or call searching again //----------------------------------------------------------------------// if(abs(unitPtr->next_x_loc()-unitPtr->action_x_loc2)<=moveStep && abs(unitPtr->next_y_loc()-unitPtr->action_y_loc2)<=moveStep && terrain_res[world.get_loc(unitPtr->action_x_loc2, unitPtr->action_y_loc2)->terrain_id]->average_type !=TERRAIN_OCEAN) { if(action_mode2==ACTION_SHIP_TO_BEACH && action_x_loc2==unitPtr->action_x_loc2 && action_y_loc2==unitPtr->action_y_loc2) { int tempX, tempY; ship_to_beach(action_x_loc2, action_y_loc2, tempX, tempY); } else { waitTerm = (nation_recno==unitPtr->nation_recno) ? MAX_WAITING_TERM_SAME : MAX_WAITING_TERM_DIFF; if(waiting_term>=waitTerm) stop2(); else set_wait(); } return; } } if(unitPtr->nation_recno==nation_recno) //-------- same nation { //------------------------------------------------------------------------------------// // units from our nation //------------------------------------------------------------------------------------// if(unitPtr->unit_group_id==unit_group_id) { //--------------- from the same group -----------------// if(way_point_count && !unitPtr->way_point_count) { //------------ reset way point --------------// stop2(); reset_way_point_array(); } else if((unitPtr->next_x_loc() != move_to_x_loc || unitPtr->next_y_loc() != move_to_y_loc) && (unitPtr->cur_action==SPRITE_IDLE && unitPtr->action_mode2==ACTION_STOP)) move_to_my_loc(unitPtr); // push the blocking unit and exchange their destination else if(unitPtr->action_mode == ACTION_SETTLE) set_wait(); // wait for the settler else if(waiting_term>MAX_WAITING_TERM_SAME) { //---------- stop if wait too long ----------// terminate_move(); waiting_term = 0; } else set_wait(); } else if(unitPtr->action_mode2==ACTION_STOP) handle_blocked_by_idle_unit(unitPtr); else if(way_point_count && !unitPtr->way_point_count) { stop2(); reset_way_point_array(); } else search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall A* algorithm by default mode } else // different nation { //------------------------------------------------------------------------------------// // units from other nations //------------------------------------------------------------------------------------// if(unitPtr->next_x_loc() == move_to_x_loc && unitPtr->next_y_loc() == move_to_y_loc) { terminate_move(); // destination occupied by other unit if(action_mode==ACTION_ATTACK_UNIT && unitPtr->nation_recno!=nation_recno && unitPtr->sprite_recno==action_para) { err_when(action_x_loc!=unitPtr->next_x_loc() || action_y_loc!=unitPtr->next_y_loc()); err_when(action_mode2!=ACTION_ATTACK_UNIT && action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET && action_mode2!=ACTION_DEFEND_TOWN_ATTACK_TARGET && action_mode2!=ACTION_MONSTER_DEFEND_ATTACK_TARGET); err_when(action_para2!=action_para); err_when(action_x_loc!=action_x_loc2 || action_y_loc!=action_y_loc2); set_dir(next_x, next_y, unitPtr->next_x, unitPtr->next_y); if(is_dir_correct()) attack_unit(action_para); else set_turn(); cur_frame = 1; } } else search_or_stop(move_to_x_loc, move_to_y_loc, 1); // recall A* algorithm by default mode } return; //------------------------------------------------------------------------------------// // don't wait for attackers from other nations, search for another path. //------------------------------------------------------------------------------------// case SPRITE_ATTACK: //----------------------------------------------------------------// // don't wait for other nation unit, call searching again //----------------------------------------------------------------// if(nation_recno!=unitPtr->nation_recno) { search_or_stop(move_to_x_loc, move_to_y_loc, 1); return; } //------------------------------------------------------------------------------------// // for attackers owned by our commander, handled blocked case by case as follows. //------------------------------------------------------------------------------------// switch(unitPtr->action_mode) { case ACTION_ATTACK_UNIT: if(action_para && !unit_array.is_deleted(action_para)) { Unit *targetPtr = unit_array[action_para]; handle_blocked_attack_unit(unitPtr, targetPtr); } else search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); break; case ACTION_ATTACK_FIRM: if(!unitPtr->action_para || firm_array.is_deleted(unitPtr->action_para)) set_wait(); else handle_blocked_attack_firm(unitPtr); break; case ACTION_ATTACK_TOWN: if(!unitPtr->action_para || town_array.is_deleted(unitPtr->action_para)) set_wait(); else handle_blocked_attack_town(unitPtr); break; case ACTION_ATTACK_WALL: if(unitPtr->action_para) set_wait(); else handle_blocked_attack_wall(unitPtr); break; case ACTION_GO_CAST_POWER: set_wait(); break; default: err_here(); break; } return; //------------------------------------------------------------------------------------// // the blocked unit can pass after the blocking unit disappears in air. //------------------------------------------------------------------------------------// case SPRITE_DIE: set_wait(); // assume this unit will not wait too long return; default: err_here(); break; } err_when(cur_x==go_x && cur_y==go_y && (cur_x!=next_x || cur_y!=next_y)); } //------- End of function Unit::handle_blocked_move_s11 --------// //------ Begin of function Unit::handle_blocked_same_target_attack ---------// // the target of the blocked unit and this unit are same // // unitPtr - pointer to blocking uit // targetPtr - pointer to target unit // void Unit::handle_blocked_same_target_attack(Unit* unitPtr, Unit* targetPtr) { //----------------------------------------------------------// // this unit is now waiting and the unit pointed by unitPtr // is attacking the unit pointed by targetPtr //----------------------------------------------------------// err_when(cur_x%ZOOM_LOC_WIDTH || cur_y%ZOOM_LOC_HEIGHT); err_when(cur_x!=next_x || cur_y!=next_y); err_when(cur_x==go_x && cur_y==go_y); if(space_for_attack(action_x_loc, action_y_loc, targetPtr->mobile_type, targetPtr->sprite_info->loc_width, targetPtr->sprite_info->loc_height)) { err_when(action_x_loc!=action_x_loc2 || action_y_loc!=action_y_loc2); search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_TO_ATTACK, targetPtr->sprite_recno); //search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_TO_ATTACK, targetPtr->sprite_recno); } else if(in_any_defense_mode()) { err_when(action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET && action_mode2!=ACTION_DEFEND_TOWN_ATTACK_TARGET && action_mode2!=ACTION_MONSTER_DEFEND_ATTACK_TARGET); general_defend_mode_detect_target(); } else if(m.points_distance(next_x_loc(), next_y_loc(), action_x_loc, action_y_loc) unitPtr - pointer to blocking uit // targetPtr - pointer to target unit // void Unit::handle_blocked_attack_unit(Unit* unitPtr, Unit* targetPtr) { if(action_para==targetPtr->sprite_recno && unitPtr->action_para==targetPtr->sprite_recno && action_mode==unitPtr->action_mode) { //----------------- both attack the same target --------------------// err_when(unit_array.is_deleted(action_para)); handle_blocked_same_target_attack(unitPtr, targetPtr); } else search_or_stop(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); // recall A* algorithm //search(move_to_x_loc, move_to_y_loc, 1, SEARCH_MODE_A_UNIT_IN_GROUP); // recall A* algorithm } //----------- End of function Unit::handle_blocked_attack_unit -----------// //--------- Begin of function Unit::handle_blocked_attack_firm ---------// // handle the case that the way of this unit to the target firm is blocked by // another unit // // unitPtr - the blocking unit // void Unit::handle_blocked_attack_firm(Unit *unitPtr) { if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_para==unitPtr->action_para && action_mode==unitPtr->action_mode) { //------------- both attacks the same firm ------------// Location *locPtr = world.get_loc(action_x_loc, action_y_loc); if(!locPtr->is_firm()) stop2(KEEP_DEFENSE_MODE); // stop since firm is deleted else { Firm *firmPtr = firm_array[action_para]; FirmInfo *firmInfo = firm_res[firmPtr->firm_id]; if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, firmInfo->loc_width, firmInfo->loc_height)) { //------------ found surrounding place to attack the firm -------------// if(mobile_type==UNIT_LAND) set_move_to_surround(firmPtr->loc_x1, firmPtr->loc_y1, firmInfo->loc_width, firmInfo->loc_height, BUILDING_TYPE_FIRM_MOVE_TO); else attack_firm(firmPtr->loc_x1, firmPtr->loc_y1); } else // no surrounding place found, stop now stop(KEEP_PRESERVE_ACTION); } } else // let process_idle() handle it stop(); } //----------- End of function Unit::handle_blocked_attack_firm -----------// //--------- Begin of function Unit::handle_blocked_attack_town ---------// // handle the case that the way of this unit to the target town is blocked by // another unit // // unitPtr - the blocking unit // void Unit::handle_blocked_attack_town(Unit *unitPtr) { if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_para==unitPtr->action_para && action_mode==unitPtr->action_mode) { //---------------- both attacks the same town ----------------------// Location *locPtr = world.get_loc(action_x_loc, action_y_loc); if(!locPtr->is_town()) stop2(KEEP_DEFENSE_MODE); // stop since town is deleted else if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT)) { //------------ found surrounding place to attack the town -------------// Town *townPtr = town_array[action_para]; { if(mobile_type==UNIT_LAND) set_move_to_surround(townPtr->loc_x1, townPtr->loc_y1, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT, BUILDING_TYPE_TOWN_MOVE_TO); else attack_town(townPtr->loc_x1, townPtr->loc_y1); } } else // no surrounding place found, stop now stop(KEEP_PRESERVE_ACTION); } else stop(); } //----------- End of function Unit::handle_blocked_attack_town -----------// //--------- Begin of function Unit::handle_blocked_attack_wall ---------// // handle the case that the way of this unit to the target wall is blocked by // another unit // // unitPtr - the blocking unit // void Unit::handle_blocked_attack_wall(Unit *unitPtr) { if(action_x_loc==unitPtr->action_x_loc && action_y_loc==unitPtr->action_y_loc && action_mode==unitPtr->action_mode) { //------------- both attacks the same wall ------------// Location *locPtr = world.get_loc(action_x_loc, action_y_loc); if(!locPtr->is_wall()) stop2(KEEP_DEFENSE_MODE); // stop since wall is deleted else if(space_for_attack(action_x_loc, action_y_loc, UNIT_LAND, 1, 1)) { //------------ found surrounding place to attack the wall -------------// if(mobile_type==UNIT_LAND) set_move_to_surround(action_x_loc, action_y_loc, 1, 1, BUILDING_TYPE_WALL); // search for a unit only, not for a group else attack_wall(action_x_loc, action_y_loc); } else // no surrounding place found, stop now stop(KEEP_PRESERVE_ACTION); // no space available, so stop to wait for space to attack the wall } else { if(action_x_loc==-1 || action_y_loc==-1) stop(); else set_wait(); } } //----------- End of function Unit::handle_blocked_attack_wall -----------//