ORTS

ST_ForceField.C

Go to the documentation of this file.
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


Generated on Fri May 18 2012 03:02:48 for ORTS by Doxygen1.7.3