|
ORTS
|
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> §ors); 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