SRB2/src/p_floor.c
Nev3r a9110c0645 Fixed more mobj thinker iterator oversights and removed all mobj thinker function checks
Mobjs got their own thinker list after all, and disappearing thinkers are automatically purged from their lists and sent to the limbo list.
So it's safe to assume all thinkers inside the mobj list must be mobjs.

Signed-off-by: Nev3r <apophycens@gmail.com>
2019-04-21 16:39:57 +02:00

3369 lines
97 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file p_floor.c
/// \brief Floor animation, elevators
#include "doomdef.h"
#include "doomstat.h"
#include "m_random.h"
#include "p_local.h"
#ifdef ESLOPE
#include "p_slopes.h"
#endif
#include "r_state.h"
#include "s_sound.h"
#include "z_zone.h"
#include "g_game.h"
#include "r_main.h"
// ==========================================================================
// FLOORS
// ==========================================================================
//
// Mini-P_IsObjectOnGroundIn for T_MovePlane hack
//
static inline boolean P_MobjReadyToMove(mobj_t *mo, sector_t *sec, boolean sectorisffloor, boolean sectorisquicksand)
{
if (sectorisquicksand)
return (mo->z > sec->floorheight && mo->z < sec->ceilingheight);
else if (!!(mo->flags & MF_SPAWNCEILING) ^ !!(mo->eflags & MFE_VERTICALFLIP))
return ((sectorisffloor) ? (mo->z+mo->height != sec->floorheight) : (mo->z+mo->height != sec->ceilingheight));
else
return ((sectorisffloor) ? (mo->z != sec->ceilingheight) : (mo->z != sec->floorheight));
}
//
// Move a plane (floor or ceiling) and check for crushing
//
result_e T_MovePlane(sector_t *sector, fixed_t speed, fixed_t dest, boolean crush,
INT32 floorOrCeiling, INT32 direction)
{
boolean flag;
fixed_t lastpos;
fixed_t destheight; // used to keep floors/ceilings from moving through each other
// Stuff used for mobj hacks.
INT32 secnum = -1;
mobj_t *mo = NULL;
sector_t *sec = NULL;
ffloor_t *rover = NULL;
boolean sectorisffloor = false;
boolean sectorisquicksand = false;
sector->moved = true;
switch (floorOrCeiling)
{
case 0:
// moving a floor
switch (direction)
{
case -1:
// Moving a floor down
if (sector->floorheight - speed < dest)
{
lastpos = sector->floorheight;
sector->floorheight = dest;
flag = P_CheckSector(sector, crush);
if (flag && sector->numattached)
{
sector->floorheight = lastpos;
P_CheckSector(sector, crush);
}
return pastdest;
}
else
{
lastpos = sector->floorheight;
sector->floorheight -= speed;
flag = P_CheckSector(sector, crush);
if (flag && sector->numattached)
{
sector->floorheight = lastpos;
P_CheckSector(sector, crush);
return crushed;
}
}
break;
case 1:
// Moving a floor up
// keep floor from moving through ceilings
destheight = (dest < sector->ceilingheight) ? dest : sector->ceilingheight;
if (sector->floorheight + speed > destheight)
{
lastpos = sector->floorheight;
sector->floorheight = destheight;
flag = P_CheckSector(sector, crush);
if (flag)
{
sector->floorheight = lastpos;
P_CheckSector(sector, crush);
}
return pastdest;
}
else
{
// crushing is possible
lastpos = sector->floorheight;
sector->floorheight += speed;
flag = P_CheckSector(sector, crush);
if (flag)
{
sector->floorheight = lastpos;
P_CheckSector(sector, crush);
return crushed;
}
}
break;
}
break;
case 1:
// moving a ceiling
switch (direction)
{
case -1:
// moving a ceiling down
// keep ceiling from moving through floors
destheight = (dest > sector->floorheight) ? dest : sector->floorheight;
if (sector->ceilingheight - speed < destheight)
{
lastpos = sector->ceilingheight;
sector->ceilingheight = destheight;
flag = P_CheckSector(sector, crush);
if (flag)
{
sector->ceilingheight = lastpos;
P_CheckSector(sector, crush);
}
return pastdest;
}
else
{
// crushing is possible
lastpos = sector->ceilingheight;
sector->ceilingheight -= speed;
flag = P_CheckSector(sector, crush);
if (flag)
{
sector->ceilingheight = lastpos;
P_CheckSector(sector, crush);
return crushed;
}
}
break;
case 1:
// moving a ceiling up
if (sector->ceilingheight + speed > dest)
{
lastpos = sector->ceilingheight;
sector->ceilingheight = dest;
flag = P_CheckSector(sector, crush);
if (flag && sector->numattached)
{
sector->ceilingheight = lastpos;
P_CheckSector(sector, crush);
}
return pastdest;
}
else
{
lastpos = sector->ceilingheight;
sector->ceilingheight += speed;
flag = P_CheckSector(sector, crush);
if (flag && sector->numattached)
{
sector->ceilingheight = lastpos;
P_CheckSector(sector, crush);
return crushed;
}
}
break;
}
break;
}
// Hack for buggy mobjs to move by gravity with moving planes.
if (sector->tagline)
sectorisffloor = true;
// Optimization condition. If the sector is not an FOF, declare sec as the main sector outside of the loop.
if (!sectorisffloor)
sec = sector;
// Optimization condition. Only run the logic if there is any Things in the sector.
if (sectorisffloor || sec->thinglist)
{
// If this is an FOF being checked, check all the affected sectors for moving mobjs.
while ((sectorisffloor ? (secnum = P_FindSectorFromLineTag(sector->tagline, secnum)) : (secnum = 1)) >= 0)
{
if (sectorisffloor)
{
// Get actual sector from the list of sectors.
sec = &sectors[secnum];
// Can't use P_InQuicksand because it will return the incorrect result
// because of checking for heights.
for (rover = sec->ffloors; rover; rover = rover->next)
{
if (rover->target == sec && (rover->flags & FF_QUICKSAND))
{
sectorisquicksand = true;
break;
}
}
}
for (mo = sec->thinglist; mo; mo = mo->snext)
{
// The object should be ready to move as defined by this function.
if (!P_MobjReadyToMove(mo, sec, sectorisffloor, sectorisquicksand))
continue;
// The object should not be moving at all.
if (mo->momx || mo->momy || mo->momz)
continue;
// These objects will be affected by this condition.
switch (mo->type)
{
case MT_GOOP: // Egg Slimer's goop objects
case MT_SPINFIRE: // Elemental Shield flame balls
case MT_SPIKE: // Floor Spike
// Is the object hang from the ceiling?
// In that case, swap the planes used.
// verticalflip inverts
if (!!(mo->flags & MF_SPAWNCEILING) ^ !!(mo->eflags & MFE_VERTICALFLIP))
{
if (sectorisffloor && !sectorisquicksand)
mo->z = mo->ceilingz - mo->height;
else
mo->z = mo->ceilingz = mo->subsector->sector->ceilingheight - mo->height;
}
else
{
if (sectorisffloor && !sectorisquicksand)
mo->z = mo->floorz;
else
mo->z = mo->floorz = mo->subsector->sector->floorheight;
}
break;
// Kill warnings...
default:
break;
}
}
// Break from loop if there is no FOFs to check.
if (!sectorisffloor)
break;
}
}
return ok;
}
//
// MOVE A FLOOR TO ITS DESTINATION (UP OR DOWN)
//
void T_MoveFloor(floormove_t *movefloor)
{
result_e res = 0;
boolean dontupdate = false;
if (movefloor->delaytimer)
{
movefloor->delaytimer--;
return;
}
res = T_MovePlane(movefloor->sector,
movefloor->speed,
movefloor->floordestheight,
movefloor->crush, 0, movefloor->direction);
if (movefloor->type == bounceFloor)
{
const fixed_t origspeed = FixedDiv(movefloor->origspeed,(ELEVATORSPEED/2));
const fixed_t fs = abs(movefloor->sector->floorheight - lines[movefloor->texture].frontsector->floorheight);
const fixed_t bs = abs(movefloor->sector->floorheight - lines[movefloor->texture].backsector->floorheight);
if (fs < bs)
movefloor->speed = FixedDiv(fs,25*FRACUNIT) + FRACUNIT/4;
else
movefloor->speed = FixedDiv(bs,25*FRACUNIT) + FRACUNIT/4;
movefloor->speed = FixedMul(movefloor->speed,origspeed);
}
if (res == pastdest)
{
if (movefloor->direction == 1)
{
switch (movefloor->type)
{
case moveFloorByFrontSector:
if (movefloor->texture < -1) // chained linedef executing
P_LinedefExecute((INT16)(movefloor->texture + INT16_MAX + 2), NULL, NULL);
/* FALLTHRU */
case instantMoveFloorByFrontSector:
if (movefloor->texture > -1) // flat changing
movefloor->sector->floorpic = movefloor->texture;
break;
case bounceFloor: // Graue 03-12-2004
if (movefloor->floordestheight == lines[movefloor->texture].frontsector->floorheight)
movefloor->floordestheight = lines[movefloor->texture].backsector->floorheight;
else
movefloor->floordestheight = lines[movefloor->texture].frontsector->floorheight;
movefloor->direction = (movefloor->floordestheight < movefloor->sector->floorheight) ? -1 : 1;
movefloor->sector->floorspeed = movefloor->speed * movefloor->direction;
movefloor->delaytimer = movefloor->delay;
P_RecalcPrecipInSector(movefloor->sector);
return; // not break, why did this work? Graue 04-03-2004
case bounceFloorCrush: // Graue 03-27-2004
if (movefloor->floordestheight == lines[movefloor->texture].frontsector->floorheight)
{
movefloor->floordestheight = lines[movefloor->texture].backsector->floorheight;
movefloor->speed = movefloor->origspeed = FixedDiv(abs(lines[movefloor->texture].dy),4*FRACUNIT); // return trip, use dy
}
else
{
movefloor->floordestheight = lines[movefloor->texture].frontsector->floorheight;
movefloor->speed = movefloor->origspeed = FixedDiv(abs(lines[movefloor->texture].dx),4*FRACUNIT); // forward again, use dx
}
movefloor->direction = (movefloor->floordestheight < movefloor->sector->floorheight) ? -1 : 1;
movefloor->sector->floorspeed = movefloor->speed * movefloor->direction;
movefloor->delaytimer = movefloor->delay;
P_RecalcPrecipInSector(movefloor->sector);
return; // not break, why did this work? Graue 04-03-2004
case crushFloorOnce:
movefloor->floordestheight = lines[movefloor->texture].frontsector->floorheight;
movefloor->direction = -1;
movefloor->sector->soundorg.z = movefloor->sector->floorheight;
S_StartSound(&movefloor->sector->soundorg,sfx_pstop);
P_RecalcPrecipInSector(movefloor->sector);
return;
default:
break;
}
}
else if (movefloor->direction == -1)
{
switch (movefloor->type)
{
case moveFloorByFrontSector:
if (movefloor->texture < -1) // chained linedef executing
P_LinedefExecute((INT16)(movefloor->texture + INT16_MAX + 2), NULL, NULL);
/* FALLTHRU */
case instantMoveFloorByFrontSector:
if (movefloor->texture > -1) // flat changing
movefloor->sector->floorpic = movefloor->texture;
break;
case bounceFloor: // Graue 03-12-2004
if (movefloor->floordestheight == lines[movefloor->texture].frontsector->floorheight)
movefloor->floordestheight = lines[movefloor->texture].backsector->floorheight;
else
movefloor->floordestheight = lines[movefloor->texture].frontsector->floorheight;
movefloor->direction = (movefloor->floordestheight < movefloor->sector->floorheight) ? -1 : 1;
movefloor->sector->floorspeed = movefloor->speed * movefloor->direction;
movefloor->delaytimer = movefloor->delay;
P_RecalcPrecipInSector(movefloor->sector);
return; // not break, why did this work? Graue 04-03-2004
case bounceFloorCrush: // Graue 03-27-2004
if (movefloor->floordestheight == lines[movefloor->texture].frontsector->floorheight)
{
movefloor->floordestheight = lines[movefloor->texture].backsector->floorheight;
movefloor->speed = movefloor->origspeed = FixedDiv(abs(lines[movefloor->texture].dy),4*FRACUNIT); // return trip, use dy
}
else
{
movefloor->floordestheight = lines[movefloor->texture].frontsector->floorheight;
movefloor->speed = movefloor->origspeed = FixedDiv(abs(lines[movefloor->texture].dx),4*FRACUNIT); // forward again, use dx
}
movefloor->direction = (movefloor->floordestheight < movefloor->sector->floorheight) ? -1 : 1;
movefloor->sector->floorspeed = movefloor->speed * movefloor->direction;
movefloor->delaytimer = movefloor->delay;
P_RecalcPrecipInSector(movefloor->sector);
return; // not break, why did this work? Graue 04-03-2004
case crushFloorOnce:
movefloor->sector->floordata = NULL; // Clear up the thinker so others can use it
P_RemoveThinker(&movefloor->thinker);
movefloor->sector->floorspeed = 0;
P_RecalcPrecipInSector(movefloor->sector);
return;
default:
break;
}
}
movefloor->sector->floordata = NULL; // Clear up the thinker so others can use it
movefloor->sector->floorspeed = 0;
P_RemoveThinker(&movefloor->thinker);
dontupdate = true;
}
if (!dontupdate)
movefloor->sector->floorspeed = movefloor->speed*movefloor->direction;
else
movefloor->sector->floorspeed = 0;
P_RecalcPrecipInSector(movefloor->sector);
}
//
// T_MoveElevator
//
// Move an elevator to it's destination (up or down)
// Called once per tick for each moving floor.
//
// Passed an elevator_t structure that contains all pertinent info about the
// move. See p_spec.h for fields.
// No return.
//
// The function moves the planes differently based on direction, so if it's
// traveling really fast, the floor and ceiling won't hit each other and
// stop the lift.
void T_MoveElevator(elevator_t *elevator)
{
result_e res1 = 0, res2 = 0, res = 0;
boolean dontupdate = false;
fixed_t oldfloor, oldceiling;
if (elevator->delaytimer)
{
elevator->delaytimer--;
return;
}
if (elevator->direction < 0) // moving down
{
if (elevator->type == elevateContinuous)
{
const fixed_t origspeed = FixedDiv(elevator->origspeed,(ELEVATORSPEED/2));
const fixed_t wh = abs(elevator->sector->floorheight - elevator->floorwasheight);
const fixed_t dh = abs(elevator->sector->floorheight - elevator->floordestheight);
// Slow down when reaching destination Tails 12-06-2000
if (wh < dh)
elevator->speed = FixedDiv(wh,25*FRACUNIT) + FRACUNIT/4;
else
elevator->speed = FixedDiv(dh,25*FRACUNIT) + FRACUNIT/4;
if (elevator->origspeed)
{
elevator->speed = FixedMul(elevator->speed,origspeed);
if (elevator->speed > elevator->origspeed)
elevator->speed = (elevator->origspeed);
if (elevator->speed < 1)
elevator->speed = 1;
}
else
{
if (elevator->speed > 3*FRACUNIT)
elevator->speed = 3*FRACUNIT;
if (elevator->speed < 1)
elevator->speed = 1;
}
}
oldfloor = elevator->sector->floorheight;
oldceiling = elevator->sector->ceilingheight;
res1 = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor
(
elevator->sector,
elevator->speed,
elevator->ceilingdestheight,
elevator->distance,
1, // move floor
elevator->direction
);
res2 = T_MovePlane
(
elevator->sector,
elevator->speed,
elevator->floordestheight,
elevator->distance,
0, // move ceiling
elevator->direction
);
if (elevator->distance && (res1 == crushed || res2 == crushed))
{
res = crushed;
elevator->sector->floorheight = oldfloor;
elevator->sector->ceilingheight = oldceiling;
}
else
res = res1;
}
else // moving up
{
if (elevator->type == elevateContinuous)
{
const fixed_t origspeed = FixedDiv(elevator->origspeed,(ELEVATORSPEED/2));
const fixed_t wc = abs(elevator->sector->ceilingheight - elevator->ceilingwasheight);
const fixed_t dc = abs(elevator->sector->ceilingheight - elevator->ceilingdestheight);
// Slow down when reaching destination Tails 12-06-2000
if (wc < dc)
elevator->speed = FixedDiv(wc,25*FRACUNIT) + FRACUNIT/4;
else
elevator->speed = FixedDiv(dc,25*FRACUNIT) + FRACUNIT/4;
if (elevator->origspeed)
{
elevator->speed = FixedMul(elevator->speed,origspeed);
if (elevator->speed > elevator->origspeed)
elevator->speed = (elevator->origspeed);
if (elevator->speed < 1)
elevator->speed = 1;
}
else
{
if (elevator->speed > 3*FRACUNIT)
elevator->speed = 3*FRACUNIT;
if (elevator->speed < 1)
elevator->speed = 1;
}
}
oldfloor = elevator->sector->floorheight;
oldceiling = elevator->sector->ceilingheight;
res1 = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor
(
elevator->sector,
elevator->speed,
elevator->floordestheight,
elevator->distance,
0, // move ceiling
elevator->direction
);
if (res1 != crushed)
{
res2 = T_MovePlane
(
elevator->sector,
elevator->speed,
elevator->ceilingdestheight,
elevator->distance,
1, // move floor
elevator->direction
);
}
if (elevator->distance && (res1 == crushed || res2 == crushed))
{
res = crushed;
elevator->sector->floorheight = oldfloor;
elevator->sector->ceilingheight = oldceiling;
}
else
res = res1;
}
/*
// make floor move sound
if (!(leveltime&7))
S_StartSound(&elevator->sector->soundorg, sfx_stnmov);
*/
if (res == pastdest || res == crushed) // if destination height acheived
{
if (elevator->type == elevateContinuous)
{
if (elevator->direction > 0)
{
elevator->high = 1;
elevator->low = 0;
elevator->direction = -1;
if (elevator->origspeed)
elevator->speed = elevator->origspeed;
else
elevator->speed = 3*FRACUNIT;
elevator->floorwasheight = elevator->floordestheight;
elevator->ceilingwasheight = elevator->ceilingdestheight;
if (elevator->low)
{
elevator->floordestheight =
P_FindNextHighestFloor(elevator->sector, elevator->sector->floorheight);
elevator->ceilingdestheight =
elevator->floordestheight + elevator->sector->ceilingheight - elevator->sector->floorheight;
}
else
{
elevator->floordestheight =
P_FindNextLowestFloor(elevator->sector,elevator->sector->floorheight);
elevator->ceilingdestheight =
elevator->floordestheight + elevator->sector->ceilingheight - elevator->sector->floorheight;
}
// T_MoveElevator(elevator);
}
else
{
elevator->high = 0;
elevator->low = 1;
elevator->direction = 1;
if (elevator->origspeed)
elevator->speed = elevator->origspeed;
else
elevator->speed = 3*FRACUNIT;
elevator->floorwasheight = elevator->floordestheight;
elevator->ceilingwasheight = elevator->ceilingdestheight;
if (elevator->low)
{
elevator->floordestheight =
P_FindNextHighestFloor(elevator->sector, elevator->sector->floorheight);
elevator->ceilingdestheight =
elevator->floordestheight + elevator->sector->ceilingheight - elevator->sector->floorheight;
}
else
{
elevator->floordestheight =
P_FindNextLowestFloor(elevator->sector,elevator->sector->floorheight);
elevator->ceilingdestheight =
elevator->floordestheight + elevator->sector->ceilingheight - elevator->sector->floorheight;
}
// T_MoveElevator(elevator);
}
elevator->delaytimer = elevator->delay;
}
else
{
elevator->sector->floordata = NULL; //jff 2/22/98
elevator->sector->ceilingdata = NULL; //jff 2/22/98
elevator->sector->ceilspeed = 0;
elevator->sector->floorspeed = 0;
P_RemoveThinker(&elevator->thinker); // remove elevator from actives
dontupdate = true;
}
// make floor stop sound
// S_StartSound(&elevator->sector->soundorg, sfx_pstop);
}
if (!dontupdate)
{
elevator->sector->floorspeed = elevator->speed*elevator->direction;
elevator->sector->ceilspeed = 42;
}
else
{
elevator->sector->floorspeed = 0;
elevator->sector->ceilspeed = 0;
elevator->sector->floordata = NULL;
elevator->sector->ceilingdata = NULL;
}
}
//
// T_ContinuousFalling
//
// A sector that continuously falls until its ceiling
// is below that of its actionsector's floor, then
// it instantly returns to its original position and
// falls again.
//
// Useful for things like intermittent falling lava.
//
void T_ContinuousFalling(levelspecthink_t *faller)
{
#define speed vars[0]
#define direction vars[1]
#define floorwasheight vars[2]
#define ceilingwasheight vars[3]
#define floordestheight vars[4]
#define ceilingdestheight vars[5]
if (faller->direction == -1)
{
faller->sector->ceilingheight -= faller->speed;
faller->sector->floorheight -= faller->speed;
}
else
{
faller->sector->ceilingheight += faller->speed;
faller->sector->floorheight += faller->speed;
}
P_CheckSector(faller->sector, false);
if (faller->direction == -1) // Down
{
if (faller->sector->ceilingheight <= faller->ceilingdestheight) // if destination height acheived
{
faller->sector->ceilingheight = faller->ceilingwasheight;
faller->sector->floorheight = faller->floorwasheight;
}
}
else // Up
{
if (faller->sector->floorheight >= faller->floordestheight) // if destination height acheived
{
faller->sector->ceilingheight = faller->ceilingwasheight;
faller->sector->floorheight = faller->floorwasheight;
}
}
faller->sector->floorspeed = faller->speed*faller->direction;
faller->sector->ceilspeed = 42;
faller->sector->moved = true;
#undef speed
#undef direction
#undef floorwasheight
#undef ceilingwasheight
#undef floordestheight
#undef ceilingdestheight
}
//
// P_SectorCheckWater
//
// Like P_MobjCheckWater, but takes a sector instead of a mobj.
static fixed_t P_SectorCheckWater(sector_t *analyzesector,
sector_t *elevatorsec)
{
fixed_t watertop;
// Default if no water exists.
watertop = analyzesector->floorheight - 512*FRACUNIT;
// see if we are in water, and set some flags for later
if (analyzesector->ffloors)
{
ffloor_t *rover;
for (rover = analyzesector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE) || rover->flags & FF_SOLID)
continue;
// If the sector is below the water, don't bother.
if ((elevatorsec->ceilingheight + elevatorsec->floorheight)>>1 < *rover->bottomheight)
continue;
// Do the same as above if the water is too shallow.
if (*rover->topheight < analyzesector->floorheight + abs((elevatorsec->ceilingheight - elevatorsec->floorheight)>>1))
continue;
if (*rover->topheight > watertop) // highest water block is the one to go for
watertop = *rover->topheight;
}
}
return watertop;
}
//////////////////////////////////////////////////
// T_BounceCheese ////////////////////////////////
//////////////////////////////////////////////////
// Bounces a floating cheese
void T_BounceCheese(levelspecthink_t *bouncer)
{
#define speed vars[0]
#define distance vars[1]
#define low vars[2]
#define ceilingwasheight vars[3]
#define floorwasheight vars[4]
fixed_t halfheight;
fixed_t waterheight;
fixed_t floorheight;
sector_t *actionsector;
INT32 i;
if (bouncer->sector->crumblestate == 4 || bouncer->sector->crumblestate == 1
|| bouncer->sector->crumblestate == 2) // Oops! Crumbler says to remove yourself!
{
bouncer->sector->crumblestate = 1;
bouncer->sector->ceilingdata = NULL;
bouncer->sector->ceilspeed = 0;
bouncer->sector->floordata = NULL;
bouncer->sector->floorspeed = 0;
P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
return;
}
// You can use multiple target sectors, but at your own risk!!!
for (i = -1; (i = P_FindSectorFromTag(bouncer->sourceline->tag, i)) >= 0 ;)
{
actionsector = &sectors[i];
actionsector->moved = true;
halfheight = abs(bouncer->sector->ceilingheight - bouncer->sector->floorheight) >> 1;
waterheight = P_SectorCheckWater(actionsector, bouncer->sector); // sorts itself out if there's no suitable water in the sector
floorheight = P_FloorzAtPos(actionsector->soundorg.x, actionsector->soundorg.y, bouncer->sector->floorheight, halfheight << 1);
// Water level is up to the ceiling.
if (waterheight > bouncer->sector->ceilingheight - halfheight && bouncer->sector->ceilingheight >= actionsector->ceilingheight) // Tails 01-08-2004
{
bouncer->sector->ceilingheight = actionsector->ceilingheight;
bouncer->sector->floorheight = bouncer->sector->ceilingheight - (halfheight*2);
T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
P_RecalcPrecipInSector(actionsector);
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
bouncer->sector->floorspeed = 0;
bouncer->sector->ceilspeed = 0;
bouncer->sector->moved = true;
P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
return;
}
// Water level is too shallow.
else if (waterheight < bouncer->sector->floorheight + halfheight && bouncer->sector->floorheight <= floorheight)
{
bouncer->sector->ceilingheight = floorheight + (halfheight << 1);
bouncer->sector->floorheight = floorheight;
T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
P_RecalcPrecipInSector(actionsector);
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
bouncer->sector->floorspeed = 0;
bouncer->sector->ceilspeed = 0;
bouncer->sector->moved = true;
P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
return;
}
else
{
bouncer->ceilingwasheight = waterheight + halfheight;
bouncer->floorwasheight = waterheight - halfheight;
}
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
70*FRACUNIT, 0, 1, -1); // move ceiling
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
0, 0, -1); // move floor
bouncer->sector->floorspeed = -bouncer->speed/2;
bouncer->sector->ceilspeed = 42;
if (bouncer->sector->ceilingheight < bouncer->ceilingwasheight && bouncer->low == 0) // Down
{
if (abs(bouncer->speed) < 6*FRACUNIT)
bouncer->speed -= bouncer->speed/3;
else
bouncer->speed -= bouncer->speed/2;
bouncer->low = 1;
if (abs(bouncer->speed) > 6*FRACUNIT)
{
mobj_t *mp = (void *)&actionsector->soundorg;
actionsector->soundorg.z = bouncer->sector->floorheight;
S_StartSound(mp, sfx_splash);
}
}
else if (bouncer->sector->ceilingheight > bouncer->ceilingwasheight && bouncer->low) // Up
{
if (abs(bouncer->speed) < 6*FRACUNIT)
bouncer->speed -= bouncer->speed/3;
else
bouncer->speed -= bouncer->speed/2;
bouncer->low = 0;
if (abs(bouncer->speed) > 6*FRACUNIT)
{
mobj_t *mp = (void *)&actionsector->soundorg;
actionsector->soundorg.z = bouncer->sector->floorheight;
S_StartSound(mp, sfx_splash);
}
}
if (bouncer->sector->ceilingheight < bouncer->ceilingwasheight) // Down
{
bouncer->speed -= bouncer->distance;
}
else if (bouncer->sector->ceilingheight > bouncer->ceilingwasheight) // Up
{
bouncer->speed += gravity;
}
if (abs(bouncer->speed) < 2*FRACUNIT
&& abs(bouncer->sector->ceilingheight-bouncer->ceilingwasheight) < FRACUNIT/4)
{
bouncer->sector->floorheight = bouncer->floorwasheight;
bouncer->sector->ceilingheight = bouncer->ceilingwasheight;
T_MovePlane(bouncer->sector, 0, bouncer->sector->ceilingheight, 0, 1, -1); // update things on ceiling
T_MovePlane(bouncer->sector, 0, bouncer->sector->floorheight, 0, 0, -1); // update things on floor
bouncer->sector->ceilingdata = NULL;
bouncer->sector->floordata = NULL;
bouncer->sector->floorspeed = 0;
bouncer->sector->ceilspeed = 0;
bouncer->sector->moved = true;
P_RemoveThinker(&bouncer->thinker); // remove bouncer from actives
}
if (bouncer->distance > 0)
bouncer->distance--;
if (actionsector)
P_RecalcPrecipInSector(actionsector);
}
#undef speed
#undef distance
#undef low
#undef ceilingwasheight
#undef floorwasheight
}
//////////////////////////////////////////////////
// T_StartCrumble ////////////////////////////////
//////////////////////////////////////////////////
// Crumbling platform Tails 03-11-2002
//
// DEFINITION OF THE 'CRUMBLESTATE'S:
//
// 0 - No crumble thinker
// 1 - Don't float on water because this is supposed to wait for a crumble
// 2 - Crumble thinker activated, but hasn't fallen yet
// 3 - Crumble thinker is falling
// 4 - Crumble thinker is about to restore to original position
//
void T_StartCrumble(elevator_t *elevator)
{
ffloor_t *rover;
sector_t *sector;
INT32 i;
// Once done, the no-return thinker just sits there,
// constantly 'returning'... kind of an oxymoron, isn't it?
if (((elevator->floordestheight == 1 && elevator->direction == -1)
|| (elevator->floordestheight == 0 && elevator->direction == 1))
&& elevator->type == elevateContinuous) // No return crumbler
{
elevator->sector->ceilspeed = 0;
elevator->sector->floorspeed = 0;
return;
}
if (elevator->distance != 0)
{
if (elevator->distance > 0) // Count down the timer
{
elevator->distance--;
if (elevator->distance <= 0)
elevator->distance = -15*TICRATE; // Timer until platform returns to original position.
else
{
// Timer isn't up yet, so just keep waiting.
elevator->sector->ceilspeed = 0;
elevator->sector->floorspeed = 0;
return;
}
}
else if (++elevator->distance == 0) // Reposition back to original spot
{
for (i = -1; (i = P_FindSectorFromTag(elevator->sourceline->tag, i)) >= 0 ;)
{
sector = &sectors[i];
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (rover->flags & FF_CRUMBLE && rover->flags & FF_FLOATBOB
&& rover->master == elevator->sourceline)
{
rover->alpha = elevator->origspeed;
if (rover->alpha == 0xff)
rover->flags &= ~FF_TRANSLUCENT;
}
}
}
// Up!
if (elevator->floordestheight == 1)
elevator->direction = -1;
else
elevator->direction = 1;
elevator->sector->ceilspeed = 0;
elevator->sector->floorspeed = 0;
return;
}
// Flash to indicate that the platform is about to return.
if (elevator->distance > -224 && (leveltime % ((abs(elevator->distance)/8) + 1) == 0))
{
for (i = -1; (i = P_FindSectorFromTag(elevator->sourceline->tag, i)) >= 0 ;)
{
sector = &sectors[i];
for (rover = sector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_NORETURN) && rover->flags & FF_CRUMBLE && rover->flags & FF_FLOATBOB
&& rover->master == elevator->sourceline)
{
if (rover->alpha == elevator->origspeed)
{
rover->flags |= FF_TRANSLUCENT;
rover->alpha = 0x00;
}
else
{
if (elevator->origspeed == 0xff)
rover->flags &= ~FF_TRANSLUCENT;
rover->alpha = elevator->origspeed;
}
}
}
}
}
// We're about to go back to the original position,
// so set this to let other thinkers know what is
// about to happen.
if (elevator->distance < 0 && elevator->distance > -3)
elevator->sector->crumblestate = 4; // makes T_BounceCheese remove itself
}
if ((elevator->floordestheight == 0 && elevator->direction == -1)
|| (elevator->floordestheight == 1 && elevator->direction == 1)) // Down
{
elevator->sector->crumblestate = 3; // Allow floating now.
// Only fall like this if it isn't meant to float on water
if (elevator->high != 42)
{
elevator->speed += gravity; // Gain more and more speed
if ((elevator->floordestheight == 0 && !(elevator->sector->ceilingheight < -16384*FRACUNIT))
|| (elevator->floordestheight == 1 && !(elevator->sector->ceilingheight > 16384*FRACUNIT)))
{
fixed_t dest;
if (elevator->floordestheight == 1)
dest = elevator->sector->ceilingheight + (elevator->speed*2);
else
dest = elevator->sector->ceilingheight - (elevator->speed*2);
T_MovePlane //jff 4/7/98 reverse order of ceiling/floor
(
elevator->sector,
elevator->speed,
dest,
0,
1, // move floor
elevator->direction
);
if (elevator->floordestheight == 1)
dest = elevator->sector->floorheight + (elevator->speed*2);
else
dest = elevator->sector->floorheight - (elevator->speed*2);
T_MovePlane
(
elevator->sector,
elevator->speed,
dest,
0,
0, // move ceiling
elevator->direction
);
elevator->sector->ceilspeed = 42;
elevator->sector->floorspeed = elevator->speed*elevator->direction;
}
}
}
else // Up (restore to original position)
{
elevator->sector->crumblestate = 1;
elevator->sector->ceilingheight = elevator->ceilingwasheight;
elevator->sector->floorheight = elevator->floorwasheight;
elevator->sector->floordata = NULL;
elevator->sector->ceilingdata = NULL;
elevator->sector->ceilspeed = 0;
elevator->sector->floorspeed = 0;
elevator->sector->moved = true;
P_RemoveThinker(&elevator->thinker);
}
for (i = -1; (i = P_FindSectorFromTag(elevator->sourceline->tag, i)) >= 0 ;)
{
sector = &sectors[i];
sector->moved = true;
P_RecalcPrecipInSector(sector);
}
}
//////////////////////////////////////////////////
// T_MarioBlock //////////////////////////////////
//////////////////////////////////////////////////
// Mario hits a block!
//
void T_MarioBlock(levelspecthink_t *block)
{
INT32 i;
#define speed vars[1]
#define direction vars[2]
#define floorwasheight vars[3]
#define ceilingwasheight vars[4]
#define distance vars[5]
#define low vars[6]
T_MovePlane
(
block->sector,
block->speed,
block->sector->ceilingheight + 70*FRACUNIT * block->direction,
0,
1, // move floor
block->direction
);
T_MovePlane
(
block->sector,
block->speed,
block->sector->floorheight + 70*FRACUNIT * block->direction,
0,
0, // move ceiling
block->direction
);
if (block->sector->ceilingheight >= block->ceilingwasheight + 32*FRACUNIT) // Go back down now..
block->direction = -block->direction;
else if (block->sector->ceilingheight <= block->ceilingwasheight)
{
block->sector->ceilingheight = block->ceilingwasheight;
block->sector->floorheight = block->floorwasheight;
P_RemoveThinker(&block->thinker);
block->sector->floordata = NULL;
block->sector->ceilingdata = NULL;
block->sector->floorspeed = 0;
block->sector->ceilspeed = 0;
block->direction = 0;
}
for (i = -1; (i = P_FindSectorFromTag((INT16)block->vars[0], i)) >= 0 ;)
P_RecalcPrecipInSector(&sectors[i]);
#undef speed
#undef direction
#undef floorwasheight
#undef ceilingwasheight
#undef distance
#undef low
}
void T_SpikeSector(levelspecthink_t *spikes)
{
mobj_t *thing;
msecnode_t *node;
boolean dothepain;
sector_t *affectsec;
node = spikes->sector->touching_thinglist; // things touching this sector
for (; node; node = node->m_thinglist_next)
{
thing = node->m_thing;
if (!thing->player)
continue;
dothepain = false;
affectsec = &sectors[spikes->vars[0]];
if (affectsec == spikes->sector) // Applied to an actual sector
{
fixed_t affectfloor = P_GetSpecialBottomZ(thing, affectsec, affectsec);
fixed_t affectceil = P_GetSpecialTopZ(thing, affectsec, affectsec);
if (affectsec->flags & SF_FLIPSPECIAL_FLOOR)
{
if (!(thing->eflags & MFE_VERTICALFLIP) && thing->momz > 0)
continue;
if (thing->z == affectfloor)
dothepain = true;
}
if (affectsec->flags & SF_FLIPSPECIAL_CEILING)
{
if ((thing->eflags & MFE_VERTICALFLIP) && thing->momz < 0)
continue;
if (thing->z + thing->height == affectceil)
dothepain = true;
}
}
else
{
fixed_t affectfloor = P_GetSpecialBottomZ(thing, affectsec, spikes->sector);
fixed_t affectceil = P_GetSpecialTopZ(thing, affectsec, spikes->sector);
if (affectsec->flags & SF_FLIPSPECIAL_FLOOR)
{
if (!(thing->eflags & MFE_VERTICALFLIP) && thing->momz > 0)
continue;
if (thing->z == affectceil)
dothepain = true;
}
if (affectsec->flags & SF_FLIPSPECIAL_CEILING)
{
if ((thing->eflags & MFE_VERTICALFLIP) && thing->momz < 0)
continue;
if (thing->z + thing->height == affectfloor)
dothepain = true;
}
}
if (dothepain)
{
P_DamageMobj(thing, NULL, NULL, 1, DMG_SPIKE);
break;
}
}
}
void T_FloatSector(levelspecthink_t *floater)
{
fixed_t cheeseheight;
sector_t *actionsector;
INT32 secnum;
cheeseheight = (floater->sector->ceilingheight + floater->sector->floorheight)>>1;
// Just find the first sector with the tag.
// Doesn't work with multiple sectors that have different floor/ceiling heights.
secnum = P_FindSectorFromTag((INT16)floater->vars[0], -1);
if (secnum > 0)
actionsector = &sectors[secnum];
else
actionsector = NULL;
if (actionsector)
{
//boolean floatanyway = false; // Ignore the crumblestate setting.
fixed_t waterheight = P_SectorCheckWater(actionsector, floater->sector); // find the highest suitable water block around
if (waterheight == cheeseheight) // same height, no floating needed
;
else if (floater->sector->floorheight == actionsector->floorheight && waterheight < cheeseheight) // too low
;
else if (floater->sector->ceilingheight == actionsector->ceilingheight && waterheight > cheeseheight) // too high
;
// we have something to float in! Or we're for some reason above the ground, let's fall anyway
else if (floater->sector->crumblestate == 0 || floater->sector->crumblestate >= 3/* || floatanyway*/)
EV_BounceSector(floater->sector, FRACUNIT, floater->sourceline);
P_RecalcPrecipInSector(actionsector);
}
}
//
// T_BridgeThinker
//
// Kind of like T_RaiseSector,
// but spreads out across
// multiple FOFs at varying
// intensity.
//
void T_BridgeThinker(levelspecthink_t *bridge)
{
msecnode_t *node;
mobj_t *thing;
sector_t *sector;
sector_t *controlsec = NULL;
INT32 i, k;
INT16 j;
boolean playeronme = false;
fixed_t ceilingdestination = 0, floordestination = 0;
result_e res = 0;
#define ORIGFLOORHEIGHT (bridge->vars[0])
#define ORIGCEILINGHEIGHT (bridge->vars[1])
#define BASESPEED (bridge->vars[2])
#define CURSPEED (bridge->vars[3])
#define STARTTAG ((INT16)bridge->vars[4])
#define ENDTAG ((INT16)bridge->vars[5])
#define DIRECTION (bridge->vars[8])
#define SAGAMT (8*FRACUNIT)
fixed_t lowceilheight = ORIGCEILINGHEIGHT - SAGAMT;
fixed_t lowfloorheight = ORIGFLOORHEIGHT - SAGAMT;
#define LOWCEILINGHEIGHT (lowceilheight)
#define LOWFLOORHEIGHT (lowfloorheight)
#define STARTCONTROLTAG (ENDTAG + 1)
#define ENDCONTROLTAG (ENDTAG + (ENDTAG - STARTTAG) + 1)
// Is someone standing on it?
for (j = STARTTAG; j <= ENDTAG; j++)
{
for (i = -1; (i = P_FindSectorFromTag(j, i)) >= 0 ;)
{
sector = &sectors[i];
// Nab the control sector that this sector belongs to.
k = P_FindSectorFromTag((INT16)(j + (ENDTAG-STARTTAG) + 1), -1);
if (k == -1)
break;
controlsec = &sectors[k];
// Is a player standing on me?
for (node = sector->touching_thinglist; node; node = node->m_thinglist_next)
{
thing = node->m_thing;
if (!thing->player)
continue;
if (!(thing->z == controlsec->ceilingheight))
continue;
playeronme = true;
goto wegotit; // Just take the first one?
}
}
}
wegotit:
if (playeronme)
{
// Lower controlsec like a regular T_RaiseSector
// Set the heights of all the other control sectors to
// be a gradient of this height toward the edges
}
else
{
// Raise controlsec like a regular T_RaiseSector
// Set the heights of all the other control sectors to
// be a gradient of this height toward the edges.
}
if (playeronme && controlsec)
{
INT32 dist;
bridge->sector = controlsec;
CURSPEED = BASESPEED;
{
// Translate tags to - 0 + range
/*so you have a number in [min, max].
let range = max - min, subtract min
from your number to get [0, range].
subtract range/2 to get [-range/2, range/2].
take absolute value and get [0, range/2] where
lower number = closer to midpoint. divide by
range/2 to get [0, 1]. subtract that number
from 1 to get [0, 1] with higher number = closer
to midpoint. multiply this by max sag amount*/
INT32 midpoint = STARTCONTROLTAG + ((ENDCONTROLTAG-STARTCONTROLTAG) + 1)/2;
// INT32 tagstart = STARTTAG - midpoint;
// INT32 tagend = ENDTAG - midpoint;
// CONS_Debug(DBG_GAMELOGIC, "tagstart is %d, tagend is %d\n", tagstart, tagend);
// Sag is adjusted by how close you are to the center
dist = ((ENDCONTROLTAG - STARTCONTROLTAG))/2 - abs(bridge->sector->tag - midpoint);
// CONS_Debug(DBG_GAMELOGIC, "Dist is %d\n", dist);
LOWCEILINGHEIGHT -= (SAGAMT) * dist;
LOWFLOORHEIGHT -= (SAGAMT) * dist;
}
// go down
if (bridge->sector->ceilingheight <= LOWCEILINGHEIGHT)
{
bridge->sector->floorheight = LOWCEILINGHEIGHT - (bridge->sector->ceilingheight - bridge->sector->floorheight);
bridge->sector->ceilingheight = LOWCEILINGHEIGHT;
bridge->sector->ceilspeed = 0;
bridge->sector->floorspeed = 0;
goto dorest;
}
DIRECTION = -1;
ceilingdestination = LOWCEILINGHEIGHT;
floordestination = LOWFLOORHEIGHT;
if ((bridge->sector->ceilingheight - LOWCEILINGHEIGHT)
< (ORIGCEILINGHEIGHT - bridge->sector->ceilingheight))
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the bottom
CURSPEED = FixedMul(CURSPEED,FixedDiv(bridge->sector->ceilingheight - LOWCEILINGHEIGHT, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
else
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the top
CURSPEED = FixedMul(CURSPEED,FixedDiv(ORIGCEILINGHEIGHT - bridge->sector->ceilingheight, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
// CONS_Debug(DBG_GAMELOGIC, "Curspeed is %d\n", CURSPEED>>FRACBITS);
res = T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
ceilingdestination, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
DIRECTION // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
floordestination, // dest
0, // crush
0, // floor or ceiling (0 for floor)
DIRECTION // direction
);
bridge->sector->ceilspeed = 42;
bridge->sector->floorspeed = CURSPEED*DIRECTION;
dorest:
// Adjust joined sector heights
{
sector_t *sourcesec = bridge->sector;
INT32 divisor = sourcesec->tag - ENDTAG + 1;
fixed_t heightdiff = ORIGCEILINGHEIGHT - sourcesec->ceilingheight;
fixed_t interval;
INT32 plusplusme = 0;
if (divisor > 0)
{
interval = heightdiff/divisor;
// CONS_Debug(DBG_GAMELOGIC, "interval is %d\n", interval>>FRACBITS);
// TODO: Use T_MovePlane
for (j = (INT16)(ENDTAG+1); j <= sourcesec->tag; j++, plusplusme++)
{
for (i = -1; (i = P_FindSectorFromTag(j, i)) >= 0 ;)
{
if (sectors[i].ceilingheight >= sourcesec->ceilingheight)
{
sectors[i].ceilingheight = ORIGCEILINGHEIGHT - (interval*plusplusme);
sectors[i].floorheight = ORIGFLOORHEIGHT - (interval*plusplusme);
}
else // Do the regular rise
{
bridge->sector = &sectors[i];
CURSPEED = BASESPEED/2;
// rise back up
if (bridge->sector->ceilingheight >= ORIGCEILINGHEIGHT)
{
bridge->sector->floorheight = ORIGCEILINGHEIGHT - (bridge->sector->ceilingheight - bridge->sector->floorheight);
bridge->sector->ceilingheight = ORIGCEILINGHEIGHT;
bridge->sector->ceilspeed = 0;
bridge->sector->floorspeed = 0;
continue;
}
DIRECTION = 1;
ceilingdestination = ORIGCEILINGHEIGHT;
floordestination = ORIGFLOORHEIGHT;
// CONS_Debug(DBG_GAMELOGIC, "ceildest: %d, floordest: %d\n", ceilingdestination>>FRACBITS, floordestination>>FRACBITS);
if ((bridge->sector->ceilingheight - LOWCEILINGHEIGHT)
< (ORIGCEILINGHEIGHT - bridge->sector->ceilingheight))
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the bottom
CURSPEED = FixedMul(CURSPEED,FixedDiv(bridge->sector->ceilingheight - LOWCEILINGHEIGHT, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
else
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the top
CURSPEED = FixedMul(CURSPEED,FixedDiv(ORIGCEILINGHEIGHT - bridge->sector->ceilingheight, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
res = T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
ceilingdestination, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
DIRECTION // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
floordestination, // dest
0, // crush
0, // floor or ceiling (0 for floor)
DIRECTION // direction
);
bridge->sector->ceilspeed = 42;
bridge->sector->floorspeed = CURSPEED*DIRECTION;
}
}
}
}
// Now the other side
divisor = ENDTAG + (ENDTAG-STARTTAG) + 1;
divisor -= sourcesec->tag;
if (divisor > 0)
{
interval = heightdiff/divisor;
plusplusme = 0;
// CONS_Debug(DBG_GAMELOGIC, "interval2 is %d\n", interval>>FRACBITS);
for (j = (INT16)(sourcesec->tag+1); j <= ENDTAG + (ENDTAG-STARTTAG) + 1; j++, plusplusme++)
{
for (i = -1; (i = P_FindSectorFromTag(j, i)) >= 0 ;)
{
if (sectors[i].ceilingheight >= sourcesec->ceilingheight)
{
sectors[i].ceilingheight = sourcesec->ceilingheight + (interval*plusplusme);
sectors[i].floorheight = sourcesec->floorheight + (interval*plusplusme);
}
else // Do the regular rise
{
bridge->sector = &sectors[i];
CURSPEED = BASESPEED/2;
// rise back up
if (bridge->sector->ceilingheight >= ORIGCEILINGHEIGHT)
{
bridge->sector->floorheight = ORIGCEILINGHEIGHT - (bridge->sector->ceilingheight - bridge->sector->floorheight);
bridge->sector->ceilingheight = ORIGCEILINGHEIGHT;
bridge->sector->ceilspeed = 0;
bridge->sector->floorspeed = 0;
continue;
}
DIRECTION = 1;
ceilingdestination = ORIGCEILINGHEIGHT;
floordestination = ORIGFLOORHEIGHT;
// CONS_Debug(DBG_GAMELOGIC, "ceildest: %d, floordest: %d\n", ceilingdestination>>FRACBITS, floordestination>>FRACBITS);
if ((bridge->sector->ceilingheight - LOWCEILINGHEIGHT)
< (ORIGCEILINGHEIGHT - bridge->sector->ceilingheight))
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the bottom
CURSPEED = FixedMul(CURSPEED,FixedDiv(bridge->sector->ceilingheight - LOWCEILINGHEIGHT, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
else
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the top
CURSPEED = FixedMul(CURSPEED,FixedDiv(ORIGCEILINGHEIGHT - bridge->sector->ceilingheight, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
res = T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
ceilingdestination, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
DIRECTION // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
floordestination, // dest
0, // crush
0, // floor or ceiling (0 for floor)
DIRECTION // direction
);
bridge->sector->ceilspeed = 42;
bridge->sector->floorspeed = CURSPEED*DIRECTION;
}
}
}
}
}
// for (i = -1; (i = P_FindSectorFromTag(bridge->sourceline->tag, i)) >= 0 ;)
// P_RecalcPrecipInSector(&sectors[i]);
}
else
{
// Iterate control sectors
for (j = (INT16)(ENDTAG+1); j <= (ENDTAG+(ENDTAG-STARTTAG)+1); j++)
{
for (i = -1; (i = P_FindSectorFromTag(j, i)) >= 0 ;)
{
bridge->sector = &sectors[i];
CURSPEED = BASESPEED/2;
// rise back up
if (bridge->sector->ceilingheight >= ORIGCEILINGHEIGHT)
{
bridge->sector->floorheight = ORIGCEILINGHEIGHT - (bridge->sector->ceilingheight - bridge->sector->floorheight);
bridge->sector->ceilingheight = ORIGCEILINGHEIGHT;
bridge->sector->ceilspeed = 0;
bridge->sector->floorspeed = 0;
continue;
}
DIRECTION = 1;
ceilingdestination = ORIGCEILINGHEIGHT;
floordestination = ORIGFLOORHEIGHT;
// CONS_Debug(DBG_GAMELOGIC, "ceildest: %d, floordest: %d\n", ceilingdestination>>FRACBITS, floordestination>>FRACBITS);
if ((bridge->sector->ceilingheight - LOWCEILINGHEIGHT)
< (ORIGCEILINGHEIGHT - bridge->sector->ceilingheight))
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the bottom
CURSPEED = FixedMul(CURSPEED,FixedDiv(bridge->sector->ceilingheight - LOWCEILINGHEIGHT, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
else
{
fixed_t origspeed = CURSPEED;
// Slow down as you get closer to the top
CURSPEED = FixedMul(CURSPEED,FixedDiv(ORIGCEILINGHEIGHT - bridge->sector->ceilingheight, (ORIGCEILINGHEIGHT - LOWCEILINGHEIGHT)>>5));
if (CURSPEED <= origspeed/16)
CURSPEED = origspeed/16;
else if (CURSPEED > origspeed)
CURSPEED = origspeed;
}
res = T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
ceilingdestination, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
DIRECTION // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
bridge->sector, // sector
CURSPEED, // speed
floordestination, // dest
0, // crush
0, // floor or ceiling (0 for floor)
DIRECTION // direction
);
bridge->sector->ceilspeed = 42;
bridge->sector->floorspeed = CURSPEED*DIRECTION;
}
}
// Update precip
}
#undef SAGAMT
#undef LOWFLOORHEIGHT
#undef LOWCEILINGHEIGHT
#undef ORIGFLOORHEIGHT
#undef ORIGCEILINGHEIGHT
#undef BASESPEED
#undef CURSPEED
#undef STARTTAG
#undef ENDTAG
#undef DIRECTION
}
static mobj_t *SearchMarioNode(msecnode_t *node)
{
mobj_t *thing = NULL;
for (; node; node = node->m_thinglist_next)
{
// Things which should NEVER be ejected from a MarioBlock, by type.
switch (node->m_thing->type)
{
case MT_NULL:
case MT_UNKNOWN:
case MT_TAILSOVERLAY:
case MT_THOK:
case MT_GHOST:
case MT_OVERLAY:
case MT_EMERALDSPAWN:
case MT_ELEMENTAL_ORB:
case MT_ATTRACT_ORB:
case MT_FORCE_ORB:
case MT_ARMAGEDDON_ORB:
case MT_WHIRLWIND_ORB:
case MT_PITY_ORB:
case MT_FLAMEAURA_ORB:
case MT_BUBBLEWRAP_ORB:
case MT_THUNDERCOIN_ORB:
case MT_IVSP:
case MT_SUPERSPARK:
case MT_RAIN:
case MT_SNOWFLAKE:
case MT_SPLISH:
case MT_SMOKE:
case MT_SMALLBUBBLE:
case MT_MEDIUMBUBBLE:
case MT_TFOG:
case MT_SEED:
case MT_PARTICLE:
case MT_SCORE:
case MT_DROWNNUMBERS:
case MT_GOTEMERALD:
case MT_LOCKON:
case MT_TAG:
case MT_GOTFLAG:
case MT_HOOP:
case MT_HOOPCOLLIDE:
case MT_NIGHTSCORE:
#ifdef SEENAMES
case MT_NAMECHECK: // DEFINITELY not this, because it is client-side.
#endif
continue;
default:
break;
}
// Ignore popped monitors, too.
if (node->m_thing->health == 0 // this only really applies for monitors
|| (!(node->m_thing->flags & MF_MONITOR) && (mobjinfo[node->m_thing->type].flags & MF_MONITOR))) // gold monitor support
continue;
// Okay, we found something valid.
if (!thing // take either the first thing
|| node->m_thing->x < thing->x // or the thing with the lowest x value (left to right item order)
|| node->m_thing->y < thing->y) // or the thing with the lowest y value (top to bottom item order)
thing = node->m_thing;
}
return thing;
}
void T_MarioBlockChecker(levelspecthink_t *block)
{
line_t *masterline = block->sourceline;
if (block->vars[2] == 1) // Don't update the textures when the block's being bumped upwards.
return;
if (SearchMarioNode(block->sector->touching_thinglist))
{
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; // Update textures
if (masterline->backsector)
{
block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->ceilingpic; // Update flats to be backside's ceiling
}
}
else
{
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture;
if (masterline->backsector)
{
block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->floorpic; // Update flats to be backside's floor
}
}
}
// This is the Thwomp's 'brain'. It looks around for players nearby, and if
// it finds any, **SMASH**!!! Muahahhaa....
void T_ThwompSector(levelspecthink_t *thwomp)
{
#define speed vars[1]
#define direction vars[2]
#define distance vars[3]
#define floorwasheight vars[4]
#define ceilingwasheight vars[5]
fixed_t thwompx, thwompy;
sector_t *actionsector;
ffloor_t *rover = NULL;
INT32 secnum;
// If you just crashed down, wait a second before coming back up.
if (--thwomp->distance > 0)
{
sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].bottomtexture;
return;
}
// Just find the first sector with the tag.
// Doesn't work with multiple sectors that have different floor/ceiling heights.
secnum = P_FindSectorFromTag((INT16)thwomp->vars[0], -1);
if (secnum > 0)
{
actionsector = &sectors[secnum];
// Look for thwomp FFloor
for (rover = actionsector->ffloors; rover; rover = rover->next)
{
if (rover->master == thwomp->sourceline)
break;
}
}
else
return; // Bad bad bad!
thwompx = actionsector->soundorg.x;
thwompy = actionsector->soundorg.y;
if (thwomp->direction > 0) // Moving back up..
{
result_e res = 0;
// Set the texture from the lower one (normal)
sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].bottomtexture;
/// \note this should only have to be done once, but is already done repeatedly, above
if (thwomp->sourceline->flags & ML_EFFECT5)
thwomp->speed = thwomp->sourceline->dx/8;
else
thwomp->speed = 2*FRACUNIT;
res = T_MovePlane
(
thwomp->sector, // sector
thwomp->speed, // speed
thwomp->floorwasheight, // dest
0, // crush
0, // floor or ceiling (0 for floor)
thwomp->direction // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
thwomp->sector, // sector
thwomp->speed, // speed
thwomp->ceilingwasheight, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
thwomp->direction // direction
);
if (res == pastdest)
thwomp->direction = 0; // stop moving
thwomp->sector->ceilspeed = 42;
thwomp->sector->floorspeed = thwomp->speed*thwomp->direction;
}
else if (thwomp->direction < 0) // Crashing down!
{
result_e res = 0;
// Set the texture from the upper one (angry)
sides[thwomp->sourceline->sidenum[0]].midtexture = sides[thwomp->sourceline->sidenum[0]].toptexture;
if (thwomp->sourceline->flags & ML_EFFECT5)
thwomp->speed = thwomp->sourceline->dy/8;
else
thwomp->speed = 10*FRACUNIT;
res = T_MovePlane
(
thwomp->sector, // sector
thwomp->speed, // speed
P_FloorzAtPos(thwompx, thwompy, thwomp->sector->floorheight,
thwomp->sector->ceilingheight - thwomp->sector->floorheight), // dest
0, // crush
0, // floor or ceiling (0 for floor)
thwomp->direction // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
thwomp->sector, // sector
thwomp->speed, // speed
P_FloorzAtPos(thwompx, thwompy, thwomp->sector->floorheight,
thwomp->sector->ceilingheight
- (thwomp->sector->floorheight + thwomp->speed))
+ (thwomp->sector->ceilingheight
- (thwomp->sector->floorheight + thwomp->speed/2)), // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
thwomp->direction // direction
);
if (res == pastdest)
{
mobj_t *mp = (void *)&actionsector->soundorg;
if (!rover || (rover->flags & FF_EXISTS))
{
if (thwomp->sourceline->flags & ML_EFFECT4)
S_StartSound(mp, sides[thwomp->sourceline->sidenum[0]].textureoffset>>FRACBITS);
else
S_StartSound(mp, sfx_thwomp);
}
thwomp->direction = 1; // start heading back up
thwomp->distance = TICRATE; // but only after a small delay
}
thwomp->sector->ceilspeed = 42;
thwomp->sector->floorspeed = thwomp->speed*thwomp->direction;
}
else // Not going anywhere, so look for players.
{
thinker_t *th;
mobj_t *mo;
if (!rover || (rover->flags & FF_EXISTS))
{
// scan the thinkers to find players!
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
mo = (mobj_t *)th;
if (mo->type == MT_PLAYER && mo->health && mo->player && !mo->player->spectator
&& mo->z <= thwomp->sector->ceilingheight
&& P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
{
thwomp->direction = -1;
break;
}
}
}
thwomp->sector->ceilspeed = 0;
thwomp->sector->floorspeed = 0;
}
P_RecalcPrecipInSector(actionsector);
#undef speed
#undef direction
#undef distance
#undef floorwasheight
#undef ceilingwasheight
}
//
// T_NoEnemiesThinker
//
// Runs a linedef exec when no more MF_ENEMY/MF_BOSS objects with health are in the area
// \sa P_AddNoEnemiesThinker
//
void T_NoEnemiesSector(levelspecthink_t *nobaddies)
{
size_t i;
fixed_t upperbound, lowerbound;
sector_t *sec = NULL;
sector_t *targetsec = NULL;
INT32 secnum = -1;
msecnode_t *node;
mobj_t *thing;
boolean FOFsector = false;
while ((secnum = P_FindSectorFromLineTag(nobaddies->sourceline, secnum)) >= 0)
{
sec = &sectors[secnum];
FOFsector = false;
// Check the lines of this sector, to see if it is a FOF control sector.
for (i = 0; i < sec->linecount; i++)
{
INT32 targetsecnum = -1;
if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300)
continue;
FOFsector = true;
while ((targetsecnum = P_FindSectorFromLineTag(sec->lines[i], targetsecnum)) >= 0)
{
targetsec = &sectors[targetsecnum];
upperbound = targetsec->ceilingheight;
lowerbound = targetsec->floorheight;
node = targetsec->touching_thinglist; // things touching this sector
while (node)
{
thing = node->m_thing;
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
&& thing->z < upperbound && thing->z+thing->height > lowerbound)
return;
node = node->m_thinglist_next;
}
}
}
if (!FOFsector)
{
upperbound = sec->ceilingheight;
lowerbound = sec->floorheight;
node = sec->touching_thinglist; // things touching this sector
while (node)
{
thing = node->m_thing;
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
&& thing->z < upperbound && thing->z+thing->height > lowerbound)
return;
node = node->m_thinglist_next;
}
}
}
CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", nobaddies->sourceline->tag);
// No enemies found, run the linedef exec and terminate this thinker
P_RunTriggerLinedef(nobaddies->sourceline, NULL, NULL);
P_RemoveThinker(&nobaddies->thinker);
}
//
// P_IsObjectOnRealGround
//
// Helper function for T_EachTimeThinker
// Like P_IsObjectOnGroundIn, except ONLY THE REAL GROUND IS CONSIDERED, NOT FOFS
// I'll consider whether to make this a more globally accessible function or whatever in future
// -- Monster Iestyn
//
static boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec)
{
// Is the object in reverse gravity?
if (mo->eflags & MFE_VERTICALFLIP)
{
// Detect if the player is on the ceiling.
if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec))
return true;
}
// Nope!
else
{
// Detect if the player is on the floor.
if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec))
return true;
}
return false;
}
//
// P_HavePlayersEnteredArea
//
// Helper function for T_EachTimeThinker
//
static INT32 P_HavePlayersEnteredArea(boolean *curPlayers, boolean *oldPlayers, boolean inAndOut)
{
INT32 i;
// Easy check... nothing has changed
if (!memcmp(curPlayers, oldPlayers, sizeof(boolean)*MAXPLAYERS))
return -1;
// Otherwise, we have to check if any new players have entered
for (i = 0; i < MAXPLAYERS; i++)
{
if (inAndOut && !curPlayers[i] && oldPlayers[i])
return i;
if (curPlayers[i] && !oldPlayers[i])
return i;
}
return -1;
}
//
// T_EachTimeThinker
//
// Runs a linedef exec whenever a player enters an area.
// Keeps track of players currently in the area and notices any changes.
//
// \sa P_AddEachTimeThinker
//
void T_EachTimeThinker(levelspecthink_t *eachtime)
{
size_t i, j;
sector_t *sec = NULL;
sector_t *targetsec = NULL;
//sector_t *usesec = NULL;
INT32 secnum = -1;
INT32 affectPlayer = 0;
boolean oldPlayersInArea[MAXPLAYERS];
boolean playersInArea[MAXPLAYERS];
boolean oldPlayersOnArea[MAXPLAYERS];
boolean playersOnArea[MAXPLAYERS];
boolean *oldPlayersArea;
boolean *playersArea;
boolean FOFsector = false;
boolean inAndOut = false;
boolean floortouch = false;
fixed_t bottomheight, topheight;
msecnode_t *node;
ffloor_t *rover;
for (i = 0; i < MAXPLAYERS; i++)
{
if (i & 1)
{
oldPlayersInArea[i] = eachtime->vars[i/2] & 65535;
oldPlayersOnArea[i] = eachtime->var2s[i/2] & 65535;
eachtime->vars[i/2] = 0;
eachtime->var2s[i/2] = 0;
}
else
{
oldPlayersInArea[i] = eachtime->vars[i/2] >> 16;
oldPlayersOnArea[i] = eachtime->var2s[i/2] >> 16;
}
playersInArea[i] = false;
playersOnArea[i] = false;
}
while ((secnum = P_FindSectorFromLineTag(eachtime->sourceline, secnum)) >= 0)
{
sec = &sectors[secnum];
FOFsector = false;
if (GETSECSPECIAL(sec->special, 2) == 3 || GETSECSPECIAL(sec->special, 2) == 5)
floortouch = true;
else if (GETSECSPECIAL(sec->special, 2) >= 1 && GETSECSPECIAL(sec->special, 2) <= 8)
floortouch = false;
else
continue;
// Check the lines of this sector, to see if it is a FOF control sector.
for (i = 0; i < sec->linecount; i++)
{
INT32 targetsecnum = -1;
if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300)
continue;
FOFsector = true;
while ((targetsecnum = P_FindSectorFromLineTag(sec->lines[i], targetsecnum)) >= 0)
{
targetsec = &sectors[targetsecnum];
// Find the FOF corresponding to the control linedef
for (rover = targetsec->ffloors; rover; rover = rover->next)
{
if (rover->master == sec->lines[i])
break;
}
if (!rover) // This should be impossible, but don't complain if it is the case somehow
continue;
if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there
continue;
for (j = 0; j < MAXPLAYERS; j++)
{
if (!playeringame[j])
continue;
if (!players[j].mo)
continue;
if (players[j].mo->health <= 0)
continue;
if ((netgame || multiplayer) && players[j].spectator)
continue;
if (players[j].mo->subsector->sector == targetsec)
;
else if (sec->flags & SF_TRIGGERSPECIAL_TOUCH)
{
boolean insector = false;
for (node = players[j].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (node->m_sector == targetsec)
{
insector = true;
break;
}
}
if (!insector)
continue;
}
else
continue;
topheight = P_GetSpecialTopZ(players[j].mo, sec, targetsec);
bottomheight = P_GetSpecialBottomZ(players[j].mo, sec, targetsec);
if (players[j].mo->z > topheight)
continue;
if (players[j].mo->z + players[j].mo->height < bottomheight)
continue;
if (floortouch == true && P_IsObjectOnGroundIn(players[j].mo, targetsec))
{
if (j & 1)
eachtime->var2s[j/2] |= 1;
else
eachtime->var2s[j/2] |= 1 << 16;
playersOnArea[j] = true;
}
else
{
if (j & 1)
eachtime->vars[j/2] |= 1;
else
eachtime->vars[j/2] |= 1 << 16;
playersInArea[j] = true;
}
}
}
}
if (!FOFsector)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (!players[i].mo)
continue;
if (players[i].mo->health <= 0)
continue;
if ((netgame || multiplayer) && players[i].spectator)
continue;
if (players[i].mo->subsector->sector == sec)
;
else if (sec->flags & SF_TRIGGERSPECIAL_TOUCH)
{
boolean insector = false;
for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (node->m_sector == sec)
{
insector = true;
break;
}
}
if (!insector)
continue;
}
else
continue;
if (!(players[i].mo->subsector->sector == sec
|| P_PlayerTouchingSectorSpecial(&players[i], 2, (GETSECSPECIAL(sec->special, 2))) == sec))
continue;
if (floortouch == true && P_IsObjectOnRealGround(players[i].mo, sec))
{
if (i & 1)
eachtime->var2s[i/2] |= 1;
else
eachtime->var2s[i/2] |= 1 << 16;
playersOnArea[i] = true;
}
else
{
if (i & 1)
eachtime->vars[i/2] |= 1;
else
eachtime->vars[i/2] |= 1 << 16;
playersInArea[i] = true;
}
}
}
}
if ((eachtime->sourceline->flags & ML_BOUNCY) == ML_BOUNCY)
inAndOut = true;
// Check if a new player entered.
// If not, check if a player hit the floor.
// If either condition is true, execute.
if (floortouch == true)
{
playersArea = playersOnArea;
oldPlayersArea = oldPlayersOnArea;
}
else
{
playersArea = playersInArea;
oldPlayersArea = oldPlayersInArea;
}
while ((affectPlayer = P_HavePlayersEnteredArea(playersArea, oldPlayersArea, inAndOut)) != -1)
{
if (GETSECSPECIAL(sec->special, 2) == 2 || GETSECSPECIAL(sec->special, 2) == 3)
{
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (!players[i].mo)
continue;
if (players[i].mo->health <= 0)
continue;
if ((netgame || multiplayer) && players[i].spectator)
continue;
if (!playersArea[i])
return;
}
}
CONS_Debug(DBG_GAMELOGIC, "Trying to activate each time executor with tag %d\n", eachtime->sourceline->tag);
// 03/08/14 -Monster Iestyn
// No more stupid hacks involving changing eachtime->sourceline's tag or special or whatever!
// This should now run ONLY the stuff for eachtime->sourceline itself, instead of all trigger linedefs sharing the same tag.
// Makes much more sense doing it this way, honestly.
P_RunTriggerLinedef(eachtime->sourceline, players[affectPlayer].mo, sec);
if (!eachtime->sourceline->special) // this happens only for "Trigger on X calls" linedefs
P_RemoveThinker(&eachtime->thinker);
oldPlayersArea[affectPlayer]=playersArea[affectPlayer];
}
}
//
// T_RaiseSector
//
// Rises up to its topmost position when a
// player steps on it. Lowers otherwise.
//
void T_RaiseSector(levelspecthink_t *raise)
{
msecnode_t *node;
mobj_t *thing;
sector_t *sector;
INT32 i;
boolean playeronme = false;
fixed_t ceilingdestination, floordestination;
result_e res = 0;
if (raise->sector->crumblestate >= 3 || raise->sector->ceilingdata)
return;
for (i = -1; (i = P_FindSectorFromTag(raise->sourceline->tag, i)) >= 0 ;)
{
sector = &sectors[i];
// Is a player standing on me?
for (node = sector->touching_thinglist; node; node = node->m_thinglist_next)
{
thing = node->m_thing;
if (!thing->player)
continue;
// Ignore spectators.
if (thing->player && thing->player->spectator)
continue;
// Option to require spindashing.
if (raise->vars[1] && !(thing->player->pflags & PF_STARTDASH))
continue;
if (!(thing->z == P_GetSpecialTopZ(thing, raise->sector, sector)))
continue;
playeronme = true;
break;
}
}
if (playeronme)
{
raise->vars[3] = raise->vars[2];
if (raise->vars[0] == 1)
{
if (raise->sector->ceilingheight <= raise->vars[7])
{
raise->sector->floorheight = raise->vars[7] - (raise->sector->ceilingheight - raise->sector->floorheight);
raise->sector->ceilingheight = raise->vars[7];
raise->sector->ceilspeed = 0;
raise->sector->floorspeed = 0;
return;
}
raise->vars[8] = -1;
ceilingdestination = raise->vars[7];
floordestination = raise->vars[6];
}
else // elevateUp
{
if (raise->sector->ceilingheight >= raise->vars[5])
{
raise->sector->floorheight = raise->vars[5] - (raise->sector->ceilingheight - raise->sector->floorheight);
raise->sector->ceilingheight = raise->vars[5];
raise->sector->ceilspeed = 0;
raise->sector->floorspeed = 0;
return;
}
raise->vars[8] = 1;
ceilingdestination = raise->vars[5];
floordestination = raise->vars[4];
}
}
else
{
raise->vars[3] = raise->vars[2]/2;
if (raise->vars[0] == 1)
{
if (raise->sector->ceilingheight >= raise->vars[5])
{
raise->sector->floorheight = raise->vars[5] - (raise->sector->ceilingheight - raise->sector->floorheight);
raise->sector->ceilingheight = raise->vars[5];
raise->sector->ceilspeed = 0;
raise->sector->floorspeed = 0;
return;
}
raise->vars[8] = 1;
ceilingdestination = raise->vars[5];
floordestination = raise->vars[4];
}
else // elevateUp
{
if (raise->sector->ceilingheight <= raise->vars[7])
{
raise->sector->floorheight = raise->vars[7] - (raise->sector->ceilingheight - raise->sector->floorheight);
raise->sector->ceilingheight = raise->vars[7];
raise->sector->ceilspeed = 0;
raise->sector->floorspeed = 0;
return;
}
raise->vars[8] = -1;
ceilingdestination = raise->vars[7];
floordestination = raise->vars[6];
}
}
if ((raise->sector->ceilingheight - raise->vars[7])
< (raise->vars[5] - raise->sector->ceilingheight))
{
fixed_t origspeed = raise->vars[3];
// Slow down as you get closer to the bottom
raise->vars[3] = FixedMul(raise->vars[3],FixedDiv(raise->sector->ceilingheight - raise->vars[7], (raise->vars[5] - raise->vars[7])>>5));
if (raise->vars[3] <= origspeed/16)
raise->vars[3] = origspeed/16;
else if (raise->vars[3] > origspeed)
raise->vars[3] = origspeed;
}
else
{
fixed_t origspeed = raise->vars[3];
// Slow down as you get closer to the top
raise->vars[3] = FixedMul(raise->vars[3],FixedDiv(raise->vars[5] - raise->sector->ceilingheight, (raise->vars[5] - raise->vars[7])>>5));
if (raise->vars[3] <= origspeed/16)
raise->vars[3] = origspeed/16;
else if (raise->vars[3] > origspeed)
raise->vars[3] = origspeed;
}
res = T_MovePlane
(
raise->sector, // sector
raise->vars[3], // speed
ceilingdestination, // dest
0, // crush
1, // floor or ceiling (1 for ceiling)
raise->vars[8] // direction
);
if (res == ok || res == pastdest)
T_MovePlane
(
raise->sector, // sector
raise->vars[3], // speed
floordestination, // dest
0, // crush
0, // floor or ceiling (0 for floor)
raise->vars[8] // direction
);
raise->sector->ceilspeed = 42;
raise->sector->floorspeed = raise->vars[3]*raise->vars[8];
for (i = -1; (i = P_FindSectorFromTag(raise->sourceline->tag, i)) >= 0 ;)
P_RecalcPrecipInSector(&sectors[i]);
}
void T_CameraScanner(elevator_t *elevator)
{
// leveltime is compared to make multiple scanners in one map function correctly.
static tic_t lastleveltime = 32000; // any number other than 0 should do here
static boolean camerascanned, camerascanned2;
if (leveltime != lastleveltime) // Back on the first camera scanner
{
camerascanned = camerascanned2 = false;
lastleveltime = leveltime;
}
if (players[displayplayer].mo)
{
if (players[displayplayer].mo->subsector->sector == elevator->actionsector)
{
if (t_cam_dist == -42)
t_cam_dist = cv_cam_dist.value;
if (t_cam_height == -42)
t_cam_height = cv_cam_height.value;
if (t_cam_rotate == -42)
t_cam_rotate = cv_cam_rotate.value;
CV_SetValue(&cv_cam_height, FixedInt(elevator->sector->floorheight));
CV_SetValue(&cv_cam_dist, FixedInt(elevator->sector->ceilingheight));
CV_SetValue(&cv_cam_rotate, elevator->distance);
camerascanned = true;
}
else if (!camerascanned)
{
if (t_cam_height != -42 && cv_cam_height.value != t_cam_height)
CV_Set(&cv_cam_height, va("%f", (double)FIXED_TO_FLOAT(t_cam_height)));
if (t_cam_dist != -42 && cv_cam_dist.value != t_cam_dist)
CV_Set(&cv_cam_dist, va("%f", (double)FIXED_TO_FLOAT(t_cam_dist)));
if (t_cam_rotate != -42 && cv_cam_rotate.value != t_cam_rotate)
CV_Set(&cv_cam_rotate, va("%f", (double)t_cam_rotate));
t_cam_dist = t_cam_height = t_cam_rotate = -42;
}
}
if (splitscreen && players[secondarydisplayplayer].mo)
{
if (players[secondarydisplayplayer].mo->subsector->sector == elevator->actionsector)
{
if (t_cam2_rotate == -42)
t_cam2_dist = cv_cam2_dist.value;
if (t_cam2_rotate == -42)
t_cam2_height = cv_cam2_height.value;
if (t_cam2_rotate == -42)
t_cam2_rotate = cv_cam2_rotate.value;
CV_SetValue(&cv_cam2_height, FixedInt(elevator->sector->floorheight));
CV_SetValue(&cv_cam2_dist, FixedInt(elevator->sector->ceilingheight));
CV_SetValue(&cv_cam2_rotate, elevator->distance);
camerascanned2 = true;
}
else if (!camerascanned2)
{
if (t_cam2_height != -42 && cv_cam2_height.value != t_cam2_height)
CV_Set(&cv_cam2_height, va("%f", (double)FIXED_TO_FLOAT(t_cam2_height)));
if (t_cam2_dist != -42 && cv_cam2_dist.value != t_cam2_dist)
CV_Set(&cv_cam2_dist, va("%f", (double)FIXED_TO_FLOAT(t_cam2_dist)));
if (t_cam2_rotate != -42 && cv_cam2_rotate.value != t_cam2_rotate)
CV_Set(&cv_cam2_rotate, va("%f", (double)t_cam2_rotate));
t_cam2_dist = t_cam2_height = t_cam2_rotate = -42;
}
}
}
void T_PlaneDisplace(planedisplace_t *pd)
{
sector_t *control, *target;
INT32 direction;
fixed_t diff;
control = &sectors[pd->control];
target = &sectors[pd->affectee];
if (control->floorheight == pd->last_height)
return; // no change, no movement
direction = (control->floorheight > pd->last_height) ? 1 : -1;
diff = FixedMul(control->floorheight-pd->last_height, pd->speed);
if (pd->reverse) // reverse direction?
{
direction *= -1;
diff *= -1;
}
if (pd->type == pd_floor || pd->type == pd_both)
T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, 0, 0, direction); // move floor
if (pd->type == pd_ceiling || pd->type == pd_both)
T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, 0, 1, direction); // move ceiling
pd->last_height = control->floorheight;
}
//
// EV_DoFloor
//
// Set up and start a floor thinker.
//
// Called by P_ProcessLineSpecial (linedef executors), P_ProcessSpecialSector
// (egg capsule button), P_PlayerInSpecialSector (buttons),
// and P_SpawnSpecials (continuous floor movers and instant lower).
//
INT32 EV_DoFloor(line_t *line, floor_e floortype)
{
INT32 rtn = 0, firstone = 1;
INT32 secnum = -1;
sector_t *sec;
floormove_t *dofloor;
while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
{
sec = &sectors[secnum];
if (sec->floordata) // if there's already a thinker on this floor,
continue; // then don't add another one
// new floor thinker
rtn = 1;
dofloor = Z_Calloc(sizeof (*dofloor), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &dofloor->thinker);
// make sure another floor thinker won't get started over this one
sec->floordata = dofloor;
// set up some generic aspects of the floormove_t
dofloor->thinker.function.acp1 = (actionf_p1)T_MoveFloor;
dofloor->type = floortype;
dofloor->crush = false; // default: types that crush will change this
dofloor->sector = sec;
switch (floortype)
{
// Lowers a floor to the lowest surrounding floor.
case lowerFloorToLowest:
dofloor->direction = -1; // down
dofloor->speed = FLOORSPEED*2; // 2 fracunits per tic
dofloor->floordestheight = P_FindLowestFloorSurrounding(sec);
break;
// Used for part of the Egg Capsule, when an FOF with type 666 is
// contacted by the player.
case raiseFloorToNearestFast:
dofloor->direction = -1; // down
dofloor->speed = FLOORSPEED*4; // 4 fracunits per tic
dofloor->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight);
break;
// Used for sectors tagged to 50 linedefs (effectively
// changing the base height for placing things in that sector).
case instantLower:
dofloor->direction = -1; // down
dofloor->speed = INT32_MAX/2; // "instant" means "takes one tic"
dofloor->floordestheight = P_FindLowestFloorSurrounding(sec);
break;
// Linedef executor command, linetype 101.
// Front sector floor = destination height.
case instantMoveFloorByFrontSector:
dofloor->speed = INT32_MAX/2; // as above, "instant" is one tic
dofloor->floordestheight = line->frontsector->floorheight;
if (dofloor->floordestheight >= sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
// New for 1.09: now you can use the no climb flag to
// DISABLE the flat changing. This makes it work
// totally opposite the way linetype 106 does. Yet
// another reason I'll be glad to break backwards
// compatibility for the final.
if (line->flags & ML_NOCLIMB)
dofloor->texture = -1; // don't mess with the floorpic
else
dofloor->texture = line->frontsector->floorpic;
break;
// Linedef executor command, linetype 106.
// Line length = speed, front sector floor = destination height.
case moveFloorByFrontSector:
dofloor->speed = P_AproxDistance(line->dx, line->dy);
dofloor->speed = FixedDiv(dofloor->speed,8*FRACUNIT);
dofloor->floordestheight = line->frontsector->floorheight;
if (dofloor->floordestheight >= sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
// chained linedef executing ability
if (line->flags & ML_BLOCKMONSTERS)
{
// Only set it on one of the moving sectors (the
// smallest numbered) and only if the front side
// x offset is positive, indicating a valid tag.
if (firstone && sides[line->sidenum[0]].textureoffset > 0)
dofloor->texture = (sides[line->sidenum[0]].textureoffset>>FRACBITS) - 32769;
else
dofloor->texture = -1;
}
// flat changing ability
else if (line->flags & ML_NOCLIMB)
dofloor->texture = line->frontsector->floorpic;
else
dofloor->texture = -1; // nothing special to do after movement completes
break;
case moveFloorByFrontTexture:
if (line->flags & ML_NOCLIMB)
dofloor->speed = INT32_MAX/2; // as above, "instant" is one tic
else
dofloor->speed = FixedDiv(sides[line->sidenum[0]].textureoffset,8*FRACUNIT); // texture x offset
dofloor->floordestheight = sec->floorheight + sides[line->sidenum[0]].rowoffset; // texture y offset
if (dofloor->floordestheight > sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
break;
/*
// Linedef executor command, linetype 108.
// dx = speed, dy = amount to lower.
case lowerFloorByLine:
dofloor->direction = -1; // down
dofloor->speed = FixedDiv(abs(line->dx),8*FRACUNIT);
dofloor->floordestheight = sec->floorheight - abs(line->dy);
if (dofloor->floordestheight > sec->floorheight) // wrapped around
I_Error("Can't lower sector %d\n", secnum);
break;
// Linedef executor command, linetype 109.
// dx = speed, dy = amount to raise.
case raiseFloorByLine:
dofloor->direction = 1; // up
dofloor->speed = FixedDiv(abs(line->dx),8*FRACUNIT);
dofloor->floordestheight = sec->floorheight + abs(line->dy);
if (dofloor->floordestheight < sec->floorheight) // wrapped around
I_Error("Can't raise sector %d\n", secnum);
break;
*/
// Linetypes 2/3.
// Move floor up and down indefinitely like the old elevators.
case bounceFloor:
dofloor->speed = P_AproxDistance(line->dx, line->dy); // same speed as elevateContinuous
dofloor->speed = FixedDiv(dofloor->speed,4*FRACUNIT);
dofloor->origspeed = dofloor->speed; // it gets slowed down at the top and bottom
dofloor->floordestheight = line->frontsector->floorheight;
if (dofloor->floordestheight >= sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
// Any delay?
dofloor->delay = sides[line->sidenum[0]].textureoffset >> FRACBITS;
dofloor->delaytimer = sides[line->sidenum[0]].rowoffset >> FRACBITS; // Initial delay
dofloor->texture = (fixed_t)(line - lines); // hack: store source line number
break;
// Linetypes 6/7.
// Like 2/3, but no slowdown at the top and bottom of movement,
// and the speed is line->dx the first way, line->dy for the
// return trip. Good for crushers.
case bounceFloorCrush:
dofloor->speed = FixedDiv(abs(line->dx),4*FRACUNIT);
dofloor->origspeed = dofloor->speed;
dofloor->floordestheight = line->frontsector->floorheight;
if (dofloor->floordestheight >= sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
// Any delay?
dofloor->delay = sides[line->sidenum[0]].textureoffset >> FRACBITS;
dofloor->delaytimer = sides[line->sidenum[0]].rowoffset >> FRACBITS; // Initial delay
dofloor->texture = (fixed_t)(line - lines); // hack: store source line number
break;
case crushFloorOnce:
dofloor->speed = FixedDiv(abs(line->dx),4*FRACUNIT);
dofloor->origspeed = dofloor->speed;
dofloor->floordestheight = line->frontsector->ceilingheight;
if (dofloor->floordestheight >= sec->floorheight)
dofloor->direction = 1; // up
else
dofloor->direction = -1; // down
// Any delay?
dofloor->delay = sides[line->sidenum[0]].textureoffset >> FRACBITS;
dofloor->delaytimer = sides[line->sidenum[0]].rowoffset >> FRACBITS;
dofloor->texture = (fixed_t)(line - lines); // hack: store source line number
break;
default:
break;
}
firstone = 0;
}
return rtn;
}
// SoM: Boom elevator support.
//
// EV_DoElevator
//
// Handle elevator linedef types
//
// Passed the linedef that triggered the elevator and the elevator action
//
// jff 2/22/98 new type to move floor and ceiling in parallel
//
INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
{
INT32 secnum = -1;
INT32 rtn = 0;
sector_t *sec;
elevator_t *elevator;
// act on all sectors with the same tag as the triggering linedef
while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0)
{
sec = &sectors[secnum];
// If either floor or ceiling is already activated, skip it
if (sec->floordata || sec->ceilingdata)
continue;
// create and initialize new elevator thinker
rtn = 1;
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &elevator->thinker);
sec->floordata = elevator;
sec->ceilingdata = elevator;
elevator->thinker.function.acp1 = (actionf_p1)T_MoveElevator;
elevator->type = elevtype;
elevator->sourceline = line;
elevator->distance = 1; // Always crush unless otherwise
// set up the fields according to the type of elevator action
switch (elevtype)
{
// elevator down to next floor
case elevateDown:
elevator->direction = -1;
elevator->sector = sec;
elevator->speed = ELEVATORSPEED/2; // half speed
elevator->floordestheight = P_FindNextLowestFloor(sec, sec->floorheight);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
break;
// elevator up to next floor
case elevateUp:
elevator->direction = 1;
elevator->sector = sec;
elevator->speed = ELEVATORSPEED/4; // quarter speed
elevator->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
break;
// elevator up to highest floor
case elevateHighest:
elevator->direction = 1;
elevator->sector = sec;
elevator->speed = ELEVATORSPEED/4; // quarter speed
elevator->floordestheight = P_FindHighestFloorSurrounding(sec);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
break;
// elevator to floor height of activating switch's front sector
case elevateCurrent:
elevator->sector = sec;
elevator->speed = ELEVATORSPEED;
elevator->floordestheight = line->frontsector->floorheight;
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
elevator->direction = elevator->floordestheight > sec->floorheight? 1 : -1;
break;
case elevateContinuous:
if (customspeed)
{
elevator->origspeed = P_AproxDistance(line->dx, line->dy);
elevator->origspeed = FixedDiv(elevator->origspeed,4*FRACUNIT);
elevator->speed = elevator->origspeed;
}
else
{
elevator->speed = ELEVATORSPEED/2;
elevator->origspeed = elevator->speed;
}
elevator->sector = sec;
elevator->low = !(line->flags & ML_NOCLIMB); // go down first unless noclimb is on
if (elevator->low)
{
elevator->direction = 1;
elevator->floordestheight = P_FindNextHighestFloor(sec, sec->floorheight);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
}
else
{
elevator->direction = -1;
elevator->floordestheight = P_FindNextLowestFloor(sec,sec->floorheight);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
}
elevator->floorwasheight = elevator->sector->floorheight;
elevator->ceilingwasheight = elevator->sector->ceilingheight;
elevator->delay = sides[line->sidenum[0]].textureoffset >> FRACBITS;
elevator->delaytimer = sides[line->sidenum[0]].rowoffset >> FRACBITS; // Initial delay
break;
case bridgeFall:
elevator->direction = -1;
elevator->sector = sec;
elevator->speed = ELEVATORSPEED*4; // quadruple speed
elevator->floordestheight = P_FindNextLowestFloor(sec, sec->floorheight);
elevator->ceilingdestheight = elevator->floordestheight
+ sec->ceilingheight - sec->floorheight;
break;
default:
break;
}
}
return rtn;
}
void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
{
size_t i;
size_t leftmostvertex = 0, rightmostvertex = 0;
size_t topmostvertex = 0, bottommostvertex = 0;
fixed_t leftx, rightx;
fixed_t topy, bottomy;
fixed_t topz, bottomz;
fixed_t widthfactor = FRACUNIT, heightfactor = FRACUNIT;
fixed_t a, b, c;
mobjtype_t type = MT_ROCKCRUMBLE1;
fixed_t spacing = (32<<FRACBITS);
tic_t lifetime = 3*TICRATE;
INT16 flags = 0;
#define controlsec rover->master->frontsector
if (controlsec->tag != 0)
{
INT32 tagline = P_FindSpecialLineFromTag(14, controlsec->tag, -1);
if (tagline != -1)
{
if (sides[lines[tagline].sidenum[0]].toptexture)
type = (mobjtype_t)sides[lines[tagline].sidenum[0]].toptexture; // Set as object type in p_setup.c...
if (sides[lines[tagline].sidenum[0]].textureoffset)
spacing = sides[lines[tagline].sidenum[0]].textureoffset;
if (sides[lines[tagline].sidenum[0]].rowoffset)
{
if (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS != -1)
lifetime = (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS);
else
lifetime = 0;
}
flags = lines[tagline].flags;
}
}
#undef controlsec
// soundorg z height never gets set normally, so MEH.
sec->soundorg.z = sec->floorheight;
S_StartSound(&sec->soundorg, mobjinfo[type].activesound);
// Find the outermost vertexes in the subsector
for (i = 0; i < sec->linecount; i++)
{
// Find the leftmost vertex in the subsector.
if ((sec->lines[i]->v1->x < sec->lines[leftmostvertex]->v1->x))
leftmostvertex = i;
// Find the rightmost vertex in the subsector.
if ((sec->lines[i]->v1->x > sec->lines[rightmostvertex]->v1->x))
rightmostvertex = i;
// Find the topmost vertex in the subsector.
if ((sec->lines[i]->v1->y > sec->lines[topmostvertex]->v1->y))
topmostvertex = i;
// Find the bottommost vertex in the subsector.
if ((sec->lines[i]->v1->y < sec->lines[bottommostvertex]->v1->y))
bottommostvertex = i;
}
leftx = sec->lines[leftmostvertex]->v1->x+(spacing>>1);
rightx = sec->lines[rightmostvertex]->v1->x;
topy = sec->lines[topmostvertex]->v1->y-(spacing>>1);
bottomy = sec->lines[bottommostvertex]->v1->y;
topz = *rover->topheight-(spacing>>1);
bottomz = *rover->bottomheight;
if (flags & ML_EFFECT1)
{
widthfactor = (rightx + topy - leftx - bottomy)>>3;
heightfactor = (topz - *rover->bottomheight)>>2;
}
for (a = leftx; a < rightx; a += spacing)
{
for (b = topy; b > bottomy; b -= spacing)
{
if (R_PointInSubsector(a, b)->sector == sec)
{
mobj_t *spawned = NULL;
#ifdef ESLOPE
if (*rover->t_slope)
topz = P_GetZAt(*rover->t_slope, a, b) - (spacing>>1);
if (*rover->b_slope)
bottomz = P_GetZAt(*rover->b_slope, a, b);
#endif
for (c = topz; c > bottomz; c -= spacing)
{
spawned = P_SpawnMobj(a, b, c, type);
spawned->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
if (flags & ML_EFFECT1)
{
P_InstaThrust(spawned, R_PointToAngle2(sec->soundorg.x, sec->soundorg.y, a, b), FixedDiv(P_AproxDistance(a - sec->soundorg.x, b - sec->soundorg.y), widthfactor));
P_SetObjectMomZ(spawned, FixedDiv((c - bottomz), heightfactor), false);
}
spawned->fuse = lifetime;
}
}
}
}
// no longer exists (can't collide with again)
rover->flags &= ~FF_EXISTS;
rover->master->frontsector->moved = true;
sec->moved = true;
}
// Used for bobbing platforms on the water
INT32 EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline)
{
#define speed vars[0]
#define distance vars[1]
#define low vars[2]
levelspecthink_t *bouncer;
// create and initialize new thinker
if (sec->ceilingdata) // One at a time, ma'am.
return 0;
bouncer = Z_Calloc(sizeof (*bouncer), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &bouncer->thinker);
sec->ceilingdata = bouncer;
bouncer->thinker.function.acp1 = (actionf_p1)T_BounceCheese;
// set up the fields according to the type of elevator action
bouncer->sector = sec;
bouncer->speed = momz/2;
bouncer->sourceline = sourceline;
bouncer->distance = FRACUNIT;
bouncer->low = 1;
return 1;
#undef speed
#undef distance
#undef low
}
// For T_ContinuousFalling special
INT32 EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boolean backwards)
{
#define speed vars[0]
#define direction vars[1]
#define floorwasheight vars[2]
#define ceilingwasheight vars[3]
#define floordestheight vars[4]
#define ceilingdestheight vars[5]
levelspecthink_t *faller;
// workaround for when there is no back sector
if (backsector == NULL)
backsector = sec;
// create and initialize new thinker
faller = Z_Calloc(sizeof (*faller), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &faller->thinker);
faller->thinker.function.acp1 = (actionf_p1)T_ContinuousFalling;
// set up the fields
faller->sector = sec;
faller->speed = spd;
faller->floorwasheight = sec->floorheight;
faller->ceilingwasheight = sec->ceilingheight;
if (backwards)
{
faller->ceilingdestheight = backsector->ceilingheight;
faller->floordestheight = faller->ceilingdestheight;
faller->direction = 1; // Up!
}
else
{
faller->floordestheight = backsector->floorheight;
faller->ceilingdestheight = faller->floordestheight;
faller->direction = -1;
}
return 1;
#undef speed
#undef direction
#undef floorwasheight
#undef ceilingwasheight
#undef floordestheight
#undef ceilingdestheight
}
// Some other 3dfloor special things Tails 03-11-2002 (Search p_mobj.c for description)
INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
player_t *player, fixed_t origalpha, boolean crumblereturn)
{
elevator_t *elevator;
sector_t *foundsec;
INT32 i;
// If floor is already activated, skip it
if (sec->floordata)
return 0;
if (sec->crumblestate > 1)
return 0;
// create and initialize new elevator thinker
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &elevator->thinker);
elevator->thinker.function.acp1 = (actionf_p1)T_StartCrumble;
// Does this crumbler return?
if (crumblereturn)
elevator->type = elevateBounce;
else
elevator->type = elevateContinuous;
// set up the fields according to the type of elevator action
elevator->sector = sec;
elevator->speed = 0;
if (player && player->mo && (player->mo->eflags & MFE_VERTICALFLIP))
{
elevator->direction = 1; // Up
elevator->floordestheight = 1;
}
else
{
elevator->direction = -1; // Down
elevator->floordestheight = 0;
}
elevator->floorwasheight = elevator->sector->floorheight;
elevator->ceilingwasheight = elevator->sector->ceilingheight;
elevator->distance = TICRATE; // Used for delay time
elevator->low = 0;
elevator->player = player;
elevator->origspeed = origalpha;
elevator->sourceline = rover->master;
sec->floordata = elevator;
if (floating)
elevator->high = 42;
else
elevator->high = 0;
elevator->sector->crumblestate = 2;
for (i = -1; (i = P_FindSectorFromTag(elevator->sourceline->tag, i)) >= 0 ;)
{
foundsec = &sectors[i];
P_SpawnMobj(foundsec->soundorg.x, foundsec->soundorg.y, elevator->direction == 1 ? elevator->sector->floorheight : elevator->sector->ceilingheight, MT_CRUMBLEOBJ);
}
return 1;
}
INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
{
sector_t *roversec = rover->master->frontsector;
fixed_t topheight = *rover->topheight;
levelspecthink_t *block;
mobj_t *thing;
fixed_t oldx = 0, oldy = 0, oldz = 0;
I_Assert(puncher != NULL);
I_Assert(puncher->player != NULL);
if (roversec->floordata || roversec->ceilingdata)
return 0;
if (!(rover->flags & FF_SOLID))
rover->flags |= (FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
// Find an item to pop out!
thing = SearchMarioNode(roversec->touching_thinglist);
// Found something!
if (thing)
{
const boolean itsamonitor = (thing->flags & MF_MONITOR) == MF_MONITOR;
// create and initialize new elevator thinker
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &block->thinker);
roversec->floordata = block;
roversec->ceilingdata = block;
block->thinker.function.acp1 = (actionf_p1)T_MarioBlock;
// Set up the fields
block->sector = roversec;
block->vars[0] = sector->tag; // actionsector
block->vars[1] = 4*FRACUNIT; // speed
block->vars[2] = 1; // Up // direction
block->vars[3] = block->sector->floorheight; // floorwasheight
block->vars[4] = block->sector->ceilingheight; // ceilingwasheight
block->vars[5] = FRACUNIT; // distance
block->vars[6] = 1; // low
if (itsamonitor)
{
oldx = thing->x;
oldy = thing->y;
oldz = thing->z;
}
P_UnsetThingPosition(thing);
thing->x = sector->soundorg.x;
thing->y = sector->soundorg.y;
thing->z = topheight;
thing->momz = FixedMul(6*FRACUNIT, thing->scale);
P_SetThingPosition(thing);
if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, puncher, puncher, 1, 0);
else if (thing->type == MT_RING || thing->type == MT_COIN || thing->type == MT_TOKEN)
{
thing->momz = FixedMul(3*FRACUNIT, thing->scale);
P_TouchSpecialThing(thing, puncher, false);
// "Thunk!" sound
S_StartSound(puncher, sfx_mario1); // Puncher is "close enough"
}
else
{
// "Powerup rise" sound
S_StartSound(puncher, sfx_mario9); // Puncher is "close enough"
}
if (itsamonitor && thing)
{
P_UnsetThingPosition(thing);
thing->x = oldx;
thing->y = oldy;
thing->z = oldz;
thing->momx = 1;
thing->momy = 1;
P_SetThingPosition(thing);
}
}
else
S_StartSound(puncher, sfx_mario1); // "Thunk!" sound - puncher is "close enough".
return 1;
}