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 = &sectors[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 = &sectors[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 = &sectors[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