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 = §ors[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