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