1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 // Floor animation: raising stairs.
17 //
18
19
20
21 #include "z_zone.h"
22 #include "doomdef.h"
23 #include "p_local.h"
24
25 #include "s_sound.h"
26
27 // State.
28 #include "doomstat.h"
29 #include "r_state.h"
30 // Data.
31 #include "sounds.h"
32
33 //e6y
34 #define STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE 10
35
36 //
37 // FLOORS
38 //
39
40 //
41 // Move a plane (floor or ceiling) and check for crushing
42 //
43 result_e
T_MovePlane(sector_t * sector,fixed_t speed,fixed_t dest,boolean crush,int floorOrCeiling,int direction)44 T_MovePlane
45 ( sector_t* sector,
46 fixed_t speed,
47 fixed_t dest,
48 boolean crush,
49 int floorOrCeiling,
50 int direction )
51 {
52 boolean flag;
53 fixed_t lastpos;
54
55 // [AM] Store old sector heights for interpolation.
56 sector->oldfloorheight = sector->floorheight;
57 sector->oldceilingheight = sector->ceilingheight;
58 sector->oldgametic = gametic;
59
60 switch(floorOrCeiling)
61 {
62 case 0:
63 // FLOOR
64 switch(direction)
65 {
66 case -1:
67 // DOWN
68 if (sector->floorheight - speed < dest)
69 {
70 lastpos = sector->floorheight;
71 sector->floorheight = dest;
72 flag = P_ChangeSector(sector,crush);
73 if (flag == true)
74 {
75 sector->floorheight =lastpos;
76 P_ChangeSector(sector,crush);
77 //return crushed;
78 }
79 return pastdest;
80 }
81 else
82 {
83 lastpos = sector->floorheight;
84 sector->floorheight -= speed;
85 flag = P_ChangeSector(sector,crush);
86 if (flag == true)
87 {
88 sector->floorheight = lastpos;
89 P_ChangeSector(sector,crush);
90 return crushed;
91 }
92 }
93 break;
94
95 case 1:
96 // UP
97 if (sector->floorheight + speed > dest)
98 {
99 lastpos = sector->floorheight;
100 sector->floorheight = dest;
101 flag = P_ChangeSector(sector,crush);
102 if (flag == true)
103 {
104 sector->floorheight = lastpos;
105 P_ChangeSector(sector,crush);
106 //return crushed;
107 }
108 return pastdest;
109 }
110 else
111 {
112 // COULD GET CRUSHED
113 lastpos = sector->floorheight;
114 sector->floorheight += speed;
115 flag = P_ChangeSector(sector,crush);
116 if (flag == true)
117 {
118 if (crush == true)
119 return crushed;
120 sector->floorheight = lastpos;
121 P_ChangeSector(sector,crush);
122 return crushed;
123 }
124 }
125 break;
126 }
127 break;
128
129 case 1:
130 // CEILING
131 switch(direction)
132 {
133 case -1:
134 // DOWN
135 if (sector->ceilingheight - speed < dest)
136 {
137 lastpos = sector->ceilingheight;
138 sector->ceilingheight = dest;
139 flag = P_ChangeSector(sector,crush);
140
141 if (flag == true)
142 {
143 sector->ceilingheight = lastpos;
144 P_ChangeSector(sector,crush);
145 //return crushed;
146 }
147 return pastdest;
148 }
149 else
150 {
151 // COULD GET CRUSHED
152 lastpos = sector->ceilingheight;
153 sector->ceilingheight -= speed;
154 flag = P_ChangeSector(sector,crush);
155
156 if (flag == true)
157 {
158 if (crush == true)
159 return crushed;
160 sector->ceilingheight = lastpos;
161 P_ChangeSector(sector,crush);
162 return crushed;
163 }
164 }
165 break;
166
167 case 1:
168 // UP
169 if (sector->ceilingheight + speed > dest)
170 {
171 lastpos = sector->ceilingheight;
172 sector->ceilingheight = dest;
173 flag = P_ChangeSector(sector,crush);
174 if (flag == true)
175 {
176 sector->ceilingheight = lastpos;
177 P_ChangeSector(sector,crush);
178 //return crushed;
179 }
180 return pastdest;
181 }
182 else
183 {
184 lastpos = sector->ceilingheight;
185 sector->ceilingheight += speed;
186 flag = P_ChangeSector(sector,crush);
187 // UNUSED
188 #if 0
189 if (flag == true)
190 {
191 sector->ceilingheight = lastpos;
192 P_ChangeSector(sector,crush);
193 return crushed;
194 }
195 #endif
196 }
197 break;
198 }
199 break;
200
201 }
202 return ok;
203 }
204
205
206 //
207 // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
208 //
T_MoveFloor(floormove_t * floor)209 void T_MoveFloor(floormove_t* floor)
210 {
211 result_e res;
212
213 res = T_MovePlane(floor->sector,
214 floor->speed,
215 floor->floordestheight,
216 floor->crush,0,floor->direction);
217
218 if (!(leveltime&7))
219 S_StartSound(&floor->sector->soundorg, sfx_stnmov);
220
221 if (res == pastdest)
222 {
223 floor->sector->specialdata = NULL;
224
225 if (floor->direction == 1)
226 {
227 switch(floor->type)
228 {
229 case donutRaise:
230 floor->sector->special = floor->newspecial;
231 floor->sector->floorpic = floor->texture;
232 default:
233 break;
234 }
235 }
236 else if (floor->direction == -1)
237 {
238 switch(floor->type)
239 {
240 case lowerAndChange:
241 floor->sector->special = floor->newspecial;
242 floor->sector->floorpic = floor->texture;
243 default:
244 break;
245 }
246 }
247 P_RemoveThinker(&floor->thinker);
248
249 S_StartSound(&floor->sector->soundorg, sfx_pstop);
250 }
251
252 }
253
254 // [crispy] easter egg: homage to an old friend (thinker)
T_MoveGoobers(floormove_t * floor)255 void T_MoveGoobers (floormove_t *floor)
256 {
257 result_e res1, res2;
258
259 // [crispy] one thinker for the floors ...
260 res1 = T_MovePlane(floor->sector, 2 * FLOORSPEED, 0,
261 true, 0, (floor->direction & 1) * 2 - 1);
262 // [crispy] ... and one for the ceilings
263 // * floordestheight is actually the ceiling destination height (either 0 or 128)
264 // * the 5th argument is "floorOrCeiling"
265 // * the actual direction is given by the second-lowest bit of the "direction" field
266 res2 = T_MovePlane(floor->sector, 2 * FLOORSPEED, floor->floordestheight,
267 true, 1, (floor->direction >> 1) * 2 - 1);
268
269 if (!(leveltime & 7))
270 {
271 S_StartSound(&floor->sector->soundorg, sfx_stnmov);
272 }
273
274 // [crispy] remove thinker once both the sector's floor and ceiling
275 // have reached their respective destination heights
276 if ((res1 & res2) == pastdest)
277 {
278 floor->sector->specialdata = NULL;
279 P_RemoveThinker(&floor->thinker);
280
281 S_StartSound(&floor->sector->soundorg, sfx_pstop);
282 }
283 }
284
285 // [crispy] easter egg: homage to an old friend
EV_DoGoobers(void)286 void EV_DoGoobers (void)
287 {
288 int i;
289
290 for (i = 0; i < numsectors; i++)
291 {
292 sector_t* sec;
293 floormove_t* floor;
294
295 sec = §ors[i];
296
297 // [crispy] remove thinker for sectors that are already moving
298 if (sec->specialdata)
299 {
300 floor = sec->specialdata;
301 P_RemoveThinker(&floor->thinker);
302 sec->specialdata = NULL;
303 }
304
305 floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
306 P_AddThinker(&floor->thinker);
307 sec->specialdata = floor;
308 floor->thinker.function.acp1 = (actionf_p1) T_MoveGoobers;
309 floor->sector = sec;
310 // [crispy] actually destination ceilingheight here (destination floorheight is always 0),
311 // leave destination ceilingheight for untagged closed sectors (i.e. DR-type doors) at 0,
312 // for all others set to 128
313 floor->floordestheight = (!sec->tag &&
314 sec->interpceilingheight == sec->interpfloorheight) ? 0 : 128 * FRACUNIT;
315 // [crispy] the lowest bit determines floor direction (i.e. 1 means "up" for floorheight < 0),
316 // the second-lowest bit determines ceiling direction (e.g. if ceiling height is below its destination height)
317 floor->direction = (sec->floorheight < 0) |
318 (sec->ceilingheight < floor->floordestheight) << 1;
319 }
320 }
321
322 //
323 // HANDLE FLOOR TYPES
324 //
325 int
EV_DoFloor(line_t * line,floor_e floortype)326 EV_DoFloor
327 ( line_t* line,
328 floor_e floortype )
329 {
330 int secnum;
331 int rtn;
332 int i;
333 sector_t* sec;
334 floormove_t* floor;
335
336 secnum = -1;
337 rtn = 0;
338 while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
339 {
340 sec = §ors[secnum];
341
342 // ALREADY MOVING? IF SO, KEEP GOING...
343 if (sec->specialdata)
344 continue;
345
346 // new floor thinker
347 rtn = 1;
348 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
349 P_AddThinker (&floor->thinker);
350 sec->specialdata = floor;
351 floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
352 floor->type = floortype;
353 floor->crush = false;
354
355 switch(floortype)
356 {
357 case lowerFloor:
358 floor->direction = -1;
359 floor->sector = sec;
360 floor->speed = FLOORSPEED;
361 floor->floordestheight =
362 P_FindHighestFloorSurrounding(sec);
363 break;
364
365 case lowerFloorToLowest:
366 floor->direction = -1;
367 floor->sector = sec;
368 floor->speed = FLOORSPEED;
369 floor->floordestheight =
370 P_FindLowestFloorSurrounding(sec);
371 break;
372
373 case turboLower:
374 floor->direction = -1;
375 floor->sector = sec;
376 floor->speed = FLOORSPEED * 4;
377 floor->floordestheight =
378 P_FindHighestFloorSurrounding(sec);
379 if (gameversion <= exe_doom_1_2 ||
380 floor->floordestheight != sec->floorheight)
381 floor->floordestheight += 8*FRACUNIT;
382 break;
383
384 case raiseFloorCrush:
385 floor->crush = true;
386 case raiseFloor:
387 floor->direction = 1;
388 floor->sector = sec;
389 floor->speed = FLOORSPEED;
390 floor->floordestheight =
391 P_FindLowestCeilingSurrounding(sec);
392 if (floor->floordestheight > sec->ceilingheight)
393 floor->floordestheight = sec->ceilingheight;
394 floor->floordestheight -= (8*FRACUNIT)*
395 (floortype == raiseFloorCrush);
396 break;
397
398 case raiseFloorTurbo:
399 floor->direction = 1;
400 floor->sector = sec;
401 floor->speed = FLOORSPEED*4;
402 floor->floordestheight =
403 P_FindNextHighestFloor(sec,sec->floorheight);
404 break;
405
406 case raiseFloorToNearest:
407 floor->direction = 1;
408 floor->sector = sec;
409 floor->speed = FLOORSPEED;
410 floor->floordestheight =
411 P_FindNextHighestFloor(sec,sec->floorheight);
412 break;
413
414 case raiseFloor24:
415 floor->direction = 1;
416 floor->sector = sec;
417 floor->speed = FLOORSPEED;
418 floor->floordestheight = floor->sector->floorheight +
419 24 * FRACUNIT;
420 break;
421 case raiseFloor512:
422 floor->direction = 1;
423 floor->sector = sec;
424 floor->speed = FLOORSPEED;
425 floor->floordestheight = floor->sector->floorheight +
426 512 * FRACUNIT;
427 break;
428
429 case raiseFloor24AndChange:
430 floor->direction = 1;
431 floor->sector = sec;
432 floor->speed = FLOORSPEED;
433 floor->floordestheight = floor->sector->floorheight +
434 24 * FRACUNIT;
435 sec->floorpic = line->frontsector->floorpic;
436 sec->special = line->frontsector->special;
437 break;
438
439 case raiseToTexture:
440 {
441 int minsize = INT_MAX;
442 side_t* side;
443
444 floor->direction = 1;
445 floor->sector = sec;
446 floor->speed = FLOORSPEED;
447 for (i = 0; i < sec->linecount; i++)
448 {
449 if (twoSided (secnum, i) )
450 {
451 side = getSide(secnum,i,0);
452 if (side->bottomtexture >= 0)
453 if (textureheight[side->bottomtexture] <
454 minsize)
455 minsize =
456 textureheight[side->bottomtexture];
457 side = getSide(secnum,i,1);
458 if (side->bottomtexture >= 0)
459 if (textureheight[side->bottomtexture] <
460 minsize)
461 minsize =
462 textureheight[side->bottomtexture];
463 }
464 }
465 floor->floordestheight =
466 floor->sector->floorheight + minsize;
467 }
468 break;
469
470 case lowerAndChange:
471 floor->direction = -1;
472 floor->sector = sec;
473 floor->speed = FLOORSPEED;
474 floor->floordestheight =
475 P_FindLowestFloorSurrounding(sec);
476 floor->texture = sec->floorpic;
477
478 for (i = 0; i < sec->linecount; i++)
479 {
480 if ( twoSided(secnum, i) )
481 {
482 if (getSide(secnum,i,0)->sector-sectors == secnum)
483 {
484 sec = getSector(secnum,i,1);
485
486 if (sec->floorheight == floor->floordestheight)
487 {
488 floor->texture = sec->floorpic;
489 floor->newspecial = sec->special;
490 break;
491 }
492 }
493 else
494 {
495 sec = getSector(secnum,i,0);
496
497 if (sec->floorheight == floor->floordestheight)
498 {
499 floor->texture = sec->floorpic;
500 floor->newspecial = sec->special;
501 break;
502 }
503 }
504 }
505 }
506 default:
507 break;
508 }
509 }
510 return rtn;
511 }
512
513
514
515
516 //
517 // BUILD A STAIRCASE!
518 //
519 int
EV_BuildStairs(line_t * line,stair_e type)520 EV_BuildStairs
521 ( line_t* line,
522 stair_e type )
523 {
524 int secnum;
525 int height;
526 int i;
527 int newsecnum;
528 int texture;
529 int ok;
530 int rtn;
531
532 sector_t* sec;
533 sector_t* tsec;
534
535 floormove_t* floor;
536
537 fixed_t stairsize = 0;
538 fixed_t speed = 0;
539
540 secnum = -1;
541 rtn = 0;
542 while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
543 {
544 sec = §ors[secnum];
545
546 // ALREADY MOVING? IF SO, KEEP GOING...
547 if (sec->specialdata)
548 continue;
549
550 // new floor thinker
551 rtn = 1;
552 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
553 P_AddThinker (&floor->thinker);
554 sec->specialdata = floor;
555 floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
556 floor->direction = 1;
557 floor->sector = sec;
558 switch(type)
559 {
560 case build8:
561 speed = FLOORSPEED/4;
562 stairsize = 8*FRACUNIT;
563 break;
564 case turbo16:
565 speed = FLOORSPEED*4;
566 stairsize = 16*FRACUNIT;
567 break;
568 }
569 floor->speed = speed;
570 height = sec->floorheight + stairsize;
571 floor->floordestheight = height;
572 // Initialize
573 floor->type = lowerFloor;
574 // e6y
575 // Uninitialized crush field will not be equal to 0 or 1 (true)
576 // with high probability. So, initialize it with any other value
577 floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE;
578
579 texture = sec->floorpic;
580
581 // Find next sector to raise
582 // 1. Find 2-sided line with same sector side[0]
583 // 2. Other side is the next sector to raise
584 do
585 {
586 ok = 0;
587 for (i = 0;i < sec->linecount;i++)
588 {
589 if ( !((sec->lines[i])->flags & ML_TWOSIDED) )
590 continue;
591
592 tsec = (sec->lines[i])->frontsector;
593 newsecnum = tsec-sectors;
594
595 if (secnum != newsecnum)
596 continue;
597
598 tsec = (sec->lines[i])->backsector;
599 newsecnum = tsec - sectors;
600
601 if (tsec->floorpic != texture)
602 continue;
603
604 height += stairsize;
605
606 if (tsec->specialdata)
607 continue;
608
609 sec = tsec;
610 secnum = newsecnum;
611 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
612
613 P_AddThinker (&floor->thinker);
614
615 sec->specialdata = floor;
616 floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
617 floor->direction = 1;
618 floor->sector = sec;
619 floor->speed = speed;
620 floor->floordestheight = height;
621 // Initialize
622 floor->type = lowerFloor;
623 // e6y
624 // Uninitialized crush field will not be equal to 0 or 1 (true)
625 // with high probability. So, initialize it with any other value
626 floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE;
627 ok = 1;
628 break;
629 }
630 } while(ok);
631 }
632 return rtn;
633 }
634
635