|
ORTS
|
00001 00002 #include "Global.H" 00003 #include "ST_ForceField.H" 00004 #include "ST_PFEngine.H" 00005 #include "Options.H" 00006 #include "GameObj.H" 00007 #include "LinAlg.H" 00008 00009 #include <utility> 00010 #include <iterator> 00011 #include <math.h> 00012 00013 00014 00015 using namespace std; 00016 00017 #define TEST(x) 00018 //#define TEST(x) x 00019 00020 namespace SimpleTerrain { 00021 00022 void ST_ForceField::add_options() 00023 { 00024 ::Options o("SimpleTerrain - ForceField"); 00025 o.put("-costonly", "represent boundaries only by higher edge cost"); 00026 //o.put("-makesquares" ,"compute the motion squares of objects"); 00027 o.put("-highres", "use a high resolution world map (slower but more accurate)"); 00028 o.put("-nopush", "turns off pushing"); 00029 o.put("-wpattract", "units are attracted to wps, and not forced to stay on the path"); 00030 o.put("-nostoptoplan", "units do not stop to plan tasks"); 00031 00032 } 00033 00034 static void __assert_object_set_valid(const std::set<const Object *> & objs) 00035 { 00036 #ifndef NDEBUG 00037 FORALL (objs, it) { 00038 const GameObj * obj = dynamic_cast<const GameObj *>( (*it) ); 00039 assert( obj != NULL ); 00040 } 00041 #else 00042 objs.empty(); // use objs to avoid warning 00043 #endif 00044 00045 #if 0 // old 00046 std::set<const Object *>::const_iterator it; 00047 for(it = objs.begin(); it != objs.end(); it++) { 00048 const GameObj * obj = dynamic_cast<const GameObj *>( (*it) ); 00049 assert( obj != NULL ); 00050 } 00051 #endif 00052 } 00053 00054 //----------------------------------------------------------------------------- 00055 static Loc get_center_loc(const Object *obj) 00056 { 00057 Loc l; 00058 obj->get_center(l.x, l.y); 00059 return l; 00060 } 00061 00062 // static ScalarPoint get_center_sp(const Object *obj) 00063 // { 00064 // ScalarPoint l; 00065 // obj->get_center(l.x, l.y); 00066 // return l; 00067 // } 00068 00069 static real8 path_length(Path &path) 00070 { 00071 if( path.locs.size() < 2 ) 00072 return 0; 00073 real8 total = 0; 00074 for( Vector<Loc>::iterator i = ++path.locs.begin() ; i != path.locs.end() ; i++ ) 00075 total += i->distance(*(i-1)); 00076 return total; 00077 } 00078 00079 // Assuming path is in the usual reversed order, with goal at locs[0] 00080 static real8 path_length_from(Path &path, const Loc& start_loc) 00081 { 00082 if( path.locs.size() < 1 ) 00083 return 0; 00084 return start_loc.distance(path.locs[path.locs.size()-1]) + path_length(path); 00085 } 00086 00087 ST_ForceField::ST_ForceField() { 00088 sint4 seed = 0; 00089 ::Options::get("-seed", seed); 00090 rand.srand(seed); 00091 bool nopush; 00092 ::Options::get("-nopush", nopush); 00093 if (nopush){ 00094 pushing_on = false; 00095 } 00096 else{ 00097 pushing_on = true; 00098 } 00099 ::Options::get("-wpattract", wp_attract); 00100 ::Options::get("-nostoptoplan", stop_to_plan); 00101 stop_to_plan = !stop_to_plan; 00102 00103 cout << "STOP TO PLAN: " << stop_to_plan << endl; 00104 } 00105 00106 //----------------------------------------------------------------------------- 00107 void ST_ForceField::init(sint4 tiles_x_, sint4 tiles_y_, sint4 tile_points_, sint4 client_pID_, sint4 neutral_pID_) 00108 { 00109 TerrainBasicImp<FF_Task>::init(tiles_x_, tiles_y_, tile_points_, 00110 client_pID_, neutral_pID_); 00111 std::cout << "tiles_x_ = " << tiles_x_ << endl; 00112 00113 // initialize the internal PFEngine 00114 Vector<sint4> levels_of_detail; 00115 bool high_resolution_only; 00116 Options::get("-highres", high_resolution_only); 00117 00118 if( !high_resolution_only ) { 00119 levels_of_detail.push_back(4); // default - use a resolution reduced by a factor of 4. 00120 } 00121 levels_of_detail.push_back(1); // also (or exclusively) maintain a full resolution view. 00122 pfEngine = boost::shared_ptr<ST_Terrain::PFEngine>(new ST_Terrain::PFEngine(tiles_x_, tiles_y_, tile_points_, levels_of_detail)); 00123 00124 //Add the boundaries of the map to the list of segments for the forcefields 00125 00126 Loc topLeft(0,0); 00127 Loc bottomRight(tiles_x_ * tile_points_, tiles_y_ * tile_points_); 00128 Loc bottomLeft(topLeft.x, bottomRight.y); 00129 Loc topRight(bottomRight.x, topLeft.y); 00130 00131 segments.push_back(Segment(topLeft, topRight)); 00132 segments.push_back(Segment(topLeft, bottomLeft)); 00133 segments.push_back(Segment(bottomLeft, bottomRight)); 00134 segments.push_back(Segment(topRight, bottomRight)); 00135 00136 segment_ends[topLeft] = true; 00137 segment_ends[bottomRight] = true; 00138 segment_ends[topRight] = true; 00139 segment_ends[bottomLeft] = true; 00140 00141 // initialize motion squares 00142 /* if(makesquares){ // temporarily commented to prevent unused var warning -- dd 00143 sint4 area = tiles_x_ * tiles_y_; 00144 // sectors = new vector<Sector>(area); 00145 }*/ 00146 } 00147 00148 bool is_obj_static(const Object * obj) 00149 { 00150 return (obj->get_max_speed() == 0); 00151 } 00152 00153 //----------------------------------------------------------------------------- 00154 void ST_ForceField::add_obj(const Object *obj) 00155 { 00156 TerrainBasicImp<FF_Task>::add_obj(obj); 00157 assert(pfEngine!=NULL); 00158 if(is_obj_static(obj)) 00159 { 00160 pfEngine->insert_object(obj); 00161 } 00162 00163 objects[obj] = Loc(-1,-1); 00164 } 00165 00166 //----------------------------------------------------------------------------- 00167 void ST_ForceField::update_obj(const Object *obj) 00168 { 00169 TerrainBasicImp<FF_Task>::update_obj(obj); 00170 assert(pfEngine!=NULL); 00171 if(is_obj_static(obj)) 00172 { 00173 pfEngine->update_object(obj); 00174 } 00175 } 00176 00177 //----------------------------------------------------------------------------- 00178 void ST_ForceField::remove_obj(const Object *obj) 00179 { 00180 // shorthand 00181 typedef ObjIsGoalInTaskId::iterator OGIT; 00182 00183 // remove from world representation 00184 assert(pfEngine!=NULL); 00185 if(is_obj_static(obj)) 00186 { 00187 pfEngine->remove_object(obj); 00188 } 00189 00190 // If we intended to stop it, we don't need to anymore 00191 objsToStop.erase(obj); 00192 00193 // If this is the target of any existing task... 00194 pair<OGIT,OGIT> range = objIsGoalInTaskId.equal_range(obj); 00195 for( OGIT i = range.first ; i != range.second ; i++ ) { 00196 // Change the target of the task to the last known location of the object 00197 FIND(taskId2task, ti2tIt, i->second); 00198 assert(ti2tIt != taskId2task.end()); 00199 00200 ti2tIt->second->task.goal.target = Goal::LOCATION; 00201 Loc obj_center; 00202 ti2tIt->second->task.goal.obj->get_center(obj_center.x, obj_center.y); 00203 ti2tIt->second->task.goal.loc = obj_center; 00204 00205 } 00206 00207 00208 00209 TerrainBasicImp<FF_Task>::remove_obj(obj); 00210 00211 objects.erase(obj); 00212 } 00213 00214 00215 //----------------------------------------------------------------------------- 00216 void ST_ForceField::add_segments(const Vector<Segment> &segs) 00217 { 00218 TerrainBasicImp<FF_Task>::add_segments(segs); 00219 assert(pfEngine!=NULL); 00220 FORALL(segs,i){ 00221 pfEngine->insert_boundary(*i); 00222 segments.push_back(*i); 00223 segment_ends[i->l1] = true; 00224 segment_ends[i->l2] = true; 00225 } 00226 invalidate_paths(segs); 00227 00228 } 00229 00230 //---------------------------------------------------------------------------------- 00231 void ST_ForceField::cancel_task(const Object *obj) 00232 { 00233 objsToStop.insert(obj); 00234 TerrainBasicImp<FF_Task>::cancel_task(obj); 00235 } 00236 00237 //---------------------------------------------------------------------------------- 00238 void ST_ForceField::remove_task(TaskId tid) 00239 { 00240 FIND (taskId2task, it, tid); 00241 if (it != taskId2task.end() ) { 00242 Task& task = it->second->task; 00243 __assert_object_set_valid(task.objs); 00244 objsToStop.insert(task.objs.begin(), task.objs.end()); 00245 } 00246 TerrainBasicImp<FF_Task>::remove_task(tid); 00247 } 00248 00249 //--------------------------------------------------------------------------- 00250 real8 ST_ForceField::find_path(const Loc &l1, const Loc &l2, sint4 radius, Path * path, ConsiderObjects consider) 00251 { 00252 Path result; 00253 bool found; 00254 found = pfEngine->find_path(l1, l2, radius, result, 0, consider); 00255 00256 if( !found ) 00257 return -1; 00258 00259 // need to return waypoints in a 'forward' order 00260 if( path != NULL ) { 00261 path->locs.clear(); 00262 reverse_copy(result.locs.begin(), result.locs.end(), back_inserter(path->locs)); 00263 } 00264 00265 // compute and return the length of the path 00266 return path_length_from(result, l2); 00267 // assert(0); // unimplemented yet 00268 //return -1; 00269 } 00270 00271 00272 //---------------------------------------------------------------------------------- 00273 real8 ST_ForceField::find_path(const Object *obj, const Goal &goal, Path *path, ConsiderObjects consider) 00274 { 00275 assert( goal.mode == Goal::TOUCH && "ATTACK, VICINITY modes are currently unimplemented"); 00276 assert( (goal.target == Goal::LOCATION || goal.target == Goal::OBJ) && 00277 "Unknown Goal::target type"); 00278 00279 Path result; 00280 bool found; 00281 if (goal.target == Goal::LOCATION) { 00282 found = pfEngine->find_path(obj, goal.loc, result, 0, consider); 00283 } else { 00284 found = pfEngine->find_path(obj, goal.obj, result, 0, consider); 00285 } 00286 00287 if( !found ) 00288 return -1; 00289 00290 // need to return waypoints in a 'forward' order 00291 if( path != NULL ) { 00292 path->locs.clear(); 00293 reverse_copy(result.locs.begin(), result.locs.end(), back_inserter(path->locs)); 00294 } 00295 00296 // compute and return the length of the path 00297 return path_length_from(result, get_center_loc(obj)); 00298 } 00299 00300 //---------------------------------------------------------------------------------- 00301 00302 // fixme: we only check the path of the *center* of the unit vs the new boundaries, 00303 // and we should account for its size (radius). 00304 void ST_ForceField::invalidate_paths(const Vector<Segment> &segments) 00305 { 00306 FORALL(tasks, ti) { 00307 if (!ti->isValid() ) continue; 00308 // fixme: better handling of zcat != ON_LAND ? 00309 if (ti->path->obj->get_zcat() != Object::ON_LAND ) continue; 00310 00311 FORALL(segments, si) { 00312 if (ti->path->is_intersect(*si) ) { 00313 // this path is invalid, mark as pending for (re)planning 00314 pendingTasks.push_back(ti); 00315 ti->isPending = true; 00316 cout << "Marked an invalid path" << endl; 00317 break; 00318 } 00319 } 00320 } 00321 } 00322 00323 void ST_ForceField::execute_tasks(Vector<MoveCmd> &cmds, Vector<StatusMsg> &msgs){ 00324 00325 //clear the reserved locations in the object map 00326 FORALL(objects, it){ 00327 it->second = Loc(-1, -1); 00328 } 00329 // Execute all active tasks 00330 /*for (Tasks::iterator j = tasks.begin(); j != tasks.end();) { 00331 Tasks::iterator i = j++;*/ 00332 FORALL(tasks, i) { 00333 00334 // Report failure of planning (no path at all) 00335 if (i->noPathFound ) { 00336 TerrainBase::StatusMsg s; 00337 // fixme: this is not robust to future changes in the meaning of TaskId. 00338 __assert_object_set_valid(i->task.objs); 00339 00340 s.task_id = (TerrainBase::TaskId)&(*i); 00341 s.obj = *(i->task.objs.begin()); 00342 s.type = TerrainBase::StatusMsg::NO_PATH_FAILURE; 00343 msgs.push_back(s); 00344 continue; 00345 } 00346 00347 // Skip invalid tasks. 00348 if (!i->isValid() ) { 00349 __assert_object_set_valid(i->task.objs); 00350 TEST(cout << *i->task.objs.begin() << " task not valid" << endl;); 00351 continue; 00352 } 00353 00354 00355 UnitPath_FF &cur_path = *(i->path); 00356 const Object *obj = cur_path.obj; 00357 00358 // Skip tasks that are waiting for a retry 00359 // if (cur_path.sleeping()) { 00360 // TEST(cout << obj << " path sleeping" << endl;); 00361 // continue; 00362 // } 00363 00364 // Skip objects for which we gave a (move) action that is still pending 00365 /* 00366 if (obj->is_pending_action()) { 00367 TEST(cout << obj << " pending action" << endl;) 00368 continue; 00369 } 00370 */ 00371 00372 bool atGoal = is_at_goal(obj, i->task); 00373 00374 // Look ahead: if the object will get to a waypoint in the next tick, 00375 // give the next action now the prevent it from wasting a tick on stopping. 00376 // This will cut a single tick at each waypoint, though it will really prevent 00377 // the object from stopping only if there's no (network or processing) lag 00378 // between this client and the server. 00379 // atWaypoint can be true only if the current waypoint is an intermediate and 00380 // not the *final* waypoint/goal. 00381 // bool atWaypoint = cur_path.current_target > 0 && // reverse order, goal==0 00382 // is_at_location(obj, cur_path.path.at(cur_path.current_target)); 00383 00384 // Skip a moving object which is currently not at a waypoint/goal 00385 if (/*(obj->get_speed() > 0 || obj->get_moving() != 0) && !atWaypoint && */ !atGoal ) { 00386 TEST(cout << obj << " atWaypoint=" << atWaypoint << " speed=" << obj->get_speed() << " is_moving=" << obj->get_moving() << endl;); 00387 Loc target; 00388 // bool deflected = get_forcefield_target(cur_path, target); 00389 // bool nearWaypoint = is_near_waypoint(obj, cur_path.path.at(cur_path.current_target)); 00390 // if (nearWaypoint && deflected && cur_path.current_target > 0){ 00391 // bool valid = slide_wp(cur_path); 00392 // if (!valid){ 00393 // i->isFailed = true; 00394 // continue; 00395 // } 00396 get_forcefield_target(cur_path, target); 00397 // } 00398 add_move_command(obj, target, cmds); 00399 00400 /////////////////////////////// 00401 00402 Loc obj_center; 00403 obj->get_center(obj_center.x, obj_center.y); 00404 00405 // FORALL(i->followers, fol_it){ 00406 // get_forcefield_target(*fol_it, obj_center, target); 00407 // add_move_command(*fol_it, target, cmds); 00408 // } 00409 00410 real8 distance = obj_center.distance(cur_path.path.at(cur_path.current_target)); 00411 if (cur_path.spot != Loc(-1,-1)){ 00412 real8 spot_distance = cur_path.spot.distance(cur_path.path.at(cur_path.current_target)); 00413 if (distance >= spot_distance/* - ((real8)obj->get_max_speed())/2*/){ 00414 if (cur_path.collision_step > REPLAN_TRIGGER){ 00415 if (i->replanned < REPLAN_HARDCAP){ //fixed cap on number of replannings 00416 i->isFailed = true; 00417 failedTasks.push_back(i); 00418 } 00419 } 00420 if (cur_path.collision_step > OCCILATION_TRIGGER){ 00421 TerrainBase::StatusMsg s; 00422 // fixme: this is not robust to future changes in the meaning of TaskId. 00423 s.task_id = (TerrainBase::TaskId)&(*i); 00424 s.obj = obj; 00425 s.type = TerrainBase::StatusMsg::MOVEMENT_FAILURE; 00426 msgs.push_back(s); 00427 cout << "Movement failure!" << endl; 00428 } 00429 00430 cur_path.collision_step++; 00431 } 00432 else{ 00433 cur_path.collision_step = 0; 00434 cur_path.spot = obj_center; 00435 } 00436 } 00437 else{ 00438 cur_path.spot = obj_center; 00439 cur_path.collision_step = 0; 00440 } 00441 00442 00443 /////////////////////////////// 00444 00445 continue; 00446 } 00447 00448 00449 00450 // ok, so it needs attention: 00451 // 1. maybe it had reached the goal 00452 // 2. maybe it's at a waypoint (with the lookahead) 00453 // 3. maybe it needs the first orders 00454 // 4. maybe it unexpectedly collided with something 00455 00456 if (atGoal ) { 00457 00458 TEST(cout << obj << " arrived" << endl;) 00459 00460 TerrainBase::StatusMsg s; 00461 // fixme: this is not robust to future changes in the meaning of TaskId. 00462 s.task_id = (TerrainBase::TaskId)&(*i); 00463 s.obj = obj; 00464 s.type = TerrainBase::StatusMsg::ARRIVED; 00465 msgs.push_back(s); 00466 00467 // BEGIN CHANGES: ADD THIS CODE 00468 if(i->task.goal.target == Goal::LOCATION) 00469 { 00470 add_move_command(obj, i->task.goal.loc, cmds); 00471 } 00472 // END CHANGES 00473 } 00474 // else if( atWaypoint || 00475 // cur_path.no_orders_yet || 00476 // cur_path.stopped_to_avoid_collision ) { 00477 00478 // // in all these cases we should just issue a move command 00479 00480 // TEST(cout << obj << " atWaypoint=" << atWaypoint 00481 // << " no_orders_yet=" << cur_path.no_orders_yet 00482 // << " stopped_to_avoid_collision=" << cur_path.stopped_to_avoid_collision 00483 // << endl; ); 00484 00485 // // if we are at a waypoint, move to next waypoint 00486 // if( atWaypoint ) 00487 // cur_path.current_target--; 00488 // cur_path.no_orders_yet = false; 00489 // cur_path.stopped_to_avoid_collision = false; 00490 // Loc target; 00491 // get_forcefield_target(cur_path, target); 00492 // add_move_command(obj, target, cmds); 00493 00494 // } 00495 else { 00496 00497 // Unexpected stop. The current policy is: 00498 // 1-2. First time => wait a little and send the move command again. 00499 // 3-4. Second stop at the same place => mark as failed and replan. 00500 // 5. Third stop at the same place => report failure. 00501 00502 // TEST(cout << obj << " unexpectedly stopped, collision_step=" << cur_path.collision_step << endl); 00503 00504 // Loc cur_position; obj->get_center(cur_position.x, cur_position.y); 00505 00506 // // First collision in this position: 00507 // if (cur_path.spot != cur_position ) { 00508 // cur_path.collision_step = 1; 00509 // cur_path.mark_spot(cur_position); 00510 // // sleep for a short random time, 500-999 millisecs 00511 // cur_path.sleep(500 + (rand.rand_uint4() % 500)); 00512 // } else { 00513 // cur_path.collision_step++; 00514 00515 // // Just finished the sleeping: 00516 // if (cur_path.collision_step == 2 ) { 00517 // Loc target; 00518 // get_forcefield_target(cur_path, target); 00519 // add_move_command(obj, target, cmds); 00520 // } 00521 // // Second collision 00522 // else if (cur_path.collision_step == 3 ) { 00523 // i->isFailed = true; 00524 // } 00525 // // Back from replanning 00526 // else if (cur_path.collision_step == 4 ) { 00527 // Loc target; 00528 // get_forcefield_target(cur_path, target); 00529 // add_move_command(obj, target, cmds); 00530 // } 00531 // // Third collision 00532 // else if (cur_path.collision_step == 5 ) { 00533 // TerrainBase::StatusMsg s; 00534 // // fixme: this is not robust to future changes in the meaning of TaskId. 00535 // s.task_id = (TerrainBase::TaskId)&(*i); 00536 // s.obj = obj; 00537 // s.type = TerrainBase::StatusMsg::MOVEMENT_FAILURE; 00538 // msgs.push_back(s); 00539 // } 00540 // } 00541 } 00542 } 00543 00544 //Pushing! 00545 if (pushing_on){ 00546 FORALL(objects, objIt){ 00547 const GameObj *gob = dynamic_cast<const GameObj*>(objIt->first); 00548 if (gob){ 00549 if (objIt->second == Loc(-1,-1) && *(gob->sod.owner) == client_pID && objIt->first->get_max_speed() > 0){ 00550 Loc target; 00551 get_push_target(objIt->first, target); 00552 add_move_command(objIt->first, target, cmds); 00553 } 00554 } 00555 } 00556 } 00557 00558 // Priority of commands: 00559 // 1. stops to avoid a collision. 00560 // 2. moves. 00561 // 3. other stops (resulting from a cancelled task, etc.). 00562 00563 // 2 above 3: 00564 FORALL(cmds, c) { 00565 objsToStop.erase(c->obj); 00566 } 00567 00568 // With all movement commands were given while assuming an ideal-no-collisions 00569 // world, now check if any of our controlled objects are going to collide 00570 // (because of an existing movement vector, and also consider commands 00571 // we just intended to give). 00572 //resolve_friendly_collisions(); 00573 00574 // 1 above 2: 00575 for(typeof(cmds.begin()) c = cmds.begin() ; c != cmds.end() ; ) { 00576 FIND(objsToStop, s, c->obj); 00577 if( s != objsToStop.end() ) 00578 c = cmds.erase(c); 00579 else 00580 c++; 00581 } 00582 00583 // Return also the stop commands 00584 FORALL(objsToStop, oi) { 00585 const Object *obj = *oi; 00586 00587 TerrainBase::MoveCmd m; 00588 m.obj = obj; 00589 obj->get_center(m.next_loc.x, m.next_loc.y); // doesn't really matter 00590 m.speed = 0; // 0 -> stop 00591 cmds.push_back(m); 00592 } 00593 objsToStop.clear(); 00594 00595 // remove finished tasks 00596 FORALL (msgs, t) { 00597 remove_task(t->task_id); 00598 } 00599 //motionSquares.clear(); 00600 } 00601 00602 // BEGIN CHANGES (Replace old function with this one) 00603 void ST_ForceField::add_move_command(const Object *obj, Loc target, Vector<TerrainBase::MoveCmd> &cmds){ 00604 //hack: 00605 if (abs(target.x) >= 500000){ 00606 target.x = 499999; 00607 } 00608 if( abs(target.y) >= 500000){ 00609 target.y = 499999; 00610 } 00611 00612 Loc curr_loc; 00613 obj->get_center(curr_loc.x, curr_loc.y); 00614 Vec_2D vec(target.x - curr_loc.x, target.y - curr_loc.y); 00615 real8 length = vec.length(); 00616 sint4 speed = std::min(obj->get_max_speed(), static_cast<sint4>(length)); 00617 00618 TerrainBase::MoveCmd m; 00619 m.obj = obj; 00620 m.next_loc = target; 00621 m.speed = speed; 00622 cmds.push_back(m); 00623 00624 FIND (objects, it, obj); 00625 // assert(it != objects.end() && "hrmm... all objects should be in objects map"); 00626 if (it != objects.end()){ 00627 real8 k = static_cast<real8>(speed) / length; 00628 00629 vec = vec.multiply(k); 00630 00631 it->second.x = curr_loc.x + vec.x; 00632 it->second.y = curr_loc.y + vec.y; 00633 } 00634 00635 } 00636 // END CHANGES 00637 00638 sint4 ST_ForceField::what_team(const Object *obj){ 00639 const GameObj *p = dynamic_cast<const GameObj*>(obj); 00640 if (p){ 00641 return *(p->sod.owner); 00642 } 00643 else 00644 return -1; 00645 } 00646 00647 void ST_ForceField::get_push_target(const Object *obj, Loc &output){ 00648 //const Object *obj = path.obj; 00649 if (obj->get_shape() != Object::CIRCLE){ 00650 return; //only circle movement implemented -- this also functions as a crude check for buildings 00651 } 00652 00653 00654 Vec_2D force_vec(0,0); 00655 00656 FORALL(objects, i){ 00657 const GameObj *gob = dynamic_cast<const GameObj*>(i->first); 00658 if (!gob){continue;} 00659 00660 //don't consider any object that's not your own as a force that can push you 00661 if (*(gob->sod.owner) != client_pID){continue;} 00662 00663 if (obj == i->first){ 00664 continue; 00665 } 00666 Vec_2D vec; 00667 if (i->first->get_shape() == Object::CIRCLE){ 00668 Loc obstacle_center; 00669 if (i->second == Loc(-1, -1)){ 00670 i->first->get_center(obstacle_center.x, obstacle_center.y); 00671 } 00672 else{ 00673 obstacle_center = i->second; 00674 } 00675 vec = get_point_repulsion(obj, obstacle_center, i->first->get_radius()); 00676 force_vec = force_vec + vec; 00677 } 00678 else if (i->first->get_shape() == Object::RECTANGLE){ 00679 Loc u_l, u_r, l_r, l_l; //upper-left, upper-right, lower-right, lower-left 00680 i->first->get_p1(u_l.x, u_l.y); 00681 i->first->get_p2(l_r.x, l_r.y); 00682 u_r.x = l_r.x; 00683 u_r.y = u_l.y; 00684 00685 l_l.x = u_l.x; 00686 l_l.y = l_r.y; 00687 00688 Segment seg; 00689 seg.l1 = u_l; 00690 seg.l2 = u_r; 00691 vec = get_line_repulsion(obj, seg); 00692 //force_vec = force_vec + vec; 00693 00694 seg.l1 = u_r; 00695 seg.l2 = l_r; 00696 vec = vec + get_line_repulsion(obj, seg); 00697 //force_vec = force_vec + vec; 00698 00699 seg.l1 = l_r; 00700 seg.l2 = l_l; 00701 vec = vec + get_line_repulsion(obj, seg); 00702 //force_vec = force_vec + vec; 00703 00704 seg.l1 = l_l; 00705 seg.l2 = u_l; 00706 vec = vec + get_line_repulsion(obj, seg); 00707 //force_vec = force_vec + vec; 00708 if (vec == Vec_2D(0,0)){ 00709 vec = vec + get_point_repulsion(obj, u_l); 00710 vec = vec + get_point_repulsion(obj, u_r); 00711 vec = vec + get_point_repulsion(obj, l_l); 00712 vec = vec + get_point_repulsion(obj, l_r); 00713 } 00714 force_vec = force_vec + vec; 00715 00716 } 00717 } 00718 Vec_2D wall_repulsion; 00719 FORALL(segment_ends, ends_it){ 00720 ends_it->second = true; 00721 } 00722 FORALL(segments, seg_it){ 00723 Vec_2D vec; 00724 vec = get_line_repulsion(obj, *seg_it); 00725 if (vec != Vec_2D(0,0)){ 00726 FIND(segment_ends, ends_it, seg_it->l1); 00727 if (ends_it != segment_ends.end()) 00728 ends_it->second = false; 00729 FIND(segment_ends, ends_it2, seg_it->l2); 00730 if (ends_it != segment_ends.end()) 00731 ends_it->second = false; 00732 } 00733 wall_repulsion = wall_repulsion + get_line_repulsion(obj, *seg_it); 00734 // force_vec = force_vec + vec; 00735 } 00736 FORALL(segment_ends, endit){ 00737 if (endit->second){ 00738 wall_repulsion = wall_repulsion + get_point_repulsion(obj, endit->first); 00739 } 00740 } 00741 force_vec = force_vec + wall_repulsion; 00742 00743 if (force_vec != Vec_2D(0,0)){ 00744 Vec_2D rand_vec; 00745 Vec_2D half_force = Vec_2D(force_vec.x/2, force_vec.y/2); 00746 00747 uint4 rand_angle_int = rand.rand_uint4() % 62; 00748 real8 rand_angle = ((real8)rand_angle_int)/ 10; 00749 rand_vec.x = Vec_2D::round(half_force.length() * cos(rand_angle)); 00750 rand_vec.y = Vec_2D::round(half_force.length() * sin(rand_angle)); 00751 00752 force_vec = force_vec + rand_vec; 00753 } 00754 00755 // if (force_vec == Vec_2D(0,0)){ 00756 // output = path.path.at(path.current_target); 00757 // return false; 00758 // } 00759 00760 Loc object_center; 00761 obj->get_center(object_center.x, object_center.y); 00762 00763 output.x = object_center.x + force_vec.x; 00764 output.y = object_center.y + force_vec.y; 00765 } 00766 00767 void ST_ForceField::get_forcefield_target(const Object *obj, const Loc &attractor, Loc &output){ 00768 //const Object *obj = path.obj; 00769 assert(obj->get_shape() == Object::CIRCLE && "Only Circle Movement implemented"); 00770 00771 00772 Vec_2D force_vec(0,0); 00773 00774 FORALL(objects, i){ 00775 if (obj == i->first){ 00776 continue; 00777 } 00778 Vec_2D vec; 00779 if (i->first->get_shape() == Object::CIRCLE){ 00780 Loc obstacle_center; 00781 if (i->second == Loc(-1, -1)){ 00782 i->first->get_center(obstacle_center.x, obstacle_center.y); 00783 } 00784 else{ 00785 obstacle_center = i->second; 00786 } 00787 vec = get_point_repulsion(obj, obstacle_center, i->first->get_radius()); 00788 force_vec = force_vec + vec; 00789 } 00790 else if (i->first->get_shape() == Object::RECTANGLE){ 00791 Loc u_l, u_r, l_r, l_l; //upper-left, upper-right, lower-right, lower-left 00792 i->first->get_p1(u_l.x, u_l.y); 00793 i->first->get_p2(l_r.x, l_r.y); 00794 u_r.x = l_r.x; 00795 u_r.y = u_l.y; 00796 00797 l_l.x = u_l.x; 00798 l_l.y = l_r.y; 00799 00800 Segment seg; 00801 seg.l1 = u_l; 00802 seg.l2 = u_r; 00803 vec = get_line_repulsion(obj, seg); 00804 //force_vec = force_vec + vec; 00805 00806 seg.l1 = u_r; 00807 seg.l2 = l_r; 00808 vec = vec + get_line_repulsion(obj, seg); 00809 //force_vec = force_vec + vec; 00810 00811 seg.l1 = l_r; 00812 seg.l2 = l_l; 00813 vec = vec + get_line_repulsion(obj, seg); 00814 //force_vec = force_vec + vec; 00815 00816 seg.l1 = l_l; 00817 seg.l2 = u_l; 00818 vec = vec + get_line_repulsion(obj, seg); 00819 //force_vec = force_vec + vec; 00820 if (vec == Vec_2D(0,0)){ 00821 vec = vec + get_point_repulsion(obj, u_l); 00822 vec = vec + get_point_repulsion(obj, u_r); 00823 vec = vec + get_point_repulsion(obj, l_l); 00824 vec = vec + get_point_repulsion(obj, l_r); 00825 } 00826 force_vec = force_vec + vec; 00827 00828 } 00829 } 00830 Vec_2D wall_repulsion; 00831 FORALL(segment_ends, ends_it){ 00832 ends_it->second = true; 00833 } 00834 FORALL(segments, seg_it){ 00835 Vec_2D vec; 00836 vec = get_line_repulsion(obj, *seg_it); 00837 if (vec != Vec_2D(0,0)){ 00838 FIND(segment_ends, ends_it, seg_it->l1); 00839 if (ends_it != segment_ends.end()) 00840 ends_it->second = false; 00841 FIND(segment_ends, ends_it2, seg_it->l2); 00842 if (ends_it != segment_ends.end()) 00843 ends_it->second = false; 00844 } 00845 wall_repulsion = wall_repulsion + get_line_repulsion(obj, *seg_it); 00846 // force_vec = force_vec + vec; 00847 } 00848 FORALL(segment_ends, endit){ 00849 if (endit->second){ 00850 wall_repulsion = wall_repulsion + get_point_repulsion(obj, endit->first); 00851 } 00852 } 00853 force_vec = force_vec + wall_repulsion; 00854 00855 // if (force_vec == Vec_2D(0,0)){ 00856 // output = path.path.at(path.current_target); 00857 // return false; 00858 // } 00859 00860 Vec_2D attract_vec, scaled_attract_vec; 00861 Loc obj_center; 00862 obj->get_center(obj_center.x, obj_center.y); 00863 attract_vec = Vec_2D(obj_center, attractor); 00864 00865 real8 k; 00866 if (attract_vec.length() < 1){ 00867 k = ATTRACTOR_FORCE; 00868 } 00869 else{ 00870 k = ATTRACTOR_FORCE / attract_vec.length(); 00871 } 00872 scaled_attract_vec = attract_vec.multiply(k); 00873 00874 force_vec = force_vec + scaled_attract_vec; 00875 00876 restrict_vector(attract_vec, force_vec); 00877 00878 if (force_vec == Vec_2D(0,0)){ 00879 force_vec = Vec_2D(attract_vec.y, -attract_vec.x); 00880 } 00881 00882 Loc object_center; 00883 obj->get_center(object_center.x, object_center.y); 00884 00885 output.x = object_center.x + force_vec.x; 00886 output.y = object_center.y + force_vec.y; 00887 00888 } 00889 00890 void ST_ForceField::get_forcefield_target(UnitPath_FF &path, Loc &output){ 00891 Loc attractor = get_attractor(path); 00892 get_forcefield_target(path.obj, attractor, output); 00893 } 00894 00895 00896 Loc ST_ForceField::get_attractor(UnitPath_FF &path){ 00897 Loc object_center, attractor; 00898 Vec_2D to_wp, scaled_to_wp; 00899 path.obj->get_center(object_center.x, object_center.y); 00900 if (path.current_target >= ((sint4)path.path.size()) - 1){ 00901 attractor = object_center; 00902 } 00903 else{ 00904 attractor = find_point_on_line(object_center, path.path.at(path.current_target), path.path.at(path.current_target+1)); 00905 } 00906 to_wp = Vec_2D(path.path.at(path.current_target).x - attractor.x, path.path.at(path.current_target).y - attractor.y); 00907 real8 offset = path.obj->get_max_speed() * PATH_LOOK_AHEAD; 00908 //std::cout << "k = " << offset/to_wp.length() << "\n"; 00909 if (to_wp.length() == 0) 00910 scaled_to_wp = Vec_2D(1,1); 00911 else 00912 scaled_to_wp = to_wp.multiply(offset / to_wp.length()); 00913 00914 //If wp_attract is set, set the attractor to the wp unless you're near it. 00915 if (wp_attract && scaled_to_wp.length() <= to_wp.length()){ 00916 return path.path.at(path.current_target); 00917 } 00918 while (scaled_to_wp.length() > to_wp.length()){ 00919 offset = offset - to_wp.length(); 00920 attractor = path.path.at(path.current_target); 00921 if (path.current_target == 0){ 00922 scaled_to_wp = Vec_2D(0,0); 00923 break; 00924 } 00925 path.current_target--; 00926 //std::cout << "new wp: " << path.path.at(path.current_target).x 00927 // << ", " << path.path.at(path.current_target).y << "\n"; 00928 to_wp = Vec_2D(path.path.at(path.current_target).x - attractor.x, path.path.at(path.current_target).y - attractor.y); 00929 scaled_to_wp = to_wp.multiply(offset / to_wp.length()); 00930 } 00931 00932 attractor.x = attractor.x + scaled_to_wp.x; 00933 attractor.y = attractor.y + scaled_to_wp.y; 00934 00935 return attractor; 00936 } 00937 00938 #if 0 00939 Loc ST_ForceField::find_point_on_line(Loc loc, Loc line_start, Loc line_end){ 00940 00941 Vec_2D line_to_obj, n; 00942 00943 line_to_obj = Vec_2D(loc.x - line_start.x, loc.y - line_start.y); 00944 n = Vec_2D(line_end.y - line_start.y, -(line_end.x - line_start.x)); //perpendicular to segment 00945 00946 line_to_obj = line_to_obj.project_onto(n); 00947 00948 Loc point_on_line(loc.x - line_to_obj.x, loc.y - line_to_obj.y); 00949 00950 return point_on_line; 00951 } 00952 #else 00953 Loc ST_ForceField::find_point_on_line(Loc loc, Loc line_start, Loc line_end) 00954 { 00955 sint4 line_x = line_end.x - line_start.x; 00956 sint4 line_y = line_end.y - line_start.y; 00957 00958 if (line_x == 0 && line_y == 0) return loc; 00959 00960 sint4 v_x = loc.x - line_start.x; 00961 sint4 v_y = loc.y - line_start.y; 00962 00963 sint4 a = line_x * v_x + line_y * v_y; 00964 sint4 b = line_x * line_x + line_y * line_y; 00965 00966 Loc proj(line_start.x + line_x * a / b, line_start.y + line_y * a / b); 00967 00968 return proj; 00969 } 00970 #endif 00971 00972 //------------------------------------------------------------------- 00973 00974 // bool ST_ForceField::slide_wp(UnitPath_FF &path){ 00975 00976 // Loc next_wp, curr_wp, temp_wp; 00977 // next_wp = path.path.at(path.current_target -1); 00978 // curr_wp = path.path.at(path.current_target); 00979 // real8 d = next_wp.distance(curr_wp); 00980 00981 // Vec_2D wp_vector(next_wp.x - curr_wp.x, next_wp.y - curr_wp.y); 00982 00983 // for(uint4 i = 1; ; i++){ 00984 // real8 k = ((real8)(i * path.obj->get_max_speed())) / d; 00985 // if (k > 1){ 00986 // return false; 00987 // } 00988 // Vec_2D scaled_wp_vec = wp_vector.multiply(k); 00989 00990 // temp_wp.x = curr_wp.x + scaled_wp_vec.x; 00991 // temp_wp.y = curr_wp.y + scaled_wp_vec.y; 00992 // if (!is_near_waypoint(path.obj, temp_wp)) 00993 // break; 00994 // } 00995 // path.path.at(path.current_target) = temp_wp; 00996 // return true; 00997 // } 00998 00999 void ST_ForceField::restrict_vector(const Vec_2D &target_vec, Vec_2D &vec){ 01000 01001 real8 dot_prod = vec.dot_prod(target_vec); 01002 if (dot_prod < 0){ 01003 Vec_2D perpendicular(target_vec.y, -target_vec.x); 01004 vec = vec.project_onto(perpendicular); 01005 } 01006 01007 01008 } 01009 01010 Vec_2D ST_ForceField::get_point_repulsion(const Object *moving_obj, const Loc &point, sint4 radius){ 01011 Vec_2D vec; 01012 Loc center; 01013 moving_obj->get_center(center.x, center.y); 01014 real8 d = center.distance(point) - moving_obj->get_radius() - radius +1; 01015 01016 if (d < moving_obj->get_max_speed() * FIELD_RANGE_MULTIPLIER){ 01017 vec.x = center.x - point.x; 01018 vec.y = center.y - point.y; 01019 real8 k = REPULSOR_BASELINE / (d * d * vec.length()); 01020 vec = vec.multiply(k); 01021 } 01022 return vec; 01023 } 01024 01025 #if 0 01026 Vec_2D ST_ForceField::get_line_repulsion(const Object *moving_obj, const Segment &seg){ 01027 Loc q, p; 01028 Vec_2D line_to_obj, n; 01029 moving_obj->get_center(q.x, q.y); 01030 p = seg.l1; 01031 line_to_obj = Vec_2D(q.x - p.x, q.y - p.y); 01032 n = Vec_2D(seg.l1.y - seg.l2.y, -(seg.l1.x - seg.l2.x)); //perpendicular to segment 01033 01034 line_to_obj = line_to_obj.project_onto(n); 01035 01036 Loc point_on_line(q.x - line_to_obj.x, q.y - line_to_obj.y); 01037 Vec_2D to_l1, to_l2; 01038 01039 to_l1 = Vec_2D(seg.l1.x - point_on_line.x, seg.l1.y - point_on_line.y); 01040 to_l2 = Vec_2D(seg.l2.x - point_on_line.x, seg.l2.y - point_on_line.y); 01041 01042 if (to_l1.dot_prod(to_l2) > 0){ //if both vectors are pointing in the same direction 01043 // if(to_l1.length() > to_l2.length()){ 01044 // return get_point_repulsion(moving_obj, seg.l2); 01045 // } 01046 // else{ 01047 // return get_point_repulsion(moving_obj, seg.l1); 01048 // } 01049 } 01050 else{ 01051 real8 d = q.distance(point_on_line) - moving_obj->get_radius() +1; 01052 if (d < moving_obj->get_max_speed() * FIELD_RANGE_MULTIPLIER){ 01053 real8 k = REPULSOR_BASELINE / (d * d * line_to_obj.length()); 01054 return line_to_obj.multiply(k); 01055 } 01056 } 01057 return Vec_2D(0,0); 01058 } 01059 #else 01060 Vec_2D ST_ForceField::get_line_repulsion(const Object *moving_obj, const Segment &seg) 01061 { 01062 Loc loc; 01063 moving_obj->get_center(loc.x, loc.y); 01064 const Loc &line_start = seg.l1; 01065 const Loc &line_end = seg.l2; 01066 01067 sint4 line_x = line_end.x - line_start.x; 01068 sint4 line_y = line_end.y - line_start.y; 01069 01070 sint4 v_x = loc.x - line_start.x; 01071 sint4 v_y = loc.y - line_start.y; 01072 01073 sint4 a = line_x * v_x + line_y * v_y; 01074 sint4 b = line_x * line_x + line_y * line_y; 01075 01076 Loc proj(line_start.x + line_x * a / b, line_start.y + line_y * a / b); 01077 01078 Vec_2D line_to_obj(loc.x - proj.x, loc.y - proj.y); 01079 Vec_2D to_l1, to_l2; 01080 01081 to_l1 = Vec_2D(seg.l1.x - proj.x, seg.l1.y - proj.y); 01082 to_l2 = Vec_2D(seg.l2.x - proj.x, seg.l2.y - proj.y); 01083 01084 if (to_l1.dot_prod(to_l2) > 0){ //if both vectors are pointing in the same direction 01085 // if(to_l1.length() > to_l2.length()){ 01086 // return get_point_repulsion(moving_obj, seg.l2); 01087 // } 01088 // else{ 01089 // return get_point_repulsion(moving_obj, seg.l1); 01090 // } 01091 } 01092 else{ 01093 real8 d = loc.distance(proj) - moving_obj->get_radius() +1; 01094 if (d < moving_obj->get_max_speed() * FIELD_RANGE_MULTIPLIER /2){ 01095 real8 k = REPULSOR_BASELINE / (d * d * line_to_obj.length()); 01096 return line_to_obj.multiply(k); 01097 } 01098 } 01099 return Vec_2D(0,0); 01100 01101 /* 01102 Loc c; 01103 moving_obj->get_center(c.x, c.y); 01104 01105 sint4 ux = seg.l2.x - seg.l1.x; 01106 sint4 uy = seg.l2.y - seg.l1.y; 01107 sint4 vx = c.x - seg.l1.x; 01108 sint4 vy = c.y - seg.l1.y; 01109 01110 sint4 u_v = ux * vx + uy * vy; // u dot v 01111 if (u_v <= 0) return Vec_2D(0,0); 01112 01113 sint4 u_u = ux * ux + uy * uy; // u dot u 01114 if (u_u <= u_v) return Vec_2D(0,0); 01115 01116 real8 b = real8(u_v) / real8(u_u); 01117 01118 Loc proj((sint4)(seg.l1.x+b*ux), (sint4)(seg.l1.y+b*uy)); // projection to the segment 01119 01120 real8 d1 = c.distance(proj); 01121 real8 d2 = d1 - moving_obj->get_radius() +1; 01122 01123 if (d2 < moving_obj->get_max_speed() * FIELD_RANGE_MULTIPLIER) { 01124 real8 k = REPULSOR_BASELINE / (d2 * d2 * d1); 01125 Vec_2D vec(c.x - proj.x, c.y - proj.y); 01126 return vec.multiply(k); 01127 } 01128 return Vec_2D(0,0); 01129 */ 01130 } 01131 #endif 01132 01133 //------------------------------------------------------------------------- 01134 01135 bool ST_ForceField::plan_tasks(uint4 max_time) 01136 { 01137 assert(pfEngine!=NULL); 01138 sint4 stop_time = 0; 01139 if(max_time > 0){ 01140 stop_time = (sint4)SDL_GetTicks() + max_time; 01141 } 01142 01143 // cout << "Chance To Plan Task\n" ; 01144 01145 // try to plan one failed and one pending tasks 01146 bool did_something = plan_failed_task(0);//stop_time); 01147 bool did_some_more = plan_pending_task(0);//stop_time); 01148 sint4 counter = 0; 01149 //Continue planning until we have reached the max_time 01150 while( (sint4)SDL_GetTicks() < stop_time && (!pendingTasks.empty() || !failedTasks.empty() )) { 01151 if(counter++ % 2 == 0) 01152 did_something = plan_failed_task(stop_time); 01153 else 01154 did_some_more = plan_pending_task(stop_time); 01155 01156 01157 did_some_more = did_something || did_some_more; // careful not to short-circuit! 01158 } 01159 01160 01161 01162 did_something = did_some_more || did_something; 01163 return did_something; 01164 01165 01166 } 01167 01168 //---------------------------------------------------------------------------------- 01169 bool ST_ForceField::plan_pending_task(uint4 stop_time) 01170 { 01171 if (pendingTasks.empty()) return false; 01172 01173 TIter tit = pendingTasks.front(); pendingTasks.pop_front(); 01174 01175 // EXPLANATION: There is currently an unsolved bug which seems to 01176 // push invalid TIters onto the pendingTasks queue. In order to guard 01177 // against this phenomena, we are going to do some basic sanity 01178 // checking on the Task. WARNING: This is a TEMPORARY measure. We 01179 // should hunt for the source of the bug ASAP, and not be content 01180 // with being able to detect it. 01181 TIter it; 01182 for(it = tasks.begin(); it != tasks.end(); it++) 01183 { 01184 if( &(*it) == &(*tit) ) 01185 { 01186 break; 01187 } 01188 } 01189 if( it == tasks.end() ) 01190 { 01191 std::cerr << "[CRITICAL WARNING] Segfault avoided in "; 01192 std::cerr << "ST_ForceField::plan_pending_task(void)" << std::endl; 01193 return false; 01194 } 01195 01196 // mark the task as handled 01197 tit->isPending = false; 01198 01199 __assert_object_set_valid(tit->task.objs); 01200 01201 if (tit->task.objs.size() <= 0) 01202 { 01203 //ERR("No Objects"); 01204 cerr << "Invalid Task: no Objects in task\n"; 01205 clean_task((TerrainBase::TaskId) &tit); 01206 } 01207 01208 // if the object is moving, stop it first and come back to it later 01209 if (stop_to_plan) 01210 { 01211 bool moving = false; 01212 std::set<const Object*>::const_iterator it; 01213 for(it = tit->task.objs.begin(); it != tit->task.objs.end(); it++) 01214 { 01215 // There is a known segfault somewhere around here 01216 const Object * obj = (*it); 01217 const GameObj * p = dynamic_cast<const GameObj*>(obj); 01218 if (p == NULL) 01219 { 01220 ERR("Object not a GameObj"); // TODO: Check 01221 } 01222 GameObj * p2 = const_cast<GameObj*>(p); 01223 GameObj::check_ptr(p2); 01224 if (!GameObj::get_obj_id(p2)) 01225 { 01226 ERR("OBJECT IS NOT THERE...\n"); 01227 } 01228 if (obj->get_speed() > 0 || obj->get_moving()) 01229 { 01230 moving = true; 01231 break; 01232 } 01233 } 01234 01235 if (moving) 01236 { 01237 __assert_object_set_valid(tit->task.objs); 01238 FORALL(tit->task.objs, it) 01239 { 01240 objsToStop.insert(*it); 01241 } 01242 pendingTasks.push_back(tit); 01243 return false; 01244 // fixme: might be better to try another pending 01245 // task already in this tick... 01246 } 01247 } 01248 // plan 01249 TEST(cout << obj << " planning pending path" << endl); 01250 plan_task(tit, stop_time); 01251 return true; 01252 } 01253 01254 //------------------------------------------------------------------- 01255 struct FindFailed { 01256 bool operator()(FF_Task &task) { return task.isFailed && !task.noPathFound; }; 01257 }; 01258 01259 //------------------------------------------------------------------- 01260 ST_ForceField::TIter ST_ForceField::get_random_failed_task(void) 01261 { 01262 // find a failed path for which we don't yet know that 'noPathFound' 01263 // (ie we still didn't try to replan and found that there is no path) 01264 if( tasks.empty() ) 01265 return tasks.end(); 01266 01267 // start the search with in a random position to avoid planning the same 01268 // path repeatedly (in case it is immediately marked as failed again). 01269 int skip = rand(tasks.size()); 01270 TIter start_at = tasks.begin(); 01271 advance(start_at, skip); 01272 01273 // search from the middle to the end 01274 TIter tit = find_if(start_at, tasks.end(), FindFailed()); 01275 // if found 01276 if( tit != tasks.end() ) 01277 return tit; 01278 // else, search from start to middle 01279 tit = find_if(tasks.begin(), start_at, FindFailed()); 01280 return (tit != start_at)? tit : tasks.end(); 01281 } 01282 //------------------------------------------------------------------------------- 01283 01284 //---------------------------------------------------------------------------------- 01285 bool ST_ForceField::plan_failed_task(uint4 stop_time) 01286 { 01287 if (failedTasks.empty()) return false; 01288 01289 TIter tit = failedTasks.front(); failedTasks.pop_front(); 01290 01291 // EXPLANATION: There is currently an unsolved bug which seems to 01292 // push invalid TIters onto the failedTasks queue. In order to guard 01293 // against this phenomena, we are going to do some basic sanity 01294 // checking on the Task. WARNING: This is a TEMPORARY measure. We 01295 // should hunt for the source of the bug ASAP, and not be content 01296 // with being able to detect it. 01297 TIter it; 01298 for(it = tasks.begin(); it != tasks.end(); it++) 01299 { 01300 if( &(*it) == &(*tit) ) 01301 { 01302 break; 01303 } 01304 } 01305 if( it == tasks.end() ) 01306 { 01307 std::cerr << "[CRITICAL WARNING] Segfault avoided in "; 01308 std::cerr << "ST_ForceField::plan_failed_task(void)" << std::endl; 01309 return false; 01310 } 01311 01312 // if the object is moving, stop it first and come back to it later 01313 01314 __assert_object_set_valid(tit->task.objs); 01315 assert(tit->task.objs.size() > 0); 01316 01317 if (stop_to_plan){ 01318 01319 bool moving = false; 01320 __assert_object_set_valid(tit->task.objs); 01321 FORALL(tit->task.objs, it){ 01322 01323 if ((*it)->get_speed()>0 || (*it)->get_moving()){ 01324 moving = true; 01325 break; 01326 } 01327 01328 01329 } 01330 01331 if (moving) { 01332 __assert_object_set_valid(tit->task.objs); 01333 FORALL(tit->task.objs, it){ 01334 objsToStop.insert(*it); 01335 } 01336 failedTasks.push_back(tit); 01337 return false; 01338 // fixme: might be better to try another pending task already in this tick... 01339 } 01340 } 01341 // plan 01342 TEST(cout << obj << " planning pending path" << endl); 01343 plan_task(tit, stop_time); 01344 tit->replanned++; 01345 01346 return true; 01347 } 01348 01349 //------------------------------------------------------------------- 01350 // bool ST_ForceField::plan_failed_task(void) 01351 // { 01352 // assert(pfEngine!=NULL); 01353 01354 // // get a failed task 01355 // TIter tit = get_random_failed_task(); 01356 // if( tit == tasks.end() ) 01357 // return false; 01358 01359 // // if the object is moving, stop it first and come back to it later 01360 // assert(tit->task.objs.size() > 0); 01361 // const Object *obj = *(tit->task.objs.begin()); 01362 01363 // if( obj->get_speed() > 0 || obj->get_moving() ) { 01364 // objsToStop.insert(obj); 01365 // return false; 01366 // // fixme: might be better to try another failed task already in this tick... 01367 // } 01368 // tit->replanned++; 01369 // // find-path 01370 // TEST(cout << obj << " replanning path" << endl); 01371 // plan_task(tit); 01372 // return true; 01373 // } 01374 01375 //------------------------------------------------------------------- 01376 void ST_ForceField::plan_task(TIter tit, uint4 stop_time) 01377 { 01378 Path newpath; 01379 bool found; 01380 01381 __assert_object_set_valid(tit->task.objs); 01382 set<const Object*>::iterator obj_it = tit->task.objs.begin(); 01383 01384 // Object to move 01385 const Object *obj = *obj_it; 01386 01387 01388 // obj_it++; 01389 //remove the members of the group from the representation 01390 // for (; obj_it != tit->task.objs.end(); obj_it++){ 01391 // tit->followers.push_back(*obj_it); 01392 // pfEngine->remove_object(*obj_it); 01393 // } 01394 // cout << "CALLING GLOBAL PATHFINDER\n"; 01395 if (tit->task.goal.target == Goal::LOCATION) { 01396 found = pfEngine->find_path(obj, tit->task.goal.loc, newpath, 0, IGNORE_ALL_MOBILE_OBJS, stop_time); 01397 if (found){ 01398 newpath.locs[0] = tit->task.goal.loc; 01399 } 01400 } else { 01401 assert (tit->task.goal.target == Goal::OBJ); 01402 /*assert (!pushing_on || 01403 what_team(tit->task.goal.obj) != client_pID || 01404 tit->task.goal.mode != Goal::TOUCH || 01405 tit->task.goal.obj->get_max_speed() <= 0);*/ //because you'll push the object out of the way 01406 found = pfEngine->find_path(obj, tit->task.goal.obj, newpath, 0, IGNORE_ALL_MOBILE_OBJS, stop_time); 01407 } 01408 01409 //add the members of the group back into the representation 01410 // obj_it = tit->task.objs.begin(); 01411 // obj_it++; 01412 // for (; obj_it != tit->task.objs.end(); obj_it++){ 01413 // pfEngine->insert_object(*obj_it); 01414 // } 01415 01416 // insert results into data structures 01417 if( found ) { 01418 assert(newpath.locs.size() > 0); 01419 tit->set_path(obj, newpath.locs); 01420 tit->isFailed = false; 01421 } 01422 else { 01423 // fixme: maybe add to "failed"/sleep etc. ? 01424 tit->noPathFound = true; 01425 } 01426 } 01427 01428 //------------------------------------------------------------------- 01429 01430 // BEGIN CHANGES 01431 bool ST_ForceField::is_at_location(const Object* obj, const Loc& target) 01432 { 01433 Loc position; obj->get_center(position.x, position.y); 01434 01435 return position.distance(target) <= obj->get_max_speed(); 01436 } 01437 // END CHANGES 01438 01439 //------------------------------------------------------------------- 01440 01441 bool ST_ForceField::is_near_location(const Object *obj, const Loc& target, sint4 range){ 01442 Loc position; obj->get_center(position.x, position.y); 01443 return position.distance(target) <= range; 01444 } 01445 01446 //------------------------------------------------------------------- 01447 bool ST_ForceField::is_at_goal(const Object* obj, const Task& task) 01448 { 01449 01450 typeof(obj2taskId.begin()) o2t; 01451 // Getting to the goal depends on the definition of the task: 01452 if (task.goal.target == Goal::OBJ ) { 01453 switch(task.goal.mode) { 01454 case Goal::TOUCH : 01455 return( obj->distance_to(*task.goal.obj) <= 1 ); 01456 // using 1-tick look-ahead: 01457 //return( obj->distance_to(*task.goal_obj) <= obj->get_speed() ); 01458 break; 01459 01460 case Goal::VICINITY : 01461 01462 return (obj->distance_to(*task.goal.obj) <= task.goal.distance); 01463 //assert(0 && "Not implemented yet"); 01464 break; 01465 01466 case Goal::ATTACK : 01467 01468 return(is_in_range(obj, task.goal.obj)); 01469 01470 break; 01471 01472 default: 01473 ERR("Unknown mode"); 01474 } 01475 } else if (task.goal.target == Goal::LOCATION ) { 01476 switch(task.goal.mode) { 01477 case Goal::TOUCH : 01478 01479 // using 1-tick look-ahead; also suffers from low-resolution accuracy problems 01480 // hack: low-resolution problems overcome by setting path[0] to the goal loc during plan_task method 01481 return is_at_location(obj, task.goal.loc); 01482 break; 01483 01484 case Goal::VICINITY : 01485 return is_near_location(obj, task.goal.loc, task.goal.distance); 01486 //assert(0 && "Not implemented yet"); 01487 break; 01488 01489 case Goal::ATTACK : 01490 return(is_in_range(obj, task.goal.loc)); 01491 break; 01492 01493 default: 01494 ERR("Unknown mode"); 01495 } 01496 } else { 01497 ERR("Unknown target type"); 01498 } 01499 return false; 01500 } 01501 01502 01503 //------------------------------------------------------------------- 01504 01505 bool ST_ForceField::is_in_range(const Object *moving_obj, const Loc goal_loc){ 01506 //fixme: does not check if unit can see the target. 01507 const GameObj *gob = dynamic_cast<const GameObj*>(moving_obj); 01508 sint4 range = 1; 01509 if (gob){ 01510 ScriptObj *weapon = gob->component("weapon"); 01511 const sintptr *r = weapon->get_int_ptr("max_ground_range"); 01512 if (r){ 01513 range = *r; 01514 } 01515 } 01516 sint4 x,y; 01517 moving_obj->get_center(x,y); 01518 Loc obj(x,y); 01519 01520 return ( obj.distance(goal_loc) <= range ); 01521 01522 // this is the check in ShooterAI. Other way makes more intuitive sense though... 01523 // real8 rdist = moving_obj->distance_to(*goal_obj); 01524 // sint4 sdist = (sint4)floor(rdist); 01525 // return (sdist <= range); 01526 } 01527 01528 bool ST_ForceField::is_in_range(const Object *moving_obj, const Object *goal_obj){ 01529 //fixme: does not check if unit can see each other. 01530 const GameObj *gob = dynamic_cast<const GameObj*>(moving_obj); 01531 sint4 range = 1; 01532 if (gob){ 01533 ScriptObj *weapon = gob->component("weapon"); 01534 const sintptr *r = weapon->get_int_ptr("max_ground_range"); 01535 if (r){ 01536 range = *r; 01537 } 01538 } 01539 01540 //if not a GameObj or with no weapon range, should behave exactly 01541 //like the check for touch. 01542 return ( moving_obj->distance_to(*goal_obj) <= range ); 01543 01544 // this is the check in ShooterAI. Other way makes more intuitive sense though... 01545 // real8 rdist = moving_obj->distance_to(*goal_obj); 01546 // sint4 sdist = (sint4)floor(rdist); 01547 // return (sdist <= range); 01548 } 01549 01550 01551 //----------------------------------------------------------------------- 01552 //------------------------Vec_2D----------------------------------------- 01553 //----------------------------------------------------------------------- 01554 Vec_2D Vec_2D::operator+(const Vec_2D &other) const{ 01555 return Vec_2D(x + other.x, y + other.y); 01556 } 01557 01558 Vec_2D Vec_2D::operator-(const Vec_2D &other) const{ 01559 return Vec_2D(x - other.x, y - other.y); 01560 } 01561 01562 sint4 Vec_2D::dot_prod(const Vec_2D &other) const{ 01563 return x*other.x + y*other.y; 01564 } 01565 01566 real8 Vec_2D::length() const{ 01567 real8 length = sqrt(((real8)x)*x + y*y); 01568 return length; 01569 } 01570 01571 Vec_2D Vec_2D::project_onto(const Vec_2D &other) const{ 01572 real8 k = ((real8)this->dot_prod(other)) / other.dot_prod(other); 01573 return other.multiply(k); 01574 } 01575 01576 Vec_2D Vec_2D::multiply(real8 k) const{ 01577 return Vec_2D(round(k * x), round(k * y)); 01578 } 01579 01580 bool Vec_2D::operator==(const Vec_2D &other) const{ 01581 return (x == other.x && y == other.y); 01582 } 01583 01584 bool Vec_2D::operator!=(const Vec_2D &other) const{ 01585 return !(*this == other); 01586 } 01587 01588 bool Loc_less::operator() (Loc a, Loc b) const { 01589 if (a.x < b.x){ 01590 return true; 01591 } 01592 else if (a.x == b.x && a.y < b.y){ 01593 return true; 01594 } 01595 return false; 01596 } 01597 01598 01599 01600 01601 //---------------------------------------------------------------------------------- 01602 01603 #define EPS 1e-9f 01604 01605 01606 // UnitPath_FF::UnitPath_FF(const Object *obj_, const Vector<Loc> &path_) 01607 // : obj(obj_), path(path_), no_orders_yet(true), 01608 // stopped_to_avoid_collision(false), collision_step(0), wakeup(0) 01609 // { 01610 // current_target = path.size()-1; 01611 // if (path.size() > 0) { 01612 // goal = path[0]; 01613 // } 01614 // spot = Loc(-1, -1); 01615 // } 01616 01617 01618 // bool UnitPath_FF::is_intersect(const Segment &segment) const 01619 // { 01620 // typeof(path.begin()) pj = path.begin(); 01621 // typeof(path.begin()) pi = pj++; 01622 // for( ; pj != path.end() ; pi++, pj++ ) { 01623 // if (Segment(*pi, *pj).touches(segment) ) 01624 // return true; 01625 // } 01626 // return false; 01627 // } 01628 01629 //---------------------------------------------------------------------------------- 01630 //---------------------------------------------------------------------------------- 01631 01632 void FF_Task::set_path(const Object *obj_, const Vector<Loc> &path_) 01633 { 01634 // overwrite an existing path, retaining the collision-monitoring data. 01635 if( path ) { 01636 Loc saved_spot = path->spot; 01637 int saved_collision_step = path->collision_step; 01638 bool saved_stopped_to_avoid_collision = path->stopped_to_avoid_collision; 01639 uint4 saved_wakeup = path->wakeup; 01640 01641 path = boost::shared_ptr<UnitPath_FF>(new UnitPath_FF(obj_, path_)); 01642 01643 path->spot = saved_spot; 01644 path->collision_step = saved_collision_step; 01645 path->stopped_to_avoid_collision = saved_stopped_to_avoid_collision; 01646 path->wakeup = saved_wakeup; 01647 } 01648 // No previous path was planned, so just create a new one. 01649 else { 01650 path = boost::shared_ptr<UnitPath_FF>(new UnitPath_FF(obj_, path_)); 01651 } 01652 } 01653 01654 01655 01656 //---------------------------------------------------------------------------------- 01657 01658 } //end of namespace