1 /** @file p_ceiling.cpp  Moving ceilings (lowering, crushing, raising).
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-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_ceiling.h"
28 
29 #include "dmu_lib.h"
30 #include "p_mapspec.h"
31 #include "p_sound.h"
32 #include "p_start.h"
33 #include "p_tick.h"
34 
35 // Sounds played by the ceilings when changing state or moving.
36 // jHexen uses sound sequences, so it's are defined as 'SFX_NONE'.
37 #if __JDOOM__
38 # define SFX_CEILINGMOVE        (SFX_STNMOV)
39 # define SFX_CEILINGSTOP        (SFX_PSTOP)
40 #elif __JDOOM64__
41 # define SFX_CEILINGMOVE        (SFX_STNMOV)
42 # define SFX_CEILINGSTOP        (SFX_PSTOP)
43 #elif __JHERETIC__
44 # define SFX_CEILINGMOVE        (SFX_DORMOV)
45 # define SFX_CEILINGSTOP        (SFX_NONE)
46 #elif __JHEXEN__
47 # define SFX_CEILINGMOVE        (SFX_NONE)
48 # define SFX_CEILINGSTOP        (SFX_NONE)
49 #endif
50 
51 /**
52  * Called when a moving ceiling needs to be removed.
53  *
54  * @param ceiling       Ptr to the ceiling to be stopped.
55  */
stopCeiling(ceiling_t * ceiling)56 static void stopCeiling(ceiling_t *ceiling)
57 {
58     P_ToXSector(ceiling->sector)->specialData = nullptr;
59     P_NotifySectorFinished(P_ToXSector(ceiling->sector)->tag);
60     Thinker_Remove(&ceiling->thinker);
61 }
62 
T_MoveCeiling(void * ceilingThinkerPtr)63 void T_MoveCeiling(void *ceilingThinkerPtr)
64 {
65     ceiling_t *ceiling = (ceiling_t *)ceilingThinkerPtr;
66     result_e res;
67 
68     switch(ceiling->state)
69     {
70     case CS_UP: // Going up.
71         res =
72             T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topHeight,
73                         false, 1, 1);
74 
75         // Play a "while-moving" sound?
76 #if !__JHEXEN__
77         if(!(mapTime & 7))
78         {
79 # if __JHERETIC__
80             S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGMOVE);
81 # else
82             switch(ceiling->type)
83             {
84             case CT_SILENTCRUSHANDRAISE:
85                 break;
86             default:
87                 S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGMOVE);
88                 break;
89             }
90 # endif
91         }
92 #endif
93 
94         if(res == pastdest)
95         {
96 #if __JHEXEN__
97             SN_StopSequence((mobj_t *)P_GetPtrp(ceiling->sector, DMU_EMITTER));
98 #endif
99             switch(ceiling->type)
100             {
101 #if !__JHEXEN__
102             case CT_RAISETOHIGHEST:
103 # if __JDOOM64__
104             case CT_CUSTOM: //jd64
105 # endif
106                 stopCeiling(ceiling);
107                 break;
108 # if !__JHERETIC__
109             case CT_SILENTCRUSHANDRAISE:
110                 S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGSTOP);
111 # endif
112             case CT_CRUSHANDRAISEFAST:
113 #endif
114             case CT_CRUSHANDRAISE:
115                 ceiling->state = CS_DOWN;
116 #if __JHEXEN__
117                 ceiling->speed *= 2;
118 #endif
119                 break;
120 
121             default:
122 #if __JHEXEN__
123                 stopCeiling(ceiling);
124 #endif
125                 break;
126             }
127 
128         }
129         break;
130 
131     case CS_DOWN: // Going down.
132         res =
133             T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomHeight,
134                         ceiling->crush, 1, -1);
135 
136         // Play a "while-moving" sound?
137 #if !__JHEXEN__
138         if(!(mapTime & 7))
139         {
140 # if __JHERETIC__
141             S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGMOVE);
142 # else
143             switch(ceiling->type)
144             {
145             case CT_SILENTCRUSHANDRAISE:
146                 break;
147             default:
148                 S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGMOVE);
149             }
150 # endif
151         }
152 #endif
153 
154         if(res == pastdest)
155         {
156 #if __JHEXEN__
157             SN_StopSequence((mobj_t *)P_GetPtrp(ceiling->sector, DMU_EMITTER));
158 #endif
159             switch(ceiling->type)
160             {
161 #if __JDOOM__ || __JDOOM64__
162             case CT_SILENTCRUSHANDRAISE:
163                 S_PlaneSound((Plane *)P_GetPtrp(ceiling->sector, DMU_CEILING_PLANE), SFX_CEILINGSTOP);
164                 ceiling->speed = CEILSPEED;
165                 ceiling->state = CS_UP;
166                 break;
167 #endif
168 
169             case CT_CRUSHANDRAISE:
170 #if __JHEXEN__
171             case CT_CRUSHRAISEANDSTAY:
172 #endif
173 #if __JHEXEN__
174                 ceiling->speed = ceiling->speed * .5;
175 #else
176                 ceiling->speed = CEILSPEED;
177 #endif
178                 ceiling->state = CS_UP;
179                 break;
180 #if !__JHEXEN__
181             case CT_CRUSHANDRAISEFAST:
182                 ceiling->state = CS_UP;
183                 break;
184 
185             case CT_LOWERANDCRUSH:
186             case CT_LOWERTOFLOOR:
187 # if __JDOOM64__
188             case CT_CUSTOM: //jd64
189 # endif
190                 stopCeiling(ceiling);
191                 break;
192 #endif
193 
194             default:
195 #if __JHEXEN__
196                 stopCeiling(ceiling);
197 #endif
198                 break;
199             }
200         }
201         else                    // ( res != pastdest )
202         {
203             if(res == crushed)
204             {
205                 switch(ceiling->type)
206                 {
207 #if __JDOOM__ || __JDOOM64__
208                 case CT_SILENTCRUSHANDRAISE:
209 #endif
210                 case CT_CRUSHANDRAISE:
211                 case CT_LOWERANDCRUSH:
212 #if __JHEXEN__
213                 case CT_CRUSHRAISEANDSTAY:
214 #endif
215 #if !__JHEXEN__
216                     ceiling->speed = CEILSPEED * .125;
217 #endif
218                     break;
219 
220                 default:
221                     break;
222                 }
223             }
224         }
225         break;
226     }
227 }
228 
write(MapStateWriter * msw) const229 void ceiling_s::write(MapStateWriter *msw) const
230 {
231     Writer1 *writer = msw->writer();
232 
233     Writer_WriteByte(writer, 2); // Write a version byte.
234 
235     Writer_WriteByte(writer, (byte) type);
236     Writer_WriteInt32(writer, P_ToIndex(sector));
237 
238     Writer_WriteInt16(writer, (int)bottomHeight);
239     Writer_WriteInt16(writer, (int)topHeight);
240     Writer_WriteInt32(writer, FLT2FIX(speed));
241 
242     Writer_WriteByte(writer, crush);
243 
244     Writer_WriteByte(writer, (byte) state);
245     Writer_WriteInt32(writer, tag);
246     Writer_WriteByte(writer, (byte) oldState);
247 }
248 
read(MapStateReader * msr)249 int ceiling_s::read(MapStateReader *msr)
250 {
251     Reader1 *reader = msr->reader();
252     int mapVersion = msr->mapVersion();
253 
254 #if __JHEXEN__
255     if(mapVersion >= 4)
256 #else
257     if(mapVersion >= 5)
258 #endif
259     {
260         // Note: the thinker class byte has already been read.
261         int ver = Reader_ReadByte(reader); // version byte.
262 
263         thinker.function = T_MoveCeiling;
264 
265 #if !__JHEXEN__
266         // Should we put this into stasis?
267         if(mapVersion == 5)
268         {
269             if(!Reader_ReadByte(reader))
270                 Thinker_SetStasis(&thinker, true);
271         }
272 #endif
273 
274         type         = (ceilingtype_e) Reader_ReadByte(reader);
275 
276         sector       = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader));
277         DENG_ASSERT(sector != 0);
278 
279         bottomHeight = (float) Reader_ReadInt16(reader);
280         topHeight    = (float) Reader_ReadInt16(reader);
281         speed        = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
282 
283         crush        = Reader_ReadByte(reader);
284 
285         if(ver == 2)
286             state    = ceilingstate_e(Reader_ReadByte(reader));
287         else
288             state    = ceilingstate_e(Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP);
289 
290         tag          = Reader_ReadInt32(reader);
291 
292         if(ver == 2)
293             oldState = ceilingstate_e(Reader_ReadByte(reader));
294         else
295             state    = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP);
296     }
297     else
298     {
299         // Its in the old format which serialized ceiling_t
300         // Padding at the start (an old thinker_t struct)
301         int32_t junk[4]; // sizeof thinker_t
302         Reader_Read(reader, (byte *)junk, 16);
303 
304         // Start of used data members.
305 #if __JHEXEN__
306         // A 32bit pointer to sector, serialized.
307         sector       = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader));
308         DENG_ASSERT(sector != 0);
309 
310         type         = ceilingtype_e(Reader_ReadInt32(reader));
311 #else
312         type         = ceilingtype_e(Reader_ReadInt32(reader));
313 
314         // A 32bit pointer to sector, serialized.
315         sector       = (Sector *)P_ToPtr(DMU_SECTOR, Reader_ReadInt32(reader));
316         DENG_ASSERT(sector != 0);
317 #endif
318 
319         bottomHeight = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
320         topHeight    = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
321         speed        = FIX2FLT((fixed_t) Reader_ReadInt32(reader));
322 
323         crush        = Reader_ReadInt32(reader);
324         state        = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP);
325         tag          = Reader_ReadInt32(reader);
326         oldState     = (Reader_ReadInt32(reader) == -1? CS_DOWN : CS_UP);
327 
328         thinker.function = T_MoveCeiling;
329 #if !__JHEXEN__
330         if(junk[2] == 0) // thinker_t::function
331         {
332             Thinker_SetStasis(&thinker, true);
333         }
334 #endif
335     }
336 
337     P_ToXSector(sector)->specialData = this;
338 
339     return true; // Add this thinker.
340 }
341 
342 #if __JDOOM64__
EV_DoCeiling2(Line * line,int tag,float basespeed,ceilingtype_e type)343 static int EV_DoCeiling2(Line *line, int tag, float basespeed, ceilingtype_e type)
344 #elif __JHEXEN__
345 static int EV_DoCeiling2(byte *arg, int tag, float basespeed, ceilingtype_e type)
346 #else
347 static int EV_DoCeiling2(int tag, float basespeed, ceilingtype_e type)
348 #endif
349 {
350     iterlist_t *list = P_GetSectorIterListForTag(tag, false);
351     if(!list) return 0;
352 
353     int rtn = 0;
354 
355     IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
356     IterList_RewindIterator(list);
357 
358     Sector *sec = 0;
359     while((sec = (Sector *)IterList_MoveIterator(list)))
360     {
361         xsector_t *xsec = P_ToXSector(sec);
362 
363         if(xsec->specialData)
364             continue;
365 
366         // new door thinker
367         rtn = 1;
368         ceiling_t *ceiling = (ceiling_t *)Z_Calloc(sizeof(*ceiling), PU_MAP, 0);
369 
370         ceiling->thinker.function = T_MoveCeiling;
371         Thinker_Add(&ceiling->thinker);
372 
373         xsec->specialData = ceiling;
374         ceiling->sector = sec;
375         ceiling->crush = false;
376         ceiling->speed = basespeed;
377 
378         switch(type)
379         {
380 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
381         case CT_CRUSHANDRAISEFAST:
382             ceiling->crush = true;
383             ceiling->topHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT);
384             ceiling->bottomHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + 8;
385 
386             ceiling->state = CS_DOWN;
387             ceiling->speed *= 2;
388             break;
389 #endif
390 #if __JHEXEN__
391         case CT_CRUSHRAISEANDSTAY:
392             ceiling->crush = (int) arg[2];    // arg[2] = crushing value
393             ceiling->topHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT);
394             ceiling->bottomHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT) + 8;
395             ceiling->state = CS_DOWN;
396             break;
397 #endif
398 #if __JDOOM__ || __JDOOM64__
399         case CT_SILENTCRUSHANDRAISE:
400 #endif
401         case CT_CRUSHANDRAISE:
402 #if !__JHEXEN__
403             ceiling->crush = true;
404 #endif
405             ceiling->topHeight = P_GetDoublep(sec, DMU_CEILING_HEIGHT);
406 
407         case CT_LOWERANDCRUSH:
408 #if __JHEXEN__
409             ceiling->crush = (int) arg[2];    // arg[2] = crushing value
410 #endif
411         case CT_LOWERTOFLOOR:
412             ceiling->bottomHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
413 
414             if(type != CT_LOWERTOFLOOR)
415                 ceiling->bottomHeight += 8;
416             ceiling->state = CS_DOWN;
417 #if __JDOOM64__
418             ceiling->speed *= 8; // jd64
419 #endif
420             break;
421 
422         case CT_RAISETOHIGHEST:
423             P_FindSectorSurroundingHighestCeiling(sec, 0, &ceiling->topHeight);
424 #if __JDOOM64__
425             ceiling->topHeight -= 8;   // jd64
426 #endif
427             ceiling->state = CS_UP;
428             break;
429 #if __JDOOM64__
430         case CT_CUSTOM: // jd64
431             {
432             //bitmip? wha?
433             Side *front = (Side *)P_GetPtrp(line, DMU_FRONT);
434             Side *back = (Side *)P_GetPtrp(line, DMU_BACK);
435             coord_t bitmipL = 0, bitmipR = 0;
436 
437             bitmipL = P_GetDoublep(front, DMU_MIDDLE_MATERIAL_OFFSET_X);
438             if(back)
439                 bitmipR = P_GetDoublep(back, DMU_MIDDLE_MATERIAL_OFFSET_X);
440 
441             if(bitmipR > 0)
442             {
443                 P_FindSectorSurroundingHighestCeiling(sec, 0, &ceiling->topHeight);
444                 ceiling->state = CS_UP;
445                 ceiling->speed *= bitmipL;
446                 ceiling->topHeight -= bitmipR;
447             }
448             else
449             {
450                 ceiling->bottomHeight = P_GetDoublep(sec, DMU_FLOOR_HEIGHT);
451                 ceiling->bottomHeight -= bitmipR;
452                 ceiling->state = CS_DOWN;
453                 ceiling->speed *= bitmipL;
454             }
455             }
456 #endif
457 #if __JHEXEN__
458         case CT_LOWERBYVALUE:
459             ceiling->bottomHeight =
460                 P_GetDoublep(sec, DMU_CEILING_HEIGHT) - (coord_t) arg[2];
461             ceiling->state = CS_DOWN;
462             break;
463 
464         case CT_RAISEBYVALUE:
465             ceiling->topHeight =
466                 P_GetDoublep(sec, DMU_CEILING_HEIGHT) + (coord_t) arg[2];
467             ceiling->state = CS_UP;
468             break;
469 
470         case CT_MOVETOVALUEMUL8:
471             {
472             coord_t destHeight = (coord_t) arg[2] * 8;
473 
474             if(arg[3]) // Going down?
475                 destHeight = -destHeight;
476 
477             if(P_GetDoublep(sec, DMU_CEILING_HEIGHT) <= destHeight)
478             {
479                 ceiling->state = CS_UP;
480                 ceiling->topHeight = destHeight;
481                 if(FEQUAL(P_GetDoublep(sec, DMU_CEILING_HEIGHT), destHeight))
482                     rtn = 0;
483             }
484             else if(P_GetDoublep(sec, DMU_CEILING_HEIGHT) > destHeight)
485             {
486                 ceiling->state = CS_DOWN;
487                 ceiling->bottomHeight = destHeight;
488             }
489             break;
490             }
491 #endif
492         default:
493 #if __JHEXEN__
494             rtn = 0;
495 #endif
496             break;
497         }
498 
499         ceiling->tag = xsec->tag;
500         ceiling->type = type;
501 
502 #if __JHEXEN__
503         if(rtn)
504         {
505             SN_StartSequence((mobj_t *)P_GetPtrp(ceiling->sector, DMU_EMITTER),
506                              SEQ_PLATFORM + P_ToXSector(ceiling->sector)->seqType);
507         }
508 #endif
509     }
510     return rtn;
511 }
512 
513 #if __JHEXEN__
EV_DoCeiling(Line *,byte * args,ceilingtype_e type)514 int EV_DoCeiling(Line * /*line*/, byte *args, ceilingtype_e type)
515 #else
516 int EV_DoCeiling(Line *line, ceilingtype_e type)
517 #endif
518 {
519 #if __JHEXEN__
520     return EV_DoCeiling2(args, (int) args[0], (float) args[1] * (1.0 / 8),
521                          type);
522 #else
523     int rtn = 0;
524     // Reactivate in-stasis ceilings...for certain types.
525     switch(type)
526     {
527     case CT_CRUSHANDRAISEFAST:
528 # if __JDOOM__ || __JDOOM64__
529     case CT_SILENTCRUSHANDRAISE:
530 # endif
531     case CT_CRUSHANDRAISE:
532         rtn = P_CeilingActivate(P_ToXLine(line)->tag);
533         break;
534 
535     default:
536         break;
537     }
538 # if __JDOOM64__
539     return EV_DoCeiling2(line, P_ToXLine(line)->tag, CEILSPEED, type) || rtn;
540 # else
541     return EV_DoCeiling2(P_ToXLine(line)->tag, CEILSPEED, type) || rtn;
542 # endif
543 #endif
544 }
545 
546 #if !__JHEXEN__
547 struct activateceilingparams_t
548 {
549     short tag;
550     int count;
551 };
552 
activateCeiling(thinker_t * th,void * context)553 static int activateCeiling(thinker_t *th, void *context)
554 {
555     ceiling_t *ceiling = (ceiling_t *) th;
556     activateceilingparams_t *params = (activateceilingparams_t *) context;
557 
558     if(ceiling->tag == (int) params->tag && Thinker_InStasis(&ceiling->thinker))
559     {
560         ceiling->state = ceiling->oldState;
561         Thinker_SetStasis(&ceiling->thinker, false);
562         params->count++;
563     }
564 
565     return false; // Continue iteration.
566 }
567 
P_CeilingActivate(short tag)568 int P_CeilingActivate(short tag)
569 {
570     activateceilingparams_t params;
571 
572     params.tag = tag;
573     params.count = 0;
574     Thinker_Iterate(T_MoveCeiling, activateCeiling, &params);
575 
576     return params.count;
577 }
578 #endif
579 
580 struct deactivateceilingparams_t
581 {
582     short tag;
583     int count;
584 };
585 
deactivateCeiling(thinker_t * th,void * context)586 static int deactivateCeiling(thinker_t *th, void *context)
587 {
588     ceiling_t *ceiling = (ceiling_t *) th;
589     deactivateceilingparams_t *params = (deactivateceilingparams_t *) context;
590 
591 #if __JHEXEN__
592     if(ceiling->tag == (int) params->tag)
593     {
594         // Destroy it.
595         SN_StopSequence((mobj_t *)P_GetPtrp(ceiling->sector, DMU_EMITTER));
596         stopCeiling(ceiling);
597         params->count++;
598         return true; // Stop iteration.
599     }
600 #else
601     if(!Thinker_InStasis(&ceiling->thinker) && ceiling->tag == (int) params->tag)
602     {
603         // Put it into stasis.
604         ceiling->oldState = ceiling->state;
605         Thinker_SetStasis(&ceiling->thinker, true);
606         params->count++;
607     }
608 #endif
609     return false; // Continue iteration.
610 }
611 
P_CeilingDeactivate(short tag)612 int P_CeilingDeactivate(short tag)
613 {
614     deactivateceilingparams_t params;
615 
616     params.tag = tag;
617     params.count = 0;
618     Thinker_Iterate(T_MoveCeiling, deactivateCeiling, &params);
619 
620     return params.count;
621 }
622