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