/* * 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 : OSPREUSE.CPP //Description : Object SeekPathReuse //Owner : Alex #include #include #include #include #include #include #ifdef NO_DEBUG_SEARCH #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_reuse_check_sub_mode_node #define debug_reuse_check_sub_mode_node(x, y) #undef DEBUG #endif //--------------------------------------------------------// // define static variables //--------------------------------------------------------// int SeekPathReuse::max_node; short SeekPathReuse::total_num_of_path; short SeekPathReuse::cur_path_num; short SeekPathReuse::unit_size; DWORD SeekPathReuse::cur_group_id; char SeekPathReuse::mobile_type; char SeekPathReuse::unit_nation_recno; short SeekPathReuse::move_scale; short SeekPathReuse::search_mode; short SeekPathReuse::reuse_mode; short SeekPathReuse::reuse_path_status; short SeekPathReuse::reuse_path_dist; short *SeekPathReuse::reuse_node_matrix=NULL; char SeekPathReuse::reuse_nation_passable[MAX_NATION+1] = {0}; char SeekPathReuse::reuse_search_sub_mode; //----------------- backup leader path(reference path) ----------------------// short SeekPathReuse::leader_path_num; ResultNode *SeekPathReuse::reuse_leader_path_backup; int SeekPathReuse::reuse_leader_path_node_num; int SeekPathReuse::leader_path_start_x; int SeekPathReuse::leader_path_start_y; int SeekPathReuse::leader_path_dest_x; int SeekPathReuse::leader_path_dest_y; //----------- decide which offset method is used ----------// int SeekPathReuse::start_x_offset; int SeekPathReuse::start_y_offset; int SeekPathReuse::dest_x_offset; int SeekPathReuse::dest_y_offset; int SeekPathReuse::x_offset; int SeekPathReuse::y_offset; int SeekPathReuse::formation_x_offset; int SeekPathReuse::formation_y_offset; int SeekPathReuse::start_x; int SeekPathReuse::start_y; int SeekPathReuse::dest_x; int SeekPathReuse::dest_y; int SeekPathReuse::vir_dest_x; int SeekPathReuse::vir_dest_y; //---------- the current constructing result path --------// ResultNode *SeekPathReuse::path_reuse_result_node_ptr; int SeekPathReuse::num_of_result_node; ResultNode *SeekPathReuse::cur_result_node_ptr; short SeekPathReuse::result_node_array_def_size; short SeekPathReuse::result_node_array_reset_amount; //-- determine the current offset difference(leader path information) --// ResultNode *SeekPathReuse::cur_leader_node_ptr; int SeekPathReuse::cur_leader_node_num; short SeekPathReuse::leader_vec_x; short SeekPathReuse::leader_vec_y; //----------- for smoothing the result path --------------// short SeekPathReuse::vec_x; short SeekPathReuse::vec_y; short SeekPathReuse::new_vec_x; short SeekPathReuse::new_vec_y; int SeekPathReuse::vec_magn; int SeekPathReuse::new_vec_magn; int SeekPathReuse::result_vec_x; int SeekPathReuse::result_vec_y; //-------- Begin of function SeekPathReuse::init ---------// void SeekPathReuse::init(int maxNode) { //------------------ initialize parameters ----------------// incomplete_search = 0; max_node = maxNode; total_num_of_path = 0; cur_path_num = 0; cur_group_id = 0; mobile_type = 0; search_mode = 0; reuse_mode = 0; reuse_path_status = 0; reuse_leader_path_backup = NULL; reuse_leader_path_node_num = 0; leader_path_start_x = 0; leader_path_start_y = 0; leader_path_dest_x = 0; leader_path_dest_y = 0; start_x_offset = 0; start_y_offset = 0; dest_x_offset = 0; dest_y_offset = 0; x_offset = 0; y_offset = 0; vir_dest_x = 0; vir_dest_y = 0; num_of_result_node = 0; result_node_array_def_size = 0; result_node_array_reset_amount = 10; // dafault value is 10 cur_result_node_ptr = NULL; path_reuse_result_node_ptr = NULL; cur_leader_node_ptr = NULL; cur_leader_node_num = 0; leader_path_num = -1; // valid value is not less than 0 leader_vec_x = 0; leader_vec_y = 0; init_reuse_node_matrix(); // initialize node matrix for setting offset path } //--------- End of function SeekPathReuse::init ---------// //-------- Begin of function SeekPathReuse::deinit ---------// void SeekPathReuse::deinit() { deinit_reuse_search(); // destruct data structure if(reuse_node_matrix) { mem_del(reuse_node_matrix); reuse_node_matrix = NULL; } } //--------- End of function SeekPathReuse::deinit ---------// //-------- Begin of function SeekPathReuse::init_reuse_node_matrix ---------// void SeekPathReuse::init_reuse_node_matrix() { reuse_node_matrix = (short*) mem_add(sizeof(short)*MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC/4); } //--------- End of function SeekPathReuse::init_reuse_node_matrix ---------// //-------- Begin of function SeekPathReuse::deinit_reuse_search ---------// // destruct data structure // void SeekPathReuse::deinit_reuse_search() { if(reuse_leader_path_backup!=NULL) { mem_del(reuse_leader_path_backup); reuse_leader_path_backup = NULL; } err_when(reuse_leader_path_backup!=NULL); reuse_leader_path_node_num = 0; leader_path_start_x = 0; leader_path_start_y = 0; leader_path_dest_x = 0; leader_path_dest_y = 0; } //--------- End of function SeekPathReuse::deinit_reuse_search ---------// //-------- Begin of function SeekPathReuse::init_reuse_search ---------// // re-construct data structure and re-initialzie parameters // void SeekPathReuse::init_reuse_search() { incomplete_search = 0; cur_path_num = 0; result_node_array_def_size = 0; leader_path_num = -1; // set to invalid value -1 after each initialization cur_result_node_ptr = NULL; path_reuse_result_node_ptr = NULL; num_of_result_node = 0; deinit_reuse_search(); reuse_leader_path_backup = NULL; reuse_leader_path_node_num = 0; leader_path_start_x = 0; leader_path_start_y = 0; leader_path_dest_x = 0; leader_path_dest_y = 0; } //--------- End of function SeekPathReuse::init_reuse_search ---------// //-------- Begin of function SeekPathReuse::get_reuse_path_status ---------// char SeekPathReuse::get_reuse_path_status() { return (char) reuse_path_status; } //--------- End of function SeekPathReuse::get_reuse_path_status ---------// //-------- Begin of function SeekPathReuse::bound_check_x ---------// /*inline void SeekPathReuse::bound_check_x(int& x) { if(x<0) x = 0; else if(x>=MAX_WORLD_X_LOC) x = MAX_WORLD_X_LOC-move_scale; }*/ //--------- End of function SeekPathReuse::bound_check_x ---------// //-------- Begin of function SeekPathReuse::bound_check_y ---------// /*inline void SeekPathReuse::bound_check_y(int& y) { if(y<0) y = 0; else if(y>=MAX_WORLD_X_LOC) y = MAX_WORLD_X_LOC-move_scale; }*/ //--------- End of function SeekPathReuse::bound_check_y ---------// //-------- Begin of function SeekPathReuse::call_seek ---------// /*inline ResultNode* SeekPathReuse::call_seek(int sx, int sy, int dx, int dy, DWORD groupId, char mobileType, short searchMode, int& nodeCount) { err_when(unit_size!=1); seek_path.seek(sx, sy, dx, dy, groupId, mobileType, searchMode); return seek_path.get_result(nodeCount, reuse_path_dist); }*/ //--------- End of function SeekPathReuse::call_seek ---------// //-------- Begin of function SeekPathReuse::set_offset_condition ---------// // store the starting location, destination, offset of each path // void SeekPathReuse::set_offset_condition(int startX, int startY, int destX, int destY) { //-------------------------------------------------------------// // the offset is used to determine what the methods is used to // do the path-reuse //-------------------------------------------------------------// err_when(leader_path_num>=cur_path_num); if(cur_path_num>0) { //---- the following value is useless if no valid leader path -------// start_x_offset = startX - leader_path_start_x; start_y_offset = startY - leader_path_start_y; dest_x_offset = destX - leader_path_dest_x; dest_y_offset = destY - leader_path_dest_y; } else // no leader path is available { err_when(reuse_path_status != REUSE_PATH_FIRST_SEEK); start_x_offset = start_y_offset = dest_x_offset = dest_y_offset = 0; leader_path_start_x = startX; leader_path_start_y = startY; leader_path_dest_x = destX; leader_path_dest_y = destY; leader_path_num = cur_path_num; } } //--------- End of function SeekPathReuse::set_offset_condition ---------// //-------- Begin of function SeekPathReuse::set_next_cur_path_num ---------// // This function may be called outside by Unit::move_to // void SeekPathReuse::set_next_cur_path_num() { cur_path_num++; } //--------- End of function SeekPathReuse::set_next_cur_path_num ---------// //-------- Begin of function SeekPathReuse::is_node_avail_empty ---------// // return 1 if node available for search is zero // return 0 otherwise // int SeekPathReuse::is_node_avail_empty() { err_when(unit_size!=1); return seek_path.total_node_availnode_x!=vir_dest_x || lastNodePtr->node_y!=vir_dest_y) { err_when(unit_size!=1); if(seek_path.path_status==PATH_NODE_USED_UP) { incomplete_search++; reuse_path_status = REUSE_PATH_INCOMPLETE_SEARCH; } } return 0; } return 1; } //--------- End of function SeekPathReuse::is_leader_path_valid ---------// //-------- Begin of function SeekPathReuse::set_nation_passable ---------// void SeekPathReuse::set_nation_passable(char nationPassable[]) { memcpy(reuse_nation_passable+1, nationPassable, sizeof(char)*MAX_NATION); } //--------- End of function SeekPathReuse::set_nation_passable ---------// //-------- Begin of function SeekPathReuse::set_sub_mode ---------// void SeekPathReuse::set_sub_mode(char subMode) { reuse_search_sub_mode = subMode; } //--------- End of function SeekPathReuse::set_sub_mode ---------// //-------- Begin of function SeekPathReuse::set_status ---------// void SeekPathReuse::set_status(char newStatus) { reuse_path_status = newStatus; } //--------- End of function SeekPathReuse::set_status ---------// //-------- Begin of function SeekPathReuse::count_path_dist ---------// short SeekPathReuse::count_path_dist(ResultNode* nodeArray, int nodeNum) { if(!nodeArray || !nodeNum) return 0; err_when(nodeNum<2); ResultNode *preNode = nodeArray; ResultNode *curNode = preNode+1; int processed = 1; int xDist, yDist; short pathDist = 0; while(processed++ < nodeNum) { xDist = abs(preNode->node_x - curNode->node_x); yDist = abs((preNode++)->node_y - (curNode++)->node_y); err_when((!xDist && !yDist) || (xDist && yDist && xDist!=yDist)); pathDist += (xDist) ? xDist : yDist; } err_when(mobile_type!=UNIT_LAND && pathDist%2); return pathDist; } //--------- End of function SeekPathReuse::count_path_dist ---------// //-------- Begin of function SeekPathReuse::add_result ---------// // add the result node in the reuse result node array and resize // the array size if default size is not large enough to hold the // data // void SeekPathReuse::add_result(int x, int y) { debug_reuse_check_sub_mode_node(x, y); if(num_of_result_node>=result_node_array_def_size) // the array is not enough to hold the data { result_node_array_def_size += result_node_array_reset_amount; path_reuse_result_node_ptr = (ResultNode*) mem_resize(path_reuse_result_node_ptr, sizeof(ResultNode)* result_node_array_def_size); cur_result_node_ptr = path_reuse_result_node_ptr+num_of_result_node; } cur_result_node_ptr->node_x = x; cur_result_node_ptr->node_y = y; cur_result_node_ptr++; num_of_result_node++; } //--------- End of function SeekPathReuse::add_result ---------// //-------- Begin of function SeekPathReuse::set_index_in_node_matrix ---------// // Generally speaking, the value of the node in node matrix is less than max_node. // In order to set a offset path in the node matrix and not conflict the shortest // path searching, value > max_node is chosed. // // In this method, 4 value(max_node+k, where k is 1,..,4) is used for 2x2 node. // The value k indicate which point in the node will be chosen to be the connection // point to the offset path. // void SeekPathReuse::set_index_in_node_matrix(int xLoc, int yLoc) { short *curLocInNodeMatrix; int x = xLoc; int y = yLoc; //-------------------- boundary checking ---------------// bound_check_x(x); bound_check_y(y); err_when(unit_size!=1); curLocInNodeMatrix = reuse_node_matrix + MAX_WORLD_X_LOC/2*(y/2) + (x/2); //-------------------------------------------------------------------------// // the location is changed into 2x2 node format. // -- -- In fact, the location is in one of the 4 points in the node. // |1 |2 | The value k is shown in the figure. The value in the // -- -- node_matrix is set to max_node+k. // |3 |4 | // -- -- The value will be set again if two points is available in the // node and the last one will be chosen. //-------------------------------------------------------------------------// if(mobile_type==UNIT_LAND) *curLocInNodeMatrix = max_node + 1 + (x%2) + 2*(y%2); else *curLocInNodeMatrix = max_node + 1 + (x%4!=0) + 2*(y%4!=0); } //--------- End of function SeekPathReuse::set_index_in_node_matrix ---------// //-------- Begin of function SeekPathReuse::move_within_map ---------// // locations (preX, preY) and (curX, curY) both are inside the map // // add one point // void SeekPathReuse::move_within_map(int preX, int preY, int curX, int curY) { err_when(preX<0 || preX>=MAX_WORLD_X_LOC || preY<0 || preY>=MAX_WORLD_Y_LOC); err_when(curX<0 || curX>=MAX_WORLD_X_LOC || curY<0 || curY>=MAX_WORLD_Y_LOC); add_result(curX, curY); } //--------- End of function SeekPathReuse::move_within_map ---------// //-------- Begin of function SeekPathReuse::move_outside_map ---------// // location (preX, preY) is inside the map while location (curX, curY) // is outside the map // // add one/two points // void SeekPathReuse::move_outside_map(int preX, int preY, int curX, int curY) { err_when(preX<0 || preX>=MAX_WORLD_X_LOC || preY<0 || preY>=MAX_WORLD_Y_LOC); err_when(curX>=0 && curX=0 && curY=MAX_WORLD_X_LOC) { xStep = MAX_WORLD_X_LOC-preX-1; horizontal = 2; } else err_here(); if(curY<0) { yStep = preY; vertical = 1; } else if(curY>=MAX_WORLD_Y_LOC) { yStep = MAX_WORLD_Y_LOC-preY-1; vertical = 2; } else err_here(); err_when(xStep!=yStep); int addXLoc = preX+xStep*vecX; int addYLoc = preY+yStep*vecY; add_result(addXLoc, addYLoc); // add the first point //-*************** codes here ***************-// //---------------------------------------------------------------// // may add the second point if exit at the edge of the map //---------------------------------------------------------------// /*if((addXLoc==0 && addYLoc==0) || (addXLoc==0 && addYLoc==MAX_WORLD_Y_LOC-1) || (addXLoc==MAX_WORLD_X_LOC-1 && addYLoc==0) || (addXLoc==MAX_WORLD_X_LOC-1 && addYLoc==MAX_WORLD_Y_LOC-1)) { err_when(!vertical || !horizontal); return; // exit at corner }*/ } //--------- End of function SeekPathReuse::move_outside_map ---------// //-------- Begin of function SeekPathReuse::move_inside_map ---------// // location (preX, preY) is outside the map and location (curX, curY) // is insode the map // // add one/two points // void SeekPathReuse::move_inside_map(int preX, int preY, int curX, int curY) { err_when(preX>=0 && preX=0 && preY=MAX_WORLD_X_LOC || curY<0 || curY>=MAX_WORLD_Y_LOC); //-*************** codes here ***************-// } //--------- End of function SeekPathReuse::move_inside_map ---------// //-------- Begin of function SeekPathReuse::move_beyond_map ---------// // locations (preX, preY) and (curX, curY) are both outside the map // // add one/two points void SeekPathReuse::move_beyond_map(int preX, int preY, int curX, int curY) { err_when(preX>=0 && preX=0 && preY=0 && curX=0 && curYnation_recno; result_node_array_def_size = 0; num_of_result_node = 0; cur_result_node_ptr = NULL; path_reuse_result_node_ptr = NULL; //-------------------------------------------------------------// set_offset_condition(sx, sy, dx, dy); //-------------------------------------------------------------// if(reuse_path_status == REUSE_PATH_FIRST_SEEK) // first seeking, usually choose to be leader path { err_when(cur_path_num != 0); path_reuse_result_node_ptr = call_seek(sx, sy, dx, dy, cur_group_id, mobileType, SEARCH_MODE_IN_A_GROUP, num_of_result_node); } else // it should be REUSE_PATH_SEARCH { //-------------------------------------------------------------// // reuse_mode = GENERAL_GROUP_MOVEMENT //-------------------------------------------------------------// if(start_x_offset==dest_x_offset && start_y_offset==dest_y_offset) { //----------------------------------------------------------// // optimal case, starting offset==ending offset, thus, using // offset method to find the shortest path directly //----------------------------------------------------------// x_offset = start_x_offset; y_offset = start_y_offset; err_when(cur_path_num==leader_path_num); seek_path_offset(); } else { //-------------------------------------------------------------// // the starting location is not the correct offset location, // using the join-offset-path method //-------------------------------------------------------------// x_offset = dest_x_offset; y_offset = dest_y_offset; seek_path_join_offset(); } } //-------------------------------------------------------------// // backup the ResultNode of this path //-------------------------------------------------------------// if(num_of_result_node && path_reuse_result_node_ptr) { #ifdef DEBUG if(mobile_type==UNIT_LAND && reuse_search_sub_mode==SEARCH_SUB_MODE_PASSABLE && num_of_result_node>1) { err_when(mobile_type!=UNIT_LAND); debug_reuse_check_sub_mode(path_reuse_result_node_ptr, num_of_result_node); } #endif path_reuse_result_node_ptr = smooth_reuse_path(path_reuse_result_node_ptr, num_of_result_node); #ifdef DEBUG if(mobile_type==UNIT_LAND && reuse_search_sub_mode==SEARCH_SUB_MODE_PASSABLE && num_of_result_node>1) { err_when(mobile_type!=UNIT_LAND); debug_reuse_check_sub_mode(path_reuse_result_node_ptr, num_of_result_node); } #endif if(leader_path_num==cur_path_num) { err_when(num_of_result_node>0 && path_reuse_result_node_ptr==NULL); reuse_leader_path_backup = (ResultNode*) mem_add(sizeof(ResultNode)*num_of_result_node); memcpy(reuse_leader_path_backup, path_reuse_result_node_ptr, sizeof(ResultNode)* num_of_result_node); reuse_leader_path_node_num = num_of_result_node; } } else { path_reuse_result_node_ptr = NULL; num_of_result_node = 0; if(leader_path_num==cur_path_num) { reuse_leader_path_backup = NULL; reuse_leader_path_node_num = 0; } } set_next_cur_path_num(); err_when(cur_path_num>total_num_of_path+3); return 1; } //--------- End of function SeekPathReuse::seek ---------// //-------- Begin of function SeekPathReuse::get_result ---------// // return the final result node path //ResultNode* SeekPathReuse::get_result(int& resultNodeCount) // ResultNode* SeekPathReuse::get_result(int& resultNodeCount, short& pathDist) { if(reuse_path_status == REUSE_PATH_FIRST_SEEK) { resultNodeCount = num_of_result_node; reuse_path_dist = count_path_dist(path_reuse_result_node_ptr, num_of_result_node); pathDist = reuse_path_dist; return path_reuse_result_node_ptr; } else { err_when(reuse_path_status!=REUSE_PATH_SEARCH && reuse_path_status!=REUSE_PATH_INCOMPLETE_SEARCH); if(path_reuse_result_node_ptr!=NULL && num_of_result_node>0) { sys_yield(); // update cursor position if(num_of_result_node<2) { mem_del(path_reuse_result_node_ptr); path_reuse_result_node_ptr = NULL; num_of_result_node = 0; } sys_yield(); // update cursor position resultNodeCount = num_of_result_node; reuse_path_dist = count_path_dist(path_reuse_result_node_ptr, num_of_result_node); pathDist = reuse_path_dist; } else { num_of_result_node = 0; if(path_reuse_result_node_ptr!=NULL) { mem_del(path_reuse_result_node_ptr); path_reuse_result_node_ptr = NULL; } err_when(num_of_result_node!=0 || path_reuse_result_node_ptr!=NULL); } //******************* debug **********************// #ifdef DEBUG err_when(mobile_type!=UNIT_LAND && pathDist%2); if(!path_reuse_result_node_ptr && num_of_result_node>0) { //---------------------------------------------------------// // final checking, error free for the result_path //---------------------------------------------------------// ResultNode* debugResultPtr = path_reuse_result_node_ptr; ResultNode* debugStartNode = path_reuse_result_node_ptr; ResultNode* debugEndNode = debugStartNode + 1; int debugCount = num_of_result_node; int dvX, dvY; // vector direction int dXLoc, dYLoc; err_when(mobile_type!=UNIT_LAND && (debugStartNode->node_x%2 || debugStartNode->node_y%2)); for(int d=1; dnode_x%2 || debugEndNode->node_y%2)); dvX = debugEndNode->node_x - debugStartNode->node_x; dvY = debugEndNode->node_y - debugStartNode->node_y; err_when(dvX!=0 && dvY!=0 && abs(dvX)!=abs(dvY)); if(dvX) dvX /= abs(dvX); if(dvY) dvY /= abs(dvY); dXLoc = debugStartNode->node_x; dYLoc = debugStartNode->node_y; //-------- check accessible ---------// while(dXLoc!=debugEndNode->node_x || dYLoc!=debugEndNode->node_y) { dXLoc += dvX; dYLoc += dvY; err_when(unit_size==1 && !can_walk(dXLoc, dYLoc)); err_when(unit_size==2 && !can_walk_s2(dXLoc, dYLoc)); } } } #endif //******************* debug **********************// return path_reuse_result_node_ptr; } } //--------- End of function SeekPathReuse::get_result ---------//