|
ORTS
|
00001 #ifndef TerrainBase_H 00002 #define TerrainBase_H 00003 00004 // $Id: TerrainBase.H 5279 2007-06-23 02:49:08Z mburo $ 00005 // This is an ORTS file (c) Michael Buro, David Deutscher, licensed under the GPL 00006 00007 // Terrain Base Class 00008 // detaches terrain code from ORTS 00009 00010 #include "Global.H" 00011 #include "Object.H" 00012 00013 class CWidget; 00014 00015 /** A base interface to terrain (path-finding) implementations. 00016 This interface detaches path-finding (PF) code from ORTS. 00017 A derived implementation is: 00018 1. notified of world changes, including object location/speed changes. 00019 2. requested to handle PF tasks in 3 stages: 00020 a. calls to add/remove tasks. 00021 b. calls to plan tasks - here the implementation can spend thinking time 00022 c. calls to execute_tasks - the implementation should return low-level 00023 move commands and arrived/failed messages per path, where relevant. 00024 */ 00025 class TerrainBase 00026 { 00027 public: 00028 //------------------------------------------------------------------------ 00029 //old: typedef const void* ObjId; // address of object on heap; id factory better? 00030 typedef const void* PathId; // address of object on heap; id factory better? 00031 typedef const void* TaskId; // address of object on heap; id factory better? 00032 //static const ObjId NOT_AN_ID; 00033 00034 00035 //------------------------------------------------------------------------ 00036 /// A terrain location 00037 struct Loc 00038 { 00039 sint4 x, y; 00040 00041 Loc(sint4 x_=0, sint4 y_=0) : x(x_), y(y_) {} 00042 00043 bool operator==(const Loc &other) const { 00044 return x == other.x && y == other.y; 00045 } 00046 00047 bool operator!=(const Loc &other) const { 00048 return x != other.x || y != other.y; 00049 } 00050 00051 bool operator<(const Loc &other) const { 00052 return x < other.x || (x == other.x && y < other.y); 00053 } 00054 00055 real8 distance(const Loc &other) const { 00056 const real8 dx = x - other.x; 00057 const real8 dy = y - other.y; 00058 return sqrt(dx*dx + dy*dy); 00059 } 00060 }; 00061 00062 //------------------------------------------------------------------------ 00063 /// A pair of locations, representing boundaries etc. 00064 struct Segment 00065 { 00066 Loc l1, l2; 00067 00068 Segment(sint4 x1=0, sint4 y1=0, sint4 x2=0, sint4 y2=0) : l1(x1,y1), l2(x2,y2) { }; 00069 Segment(const Loc &l1_, const Loc &l2_) : l1(l1_), l2(l2_) { }; 00070 00071 /** positive if l3 is on the left of the line from l1 to l2; 00072 negative if l3 is on the right; 00073 0 if l3 is on the line containing the segment. 00074 Adapted for the reversed y-axis used by ORTS. */ 00075 sint4 is_left_turn(const Loc &l3) const { return (l2.y-l1.y)*(l3.x-l1.x) - (l2.x-l1.x)*(l3.y-l1.y); }; 00076 00077 /// true if the segments weakly intersect 00078 bool touches(const Segment &o) const { 00079 return is_left_turn(o.l1)*is_left_turn(o.l2) <= 0 && 00080 o.is_left_turn(l1)*o.is_left_turn(l2) <= 0; 00081 }; 00082 00083 /// true if the segments strictly intersect 00084 bool intersects(const Segment &o) const { 00085 return is_left_turn(o.l1)*is_left_turn(o.l2) < 0 && 00086 o.is_left_turn(l1)*o.is_left_turn(l2) < 0; 00087 }; 00088 }; 00089 00090 00091 //------------------------------------------------------------------------ 00092 /// A path is a sequence of locations (waypoints) 00093 struct Path 00094 { 00095 PathId id; //< for identifying path information 00096 00097 Vector<Loc> locs; 00098 00099 Path() : id() { } 00100 Path(const Path &other) : id(other.id), locs(other.locs) {}; 00101 Path& operator=(const Path &other) { 00102 if( this != &other ) { 00103 id = other.id; 00104 locs = other.locs; 00105 } 00106 return *this; 00107 }; 00108 }; 00109 00110 00111 //------------------------------------------------------------------------ 00112 /// A movement goal 00113 struct Goal 00114 { 00115 /// target location or an object to reach? 00116 enum Target { LOCATION, OBJ } target; 00117 00118 /// reach vicinity, touch object, attack object? 00119 enum Mode { VICINITY, TOUCH, ATTACK } mode; 00120 00121 /// The goal location (if target==LOCATION) 00122 Loc loc; 00123 00124 /** The goal object (if target==OBJ). 00125 It's the implementation's responsibility to track remove_object() 00126 calls and handle cases of a goal object dying or vanishing in the 00127 FOW (and this pointer becoming invalid). */ 00128 const Object *obj; 00129 00130 /// The distance from the target that is 'close enough' (if mode==VICINITY) 00131 sint4 distance; 00132 00133 Goal(Target t, Mode m) : target(t), mode(m), obj(NULL), distance(0) {}; 00134 Goal(const Object *o, Mode m) : target(OBJ), mode(m), obj(o), distance(0) {}; 00135 Goal(const Loc &l, Mode m) : target(LOCATION), mode(m), loc(l), obj(NULL), distance(0) {}; 00136 00137 Goal(const Goal &other) 00138 : target(other.target), mode(other.mode), loc(other.loc), obj(other.obj), 00139 distance(other.distance) {}; 00140 // private: 00141 //Goal& operator=(const Goal &other); 00142 }; 00143 00144 /// A pathfinding task to be executed 00145 struct Task 00146 { 00147 /// The goal of the movement. 00148 Goal goal; 00149 00150 /// Units traveling at individual speeds or in formation? 00151 enum Group { INDIVIDUAL, FORMATION } group; 00152 00153 /** The objects to be moved. 00154 It's the implementation's responsibility to track remove_object() 00155 calls and handle cases of a objects dying or vanishing in the 00156 FOW (and pointers becoming invalid). */ 00157 std::set<const Object*> objs; 00158 00159 Task(Goal::Target t = Goal::LOCATION, Goal::Mode m = Goal::VICINITY, Group g = INDIVIDUAL) 00160 : goal(t,m), group(g) {}; 00161 Task(const Task &other) : goal(other.goal), group(other.group), objs(other.objs) {}; 00162 //private: 00163 //Task& operator=(const Task &other); 00164 }; 00165 00166 00167 //------------------------------------------------------------------------ 00168 /// Shortcut construction of a "move a single obj to a location target" Task 00169 struct Obj2LocTask : public TerrainBase::Task { 00170 Obj2LocTask(Object *obj, const Loc& goal_loc_) : Task(Goal::LOCATION, Goal::TOUCH, INDIVIDUAL) { 00171 goal.loc = goal_loc_; 00172 objs.insert(obj); 00173 }; 00174 }; 00175 00176 00177 //------------------------------------------------------------------------ 00178 /// Shortcut construction of a "move a single obj to an object target" Task 00179 struct Obj2ObjTask : public TerrainBase::Task { 00180 Obj2ObjTask(Object *obj, const Object *goal_obj_) : Task(Goal::OBJ, Goal::TOUCH, INDIVIDUAL) { 00181 goal.obj = goal_obj_; 00182 objs.insert(obj); 00183 }; 00184 }; 00185 00186 00187 //------------------------------------------------------------------------ 00188 /// A low-level move command to be executed 00189 struct MoveCmd 00190 { 00191 const Object *obj; // link 00192 sint4 speed; // 0 -> stop 00193 Loc next_loc; 00194 00195 MoveCmd() : obj(0), speed(0) { } 00196 }; 00197 00198 00199 //------------------------------------------------------------------------ 00200 /** A task status notification to be sent. A seperate msg is 00201 created for each object in multi-object tasks, since each 00202 can arrive/fail individually. */ 00203 struct StatusMsg 00204 { 00205 enum Type { ARRIVED, ///< object arrived at goal 00206 NO_PATH_FAILURE, ///< no path to goal could be computed 00207 MOVEMENT_FAILURE ///< failed to execute the planned task 00208 } type; 00209 00210 TaskId task_id; 00211 const Object *obj; // link 00212 00213 StatusMsg() : type(ARRIVED), task_id(), obj(0) { } 00214 }; 00215 00216 //------------------------------------------------------------------------ 00217 // TerrainBase interface itself 00218 00219 TerrainBase() 00220 { 00221 tiles_x = tiles_y = tile_points = 0; 00222 client_pID = neutral_pID = -1; 00223 00224 }; 00225 00226 TerrainBase(const TerrainBase &other) 00227 :tiles_x(other.tiles_x), tiles_y(other.tiles_y), 00228 tile_points(other.tile_points), client_pID(other.client_pID), 00229 neutral_pID(other.neutral_pID){}; 00230 00231 virtual ~TerrainBase(); 00232 00233 /// Set map dimensions 00234 /** set playfield dimensions and gives the pathfinder the IDs of the client 00235 and the neutral players 00236 tiles_x/y : dimensions in tiles 00237 tile_points : points on tile edge on fine grid 00238 */ 00239 virtual void init(sint4 tiles_x_, sint4 tiles_y_, sint4 tile_points_, sint4 client_pID_, sint4 neutral_pID_) 00240 { 00241 tiles_x = tiles_x_; 00242 tiles_y = tiles_y_; 00243 tile_points = tile_points_; 00244 client_pID = client_pID_; 00245 neutral_pID = neutral_pID_; 00246 }; 00247 00248 //--------------------------------------------------------------------- 00249 /// \name Notifications on world changes 00250 //--------------------------------------------------------------------- 00251 //@{ 00252 virtual void add_obj(const Object *obj) = 0; 00253 virtual void remove_obj(const Object *obj) = 0; 00254 virtual void update_obj(const Object *obj) = 0; 00255 virtual void add_segments(const Vector<Segment> &segs) = 0; 00256 //}@ 00257 //--------------------------------------------------------------------- 00258 00259 00260 //--------------------------------------------------------------------- 00261 /// \name Requests for an immediate planning of a path 00262 //--------------------------------------------------------------------- 00263 //@{ 00264 /// Flags that specify what objects to consider as obstacles. 00265 enum ConsiderObjects { 00266 CONSIDER_ALL = 0, 00267 /// Ignore all friendly (owned) mobile objects. 00268 IGNORE_MOBILE_FRIENDS = 0x001, 00269 /// Ignore all enemy mobile objects. 00270 IGNORE_MOBILE_FOES = 0x010, 00271 /// Ignore all neutral ("sheep") mobile objects. 00272 IGNORE_MOBILE_NEUTRALS = 0x100, 00273 /// Ignore all mobile objects, considering only boundaries and non-mobile objects. 00274 IGNORE_ALL_MOBILE_OBJS = IGNORE_MOBILE_FRIENDS | IGNORE_MOBILE_FOES | IGNORE_MOBILE_NEUTRALS, 00275 }; 00276 00277 /** Compute path between l1 and l2 for a circular object. 00278 @param l1 Start of requested path. 00279 @param l2 End of requested path. 00280 @param radius Radius (half width) of the requested path. 00281 @param path If != NULL, and a path was found, this will contain the it 00282 (including l2 but not l1), with the first waypoint in locs[0]. 00283 @param consider What objects to consider as obstacles when searching for a path. 00284 @return The length of the path (in tile_points); < 0 iff path doesn't exist. 00285 */ 00286 virtual real8 find_path(const Loc &l1, const Loc &l2, sint4 radius, Path *path, 00287 ConsiderObjects consider = CONSIDER_ALL) = 0; 00288 00289 /** Compute path for a given (circular only) Object with a given Goal, 00290 @param obj The object to move, including the start position and radius. 00291 @param goal Target of the requested path (location or object etc.). 00292 @param path If != NULL, and a path was found, this will contain it 00293 (including the final but not the start position), with the 00294 first waypoint in locs[0]. 00295 @param consider What objects to consider as obstacles when searching for a path. 00296 @return The length of the path (in tile_points); < 0 iff path doesn't exist. 00297 */ 00298 virtual real8 find_path(const Object *obj, const Goal &goal, Path *path, 00299 ConsiderObjects consider = CONSIDER_ALL) = 0; 00300 //--------------------------------------------------------------------- 00301 00302 00303 //--------------------------------------------------------------------- 00304 /** \name Initiate/end pathfinding and execution 00305 These methods are expected to return quickly. */ 00306 //--------------------------------------------------------------------- 00307 //@{ 00308 virtual TaskId add_task(const Task &task) = 0; 00309 virtual void remove_task(TaskId tid) = 0; 00310 virtual void cancel_task(const Object *obj) = 0; 00311 //@} 00312 //--------------------------------------------------------------------- 00313 00314 00315 //--------------------------------------------------------------------- 00316 /// \name "Do some work" methods 00317 //--------------------------------------------------------------------- 00318 //@{ 00319 /** \brief Monitor path execution. Should return move commands and status 00320 messages, and is expected to return quickly. */ 00321 virtual void execute_tasks(Vector<MoveCmd> &cmds, Vector<StatusMsg> &msgs) = 0; 00322 /** Plan the paths you're handling. This is where long thinking should be done. 00323 Return true iff did some lengthy processing. 00324 Try to use only the specified amount of time (milliseconds) for planning before returning 00325 control to the caller. If max_time == 0 then there is no requested time limit. */ 00326 virtual bool plan_tasks(uint4 max_time = 0) = 0; 00327 //@} 00328 //--------------------------------------------------------------------- 00329 00330 protected: 00331 // data 00332 sint4 tiles_x, ///< playfield width 00333 tiles_y; ///< playfield height 00334 sint4 tile_points; ///< points on tile edge on fine grid 00335 sint4 client_pID, ///< ID of the client player 00336 neutral_pID; ///< ID of neutral player (eg. sheep) 00337 }; 00338 00339 extern std::ostream& operator<<(std::ostream& os, const TerrainBase::Loc& l); 00340 extern std::ostream& operator<<(std::ostream& os, const TerrainBase::Segment& s); 00341 00342 REGISTER_TYPEOF(2401, Vector<TerrainBase::Segment>::iterator); 00343 REGISTER_TYPEOF(2402, Vector<TerrainBase::Segment>::const_iterator); 00344 00345 REGISTER_TYPEOF(2403, Vector<TerrainBase::Loc>::iterator); 00346 REGISTER_TYPEOF(2404, Vector<TerrainBase::Loc>::const_iterator); 00347 00348 REGISTER_TYPEOF(2405, Vector<TerrainBase::StatusMsg>::iterator); 00349 REGISTER_TYPEOF(2406, Vector<TerrainBase::StatusMsg>::const_iterator); 00350 00351 REGISTER_TYPEOF(2407, Vector<TerrainBase::MoveCmd>::iterator); 00352 REGISTER_TYPEOF(2408, Vector<TerrainBase::MoveCmd>::const_iterator); 00353 00354 REGISTER_TYPEOF(2409, Vector<TerrainBase::Loc>::reverse_iterator); 00355 REGISTER_TYPEOF(2410, Vector<TerrainBase::Loc>::const_reverse_iterator); 00356 00357 REGISTER_TYPEOF(2411, Vector<TerrainBase::Path>::iterator); 00358 REGISTER_TYPEOF(2412, Vector<TerrainBase::Path>::const_iterator); 00359 00360 #if 0 00361 // later 00362 00363 struct Tile 00364 { 00365 enum Split { 00366 NO, 00367 TOPBOTTOM, // |\| 00368 BOTTOMTOP, // |/| 00369 } type; 00370 sint4 tx, ty; // tile position 00371 sint4 type_w, type_e; // triangle types: west, east 00372 }; 00373 00374 virtual void remove_segment(const Segment &s) = 0; 00375 virtual void new_tile(const Tile &tile) = 0; 00376 00377 // path finding 00378 virtual real8 path_len(const Loc &start, const Loc &goal, sint4 radius) = 0; 00379 virtual void find_path(const Loc &start, const Loc &goal, sint4 radius, 00380 Path &path) = 0; 00381 #endif 00382 00383 #endif