1 /*
2 ** p_linkedsectors.cpp
3 **
4 ** Linked sector movement
5 **
6 **---------------------------------------------------------------------------
7 ** Copyright 2008 Christoph Oelckers
8 ** All rights reserved.
9 **
10 ** Redistribution and use in source and binary forms, with or without
11 ** modification, are permitted provided that the following conditions
12 ** are met:
13 **
14 ** 1. Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** 2. Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in the
18 ** documentation and/or other materials provided with the distribution.
19 ** 3. The name of the author may not be used to endorse or promote products
20 ** derived from this software without specific prior written permission.
21 **
22 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 **---------------------------------------------------------------------------
33 **
34 */
35
36 #include "templates.h"
37 #include "p_local.h"
38 #include "p_lnspec.h"
39
40 enum
41 {
42 // The different movement types.
43 LINK_NONE=0,
44 LINK_FLOOR=1,
45 LINK_CEILING=2,
46 LINK_BOTH=3,
47 LINK_FLOORMIRRORFLAG=4,
48 LINK_CEILINGMIRRORFLAG=8,
49
50 LINK_FLOORMIRROR=5,
51 LINK_CEILINGMIRROR=10,
52 LINK_BOTHMIRROR=15,
53
54 LINK_FLOORBITS=5,
55 LINK_CEILINGBITS=10,
56
57 LINK_FLAGMASK = 15
58
59 };
60
61
62 //============================================================================
63 //
64 // Checks whether the other sector is linked to this one
65 // Used to ignore linked sectors in the FindNextLowest/Highest*
66 // functions
67 //
68 // NOTE: After looking at Eternity's code I discovered that this check
69 // is not done for the FindNext* function but instead only for
70 // FindLowestCeilingSurrounding where IMO it makes much less sense
71 // because the most frequent use of this feature is most likely lifts
72 // with a more detailed surface. Needs to be investigated!
73 //
74 //============================================================================
75
IsLinked(sector_t * other,bool ceiling) const76 bool sector_t::IsLinked(sector_t *other, bool ceiling) const
77 {
78 extsector_t::linked::plane &scrollplane = ceiling? e->Linked.Ceiling : e->Linked.Floor;
79 int flag = ceiling? LINK_CEILING : LINK_FLOOR;
80
81 for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
82 {
83 if (other == scrollplane.Sectors[i].Sector && scrollplane.Sectors[i].Type & flag) return true;
84 }
85 return false;
86 }
87
88
89 //============================================================================
90 //
91 // Helper functions for P_MoveLinkedSectors
92 //
93 //============================================================================
94
MoveCeiling(sector_t * sector,int crush,fixed_t move)95 static bool MoveCeiling(sector_t *sector, int crush, fixed_t move)
96 {
97 sector->ceilingplane.ChangeHeight (move);
98 sector->ChangePlaneTexZ(sector_t::ceiling, move);
99
100 if (P_ChangeSector(sector, crush, move, 1, true)) return false;
101
102 // Don't let the ceiling go below the floor
103 if ((sector->ceilingplane.a | sector->ceilingplane.b |
104 sector->floorplane.a | sector->floorplane.b) == 0 &&
105 sector->GetPlaneTexZ(sector_t::floor) > sector->GetPlaneTexZ(sector_t::ceiling)) return false;
106
107 return true;
108 }
109
MoveFloor(sector_t * sector,int crush,fixed_t move)110 static bool MoveFloor(sector_t *sector, int crush, fixed_t move)
111 {
112 sector->floorplane.ChangeHeight (move);
113 sector->ChangePlaneTexZ(sector_t::floor, move);
114
115 if (P_ChangeSector(sector, crush, move, 0, true)) return false;
116
117 // Don't let the floor go above the ceiling
118 if ((sector->ceilingplane.a | sector->ceilingplane.b |
119 sector->floorplane.a | sector->floorplane.b) == 0 &&
120 sector->GetPlaneTexZ(sector_t::floor) > sector->GetPlaneTexZ(sector_t::ceiling)) return false;
121
122 return true;
123 }
124
125 //============================================================================
126 //
127 // P_MoveLinkedSectors
128 //
129 // Moves all floors/ceilings linked to the control sector
130 // Important: All sectors must complete their movement
131 // even if a previous one already failed.
132 //
133 //============================================================================
134
P_MoveLinkedSectors(sector_t * sector,int crush,fixed_t move,bool ceiling)135 bool P_MoveLinkedSectors(sector_t *sector, int crush, fixed_t move, bool ceiling)
136 {
137 extsector_t::linked::plane &scrollplane = ceiling? sector->e->Linked.Ceiling : sector->e->Linked.Floor;
138 bool ok = true;
139
140 for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
141 {
142 switch(scrollplane.Sectors[i].Type)
143 {
144 case LINK_FLOOR:
145 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move);
146 break;
147
148 case LINK_CEILING:
149 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move);
150 break;
151
152 case LINK_BOTH:
153 if (move < 0)
154 {
155 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move);
156 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move);
157 }
158 else
159 {
160 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move);
161 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move);
162 }
163 break;
164
165 case LINK_FLOORMIRROR:
166 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move);
167 break;
168
169 case LINK_CEILINGMIRROR:
170 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move);
171 break;
172
173 case LINK_BOTHMIRROR:
174 if (move > 0)
175 {
176 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move);
177 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move);
178 }
179 else
180 {
181 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move);
182 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move);
183 }
184 break;
185
186 case LINK_FLOOR+LINK_CEILINGMIRROR:
187 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move);
188 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move);
189 break;
190
191 case LINK_CEILING+LINK_FLOORMIRROR:
192 ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move);
193 ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move);
194 break;
195
196 default:
197 // all other types are invalid and have to be elimintated in the attachment stage
198 break;
199 }
200 }
201 return ok;
202 }
203
204 //============================================================================
205 //
206 // P_StartLinkedSectorInterpolations
207 //
208 // Starts interpolators for every sector plane is being changed by moving
209 // this sector
210 //
211 //============================================================================
212
P_StartLinkedSectorInterpolations(TArray<DInterpolation * > & list,sector_t * sector,bool ceiling)213 void P_StartLinkedSectorInterpolations(TArray<DInterpolation *> &list, sector_t *sector, bool ceiling)
214 {
215 extsector_t::linked::plane &scrollplane = ceiling? sector->e->Linked.Ceiling : sector->e->Linked.Floor;
216
217 for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
218 {
219 if (scrollplane.Sectors[i].Type & LINK_FLOOR)
220 {
221 list.Push(scrollplane.Sectors[i].Sector->SetInterpolation(sector_t::FloorMove, false));
222 }
223 if (scrollplane.Sectors[i].Type & LINK_CEILING)
224 {
225 list.Push(scrollplane.Sectors[i].Sector->SetInterpolation(sector_t::CeilingMove, false));
226 }
227 }
228 }
229
230 //============================================================================
231 //
232 // AddSingleSector
233 //
234 // Adds a single sector to a scroll plane. Checks for invalid
235 // flag combinations if the sector is already added and removes it if necessary.
236 //
237 //============================================================================
238
AddSingleSector(extsector_t::linked::plane & scrollplane,sector_t * sector,int movetype)239 static void AddSingleSector(extsector_t::linked::plane &scrollplane, sector_t *sector, int movetype)
240 {
241 // First we have to check the list if the sector is already in it
242 // If so the move type must be adjusted
243
244 for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
245 {
246 if (scrollplane.Sectors[i].Sector == sector)
247 {
248 if (movetype & LINK_FLOORBITS)
249 {
250 scrollplane.Sectors[i].Type &= ~LINK_FLOORBITS;
251 }
252
253 if (movetype & LINK_CEILINGBITS)
254 {
255 scrollplane.Sectors[i].Type &= ~LINK_CEILINGBITS;
256 }
257
258 scrollplane.Sectors[i].Type |= movetype;
259 return;
260 }
261 }
262
263 // The sector hasn't been attached yet so do it now
264
265 FLinkedSector newlink = {sector, movetype};
266 scrollplane.Sectors.Push(newlink);
267 }
268
269 //============================================================================
270 //
271 // RemoveTaggedSectors
272 //
273 // Remove all sectors with the given tag from the link list
274 //
275 //============================================================================
276
RemoveTaggedSectors(extsector_t::linked::plane & scrollplane,int tag)277 static void RemoveTaggedSectors(extsector_t::linked::plane &scrollplane, int tag)
278 {
279 for(int i = scrollplane.Sectors.Size()-1; i>=0; i--)
280 {
281 if (tagManager.SectorHasTag(scrollplane.Sectors[i].Sector, tag))
282 {
283 scrollplane.Sectors.Delete(i);
284 }
285 }
286 }
287
288
289
290 //============================================================================
291 //
292 // P_AddSectorLink
293 //
294 // Links sector planes to a control sector based on the sector's tag
295 //
296 //============================================================================
297
P_AddSectorLinks(sector_t * control,int tag,INTBOOL ceiling,int movetype)298 bool P_AddSectorLinks(sector_t *control, int tag, INTBOOL ceiling, int movetype)
299 {
300 int param = movetype;
301
302 // can't be done if the control sector is moving.
303 if ((ceiling && control->PlaneMoving(sector_t::ceiling)) ||
304 (!ceiling && control->PlaneMoving(sector_t::floor))) return false;
305
306 // Make sure we have only valid combinations
307 movetype &= LINK_FLAGMASK;
308 if ((movetype & LINK_FLOORMIRROR) == LINK_FLOORMIRRORFLAG) movetype &= ~LINK_FLOORMIRRORFLAG;
309 if ((movetype & LINK_CEILINGMIRROR) == LINK_CEILINGMIRRORFLAG) movetype &= ~LINK_CEILINGMIRRORFLAG;
310
311 // Don't remove any sector if the parameter is invalid.
312 // Addition may still be performed based on the given value.
313 if (param != 0 && movetype == 0) return false;
314
315 extsector_t::linked::plane &scrollplane = ceiling? control->e->Linked.Ceiling : control->e->Linked.Floor;
316
317 if (movetype > 0)
318 {
319 int sec;
320 FSectorTagIterator itr(tag);
321 while ((sec = itr.Next()) >= 0)
322 {
323 // Don't attach to self!
324 if (control != §ors[sec])
325 {
326 AddSingleSector(scrollplane, §ors[sec], movetype);
327 }
328 }
329 }
330 else
331 {
332 RemoveTaggedSectors(scrollplane, tag);
333 }
334 return true;
335 }
336
337 //============================================================================
338 //
339 // P_AddSectorLinksByID
340 //
341 // Links sector planes to a control sector based on a control linedef
342 // touching the sectors. This is the method Eternity uses and is here
343 // mostly so that the Eternity line types can be emulated
344 //
345 //============================================================================
346
P_AddSectorLinksByID(sector_t * control,int id,INTBOOL ceiling)347 void P_AddSectorLinksByID(sector_t *control, int id, INTBOOL ceiling)
348 {
349 extsector_t::linked::plane &scrollplane = ceiling? control->e->Linked.Ceiling : control->e->Linked.Floor;
350
351 FLineIdIterator itr(id);
352 int line;
353 while ((line = itr.Next()) >= 0)
354 {
355 line_t *ld = &lines[line];
356
357 if (ld->special == Static_Init && ld->args[1] == Init_SectorLink)
358 {
359 int movetype = ld->args[3];
360
361 // [GZ] Eternity does allow the attached sector to be the control sector,
362 // this permits elevator effects (ceiling attached to floors), so instead
363 // of checking whether the two sectors are the same, we prevent a plane
364 // from being attached to itself. This should be enough to do the trick.
365 if (ld->frontsector == control)
366 {
367 if (ceiling) movetype &= ~LINK_CEILING;
368 else movetype &= ~LINK_FLOOR;
369 }
370
371 // Make sure we have only valid combinations
372 movetype &= LINK_FLAGMASK;
373 if ((movetype & LINK_FLOORMIRROR) == LINK_FLOORMIRRORFLAG) movetype &= ~LINK_FLOORMIRRORFLAG;
374 if ((movetype & LINK_CEILINGMIRROR) == LINK_CEILINGMIRRORFLAG) movetype &= ~LINK_CEILINGMIRRORFLAG;
375
376 if (movetype != 0 && ld->frontsector != NULL)//&& ld->frontsector != control) Needs to be allowed!
377 {
378 AddSingleSector(scrollplane, ld->frontsector, movetype);
379 }
380 }
381 }
382 }
383