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 #include "main.h"
28 
29 #include <algorithm>
30 #include <list>
31 #include <string>
32 
33 #include "lib_adler.h"
34 
35 #include "e_basis.h"
36 #include "e_main.h"
37 #include "m_strings.h"
38 
39 #include "m_game.h"  // g_default_xxx
40 
41 // need these for the XXX_Notify() prototypes
42 #include "e_cutpaste.h"
43 #include "e_objects.h"
44 #include "r_render.h"
45 
46 
47 std::vector<Thing *>   Things;
48 std::vector<Vertex *>  Vertices;
49 std::vector<Sector *>  Sectors;
50 std::vector<SideDef *> SideDefs;
51 std::vector<LineDef *> LineDefs;
52 
53 std::vector<byte>  HeaderData;
54 std::vector<byte>  BehaviorData;
55 std::vector<byte>  ScriptsData;
56 
57 
58 int default_floor_h		=   0;
59 int default_ceil_h		= 128;
60 int default_light_level	= 176;
61 int default_thing		= 2001;
62 
63 const char * default_wall_tex	= "GRAY1";
64 const char * default_floor_tex	= "FLAT1";
65 const char * default_ceil_tex	= "FLAT1";
66 
67 
68 static bool did_make_changes;
69 
70 
71 string_table_c basis_strtab;
72 
73 
NumObjects(obj_type_e type)74 int NumObjects(obj_type_e type)
75 {
76 	switch (type)
77 	{
78 		case OBJ_THINGS:   return NumThings;
79 		case OBJ_LINEDEFS: return NumLineDefs;
80 		case OBJ_SIDEDEFS: return NumSideDefs;
81 		case OBJ_VERTICES: return NumVertices;
82 		case OBJ_SECTORS:  return NumSectors;
83 
84 		default:
85 			BugError("NumObjects: bad type: %d\n", (int)type);
86 			return 0; /* NOT REACHED */
87 	}
88 }
89 
90 
NameForObjectType(obj_type_e type,bool plural)91 const char * NameForObjectType(obj_type_e type, bool plural)
92 {
93 	switch (type)
94 	{
95 		case OBJ_THINGS:   return plural ? "things"   : "thing";
96 		case OBJ_LINEDEFS: return plural ? "linedefs" : "linedef";
97 		case OBJ_SIDEDEFS: return plural ? "sidedefs" : "sidedef";
98 		case OBJ_VERTICES: return plural ? "vertices" : "vertex";
99 		case OBJ_SECTORS:  return plural ? "sectors"  : "sector";
100 
101 		default:
102 			BugError("NameForObjectType: bad type: %d\n", (int)type);
103 			return "XXX"; /* NOT REACHED */
104 	}
105 }
106 
107 
DoClearChangeStatus()108 static void DoClearChangeStatus()
109 {
110 	did_make_changes = false;
111 
112 	Clipboard_NotifyBegin();
113 	Selection_NotifyBegin();
114 	 MapStuff_NotifyBegin();
115 	 Render3D_NotifyBegin();
116 	ObjectBox_NotifyBegin();
117 }
118 
DoProcessChangeStatus()119 static void DoProcessChangeStatus()
120 {
121 	if (did_make_changes)
122 	{
123 		MadeChanges = 1;
124 		RedrawMap();
125 	}
126 
127 	Clipboard_NotifyEnd();
128 	Selection_NotifyEnd();
129 	 MapStuff_NotifyEnd();
130 	 Render3D_NotifyEnd();
131 	ObjectBox_NotifyEnd();
132 }
133 
134 
BA_InternaliseString(const char * str)135 int BA_InternaliseString(const char *str)
136 {
137 	return basis_strtab.add(str);
138 }
139 
BA_InternaliseShortStr(const char * str,int max_len)140 int BA_InternaliseShortStr(const char *str, int max_len)
141 {
142 	std::string goodie(str, max_len);
143 
144 	int result = BA_InternaliseString(goodie.c_str());
145 
146 	return result;
147 }
148 
BA_GetString(int offset)149 const char *BA_GetString(int offset)
150 {
151 	return basis_strtab.get(offset);
152 }
153 
154 
MakeValidCoord(double x)155 fixcoord_t MakeValidCoord(double x)
156 {
157 	if (Level_format == MAPF_UDMF)
158 		return TO_COORD(x);
159 
160 	// in standard format, coordinates must be integral
161 	return TO_COORD(I_ROUND(x));
162 }
163 
164 
FloorTex() const165 const char * Sector::FloorTex() const
166 {
167 	return basis_strtab.get(floor_tex);
168 }
169 
CeilTex() const170 const char * Sector::CeilTex() const
171 {
172 	return basis_strtab.get(ceil_tex);
173 }
174 
SetDefaults()175 void Sector::SetDefaults()
176 {
177 	floorh = default_floor_h;
178 	 ceilh = default_ceil_h;
179 
180 	floor_tex = BA_InternaliseString(default_floor_tex);
181 	 ceil_tex = BA_InternaliseString(default_ceil_tex);
182 
183 	light = default_light_level;
184 }
185 
186 
UpperTex() const187 const char * SideDef::UpperTex() const
188 {
189 	return basis_strtab.get(upper_tex);
190 }
191 
MidTex() const192 const char * SideDef::MidTex() const
193 {
194 	return basis_strtab.get(mid_tex);
195 }
196 
LowerTex() const197 const char * SideDef::LowerTex() const
198 {
199 	return basis_strtab.get(lower_tex);
200 }
201 
SetDefaults(bool two_sided,int new_tex)202 void SideDef::SetDefaults(bool two_sided, int new_tex)
203 {
204 	if (new_tex < 0)
205 		new_tex = BA_InternaliseString(default_wall_tex);
206 
207 	lower_tex = new_tex;
208 	upper_tex = new_tex;
209 
210 	if (two_sided)
211 		mid_tex = BA_InternaliseString("-");
212 	else
213 		mid_tex = new_tex;
214 }
215 
216 
SecRef() const217 Sector * SideDef::SecRef() const
218 {
219 	return Sectors[sector];
220 }
221 
Start() const222 Vertex * LineDef::Start() const
223 {
224 	return Vertices[start];
225 }
226 
End() const227 Vertex * LineDef::End() const
228 {
229 	return Vertices[end];
230 }
231 
Right() const232 SideDef * LineDef::Right() const
233 {
234 	return (right >= 0) ? SideDefs[right] : NULL;
235 }
236 
Left() const237 SideDef * LineDef::Left() const
238 {
239 	return (left >= 0) ? SideDefs[left] : NULL;
240 }
241 
242 
TouchesSector(int sec_num) const243 bool LineDef::TouchesSector(int sec_num) const
244 {
245 	if (right >= 0 && SideDefs[right]->sector == sec_num)
246 		return true;
247 
248 	if (left >= 0 && SideDefs[left]->sector == sec_num)
249 		return true;
250 
251 	return false;
252 }
253 
254 
WhatSector(int side) const255 int LineDef::WhatSector(int side) const
256 {
257 	switch (side)
258 	{
259 		case SIDE_LEFT:
260 			return Left() ? Left()->sector : -1;
261 
262 		case SIDE_RIGHT:
263 			return Right() ? Right()->sector : -1;
264 
265 		default:
266 			BugError("bad side : %d\n", side);
267 			return -1;
268 	}
269 }
270 
271 
WhatSideDef(int side) const272 int LineDef::WhatSideDef(int side) const
273 {
274 	switch (side)
275 	{
276 		case SIDE_LEFT:  return left;
277 		case SIDE_RIGHT: return right;
278 
279 		default:
280 			BugError("bad side : %d\n", side);
281 			return -1;
282 	}
283 }
284 
285 
IsSelfRef() const286 bool LineDef::IsSelfRef() const
287 {
288 	return (left >= 0) && (right >= 0) &&
289 			SideDefs[left]->sector == SideDefs[right]->sector;
290 }
291 
292 
CalcLength() const293 double LineDef::CalcLength() const
294 {
295 	double dx = Start()->x() - End()->x();
296 	double dy = Start()->y() - End()->y();
297 
298 	return hypot(dx, dy);
299 }
300 
301 
302 //------------------------------------------------------------------------
303 
304 
RawInsertThing(int objnum,int * ptr)305 static void RawInsertThing(int objnum, int *ptr)
306 {
307 	SYS_ASSERT(0 <= objnum && objnum <= NumThings);
308 
309 	Things.push_back(NULL);
310 
311 	for (int n = NumThings-1 ; n > objnum ; n--)
312 		Things[n] = Things[n - 1];
313 
314 	Things[objnum] = (Thing*) ptr;
315 }
316 
RawInsertLineDef(int objnum,int * ptr)317 static void RawInsertLineDef(int objnum, int *ptr)
318 {
319 	SYS_ASSERT(0 <= objnum && objnum <= NumLineDefs);
320 
321 	LineDefs.push_back(NULL);
322 
323 	for (int n = NumLineDefs-1 ; n > objnum ; n--)
324 		LineDefs[n] = LineDefs[n - 1];
325 
326 	LineDefs[objnum] = (LineDef*) ptr;
327 }
328 
RawInsertVertex(int objnum,int * ptr)329 static void RawInsertVertex(int objnum, int *ptr)
330 {
331 	SYS_ASSERT(0 <= objnum && objnum <= NumVertices);
332 
333 	Vertices.push_back(NULL);
334 
335 	for (int n = NumVertices-1 ; n > objnum ; n--)
336 		Vertices[n] = Vertices[n - 1];
337 
338 	Vertices[objnum] = (Vertex*) ptr;
339 
340 	// fix references in linedefs
341 
342 	if (objnum+1 < NumVertices)
343 	{
344 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
345 		{
346 			LineDef *L = LineDefs[n];
347 
348 			if (L->start >= objnum)
349 				L->start++;
350 
351 			if (L->end >= objnum)
352 				L->end++;
353 		}
354 	}
355 }
356 
RawInsertSideDef(int objnum,int * ptr)357 static void RawInsertSideDef(int objnum, int *ptr)
358 {
359 	SYS_ASSERT(0 <= objnum && objnum <= NumSideDefs);
360 
361 	SideDefs.push_back(NULL);
362 
363 	for (int n = NumSideDefs-1 ; n > objnum ; n--)
364 		SideDefs[n] = SideDefs[n - 1];
365 
366 	SideDefs[objnum] = (SideDef*) ptr;
367 
368 	// fix the linedefs references
369 
370 	if (objnum+1 < NumSideDefs)
371 	{
372 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
373 		{
374 			LineDef *L = LineDefs[n];
375 
376 			if (L->right >= objnum)
377 				L->right++;
378 
379 			if (L->left >= objnum)
380 				L->left++;
381 		}
382 	}
383 }
384 
RawInsertSector(int objnum,int * ptr)385 static void RawInsertSector(int objnum, int *ptr)
386 {
387 	SYS_ASSERT(0 <= objnum && objnum <= NumSectors);
388 
389 	Sectors.push_back(NULL);
390 
391 	for (int n = NumSectors-1 ; n > objnum ; n--)
392 		Sectors[n] = Sectors[n - 1];
393 
394 	Sectors[objnum] = (Sector*) ptr;
395 
396 	// fix all sidedef references
397 
398 	if (objnum+1 < NumSectors)
399 	{
400 		for (int n = NumSideDefs-1 ; n >= 0 ; n--)
401 		{
402 			SideDef *S = SideDefs[n];
403 
404 			if (S->sector >= objnum)
405 				S->sector++;
406 		}
407 	}
408 }
409 
RawDeleteThing(int objnum)410 static int * RawDeleteThing(int objnum)
411 {
412 	SYS_ASSERT(0 <= objnum && objnum < NumThings);
413 
414 	int * result = (int*) Things[objnum];
415 
416 	for (int n = objnum ; n < NumThings-1 ; n++)
417 		Things[n] = Things[n + 1];
418 
419 	Things.pop_back();
420 
421 	return result;
422 }
423 
RawInsert(obj_type_e objtype,int objnum,int * ptr)424 static void RawInsert(obj_type_e objtype, int objnum, int *ptr)
425 {
426 	did_make_changes = true;
427 
428 	Clipboard_NotifyInsert(objtype, objnum);
429 	Selection_NotifyInsert(objtype, objnum);
430 	 MapStuff_NotifyInsert(objtype, objnum);
431 	 Render3D_NotifyInsert(objtype, objnum);
432 	ObjectBox_NotifyInsert(objtype, objnum);
433 
434 	switch (objtype)
435 	{
436 		case OBJ_THINGS:
437 			RawInsertThing(objnum, ptr);
438 			break;
439 
440 		case OBJ_VERTICES:
441 			RawInsertVertex(objnum, ptr);
442 			break;
443 
444 		case OBJ_SIDEDEFS:
445 			RawInsertSideDef(objnum, ptr);
446 			break;
447 
448 		case OBJ_SECTORS:
449 			RawInsertSector(objnum, ptr);
450 			break;
451 
452 		case OBJ_LINEDEFS:
453 			RawInsertLineDef(objnum, ptr);
454 			break;
455 
456 		default:
457 			BugError("RawInsert: bad objtype %d\n", (int)objtype);
458 	}
459 }
460 
461 
RawDeleteLineDef(int objnum)462 static int * RawDeleteLineDef(int objnum)
463 {
464 	SYS_ASSERT(0 <= objnum && objnum < NumLineDefs);
465 
466 	int * result = (int*) LineDefs[objnum];
467 
468 	for (int n = objnum ; n < NumLineDefs-1 ; n++)
469 		LineDefs[n] = LineDefs[n + 1];
470 
471 	LineDefs.pop_back();
472 
473 	return result;
474 }
475 
RawDeleteVertex(int objnum)476 static int * RawDeleteVertex(int objnum)
477 {
478 	SYS_ASSERT(0 <= objnum && objnum < NumVertices);
479 
480 	int * result = (int*) Vertices[objnum];
481 
482 	for (int n = objnum ; n < NumVertices-1 ; n++)
483 		Vertices[n] = Vertices[n + 1];
484 
485 	Vertices.pop_back();
486 
487 	// fix the linedef references
488 
489 	if (objnum < NumVertices)
490 	{
491 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
492 		{
493 			LineDef *L = LineDefs[n];
494 
495 			if (L->start > objnum)
496 				L->start--;
497 
498 			if (L->end > objnum)
499 				L->end--;
500 		}
501 	}
502 
503 	return result;
504 }
505 
RawDeleteSideDef(int objnum)506 static int * RawDeleteSideDef(int objnum)
507 {
508 	SYS_ASSERT(0 <= objnum && objnum < NumSideDefs);
509 
510 	int * result = (int*) SideDefs[objnum];
511 
512 	for (int n = objnum ; n < NumSideDefs-1 ; n++)
513 		SideDefs[n] = SideDefs[n + 1];
514 
515 	SideDefs.pop_back();
516 
517 	// fix the linedefs references
518 
519 	if (objnum < NumSideDefs)
520 	{
521 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
522 		{
523 			LineDef *L = LineDefs[n];
524 
525 			if (L->right > objnum)
526 				L->right--;
527 
528 			if (L->left > objnum)
529 				L->left--;
530 		}
531 	}
532 
533 	return result;
534 }
535 
RawDeleteSector(int objnum)536 static int * RawDeleteSector(int objnum)
537 {
538 	SYS_ASSERT(0 <= objnum && objnum < NumSectors);
539 
540 	int * result = (int*) Sectors[objnum];
541 
542 	for (int n = objnum ; n < NumSectors-1 ; n++)
543 		Sectors[n] = Sectors[n + 1];
544 
545 	Sectors.pop_back();
546 
547 	// fix sidedef references
548 
549 	if (objnum < NumSectors)
550 	{
551 		for (int n = NumSideDefs-1 ; n >= 0 ; n--)
552 		{
553 			SideDef *S = SideDefs[n];
554 
555 			if (S->sector > objnum)
556 				S->sector--;
557 		}
558 	}
559 
560 	return result;
561 }
562 
RawDelete(obj_type_e objtype,int objnum)563 static int * RawDelete(obj_type_e objtype, int objnum)
564 {
565 	did_make_changes = true;
566 
567 	Clipboard_NotifyDelete(objtype, objnum);
568 	Selection_NotifyDelete(objtype, objnum);
569 	 MapStuff_NotifyDelete(objtype, objnum);
570 	 Render3D_NotifyDelete(objtype, objnum);
571 	ObjectBox_NotifyDelete(objtype, objnum);
572 
573 	switch (objtype)
574 	{
575 		case OBJ_THINGS:
576 			return RawDeleteThing(objnum);
577 
578 		case OBJ_VERTICES:
579 			return RawDeleteVertex(objnum);
580 
581 		case OBJ_SECTORS:
582 			return RawDeleteSector(objnum);
583 
584 		case OBJ_SIDEDEFS:
585 			return RawDeleteSideDef(objnum);
586 
587 		case OBJ_LINEDEFS:
588 			return RawDeleteLineDef(objnum);
589 
590 		default:
591 			BugError("RawDelete: bad objtype %d\n", (int)objtype);
592 			return NULL; /* NOT REACHED */
593 	}
594 }
595 
596 
DeleteFinally(obj_type_e objtype,int * ptr)597 static void DeleteFinally(obj_type_e objtype, int *ptr)
598 {
599 // fprintf(stderr, "DeleteFinally: %p\n", ptr);
600 	switch (objtype)
601 	{
602 		case OBJ_THINGS:   delete (Thing *)   ptr; break;
603 		case OBJ_VERTICES: delete (Vertex *)  ptr; break;
604 		case OBJ_SECTORS:  delete (Sector *)  ptr; break;
605 		case OBJ_SIDEDEFS: delete (SideDef *) ptr; break;
606 		case OBJ_LINEDEFS: delete (LineDef *) ptr; break;
607 
608 		default:
609 			BugError("DeleteFinally: bad objtype %d\n", (int)objtype);
610 	}
611 }
612 
613 
RawChange(obj_type_e objtype,int objnum,int field,int * value)614 static void RawChange(obj_type_e objtype, int objnum, int field, int *value)
615 {
616 	int * pos = NULL;
617 
618 	switch (objtype)
619 	{
620 		case OBJ_THINGS:
621 			SYS_ASSERT(0 <= objnum && objnum < NumThings);
622 			pos = (int*) Things[objnum];
623 			break;
624 
625 		case OBJ_VERTICES:
626 			SYS_ASSERT(0 <= objnum && objnum < NumVertices);
627 			pos = (int*) Vertices[objnum];
628 			break;
629 
630 		case OBJ_SECTORS:
631 			SYS_ASSERT(0 <= objnum && objnum < NumSectors);
632 			pos = (int*) Sectors[objnum];
633 			break;
634 
635 		case OBJ_SIDEDEFS:
636 			SYS_ASSERT(0 <= objnum && objnum < NumSideDefs);
637 			pos = (int*) SideDefs[objnum];
638 			break;
639 
640 		case OBJ_LINEDEFS:
641 			SYS_ASSERT(0 <= objnum && objnum < NumLineDefs);
642 			pos = (int*) LineDefs[objnum];
643 			break;
644 
645 		default:
646 			BugError("RawGetBase: bad objtype %d\n", (int)objtype);
647 			return; /* NOT REACHED */
648 	}
649 
650 	std::swap(pos[field], *value);
651 
652 	did_make_changes = true;
653 
654 	Clipboard_NotifyChange(objtype, objnum, field);
655 	Selection_NotifyChange(objtype, objnum, field);
656 	 MapStuff_NotifyChange(objtype, objnum, field);
657 	 Render3D_NotifyChange(objtype, objnum, field);
658 	ObjectBox_NotifyChange(objtype, objnum, field);
659 }
660 
661 
662 //------------------------------------------------------------------------
663 //  BASIS API IMPLEMENTATION
664 //------------------------------------------------------------------------
665 
666 
667 enum
668 {
669 	OP_CHANGE = 'c',
670 	OP_INSERT = 'i',
671 	OP_DELETE = 'd',
672 };
673 
674 
675 class edit_op_c
676 {
677 public:
678 	char action;
679 
680 	byte objtype;
681 	byte field;
682 	byte _pad;
683 
684 	int objnum;
685 
686 	int *ptr;
687 	int value;
688 
689 public:
edit_op_c()690 	edit_op_c() : action(0), objtype(0), field(0), _pad(0), objnum(0), ptr(NULL), value(0)
691 	{ }
692 
~edit_op_c()693 	~edit_op_c()
694 	{ }
695 
Apply()696 	void Apply()
697 	{
698 		switch (action)
699 		{
700 			case OP_CHANGE:
701 				RawChange((obj_type_e)objtype, objnum, (int)field, &value);
702 				return;
703 
704 			case OP_DELETE:
705 				ptr = RawDelete((obj_type_e)objtype, objnum);
706 				action = OP_INSERT;  // reverse the operation
707 				return;
708 
709 			case OP_INSERT:
710 				RawInsert((obj_type_e)objtype, objnum, ptr);
711 				ptr = NULL;
712 				action = OP_DELETE;  // reverse the operation
713 				return;
714 
715 			default:
716 				BugError("edit_op_c::Apply\n");
717 		}
718 	}
719 
Destroy()720 	void Destroy()
721 	{
722 // fprintf(stderr, "edit_op_c::Destroy %p action = '%c'\n", this, (action == 0) ? ' ' : action);
723 		if (action == OP_INSERT)
724 		{
725 			SYS_ASSERT(ptr);
726 			DeleteFinally((obj_type_e) objtype, ptr);
727 		}
728 		else if (action == OP_DELETE)
729 		{
730 			SYS_ASSERT(! ptr);
731 		}
732 	}
733 };
734 
735 
736 #define MAX_UNDO_MESSAGE  200
737 
738 class undo_group_c
739 {
740 private:
741 	std::vector<edit_op_c> ops;
742 
743 	int dir;
744 
745 	char message[MAX_UNDO_MESSAGE];
746 
747 public:
undo_group_c()748 	undo_group_c() : ops(), dir(+1)
749 	{
750 		strcpy(message, "[something]");
751 	}
752 
~undo_group_c()753 	~undo_group_c()
754 	{
755 		for (int i = (int)ops.size() - 1 ; i >= 0 ; i--)
756 		{
757 			ops[i].Destroy();
758 		}
759 	}
760 
Empty() const761 	bool Empty() const
762 	{
763 		return ops.empty();
764 	}
765 
Add_Apply(edit_op_c & op)766 	void Add_Apply(edit_op_c& op)
767 	{
768 		ops.push_back(op);
769 
770 		ops.back().Apply();
771 	}
772 
End()773 	void End()
774 	{
775 		dir = -1;
776 	}
777 
ReApply()778 	void ReApply()
779 	{
780 		int total = (int)ops.size();
781 
782 		if (dir > 0)
783 			for (int i = 0; i < total; i++)
784 				ops[i].Apply();
785 		else
786 			for (int i = total-1; i >= 0; i--)
787 				ops[i].Apply();
788 
789 		// reverse the order for next time
790 		dir = -dir;
791 	}
792 
SetMsg(const char * buf)793 	void SetMsg(const char *buf)
794 	{
795 		strncpy(message, buf, sizeof(message));
796 		message[sizeof(message) - 1] = 0;
797 	}
798 
GetMsg() const799 	const char *GetMsg() const
800 	{
801 		return message;
802 	}
803 };
804 
805 
806 static undo_group_c *cur_group;
807 
808 static std::list<undo_group_c *> undo_history;
809 static std::list<undo_group_c *> redo_future;
810 
811 
ClearUndoHistory()812 static void ClearUndoHistory()
813 {
814 	std::list<undo_group_c *>::iterator LI;
815 
816 	for (LI = undo_history.begin(); LI != undo_history.end(); LI++)
817 		delete *LI;
818 
819 	undo_history.clear();
820 }
821 
ClearRedoFuture()822 static void ClearRedoFuture()
823 {
824 	std::list<undo_group_c *>::iterator LI;
825 
826 	for (LI = redo_future.begin(); LI != redo_future.end(); LI++)
827 		delete *LI;
828 
829 	redo_future.clear();
830 }
831 
832 
BA_Begin()833 void BA_Begin()
834 {
835 	if (cur_group)
836 		BugError("BA_Begin called twice without BA_End\n");
837 
838 	ClearRedoFuture();
839 
840 	cur_group = new undo_group_c();
841 
842 	DoClearChangeStatus();
843 }
844 
845 
BA_End()846 void BA_End()
847 {
848 	if (! cur_group)
849 		BugError("BA_End called without a previous BA_Begin\n");
850 
851 	cur_group->End();
852 
853 	if (cur_group->Empty())
854 		delete cur_group;
855 	else
856 	{
857 		undo_history.push_front(cur_group);
858 		Status_Set("%s", cur_group->GetMsg());
859 	}
860 
861 	cur_group = NULL;
862 
863 	DoProcessChangeStatus();
864 }
865 
866 
BA_Abort(bool keep_changes)867 void BA_Abort(bool keep_changes)
868 {
869 	if (! cur_group)
870 		BugError("BA_Abort called without a previous BA_Begin\n");
871 
872 	cur_group->End();
873 
874 	if (! keep_changes && ! cur_group->Empty())
875 	{
876 		cur_group->ReApply();
877 	}
878 
879 	delete cur_group;
880 	cur_group = NULL;
881 
882 	did_make_changes  = false;
883 
884 	DoProcessChangeStatus();
885 }
886 
887 
BA_Message(const char * msg,...)888 void BA_Message(const char *msg, ...)
889 {
890 	SYS_ASSERT(msg);
891 	SYS_ASSERT(cur_group);
892 
893 	va_list arg_ptr;
894 
895 	char buffer[MAX_UNDO_MESSAGE];
896 
897 	va_start(arg_ptr, msg);
898 	vsnprintf(buffer, MAX_UNDO_MESSAGE, msg, arg_ptr);
899 	va_end(arg_ptr);
900 
901 	buffer[MAX_UNDO_MESSAGE-1] = 0;
902 
903 	cur_group->SetMsg(buffer);
904 }
905 
906 
BA_MessageForSel(const char * verb,selection_c * list,const char * suffix)907 void BA_MessageForSel(const char *verb, selection_c *list, const char *suffix)
908 {
909 	// utility for creating messages like "moved 3 things"
910 
911 	int total = list->count_obj();
912 
913 	if (total < 1)  // oops
914 		return;
915 
916 	if (total == 1)
917 	{
918 		BA_Message("%s %s #%d%s", verb, NameForObjectType(list->what_type()), list->find_first(), suffix);
919 	}
920 	else
921 	{
922 		BA_Message("%s %d %s%s", verb, total, NameForObjectType(list->what_type(), true /* plural */), suffix);
923 	}
924 }
925 
926 
BA_New(obj_type_e type)927 int BA_New(obj_type_e type)
928 {
929 	SYS_ASSERT(cur_group);
930 
931 	edit_op_c op;
932 
933 	op.action  = OP_INSERT;
934 	op.objtype = type;
935 
936 	switch (type)
937 	{
938 		case OBJ_THINGS:
939 			op.objnum = NumThings;
940 			op.ptr = (int*) new Thing;
941 			break;
942 
943 		case OBJ_VERTICES:
944 			op.objnum = NumVertices;
945 			op.ptr = (int*) new Vertex;
946 			break;
947 
948 		case OBJ_SIDEDEFS:
949 			op.objnum = NumSideDefs;
950 			op.ptr = (int*) new SideDef;
951 			break;
952 
953 		case OBJ_LINEDEFS:
954 			op.objnum = NumLineDefs;
955 			op.ptr = (int*) new LineDef;
956 			break;
957 
958 		case OBJ_SECTORS:
959 			op.objnum = NumSectors;
960 			op.ptr = (int*) new Sector;
961 			break;
962 
963 		default:
964 			BugError("BA_New: unknown type\n");
965 	}
966 
967 	SYS_ASSERT(cur_group);
968 
969 	cur_group->Add_Apply(op);
970 
971 	return op.objnum;
972 }
973 
974 
BA_Delete(obj_type_e type,int objnum)975 void BA_Delete(obj_type_e type, int objnum)
976 {
977 	SYS_ASSERT(cur_group);
978 
979 	edit_op_c op;
980 
981 	op.action  = OP_DELETE;
982 	op.objtype = type;
983 	op.objnum  = objnum;
984 
985 	// this must happen _before_ doing the deletion (otherwise
986 	// when we undo, the insertion will mess up the references).
987 	if (type == OBJ_SIDEDEFS)
988 	{
989 		// unbind sidedef from any linedefs using it
990 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
991 		{
992 			LineDef *L = LineDefs[n];
993 
994 			if (L->right == objnum)
995 				BA_ChangeLD(n, LineDef::F_RIGHT, -1);
996 
997 			if (L->left == objnum)
998 				BA_ChangeLD(n, LineDef::F_LEFT, -1);
999 		}
1000 	}
1001 	else if (type == OBJ_VERTICES)
1002 	{
1003 		// delete any linedefs bound to this vertex
1004 		for (int n = NumLineDefs-1 ; n >= 0 ; n--)
1005 		{
1006 			LineDef *L = LineDefs[n];
1007 
1008 			if (L->start == objnum || L->end == objnum)
1009 			{
1010 				BA_Delete(OBJ_LINEDEFS, n);
1011 			}
1012 		}
1013 	}
1014 	else if (type == OBJ_SECTORS)
1015 	{
1016 		// delete the sidedefs bound to this sector
1017 		for (int n = NumSideDefs-1 ; n >= 0 ; n--)
1018 		{
1019 			if (SideDefs[n]->sector == objnum)
1020 			{
1021 				BA_Delete(OBJ_SIDEDEFS, n);
1022 			}
1023 		}
1024 	}
1025 
1026 	SYS_ASSERT(cur_group);
1027 
1028 	cur_group->Add_Apply(op);
1029 }
1030 
1031 
BA_Change(obj_type_e type,int objnum,byte field,int value)1032 bool BA_Change(obj_type_e type, int objnum, byte field, int value)
1033 {
1034 	// TODO: optimise, check whether value actually changes
1035 
1036 	edit_op_c op;
1037 
1038 	op.action  = OP_CHANGE;
1039 	op.objtype = type;
1040 	op.field   = field;
1041 	op.objnum  = objnum;
1042 	op.value   = value;
1043 
1044 	SYS_ASSERT(cur_group);
1045 
1046 	cur_group->Add_Apply(op);
1047 	return true;
1048 }
1049 
1050 
BA_Undo()1051 bool BA_Undo()
1052 {
1053 	if (undo_history.empty())
1054 		return false;
1055 
1056 	DoClearChangeStatus();
1057 
1058 	undo_group_c * grp = undo_history.front();
1059 	undo_history.pop_front();
1060 
1061 	Status_Set("UNDO: %s", grp->GetMsg());
1062 
1063 	grp->ReApply();
1064 
1065 	redo_future.push_front(grp);
1066 
1067 	DoProcessChangeStatus();
1068 	return true;
1069 }
1070 
BA_Redo()1071 bool BA_Redo()
1072 {
1073 	if (redo_future.empty())
1074 		return false;
1075 
1076 	DoClearChangeStatus();
1077 
1078 	undo_group_c * grp = redo_future.front();
1079 	redo_future.pop_front();
1080 
1081 	Status_Set("Redo: %s", grp->GetMsg());
1082 
1083 	grp->ReApply();
1084 
1085 	undo_history.push_front(grp);
1086 
1087 	DoProcessChangeStatus();
1088 	return true;
1089 }
1090 
1091 
BA_ClearAll()1092 void BA_ClearAll()
1093 {
1094 	int i;
1095 
1096 	for (i = 0 ; i < NumThings   ; i++) delete Things[i];
1097 	for (i = 0 ; i < NumVertices ; i++) delete Vertices[i];
1098 	for (i = 0 ; i < NumSectors  ; i++) delete Sectors[i];
1099 	for (i = 0 ; i < NumSideDefs ; i++) delete SideDefs[i];
1100 	for (i = 0 ; i < NumLineDefs ; i++) delete LineDefs[i];
1101 
1102 	Things.clear();
1103 	Vertices.clear();
1104 	Sectors.clear();
1105 	SideDefs.clear();
1106 	LineDefs.clear();
1107 
1108 	HeaderData.clear();
1109 	BehaviorData.clear();
1110 	ScriptsData.clear();
1111 
1112 	ClearUndoHistory();
1113 	ClearRedoFuture();
1114 
1115 	// Note: we don't clear the string table, since there can be
1116 	//       string references in the clipboard.
1117 
1118 	Clipboard_ClearLocals();
1119 }
1120 
1121 
1122 /* HELPERS */
1123 
BA_ChangeTH(int thing,byte field,int value)1124 bool BA_ChangeTH(int thing, byte field, int value)
1125 {
1126 	SYS_ASSERT(is_thing(thing));
1127 	SYS_ASSERT(field <= Thing::F_ARG5);
1128 
1129 	if (field == Thing::F_TYPE)
1130 		recent_things.insert_number(value);
1131 
1132 	return BA_Change(OBJ_THINGS, thing, field, value);
1133 }
1134 
BA_ChangeVT(int vert,byte field,int value)1135 bool BA_ChangeVT(int vert, byte field, int value)
1136 {
1137 	SYS_ASSERT(is_vertex(vert));
1138 	SYS_ASSERT(field <= Vertex::F_Y);
1139 
1140 	return BA_Change(OBJ_VERTICES, vert, field, value);
1141 }
1142 
BA_ChangeSEC(int sec,byte field,int value)1143 bool BA_ChangeSEC(int sec, byte field, int value)
1144 {
1145 	SYS_ASSERT(is_sector(sec));
1146 	SYS_ASSERT(field <= Sector::F_TAG);
1147 
1148 	if (field == Sector::F_FLOOR_TEX ||
1149 		field == Sector::F_CEIL_TEX)
1150 	{
1151 		recent_flats.insert(BA_GetString(value));
1152 	}
1153 
1154 	return BA_Change(OBJ_SECTORS, sec, field, value);
1155 }
1156 
BA_ChangeSD(int side,byte field,int value)1157 bool BA_ChangeSD(int side, byte field, int value)
1158 {
1159 	SYS_ASSERT(is_sidedef(side));
1160 	SYS_ASSERT(field <= SideDef::F_SECTOR);
1161 
1162 	if (field == SideDef::F_LOWER_TEX ||
1163 		field == SideDef::F_UPPER_TEX ||
1164 		field == SideDef::F_MID_TEX)
1165 	{
1166 		recent_textures.insert(BA_GetString(value));
1167 	}
1168 
1169 	return BA_Change(OBJ_SIDEDEFS, side, field, value);
1170 }
1171 
BA_ChangeLD(int line,byte field,int value)1172 bool BA_ChangeLD(int line, byte field, int value)
1173 {
1174 	SYS_ASSERT(is_linedef(line));
1175 	SYS_ASSERT(field <= LineDef::F_ARG5);
1176 
1177 	return BA_Change(OBJ_LINEDEFS, line, field, value);
1178 }
1179 
1180 
1181 //------------------------------------------------------------------------
1182 //   CHECKSUM LOGIC
1183 //------------------------------------------------------------------------
1184 
ChecksumThing(crc32_c & crc,const Thing * T)1185 static void ChecksumThing(crc32_c& crc, const Thing * T)
1186 {
1187 	crc += T->raw_x;
1188 	crc += T->raw_y;
1189 	crc += T->angle;
1190 	crc += T->type;
1191 	crc += T->options;
1192 }
1193 
ChecksumVertex(crc32_c & crc,const Vertex * V)1194 static void ChecksumVertex(crc32_c& crc, const Vertex * V)
1195 {
1196 	crc += V->raw_x;
1197 	crc += V->raw_y;
1198 }
1199 
ChecksumSector(crc32_c & crc,const Sector * SEC)1200 static void ChecksumSector(crc32_c& crc, const Sector * SEC)
1201 {
1202 	crc += SEC->floorh;
1203 	crc += SEC->ceilh;
1204 	crc += SEC->light;
1205 	crc += SEC->type;
1206 	crc += SEC->tag;
1207 
1208 	crc += SEC->FloorTex();
1209 	crc += SEC->CeilTex();
1210 }
1211 
ChecksumSideDef(crc32_c & crc,const SideDef * S)1212 static void ChecksumSideDef(crc32_c& crc, const SideDef * S)
1213 {
1214 	crc += S->x_offset;
1215 	crc += S->y_offset;
1216 
1217 	crc += S->LowerTex();
1218 	crc += S->MidTex();
1219 	crc += S->UpperTex();
1220 
1221 	ChecksumSector(crc, S->SecRef());
1222 }
1223 
ChecksumLineDef(crc32_c & crc,const LineDef * L)1224 static void ChecksumLineDef(crc32_c& crc, const LineDef * L)
1225 {
1226 	crc += L->flags;
1227 	crc += L->type;
1228 	crc += L->tag;
1229 
1230 	ChecksumVertex(crc, L->Start());
1231 	ChecksumVertex(crc, L->End());
1232 
1233 	if (L->Right())
1234 		ChecksumSideDef(crc, L->Right());
1235 
1236 	if (L->Left())
1237 		ChecksumSideDef(crc, L->Left());
1238 }
1239 
1240 
BA_LevelChecksum(crc32_c & crc)1241 void BA_LevelChecksum(crc32_c& crc)
1242 {
1243 	// the following method conveniently skips any unused vertices,
1244 	// sidedefs and sectors.  It also adds each sector umpteen times
1245 	// (for each line in the sector), but that should not affect the
1246 	// validity of the final checksum.
1247 
1248 	int i;
1249 
1250 	for (i = 0 ; i < NumThings ; i++)
1251 		ChecksumThing(crc, Things[i]);
1252 
1253 	for (i = 0 ; i < NumLineDefs ; i++)
1254 		ChecksumLineDef(crc, LineDefs[i]);
1255 }
1256 
1257 
1258 //--- editor settings ---
1259 // vi:ts=4:sw=4:noexpandtab
1260