1 /** @file p_floor.cpp Common playsim routines relating to moving floors.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006 Martin Eyre <martineyre@btinternet.com>
6 * @authors Copyright © 2003-2005 Samuel Villarreal <svkaiser@gmail.com>
7 * @authors Copyright © 1999 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman (PrBoom 2.2.6)
8 * @authors Copyright © 1999-2000 Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze (PrBoom 2.2.6)
9 * @authors Copyright © 1993-1996 id Software, Inc.
10 *
11 * @par License
12 * GPL: http://www.gnu.org/licenses/gpl.html
13 *
14 * <small>This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by the
16 * Free Software Foundation; either version 2 of the License, or (at your
17 * option) any later version. This program is distributed in the hope that it
18 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
19 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
20 * Public License for more details. You should have received a copy of the GNU
21 * General Public License along with this program; if not, write to the Free
22 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA</small>
24 */
25
26 #include "common.h"
27 #include "p_floor.h"
28
29 #include "dmu_lib.h"
30 #include "p_map.h"
31 #include "p_mapspec.h"
32 #include "p_tick.h"
33 #if __JHEXEN__ || __JDOOM64__
34 #include "p_ceiling.h"
35 #endif
36 #include "p_sound.h"
37 #include "p_saveg.h"
38
39 #if __JHERETIC__
40 # define SFX_FLOORMOVE (SFX_DORMOV)
41 #else
42 # define SFX_FLOORMOVE (SFX_STNMOV)
43 #endif
44
45 #if __JHEXEN__
46 #define STAIR_SECTOR_TYPE 26
47 #define STAIR_QUEUE_SIZE 32
48 #endif
49
50 #if __JHEXEN__
51 typedef struct stairqueue_s {
52 Sector *sector;
53 int type;
54 coord_t height;
55 } stairqueue_t;
56
57 // Global vars for stair building, in a struct for neatness.
58 typedef struct stairdata_s {
59 coord_t stepDelta;
60 int direction;
61 float speed;
62 world_Material *material;
63 int startDelay;
64 int startDelayDelta;
65 int textureChange;
66 coord_t startHeight;
67 } stairdata_t;
68 #endif
69
70 #if __JHEXEN__
71 static void enqueueStairSector(Sector *sec, int type, coord_t height);
72 #endif
73
74 #if __JHEXEN__
75 stairdata_t stairData;
76 stairqueue_t stairQueue[STAIR_QUEUE_SIZE];
77 static int stairQueueHead;
78 static int stairQueueTail;
79 #endif
80
T_MovePlane(Sector * sector,float speed,coord_t dest,int crush,int isCeiling,int direction)81 result_e T_MovePlane(Sector *sector, float speed, coord_t dest, int crush,
82 int isCeiling, int direction)
83 {
84 dd_bool flag;
85 coord_t lastpos;
86 coord_t floorheight, ceilingheight;
87 int ptarget = (isCeiling? DMU_CEILING_TARGET_HEIGHT : DMU_FLOOR_TARGET_HEIGHT);
88 int pspeed = (isCeiling? DMU_CEILING_SPEED : DMU_FLOOR_SPEED);
89
90 // Let the engine know about the movement of this plane.
91 P_SetDoublep(sector, ptarget, dest);
92 P_SetFloatp(sector, pspeed, speed);
93
94 floorheight = P_GetDoublep(sector, DMU_FLOOR_HEIGHT);
95 ceilingheight = P_GetDoublep(sector, DMU_CEILING_HEIGHT);
96
97 switch(isCeiling)
98 {
99 case 0:
100 // FLOOR
101 switch(direction)
102 {
103 case -1:
104 // DOWN
105 if(floorheight - speed < dest)
106 {
107 // The move is complete.
108 lastpos = floorheight;
109 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, dest);
110 flag = P_ChangeSector(sector, crush);
111 if(flag)
112 {
113 // Oh no, the move failed.
114 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, lastpos);
115 P_SetDoublep(sector, ptarget, lastpos);
116 P_ChangeSector(sector, crush);
117 }
118 #if __JHEXEN__
119 P_SetFloatp(sector, pspeed, 0);
120 #endif
121 return pastdest;
122 }
123 else
124 {
125 lastpos = floorheight;
126 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, floorheight - speed);
127 flag = P_ChangeSector(sector, crush);
128 if(flag)
129 {
130 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, lastpos);
131 P_SetDoublep(sector, ptarget, lastpos);
132 #if __JHEXEN__
133 P_SetFloatp(sector, pspeed, 0);
134 #endif
135 P_ChangeSector(sector, crush);
136 return crushed;
137 }
138 }
139 break;
140
141 case 1:
142 // UP
143 if(floorheight + speed > dest)
144 {
145 // The move is complete.
146 lastpos = floorheight;
147 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, dest);
148 flag = P_ChangeSector(sector, crush);
149 if(flag)
150 {
151 // Oh no, the move failed.
152 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, lastpos);
153 P_SetDoublep(sector, ptarget, lastpos);
154 P_ChangeSector(sector, crush);
155 }
156 #if __JHEXEN__
157 P_SetFloatp(sector, pspeed, 0);
158 #endif
159 return pastdest;
160 }
161 else
162 {
163 // COULD GET CRUSHED
164 lastpos = floorheight;
165 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, floorheight + speed);
166 flag = P_ChangeSector(sector, crush);
167 if(flag)
168 {
169 #if !__JHEXEN__
170 if(crush)
171 return crushed;
172 #endif
173 P_SetDoublep(sector, DMU_FLOOR_HEIGHT, lastpos);
174 P_SetDoublep(sector, ptarget, lastpos);
175 #if __JHEXEN__
176 P_SetFloatp(sector, pspeed, 0);
177 #endif
178 P_ChangeSector(sector, crush);
179 return crushed;
180 }
181 }
182 break;
183
184 default:
185 break;
186 }
187 break;
188
189 case 1:
190 // CEILING
191 switch(direction)
192 {
193 case -1:
194 // DOWN
195 if(ceilingheight - speed < dest)
196 {
197 // The move is complete.
198 lastpos = ceilingheight;
199 P_SetDoublep(sector, DMU_CEILING_HEIGHT, dest);
200 flag = P_ChangeSector(sector, crush);
201 if(flag)
202 {
203 P_SetDoublep(sector, DMU_CEILING_HEIGHT, lastpos);
204 P_SetDoublep(sector, ptarget, lastpos);
205 P_ChangeSector(sector, crush);
206 }
207 #if __JHEXEN__
208 P_SetFloatp(sector, pspeed, 0);
209 #endif
210 return pastdest;
211 }
212 else
213 {
214 // COULD GET CRUSHED
215 lastpos = ceilingheight;
216 P_SetDoublep(sector, DMU_CEILING_HEIGHT, ceilingheight - speed);
217 flag = P_ChangeSector(sector, crush);
218 if(flag)
219 {
220 #if !__JHEXEN__
221 if(crush)
222 return crushed;
223 #endif
224 P_SetDoublep(sector, DMU_CEILING_HEIGHT, lastpos);
225 P_SetDoublep(sector, ptarget, lastpos);
226 #if __JHEXEN__
227 P_SetFloatp(sector, pspeed, 0);
228 #endif
229 P_ChangeSector(sector, crush);
230 return crushed;
231 }
232 }
233 break;
234
235 case 1:
236 // UP
237 if(ceilingheight + speed > dest)
238 {
239 // The move is complete.
240 lastpos = ceilingheight;
241 P_SetDoublep(sector, DMU_CEILING_HEIGHT, dest);
242 flag = P_ChangeSector(sector, crush);
243 if(flag)
244 {
245 P_SetDoublep(sector, DMU_CEILING_HEIGHT, lastpos);
246 P_SetDoublep(sector, ptarget, lastpos);
247 P_ChangeSector(sector, crush);
248 }
249 #if __JHEXEN__
250 P_SetFloatp(sector, pspeed, 0);
251 #endif
252 return pastdest;
253 }
254 else
255 {
256 lastpos = ceilingheight;
257 P_SetDoublep(sector, DMU_CEILING_HEIGHT, ceilingheight + speed);
258 flag = P_ChangeSector(sector, crush);
259 }
260 break;
261
262 default:
263 break;
264 }
265 break;
266
267 default:
268 break;
269 }
270
271 return ok;
272 }
273
274 /**
275 * Move a floor to it's destination (up or down).
276 */
T_MoveFloor(void * floorThinkerPtr)277 void T_MoveFloor(void *floorThinkerPtr)
278 {
279 floor_t *floor = (floor_t *)floorThinkerPtr;
280 result_e res;
281
282 #if __JHEXEN__
283 if(floor->resetDelayCount)
284 {
285 floor->resetDelayCount--;
286 if(!floor->resetDelayCount)
287 {
288 floor->floorDestHeight = floor->resetHeight;
289 floor->state = ((floor->state == FS_UP)? FS_DOWN : FS_UP);
290 floor->resetDelay = 0;
291 floor->delayCount = 0;
292 floor->delayTotal = 0;
293 }
294 }
295 if(floor->delayCount)
296 {
297 floor->delayCount--;
298 if(!floor->delayCount && floor->material)
299 {
300 P_SetPtrp(floor->sector, DMU_FLOOR_MATERIAL, floor->material);
301 }
302 return;
303 }
304 #endif
305
306 res =
307 T_MovePlane(floor->sector, floor->speed, floor->floorDestHeight,
308 floor->crush, 0, floor->state);
309
310 #if __JHEXEN__
311 if(floor->type == FT_RAISEBUILDSTEP)
312 {
313 if((floor->state == FS_UP &&
314 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) >= floor->stairsDelayHeight) ||
315 (floor->state == FS_DOWN &&
316 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) <= floor->stairsDelayHeight))
317 {
318 floor->delayCount = floor->delayTotal;
319 floor->stairsDelayHeight += floor->stairsDelayHeightDelta;
320 }
321 }
322 #endif
323
324 #if !__JHEXEN__
325 if(!(mapTime & 7))
326 S_PlaneSound((Plane *)P_GetPtrp(floor->sector, DMU_FLOOR_PLANE), SFX_FLOORMOVE);
327 #endif
328
329 if(res == pastdest)
330 {
331 xsector_t *xsec = P_ToXSector(floor->sector);
332 P_SetFloatp(floor->sector, DMU_FLOOR_SPEED, 0);
333
334 #if __JHEXEN__
335 SN_StopSequence((mobj_t *)P_GetPtrp(floor->sector, DMU_EMITTER));
336 #else
337 # if __JHERETIC__
338 if(floor->type == FT_RAISEBUILDSTEP)
339 # endif
340 S_PlaneSound((Plane *)P_GetPtrp(floor->sector, DMU_FLOOR_PLANE), SFX_PSTOP);
341
342 #endif
343 #if __JHEXEN__
344 if(floor->delayTotal)
345 floor->delayTotal = 0;
346
347 if(floor->resetDelay)
348 {
349 // floor->resetDelayCount = floor->resetDelay;
350 // floor->resetDelay = 0;
351 return;
352 }
353 #endif
354 xsec->specialData = NULL;
355 #if __JHEXEN__
356 if(floor->material)
357 {
358 P_SetPtrp(floor->sector, DMU_FLOOR_MATERIAL, floor->material);
359 }
360 #else
361 if(floor->state == FS_UP)
362 {
363 switch(floor->type)
364 {
365 case FT_RAISEDONUT:
366 xsec->special = floor->newSpecial;
367 P_SetPtrp(floor->sector, DMU_FLOOR_MATERIAL, floor->material);
368 break;
369
370 default:
371 break;
372 }
373 }
374 else if(floor->state == FS_DOWN)
375 {
376 switch(floor->type)
377 {
378 case FT_LOWERANDCHANGE:
379 xsec->special = floor->newSpecial;
380 P_SetPtrp(floor->sector, DMU_FLOOR_MATERIAL, floor->material);
381 break;
382
383 default:
384 break;
385 }
386 }
387 #endif
388 P_NotifySectorFinished(P_ToXSector(floor->sector)->tag);
389 Thinker_Remove(&floor->thinker);
390 }
391 }
392
write(MapStateWriter * msw) const393 void floor_s::write(MapStateWriter *msw) const
394 {
395 Writer1 *writer = msw->writer();
396
397 Writer_WriteByte(writer, 3); // Write a version byte.
398
399 // Note we don't bother to save a byte to tell if the function
400 // is present as we ALWAYS add one when loading.
401
402 Writer_WriteByte(writer, (byte) type);
403
404 Writer_WriteInt32(writer, P_ToIndex(sector));
405
406 Writer_WriteByte(writer, (byte) crush);
407
408 Writer_WriteInt32(writer, (int) state);
409 Writer_WriteInt32(writer, newSpecial);
410
411 Writer_WriteInt16(writer, msw->serialIdFor(material));
412
413 Writer_WriteInt16(writer, (int) floorDestHeight);
414 Writer_WriteInt32(writer, FLT2FIX(speed));
415
416 #if __JHEXEN__
417 Writer_WriteInt32(writer, delayCount);
418 Writer_WriteInt32(writer, delayTotal);
419 Writer_WriteInt32(writer, FLT2FIX(stairsDelayHeight));
420 Writer_WriteInt32(writer, FLT2FIX(stairsDelayHeightDelta));
421 Writer_WriteInt32(writer, FLT2FIX(resetHeight));
422 Writer_WriteInt16(writer, resetDelay);
423 Writer_WriteInt16(writer, resetDelayCount);
424 #endif
425 }
426
read(MapStateReader * msr)427 int floor_s::read(MapStateReader *msr)
428 {
429 Reader1 *reader = msr->reader();
430 int mapVersion = msr->mapVersion();
431
432 #if __JHEXEN__
433 if(mapVersion >= 4)
434 #else
435 if(mapVersion >= 5)
436 #endif
437 { // Note: the thinker class byte has already been read.
438 byte ver = Reader_ReadByte(reader); // version byte.
439
440 type = floortype_e(Reader_ReadByte(reader));
441 sector = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader));
442 DENG_ASSERT(sector != 0);
443 crush = dd_bool(Reader_ReadByte(reader));
444 state = floorstate_e(Reader_ReadInt32(reader));
445 newSpecial = Reader_ReadInt32(reader);
446
447 if(ver >= 2)
448 {
449 material = (world_Material *) msr->material(Reader_ReadInt16(reader), 0);
450 }
451 else
452 {
453 // Flat number is an absolute lump index.
454 de::Uri uri("Flats:", CentralLumpIndex()[Reader_ReadInt16(reader)].name().fileNameWithoutExtension());
455 material = (world_Material *)P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(reinterpret_cast<uri_s *>(&uri)));
456 }
457
458 floorDestHeight = (float) Reader_ReadInt16(reader);
459 speed = FIX2FLT(Reader_ReadInt32(reader));
460
461 #if __JHEXEN__
462 delayCount = Reader_ReadInt32(reader);
463 delayTotal = Reader_ReadInt32(reader);
464 stairsDelayHeight = FIX2FLT(Reader_ReadInt32(reader));
465 stairsDelayHeightDelta = FIX2FLT(Reader_ReadInt32(reader));
466 resetHeight = FIX2FLT(Reader_ReadInt32(reader));
467 resetDelay = Reader_ReadInt16(reader);
468 resetDelayCount = Reader_ReadInt16(reader);
469 #endif
470 }
471 else
472 {
473 // Its in the old format which serialized floor_t
474 // Padding at the start (an old thinker_t struct)
475 byte junk[16]; // sizeof thinker_t
476 Reader_Read(reader, junk, 16);
477
478 // Start of used data members.
479 #if __JHEXEN__
480 // A 32bit pointer to sector, serialized.
481 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
482 DENG_ASSERT(sector != 0);
483
484 type = floortype_e(Reader_ReadInt32(reader));
485 crush = Reader_ReadInt32(reader);
486 #else
487 type = floortype_e(Reader_ReadInt32(reader));
488 crush = Reader_ReadInt32(reader);
489
490 // A 32bit pointer to sector, serialized.
491 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
492 DENG_ASSERT(sector != 0);
493 #endif
494 state = floorstate_e(Reader_ReadInt32(reader));
495 newSpecial = Reader_ReadInt32(reader);
496
497 // Flat number is an absolute lump index.
498 de::Uri uri("Flats:", CentralLumpIndex()[Reader_ReadInt16(reader)].name().fileNameWithoutExtension());
499 material = (world_Material *)P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(reinterpret_cast<uri_s *>(&uri)));
500
501 floorDestHeight = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
502 speed = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
503
504 #if __JHEXEN__
505 delayCount = Reader_ReadInt32(reader);
506 delayTotal = Reader_ReadInt32(reader);
507 stairsDelayHeight = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
508 stairsDelayHeightDelta = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
509 resetHeight = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
510 resetDelay = Reader_ReadInt16(reader);
511 resetDelayCount = Reader_ReadInt16(reader);
512 /*textureChange =*/ Reader_ReadByte(reader);
513 #endif
514 }
515
516 P_ToXSector(sector)->specialData = this;
517 thinker.function = T_MoveFloor;
518
519 return true; // Add this thinker.
520 }
521
522 struct findlineinsectorsmallestbottommaterialparams_t
523 {
524 Sector *baseSec;
525 int minSize;
526 Line *foundLine;
527 };
528
findLineInSectorSmallestBottomMaterial(void * ptr,void * context)529 int findLineInSectorSmallestBottomMaterial(void *ptr, void *context)
530 {
531 Line *li = (Line *) ptr;
532 findlineinsectorsmallestbottommaterialparams_t *params = (findlineinsectorsmallestbottommaterialparams_t *) context;
533
534 Sector *frontSec = (Sector *)P_GetPtrp(li, DMU_FRONT_SECTOR);
535 Sector *backSec = (Sector *)P_GetPtrp(li, DMU_BACK_SECTOR);
536
537 if(frontSec && backSec)
538 {
539 Side *side = (Side *)P_GetPtrp(li, DMU_FRONT);
540 world_Material *mat = (world_Material *)P_GetPtrp(side, DMU_BOTTOM_MATERIAL);
541
542 /**
543 * Emulate DOOM.exe behaviour. In the instance where no material is
544 * present, the height is taken from the very first texture.
545 */
546 if(!mat)
547 {
548 uri_s *textureUrn = Uri_NewWithPath2("urn:Textures:0", RC_NULL);
549 mat = DD_MaterialForTextureUri(textureUrn);
550 Uri_Delete(textureUrn);
551 }
552
553 if(mat)
554 {
555 int height = P_GetIntp(mat, DMU_HEIGHT);
556
557 if(height < params->minSize)
558 {
559 params->minSize = height;
560 params->foundLine = li;
561 }
562 }
563
564 side = (Side *)P_GetPtrp(li, DMU_BACK);
565 mat = (world_Material *)P_GetPtrp(side, DMU_BOTTOM_MATERIAL);
566 if(!mat)
567 {
568 uri_s *textureUrn = Uri_NewWithPath2("urn:Textures:0", RC_NULL);
569 mat = DD_MaterialForTextureUri(textureUrn);
570 Uri_Delete(textureUrn);
571 }
572
573 if(mat)
574 {
575 int height = P_GetIntp(mat, DMU_HEIGHT);
576
577 if(height < params->minSize)
578 {
579 params->minSize = height;
580 params->foundLine = li;
581 }
582 }
583 }
584
585 return false; // Continue iteration.
586 }
587
P_FindLineInSectorSmallestBottomMaterial(Sector * sec,int * val)588 Line* P_FindLineInSectorSmallestBottomMaterial(Sector *sec, int *val)
589 {
590 findlineinsectorsmallestbottommaterialparams_t params;
591
592 params.baseSec = sec;
593 params.minSize = DDMAXINT;
594 params.foundLine = NULL;
595 P_Iteratep(sec, DMU_LINE, findLineInSectorSmallestBottomMaterial, ¶ms);
596
597 if(val)
598 *val = params.minSize;
599
600 return params.foundLine;
601 }
602
603 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
604 /**
605 * Find the first sector which shares a border to the specified sector
606 * and whose floor height matches that specified.
607 *
608 * @note Behaviour here is dependant upon the order of the sector-linked
609 * Lines list. This is necessary to emulate the flawed algorithm used in
610 * DOOM.exe In addition, this algorithm was further broken in Heretic as
611 * the test which compares floor heights was removed.
612 *
613 * @warning DO NOT USE THIS ANYWHERE ELSE!
614 */
615
616 typedef struct findfirstneighbouratfloorheightparams_s {
617 Sector* baseSec;
618 coord_t height;
619 Sector* foundSec;
620 } findfirstneighbouratfloorheightparams_t;
621
findFirstNeighbourAtFloorHeight(void * ptr,void * context)622 static int findFirstNeighbourAtFloorHeight(void* ptr, void* context)
623 {
624 Line* ln = (Line*) ptr;
625 findfirstneighbouratfloorheightparams_t* params =
626 (findfirstneighbouratfloorheightparams_t*) context;
627 Sector* other;
628
629 other = P_GetNextSector(ln, params->baseSec);
630 # if __JDOOM__ || __JDOOM64__
631 if(other && FEQUAL(P_GetDoublep(other, DMU_FLOOR_HEIGHT), params->height))
632 # elif __JHERETIC__
633 if(other)
634 # endif
635 {
636 params->foundSec = other;
637 return true; // Stop iteration.
638 }
639
640 return false; // Continue iteration.
641 }
642
findSectorSurroundingAtFloorHeight(Sector * sec,coord_t height)643 static Sector *findSectorSurroundingAtFloorHeight(Sector *sec, coord_t height)
644 {
645 findfirstneighbouratfloorheightparams_t params;
646
647 params.baseSec = sec;
648 params.foundSec = NULL;
649 params.height = height;
650 P_Iteratep(sec, DMU_LINE, findFirstNeighbourAtFloorHeight, ¶ms);
651 return params.foundSec;
652 }
653 #endif
654
655 #if __JHEXEN__
EV_DoFloor(Line *,byte * args,floortype_e floortype)656 int EV_DoFloor(Line * /*line*/, byte* args, floortype_e floortype)
657 #else
658 int EV_DoFloor(Line *line, floortype_e floortype)
659 #endif
660 {
661 #if !__JHEXEN__
662 Sector *frontsector;
663 #endif
664 #if __JHEXEN__
665 int tag = (int) args[0];
666 #else
667 int tag = P_ToXLine(line)->tag;
668 #endif
669
670 #if __JDOOM64__
671 // jd64 > bitmip? wha?
672 coord_t bitmipL = 0, bitmipR = 0;
673 Side *front = (Side *)P_GetPtrp(line, DMU_FRONT);
674 Side *back = (Side *)P_GetPtrp(line, DMU_BACK);
675
676 bitmipL = P_GetDoublep(front, DMU_MIDDLE_MATERIAL_OFFSET_X);
677 if(back)
678 bitmipR = P_GetDoublep(back, DMU_MIDDLE_MATERIAL_OFFSET_X);
679 // < d64tc
680 #endif
681
682 iterlist_t *list = P_GetSectorIterListForTag(tag, false);
683 if(!list) return 0;
684
685 int rtn = 0;
686 floor_t *floor = 0;
687
688 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
689 IterList_RewindIterator(list);
690
691 Sector *sec;
692 while((sec = (Sector *)IterList_MoveIterator(list)))
693 {
694 xsector_t *xsec = P_ToXSector(sec);
695 // If already moving, keep going...
696 if(xsec->specialData)
697 continue;
698 rtn = 1;
699
700 // New floor thinker.
701 floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
702 floor->thinker.function = T_MoveFloor;
703 Thinker_Add(&floor->thinker);
704 xsec->specialData = floor;
705
706 floor->type = floortype;
707 floor->crush = false;
708 #if __JHEXEN__
709 floor->speed = (float) args[1] * (1.0 / 8);
710 if(floortype == FT_LOWERMUL8INSTANT ||
711 floortype == FT_RAISEMUL8INSTANT)
712 {
713 floor->speed = 2000;
714 }
715 #endif
716 switch(floortype)
717 {
718 case FT_LOWER:
719 floor->state = FS_DOWN;
720 floor->sector = sec;
721 #if !__JHEXEN__
722 floor->speed = FLOORSPEED;
723 # if __JDOOM64__
724 floor->speed *= 4;
725 # endif
726 #endif
727 P_FindSectorSurroundingHighestFloor(sec, -500, &floor->floorDestHeight);
728 break;
729
730 case FT_LOWERTOLOWEST:
731 floor->state = FS_DOWN;
732 floor->sector = sec;
733 #if !__JHEXEN__
734 floor->speed = FLOORSPEED;
735 # if __JDOOM64__
736 floor->speed *= 4;
737 # endif
738 #endif
739 P_FindSectorSurroundingLowestFloor(sec,
740 P_GetDoublep(sec, DMU_FLOOR_HEIGHT), &floor->floorDestHeight);
741 break;
742 #if __JHEXEN__
743 case FT_LOWERBYVALUE:
744 floor->state = FS_DOWN;
745 floor->sector = sec;
746 floor->floorDestHeight =
747 P_GetDoublep(sec, DMU_FLOOR_HEIGHT) - (coord_t) args[2];
748 break;
749
750 case FT_LOWERMUL8INSTANT:
751 case FT_LOWERBYVALUEMUL8:
752 floor->state = FS_DOWN;
753 floor->sector = sec;
754 floor->floorDestHeight =
755 P_GetDoublep(sec, DMU_FLOOR_HEIGHT) - (coord_t) args[2] * 8;
756 break;
757 #endif
758 #if !__JHEXEN__
759 case FT_LOWERTURBO:
760 floor->state = FS_DOWN;
761 floor->sector = sec;
762 floor->speed = FLOORSPEED * 4;
763 P_FindSectorSurroundingHighestFloor(sec, -500, &floor->floorDestHeight);
764 # if __JHERETIC__
765 floor->floorDestHeight += 8;
766 # else
767 if(!FEQUAL(floor->floorDestHeight, P_GetDoublep(sec, DMU_FLOOR_HEIGHT)))
768 floor->floorDestHeight += 8;
769 # endif
770 break;
771 #endif
772 #if __JDOOM64__
773 case FT_TOHIGHESTPLUS8: // jd64
774 floor->state = FS_DOWN;
775 floor->sector = sec;
776 floor->speed = FLOORSPEED;
777 P_FindSectorSurroundingHighestFloor(sec, -500, &floor->floorDestHeight);
778 if(!FEQUAL(floor->floorDestHeight, P_GetDoublep(sec, DMU_FLOOR_HEIGHT)))
779 floor->floorDestHeight += 8;
780 break;
781
782 case FT_TOHIGHESTPLUSBITMIP: // jd64
783 if(bitmipR > 0)
784 {
785 floor->state = FS_DOWN;
786 floor->sector = sec;
787 floor->speed = FLOORSPEED * bitmipL;
788 P_FindSectorSurroundingHighestFloor(sec, -500, &floor->floorDestHeight);
789
790 if(!FEQUAL(floor->floorDestHeight, P_GetDoublep(sec, DMU_FLOOR_HEIGHT)))
791 floor->floorDestHeight += bitmipR;
792 }
793 else
794 {
795 floor->state = FS_UP;
796 floor->sector = sec;
797 floor->speed = FLOORSPEED * bitmipL;
798 floor->floorDestHeight =
799 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) - bitmipR;
800 }
801 break;
802
803 case FT_CUSTOMCHANGESEC: // jd64
804 floor->state = FS_UP;
805 floor->sector = sec;
806 floor->speed = FLOORSPEED * 16;
807 floor->floorDestHeight = P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT);
808
809 /// @fixme Should not clear the special like this!
810 P_ToXSector(sec)->special = bitmipR;
811 break;
812 #endif
813 case FT_RAISEFLOORCRUSH:
814 #if __JHEXEN__
815 floor->crush = (int) args[2]; // arg[2] = crushing value
816 #else
817 floor->crush = true;
818 #endif
819 floor->state = FS_UP;
820 floor->sector = sec;
821 #if !__JHEXEN__
822 floor->speed = FLOORSPEED;
823 # if __JDOOM64__
824 floor->speed *= 4;
825 # endif
826 #endif
827 #if __JHEXEN__
828 floor->floorDestHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT)-8;
829 #else
830 P_FindSectorSurroundingLowestCeiling(sec, (coord_t) MAXINT, &floor->floorDestHeight);
831
832 if(floor->floorDestHeight > P_GetDoublep(sec, DMU_CEILING_HEIGHT))
833 floor->floorDestHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT);
834
835 floor->floorDestHeight -= 8 * (floortype == FT_RAISEFLOORCRUSH);
836 #endif
837 break;
838
839 case FT_RAISEFLOOR:
840 floor->state = FS_UP;
841 floor->sector = sec;
842 #if !__JHEXEN__
843 floor->speed = FLOORSPEED;
844 # if __JDOOM64__
845 floor->speed *= 4;
846 # endif
847 #endif
848 P_FindSectorSurroundingLowestCeiling(sec, (coord_t) MAXINT, &floor->floorDestHeight);
849
850 if(floor->floorDestHeight > P_GetDoublep(sec, DMU_CEILING_HEIGHT))
851 floor->floorDestHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT);
852
853 #if !__JHEXEN__
854 floor->floorDestHeight -= 8 * (floortype == FT_RAISEFLOORCRUSH);
855 #endif
856 break;
857
858 #if __JDOOM__ || __JDOOM64__
859 case FT_RAISEFLOORTURBO:
860 floor->state = FS_UP;
861 floor->sector = sec;
862 floor->speed = FLOORSPEED * 4;
863 # if __JDOOM64__
864 floor->speed *= 2;
865 # endif
866 {
867 coord_t floorHeight, nextFloor;
868
869 floorHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
870 if(P_FindSectorSurroundingNextHighestFloor(sec, floorHeight, &nextFloor))
871 floor->floorDestHeight = nextFloor;
872 else
873 floor->floorDestHeight = floorHeight;
874 }
875 break;
876 #endif
877
878 case FT_RAISEFLOORTONEAREST:
879 floor->state = FS_UP;
880 floor->sector = sec;
881 #if !__JHEXEN__
882 floor->speed = FLOORSPEED;
883 # if __JDOOM64__
884 floor->speed *= 8;
885 # endif
886 #endif
887 {
888 coord_t floorHeight, nextFloor;
889
890 floorHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
891 if(P_FindSectorSurroundingNextHighestFloor(sec, floorHeight, &nextFloor))
892 floor->floorDestHeight = nextFloor;
893 else
894 floor->floorDestHeight = floorHeight;
895 }
896 break;
897
898 #if __JHEXEN__
899 case FT_RAISEFLOORBYVALUE:
900 floor->state = FS_UP;
901 floor->sector = sec;
902 floor->floorDestHeight =
903 P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + (coord_t) args[2];
904 break;
905
906 case FT_RAISEMUL8INSTANT:
907 case FT_RAISEBYVALUEMUL8:
908 floor->state = FS_UP;
909 floor->sector = sec;
910 floor->floorDestHeight =
911 P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + (coord_t) args[2] * 8;
912 break;
913
914 case FT_TOVALUEMUL8:
915 floor->sector = sec;
916 floor->floorDestHeight = (coord_t) args[2] * 8;
917 if(args[3])
918 floor->floorDestHeight = -floor->floorDestHeight;
919
920 if(floor->floorDestHeight > P_GetDoublep(sec, DMU_FLOOR_HEIGHT))
921 floor->state = FS_UP;
922 else if(floor->floorDestHeight < P_GetDoublep(sec, DMU_FLOOR_HEIGHT))
923 floor->state = FS_DOWN;
924 else
925 rtn = 0; // Already at lowest position.
926
927 break;
928 #endif
929 #if !__JHEXEN__
930 case FT_RAISE24:
931 floor->state = FS_UP;
932 floor->sector = sec;
933 floor->speed = FLOORSPEED;
934 # if __JDOOM64__
935 floor->speed *= 8;
936 # endif
937 floor->floorDestHeight =
938 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) + 24;
939 break;
940 #endif
941 #if !__JHEXEN__
942 case FT_RAISE24ANDCHANGE:
943 floor->state = FS_UP;
944 floor->sector = sec;
945 floor->speed = FLOORSPEED;
946 # if __JDOOM64__
947 floor->speed *= 8;
948 # endif
949 floor->floorDestHeight =
950 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) + 24;
951
952 frontsector = (Sector *)P_GetPtrp(line, DMU_FRONT_SECTOR);
953
954 P_SetPtrp(sec, DMU_FLOOR_MATERIAL,
955 P_GetPtrp(frontsector, DMU_FLOOR_MATERIAL));
956
957 xsec->special = P_ToXSector(frontsector)->special;
958 break;
959
960 #if __JDOOM__ || __JDOOM64__
961 case FT_RAISE512:
962 floor->state = FS_UP;
963 floor->sector = sec;
964 floor->speed = FLOORSPEED;
965 floor->floorDestHeight =
966 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) + 512;
967 break;
968 #endif
969
970 # if __JDOOM64__
971 case FT_RAISE32: // jd64
972 floor->state = FS_UP;
973 floor->sector = sec;
974 floor->speed = FLOORSPEED * 8;
975 floor->floorDestHeight =
976 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) + 32;
977 break;
978 # endif
979 case FT_RAISETOTEXTURE:
980 {
981 int minSize = DDMAXINT;
982
983 floor->state = FS_UP;
984 floor->sector = sec;
985 floor->speed = FLOORSPEED;
986 P_FindLineInSectorSmallestBottomMaterial(sec, &minSize);
987 floor->floorDestHeight =
988 P_GetDoublep(floor->sector, DMU_FLOOR_HEIGHT) +
989 (coord_t) minSize;
990 }
991 break;
992
993 case FT_LOWERANDCHANGE:
994 floor->state = FS_DOWN;
995 floor->sector = sec;
996 floor->speed = FLOORSPEED;
997 P_FindSectorSurroundingLowestFloor(sec,
998 P_GetDoublep(sec, DMU_FLOOR_HEIGHT), &floor->floorDestHeight);
999 floor->material = (world_Material *)P_GetPtrp(sec, DMU_FLOOR_MATERIAL);
1000
1001 {
1002 Sector* otherSec = findSectorSurroundingAtFloorHeight(sec,
1003 floor->floorDestHeight);
1004
1005 if(otherSec)
1006 {
1007 floor->material = (world_Material *)P_GetPtrp(otherSec, DMU_FLOOR_MATERIAL);
1008 floor->newSpecial = P_ToXSector(otherSec)->special;
1009 }
1010 }
1011 break;
1012 #endif
1013 default:
1014 #if __JHEXEN__
1015 rtn = 0;
1016 #endif
1017 break;
1018 }
1019 }
1020
1021 #if __JHEXEN__
1022 if(rtn && floor)
1023 {
1024 SN_StartSequence((mobj_t *)P_GetPtrp(floor->sector, DMU_EMITTER),
1025 SEQ_PLATFORM + P_ToXSector(floor->sector)->seqType);
1026 }
1027 #endif
1028
1029 return rtn;
1030 }
1031
1032 #if __JHEXEN__
1033 struct findsectorneighborsforstairbuildparams_t
1034 {
1035 int type;
1036 coord_t height;
1037 };
1038
1039 /// @return @c 0= Continue iteration.
findSectorNeighborsForStairBuild(void * ptr,void * context)1040 static int findSectorNeighborsForStairBuild(void *ptr, void *context)
1041 {
1042 Line *li = (Line *) ptr;
1043 findsectorneighborsforstairbuildparams_t *params = (findsectorneighborsforstairbuildparams_t *) context;
1044
1045 Sector *frontSec = (Sector *)P_GetPtrp(li, DMU_FRONT_SECTOR);
1046 if(!frontSec) return false;
1047
1048 Sector *backSec = (Sector *)P_GetPtrp(li, DMU_BACK_SECTOR);
1049 if(!backSec) return false;
1050
1051 xsector_t *xsec = P_ToXSector(frontSec);
1052 if(xsec->special == params->type + STAIR_SECTOR_TYPE && !xsec->specialData &&
1053 P_GetPtrp(frontSec, DMU_FLOOR_MATERIAL) == stairData.material &&
1054 P_GetIntp(frontSec, DMU_VALID_COUNT) != VALIDCOUNT)
1055 {
1056 enqueueStairSector(frontSec, params->type ^ 1, params->height);
1057 P_SetIntp(frontSec, DMU_VALID_COUNT, VALIDCOUNT);
1058 }
1059
1060 xsec = P_ToXSector(backSec);
1061 if(xsec->special == params->type + STAIR_SECTOR_TYPE && !xsec->specialData &&
1062 P_GetPtrp(backSec, DMU_FLOOR_MATERIAL) == stairData.material &&
1063 P_GetIntp(backSec, DMU_VALID_COUNT) != VALIDCOUNT)
1064 {
1065 enqueueStairSector(backSec, params->type ^ 1, params->height);
1066 P_SetIntp(backSec, DMU_VALID_COUNT, VALIDCOUNT);
1067 }
1068
1069 return false;
1070 }
1071 #endif
1072
1073 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
1074 /**
1075 * @note Behaviour here is dependant upon the order of the sector-linked
1076 * LineDefs list. This is necessary to emulate the flawed algorithm used in
1077 * DOOM.exe In addition, this algorithm was further broken in Heretic as the
1078 * test which compares floor heights was removed.
1079 *
1080 * @warning DO NOT USE THIS ANYWHERE ELSE!
1081 */
1082 struct spreadsectorparams_t
1083 {
1084 Sector *baseSec;
1085 world_Material *material;
1086 Sector *foundSec;
1087 coord_t height, stairSize;
1088 };
1089
1090 /// @return 0= continue iteration; otherwise stop.
findAdjacentSectorForSpread(void * ptr,void * context)1091 static int findAdjacentSectorForSpread(void *ptr, void *context)
1092 {
1093 Line *li = (Line *) ptr;
1094 spreadsectorparams_t *params = (spreadsectorparams_t *) context;
1095
1096 if(!(P_ToXLine(li)->flags & ML_TWOSIDED))
1097 return false;
1098
1099 Sector *frontSec = (Sector *)P_GetPtrp(li, DMU_FRONT_SECTOR);
1100 if(!frontSec) return false;
1101
1102 if(params->baseSec != frontSec)
1103 return false;
1104
1105 Sector *backSec = (Sector *)P_GetPtrp(li, DMU_BACK_SECTOR);
1106 if(!backSec) return false;
1107
1108 if(P_GetPtrp(backSec, DMU_FLOOR_MATERIAL) != params->material)
1109 return false;
1110
1111 /**
1112 * @note The placement of this step height addition is vital to ensure
1113 * the exact behaviour of the original DOOM algorithm. Logically this
1114 * should occur after the test below...
1115 */
1116 params->height += params->stairSize;
1117
1118 xsector_t *xsec = P_ToXSector(backSec);
1119 if(xsec->specialData)
1120 return false;
1121
1122 // This looks good.
1123 params->foundSec = backSec;
1124 return true;
1125 }
1126 #endif
1127
1128 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
EV_BuildStairs(Line * line,stair_e type)1129 int EV_BuildStairs(Line *line, stair_e type)
1130 {
1131 xsector_t *xsec;
1132 coord_t height = 0, stairsize = 0;
1133 float speed = 0;
1134
1135 iterlist_t *list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false);
1136 if(!list) return 0;
1137
1138 int rtn = 0;
1139
1140 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1141 IterList_RewindIterator(list);
1142
1143 spreadsectorparams_t params;
1144 Sector *sec;
1145 while((sec = (Sector *)IterList_MoveIterator(list)))
1146 {
1147 xsec = P_ToXSector(sec);
1148
1149 // Already moving? If so, keep going...
1150 if(xsec->specialData)
1151 continue;
1152
1153 // New floor thinker.
1154 rtn = 1;
1155 floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
1156 floor->thinker.function = T_MoveFloor;
1157 Thinker_Add(&floor->thinker);
1158
1159 xsec->specialData = floor;
1160 floor->state = FS_UP;
1161 floor->sector = sec;
1162 switch(type)
1163 {
1164 #if __JHERETIC__
1165 case build8:
1166 stairsize = 8;
1167 break;
1168 case build16:
1169 stairsize = 16;
1170 break;
1171 #else
1172 case build8:
1173 speed = FLOORSPEED * .25;
1174 stairsize = 8;
1175 break;
1176 case turbo16:
1177 speed = FLOORSPEED * 4;
1178 stairsize = 16;
1179 break;
1180 #endif
1181 default:
1182 break;
1183 }
1184 #if __JHERETIC__
1185 floor->type = FT_RAISEBUILDSTEP;
1186 speed = FLOORSPEED;
1187 floor->speed = speed;
1188 #else
1189 floor->speed = speed;
1190 #endif
1191 height = P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + stairsize;
1192 floor->floorDestHeight = height;
1193
1194 // Find next sector to raise.
1195 // 1. Find 2-sided line with a front side in the same sector.
1196 // 2. Other side is the next sector to raise.
1197 params.baseSec = sec;
1198 params.material = (world_Material *)P_GetPtrp(sec, DMU_FLOOR_MATERIAL);
1199 params.foundSec = 0;
1200 params.height = height;
1201 params.stairSize = stairsize;
1202
1203 while(P_Iteratep(params.baseSec, DMU_LINE, findAdjacentSectorForSpread, ¶ms))
1204 {
1205 // We found another sector to spread to.
1206 floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
1207 floor->thinker.function = T_MoveFloor;
1208 Thinker_Add(&floor->thinker);
1209
1210 P_ToXSector(params.foundSec)->specialData = floor;
1211 #if __JHERETIC__
1212 floor->type = FT_RAISEBUILDSTEP;
1213 #endif
1214 floor->state = FS_UP;
1215 floor->speed = speed;
1216 floor->sector = params.foundSec;
1217 floor->floorDestHeight = params.height;
1218
1219 // Prepare for the next pass.
1220 params.baseSec = params.foundSec;
1221 params.foundSec = NULL;
1222 }
1223 }
1224
1225 return rtn;
1226 }
1227 #endif
1228
1229 #if __JHEXEN__
enqueueStairSector(Sector * sec,int type,coord_t height)1230 static void enqueueStairSector(Sector* sec, int type, coord_t height)
1231 {
1232 if((stairQueueTail + 1) % STAIR_QUEUE_SIZE == stairQueueHead)
1233 {
1234 Con_Error("BuildStairs: Too many branches located.\n");
1235 }
1236 stairQueue[stairQueueTail].sector = sec;
1237 stairQueue[stairQueueTail].type = type;
1238 stairQueue[stairQueueTail].height = height;
1239
1240 stairQueueTail = (stairQueueTail + 1) % STAIR_QUEUE_SIZE;
1241 }
1242
dequeueStairSector(int * type,coord_t * height)1243 static Sector* dequeueStairSector(int* type, coord_t* height)
1244 {
1245 Sector* sec;
1246
1247 if(stairQueueHead == stairQueueTail)
1248 {
1249 // Queue is empty.
1250 return NULL;
1251 }
1252
1253 *type = stairQueue[stairQueueHead].type;
1254 *height = stairQueue[stairQueueHead].height;
1255 sec = stairQueue[stairQueueHead].sector;
1256 stairQueueHead = (stairQueueHead + 1) % STAIR_QUEUE_SIZE;
1257
1258 return sec;
1259 }
1260
processStairSector(Sector * sec,int type,coord_t height,stairs_e stairsType,int delay,int resetDelay)1261 static void processStairSector(Sector *sec, int type, coord_t height,
1262 stairs_e stairsType, int delay, int resetDelay)
1263 {
1264 findsectorneighborsforstairbuildparams_t params;
1265
1266 height += stairData.stepDelta;
1267
1268 floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
1269 floor->thinker.function = T_MoveFloor;
1270 Thinker_Add(&floor->thinker);
1271 P_ToXSector(sec)->specialData = floor;
1272 floor->type = FT_RAISEBUILDSTEP;
1273 floor->state = (stairData.direction == -1? FS_DOWN : FS_UP);
1274 floor->sector = sec;
1275 floor->floorDestHeight = height;
1276 switch(stairsType)
1277 {
1278 case STAIRS_NORMAL:
1279 floor->speed = stairData.speed;
1280 if(delay)
1281 {
1282 floor->delayTotal = delay;
1283 floor->stairsDelayHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + stairData.stepDelta;
1284 floor->stairsDelayHeightDelta = stairData.stepDelta;
1285 }
1286 floor->resetDelay = resetDelay;
1287 floor->resetDelayCount = resetDelay;
1288 floor->resetHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
1289 break;
1290
1291 case STAIRS_SYNC:
1292 floor->speed =
1293 stairData.speed * ((height - stairData.startHeight) / stairData.stepDelta);
1294 floor->resetDelay = delay; //arg4
1295 floor->resetDelayCount = delay;
1296 floor->resetHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
1297 break;
1298
1299 default:
1300 break;
1301 }
1302
1303 SN_StartSequence((mobj_t *)P_GetPtrp(sec, DMU_EMITTER),
1304 SEQ_PLATFORM + P_ToXSector(sec)->seqType);
1305
1306 params.type = type;
1307 params.height = height;
1308
1309 // Find all neigboring sectors with sector special equal to type and add
1310 // them to the stairbuild queue.
1311 P_Iteratep(sec, DMU_LINE, findSectorNeighborsForStairBuild, ¶ms);
1312 }
1313 #endif
1314
1315 /**
1316 * @param direction Positive = up. Negative = down.
1317 */
1318 #if __JHEXEN__
EV_BuildStairs(Line *,byte * args,int direction,stairs_e stairsType)1319 int EV_BuildStairs(Line * /*line*/, byte *args, int direction, stairs_e stairsType)
1320 {
1321 // Set global stairs variables
1322 stairData.textureChange = 0;
1323 stairData.direction = direction;
1324 stairData.stepDelta = stairData.direction * (coord_t) args[2];
1325 stairData.speed = (float) args[1] * (1.0 / 8);
1326
1327 int resetDelay = (int) args[4];
1328 int delay = (int) args[3];
1329 if(stairsType == STAIRS_PHASED)
1330 {
1331 stairData.startDelay = stairData.startDelayDelta = (int) args[3];
1332 resetDelay = stairData.startDelayDelta;
1333 delay = 0;
1334 stairData.textureChange = (int) args[4];
1335 }
1336
1337 VALIDCOUNT++;
1338
1339 iterlist_t *list = P_GetSectorIterListForTag((int) args[0], false);
1340 if(!list) return 0;
1341
1342 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1343 IterList_RewindIterator(list);
1344
1345 Sector *sec;
1346 while((sec = (Sector *)IterList_MoveIterator(list)))
1347 {
1348 stairData.material = (world_Material *)P_GetPtrp(sec, DMU_FLOOR_MATERIAL);
1349 stairData.startHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
1350
1351 // ALREADY MOVING? IF SO, KEEP GOING...
1352 if(P_ToXSector(sec)->specialData)
1353 continue; // Already moving, so keep going...
1354
1355 enqueueStairSector(sec, 0, P_GetDoublep(sec, DMU_FLOOR_HEIGHT));
1356 P_ToXSector(sec)->special = 0;
1357 }
1358
1359 int type;
1360 coord_t height;
1361 while((sec = dequeueStairSector(&type, &height)))
1362 {
1363 processStairSector(sec, type, height, stairsType, delay, resetDelay);
1364 }
1365
1366 return 1;
1367 }
1368 #endif
1369
1370 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
1371 struct findfirsttwosidedparams_t
1372 {
1373 Sector *sector;
1374 Line *foundLine;
1375 };
1376
findFirstTwosided(void * ptr,void * context)1377 static int findFirstTwosided(void *ptr, void *context)
1378 {
1379 Line *li = (Line *) ptr;
1380 findfirsttwosidedparams_t *params = (findfirsttwosidedparams_t *) context;
1381
1382 Sector *backSec = (Sector *)P_GetPtrp(li, DMU_BACK_SECTOR);
1383
1384 if(!(P_ToXLine(li)->flags & ML_TWOSIDED))
1385 return false;
1386
1387 if(backSec && !(params->sector && backSec == params->sector))
1388 {
1389 params->foundLine = li;
1390 return true; // Stop iteration, this will do.
1391 }
1392
1393 return false; // Continue iteration.
1394 }
1395 #endif
1396
1397 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
EV_DoDonut(Line * line)1398 int EV_DoDonut(Line *line)
1399 {
1400 iterlist_t *list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false);
1401 if(!list) return 0;
1402
1403 int rtn = 0;
1404
1405 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1406 IterList_RewindIterator(list);
1407
1408 Sector *sec;
1409 while((sec = (Sector *)IterList_MoveIterator(list)))
1410 {
1411 findfirsttwosidedparams_t params;
1412
1413 // Already moving? If so, keep going...
1414 if(P_ToXSector(sec)->specialData)
1415 continue;
1416
1417 rtn = 1;
1418 Sector *outer = 0, *ring = 0;
1419
1420 params.sector = 0;
1421 params.foundLine = 0;
1422
1423 if(P_Iteratep(sec, DMU_LINE, findFirstTwosided, ¶ms))
1424 {
1425 ring = (Sector *)P_GetPtrp(params.foundLine, DMU_BACK_SECTOR);
1426 if(ring == sec)
1427 {
1428 ring = (Sector *)P_GetPtrp(params.foundLine, DMU_FRONT_SECTOR);
1429 }
1430
1431 params.sector = sec;
1432 params.foundLine = NULL;
1433 if(P_Iteratep(ring, DMU_LINE, findFirstTwosided, ¶ms))
1434 {
1435 outer = (Sector *)P_GetPtrp(params.foundLine, DMU_BACK_SECTOR);
1436 }
1437 }
1438
1439 if(outer && ring)
1440 {
1441 // Found both parts of the donut.
1442 coord_t destHeight = P_GetDoublep(outer, DMU_FLOOR_HEIGHT);
1443
1444 // Spawn rising slime.
1445 floor_t *floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
1446 floor->thinker.function = T_MoveFloor;
1447 Thinker_Add(&floor->thinker);
1448
1449 P_ToXSector(ring)->specialData = floor;
1450
1451 floor->type = FT_RAISEDONUT;
1452 floor->crush = false;
1453 floor->state = FS_UP;
1454 floor->sector = ring;
1455 floor->speed = FLOORSPEED * .5;
1456 floor->material = (world_Material *)P_GetPtrp(outer, DMU_FLOOR_MATERIAL);
1457 floor->newSpecial = 0;
1458 floor->floorDestHeight = destHeight;
1459
1460 // Spawn lowering donut-hole.
1461 floor = (floor_t *)Z_Calloc(sizeof(*floor), PU_MAP, 0);
1462 floor->thinker.function = T_MoveFloor;
1463 Thinker_Add(&floor->thinker);
1464
1465 P_ToXSector(sec)->specialData = floor;
1466 floor->type = FT_LOWER;
1467 floor->crush = false;
1468 floor->state = FS_DOWN;
1469 floor->sector = sec;
1470 floor->speed = FLOORSPEED * .5;
1471 floor->floorDestHeight = destHeight;
1472 }
1473 }
1474
1475 return rtn;
1476 }
1477 #endif
1478
1479 #if __JHEXEN__
stopFloorCrush(thinker_t * th,void * context)1480 static int stopFloorCrush(thinker_t *th, void *context)
1481 {
1482 dd_bool *found = (dd_bool *) context;
1483 floor_t *floor = (floor_t *) th;
1484
1485 if(floor->type == FT_RAISEFLOORCRUSH)
1486 {
1487 // Completely remove the crushing floor
1488 SN_StopSequence((mobj_t *)P_GetPtrp(floor->sector, DMU_EMITTER));
1489 P_ToXSector(floor->sector)->specialData = nullptr;
1490 P_NotifySectorFinished(P_ToXSector(floor->sector)->tag);
1491 Thinker_Remove(&floor->thinker);
1492 (*found) = true;
1493 }
1494
1495 return false; // Continue iteration.
1496 }
1497
EV_FloorCrushStop(Line *,byte *)1498 int EV_FloorCrushStop(Line * /*line*/, byte * /*args*/)
1499 {
1500 dd_bool found = false;
1501
1502 Thinker_Iterate(T_MoveFloor, stopFloorCrush, &found);
1503
1504 return (found? 1 : 0);
1505 }
1506 #endif
1507
1508 #if __JHEXEN__ || __JDOOM64__
1509 # if __JHEXEN__
EV_DoFloorAndCeiling(Line * line,byte * args,int ftype,int ctype)1510 int EV_DoFloorAndCeiling(Line *line, byte *args, int ftype, int ctype)
1511 # else
1512 int EV_DoFloorAndCeiling(Line* line, int ftype, int ctype)
1513 # endif
1514 {
1515 # if __JHEXEN__
1516 int tag = (int) args[0];
1517 # else
1518 int tag = P_ToXLine(line)->tag;
1519 # endif
1520
1521 iterlist_t *list = P_GetSectorIterListForTag(tag, false);
1522 if(!list) return 0;
1523
1524 /**
1525 * @attention Kludge:
1526 * Due to the fact that sectors can only have one special thinker
1527 * linked at a time, this routine manually removes the link before
1528 * then creating a second thinker for the sector.
1529 * In order to commonize this we should maintain seperate links in
1530 * xsector_t for each type of special (not thinker type) i.e:
1531 *
1532 * floor, ceiling, lightlevel
1533 *
1534 * @note: Floor and ceiling are capable of moving at different speeds
1535 * and with different target heights, we must remain compatible.
1536 */
1537
1538 # if __JHEXEN__
1539 dd_bool floor = EV_DoFloor(line, args, floortype_e(ftype));
1540 # else
1541 dd_bool floor = EV_DoFloor(line, floortype_e(ftype));
1542 # endif
1543
1544 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1545 IterList_RewindIterator(list);
1546
1547 Sector *sec;
1548 while((sec = (Sector *)IterList_MoveIterator(list)))
1549 {
1550 P_ToXSector(sec)->specialData = 0;
1551 }
1552
1553 # if __JHEXEN__
1554 dd_bool ceiling = EV_DoCeiling(line, args, ceilingtype_e(ctype));
1555 # else
1556 dd_bool ceiling = EV_DoCeiling(line, ceilingtype_e(ctype));
1557 # endif
1558 // < KLUDGE
1559
1560 return (floor | ceiling);
1561 }
1562 #endif
1563