/* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom: a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * Copyright 2005, 2006 by * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * DESCRIPTION: * Plats (i.e. elevator platforms) code, raising/lowering. * *-----------------------------------------------------------------------------*/ #include "doomstat.h" #include "m_random.h" #include "r_main.h" #include "p_spec.h" #include "p_tick.h" #include "s_sound.h" #include "sounds.h" platlist_t *activeplats; // killough 2/14/98: made global again // // T_PlatRaise() // // Action routine to move a plat up and down // // Passed a plat structure containing all pertinent information about the move // No return // // jff 02/08/98 all cases with labels beginning with gen added to support // generalized line type behaviors. void T_PlatRaise(plat_t* plat) { result_e res; // handle plat moving, up, down, waiting, or in stasis, switch(plat->status) { case up: // plat moving up res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1); // if a pure raise type, make the plat moving sound if (plat->type == raiseAndChange || plat->type == raiseToNearestAndChange) { if (!(leveltime&7)) S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov); } // if encountered an obstacle, and not a crush type, reverse direction if (res == crushed && (!plat->crush)) { plat->count = plat->wait; plat->status = down; S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart); } else // else handle reaching end of up stroke { if (res == pastdest) // end of stroke { // if not an instant toggle type, wait, make plat stop sound if (plat->type!=toggleUpDn) { plat->count = plat->wait; plat->status = waiting; S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop); } else // else go into stasis awaiting next toggle activation { plat->oldstatus = plat->status;//jff 3/14/98 after action wait plat->status = in_stasis; //for reactivation of toggle } // lift types and pure raise types are done at end of up stroke // only the perpetual type waits then goes back up switch(plat->type) { case blazeDWUS: case downWaitUpStay: case raiseAndChange: case raiseToNearestAndChange: case genLift: P_RemoveActivePlat(plat); // killough default: break; } } } break; case down: // plat moving down res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); // handle reaching end of down stroke if (res == pastdest) { // if not an instant toggle, start waiting, make plat stop sound if (plat->type!=toggleUpDn) //jff 3/14/98 toggle up down { // is silent, instant, no waiting plat->count = plat->wait; plat->status = waiting; S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop); } else // instant toggles go into stasis awaiting next activation { plat->oldstatus = plat->status;//jff 3/14/98 after action wait plat->status = in_stasis; //for reactivation of toggle } //jff 1/26/98 remove the plat if it bounced so it can be tried again //only affects plats that raise and bounce //killough 1/31/98: relax compatibility to demo_compatibility // remove the plat if its a pure raise type if (!comp[comp_floors]) { switch(plat->type) { case raiseAndChange: case raiseToNearestAndChange: P_RemoveActivePlat(plat); default: break; } } } break; case waiting: // plat is waiting if (!--plat->count) // downcount and check for delay elapsed { if (plat->sector->floorheight == plat->low) plat->status = up; // if at bottom, start up else plat->status = down; // if at top, start down // make plat start sound S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart); } break; //jff 1/27/98 don't pickup code added later to in_stasis case in_stasis: // do nothing if in stasis break; } } // // EV_DoPlat // // Handle Plat linedef types // // Passed the linedef that activated the plat, the type of plat action, // and for some plat types, an amount to raise // Returns true if a thinker is started, or restarted from stasis // int EV_DoPlat ( line_t* line, plattype_e type, int amount ) { plat_t* plat; int secnum; int rtn; sector_t* sec; secnum = -1; rtn = 0; // Activate all plats that are in_stasis switch(type) { case perpetualRaise: P_ActivateInStasis(line->tag); break; case toggleUpDn: P_ActivateInStasis(line->tag); rtn=1; break; default: break; } // act on all sectors tagged the same as the activating linedef while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // don't start a second floor function if already moving if (P_SectorActive(floor_special,sec)) //jff 2/23/98 multiple thinkers continue; // Create a thinker rtn = 1; plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); memset(plat, 0, sizeof(*plat)); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers plat->thinker.function = T_PlatRaise; plat->crush = false; plat->tag = line->tag; //jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then //going down forever -- default low to plat height when triggered plat->low = sec->floorheight; // set up plat according to type switch(type) { case raiseToNearestAndChange: plat->speed = PLATSPEED/2; sec->floorpic = sides[line->sidenum[0]].sector->floorpic; plat->high = P_FindNextHighestFloor(sec,sec->floorheight); plat->wait = 0; plat->status = up; sec->special = 0; //jff 3/14/98 clear old field as well sec->oldspecial = 0; S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); break; case raiseAndChange: plat->speed = PLATSPEED/2; sec->floorpic = sides[line->sidenum[0]].sector->floorpic; plat->high = sec->floorheight + amount*FRACUNIT; plat->wait = 0; plat->status = up; S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); break; case downWaitUpStay: plat->speed = PLATSPEED * 4; plat->low = P_FindLowestFloorSurrounding(sec); if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = sec->floorheight; plat->wait = 35*PLATWAIT; plat->status = down; S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); break; case blazeDWUS: plat->speed = PLATSPEED * 8; plat->low = P_FindLowestFloorSurrounding(sec); if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = sec->floorheight; plat->wait = 35*PLATWAIT; plat->status = down; S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); break; case perpetualRaise: plat->speed = PLATSPEED; plat->low = P_FindLowestFloorSurrounding(sec); if (plat->low > sec->floorheight) plat->low = sec->floorheight; plat->high = P_FindHighestFloorSurrounding(sec); if (plat->high < sec->floorheight) plat->high = sec->floorheight; plat->wait = 35*PLATWAIT; plat->status = P_Random(pr_plats)&1; S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); break; case toggleUpDn: //jff 3/14/98 add new type to support instant toggle plat->speed = PLATSPEED; //not used plat->wait = 35*PLATWAIT; //not used plat->crush = true; //jff 3/14/98 crush anything in the way // set up toggling between ceiling, floor inclusive plat->low = sec->ceilingheight; plat->high = sec->floorheight; plat->status = down; break; default: break; } P_AddActivePlat(plat); // add plat to list of active plats } return rtn; } // The following were all rewritten by Lee Killough // to use the new structure which places no limits // on active plats. It also avoids spending as much // time searching for active plats. Previously a // fixed-size array was used, with NULL indicating // empty entries, while now a doubly-linked list // is used. // // P_ActivateInStasis() // // Activate a plat that has been put in stasis // (stopped perpetual floor, instant floor/ceil toggle) // // Passed the tag of the plat that should be reactivated // Returns nothing // void P_ActivateInStasis(int tag) { platlist_t *pl; for (pl=activeplats; pl; pl=pl->next) // search the active plats { plat_t *plat = pl->plat; // for one in stasis with right tag if (plat->tag == tag && plat->status == in_stasis) { if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type plat->status = plat->oldstatus==up? down : up; else plat->status = plat->oldstatus; plat->thinker.function = T_PlatRaise; } } } // // EV_StopPlat() // // Handler for "stop perpetual floor" linedef type // // Passed the linedef that stopped the plat // Returns true if a plat was put in stasis // // jff 2/12/98 added int return value, fixed return // int EV_StopPlat(line_t* line) { platlist_t *pl; for (pl=activeplats; pl; pl=pl->next) // search the active plats { plat_t *plat = pl->plat; // for one with the tag not in stasis if (plat->status != in_stasis && plat->tag == line->tag) { plat->oldstatus = plat->status; // put it in stasis plat->status = in_stasis; plat->thinker.function = NULL; } } return 1; } // // P_AddActivePlat() // // Add a plat to the head of the active plat list // // Passed a pointer to the plat to add // Returns nothing // void P_AddActivePlat(plat_t* plat) { platlist_t *list = malloc(sizeof *list); list->plat = plat; plat->list = list; if ((list->next = activeplats)) list->next->prev = &list->next; list->prev = &activeplats; activeplats = list; } // // P_RemoveActivePlat() // // Remove a plat from the active plat list // // Passed a pointer to the plat to remove // Returns nothing // void P_RemoveActivePlat(plat_t* plat) { platlist_t *list = plat->list; plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers P_RemoveThinker(&plat->thinker); if ((*list->prev = list->next)) list->next->prev = list->prev; free(list); } // // P_RemoveAllActivePlats() // // Remove all plats from the active plat list // // Passed nothing, returns nothing // void P_RemoveAllActivePlats(void) { while (activeplats) { platlist_t *next = activeplats->next; free(activeplats); activeplats = next; } }