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, ¶ms);
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, ¶ms);
619
620 return params.count;
621 }
622