/* 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: * Door animation code (opening/closing) * *-----------------------------------------------------------------------------*/ #include "doomstat.h" #include "p_spec.h" #include "p_tick.h" #include "s_sound.h" #include "sounds.h" #include "r_main.h" #include "dstrings.h" #include "d_deh.h" // Ty 03/27/98 - externalized #include "lprintf.h" /////////////////////////////////////////////////////////////// // // Door action routines, called once per tick // /////////////////////////////////////////////////////////////// // // T_VerticalDoor // // Passed a door structure containing all info about the door. // See P_SPEC.H for fields. // Returns nothing. // // jff 02/08/98 all cases with labels beginning with gen added to support // generalized line type behaviors. void T_VerticalDoor (vldoor_t* door) { result_e res; // Is the door waiting, going up, or going down? switch(door->direction) { case 0: // Door is waiting if (!--door->topcountdown) // downcount and check { switch(door->type) { case blazeRaise: case genBlazeRaise: door->direction = -1; // time to go back down S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); break; case normal: case genRaise: door->direction = -1; // time to go back down S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); break; case close30ThenOpen: case genCdO: door->direction = 1; // time to go back up S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); break; case genBlazeCdO: door->direction = 1; // time to go back up S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); break; default: break; } } break; case 2: // Special case for sector type door that opens in 5 mins if (!--door->topcountdown) // 5 minutes up? { switch(door->type) { case raiseIn5Mins: door->direction = 1; // time to raise then door->type = normal; // door acts just like normal 1 DR door now S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); break; default: break; } } break; case -1: // Door is moving down res = T_MovePlane ( door->sector, door->speed, door->sector->floorheight, false, 1, door->direction ); /* killough 10/98: implement gradual lighting effects */ // e6y: "Tagged doors don't trigger special lighting" handled wrong // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) EV_LightTurnOnPartway(door->line, FixedDiv(door->sector->ceilingheight - door->sector->floorheight, door->topheight - door->sector->floorheight)); // handle door reaching bottom if (res == pastdest) { switch(door->type) { // regular open and close doors are all done, remove them case blazeRaise: case blazeClose: case genBlazeRaise: case genBlazeClose: door->sector->ceilingdata = NULL; //jff 2/22/98 P_RemoveThinker (&door->thinker); // unlink and free // killough 4/15/98: remove double-closing sound of blazing doors if (comp[comp_blazing]) S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); break; case normal: case close: case genRaise: case genClose: door->sector->ceilingdata = NULL; //jff 2/22/98 P_RemoveThinker (&door->thinker); // unlink and free break; // close then open doors start waiting case close30ThenOpen: door->direction = 0; door->topcountdown = TICRATE*30; break; case genCdO: case genBlazeCdO: door->direction = 0; door->topcountdown = door->topwait; // jff 5/8/98 insert delay break; default: break; } // e6y: "Tagged doors don't trigger special lighting" handled wrong // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) EV_LightTurnOnPartway(door->line,0); } /* jff 1/31/98 turn lighting off in tagged sectors of manual doors * killough 10/98: replaced with gradual lighting code */ else if (res == crushed) // handle door meeting obstruction on way down { switch(door->type) { case genClose: case genBlazeClose: case blazeClose: case close: // Close types do not bounce, merely wait break; case blazeRaise: case genBlazeRaise: door->direction = 1; if (!comp[comp_blazing]) { S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); break; } default: // other types bounce off the obstruction door->direction = 1; S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); break; } } break; case 1: // Door is moving up res = T_MovePlane ( door->sector, door->speed, door->topheight, false, 1, door->direction ); /* killough 10/98: implement gradual lighting effects */ // e6y: "Tagged doors don't trigger special lighting" handled wrong // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) EV_LightTurnOnPartway(door->line, FixedDiv(door->sector->ceilingheight - door->sector->floorheight, door->topheight - door->sector->floorheight)); // handle door reaching the top if (res == pastdest) { switch(door->type) { case blazeRaise: // regular open/close doors start waiting case normal: case genRaise: case genBlazeRaise: door->direction = 0; // wait at top with delay door->topcountdown = door->topwait; break; case close30ThenOpen: // close and close/open doors are done case blazeOpen: case open: case genBlazeOpen: case genOpen: case genCdO: case genBlazeCdO: door->sector->ceilingdata = NULL; //jff 2/22/98 P_RemoveThinker (&door->thinker); // unlink and free break; default: break; } /* jff 1/31/98 turn lighting on in tagged sectors of manual doors * killough 10/98: replaced with gradual lighting code */ // e6y: "Tagged doors don't trigger special lighting" handled wrong // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) EV_LightTurnOnPartway(door->line,FRACUNIT); } break; } } /////////////////////////////////////////////////////////////// // // Door linedef handlers // /////////////////////////////////////////////////////////////// // // EV_DoLockedDoor // // Handle opening a tagged locked door // // Passed the line activating the door, the type of door, // and the thing that activated the line // Returns true if a thinker created // int EV_DoLockedDoor ( line_t* line, vldoor_e type, mobj_t* thing ) { player_t* p; // only players can open locked doors p = thing->player; if (!p) return 0; // check type of linedef, and if key is possessed to open it switch(line->special) { case 99: // Blue Lock case 133: if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) { p->message = s_PD_BLUEO; // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } break; case 134: // Red Lock case 135: if (!p->cards[it_redcard] && !p->cards[it_redskull]) { p->message = s_PD_REDO; // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } break; case 136: // Yellow Lock case 137: if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) { p->message = s_PD_YELLOWO; // Ty 03/27/98 - externalized S_StartSound(p->mo,sfx_oof); // killough 3/20/98 return 0; } break; } // got the key, so open the door return EV_DoDoor(line,type); } // // EV_DoDoor // // Handle opening a tagged door // // Passed the line activating the door and the type of door // Returns true if a thinker created // int EV_DoDoor ( line_t* line, vldoor_e type ) { int secnum,rtn; sector_t* sec; vldoor_t* door; secnum = -1; rtn = 0; // open all doors with the same tag as the activating line while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // if the ceiling already moving, don't start the door action if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 continue; // new door thinker rtn = 1; door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); memset(door, 0, sizeof(*door)); P_AddThinker (&door->thinker); sec->ceilingdata = door; //jff 2/22/98 door->thinker.function = T_VerticalDoor; door->sector = sec; door->type = type; door->topwait = VDOORWAIT; door->speed = VDOORSPEED; door->line = line; // jff 1/31/98 remember line that triggered us door->lighttag = 0; /* killough 10/98: no light effects with tagged doors */ // setup door parameters according to type of door switch(type) { case blazeClose: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; door->speed = VDOORSPEED * 4; S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); break; case close: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); break; case close30ThenOpen: door->topheight = sec->ceilingheight; door->direction = -1; S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); break; case blazeRaise: case blazeOpen: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = VDOORSPEED * 4; if (door->topheight != sec->ceilingheight) S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); break; case normal: case open: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; if (door->topheight != sec->ceilingheight) S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); break; default: break; } } return rtn; } // // EV_VerticalDoor // // Handle opening a door manually, no tag value // // Passed the line activating the door and the thing activating it // Returns true if a thinker created // // jff 2/12/98 added int return value, fixed all returns // int EV_VerticalDoor ( line_t* line, mobj_t* thing ) { player_t* player; int secnum; sector_t* sec; vldoor_t* door; // Check for locks player = thing->player; switch(line->special) { case 26: // Blue Lock case 32: if ( !player ) return 0; if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) { player->message = s_PD_BLUEK; // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } break; case 27: // Yellow Lock case 34: if ( !player ) return 0; if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) { player->message = s_PD_YELLOWK; // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } break; case 28: // Red Lock case 33: if ( !player ) return 0; if (!player->cards[it_redcard] && !player->cards[it_redskull]) { player->message = s_PD_REDK; // Ty 03/27/98 - externalized S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } break; default: break; } // if the wrong side of door is pushed, give oof sound if (line->sidenum[1]==NO_INDEX) // killough { S_StartSound(player->mo,sfx_oof); // killough 3/20/98 return 0; } // get the sector on the second side of activating linedef sec = sides[line->sidenum[1]].sector; secnum = sec-sectors; /* if door already has a thinker, use it * cph 2001/04/05 - * Ok, this is a disaster area. We're assuming that sec->ceilingdata * is a vldoor_t! What if this door is controlled by both DR lines * and by switches? I don't know how to fix that. * Secondly, original Doom didn't distinguish floor/lighting/ceiling * actions, so we need to do the same in demo compatibility mode. */ door = sec->ceilingdata; if (demo_compatibility) { if (!door) door = sec->floordata; if (!door) door = sec->lightingdata; } /* If this is a repeatable line, and the door is already moving, then we can just reverse the current action. Note that in prboom 2.3.0 I erroneously removed the if-this-is-repeatable check, hence the prboom_4_compatibility clause below (foolishly assumed that already moving implies repeatable - but it could be moving due to another switch, e.g. lv19-509) */ if (door && ((compatibility_level == prboom_4_compatibility) || (line->special == 1) || (line->special == 117) || (line->special == 26) || (line->special == 27) || (line->special == 28) ) ) { /* For old demos we have to emulate the old buggy behavior and * mess up non-T_VerticalDoor actions. */ if (compatibility_level < prboom_4_compatibility || door->thinker.function == T_VerticalDoor) { /* cph - we are writing outval to door->direction iff it is non-zero */ signed int outval = 0; /* An already moving repeatable door which is being re-pressed, or a * monster is trying to open a closing door - so change direction * DEMOSYNC: we only read door->direction now if it really is a door. */ if (door->thinker.function == T_VerticalDoor && door->direction == -1) { outval = 1; /* go back up */ } else if (player) { outval = -1; /* go back down */ } /* Write this to the thinker. In demo compatibility mode, we might be * overwriting a field of a non-vldoor_t thinker - we need to add any * other thinker types here if any demos depend on specific fields * being corrupted by this. */ if (outval) { if (door->thinker.function == T_VerticalDoor) { door->direction = outval; } else if (door->thinker.function == T_PlatRaise) { plat_t* p = (plat_t*)door; p->wait = outval; } else { lprintf(LO_DEBUG, "EV_VerticalDoor: unknown thinker.function in thinker corruption emulation"); } return 1; } } /* Either we're in prboom >=v2.3 and it's not a door, or it's a door but * we're a monster and don't want to shut it; exit with no action. */ return 0; } // emit proper sound switch(line->special) { case 117: // blazing door raise case 118: // blazing door open S_StartSound((mobj_t *)&sec->soundorg,sfx_bdopn); break; default: // normal or locked door sound S_StartSound((mobj_t *)&sec->soundorg,sfx_doropn); break; } // new door thinker door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); memset(door, 0, sizeof(*door)); P_AddThinker (&door->thinker); sec->ceilingdata = door; //jff 2/22/98 door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 1; door->speed = VDOORSPEED; door->topwait = VDOORWAIT; door->line = line; // jff 1/31/98 remember line that triggered us /* killough 10/98: use gradual lighting changes if nonzero tag given */ door->lighttag = comp[comp_doorlight] ? 0 : line->tag; // set the type of door from the activating linedef type switch(line->special) { case 1: case 26: case 27: case 28: door->type = normal; break; case 31: case 32: case 33: case 34: door->type = open; line->special = 0; break; case 117: // blazing door raise door->type = blazeRaise; door->speed = VDOORSPEED*4; break; case 118: // blazing door open door->type = blazeOpen; line->special = 0; door->speed = VDOORSPEED*4; break; default: door->lighttag = 0; // killough 10/98 break; } // find the top and bottom of the movement range door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; return 1; } /////////////////////////////////////////////////////////////// // // Sector type door spawners // /////////////////////////////////////////////////////////////// // // P_SpawnDoorCloseIn30() // // Spawn a door that closes after 30 seconds (called at level init) // // Passed the sector of the door, whose type specified the door action // Returns nothing // void P_SpawnDoorCloseIn30 (sector_t* sec) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); memset(door, 0, sizeof(*door)); P_AddThinker (&door->thinker); sec->ceilingdata = door; //jff 2/22/98 sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = normal; door->speed = VDOORSPEED; door->topcountdown = 30 * 35; door->line = NULL; // jff 1/31/98 remember line that triggered us door->lighttag = 0; /* killough 10/98: no lighting changes */ } // // P_SpawnDoorRaiseIn5Mins() // // Spawn a door that opens after 5 minutes (called at level init) // // Passed the sector of the door, whose type specified the door action // Returns nothing // void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); memset(door, 0, sizeof(*door)); P_AddThinker (&door->thinker); sec->ceilingdata = door; //jff 2/22/98 sec->special = 0; door->thinker.function = T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = raiseIn5Mins; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5 * 60 * 35; door->line = NULL; // jff 1/31/98 remember line that triggered us door->lighttag = 0; /* killough 10/98: no lighting changes */ }