1 //------------------------------------------------------------------------ 2 // BASIC OBJECT HANDLING 3 //------------------------------------------------------------------------ 4 // 5 // Eureka DOOM Editor 6 // 7 // Copyright (C) 2001-2019 Andrew Apted 8 // Copyright (C) 1997-2003 André Majorel et al 9 // 10 // This program is free software; you can redistribute it and/or 11 // modify it under the terms of the GNU General Public License 12 // as published by the Free Software Foundation; either version 2 13 // of the License, or (at your option) any later version. 14 // 15 // This program is distributed in the hope that it will be useful, 16 // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 //------------------------------------------------------------------------ 21 // 22 // Based on Yadex which incorporated code from DEU 5.21 that was put 23 // in the public domain in 1994 by Raphaël Quinet and Brendon Wyber. 24 // 25 //------------------------------------------------------------------------ 26 27 #ifndef __EUREKA_E_BASIS_H__ 28 #define __EUREKA_E_BASIS_H__ 29 30 class crc32_c; 31 32 33 // 34 // DESIGN NOTES 35 // 36 // Every field in these structures are a plain 'int'. This is a 37 // design decision aiming to simplify the logic and code for undo 38 // and redo. 39 // 40 // Strings are represented as offsets into a string table, where 41 // fetching the actual (read-only) string is fast, but adding new 42 // strings is slow (with the current code). 43 // 44 // These structures are always ensured to have valid fields, e.g. 45 // the LineDef vertex numbers are OK, the SideDef sector number is 46 // valid, etc. For LineDefs, the left and right fields can contain 47 // -1 to mean "no sidedef", but note that a missing right sidedef 48 // can cause problems or crashes when playing the map in DOOM. 49 // 50 51 52 // a fixed-point coordinate with 12 bits of fractional part. 53 typedef int fixcoord_t; 54 55 #define FROM_COORD(fx) ((double)(fx) / 4096.0) 56 #define TO_COORD(db) ((fixcoord_t) I_ROUND((db) * 4096.0)) 57 58 #define INT_TO_COORD(i) ((fixcoord_t) ((i) << 12)) 59 #define COORD_TO_INT(i) ((int) ((i) / 4096)) 60 61 fixcoord_t MakeValidCoord(double x); 62 63 64 typedef enum 65 { 66 SIDE_RIGHT = +1, 67 SIDE_LEFT = -1 68 } 69 side_ref_e; 70 71 72 // See objid.h for obj_type_e (OBJ_THINGS etc) 73 74 75 class Thing 76 { 77 public: 78 fixcoord_t raw_x; 79 fixcoord_t raw_y; 80 81 int angle; 82 int type; 83 int options; 84 85 // Hexen stuff 86 fixcoord_t raw_h; 87 88 int tid; 89 int special; 90 int arg1, arg2, arg3, arg4, arg5; 91 92 enum { F_X, F_Y, F_ANGLE, F_TYPE, F_OPTIONS, 93 F_H, F_TID, F_SPECIAL, 94 F_ARG1, F_ARG2, F_ARG3, F_ARG4, F_ARG5 }; 95 96 public: Thing()97 Thing() : raw_x(0), raw_y(0), angle(0), type(0), options(0), 98 raw_h(0), tid(0), special(0), 99 arg1(0), arg2(0), arg3(0), arg4(0), arg5(0) 100 { } 101 x()102 inline double x() const 103 { 104 return FROM_COORD(raw_x); 105 } y()106 inline double y() const 107 { 108 return FROM_COORD(raw_y); 109 } h()110 inline double h() const 111 { 112 return FROM_COORD(raw_h); 113 } 114 115 // these handle rounding to integer in non-UDMF mode SetRawX(double x)116 void SetRawX(double x) { raw_x = MakeValidCoord(x); } SetRawY(double y)117 void SetRawY(double y) { raw_y = MakeValidCoord(y); } SetRawH(double h)118 void SetRawH(double h) { raw_h = MakeValidCoord(h); } 119 SetRawXY(double x,double y)120 void SetRawXY(double x, double y) 121 { 122 SetRawX(x); 123 SetRawY(y); 124 } 125 RawCopy(const Thing * other)126 void RawCopy(const Thing *other) 127 { 128 raw_x = other->raw_x; 129 raw_y = other->raw_y; 130 raw_h = other->raw_h; 131 132 angle = other->angle; 133 type = other->type; 134 options = other->options; 135 tid = other->tid; 136 special = other->special; 137 138 arg1 = other->arg1; 139 arg2 = other->arg2; 140 arg3 = other->arg3; 141 arg4 = other->arg4; 142 arg5 = other->arg5; 143 } 144 Arg(int which)145 int Arg(int which /* 1..5 */) const 146 { 147 if (which == 1) return arg1; 148 if (which == 2) return arg2; 149 if (which == 3) return arg3; 150 if (which == 4) return arg4; 151 if (which == 5) return arg5; 152 153 return 0; 154 } 155 }; 156 157 158 class Vertex 159 { 160 public: 161 fixcoord_t raw_x; 162 fixcoord_t raw_y; 163 164 enum { F_X, F_Y }; 165 166 public: Vertex()167 Vertex() : raw_x(0), raw_y(0) 168 { } 169 x()170 inline double x() const 171 { 172 return FROM_COORD(raw_x); 173 } y()174 inline double y() const 175 { 176 return FROM_COORD(raw_y); 177 } 178 179 // these handle rounding to integer in non-UDMF mode SetRawX(double x)180 void SetRawX(double x) { raw_x = MakeValidCoord(x); } SetRawY(double y)181 void SetRawY(double y) { raw_y = MakeValidCoord(y); } 182 SetRawXY(double x,double y)183 void SetRawXY(double x, double y) 184 { 185 SetRawX(x); 186 SetRawY(y); 187 } 188 RawCopy(const Vertex * other)189 void RawCopy(const Vertex *other) 190 { 191 raw_x = other->raw_x; 192 raw_y = other->raw_y; 193 } 194 Matches(fixcoord_t ox,fixcoord_t oy)195 bool Matches(fixcoord_t ox, fixcoord_t oy) const 196 { 197 return (raw_x == ox) && (raw_y == oy); 198 } 199 Matches(const Vertex * other)200 bool Matches(const Vertex *other) const 201 { 202 return (raw_x == other->raw_x) && (raw_y == other->raw_y); 203 } 204 }; 205 206 207 class Sector 208 { 209 public: 210 int floorh; 211 int ceilh; 212 int floor_tex; 213 int ceil_tex; 214 int light; 215 int type; 216 int tag; 217 218 enum { F_FLOORH, F_CEILH, F_FLOOR_TEX, F_CEIL_TEX, F_LIGHT, F_TYPE, F_TAG }; 219 220 public: Sector()221 Sector() : floorh(0), ceilh(0), floor_tex(0), ceil_tex(0), 222 light(0), type(0), tag(0) 223 { } 224 RawCopy(const Sector * other)225 void RawCopy(const Sector *other) 226 { 227 floorh = other->floorh; 228 ceilh = other->ceilh; 229 floor_tex = other->floor_tex; 230 ceil_tex = other->ceil_tex; 231 light = other->light; 232 type = other->type; 233 tag = other->tag; 234 } 235 236 const char *FloorTex() const; 237 const char *CeilTex() const; 238 HeadRoom()239 int HeadRoom() const 240 { 241 return ceilh - floorh; 242 } 243 244 void SetDefaults(); 245 }; 246 247 248 class SideDef 249 { 250 public: 251 int x_offset; 252 int y_offset; 253 int upper_tex; 254 int mid_tex; 255 int lower_tex; 256 int sector; 257 258 enum { F_X_OFFSET, F_Y_OFFSET, F_UPPER_TEX, F_MID_TEX, F_LOWER_TEX, F_SECTOR }; 259 260 public: SideDef()261 SideDef() : x_offset(0), y_offset(0), upper_tex(0), mid_tex(0), 262 lower_tex(0), sector(0) 263 { } 264 RawCopy(const SideDef * other)265 void RawCopy(const SideDef *other) 266 { 267 x_offset = other->x_offset; 268 y_offset = other->y_offset; 269 upper_tex = other->upper_tex; 270 mid_tex = other->mid_tex; 271 lower_tex = other->lower_tex; 272 sector = other->sector; 273 } 274 275 const char *UpperTex() const; 276 const char *MidTex() const; 277 const char *LowerTex() const; 278 279 Sector *SecRef() const; 280 281 // use new_tex when >= 0, otherwise use default_wall_tex 282 void SetDefaults(bool two_sided, int new_tex = -1); 283 }; 284 285 286 class LineDef 287 { 288 public: 289 int start; 290 int end; 291 int right; 292 int left; 293 294 int flags; 295 int type; 296 int tag; 297 298 // Hexen stuff [NOTE: tag is 'arg1'] 299 int arg2; 300 int arg3; 301 int arg4; 302 int arg5; 303 304 enum { F_START, F_END, F_RIGHT, F_LEFT, 305 F_FLAGS, F_TYPE, F_TAG, 306 F_ARG2, F_ARG3, F_ARG4, F_ARG5 }; 307 308 public: LineDef()309 LineDef() : start(0), end(0), right(-1), left(-1), 310 flags(0), type(0), tag(0), 311 arg2(0), arg3(0), arg4(0), arg5(0) 312 { } 313 RawCopy(const LineDef * other)314 void RawCopy(const LineDef *other) 315 { 316 start = other->start; 317 end = other->end; 318 right = other->right; 319 left = other->left; 320 flags = other->flags; 321 322 type = other->type; 323 tag = other->tag; // arg1 324 arg2 = other->arg2; 325 arg3 = other->arg3; 326 arg4 = other->arg4; 327 arg5 = other->arg5; 328 } 329 330 Vertex *Start() const; 331 Vertex *End() const; 332 333 // remember: these two can return NULL! 334 SideDef *Right() const; 335 SideDef *Left() const; 336 TouchesVertex(int v_num)337 bool TouchesVertex(int v_num) const 338 { 339 return (start == v_num) || (end == v_num); 340 } 341 TouchesCoord(fixcoord_t tx,fixcoord_t ty)342 bool TouchesCoord(fixcoord_t tx, fixcoord_t ty) const 343 { 344 return Start()->Matches(tx, ty) || End()->Matches(tx, ty); 345 } 346 347 bool TouchesSector(int sec_num) const; 348 NoSided()349 bool NoSided() const 350 { 351 return (right < 0) && (left < 0); 352 } 353 OneSided()354 bool OneSided() const 355 { 356 return (right >= 0) && (left < 0); 357 } 358 TwoSided()359 bool TwoSided() const 360 { 361 return (right >= 0) && (left >= 0); 362 } 363 364 // side is either SIDE_LEFT or SIDE_RIGHT 365 int WhatSector(int side) const; 366 int WhatSideDef(int side) const; 367 368 double CalcLength() const; 369 IsZeroLength()370 bool IsZeroLength() const 371 { 372 return (Start()->raw_x == End()->raw_x) && (Start()->raw_y == End()->raw_y); 373 } 374 375 bool IsSelfRef() const; 376 IsHorizontal()377 bool IsHorizontal() const 378 { 379 return (Start()->raw_y == End()->raw_y); 380 } 381 IsVertical()382 bool IsVertical() const 383 { 384 return (Start()->raw_x == End()->raw_x); 385 } 386 Arg(int which)387 int Arg(int which /* 1..5 */) const 388 { 389 if (which == 1) return tag; 390 if (which == 2) return arg2; 391 if (which == 3) return arg3; 392 if (which == 4) return arg4; 393 if (which == 5) return arg5; 394 395 return 0; 396 } 397 }; 398 399 400 extern std::vector<Thing *> Things; 401 extern std::vector<Vertex *> Vertices; 402 extern std::vector<Sector *> Sectors; 403 extern std::vector<SideDef *> SideDefs; 404 extern std::vector<LineDef *> LineDefs; 405 406 extern std::vector<byte> HeaderData; 407 extern std::vector<byte> BehaviorData; 408 extern std::vector<byte> ScriptsData; 409 410 411 #define NumThings ((int)Things.size()) 412 #define NumVertices ((int)Vertices.size()) 413 #define NumSectors ((int)Sectors.size()) 414 #define NumSideDefs ((int)SideDefs.size()) 415 #define NumLineDefs ((int)LineDefs.size()) 416 417 int NumObjects(obj_type_e type); 418 419 #define is_thing(n) ((n) >= 0 && (n) < NumThings ) 420 #define is_vertex(n) ((n) >= 0 && (n) < NumVertices) 421 #define is_sector(n) ((n) >= 0 && (n) < NumSectors ) 422 #define is_sidedef(n) ((n) >= 0 && (n) < NumSideDefs) 423 #define is_linedef(n) ((n) >= 0 && (n) < NumLineDefs) 424 425 const char * NameForObjectType(obj_type_e type, bool plural = false); 426 427 428 /* BASIS API */ 429 430 // begin a group of operations that will become a single undo/redo 431 // step. Any stored _redo_ steps will be forgotten. The BA_New, 432 // BA_Delete, BA_Change and BA_Message functions must only be called 433 // between BA_Begin() and BA_End() pairs. 434 void BA_Begin(); 435 436 // finish a group of operations. 437 void BA_End(); 438 439 // abort the group of operations -- the undo/redo history is not 440 // modified and any changes since BA_Begin() are undone except 441 // when 'keep_changes' is true. 442 void BA_Abort(bool keep_changes = false); 443 444 // assign a message to the current operation. 445 // this can be called multiple times. 446 void BA_Message(const char *msg = NULL, ...); 447 448 void BA_MessageForSel(const char *verb, selection_c *list, const char *suffix = ""); 449 450 // create a new object, returning its objnum. It is safe to 451 // directly set the new object's fields after calling BA_New(). 452 int BA_New(obj_type_e type); 453 454 // deletes the given object, and in certain cases other types of 455 // objects bound to it (e.g. deleting a vertex will cause all 456 // bound linedefs to also be deleted). 457 void BA_Delete(obj_type_e type, int objnum); 458 459 // change a field of an existing object. If the value was the 460 // same as before, nothing happens and false is returned. 461 // Otherwise returns true. 462 bool BA_Change(obj_type_e type, int objnum, byte field, int value); 463 464 // attempt to undo the last normal or redo operation. Returns 465 // false if the undo history is empty. 466 bool BA_Undo(); 467 468 // attempt to re-do the last undo operation. Returns false if 469 // there is no stored redo steps. 470 bool BA_Redo(); 471 472 // add this string to the basis string table (if it doesn't 473 // already exist) and return its integer offset. 474 int BA_InternaliseString(const char *str); 475 int BA_InternaliseShortStr(const char *str, int max_len); 476 477 // get the string from the basis string table. 478 const char * BA_GetString(int offset); 479 480 // clear everything (before loading a new level). 481 void BA_ClearAll(); 482 483 // compute a checksum for the current level 484 void BA_LevelChecksum(crc32_c& crc); 485 486 487 /* HELPERS */ 488 489 bool BA_ChangeTH(int thing, byte field, int value); 490 bool BA_ChangeVT(int vert, byte field, int value); 491 bool BA_ChangeSEC(int sec, byte field, int value); 492 bool BA_ChangeSD(int side, byte field, int value); 493 bool BA_ChangeLD(int line, byte field, int value); 494 bool BA_ChangeRAD(int rad, byte field, int value); 495 496 497 #endif /* __EUREKA_E_BASIS_H__ */ 498 499 //--- editor settings --- 500 // vi:ts=4:sw=4:noexpandtab 501