1 /* Emacs style mode select   -*- C++ -*-
2  *-----------------------------------------------------------------------------
3  *
4  *
5  *  PrBoom: a Doom port merged with LxDoom and LSDLDoom
6  *  based on BOOM, a modified and improved DOOM engine
7  *  Copyright (C) 1999 by
8  *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9  *  Copyright (C) 1999-2000 by
10  *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11  *  Copyright 2005, 2006 by
12  *  Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  *  02111-1307, USA.
28  *
29  * DESCRIPTION:
30  *  Plats (i.e. elevator platforms) code, raising/lowering.
31  *
32  *-----------------------------------------------------------------------------*/
33 
34 #include "doomstat.h"
35 #include "m_random.h"
36 #include "r_main.h"
37 #include "p_spec.h"
38 #include "p_tick.h"
39 #include "s_sound.h"
40 #include "sounds.h"
41 
42 platlist_t *activeplats;       // killough 2/14/98: made global again
43 
44 //
45 // T_PlatRaise()
46 //
47 // Action routine to move a plat up and down
48 //
49 // Passed a plat structure containing all pertinent information about the move
50 // No return
51 //
52 // jff 02/08/98 all cases with labels beginning with gen added to support
53 // generalized line type behaviors.
54 
T_PlatRaise(plat_t * plat)55 void T_PlatRaise(plat_t* plat)
56 {
57    result_e      res;
58 
59    /* handle plat moving, up, down, waiting, or in stasis, */
60    switch(plat->status)
61    {
62       case PLAT_UP: // plat moving up
63          res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1);
64 
65          // if a pure raise type, make the plat moving sound
66          if (plat->type == raiseAndChange
67                || plat->type == raiseToNearestAndChange)
68          {
69             if (!(leveltime&7))
70                S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov);
71          }
72 
73          // if encountered an obstacle, and not a crush type, reverse direction
74          if (res == RES_CRUSHED && (!plat->crush))
75          {
76             plat->count  = plat->wait;
77             plat->status = PLAT_DOWN;
78             S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart);
79          }
80          else if (res == RES_PASTDEST) // end of stroke
81          {
82             /* if not an instant toggle type, wait, make plat stop sound */
83             if (plat->type!=toggleUpDn)
84             {
85                plat->count  = plat->wait;
86                plat->status = PLAT_WAITING;
87                S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop);
88             }
89             else /* else go into stasis awaiting next toggle activation */
90             {
91                plat->oldstatus = plat->status;        /* jff 3/14/98 after action wait */
92                plat->status    = PLAT_IN_STASIS;      /* for reactivation of toggle */
93             }
94 
95             /* lift types and pure raise types are done at end of up stroke
96              * only the perpetual type waits then goes back up */
97             switch(plat->type)
98             {
99                case blazeDWUS:
100                case downWaitUpStay:
101                case raiseAndChange:
102                case raiseToNearestAndChange:
103                case genLift:
104                   P_RemoveActivePlat(plat);     // killough
105                default:
106                   break;
107             }
108          }
109          break;
110 
111       case PLAT_DOWN: // plat moving down
112          res = T_MovePlane(plat->sector,plat->speed,plat->low,FALSE,0,-1);
113 
114          // handle reaching end of down stroke
115          if (res == RES_PASTDEST)
116          {
117             /* if not an instant toggle, start waiting, make plat stop sound */
118             if (plat->type!=toggleUpDn) /* jff 3/14/98 toggle up down */
119             {                           /* is silent, instant, no waiting */
120                plat->count  = plat->wait;
121                plat->status = PLAT_WAITING;
122                S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop);
123             }
124             else // instant toggles go into stasis awaiting next activation
125             {
126                plat->oldstatus = plat->status;//jff 3/14/98 after action wait
127                plat->status    = PLAT_IN_STASIS;      //for reactivation of toggle
128             }
129 
130             //jff 1/26/98 remove the plat if it bounced so it can be tried again
131             //only affects plats that raise and bounce
132             //killough 1/31/98: relax compatibility to demo_compatibility
133 
134             // remove the plat if its a pure raise type
135             if (!comp[comp_floors])
136             {
137                switch(plat->type)
138                {
139                   case raiseAndChange:
140                   case raiseToNearestAndChange:
141                      P_RemoveActivePlat(plat);
142                   default:
143                      break;
144                }
145             }
146          }
147          break;
148 
149       case PLAT_WAITING:
150          if (!--plat->count)  // downcount and check for delay elapsed
151          {
152             if (plat->sector->floorheight == plat->low)
153                plat->status = PLAT_UP;     // if at bottom, start up
154             else
155                plat->status = PLAT_DOWN;   // if at top, start down
156 
157 #ifdef HEXEN
158             SN_StartSequence((mobj_t *)&plat->sector->soundorg,
159                   SEQ_PLATFORM+plat->sector->seqType);
160 #else
161             S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart);
162 #endif
163          }
164          break; //jff 1/27/98 don't pickup code added later to in_stasis
165 
166       case PLAT_IN_STASIS: // do nothing if in stasis
167          break;
168    }
169 }
170 
171 
172 //
173 // EV_DoPlat
174 //
175 // Handle Plat linedef types
176 //
177 // Passed the linedef that activated the plat, the type of plat action,
178 // and for some plat types, an amount to raise
179 // Returns TRUE if a thinker is started, or restarted from stasis
180 //
EV_DoPlat(line_t * line,plattype_e type,int amount)181 int EV_DoPlat
182 ( line_t*       line,
183   plattype_e    type,
184   int           amount )
185 {
186   plat_t* plat;
187   int             secnum = -1;
188   int                rtn = 0;
189 
190 #ifndef HEXEN
191   /* Activate all <type> plats that are in_stasis */
192   switch(type)
193   {
194     case perpetualRaise:
195       P_ActivateInStasis(line->tag);
196       break;
197 
198     case toggleUpDn:
199       P_ActivateInStasis(line->tag);
200       rtn=1;
201       break;
202 
203     default:
204       break;
205   }
206 #endif
207 
208   /* act on all sectors tagged the same as the activating linedef */
209   while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
210   {
211      sector_t *sec = &sectors[secnum];
212 
213 #ifndef HEXEN
214      /* don't start a second floor function if already moving */
215      if (P_SectorActive(floor_special,sec)) /* jff 2/23/98 multiple thinkers */
216         continue;
217 #endif
218 
219      /* Create a thinker */
220      rtn = 1;
221      plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0);
222      memset(plat, 0, sizeof(*plat));
223      P_AddThinker(&plat->thinker);
224 
225      plat->type              = type;
226      plat->sector            = sec;
227      plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers
228      plat->thinker.function  = T_PlatRaise;
229      plat->crush             = false;
230      plat->tag               = line->tag;
231 
232      /* jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then
233       * going down forever -- default low to plat height when triggered */
234      plat->low              = sec->floorheight;
235 
236      /* set up plat according to type */
237      switch(type)
238      {
239         case raiseToNearestAndChange:
240            plat->speed = PLATSPEED/2;
241            sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
242            plat->high    = P_FindNextHighestFloor(sec,sec->floorheight);
243            plat->wait    = 0;
244            plat->status  = PLAT_UP;
245            sec->special  = 0;
246            //jff 3/14/98 clear old field as well
247            sec->oldspecial = 0;
248 
249            S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov);
250            break;
251 
252         case raiseAndChange:
253            plat->speed   = PLATSPEED/2;
254            sec->floorpic = sides[line->sidenum[0]].sector->floorpic;
255            plat->high    = sec->floorheight + amount*FRACUNIT;
256            plat->wait    = 0;
257            plat->status  = PLAT_UP;
258 
259            S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov);
260            break;
261 
262         case downWaitUpStay:
263            plat->speed = PLATSPEED * 4;
264            plat->low = P_FindLowestFloorSurrounding(sec);
265 
266            if (plat->low > sec->floorheight)
267               plat->low  = sec->floorheight;
268 
269            plat->high   = sec->floorheight;
270            plat->wait   = 35*PLATWAIT;
271            plat->status = PLAT_DOWN;
272            S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
273            break;
274 
275         case blazeDWUS:
276            plat->speed = PLATSPEED * 8;
277            plat->low = P_FindLowestFloorSurrounding(sec);
278 
279            if (plat->low > sec->floorheight)
280               plat->low = sec->floorheight;
281 
282            plat->high   = sec->floorheight;
283            plat->wait   = 35*PLATWAIT;
284            plat->status = PLAT_DOWN;
285            S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
286            break;
287 
288         case perpetualRaise:
289            plat->speed = PLATSPEED;
290            plat->low = P_FindLowestFloorSurrounding(sec);
291 
292            if (plat->low > sec->floorheight)
293               plat->low = sec->floorheight;
294 
295            plat->high = P_FindHighestFloorSurrounding(sec);
296 
297            if (plat->high < sec->floorheight)
298               plat->high = sec->floorheight;
299 
300            plat->wait = 35*PLATWAIT;
301            plat->status = P_Random(pr_plats)&1;
302 
303            S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart);
304            break;
305 
306         case toggleUpDn: //jff 3/14/98 add new type to support instant toggle
307            plat->speed = PLATSPEED;  //not used
308            plat->wait = 35*PLATWAIT; //not used
309            plat->crush = TRUE; //jff 3/14/98 crush anything in the way
310 
311            // set up toggling between ceiling, floor inclusive
312            plat->low    = sec->ceilingheight;
313            plat->high   = sec->floorheight;
314            plat->status = PLAT_DOWN;
315            break;
316 
317         default:
318            break;
319      }
320      P_AddActivePlat(plat);  // add plat to list of active plats
321   }
322 
323   return rtn;
324 }
325 
326 // The following were all rewritten by Lee Killough
327 // to use the new structure which places no limits
328 // on active plats. It also avoids spending as much
329 // time searching for active plats. Previously a
330 // fixed-size array was used, with NULL indicating
331 // empty entries, while now a doubly-linked list
332 // is used.
333 
334 //
335 // P_ActivateInStasis()
336 //
337 // Activate a plat that has been put in stasis
338 // (stopped perpetual floor, instant floor/ceil toggle)
339 //
340 // Passed the tag of the plat that should be reactivated
341 // Returns nothing
342 //
P_ActivateInStasis(int tag)343 void P_ActivateInStasis(int tag)
344 {
345   platlist_t *pl;
346   for (pl=activeplats; pl; pl=pl->next)   // search the active plats
347   {
348     plat_t *plat = pl->plat;              // for one in stasis with right tag
349     if (plat->tag == tag && plat->status == PLAT_IN_STASIS)
350     {
351       if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type
352         plat->status = plat->oldstatus== PLAT_UP ? PLAT_DOWN : PLAT_UP;
353       else
354         plat->status = plat->oldstatus;
355       plat->thinker.function = T_PlatRaise;
356     }
357   }
358 }
359 
360 //
361 // EV_StopPlat()
362 //
363 // Handler for "stop perpetual floor" linedef type
364 //
365 // Passed the linedef that stopped the plat
366 // Returns TRUE if a plat was put in stasis
367 //
368 // jff 2/12/98 added int return value, fixed return
369 //
EV_StopPlat(line_t * line)370 int EV_StopPlat(line_t* line)
371 {
372    platlist_t *pl;
373    for (pl=activeplats; pl; pl=pl->next)  // search the active plats
374    {
375       plat_t *plat = pl->plat;             // for one with the tag not in stasis
376       if (plat->status != PLAT_IN_STASIS && plat->tag == line->tag)
377       {
378          plat->oldstatus = plat->status;    // put it in stasis
379          plat->status    = PLAT_IN_STASIS;
380          plat->thinker.function = NULL;
381       }
382    }
383    return 1;
384 }
385 
386 //
387 // P_AddActivePlat()
388 //
389 // Add a plat to the head of the active plat list
390 //
391 // Passed a pointer to the plat to add
392 // Returns nothing
393 //
P_AddActivePlat(plat_t * plat)394 void P_AddActivePlat(plat_t* plat)
395 {
396    platlist_t *list = malloc(sizeof *list);
397    list->plat = plat;
398    plat->list = list;
399    if ((list->next = activeplats))
400       list->next->prev = &list->next;
401    list->prev = &activeplats;
402    activeplats = list;
403 }
404 
405 //
406 // P_RemoveActivePlat()
407 //
408 // Remove a plat from the active plat list
409 //
410 // Passed a pointer to the plat to remove
411 // Returns nothing
412 //
P_RemoveActivePlat(plat_t * plat)413 void P_RemoveActivePlat(plat_t* plat)
414 {
415    platlist_t *list = plat->list;
416    plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers
417    P_RemoveThinker(&plat->thinker);
418    if ((*list->prev = list->next))
419       list->next->prev = list->prev;
420    free(list);
421 }
422 
423 //
424 // P_RemoveAllActivePlats()
425 //
426 // Remove all plats from the active plat list
427 //
428 // Passed nothing, returns nothing
429 //
P_RemoveAllActivePlats(void)430 void P_RemoveAllActivePlats(void)
431 {
432    while (activeplats)
433    {
434       platlist_t *next = activeplats->next;
435       free(activeplats);
436       activeplats = next;
437    }
438 }
439