// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // #include "h2def.h" #include "i_system.h" #include "p_local.h" extern fixed_t FloatBobOffsets[64]; //================================================================== //================================================================== // // FLOORS // //================================================================== //================================================================== //================================================================== // // Move a plane (floor or ceiling) and check for crushing // //================================================================== result_e T_MovePlane(sector_t * sector, fixed_t speed, fixed_t dest, int crush, int floorOrCeiling, int direction) { boolean flag; fixed_t lastpos; switch (floorOrCeiling) { case 0: // FLOOR switch (direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else { lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else // COULD GET CRUSHED { lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector, crush); if (flag == true) { //if (crush == true) //{ // return RES_CRUSHED; //} sector->floorheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; } break; case 1: // CEILING switch (direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else // COULD GET CRUSHED { lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector, crush); if (flag == true) { //if (crush == true) //{ // return RES_CRUSHED; //} sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector, crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); //return RES_CRUSHED; } return RES_PASTDEST; } else { lastpos = sector->ceilingheight; sector->ceilingheight += speed; flag = P_ChangeSector(sector, crush); #if 0 if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector, crush); return RES_CRUSHED; } #endif } break; } break; } return RES_OK; } //================================================================== // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // //================================================================== void T_MoveFloor(floormove_t * floor) { result_e res; if (floor->resetDelayCount) { floor->resetDelayCount--; if (!floor->resetDelayCount) { floor->floordestheight = floor->resetHeight; floor->direction = -floor->direction; floor->resetDelay = 0; floor->delayCount = 0; floor->delayTotal = 0; } } if (floor->delayCount) { floor->delayCount--; if (!floor->delayCount && floor->textureChange) { floor->sector->floorpic += floor->textureChange; } return; } res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush, 0, floor->direction); if (floor->type == FLEV_RAISEBUILDSTEP) { if ((floor->direction == 1 && floor->sector->floorheight >= floor->stairsDelayHeight) || (floor->direction == -1 && floor->sector->floorheight <= floor->stairsDelayHeight)) { floor->delayCount = floor->delayTotal; floor->stairsDelayHeight += floor->stairsDelayHeightDelta; } } if (res == RES_PASTDEST) { SN_StopSequence((mobj_t *) & floor->sector->soundorg); if (floor->delayTotal) { floor->delayTotal = 0; } if (floor->resetDelay) { // floor->resetDelayCount = floor->resetDelay; // floor->resetDelay = 0; return; } floor->sector->specialdata = NULL; /* if (floor->direction == 1) switch(floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } else if (floor->direction == -1) switch(floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } */ if (floor->textureChange) { floor->sector->floorpic -= floor->textureChange; } P_TagFinished(floor->sector->tag); P_RemoveThinker(&floor->thinker); } } //================================================================== // // HANDLE FLOOR TYPES // //================================================================== int EV_DoFloor(line_t * line, byte * args, floor_e floortype) { int secnum; int rtn; sector_t *sec; floormove_t *floor = NULL; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // // new floor thinker // rtn = 1; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); memset(floor, 0, sizeof(*floor)); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = floortype; floor->crush = 0; floor->speed = args[1] * (FRACUNIT / 8); if (floortype == FLEV_LOWERTIMES8INSTANT || floortype == FLEV_RAISETIMES8INSTANT) { floor->speed = 2000 << FRACBITS; } switch (floortype) { case FLEV_LOWERFLOOR: floor->direction = -1; floor->sector = sec; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case FLEV_LOWERFLOORTOLOWEST: floor->direction = -1; floor->sector = sec; floor->floordestheight = P_FindLowestFloorSurrounding(sec); break; case FLEV_LOWERFLOORBYVALUE: floor->direction = -1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight - args[2] * FRACUNIT; break; case FLEV_LOWERTIMES8INSTANT: case FLEV_LOWERBYVALUETIMES8: floor->direction = -1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight - args[2] * FRACUNIT * 8; break; case FLEV_RAISEFLOORCRUSH: floor->crush = args[2]; // arg[2] = crushing value floor->direction = 1; floor->sector = sec; floor->floordestheight = sec->ceilingheight - 8 * FRACUNIT; break; case FLEV_RAISEFLOOR: floor->direction = 1; floor->sector = sec; floor->floordestheight = P_FindLowestCeilingSurrounding(sec); if (floor->floordestheight > sec->ceilingheight) floor->floordestheight = sec->ceilingheight; break; case FLEV_RAISEFLOORTONEAREST: floor->direction = 1; floor->sector = sec; floor->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight); break; case FLEV_RAISEFLOORBYVALUE: floor->direction = 1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight + args[2] * FRACUNIT; break; case FLEV_RAISETIMES8INSTANT: case FLEV_RAISEBYVALUETIMES8: floor->direction = 1; floor->sector = sec; floor->floordestheight = floor->sector->floorheight + args[2] * FRACUNIT * 8; break; case FLEV_MOVETOVALUETIMES8: floor->sector = sec; floor->floordestheight = args[2] * FRACUNIT * 8; if (args[3]) { floor->floordestheight = -floor->floordestheight; } if (floor->floordestheight > floor->sector->floorheight) { floor->direction = 1; } else if (floor->floordestheight < floor->sector->floorheight) { floor->direction = -1; } else { // already at lowest position rtn = 0; } break; default: rtn = 0; break; } } if (rtn) { SN_StartSequence((mobj_t *) & floor->sector->soundorg, SEQ_PLATFORM + floor->sector->seqType); } return rtn; } //============================================================================ // // EV_DoFloorAndCeiling // //============================================================================ int EV_DoFloorAndCeiling(line_t * line, byte * args, boolean raise) { boolean floor, ceiling; int secnum; sector_t *sec; if (raise) { floor = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE); secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; sec->specialdata = NULL; } ceiling = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE); } else { floor = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE); secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; sec->specialdata = NULL; } ceiling = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE); } return (floor | ceiling); } // ===== Build Stairs Private Data ===== #define STAIR_SECTOR_TYPE 26 #define STAIR_QUEUE_SIZE 32 struct { sector_t *sector; int type; int height; } StairQueue[STAIR_QUEUE_SIZE]; static int QueueHead; static int QueueTail; static int StepDelta; static int Direction; static int Speed; static int Texture; static int StartDelay; static int StartDelayDelta; static int TextureChange; static int StartHeight; //========================================================================== // // QueueStairSector // //========================================================================== static void QueueStairSector(sector_t * sec, int type, int height) { if ((QueueTail + 1) % STAIR_QUEUE_SIZE == QueueHead) { I_Error("BuildStairs: Too many branches located.\n"); } StairQueue[QueueTail].sector = sec; StairQueue[QueueTail].type = type; StairQueue[QueueTail].height = height; QueueTail = (QueueTail + 1) % STAIR_QUEUE_SIZE; } //========================================================================== // // DequeueStairSector // //========================================================================== static sector_t *DequeueStairSector(int *type, int *height) { sector_t *sec; if (QueueHead == QueueTail) { // queue is empty return NULL; } *type = StairQueue[QueueHead].type; *height = StairQueue[QueueHead].height; sec = StairQueue[QueueHead].sector; QueueHead = (QueueHead + 1) % STAIR_QUEUE_SIZE; return sec; } //========================================================================== // // ProcessStairSector // //========================================================================== static void ProcessStairSector(sector_t * sec, int type, int height, stairs_e stairsType, int delay, int resetDelay) { int i; sector_t *tsec; floormove_t *floor; // // new floor thinker // height += StepDelta; floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0); memset(floor, 0, sizeof(*floor)); P_AddThinker(&floor->thinker); sec->specialdata = floor; floor->thinker.function = T_MoveFloor; floor->type = FLEV_RAISEBUILDSTEP; floor->direction = Direction; floor->sector = sec; floor->floordestheight = height; switch (stairsType) { case STAIRS_NORMAL: floor->speed = Speed; if (delay) { floor->delayTotal = delay; floor->stairsDelayHeight = sec->floorheight + StepDelta; floor->stairsDelayHeightDelta = StepDelta; } floor->resetDelay = resetDelay; floor->resetDelayCount = resetDelay; floor->resetHeight = sec->floorheight; break; case STAIRS_SYNC: floor->speed = FixedMul(Speed, FixedDiv(height - StartHeight, StepDelta)); floor->resetDelay = delay; //arg4 floor->resetDelayCount = delay; floor->resetHeight = sec->floorheight; break; /* case STAIRS_PHASED: floor->floordestheight = sec->floorheight+StepDelta; floor->speed = Speed; floor->delayCount = StartDelay; StartDelay += StartDelayDelta; floor->textureChange = TextureChange; floor->resetDelayCount = StartDelay; break; */ default: break; } SN_StartSequence((mobj_t *) & sec->soundorg, SEQ_PLATFORM + sec->seqType); // // Find next sector to raise // Find nearby sector with sector special equal to type // for (i = 0; i < sec->linecount; i++) { if (!((sec->lines[i])->flags & ML_TWOSIDED)) { continue; } tsec = (sec->lines[i])->frontsector; if (tsec->special == type + STAIR_SECTOR_TYPE && !tsec->specialdata && tsec->floorpic == Texture && tsec->validcount != validcount) { QueueStairSector(tsec, type ^ 1, height); tsec->validcount = validcount; //tsec->special = 0; } tsec = (sec->lines[i])->backsector; if (tsec->special == type + STAIR_SECTOR_TYPE && !tsec->specialdata && tsec->floorpic == Texture && tsec->validcount != validcount) { QueueStairSector(tsec, type ^ 1, height); tsec->validcount = validcount; //tsec->special = 0; } } } //================================================================== // // BUILD A STAIRCASE! // // Direction is either positive or negative, denoting build stairs // up or down. //================================================================== int EV_BuildStairs(line_t * line, byte * args, int direction, stairs_e stairsType) { int secnum; int height; int delay; int resetDelay; sector_t *sec; sector_t *qSec; int type; // Set global stairs variables TextureChange = 0; Direction = direction; StepDelta = Direction * (args[2] * FRACUNIT); Speed = args[1] * (FRACUNIT / 8); resetDelay = args[4]; delay = args[3]; if (stairsType == STAIRS_PHASED) { StartDelayDelta = args[3]; StartDelay = StartDelayDelta; resetDelay = StartDelayDelta; delay = 0; TextureChange = args[4]; } secnum = -1; validcount++; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; Texture = sec->floorpic; StartHeight = sec->floorheight; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; QueueStairSector(sec, 0, sec->floorheight); sec->special = 0; } while ((qSec = DequeueStairSector(&type, &height)) != NULL) { ProcessStairSector(qSec, type, height, stairsType, delay, resetDelay); } return (1); } //========================================================================= // // T_BuildPillar // //========================================================================= void T_BuildPillar(pillar_t * pillar) { result_e res1; result_e res2; // First, raise the floor res1 = T_MovePlane(pillar->sector, pillar->floorSpeed, pillar->floordest, pillar->crush, 0, pillar->direction); // floorOrCeiling, direction // Then, lower the ceiling res2 = T_MovePlane(pillar->sector, pillar->ceilingSpeed, pillar->ceilingdest, pillar->crush, 1, -pillar->direction); if (res1 == RES_PASTDEST && res2 == RES_PASTDEST) { pillar->sector->specialdata = NULL; SN_StopSequence((mobj_t *) & pillar->sector->soundorg); P_TagFinished(pillar->sector->tag); P_RemoveThinker(&pillar->thinker); } } //========================================================================= // // EV_BuildPillar // //========================================================================= int EV_BuildPillar(line_t * line, byte * args, boolean crush) { int secnum; sector_t *sec; pillar_t *pillar; int newHeight; int rtn; rtn = 0; secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // already moving if (sec->floorheight == sec->ceilingheight) { // pillar is already closed continue; } rtn = 1; if (!args[2]) { newHeight = sec->floorheight + ((sec->ceilingheight - sec->floorheight) / 2); } else { newHeight = sec->floorheight + (args[2] << FRACBITS); } pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0); sec->specialdata = pillar; P_AddThinker(&pillar->thinker); pillar->thinker.function = T_BuildPillar; pillar->sector = sec; if (!args[2]) { pillar->ceilingSpeed = pillar->floorSpeed = args[1] * (FRACUNIT / 8); } else if (newHeight - sec->floorheight > sec->ceilingheight - newHeight) { pillar->floorSpeed = args[1] * (FRACUNIT / 8); pillar->ceilingSpeed = FixedMul(sec->ceilingheight - newHeight, FixedDiv(pillar->floorSpeed, newHeight - sec->floorheight)); } else { pillar->ceilingSpeed = args[1] * (FRACUNIT / 8); pillar->floorSpeed = FixedMul(newHeight - sec->floorheight, FixedDiv(pillar->ceilingSpeed, sec->ceilingheight - newHeight)); } pillar->floordest = newHeight; pillar->ceilingdest = newHeight; pillar->direction = 1; pillar->crush = crush * args[3]; SN_StartSequence((mobj_t *) & pillar->sector->soundorg, SEQ_PLATFORM + pillar->sector->seqType); } return rtn; } //========================================================================= // // EV_OpenPillar // //========================================================================= int EV_OpenPillar(line_t * line, byte * args) { int secnum; sector_t *sec; pillar_t *pillar; int rtn; rtn = 0; secnum = -1; while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // already moving if (sec->floorheight != sec->ceilingheight) { // pillar isn't closed continue; } rtn = 1; pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0); sec->specialdata = pillar; P_AddThinker(&pillar->thinker); pillar->thinker.function = T_BuildPillar; pillar->sector = sec; if (!args[2]) { pillar->floordest = P_FindLowestFloorSurrounding(sec); } else { pillar->floordest = sec->floorheight - (args[2] << FRACBITS); } if (!args[3]) { pillar->ceilingdest = P_FindHighestCeilingSurrounding(sec); } else { pillar->ceilingdest = sec->ceilingheight + (args[3] << FRACBITS); } if (sec->floorheight - pillar->floordest >= pillar->ceilingdest - sec->ceilingheight) { pillar->floorSpeed = args[1] * (FRACUNIT / 8); pillar->ceilingSpeed = FixedMul(sec->ceilingheight - pillar->ceilingdest, FixedDiv(pillar->floorSpeed, pillar->floordest - sec->floorheight)); } else { pillar->ceilingSpeed = args[1] * (FRACUNIT / 8); pillar->floorSpeed = FixedMul(pillar->floordest - sec->floorheight, FixedDiv(pillar->ceilingSpeed, sec->ceilingheight - pillar->ceilingdest)); } pillar->direction = -1; // open the pillar SN_StartSequence((mobj_t *) & pillar->sector->soundorg, SEQ_PLATFORM + pillar->sector->seqType); } return rtn; } //========================================================================= // // EV_FloorCrushStop // //========================================================================= int EV_FloorCrushStop(line_t * line, byte * args) { thinker_t *think; floormove_t *floor; boolean rtn; rtn = 0; for (think = thinkercap.next; think != &thinkercap; think = think->next) { if (think->function != T_MoveFloor) { continue; } floor = (floormove_t *) think; if (floor->type != FLEV_RAISEFLOORCRUSH) { continue; } // Completely remove the crushing floor SN_StopSequence((mobj_t *) & floor->sector->soundorg); floor->sector->specialdata = NULL; P_TagFinished(floor->sector->tag); P_RemoveThinker(&floor->thinker); rtn = 1; } return rtn; } //========================================================================== // // T_FloorWaggle // //========================================================================== #define WGLSTATE_EXPAND 1 #define WGLSTATE_STABLE 2 #define WGLSTATE_REDUCE 3 void T_FloorWaggle(floorWaggle_t * waggle) { switch (waggle->state) { case WGLSTATE_EXPAND: if ((waggle->scale += waggle->scaleDelta) >= waggle->targetScale) { waggle->scale = waggle->targetScale; waggle->state = WGLSTATE_STABLE; } break; case WGLSTATE_REDUCE: if ((waggle->scale -= waggle->scaleDelta) <= 0) { // Remove waggle->sector->floorheight = waggle->originalHeight; P_ChangeSector(waggle->sector, true); waggle->sector->specialdata = NULL; P_TagFinished(waggle->sector->tag); P_RemoveThinker(&waggle->thinker); return; } break; case WGLSTATE_STABLE: if (waggle->ticker != -1) { if (!--waggle->ticker) { waggle->state = WGLSTATE_REDUCE; } } break; } waggle->accumulator += waggle->accDelta; waggle->sector->floorheight = waggle->originalHeight + FixedMul(FloatBobOffsets[(waggle->accumulator >> FRACBITS) & 63], waggle->scale); P_ChangeSector(waggle->sector, true); } //========================================================================== // // EV_StartFloorWaggle // //========================================================================== boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset, int timer) { int sectorIndex; sector_t *sector; floorWaggle_t *waggle; boolean retCode; retCode = false; sectorIndex = -1; while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0) { sector = §ors[sectorIndex]; if (sector->specialdata) { // Already busy with another thinker continue; } retCode = true; waggle = Z_Malloc(sizeof(*waggle), PU_LEVSPEC, 0); sector->specialdata = waggle; waggle->thinker.function = T_FloorWaggle; waggle->sector = sector; waggle->originalHeight = sector->floorheight; waggle->accumulator = offset * FRACUNIT; waggle->accDelta = speed << 10; waggle->scale = 0; waggle->targetScale = height << 10; waggle->scaleDelta = waggle->targetScale / (35 + ((3 * 35) * height) / 255); waggle->ticker = timer ? timer * 35 : -1; waggle->state = WGLSTATE_EXPAND; P_AddThinker(&waggle->thinker); } return retCode; }