ORTS

SimpleTerrain.H

Go to the documentation of this file.
00001 #ifndef SimpleTerrain_H
00002 #define SimpleTerrain_H
00003 
00004 // $Id: SimpleTerrain.H 5279 2007-06-23 02:49:08Z mburo $
00005 
00006 // This is an ORTS file 
00007 // (c) Michael Buro, Sami Wagiaalla, David Deutscher
00008 // licensed under the GPL
00009 
00010 // simple grid-based terrain representation
00011 
00012 #include "Global.H"
00013 #include "TerrainBasicImp.H"
00014 #include "ST_SimpleMap.H"
00015 #include "Random.H"
00016 #include "Coord.H"
00017 #include "Distance.H"
00018 
00019 #include "boost/shared_ptr.hpp"
00020 
00021 
00022 namespace SimpleTerrain {
00023 
00024   typedef TerrainBase::Loc              Loc;
00025   typedef TerrainBase::Segment          Segment;  
00026   typedef TerrainBase::Task             Task;
00027   typedef TerrainBase::Path             Path;
00028   typedef TerrainBase::Goal             Goal;
00029   typedef TerrainBase::MoveCmd          MoveCmd;
00030   typedef TerrainBase::StatusMsg        StatusMsg;
00031   typedef TerrainBase::TaskId           TaskId;
00032   typedef TerrainBase::ConsiderObjects  ConsiderObjects;
00033 
00034 //-----------------------------------------------------------------------------
00035 //-----------------------------------------------------------------------------
00036   /// make a CIRCLE Object appear to be at an arbitrary location
00037   struct ObjectTranslocator : public Object {
00038     const Object *obj;
00039     sint4 newx, newy;
00040 
00041     ObjectTranslocator(const Object *obj_) : obj(obj_), newx(0), newy(0) { 
00042       assert(obj->get_shape() == Object::CIRCLE);
00043     };
00044     void set_position(sint4 newx_, sint4 newy_) { newx = newx_; newy = newy_; };
00045 
00046     // make it look like it's in a new location
00047     void  get_center(sint4 &x, sint4 &y, sint4 *px=0, sint4 *py=0) const
00048       { x = newx; y = newy; if(px) *px = newx; if(py) *py = newy; };
00049     real8 distance_to(const Object &other) const 
00050       { return Distance::distance(*this, other); };
00051 
00052     // forward calls to the internal 'real' object
00053     Shape get_shape() const               { return obj->get_shape(); };
00054     sint4 get_max_speed() const           { return obj->get_max_speed(); };
00055     sint4 get_radius() const              { return obj->get_radius(); };
00056     ZCat  get_zcat(ZCat *prev=0) const    { return obj->get_zcat(prev); };
00057     sint4 get_speed(sint4 *prev=0) const  { return obj->get_speed(prev); };
00058     bool  get_moving(bool *prev=0) const  { return obj->get_moving(prev); }; 
00059     bool is_pending_action(void) const    { return obj->is_pending_action(); };
00060 
00061     // not relevant for circles
00062     void get_p1(sint4 &, sint4 &, sint4 *, sint4 *) const { assert(0 && "Unimplemented for SimpleTerrain::ObjectTranslocator"); }; 
00063     void get_p2(sint4 &, sint4 &, sint4 *, sint4 *) const { assert(0 && "Unimplemented for SimpleTerrain::ObjectTranslocator"); };
00064   };
00065 
00066 //-----------------------------------------------------------------------------
00067 //-----------------------------------------------------------------------------
00068   // fixme: Transfer this functionality into ST_Task, this is a heritage from before
00069   // the TerrainBase interface.
00070   struct UnitPath
00071   {
00072     /// Path data
00073     //@{
00074     const Object *obj;
00075     Vector<Loc> path;
00076     Loc goal;
00077     int current_target; // index of current way point in path vector
00078     bool no_orders_yet; // true at the beginning, before the first move command is issued
00079     //@}
00080 
00081     /// Collision avoidance and resolution
00082     //@{
00083     // If adding fields - check ST_Task::set_path for needed updates.
00084     bool stopped_to_avoid_collision; // true if the object was stopped temporarily to avoid a collision
00085     int collision_step; // this is incremented while solving unexpected stops
00086     uint4 wakeup;       // if SDL_GetTicks() >= wakeup then this UnitPath is not
00087                         // sleeping ie not to be skipped over.
00088     Loc spot;           // spot of the last unexpected collision
00089     //@}
00090 
00091     UnitPath(const Object *obj_, const Vector<Loc> &path_);
00092     ~UnitPath() {};
00093 
00094     void mark_spot(const Loc &p) { spot = p; };
00095     void sleep(int time) { wakeup = SDL_GetTicks() + time; };
00096     bool sleeping() const { return wakeup > SDL_GetTicks(); };
00097 
00098     bool is_intersect(const Segment &) const;
00099   };
00100 
00101 
00102   /// The internal data complementing Task data:
00103   struct ST_Task {
00104     Task task;                           //< This is the actual task and is characterized by:
00105     bool isPending;                      //< pending or yet to be planned
00106     bool isFailed;                       //< failed, and try to replan 
00107     bool noPathFound;                    //< could not find a path to that location 
00108     boost::shared_ptr<UnitPath> path;    // todo: replace with a cleaner implementation
00109 
00110     // These are used to calculate the approximate priority of the task:
00111 
00112     /// When was the task created, as return by SDL_GetTicks()
00113     uint8 start_time;
00114     /// Straight-line length of the path, to prefer planning shorter paths first
00115     real8 path_dist;
00116  
00117     ST_Task(const Task& task_);
00118     ST_Task(const ST_Task& other) 
00119       : task(other.task), isPending(other.isPending),
00120         isFailed(other.isFailed), noPathFound(other.noPathFound), path(other.path),
00121         start_time(other.start_time), path_dist(other.path_dist) {};
00122     void set_path(const Object *obj_, const Vector<Loc> &path_);
00123     bool isValid() const { return !isPending && !isFailed && !noPathFound && path; };
00124   private:
00125     ST_Task operator=(const ST_Task& other);
00126   };
00127 
00128   /// The definition of a square using two corners.
00129   struct UnitSquare {
00130     Loc topLeft;
00131     Loc bottomRight;
00132     const Object *obj;
00133     ST_Task *task;
00134 
00135     UnitSquare() : obj(NULL), task(NULL) {};
00136     UnitSquare(Loc topLeft_, Loc bottomRight_, const Object *obj_, ST_Task *task_)
00137       : topLeft(topLeft_), bottomRight(bottomRight_), obj(obj_), task(task_) {};
00138     ~UnitSquare() {};
00139 
00140     UnitSquare& operator=(const UnitSquare& other) {
00141       if( this != &other ) {
00142         topLeft = other.topLeft;
00143         bottomRight = other.bottomRight;
00144         obj = other.obj;
00145         task = other.task;
00146       }
00147       return *this;
00148     }
00149     
00150   };
00151 
00152   /** A sector is a large area, containing objects (actually tasks) whose paths 
00153       in the near future will pass through the area, and hence may collide. */
00154   struct Sector {
00155     Vector<ST_Task*> tasks;
00156     
00157     Sector() {};
00158     Sector(const Sector &other) : tasks(other.tasks) {};
00159     Sector& operator=(const Sector &other) { 
00160       if( this != &other ) 
00161         tasks = other.tasks; 
00162       return *this; 
00163     };
00164   };
00165 
00166 //-----------------------------------------------------------------------------
00167 //-----------------------------------------------------------------------------
00168 
00169   /// A Simple implementation of the TerrainBase interface.
00170   /** This TerrainBase implementation is using a relatively basic Astar algorithm,
00171       originally written by Sami Wagiaalla and adapted by David Deutscher. 
00172       Limitations:
00173       - Only considers a single Obj per Task (ignoring the second and on obj_ids).
00174       - Assumes all objects are either ON_LAND or IN_AIR (and that boundaries do not 
00175         apply to the latter, though this is currently always true so it's not really a
00176         limitation).
00177       - Using a target *object*:
00178         a) No pursuit is implemented. Instead, the target location at the moment 
00179            of pathfinding is used as the goal.
00180         b) If the object is removed, it's last known location is used as goal.
00181         Nonetheless, it is useful (mainly for static targets) since it allows a 
00182         'touch' condition - where the unit is not really at the exact goal location -
00183         to be reported as a successful arrival and not retried.
00184   */
00185   class ST_Terrain : public TerrainBasicImp<ST_Task>
00186   {
00187   public:
00188     ST_Terrain();
00189     virtual ~ST_Terrain() {};
00190 
00191     /// add command-line options specific to this implementation, using static methods of class Options
00192     static void add_options(void);
00193 
00194     //---------------------------------------------------------------------
00195     /// \name Implementation of relevant parts of the TerrainBase interface
00196     //---------------------------------------------------------------------
00197     //@{
00198     virtual void init(sint4 tiles_x_, sint4 tiles_y_, sint4 tile_points_, sint4 client_pID_, sint4 neutral_pID_);
00199 
00200     virtual void add_obj(const Object *obj);
00201     virtual void update_obj(const Object *obj);
00202     virtual void remove_obj(const Object *obj);
00203     virtual void add_segments(const Vector<Segment> &s);
00204 
00205     virtual void cancel_task(const Object *obj);
00206     virtual void remove_task(TaskId tid);
00207 
00208     virtual void execute_tasks(Vector<MoveCmd> &cmds, Vector<StatusMsg> &msgs);
00209     virtual bool plan_tasks(uint4 max_time=0);
00210 
00211     virtual real8 find_path(const Loc &l1, const Loc &l2, sint4 radius, Path *path, ConsiderObjects consider = CONSIDER_ALL);
00212     virtual real8 find_path(const Object *obj, const Goal &goal, Path *path, ConsiderObjects consider = CONSIDER_ALL);
00213     //@}
00214     //---------------------------------------------------------------------
00215 
00216    
00217     //---------------------------------------------------------------------
00218     /// \name Richer interfaces, candidates for adding to TerrainBase
00219     //---------------------------------------------------------------------
00220     //@{
00221     /// mark the path as an obstacle to be avoided in future PFing.
00222     void block_path(const Object *obj, const Path &path);
00223     /** Release a previously marked obstacle-path. 
00224         Releasing an unblocked-path might corrupt the inner world view. */
00225     void release_path(const Object *obj, const Path &path);
00226     /** Execute a previously planned path. The waypoints are assumed
00227         to be in forward order, as returned by TerrainBase::find_path().
00228         The caller is responsible that the given arguments are consistent. */
00229     TaskId execute_task(const Task &task, const Path &path);
00230     //@}
00231     //---------------------------------------------------------------------
00232 
00233 
00234     /// The mechanics of world representation and basic A* path-finding
00235     class PFEngine; friend class ST_Terrain::PFEngine;
00236 
00237   private:
00238 
00239     // prevent copying as we currently don't need it.
00240     ST_Terrain(const ST_Terrain& other);
00241     ST_Terrain operator=(const ST_Terrain& other);
00242 
00243   protected:
00244  
00245     //---------------------------------------------------------------------
00246     /// \name Internal accessories
00247     //---------------------------------------------------------------------
00248     //@{
00249     /// Define the maximum amount of time that a long distance task can be put off
00250     static const uint4 MAXIMUM_TASK_AGE;
00251 
00252     /** Perform PF for a single pending task, and insert results to data structures. 
00253         Return true iff did some planning. */
00254     bool plan_pending_task(uint4 stop_time = 0);
00255     /** Perform PF for a single failed task, and insert results to data structures. 
00256         Return true iff did some planning. */
00257     bool plan_failed_task(uint4 stop_time = 0);
00258     /** Accessory function for planning either a pending or a failed task. */
00259     void plan_task(TIter tit, uint4 stop_time);
00260     /** Randomly choose a failed task. Return tasks.end() if no failed task exists. */
00261     TIter get_random_failed_task(void);
00262  
00263     /** Invalidate existing paths that intersect a given new boundary. */
00264     void invalidate_paths(const Vector<Segment> &segments);
00265     
00266     /** Test if the object is at the given location, 
00267     accounting for a 1-tick look-ahead for moving objects. */
00268     static bool is_at_location(const Object* obj, const Loc& target);
00269     /** Test if the distance from the object to the target is within the given range.*/
00270     static bool is_near_location(const Object* obj, const Loc& target, sint4 range);
00271     /** Test if the object is at the goal of the given task, implementing 
00272         (some) of the various definitions of 'goal'. */
00273     bool is_at_goal(const Object* obj, const Task& task);
00274     /** Test if the object is within firing range */
00275     static bool is_in_range(const Object* moving_obj, const Object *goal_obj);
00276     /** Test if the location is within firing range */
00277     static bool is_in_range(const Object* moving_obj, const Loc goal_loc);
00278    
00279     /** Find the best task to plan now.  This is based on the task's age and also
00280     how short its path is.*/
00281     std::deque<TIter>::iterator get_best_task(void);
00282 
00283     /// Create a MoveCmd for the current waypoint of the given UnitPath, and add to the vector.
00284     static void add_move_command(const UnitPath& path, Vector<MoveCmd> &cmds);
00285     //@}
00286     //---------------------------------------------------------------------
00287 
00288 
00289     //---------------------------------------------------------------------
00290     /// \name Local obstacle avoidance
00291     //---------------------------------------------------------------------
00292     //@{
00293     /// Anticipate and handle collisions that are expected to occur in the next X ticks.
00294     static const sint4 ANTICIPATE_COLLISION_TICKS;
00295     /// Compute expected collisions using integral coordinates but with a higher accuracy.
00296     static const sint4 FINE_GRID_RES_BITS;
00297 
00298     /// Identify and resolve expected collisions between controlled units.
00299     void resolve_friendly_collisions(void);
00300     /** true iff the objects of the given tasks will collide before 
00301         reaching their next waypoint, and within the ANTICIPATE_COLLISION_TICKS 
00302         next ticks. */
00303     bool will_collide(Vector<ST_Task*>::iterator i, 
00304               Vector<ST_Task*>::iterator j, 
00305                       bool assume_obj1_stopped=false, 
00306                       bool assume_obj2_stopped=false);
00307     /// Convert to the higher resolution
00308     static sint4 fine_grid(sint4 v) { return v << FINE_GRID_RES_BITS; };
00309     /** \brief Get the position of a given object and its heading towards a given target, using fine coordinates.
00310         Compute the spatial params (position, target, velocity) using the fine-grid
00311         coordinates.
00312         @param o              The object
00313         @param target         The location it is moving to, in world (not fine) coordinates. This will be translated to fine coords as output.
00314         @param fine_position  If != NULL, will contain o's position in fine coords, as output.
00315         @param fine_velocity  If != NULL, will contain o's velocity (direction+speed) in fine coords, as output.
00316     */
00317     void get_fine_spatial_params(const Object *o, ScalarPoint &target,
00318                                  ScalarPoint *fine_position, ScalarPoint *fine_heading);
00319     //@}
00320     //---------------------------------------------------------------------
00321 
00322 
00323     //---------------------------------------------------------------------
00324     /// \name Motion Sectors for faster collision detection - James Bergsma
00325     //---------------------------------------------------------------------
00326     //@{
00327     /// How many tiles per motion sector (side)
00328     static const uint4 TILES_PER_MOTION_SECTOR;
00329 
00330     /** Find the position of an object in 'when' ticks. 
00331         For multiple-waypoints paths, this is only an approximation:
00332         1. It assumes a continuous motion, disregarding the rounding of the
00333            time it takes to travel each straight line leg of the path to 
00334            integer ticks (rounding done by the ORTS engine).
00335         2. It doesn't consider delays in giving the "go to next 
00336            waypoint" actions (eg due to network lag or slow client sise 
00337            processing), which cause the unit to stop at waypoints.
00338 
00339         @param  cur_path  The path to analyze.
00340         @param  when      Compute the location in that many ticks.
00341         @param  where     Returns the future position in this parameter.
00342         @return the waypoint number (0 is first waypoint, located at the back() 
00343                 of cur_path.path) that will already be passed through by 
00344                 that time, -1 if the first waypoint will not be reached.
00345     */
00346     sint4 get_future_position(const UnitPath &cur_path, sint4 when, Loc &where);
00347 
00348     /** Using a future position create a square containing the object. 
00349         @param task   The task to analyze. Assuming that task->path is valid.
00350         @return       The UnitSquare that will contain the object in the close future.
00351     */   
00352     UnitSquare find_motion_square(const TIter &task);
00353 
00354     /** Insert each motion square into the sectors that contain it. Sectors
00355         with >= 2 squares are marked as active (meaning they should be 
00356         tested for potential collisions. 
00357     @param motionSquares    These are the squares in which units are currently active.
00358     @param sectors          This is a large grid representation in which the sectors will be placed.
00359     */
00360     void insert_into_sectors(const Vector<UnitSquare> &motionSquares,
00361                                          SimpleMap<Sector> &sectors);
00362     //@}
00363     //---------------------------------------------------------------------
00364 
00365 
00366     boost::shared_ptr<PFEngine> pfEngine; ///< The internal Path Finding engine
00367     std::set<const Object*> objsToStop;   ///< Objects for which we should set a stop-moving action
00368     Random rand;
00369     bool noMotionSectors;                ///< Should the motion sectors optimization be used for collision detection
00370 
00371     friend class SimpleTerrainWidget;
00372     friend class SimpleTerrainOverlay;
00373   };
00374 
00375 } // end of namespace SimpleTerrain
00376 
00377 REGISTER_TYPEOF(2501, Vector<SimpleTerrain::Sector>::iterator);
00378 REGISTER_TYPEOF(2502, Vector<SimpleTerrain::Sector>::const_iterator);
00379 REGISTER_TYPEOF(2503, Vector<SimpleTerrain::Sector*>::iterator);
00380 REGISTER_TYPEOF(2504, Vector<SimpleTerrain::Sector*>::const_iterator);
00381 REGISTER_TYPEOF(2505, Vector<SimpleTerrain::ST_Task*>::iterator);
00382 REGISTER_TYPEOF(2506, Vector<SimpleTerrain::ST_Task*>::const_iterator);
00383 REGISTER_TYPEOF(2507, Vector<const SimpleTerrain::ST_Task*>::iterator);
00384 REGISTER_TYPEOF(2508, Vector<const SimpleTerrain::ST_Task*>::const_iterator);
00385 REGISTER_TYPEOF(2509, Vector<SimpleTerrain::UnitSquare>::iterator);
00386 REGISTER_TYPEOF(2510, Vector<SimpleTerrain::UnitSquare>::const_iterator);
00387 REGISTER_TYPEOFS_TerrainBasicImp_OneTime(2511, SimpleTerrain::ST_Task);
00388 REGISTER_TYPEOFS_TerrainBasicImp_ForEach(2515, SimpleTerrain::ST_Task);
00389 #endif


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