mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-15 01:01:33 +00:00
3949 lines
110 KiB
C
3949 lines
110 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-2014 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_map.c
|
|
/// \brief Movement, collision handling
|
|
///
|
|
/// Shooting and aiming
|
|
|
|
#include "doomdef.h"
|
|
#include "g_game.h"
|
|
#include "m_bbox.h"
|
|
#include "m_random.h"
|
|
#include "p_local.h"
|
|
#include "p_setup.h" // NiGHTS stuff
|
|
#include "r_state.h"
|
|
#include "r_main.h"
|
|
#include "r_sky.h"
|
|
#include "s_sound.h"
|
|
#include "w_wad.h"
|
|
|
|
#include "r_splats.h"
|
|
|
|
#include "z_zone.h"
|
|
|
|
#include "lua_hook.h"
|
|
|
|
fixed_t tmbbox[4];
|
|
mobj_t *tmthing;
|
|
static INT32 tmflags;
|
|
static fixed_t tmx;
|
|
static fixed_t tmy;
|
|
|
|
static precipmobj_t *tmprecipthing;
|
|
static fixed_t preciptmbbox[4];
|
|
|
|
// If "floatok" true, move would be ok
|
|
// if within "tmfloorz - tmceilingz".
|
|
boolean floatok;
|
|
|
|
fixed_t tmfloorz, tmceilingz;
|
|
static fixed_t tmdropoffz;
|
|
mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz is from a sector
|
|
static mobj_t *tmhitthing; // the solid thing you bumped into (for collisions)
|
|
|
|
// turned on or off in PIT_CheckThing
|
|
boolean tmsprung;
|
|
|
|
// keep track of the line that lowers the ceiling,
|
|
// so missiles don't explode against sky hack walls
|
|
line_t *ceilingline;
|
|
|
|
// set by PIT_CheckLine() for any line that stopped the PIT_CheckLine()
|
|
// that is, for any line which is 'solid'
|
|
line_t *blockingline;
|
|
|
|
msecnode_t *sector_list = NULL;
|
|
mprecipsecnode_t *precipsector_list = NULL;
|
|
camera_t *mapcampointer;
|
|
|
|
//
|
|
// TELEPORT MOVE
|
|
//
|
|
|
|
//
|
|
// P_TeleportMove
|
|
//
|
|
boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
|
|
{
|
|
// the move is ok,
|
|
// so link the thing into its new position
|
|
P_UnsetThingPosition(thing);
|
|
|
|
// Remove touching_sectorlist from mobj.
|
|
if (sector_list)
|
|
{
|
|
P_DelSeclist(sector_list);
|
|
sector_list = NULL;
|
|
}
|
|
|
|
thing->x = x;
|
|
thing->y = y;
|
|
thing->z = z;
|
|
|
|
P_SetThingPosition(thing);
|
|
|
|
P_CheckPosition(thing, thing->x, thing->y);
|
|
|
|
if (P_MobjWasRemoved(thing))
|
|
return true;
|
|
|
|
thing->floorz = tmfloorz;
|
|
thing->ceilingz = tmceilingz;
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// MOVEMENT ITERATOR FUNCTIONS
|
|
// =========================================================================
|
|
|
|
static void P_DoSpring(mobj_t *spring, mobj_t *object)
|
|
{
|
|
INT32 pflags;
|
|
fixed_t offx, offy;
|
|
fixed_t vertispeed = spring->info->mass;
|
|
fixed_t horizspeed = spring->info->damage;
|
|
fixed_t origvertispeed = vertispeed; // for vertical flipping
|
|
|
|
// Spectators don't trigger springs.
|
|
if (object->player && object->player->spectator)
|
|
return;
|
|
|
|
if (object->player && (object->player->pflags & PF_NIGHTSMODE))
|
|
{
|
|
/*Someone want to make these work like bumpers?*/
|
|
return;
|
|
}
|
|
|
|
spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify
|
|
|
|
if (horizspeed && vertispeed) // Mimic SA
|
|
{
|
|
object->momx = object->momy = 0;
|
|
P_TryMove(object, spring->x, spring->y, true);
|
|
}
|
|
|
|
if (spring->eflags & MFE_VERTICALFLIP)
|
|
vertispeed *= -1;
|
|
|
|
if (vertispeed > 0)
|
|
object->z = spring->z + spring->height + 1;
|
|
else if (vertispeed < 0)
|
|
object->z = spring->z - object->height - 1;
|
|
else
|
|
{
|
|
// Horizontal springs teleport you in FRONT of them.
|
|
object->momx = object->momy = 0;
|
|
|
|
// Overestimate the distance to position you at
|
|
offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
|
|
offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
|
|
|
|
// Make it square by clipping
|
|
if (offx > (spring->radius + object->radius + 1))
|
|
offx = spring->radius + object->radius + 1;
|
|
else if (offx < -(spring->radius + object->radius + 1))
|
|
offx = -(spring->radius + object->radius + 1);
|
|
|
|
if (offy > (spring->radius + object->radius + 1))
|
|
offy = spring->radius + object->radius + 1;
|
|
else if (offy < -(spring->radius + object->radius + 1))
|
|
offy = -(spring->radius + object->radius + 1);
|
|
|
|
// Set position!
|
|
P_TryMove(object, spring->x + offx, spring->y + offy, true);
|
|
}
|
|
|
|
if (vertispeed)
|
|
object->momz = FixedMul(vertispeed,FixedSqrt(FixedMul(object->scale, spring->scale)));
|
|
|
|
if (horizspeed)
|
|
P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(horizspeed,FixedSqrt(FixedMul(object->scale, spring->scale))));
|
|
|
|
// Re-solidify
|
|
spring->flags |= (spring->info->flags & (MF_SPECIAL|MF_SOLID));
|
|
|
|
P_SetMobjState(spring, spring->info->raisestate);
|
|
|
|
if (object->player)
|
|
{
|
|
if (spring->flags & MF_ENEMY) // Spring shells
|
|
P_SetTarget(&spring->target, object);
|
|
|
|
if (horizspeed && object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0)
|
|
{
|
|
object->angle = spring->angle;
|
|
|
|
if (object->player == &players[consoleplayer])
|
|
localangle = spring->angle;
|
|
else if (object->player == &players[secondarydisplayplayer])
|
|
localangle2 = spring->angle;
|
|
}
|
|
|
|
pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these.
|
|
P_ResetPlayer(object->player);
|
|
|
|
if (origvertispeed > 0)
|
|
P_SetPlayerMobjState(object, S_PLAY_SPRING);
|
|
else if (origvertispeed < 0)
|
|
P_SetPlayerMobjState(object, S_PLAY_FALL1);
|
|
else // horizontal spring
|
|
{
|
|
if (pflags & (PF_JUMPED|PF_SPINNING) && object->player->panim == PA_ROLL)
|
|
object->player->pflags = pflags;
|
|
else
|
|
P_SetPlayerMobjState(object, S_PLAY_RUN1);
|
|
}
|
|
|
|
if (spring->info->painchance)
|
|
{
|
|
object->player->pflags |= PF_JUMPED;
|
|
P_SetPlayerMobjState(object, S_PLAY_ATK1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// PIT_CheckThing
|
|
//
|
|
static boolean PIT_CheckThing(mobj_t *thing)
|
|
{
|
|
fixed_t blockdist, topz, tmtopz;
|
|
|
|
// don't clip against self
|
|
tmsprung = false;
|
|
|
|
// Ignore... things.
|
|
if (!tmthing || !thing)
|
|
return true;
|
|
|
|
I_Assert(!P_MobjWasRemoved(tmthing));
|
|
I_Assert(!P_MobjWasRemoved(thing));
|
|
|
|
// Ignore spectators
|
|
if ((tmthing->player && tmthing->player->spectator)
|
|
|| (thing->player && thing->player->spectator))
|
|
return true;
|
|
|
|
#ifdef SEENAMES
|
|
// Do name checks all the way up here
|
|
// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
|
|
if (tmthing->type == MT_NAMECHECK)
|
|
{
|
|
// Ignore things that aren't players, ignore spectators, ignore yourself.
|
|
// (also don't bother to check that tmthing->target->player is non-NULL because we're not actually using it here.)
|
|
if (!thing->player || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
|
|
return true;
|
|
|
|
// Now check that you actually hit them.
|
|
blockdist = thing->radius + tmthing->radius;
|
|
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
|
|
return true; // didn't hit it
|
|
// see if it went over / under
|
|
if (tmthing->z > thing->z + thing->height)
|
|
return true; // overhead
|
|
if (tmthing->z + tmthing->height < thing->z)
|
|
return true; // underneath
|
|
|
|
seenplayer = thing->player;
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// Metal Sonic destroys tiny baby objects.
|
|
if (tmthing->type == MT_METALSONIC_RACE
|
|
&& (thing->flags & MF_MISSILE || thing->flags & MF_ENEMY
|
|
|| thing->flags & MF_BOSS || thing->type == MT_SPIKE))
|
|
{
|
|
if ((thing->flags & MF_ENEMY || thing->flags & MF_BOSS) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
|
|
return true;
|
|
blockdist = thing->radius + tmthing->radius;
|
|
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
|
|
return true; // didn't hit it
|
|
// see if it went over / under
|
|
if (tmthing->z > thing->z + thing->height)
|
|
return true; // overhead
|
|
if (tmthing->z + tmthing->height < thing->z)
|
|
return true; // underneath
|
|
if (thing->type == MT_SPIKE)
|
|
{
|
|
S_StartSound(tmthing, thing->info->deathsound);
|
|
for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext)
|
|
if (thing->type == MT_SPIKE && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y) < FixedMul(56*FRACUNIT, thing->scale))
|
|
P_KillMobj(thing, tmthing, tmthing);
|
|
}
|
|
else
|
|
{
|
|
thing->health = 0;
|
|
P_KillMobj(thing, tmthing, tmthing);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE)))
|
|
return true;
|
|
|
|
if (!tmthing || !thing || thing == tmthing || P_MobjWasRemoved(thing))
|
|
return true;
|
|
|
|
// Don't collide with your buddies while NiGHTS-flying.
|
|
if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
|
|
&& ((tmthing->player->pflags & PF_NIGHTSMODE) || (thing->player->pflags & PF_NIGHTSMODE)))
|
|
return true;
|
|
|
|
blockdist = thing->radius + tmthing->radius;
|
|
|
|
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
|
|
return true; // didn't hit it
|
|
|
|
#ifdef HAVE_BLUA
|
|
{
|
|
UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
|
|
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
|
|
return true; // one of them was removed???
|
|
if (shouldCollide == 1)
|
|
return false; // force collide
|
|
else if (shouldCollide == 2)
|
|
return true; // force no collide
|
|
}
|
|
{
|
|
UINT8 shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type
|
|
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
|
|
return true; // one of them was removed???
|
|
if (shouldCollide == 1)
|
|
return false; // force collide
|
|
else if (shouldCollide == 2)
|
|
return true; // force no collide
|
|
}
|
|
#endif
|
|
|
|
// When solid spikes move, assume they just popped up and teleport things on top of them to hurt.
|
|
if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID)
|
|
{
|
|
if (thing->z > tmthing->z + tmthing->height)
|
|
return true; // overhead
|
|
if (thing->z + thing->height < tmthing->z)
|
|
return true; // underneath
|
|
|
|
if (tmthing->eflags & MFE_VERTICALFLIP)
|
|
thing->z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale);
|
|
else
|
|
thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale);
|
|
if (thing->flags & MF_SHOOTABLE)
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
return true;
|
|
}
|
|
|
|
if (thing->flags & MF_PAIN)
|
|
{ // Player touches painful thing sitting on the floor
|
|
// see if it went over / under
|
|
if (thing->z > tmthing->z + tmthing->height)
|
|
return true; // overhead
|
|
if (thing->z + thing->height < tmthing->z)
|
|
return true; // underneath
|
|
if (tmthing->player && tmthing->flags & MF_SHOOTABLE)
|
|
P_DamageMobj(tmthing, thing, thing, 1);
|
|
return true;
|
|
}
|
|
else if (tmthing->flags & MF_PAIN)
|
|
{ // Painful thing splats player in the face
|
|
// see if it went over / under
|
|
if (tmthing->z > thing->z + thing->height)
|
|
return true; // overhead
|
|
if (tmthing->z + tmthing->height < thing->z)
|
|
return true; // underneath
|
|
if (thing->player && thing->flags & MF_SHOOTABLE)
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
return true;
|
|
}
|
|
|
|
if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player)
|
|
{
|
|
P_TouchSpecialThing(thing, tmthing, true);
|
|
return true;
|
|
}
|
|
|
|
// check for skulls slamming into things
|
|
if (tmthing->flags2 & MF2_SKULLFLY)
|
|
{
|
|
if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop!
|
|
return true; // Let him RUN YOU RIGHT OVER. >:3
|
|
else
|
|
{
|
|
// see if it went over / under
|
|
if (tmthing->z > thing->z + thing->height)
|
|
return true; // overhead
|
|
if (tmthing->z + tmthing->height < thing->z)
|
|
return true; // underneath
|
|
|
|
tmthing->flags2 &= ~MF2_SKULLFLY;
|
|
tmthing->momx = tmthing->momy = tmthing->momz = 0;
|
|
return false; // stop moving
|
|
}
|
|
}
|
|
|
|
if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0
|
|
&& (tmthing->player || (tmthing->flags & MF_PUSHABLE)) && tmthing->health > 0)
|
|
{
|
|
// Multiplying by -1 inherently flips "less than" and "greater than"
|
|
fixed_t tmz = ((thing->eflags & MFE_VERTICALFLIP) ? -(tmthing->z + tmthing->height) : tmthing->z);
|
|
fixed_t tmznext = ((thing->eflags & MFE_VERTICALFLIP) ? -tmthing->momz : tmthing->momz) + tmz;
|
|
fixed_t thzh = ((thing->eflags & MFE_VERTICALFLIP) ? -thing->z : thing->z + thing->height);
|
|
fixed_t sprarea = FixedMul(8*FRACUNIT, thing->scale) * P_MobjFlip(thing);
|
|
|
|
if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh))
|
|
{
|
|
P_DoSpring(thing, tmthing);
|
|
tmsprung = true;
|
|
return true;
|
|
}
|
|
else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
|
|
return true;
|
|
}
|
|
// missiles can hit other things
|
|
if (tmthing->flags & MF_MISSILE || tmthing->type == MT_SHELL)
|
|
{
|
|
// see if it went over / under
|
|
if (tmthing->z > thing->z + thing->height)
|
|
return true; // overhead
|
|
if (tmthing->z + tmthing->height < thing->z)
|
|
return true; // underneath
|
|
|
|
if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
|
|
{
|
|
// Don't hit same species as originator.
|
|
if (thing == tmthing->target)
|
|
return true;
|
|
|
|
if (thing->type != MT_PLAYER)
|
|
{
|
|
// Explode, but do no damage.
|
|
// Let players missile other players.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Special case for bounce rings so they don't get caught behind solid objects.
|
|
if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID)
|
|
return true;
|
|
|
|
// Missiles ignore Brak's helper.
|
|
if (thing->type == MT_BLACKEGGMAN_HELPER)
|
|
return true;
|
|
|
|
// Hurting Brak
|
|
if (tmthing->type == MT_BLACKEGGMAN_MISSILE
|
|
&& thing->type == MT_BLACKEGGMAN && tmthing->target != thing)
|
|
{
|
|
// Not if Brak's already in pain
|
|
if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35]))
|
|
P_SetMobjState(thing, thing->info->painstate);
|
|
return false;
|
|
}
|
|
|
|
if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD))
|
|
{
|
|
// didn't do any damage
|
|
return !(thing->flags & MF_SOLID);
|
|
}
|
|
|
|
if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player
|
|
&& thing->player->ctfteam == tmthing->target->player->ctfteam
|
|
&& thing->player->pflags & PF_CARRIED && thing->tracer == tmthing->target)
|
|
return true; // Don't give rings to your carry player by accident.
|
|
|
|
if (thing->type == MT_EGGSHIELD)
|
|
{
|
|
fixed_t touchx, touchy;
|
|
angle_t angle;
|
|
|
|
if (P_AproxDistance(tmthing->x-thing->x, tmthing->y-thing->y) >
|
|
P_AproxDistance((tmthing->x-tmthing->momx)-thing->x, (tmthing->y-tmthing->momy)-thing->y))
|
|
{
|
|
touchx = tmthing->x + tmthing->momx;
|
|
touchy = tmthing->y + tmthing->momy;
|
|
}
|
|
else
|
|
{
|
|
touchx = tmthing->x;
|
|
touchy = tmthing->y;
|
|
}
|
|
|
|
angle = R_PointToAngle2(thing->x, thing->y, touchx, touchy) - thing->angle;
|
|
|
|
if (!(angle > ANGLE_90 && angle < ANGLE_270)) // hit front of shield, didn't destroy it
|
|
return false;
|
|
else // hit shield from behind, shield is destroyed!
|
|
{
|
|
P_KillMobj(thing, tmthing, tmthing);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (tmthing->type == MT_SHELL && tmthing->threshold > TICRATE)
|
|
return true;
|
|
// damage / explode
|
|
if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player
|
|
&& (thing->player->pflags & PF_JUMPED)
|
|
&& !thing->player->powers[pw_flashing]
|
|
&& thing->tracer != tmthing
|
|
&& tmthing->target != thing)
|
|
{
|
|
// Hop on the missile for a ride!
|
|
thing->player->pflags |= PF_ITEMHANG;
|
|
thing->player->pflags &= ~PF_JUMPED;
|
|
P_SetTarget(&thing->tracer, tmthing);
|
|
P_SetTarget(&tmthing->target, thing); // Set owner to the player
|
|
P_SetTarget(&tmthing->tracer, NULL); // Disable homing-ness
|
|
tmthing->momz = 0;
|
|
|
|
thing->angle = tmthing->angle;
|
|
|
|
if (thing->player == &players[consoleplayer])
|
|
localangle = thing->angle;
|
|
else if (thing->player == &players[secondarydisplayplayer])
|
|
localangle2 = thing->angle;
|
|
|
|
return true;
|
|
}
|
|
else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->pflags & PF_ITEMHANG) || (thing->player->pflags & PF_JUMPED)))
|
|
{
|
|
// Ignore
|
|
}
|
|
else if (tmthing->type == MT_BLACKEGGMAN_GOOPFIRE)
|
|
{
|
|
P_UnsetThingPosition(tmthing);
|
|
tmthing->x = thing->x;
|
|
tmthing->y = thing->y;
|
|
P_SetThingPosition(tmthing);
|
|
}
|
|
else
|
|
P_DamageMobj(thing, tmthing, tmthing->target, 1);
|
|
|
|
// don't traverse any more
|
|
|
|
if (tmthing->type == MT_SHELL)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
if ((thing->flags & MF_PUSHABLE) && (tmthing->player || tmthing->flags & MF_PUSHABLE)
|
|
&& tmthing->z + tmthing->height > thing->z && tmthing->z < thing->z + thing->height
|
|
&& !(netgame && tmthing->player && tmthing->player->spectator)) // Push thing!
|
|
{
|
|
if (thing->flags2 & MF2_SLIDEPUSH) // Make it slide
|
|
{
|
|
if (tmthing->momy > 0 && tmthing->momy > FixedMul(4*FRACUNIT, thing->scale) && tmthing->momy > thing->momy)
|
|
{
|
|
thing->momy += FixedMul(PUSHACCEL, thing->scale);
|
|
tmthing->momy -= FixedMul(PUSHACCEL, thing->scale);
|
|
}
|
|
else if (tmthing->momy < 0 && tmthing->momy < FixedMul(-4*FRACUNIT, thing->scale)
|
|
&& tmthing->momy < thing->momy)
|
|
{
|
|
thing->momy -= FixedMul(PUSHACCEL, thing->scale);
|
|
tmthing->momy += FixedMul(PUSHACCEL, thing->scale);
|
|
}
|
|
if (tmthing->momx > 0 && tmthing->momx > FixedMul(4*FRACUNIT, thing->scale)
|
|
&& tmthing->momx > thing->momx)
|
|
{
|
|
thing->momx += FixedMul(PUSHACCEL, thing->scale);
|
|
tmthing->momx -= FixedMul(PUSHACCEL, thing->scale);
|
|
}
|
|
else if (tmthing->momx < 0 && tmthing->momx < FixedMul(-4*FRACUNIT, thing->scale)
|
|
&& tmthing->momx < thing->momx)
|
|
{
|
|
thing->momx -= FixedMul(PUSHACCEL, thing->scale);
|
|
tmthing->momx += FixedMul(PUSHACCEL, thing->scale);
|
|
}
|
|
|
|
if (thing->momx > FixedMul(thing->info->speed, thing->scale))
|
|
thing->momx = FixedMul(thing->info->speed, thing->scale);
|
|
else if (thing->momx < -FixedMul(thing->info->speed, thing->scale))
|
|
thing->momx = -FixedMul(thing->info->speed, thing->scale);
|
|
if (thing->momy > FixedMul(thing->info->speed, thing->scale))
|
|
thing->momy = FixedMul(thing->info->speed, thing->scale);
|
|
else if (thing->momy < -FixedMul(thing->info->speed, thing->scale))
|
|
thing->momy = -FixedMul(thing->info->speed, thing->scale);
|
|
}
|
|
else
|
|
{
|
|
if (tmthing->momx > FixedMul(4*FRACUNIT, thing->scale))
|
|
tmthing->momx = FixedMul(4*FRACUNIT, thing->scale);
|
|
else if (tmthing->momx < FixedMul(-4*FRACUNIT, thing->scale))
|
|
tmthing->momx = FixedMul(-4*FRACUNIT, thing->scale);
|
|
if (tmthing->momy > FixedMul(4*FRACUNIT, thing->scale))
|
|
tmthing->momy = FixedMul(4*FRACUNIT, thing->scale);
|
|
else if (tmthing->momy < FixedMul(-4*FRACUNIT, thing->scale))
|
|
tmthing->momy = FixedMul(-4*FRACUNIT, thing->scale);
|
|
|
|
thing->momx = tmthing->momx;
|
|
thing->momy = tmthing->momy;
|
|
}
|
|
|
|
if (thing->type != MT_GARGOYLE || P_IsObjectOnGround(thing))
|
|
S_StartSound(thing, thing->info->activesound);
|
|
|
|
P_SetTarget(&thing->target, tmthing);
|
|
}
|
|
|
|
// Respawn rings and items
|
|
if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE)
|
|
&& (tmthing->player || thing->player))
|
|
{
|
|
mobj_t *droneobj = (tmthing->type == MT_NIGHTSDRONE) ? tmthing : thing;
|
|
player_t *pl = (droneobj == thing) ? tmthing->player : thing->player;
|
|
|
|
// Must be in bonus time, and must be NiGHTS, must wait about a second
|
|
// must be flying in the SAME DIRECTION as the last time you came through.
|
|
// not (your direction) xor (stored direction)
|
|
// In other words, you can't u-turn and respawn rings near the drone.
|
|
if (pl->bonustime && (pl->pflags & PF_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && (
|
|
!(pl->anotherflyangle >= 90 && pl->anotherflyangle <= 270)
|
|
^ (droneobj->extravalue1 >= 90 && droneobj->extravalue1 <= 270)
|
|
))
|
|
{
|
|
// Reload all the fancy ring stuff!
|
|
P_ReloadRings();
|
|
}
|
|
droneobj->extravalue1 = pl->anotherflyangle;
|
|
droneobj->extravalue2 = (INT32)leveltime + TICRATE;
|
|
}
|
|
|
|
// check for special pickup
|
|
if (thing->flags & MF_SPECIAL && tmthing->player)
|
|
{
|
|
P_TouchSpecialThing(thing, tmthing, true); // can remove thing
|
|
return true;
|
|
}
|
|
// check again for special pickup
|
|
if (tmthing->flags & MF_SPECIAL && thing->player)
|
|
{
|
|
P_TouchSpecialThing(tmthing, thing, true); // can remove thing
|
|
return true;
|
|
}
|
|
|
|
// Sprite Spikes!
|
|
// Do not return because solidity code comes below.
|
|
if (tmthing->type == MT_SPIKE && thing->player) // moving spike rams into player?!
|
|
{
|
|
if (tmthing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (thing->z + thing->height <= tmthing->z + FixedMul(FRACUNIT, tmthing->scale)
|
|
&& thing->z + thing->height >= tmthing->z + FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz)
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
}
|
|
else if (thing->z >= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale)
|
|
&& thing->z + thing->momz <= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz)
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
}
|
|
else if (thing->type == MT_SPIKE && tmthing->player) // unfortunate player falls into spike?!
|
|
{
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
|
|
&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale))
|
|
P_DamageMobj(tmthing, thing, thing, 1);
|
|
}
|
|
else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
|
|
&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale))
|
|
P_DamageMobj(tmthing, thing, thing, 1);
|
|
}
|
|
|
|
if (thing->flags & MF_PUSHABLE)
|
|
{
|
|
if (tmthing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (thing->z + thing->height <= tmthing->z + tmthing->height)
|
|
{
|
|
switch (tmthing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (thing->z + thing->height >= tmthing->z + tmthing->height - (tmthing->health << FRACBITS) && thing->momz > -FixedMul(tmthing->info->mass, tmthing->scale))
|
|
{
|
|
thing->momz -= FixedMul(tmthing->info->mass/4, tmthing->scale);
|
|
|
|
if (thing->momz < -FixedMul(tmthing->info->mass, tmthing->scale))
|
|
thing->momz = -FixedMul(tmthing->info->mass, tmthing->scale);
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (tmthing->state == &states[S_STEAM1] && thing->z + thing->height >= tmthing->z + tmthing->height - FixedMul(16*FRACUNIT, tmthing->scale)) // Only when it bursts
|
|
thing->momz = -FixedMul(tmthing->info->mass, FixedSqrt(FixedMul(tmthing->scale, thing->scale)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (thing->z >= tmthing->z)
|
|
{
|
|
switch (tmthing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (thing->z <= tmthing->z + (tmthing->health << FRACBITS) && thing->momz < FixedMul(tmthing->info->mass, tmthing->scale))
|
|
{
|
|
thing->momz += FixedMul(tmthing->info->mass/4, tmthing->scale);
|
|
|
|
if (thing->momz > FixedMul(tmthing->info->mass, tmthing->scale))
|
|
thing->momz = FixedMul(tmthing->info->mass, tmthing->scale);
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (tmthing->state == &states[S_STEAM1] && thing->z <= tmthing->z + FixedMul(16*FRACUNIT, tmthing->scale)) // Only when it bursts
|
|
thing->momz = FixedMul(tmthing->info->mass, FixedSqrt(FixedMul(tmthing->scale, thing->scale)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tmthing->flags & MF_PUSHABLE)
|
|
{
|
|
if ((thing->eflags & MFE_VERTICALFLIP) && tmthing->z + tmthing->height <= thing->z + thing->height)
|
|
{
|
|
switch (thing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (tmthing->z + tmthing->height >= thing->z + thing->height - (thing->health << FRACBITS) && tmthing->momz > -FixedMul(thing->info->mass, thing->scale))
|
|
{
|
|
tmthing->momz -= FixedMul(thing->info->mass/4, thing->scale);
|
|
|
|
if (tmthing->momz < -FixedMul(thing->info->mass, thing->scale))
|
|
tmthing->momz = -FixedMul(thing->info->mass, thing->scale);
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (thing->state == &states[S_STEAM1] && tmthing->z + tmthing->height >= thing->z + thing->height - FixedMul(16*FRACUNIT, thing->scale)) // Only when it bursts
|
|
tmthing->momz = -FixedMul(thing->info->mass, FixedSqrt(FixedMul(thing->scale, tmthing->scale)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (!(thing->eflags & MFE_VERTICALFLIP) && tmthing->z >= thing->z)
|
|
{
|
|
switch (thing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (tmthing->z <= thing->z + (thing->health << FRACBITS) && tmthing->momz < FixedMul(thing->info->mass, thing->scale))
|
|
{
|
|
tmthing->momz += FixedMul(thing->info->mass/4, thing->scale);
|
|
|
|
if (tmthing->momz > FixedMul(thing->info->mass, thing->scale))
|
|
tmthing->momz = FixedMul(thing->info->mass, thing->scale);
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (thing->state == &states[S_STEAM1] && tmthing->z <= thing->z + FixedMul(16*FRACUNIT, thing->scale)) // Only when it bursts
|
|
tmthing->momz = FixedMul(thing->info->mass, FixedSqrt(FixedMul(thing->scale, tmthing->scale)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((!(thing->eflags & MFE_VERTICALFLIP) && (tmthing->z <= (thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)) && (tmthing->z + tmthing->height) >= thing->z))
|
|
|| ((thing->eflags & MFE_VERTICALFLIP) && (tmthing->z + tmthing->height >= (thing->z - FixedMul(FRACUNIT, thing->scale)) && tmthing->z <= (thing->z + thing->height))))
|
|
{
|
|
if (thing->flags & MF_SPRING)
|
|
{
|
|
P_DoSpring(thing, tmthing);
|
|
tmsprung = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Damage other players when invincible
|
|
if (tmthing->player && thing->player
|
|
// Make sure they aren't able to damage you ANYWHERE along the Z axis, you have to be TOUCHING the person.
|
|
&& !(thing->z + thing->height < tmthing->z || thing->z > tmthing->z + tmthing->height))
|
|
{
|
|
if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam))
|
|
{
|
|
if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super])
|
|
&& !thing->player->powers[pw_super])
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super])
|
|
&& !tmthing->player->powers[pw_super])
|
|
P_DamageMobj(tmthing, thing, thing, 1);
|
|
}
|
|
|
|
// If players are using touch tag, seekers damage hiders.
|
|
if (G_TagGametype() && cv_touchtag.value &&
|
|
((thing->player->pflags & PF_TAGIT) != (tmthing->player->pflags & PF_TAGIT)))
|
|
{
|
|
if ((tmthing->player->pflags & PF_TAGIT) && !(thing->player->pflags & PF_TAGIT))
|
|
P_DamageMobj(thing, tmthing, tmthing, 1);
|
|
else if ((thing->player->pflags & PF_TAGIT) && !(tmthing->player->pflags & PF_TAGIT))
|
|
P_DamageMobj(tmthing, thing, tmthing, 1);
|
|
}
|
|
}
|
|
|
|
// Force solid players in hide and seek to avoid corner stacking.
|
|
if (cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
|
|
{
|
|
if (tmthing->player && thing->player)
|
|
{
|
|
if ((tmthing->player->pflags & PF_CARRIED) && tmthing->tracer == thing)
|
|
return true;
|
|
else if ((thing->player->pflags & PF_CARRIED) && thing->tracer == tmthing)
|
|
return true;
|
|
else if (tmthing->player->powers[pw_tailsfly]
|
|
|| (tmthing->player->charability == CA_FLY && (tmthing->state >= &states[S_PLAY_SPC1] && tmthing->state <= &states[S_PLAY_SPC4])))
|
|
{
|
|
INT32 p;
|
|
|
|
if (tmthing->player->bot == 1)
|
|
return true;
|
|
|
|
if (thing->player->pflags & PF_NIGHTSMODE)
|
|
return true;
|
|
|
|
if ((thing->eflags & MFE_VERTICALFLIP) != (tmthing->eflags & MFE_VERTICALFLIP))
|
|
return true; // Both should be in same gravity
|
|
|
|
if ((tmthing->eflags & MFE_VERTICALFLIP)
|
|
&& tmthing->ceilingz - (tmthing->z + tmthing->height) < thing->height-FixedMul(2*FRACUNIT, thing->scale))
|
|
return true;
|
|
else if (tmthing->z - tmthing->floorz < thing->height-FixedMul(2*FRACUNIT, thing->scale))
|
|
return true; // No room to pick up this guy!
|
|
|
|
// Search in case another player is already being carried by this fox.
|
|
for (p = 0; p < MAXPLAYERS; p++)
|
|
if (playeringame[p] && players[p].mo
|
|
&& players[p].pflags & PF_CARRIED && players[p].mo->tracer == tmthing)
|
|
return true;
|
|
|
|
if ((!(tmthing->eflags & MFE_VERTICALFLIP) && (tmthing->z <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale))
|
|
&& tmthing->z > thing->z + thing->height*2/3
|
|
&& thing->momz <= 0)
|
|
|| ((tmthing->eflags & MFE_VERTICALFLIP) && (tmthing->z + tmthing->height >= thing->z - FixedMul(FRACUNIT, thing->scale))
|
|
&& tmthing->z + tmthing->height < thing->z + thing->height - thing->height*2/3
|
|
&& thing->momz >= 0))
|
|
{
|
|
// Why block opposing teams from tailsflying each other?
|
|
// Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows.
|
|
/*
|
|
if (gametype == GT_RACE || gametype == GT_COMPETITION
|
|
|| (netgame && (tmthing->player->spectator || thing->player->spectator))
|
|
|| (G_TagGametype() && (!(tmthing->player->pflags & PF_TAGIT) != !(thing->player->pflags & PF_TAGIT)))
|
|
|| (gametype == GT_MATCH)
|
|
|| (G_GametypeHasTeams() && tmthing->player->ctfteam != thing->player->ctfteam))
|
|
thing->player->pflags &= ~PF_CARRIED; */
|
|
if (tmthing->player->spectator || thing->player->spectator)
|
|
thing->player->pflags &= ~PF_CARRIED;
|
|
else
|
|
{
|
|
if (thing->player-players == consoleplayer && botingame)
|
|
CV_SetValue(&cv_analog2, false);
|
|
P_ResetPlayer(thing->player);
|
|
P_SetTarget(&thing->tracer, tmthing);
|
|
thing->player->pflags |= PF_CARRIED;
|
|
S_StartSound(thing, sfx_s3k4a);
|
|
P_UnsetThingPosition(thing);
|
|
thing->x = tmthing->x;
|
|
thing->y = tmthing->y;
|
|
P_SetThingPosition(thing);
|
|
}
|
|
}
|
|
else {
|
|
if (thing->player-players == consoleplayer && botingame)
|
|
CV_SetValue(&cv_analog2, true);
|
|
thing->player->pflags &= ~PF_CARRIED;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if (thing->player) {
|
|
if (thing->player-players == consoleplayer && botingame)
|
|
CV_SetValue(&cv_analog2, true);
|
|
thing->player->pflags &= ~PF_CARRIED;
|
|
}
|
|
|
|
if (thing->player)
|
|
{
|
|
if (tmthing->eflags & MFE_VERTICALFLIP) //doesn't matter what gravity player's following! Just do your stuff in YOUR direction only
|
|
{
|
|
// Objects kill you if it falls from above.
|
|
if (tmthing->z + tmthing->height + tmthing->momz >= thing->z
|
|
&& tmthing->z + tmthing->height + tmthing->momz < thing->z + thing->height
|
|
&& P_IsObjectOnGround(thing)
|
|
&& (tmthing->flags & MF_SOLID))
|
|
{
|
|
if ((tmthing->flags & MF_MONITOR) || (tmthing->flags & MF_PUSHABLE))
|
|
{
|
|
if (thing != tmthing->target)
|
|
P_DamageMobj(thing, tmthing, tmthing->target, 10000);
|
|
|
|
tmthing->momz = -tmthing->momz/2; // Bounce, just for fun!
|
|
// The tmthing->target allows the pusher of the object
|
|
// to get the point if he topples it on an opponent.
|
|
}
|
|
}
|
|
|
|
if (thing->z + thing->height <= tmthing->z + tmthing->height && !(thing->state == &states[thing->info->painstate])) // Stuff where da player don't gotta move
|
|
{
|
|
switch (tmthing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (thing->z + thing->height >= tmthing->z + tmthing->height - (tmthing->health << FRACBITS) && thing->momz > -FixedMul(tmthing->info->mass, tmthing->scale) && !(thing->player->climbing || (thing->player->pflags & PF_GLIDING)))
|
|
{
|
|
thing->momz -= FixedMul(tmthing->info->mass/4, tmthing->scale);
|
|
|
|
if (thing->momz < -FixedMul(tmthing->info->mass, tmthing->scale))
|
|
thing->momz = -FixedMul(tmthing->info->mass, tmthing->scale);
|
|
|
|
if (!thing->player->powers[pw_tailsfly])
|
|
{
|
|
P_ResetPlayer(thing->player);
|
|
if (thing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(thing, S_PLAY_FALL1);
|
|
}
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (tmthing->state == &states[S_STEAM1] && thing->z + thing->height >= tmthing->z + tmthing->height - FixedMul(16*FRACUNIT, tmthing->scale)) // Only when it bursts
|
|
{
|
|
thing->momz = -FixedMul(tmthing->info->mass, FixedSqrt(FixedMul(tmthing->scale, thing->scale)));
|
|
P_ResetPlayer(thing->player);
|
|
if (thing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(thing, S_PLAY_FALL1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Objects kill you if it falls from above.
|
|
if (tmthing->z + tmthing->momz <= thing->z + thing->height
|
|
&& tmthing->z + tmthing->momz > thing->z
|
|
&& P_IsObjectOnGround(thing)
|
|
&& (tmthing->flags & MF_SOLID))
|
|
{
|
|
if ((tmthing->flags & MF_MONITOR) || (tmthing->flags & MF_PUSHABLE))
|
|
{
|
|
if (thing != tmthing->target)
|
|
P_DamageMobj(thing, tmthing, tmthing->target, 10000);
|
|
|
|
tmthing->momz = -tmthing->momz/2; // Bounce, just for fun!
|
|
// The tmthing->target allows the pusher of the object
|
|
// to get the point if he topples it on an opponent.
|
|
}
|
|
}
|
|
|
|
if (thing->z >= tmthing->z && !(thing->state == &states[thing->info->painstate])) // Stuff where da player don't gotta move
|
|
{
|
|
switch (tmthing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (thing->z <= tmthing->z + (tmthing->health << FRACBITS) && thing->momz < FixedMul(tmthing->info->mass, tmthing->scale) && !(thing->player->climbing || (thing->player->pflags & PF_GLIDING)))
|
|
{
|
|
thing->momz += FixedMul(tmthing->info->mass/4, tmthing->scale);
|
|
|
|
if (thing->momz > FixedMul(tmthing->info->mass, tmthing->scale))
|
|
thing->momz = FixedMul(tmthing->info->mass, tmthing->scale);
|
|
|
|
if (!thing->player->powers[pw_tailsfly])
|
|
{
|
|
P_ResetPlayer(thing->player);
|
|
if (thing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(thing, S_PLAY_FALL1);
|
|
}
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (tmthing->state == &states[S_STEAM1] && thing->z <= tmthing->z + FixedMul(16*FRACUNIT, tmthing->scale)) // Only when it bursts
|
|
{
|
|
thing->momz = FixedMul(tmthing->info->mass, FixedSqrt(FixedMul(tmthing->scale, thing->scale)));
|
|
P_ResetPlayer(thing->player);
|
|
if (thing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(thing, S_PLAY_FALL1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tmthing->player) // Is the moving/interacting object the player?
|
|
{
|
|
if (!tmthing->health)
|
|
return true;
|
|
|
|
if ((thing->eflags & MFE_VERTICALFLIP) && tmthing->z + tmthing->height <= thing->z + thing->height && !(tmthing->state == &states[tmthing->info->painstate]))
|
|
{
|
|
switch (thing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (tmthing->z + tmthing->height >= thing->z + thing->height - (thing->health << FRACBITS) && tmthing->momz > -FixedMul(thing->info->mass, thing->scale) && !(tmthing->player->climbing || (tmthing->player->pflags & PF_GLIDING)))
|
|
{
|
|
tmthing->momz -= FixedMul(thing->info->mass/4, thing->scale);
|
|
|
|
if (tmthing->momz < -FixedMul(thing->info->mass, thing->scale))
|
|
tmthing->momz = -FixedMul(thing->info->mass, thing->scale);
|
|
|
|
if (!tmthing->player->powers[pw_tailsfly])
|
|
{
|
|
P_ResetPlayer(tmthing->player);
|
|
if (tmthing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(tmthing, S_PLAY_FALL1);
|
|
}
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (thing->state == &states[S_STEAM1] && tmthing->z + tmthing->height >= thing->z + thing->height - FixedMul(16*FRACUNIT, thing->scale)) // Only when it bursts
|
|
{
|
|
tmthing->momz = -FixedMul(thing->info->mass, FixedSqrt(FixedMul(thing->scale, tmthing->scale)));
|
|
P_ResetPlayer(tmthing->player);
|
|
if (tmthing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(tmthing, S_PLAY_FALL1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (!(thing->eflags & MFE_VERTICALFLIP) && tmthing->z >= thing->z && !(tmthing->state == &states[tmthing->info->painstate]))
|
|
{
|
|
switch (thing->type)
|
|
{
|
|
case MT_FAN: // fan
|
|
if (tmthing->z <= thing->z + (thing->health << FRACBITS) && tmthing->momz < FixedMul(thing->info->mass, thing->scale) && !(tmthing->player->climbing || (tmthing->player->pflags & PF_GLIDING)))
|
|
{
|
|
tmthing->momz += FixedMul(thing->info->mass/4, thing->scale);
|
|
|
|
if (tmthing->momz > FixedMul(thing->info->mass, thing->scale))
|
|
tmthing->momz = FixedMul(thing->info->mass, thing->scale);
|
|
|
|
if (!tmthing->player->powers[pw_tailsfly])
|
|
{
|
|
P_ResetPlayer(tmthing->player);
|
|
if (tmthing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(tmthing, S_PLAY_FALL1);
|
|
}
|
|
}
|
|
break;
|
|
case MT_STEAM: // Steam
|
|
if (thing->state == &states[S_STEAM1] && tmthing->z <= thing->z + FixedMul(16*FRACUNIT, thing->scale)) // Only when it bursts
|
|
{
|
|
tmthing->momz = FixedMul(thing->info->mass, FixedSqrt(FixedMul(thing->scale, tmthing->scale)));
|
|
P_ResetPlayer(tmthing->player);
|
|
if (tmthing->player->panim != PA_FALL)
|
|
P_SetPlayerMobjState(tmthing, S_PLAY_FALL1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Are you touching the side of the object you're interacting with?
|
|
if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height
|
|
&& thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z)
|
|
{
|
|
if (thing->flags & MF_SPRING)
|
|
{
|
|
P_DoSpring(thing, tmthing);
|
|
tmsprung = true;
|
|
}
|
|
else if (thing->flags & MF_MONITOR
|
|
&& tmthing->player->pflags & (PF_JUMPED|PF_SPINNING|PF_GLIDING))
|
|
{
|
|
boolean flip = (thing->eflags & MFE_VERTICALFLIP) != 0; // Save this flag in case monitor gets removed.
|
|
fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
|
|
P_DamageMobj(thing, tmthing, tmthing, 1); // break the monitor
|
|
// Going down? Then bounce back up.
|
|
if ((P_MobjWasRemoved(thing) // Monitor was removed
|
|
|| !thing->health) // or otherwise popped
|
|
&& ((!flip && *momz < 0) // monitor is on the floor and you're going down
|
|
|| (flip && *momz > 0))) // or on the ceiling and you're going up
|
|
*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
|
|
return false;
|
|
}
|
|
else if (thing->flags & MF_BOSS
|
|
&& ((tmthing->player->pflags & PF_JUMPED) || (tmthing->player->pflags & PF_SPINNING)
|
|
|| tmthing->player->powers[pw_invulnerability]
|
|
|| tmthing->player->powers[pw_super]))
|
|
{
|
|
// Going down? Then bounce back up.
|
|
if (abs(tmthing->momz) > 0)
|
|
tmthing->momz = -tmthing->momz;
|
|
|
|
// Also, bounce back.
|
|
tmthing->momx = -tmthing->momx;
|
|
tmthing->momy = -tmthing->momy;
|
|
P_DamageMobj(thing, tmthing, tmthing, 1); // fight the boss!
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// z checking at last
|
|
// Treat noclip things as non-solid!
|
|
if ((thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID) &&
|
|
!(thing->flags & MF_NOCLIP) && !(tmthing->flags & MF_NOCLIP))
|
|
{
|
|
if (tmthing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
// pass under
|
|
tmtopz = tmthing->z;
|
|
|
|
if (tmtopz > thing->z + thing->height)
|
|
{
|
|
if (thing->z + thing->height > tmfloorz)
|
|
{
|
|
tmfloorz = thing->z + thing->height;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
topz = thing->z - FixedMul(FRACUNIT, thing->scale);
|
|
|
|
// block only when jumping not high enough,
|
|
// (dont climb max. 24units while already in air)
|
|
// if not in air, let P_TryMove() decide if it's not too high
|
|
if (tmthing->player && tmthing->z + tmthing->height > topz
|
|
&& tmthing->z + tmthing->height < tmthing->ceilingz)
|
|
return false; // block while in air
|
|
|
|
if (topz < tmceilingz && !(thing->flags & MF_SPRING))
|
|
{
|
|
tmceilingz = topz;
|
|
tmfloorthing = thing; // thing we may stand on
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pass under
|
|
tmtopz = tmthing->z + tmthing->height;
|
|
|
|
if (tmtopz < thing->z)
|
|
{
|
|
if (thing->z < tmceilingz)
|
|
{
|
|
tmceilingz = thing->z;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
topz = thing->z + thing->height + FixedMul(FRACUNIT, thing->scale);
|
|
|
|
// block only when jumping not high enough,
|
|
// (dont climb max. 24units while already in air)
|
|
// if not in air, let P_TryMove() decide if it's not too high
|
|
if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz)
|
|
return false; // block while in air
|
|
|
|
if (topz > tmfloorz && !(thing->flags & MF_SPRING))
|
|
{
|
|
tmfloorz = topz;
|
|
tmfloorthing = thing; // thing we may stand on
|
|
}
|
|
}
|
|
}
|
|
|
|
// not solid not blocked
|
|
return true;
|
|
}
|
|
|
|
// PIT_CheckCameraLine
|
|
// Adjusts tmfloorz and tmceilingz as lines are contacted - FOR CAMERA ONLY
|
|
static boolean PIT_CheckCameraLine(line_t *ld)
|
|
{
|
|
if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID))
|
|
return true;
|
|
|
|
if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
|
|
|| tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (P_BoxOnLineSide(tmbbox, ld) != -1)
|
|
return true;
|
|
|
|
// A line has been hit
|
|
|
|
// The moving thing's destination position will cross
|
|
// the given line.
|
|
// If this should not be allowed, return false.
|
|
// If the line is special, keep track of it
|
|
// to process later if the move is proven ok.
|
|
// NOTE: specials are NOT sorted by order,
|
|
// so two special lines that are only 8 pixels apart
|
|
// could be crossed in either order.
|
|
|
|
// this line is out of the if so upper and lower textures can be hit by a splat
|
|
blockingline = ld;
|
|
if (!ld->backsector) // one sided line
|
|
{
|
|
if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld))
|
|
return true; // don't hit the back side
|
|
return false;
|
|
}
|
|
|
|
// set openrange, opentop, openbottom
|
|
P_CameraLineOpening(ld);
|
|
|
|
// adjust floor / ceiling heights
|
|
if (opentop < tmceilingz)
|
|
{
|
|
tmceilingz = opentop;
|
|
ceilingline = ld;
|
|
}
|
|
|
|
if (openbottom > tmfloorz)
|
|
{
|
|
tmfloorz = openbottom;
|
|
}
|
|
|
|
if (lowfloor < tmdropoffz)
|
|
tmdropoffz = lowfloor;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// PIT_CheckLine
|
|
// Adjusts tmfloorz and tmceilingz as lines are contacted
|
|
//
|
|
static boolean PIT_CheckLine(line_t *ld)
|
|
{
|
|
if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID))
|
|
return true;
|
|
|
|
if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
|
|
|| tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
|
|
return true;
|
|
|
|
if (P_BoxOnLineSide(tmbbox, ld) != -1)
|
|
return true;
|
|
|
|
// A line has been hit
|
|
|
|
// The moving thing's destination position will cross
|
|
// the given line.
|
|
// If this should not be allowed, return false.
|
|
// If the line is special, keep track of it
|
|
// to process later if the move is proven ok.
|
|
// NOTE: specials are NOT sorted by order,
|
|
// so two special lines that are only 8 pixels apart
|
|
// could be crossed in either order.
|
|
|
|
// this line is out of the if so upper and lower textures can be hit by a splat
|
|
blockingline = ld;
|
|
if (!ld->backsector) // one sided line
|
|
{
|
|
if (P_PointOnLineSide(tmthing->x, tmthing->y, ld))
|
|
return true; // don't hit the back side
|
|
return false;
|
|
}
|
|
|
|
// missiles can cross uncrossable lines
|
|
if (!(tmthing->flags & MF_MISSILE))
|
|
{
|
|
if (ld->flags & ML_IMPASSIBLE) // block objects from moving through this linedef.
|
|
return false;
|
|
if (((tmthing->flags & MF_ENEMY) || (tmthing->flags & MF_BOSS)) && ld->flags & ML_BLOCKMONSTERS)
|
|
return false; // block monsters only
|
|
}
|
|
|
|
// set openrange, opentop, openbottom
|
|
P_LineOpening(ld);
|
|
|
|
// adjust floor / ceiling heights
|
|
if (opentop < tmceilingz)
|
|
{
|
|
tmceilingz = opentop;
|
|
ceilingline = ld;
|
|
}
|
|
|
|
if (openbottom > tmfloorz)
|
|
{
|
|
tmfloorz = openbottom;
|
|
}
|
|
|
|
if (lowfloor < tmdropoffz)
|
|
tmdropoffz = lowfloor;
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================================
|
|
// MOVEMENT CLIPPING
|
|
// =========================================================================
|
|
|
|
//
|
|
// P_CheckPosition
|
|
// This is purely informative, nothing is modified
|
|
// (except things picked up).
|
|
//
|
|
// in:
|
|
// a mobj_t (can be valid or invalid)
|
|
// a position to be checked
|
|
// (doesn't need to be related to the mobj_t->x,y)
|
|
//
|
|
// during:
|
|
// special things are touched if MF_PICKUP
|
|
// early out on solid lines?
|
|
//
|
|
// out:
|
|
// newsubsec
|
|
// tmfloorz
|
|
// tmceilingz
|
|
// tmdropoffz
|
|
// the lowest point contacted
|
|
// (monsters won't move to a dropoff)
|
|
// speciallines[]
|
|
// numspeciallines
|
|
//
|
|
|
|
// tmfloorz
|
|
// the nearest floor or thing's top under tmthing
|
|
// tmceilingz
|
|
// the nearest ceiling or thing's bottom over tmthing
|
|
//
|
|
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
|
|
{
|
|
INT32 xl, xh, yl, yh, bx, by;
|
|
subsector_t *newsubsec;
|
|
boolean blockval = true;
|
|
|
|
I_Assert(thing != NULL);
|
|
#ifdef PARANOIA
|
|
if (P_MobjWasRemoved(thing))
|
|
I_Error("Previously-removed Thing of type %u crashes P_CheckPosition!", thing->type);
|
|
#endif
|
|
|
|
P_SetTarget(&tmthing, thing);
|
|
tmflags = thing->flags;
|
|
|
|
tmx = x;
|
|
tmy = y;
|
|
|
|
tmbbox[BOXTOP] = y + tmthing->radius;
|
|
tmbbox[BOXBOTTOM] = y - tmthing->radius;
|
|
tmbbox[BOXRIGHT] = x + tmthing->radius;
|
|
tmbbox[BOXLEFT] = x - tmthing->radius;
|
|
|
|
newsubsec = R_PointInSubsector(x, y);
|
|
ceilingline = blockingline = NULL;
|
|
|
|
// The base floor / ceiling is from the subsector
|
|
// that contains the point.
|
|
// Any contacted lines the step closer together
|
|
// will adjust them.
|
|
tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
|
|
tmceilingz = newsubsec->sector->ceilingheight;
|
|
|
|
// Check list of fake floors and see if tmfloorz/tmceilingz need to be altered.
|
|
if (newsubsec->sector->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
fixed_t delta1, delta2;
|
|
INT32 thingtop = thing->z + thing->height;
|
|
|
|
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(rover->flags & FF_EXISTS))
|
|
continue;
|
|
|
|
if (rover->flags & FF_GOOWATER && !(thing->flags & MF_NOGRAVITY))
|
|
{
|
|
// If you're inside goowater and slowing down
|
|
fixed_t sinklevel = FixedMul(thing->info->height/6, thing->scale);
|
|
fixed_t minspeed = FixedMul(thing->info->height/12, thing->scale);
|
|
if (thing->z < *rover->topheight && *rover->bottomheight < thingtop
|
|
&& abs(thing->momz) < minspeed)
|
|
{
|
|
// Oh no! The object is stick in between the surface of the goo and sinklevel! help them out!
|
|
if (!(thing->eflags & MFE_VERTICALFLIP) && thing->z > *rover->topheight - sinklevel
|
|
&& thing->momz >= 0 && thing->momz < (minspeed>>2))
|
|
thing->momz += minspeed>>2;
|
|
else if (thing->eflags & MFE_VERTICALFLIP && thingtop < *rover->bottomheight + sinklevel
|
|
&& thing->momz <= 0 && thing->momz > -(minspeed>>2))
|
|
thing->momz -= minspeed>>2;
|
|
|
|
// Land on the top or the bottom, depending on gravity flip.
|
|
if (!(thing->eflags & MFE_VERTICALFLIP) && thing->z >= *rover->topheight - sinklevel && thing->momz <= 0)
|
|
{
|
|
if (tmfloorz < *rover->topheight - sinklevel)
|
|
tmfloorz = *rover->topheight - sinklevel;
|
|
}
|
|
else if (thing->eflags & MFE_VERTICALFLIP && thingtop <= *rover->bottomheight + sinklevel && thing->momz >= 0)
|
|
{
|
|
if (tmceilingz > *rover->bottomheight + sinklevel)
|
|
tmceilingz = *rover->bottomheight + sinklevel;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (thing->player && (P_CheckSolidLava(thing, rover) || P_CanRunOnWater(thing->player, rover)))
|
|
;
|
|
else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE))
|
|
;
|
|
else if (!((((rover->flags & FF_BLOCKPLAYER) && thing->player)
|
|
|| ((rover->flags & FF_BLOCKOTHERS) && !thing->player))
|
|
|| rover->flags & FF_QUICKSAND))
|
|
continue;
|
|
|
|
if (rover->flags & FF_QUICKSAND)
|
|
{
|
|
if (thing->z < *rover->topheight && *rover->bottomheight < thingtop)
|
|
{
|
|
if (tmfloorz < thing->z)
|
|
tmfloorz = thing->z;
|
|
}
|
|
// Quicksand blocks never change heights otherwise.
|
|
continue;
|
|
}
|
|
|
|
delta1 = thing->z - (*rover->bottomheight
|
|
+ ((*rover->topheight - *rover->bottomheight)/2));
|
|
delta2 = thingtop - (*rover->bottomheight
|
|
+ ((*rover->topheight - *rover->bottomheight)/2));
|
|
|
|
if (*rover->topheight > tmfloorz && abs(delta1) < abs(delta2)
|
|
&& (!(rover->flags & FF_REVERSEPLATFORM)))
|
|
{
|
|
tmfloorz = tmdropoffz = *rover->topheight;
|
|
}
|
|
if (*rover->bottomheight < tmceilingz && abs(delta1) >= abs(delta2)
|
|
&& (/*thing->z + thing->height <= *rover->bottomheight
|
|
|| */!(rover->flags & FF_PLATFORM))
|
|
&& !(thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE)))
|
|
{
|
|
tmceilingz = *rover->bottomheight;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef POLYOBJECTS
|
|
// Check polyobjects and see if tmfloorz/tmceilingz need to be altered
|
|
{
|
|
validcount++;
|
|
|
|
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
|
|
for (by = yl; by <= yh; by++)
|
|
for (bx = xl; bx <= xh; bx++)
|
|
{
|
|
INT32 offset;
|
|
polymaplink_t *plink; // haleyjd 02/22/06
|
|
|
|
if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
|
|
continue;
|
|
|
|
offset = by*bmapwidth + bx;
|
|
|
|
// haleyjd 02/22/06: consider polyobject lines
|
|
plink = polyblocklinks[offset];
|
|
|
|
while (plink)
|
|
{
|
|
polyobj_t *po = plink->po;
|
|
|
|
if (po->validcount != validcount) // if polyobj hasn't been checked
|
|
{
|
|
sector_t *polysec;
|
|
fixed_t delta1, delta2, thingtop;
|
|
fixed_t polytop, polybottom;
|
|
|
|
po->validcount = validcount;
|
|
|
|
if (!P_BBoxInsidePolyobj(po, tmbbox)
|
|
|| !(po->flags & POF_SOLID))
|
|
{
|
|
plink = (polymaplink_t *)(plink->link.next);
|
|
continue;
|
|
}
|
|
|
|
// We're inside it! Yess...
|
|
polysec = po->lines[0]->backsector;
|
|
|
|
if (po->flags & POF_CLIPPLANES)
|
|
{
|
|
polytop = polysec->ceilingheight;
|
|
polybottom = polysec->floorheight;
|
|
}
|
|
else
|
|
{
|
|
polytop = INT32_MAX;
|
|
polybottom = INT32_MIN;
|
|
}
|
|
|
|
thingtop = thing->z + thing->height;
|
|
delta1 = thing->z - (polybottom + ((polytop - polybottom)/2));
|
|
delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
|
|
|
|
if (polytop > tmfloorz && abs(delta1) < abs(delta2))
|
|
tmfloorz = tmdropoffz = polytop;
|
|
|
|
if (polybottom < tmceilingz && abs(delta1) >= abs(delta2))
|
|
tmceilingz = polybottom;
|
|
}
|
|
plink = (polymaplink_t *)(plink->link.next);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// tmfloorthing is set when tmfloorz comes from a thing's top
|
|
tmfloorthing = NULL;
|
|
tmhitthing = NULL;
|
|
|
|
validcount++;
|
|
|
|
if (tmflags & MF_NOCLIP)
|
|
return true;
|
|
|
|
// Check things first, possibly picking things up.
|
|
// The bounding box is extended by MAXRADIUS
|
|
// because mobj_ts are grouped into mapblocks
|
|
// based on their origin point, and can overlap
|
|
// into adjacent blocks by up to MAXRADIUS units.
|
|
|
|
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
|
|
|
|
// MF_NOCLIPTHING: used by camera to not be blocked by things
|
|
if (!(thing->flags & MF_NOCLIPTHING))
|
|
{
|
|
for (bx = xl; bx <= xh; bx++)
|
|
for (by = yl; by <= yh; by++)
|
|
{
|
|
if (!P_BlockThingsIterator(bx, by, PIT_CheckThing))
|
|
blockval = false;
|
|
if (P_MobjWasRemoved(tmthing))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
validcount++;
|
|
|
|
// check lines
|
|
for (bx = xl; bx <= xh; bx++)
|
|
for (by = yl; by <= yh; by++)
|
|
if (!P_BlockLinesIterator(bx, by, PIT_CheckLine))
|
|
blockval = false;
|
|
|
|
return blockval;
|
|
}
|
|
|
|
static const fixed_t hoopblockdist = 16*FRACUNIT + 8*FRACUNIT;
|
|
static const fixed_t hoophalfheight = (56*FRACUNIT)/2;
|
|
|
|
// P_CheckPosition optimized for the MT_HOOPCOLLIDE object. This needs to be as fast as possible!
|
|
void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius)
|
|
{
|
|
INT32 i;
|
|
|
|
(void)radius; //unused
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i] || !players[i].mo || players[i].spectator)
|
|
continue;
|
|
|
|
if (abs(players[i].mo->x - x) >= hoopblockdist ||
|
|
abs(players[i].mo->y - y) >= hoopblockdist ||
|
|
abs((players[i].mo->z+hoophalfheight) - z) >= hoopblockdist)
|
|
continue; // didn't hit it
|
|
|
|
// can remove thing
|
|
P_TouchSpecialThing(hoopthing, players[i].mo, false);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// P_CheckCameraPosition
|
|
//
|
|
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
|
|
{
|
|
INT32 xl, xh, yl, yh, bx, by;
|
|
subsector_t *newsubsec;
|
|
|
|
tmx = x;
|
|
tmy = y;
|
|
|
|
tmbbox[BOXTOP] = y + thiscam->radius;
|
|
tmbbox[BOXBOTTOM] = y - thiscam->radius;
|
|
tmbbox[BOXRIGHT] = x + thiscam->radius;
|
|
tmbbox[BOXLEFT] = x - thiscam->radius;
|
|
|
|
newsubsec = R_PointInSubsector(x, y);
|
|
ceilingline = blockingline = NULL;
|
|
|
|
mapcampointer = thiscam;
|
|
|
|
if (GETSECSPECIAL(newsubsec->sector->special, 4) == 12)
|
|
{ // Camera noclip on entire sector.
|
|
tmfloorz = tmdropoffz = thiscam->z;
|
|
tmceilingz = thiscam->z + thiscam->height;
|
|
return true;
|
|
}
|
|
|
|
// The base floor / ceiling is from the subsector
|
|
// that contains the point.
|
|
// Any contacted lines the step closer together
|
|
// will adjust them.
|
|
tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
|
|
tmceilingz = newsubsec->sector->ceilingheight;
|
|
|
|
// Cameras use the heightsec's heights rather then the actual sector heights.
|
|
// If you can see through it, why not move the camera through it too?
|
|
if (newsubsec->sector->heightsec >= 0)
|
|
{
|
|
tmfloorz = tmdropoffz = sectors[newsubsec->sector->heightsec].floorheight;
|
|
tmceilingz = sectors[newsubsec->sector->heightsec].ceilingheight;
|
|
}
|
|
|
|
// Check list of fake floors and see if tmfloorz/tmceilingz need to be altered.
|
|
if (newsubsec->sector->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
fixed_t delta1, delta2;
|
|
INT32 thingtop = thiscam->z + thiscam->height;
|
|
|
|
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(rover->flags & FF_BLOCKOTHERS) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERALL) || GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
|
|
continue;
|
|
|
|
delta1 = thiscam->z - (*rover->bottomheight
|
|
+ ((*rover->topheight - *rover->bottomheight)/2));
|
|
delta2 = thingtop - (*rover->bottomheight
|
|
+ ((*rover->topheight - *rover->bottomheight)/2));
|
|
if (*rover->topheight > tmfloorz && abs(delta1) < abs(delta2))
|
|
{
|
|
tmfloorz = tmdropoffz = *rover->topheight;
|
|
}
|
|
if (*rover->bottomheight < tmceilingz && abs(delta1) >= abs(delta2))
|
|
{
|
|
tmceilingz = *rover->bottomheight;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef POLYOBJECTS
|
|
// Check polyobjects and see if tmfloorz/tmceilingz need to be altered
|
|
{
|
|
validcount++;
|
|
|
|
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
|
|
for (by = yl; by <= yh; by++)
|
|
for (bx = xl; bx <= xh; bx++)
|
|
{
|
|
INT32 offset;
|
|
polymaplink_t *plink; // haleyjd 02/22/06
|
|
|
|
if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
|
|
continue;
|
|
|
|
offset = by*bmapwidth + bx;
|
|
|
|
// haleyjd 02/22/06: consider polyobject lines
|
|
plink = polyblocklinks[offset];
|
|
|
|
while (plink)
|
|
{
|
|
polyobj_t *po = plink->po;
|
|
|
|
if (po->validcount != validcount) // if polyobj hasn't been checked
|
|
{
|
|
sector_t *polysec;
|
|
fixed_t delta1, delta2, thingtop;
|
|
fixed_t polytop, polybottom;
|
|
|
|
po->validcount = validcount;
|
|
|
|
if (!P_PointInsidePolyobj(po, x, y))
|
|
{
|
|
plink = (polymaplink_t *)(plink->link.next);
|
|
continue;
|
|
}
|
|
|
|
// We're inside it! Yess...
|
|
polysec = po->lines[0]->backsector;
|
|
|
|
if (GETSECSPECIAL(polysec->special, 4) == 12)
|
|
{ // Camera noclip polyobj.
|
|
plink = (polymaplink_t *)(plink->link.next);
|
|
continue;
|
|
}
|
|
|
|
if (po->flags & POF_CLIPPLANES)
|
|
{
|
|
polytop = polysec->ceilingheight;
|
|
polybottom = polysec->floorheight;
|
|
}
|
|
else
|
|
{
|
|
polytop = INT32_MAX;
|
|
polybottom = INT32_MIN;
|
|
}
|
|
|
|
thingtop = thiscam->z + thiscam->height;
|
|
delta1 = thiscam->z - (polybottom + ((polytop - polybottom)/2));
|
|
delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
|
|
|
|
if (polytop > tmfloorz && abs(delta1) < abs(delta2))
|
|
tmfloorz = tmdropoffz = polytop;
|
|
|
|
if (polybottom < tmceilingz && abs(delta1) >= abs(delta2))
|
|
tmceilingz = polybottom;
|
|
}
|
|
plink = (polymaplink_t *)(plink->link.next);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Check things.
|
|
// The bounding box is extended by MAXRADIUS
|
|
// because mobj_ts are grouped into mapblocks
|
|
// based on their origin point, and can overlap
|
|
// into adjacent blocks by up to MAXRADIUS units.
|
|
|
|
// check lines
|
|
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
|
|
for (bx = xl; bx <= xh; bx++)
|
|
for (by = yl; by <= yh; by++)
|
|
if (!P_BlockLinesIterator(bx, by, PIT_CheckCameraLine))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// CheckMissileImpact
|
|
//
|
|
static void CheckMissileImpact(mobj_t *mobj)
|
|
{
|
|
if (!(mobj->flags & MF_MISSILE) || !mobj->target)
|
|
return;
|
|
|
|
if (!mobj->target->player)
|
|
return;
|
|
}
|
|
|
|
// The highest the camera will "step up" onto another floor.
|
|
#define MAXCAMERASTEPMOVE MAXSTEPMOVE
|
|
|
|
//
|
|
// P_TryCameraMove
|
|
//
|
|
// Attempt to move the camera to a new position
|
|
//
|
|
// Return true if the move succeeded and no sliding should be done.
|
|
//
|
|
boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
|
|
{
|
|
subsector_t *s = R_PointInSubsector(x, y);
|
|
boolean retval = true;
|
|
boolean itsatwodlevel = false;
|
|
|
|
floatok = false;
|
|
|
|
if (twodlevel
|
|
|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
|
|
|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
|
|
itsatwodlevel = true;
|
|
|
|
if (!itsatwodlevel && players[displayplayer].mo)
|
|
{
|
|
fixed_t tryx = thiscam->x;
|
|
fixed_t tryy = thiscam->y;
|
|
|
|
if ((thiscam == &camera && (players[displayplayer].pflags & PF_NOCLIP))
|
|
|| (thiscam == &camera2 && (players[secondarydisplayplayer].pflags & PF_NOCLIP)))
|
|
{ // Noclipping player camera noclips too!!
|
|
floatok = true;
|
|
thiscam->floorz = thiscam->z;
|
|
thiscam->ceilingz = thiscam->z + thiscam->height;
|
|
thiscam->x = x;
|
|
thiscam->y = y;
|
|
thiscam->subsector = s;
|
|
return true;
|
|
}
|
|
|
|
do {
|
|
if (x-tryx > MAXRADIUS)
|
|
tryx += MAXRADIUS;
|
|
else if (x-tryx < -MAXRADIUS)
|
|
tryx -= MAXRADIUS;
|
|
else
|
|
tryx = x;
|
|
if (y-tryy > MAXRADIUS)
|
|
tryy += MAXRADIUS;
|
|
else if (y-tryy < -MAXRADIUS)
|
|
tryy -= MAXRADIUS;
|
|
else
|
|
tryy = y;
|
|
|
|
if (!P_CheckCameraPosition(tryx, tryy, thiscam))
|
|
return false; // solid wall or thing
|
|
|
|
if (tmceilingz - tmfloorz < thiscam->height)
|
|
return false; // doesn't fit
|
|
|
|
floatok = true;
|
|
|
|
if (tmceilingz - thiscam->z < thiscam->height)
|
|
{
|
|
if (s == thiscam->subsector && tmceilingz >= thiscam->z)
|
|
{
|
|
floatok = true;
|
|
thiscam->floorz = tmfloorz;
|
|
thiscam->ceilingz = tmfloorz + thiscam->height;
|
|
thiscam->x = x;
|
|
thiscam->y = y;
|
|
thiscam->subsector = s;
|
|
return true;
|
|
}
|
|
else
|
|
return false; // mobj must lower itself to fit
|
|
}
|
|
|
|
if ((tmfloorz - thiscam->z > MAXCAMERASTEPMOVE))
|
|
return false; // too big a step up
|
|
} while(tryx != x || tryy != y);
|
|
}
|
|
else
|
|
{
|
|
tmfloorz = thiscam->subsector->sector->floorheight;
|
|
tmceilingz = thiscam->subsector->sector->ceilingheight;
|
|
}
|
|
|
|
// the move is ok,
|
|
// so link the thing into its new position
|
|
|
|
thiscam->floorz = tmfloorz;
|
|
thiscam->ceilingz = tmceilingz;
|
|
thiscam->x = x;
|
|
thiscam->y = y;
|
|
thiscam->subsector = s;
|
|
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// PIT_PushableMoved
|
|
//
|
|
// Move things standing on top
|
|
// of pushable things being pushed.
|
|
//
|
|
static mobj_t *stand;
|
|
static fixed_t standx, standy;
|
|
|
|
boolean PIT_PushableMoved(mobj_t *thing)
|
|
{
|
|
fixed_t blockdist;
|
|
|
|
if (!(thing->flags & MF_SOLID)
|
|
|| (thing->flags & MF_NOGRAVITY))
|
|
return true; // Don't move something non-solid!
|
|
|
|
// Only pushables are supported... in 2.0. Now players can be moved too!
|
|
if (!(thing->flags & MF_PUSHABLE || thing->player))
|
|
return true;
|
|
|
|
if (thing == stand)
|
|
return true;
|
|
|
|
blockdist = stand->radius + thing->radius;
|
|
|
|
if (abs(thing->x - stand->x) >= blockdist || abs(thing->y - stand->y) >= blockdist)
|
|
return true; // didn't hit it
|
|
|
|
if ((!(stand->eflags & MFE_VERTICALFLIP) && thing->z != stand->z + stand->height + FixedMul(FRACUNIT, stand->scale))
|
|
|| ((stand->eflags & MFE_VERTICALFLIP) && thing->z + thing->height != stand->z - FixedMul(FRACUNIT, stand->scale)))
|
|
return true; // Not standing on top
|
|
|
|
if (!stand->momx && !stand->momy)
|
|
return true;
|
|
|
|
// Move this guy!
|
|
if (thing->player)
|
|
{
|
|
// Monster Iestyn - 29/11/13
|
|
// Ridiculous amount of newly declared stuff so players can't get stuck in walls AND so gargoyles don't break themselves at the same time either
|
|
// These are all non-static map variables that are changed for each and every single mobj
|
|
// See, changing player's momx/y would possibly trigger stuff as if the player were running somehow, so this must be done to keep the player standing
|
|
// All this so players can ride gargoyles!
|
|
boolean oldfltok = floatok;
|
|
fixed_t oldflrz = tmfloorz;
|
|
fixed_t oldceilz = tmceilingz;
|
|
boolean oldsprung = tmsprung;
|
|
mobj_t *oldflrthing = tmfloorthing;
|
|
mobj_t *oldthing = tmthing;
|
|
line_t *oldceilline = ceilingline;
|
|
line_t *oldblockline = blockingline;
|
|
|
|
// Move the player
|
|
P_TryMove(thing, thing->x+stand->momx, thing->y+stand->momy, true);
|
|
|
|
// Now restore EVERYTHING so the gargoyle doesn't keep the player's tmstuff and break
|
|
floatok = oldfltok;
|
|
tmfloorz = oldflrz;
|
|
tmceilingz = oldceilz;
|
|
tmsprung = oldsprung;
|
|
tmfloorthing = oldflrthing;
|
|
P_SetTarget(&tmthing, oldthing);
|
|
ceilingline = oldceilline;
|
|
blockingline = oldblockline;
|
|
}
|
|
else
|
|
{
|
|
thing->momx = stand->momx;
|
|
thing->momy = stand->momy;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// P_TryMove
|
|
// Attempt to move to a new position.
|
|
//
|
|
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
|
|
{
|
|
fixed_t tryx = thing->x;
|
|
fixed_t tryy = thing->y;
|
|
fixed_t radius = thing->radius;
|
|
floatok = false;
|
|
|
|
if (radius < MAXRADIUS/2)
|
|
radius = MAXRADIUS/2;
|
|
|
|
do {
|
|
if (thing->flags & MF_NOCLIP) {
|
|
tryx = x;
|
|
tryy = y;
|
|
} else {
|
|
if (x-tryx > radius)
|
|
tryx += radius;
|
|
else if (x-tryx < -radius)
|
|
tryx -= radius;
|
|
else
|
|
tryx = x;
|
|
if (y-tryy > radius)
|
|
tryy += radius;
|
|
else if (y-tryy < -radius)
|
|
tryy -= radius;
|
|
else
|
|
tryy = y;
|
|
}
|
|
|
|
if (!P_CheckPosition(thing, tryx, tryy))
|
|
{
|
|
if (!P_MobjWasRemoved(thing))
|
|
CheckMissileImpact(thing);
|
|
return false; // solid wall or thing
|
|
}
|
|
|
|
if (!(thing->flags & MF_NOCLIP))
|
|
{
|
|
//All things are affected by their scale.
|
|
fixed_t maxstep = FixedMul(MAXSTEPMOVE, thing->scale);
|
|
|
|
if (thing->player)
|
|
{
|
|
// Don't 'step up' while springing,
|
|
// Only step up "if needed".
|
|
if (thing->state == &states[S_PLAY_SPRING]
|
|
&& P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale))
|
|
maxstep = 0;
|
|
}
|
|
|
|
if (thing->type == MT_SKIM)
|
|
maxstep = 0;
|
|
|
|
if (tmceilingz - tmfloorz < thing->height)
|
|
{
|
|
CheckMissileImpact(thing);
|
|
if (tmfloorthing)
|
|
tmhitthing = tmfloorthing;
|
|
return false; // doesn't fit
|
|
}
|
|
|
|
floatok = true;
|
|
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (thing->z < tmfloorz)
|
|
{
|
|
CheckMissileImpact(thing);
|
|
return false; // mobj must raise itself to fit
|
|
}
|
|
}
|
|
else if (tmceilingz - thing->z < thing->height)
|
|
{
|
|
CheckMissileImpact(thing);
|
|
return false; // mobj must lower itself to fit
|
|
}
|
|
|
|
// If using type Section1:13, double the maxstep.
|
|
if (thing->player && (P_PlayerTouchingSectorSpecial(thing->player, 1, 13)
|
|
|| GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13))
|
|
maxstep <<= 1;
|
|
|
|
// Ramp test
|
|
if (thing->player && !P_PlayerTouchingSectorSpecial(thing->player, 1, 14)
|
|
&& GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) != 14)
|
|
{
|
|
// If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS
|
|
// step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more.
|
|
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (thing->z+thing->height == thing->ceilingz && tmceilingz > thing->z+thing->height && tmceilingz - thing->z+thing->height <= maxstep)
|
|
{
|
|
thing->z = tmceilingz - thing->height;
|
|
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
|
|
}
|
|
}
|
|
else if (thing->z == thing->floorz && tmfloorz < thing->z && thing->z - tmfloorz <= maxstep)
|
|
{
|
|
thing->z = tmfloorz;
|
|
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
|
|
}
|
|
}
|
|
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (thing->z + thing->height > tmceilingz + maxstep)
|
|
{
|
|
CheckMissileImpact(thing);
|
|
return false; // too big a step up
|
|
}
|
|
}
|
|
else if (tmfloorz - thing->z > maxstep)
|
|
{
|
|
CheckMissileImpact(thing);
|
|
if (tmfloorthing)
|
|
tmhitthing = tmfloorthing;
|
|
return false; // too big a step up
|
|
}
|
|
|
|
if (tmfloorz > thing->z)
|
|
{
|
|
if ((thing->flags & MF_MISSILE))
|
|
CheckMissileImpact(thing);
|
|
}
|
|
|
|
if (!allowdropoff)
|
|
if (!(thing->flags & MF_FLOAT) && thing->type != MT_SKIM && !tmfloorthing
|
|
&& tmfloorz - tmdropoffz > maxstep)
|
|
return false; // don't stand over a dropoff
|
|
}
|
|
} while (tryx != x || tryy != y);
|
|
|
|
// The move is ok!
|
|
|
|
// If it's a pushable object, check if anything is
|
|
// standing on top and move it, too.
|
|
if (thing->flags & MF_PUSHABLE)
|
|
{
|
|
INT32 bx, by, xl, xh, yl, yh;
|
|
|
|
yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(thing->x + MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xl = (unsigned)(thing->x - MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
|
|
|
|
stand = thing;
|
|
standx = x;
|
|
standy = y;
|
|
|
|
for (by = yl; by <= yh; by++)
|
|
for (bx = xl; bx <= xh; bx++)
|
|
P_BlockThingsIterator(bx, by, PIT_PushableMoved);
|
|
}
|
|
|
|
// Link the thing into its new position
|
|
P_UnsetThingPosition(thing);
|
|
|
|
thing->floorz = tmfloorz;
|
|
thing->ceilingz = tmceilingz;
|
|
thing->x = x;
|
|
thing->y = y;
|
|
|
|
if (tmfloorthing)
|
|
thing->eflags &= ~MFE_ONGROUND; // not on real floor
|
|
else
|
|
thing->eflags |= MFE_ONGROUND;
|
|
|
|
P_SetThingPosition(thing);
|
|
return true;
|
|
}
|
|
|
|
boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
|
|
{
|
|
fixed_t tryx, tryy;
|
|
tryx = thing->x;
|
|
tryy = thing->y;
|
|
do {
|
|
if (x-tryx > MAXRADIUS)
|
|
tryx += MAXRADIUS;
|
|
else if (x-tryx < -MAXRADIUS)
|
|
tryx -= MAXRADIUS;
|
|
else
|
|
tryx = x;
|
|
if (y-tryy > MAXRADIUS)
|
|
tryy += MAXRADIUS;
|
|
else if (y-tryy < -MAXRADIUS)
|
|
tryy -= MAXRADIUS;
|
|
else
|
|
tryy = y;
|
|
|
|
if (!P_CheckPosition(thing, tryx, tryy))
|
|
return false; // solid wall or thing
|
|
|
|
if (!(thing->flags & MF_NOCLIP))
|
|
{
|
|
const fixed_t maxstep = MAXSTEPMOVE;
|
|
|
|
if (tmceilingz - tmfloorz < thing->height)
|
|
return false; // doesn't fit
|
|
|
|
if (tmceilingz - thing->z < thing->height)
|
|
return false; // mobj must lower itself to fit
|
|
|
|
if (tmfloorz - thing->z > maxstep)
|
|
return false; // too big a step up
|
|
}
|
|
} while(tryx != x || tryy != y);
|
|
|
|
// the move is ok,
|
|
// so link the thing into its new position
|
|
P_UnsetThingPosition(thing);
|
|
|
|
thing->floorz = tmfloorz;
|
|
thing->ceilingz = tmceilingz;
|
|
thing->x = x;
|
|
thing->y = y;
|
|
|
|
if (tmfloorthing)
|
|
thing->eflags &= ~MFE_ONGROUND; // not on real floor
|
|
else
|
|
thing->eflags |= MFE_ONGROUND;
|
|
|
|
P_SetThingPosition(thing);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// P_ThingHeightClip
|
|
// Takes a valid thing and adjusts the thing->floorz,
|
|
// thing->ceilingz, and possibly thing->z.
|
|
// This is called for all nearby monsters
|
|
// whenever a sector changes height.
|
|
// If the thing doesn't fit,
|
|
// the z will be set to the lowest value
|
|
// and false will be returned.
|
|
//
|
|
static boolean P_ThingHeightClip(mobj_t *thing)
|
|
{
|
|
fixed_t oldfloorz = thing->floorz;
|
|
boolean onfloor = P_IsObjectOnGround(thing);//(thing->z <= thing->floorz);
|
|
|
|
if (thing->flags & MF_NOCLIPHEIGHT)
|
|
return true;
|
|
|
|
P_CheckPosition(thing, thing->x, thing->y);
|
|
|
|
if (P_MobjWasRemoved(thing))
|
|
return true;
|
|
|
|
thing->floorz = tmfloorz;
|
|
thing->ceilingz = tmceilingz;
|
|
|
|
// Ugly hack?!?! As long as just ceilingz is the lowest,
|
|
// you'll still get crushed, right?
|
|
if (tmfloorz > oldfloorz+thing->height)
|
|
return true;
|
|
|
|
if (/*!tmfloorthing && */onfloor && !(thing->flags & MF_NOGRAVITY))
|
|
{
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
thing->pmomz = thing->ceilingz - (thing->z + thing->height);
|
|
else
|
|
thing->pmomz = thing->floorz - thing->z;
|
|
|
|
if (thing->player)
|
|
{
|
|
if (splitscreen && camera2.chase && thing->player == &players[secondarydisplayplayer])
|
|
camera2.z += thing->pmomz;
|
|
else if (camera.chase && thing->player == &players[displayplayer])
|
|
camera.z += thing->pmomz;
|
|
}
|
|
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
thing->z = thing->ceilingz - thing->height;
|
|
else
|
|
thing->z = thing->floorz;
|
|
}
|
|
else if (!tmfloorthing)
|
|
{
|
|
// don't adjust a floating monster unless forced to
|
|
if (thing->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if (!onfloor && thing->z < tmfloorz)
|
|
thing->z = thing->floorz;
|
|
}
|
|
else if (!onfloor && thing->z + thing->height > tmceilingz)
|
|
thing->z = thing->ceilingz - thing->height;
|
|
}
|
|
|
|
// debug: be sure it falls to the floor
|
|
thing->eflags &= ~MFE_ONGROUND;
|
|
|
|
if (thing->ceilingz - thing->floorz < thing->height && thing->z >= thing->floorz)
|
|
// BP: i know that this code cause many trouble but this also fixes
|
|
// a lot of problems, mainly this is implementation of the stepping
|
|
// for mobj (walk on solid corpse without jumping or fake 3d bridge)
|
|
// problem is imp into imp at map01 and monster going at top of others
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// SLIDE MOVE
|
|
// Allows the player to slide along any angled walls.
|
|
//
|
|
static fixed_t bestslidefrac, secondslidefrac;
|
|
static line_t *bestslideline;
|
|
static line_t *secondslideline;
|
|
static mobj_t *slidemo;
|
|
static fixed_t tmxmove, tmymove;
|
|
|
|
//
|
|
// P_HitCameraSlideLine
|
|
//
|
|
static void P_HitCameraSlideLine(line_t *ld, camera_t *thiscam)
|
|
{
|
|
INT32 side;
|
|
angle_t lineangle, moveangle, deltaangle;
|
|
fixed_t movelen, newlen;
|
|
|
|
if (ld->slopetype == ST_HORIZONTAL)
|
|
{
|
|
tmymove = 0;
|
|
return;
|
|
}
|
|
|
|
if (ld->slopetype == ST_VERTICAL)
|
|
{
|
|
tmxmove = 0;
|
|
return;
|
|
}
|
|
|
|
side = P_PointOnLineSide(thiscam->x, thiscam->y, ld);
|
|
lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
|
|
|
|
if (side == 1)
|
|
lineangle += ANGLE_180;
|
|
|
|
moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
|
deltaangle = moveangle-lineangle;
|
|
|
|
if (deltaangle > ANGLE_180)
|
|
deltaangle += ANGLE_180;
|
|
|
|
lineangle >>= ANGLETOFINESHIFT;
|
|
deltaangle >>= ANGLETOFINESHIFT;
|
|
|
|
movelen = P_AproxDistance(tmxmove, tmymove);
|
|
newlen = FixedMul(movelen, FINECOSINE(deltaangle));
|
|
|
|
tmxmove = FixedMul(newlen, FINECOSINE(lineangle));
|
|
tmymove = FixedMul(newlen, FINESINE(lineangle));
|
|
}
|
|
|
|
//
|
|
// P_HitSlideLine
|
|
// Adjusts the xmove / ymove
|
|
// so that the next move will slide along the wall.
|
|
//
|
|
static void P_HitSlideLine(line_t *ld)
|
|
{
|
|
INT32 side;
|
|
angle_t lineangle, moveangle, deltaangle;
|
|
fixed_t movelen, newlen;
|
|
|
|
if (ld->slopetype == ST_HORIZONTAL)
|
|
{
|
|
tmymove = 0;
|
|
return;
|
|
}
|
|
|
|
if (ld->slopetype == ST_VERTICAL)
|
|
{
|
|
tmxmove = 0;
|
|
return;
|
|
}
|
|
|
|
side = P_PointOnLineSide(slidemo->x, slidemo->y, ld);
|
|
|
|
lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
|
|
|
|
if (side == 1)
|
|
lineangle += ANGLE_180;
|
|
|
|
moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
|
deltaangle = moveangle-lineangle;
|
|
|
|
if (deltaangle > ANGLE_180)
|
|
deltaangle += ANGLE_180;
|
|
|
|
lineangle >>= ANGLETOFINESHIFT;
|
|
deltaangle >>= ANGLETOFINESHIFT;
|
|
|
|
movelen = P_AproxDistance(tmxmove, tmymove);
|
|
newlen = FixedMul(movelen, FINECOSINE(deltaangle));
|
|
|
|
tmxmove = FixedMul(newlen, FINECOSINE(lineangle));
|
|
tmymove = FixedMul(newlen, FINESINE(lineangle));
|
|
}
|
|
|
|
//
|
|
// P_HitBounceLine
|
|
//
|
|
// Adjusts the xmove / ymove so that the next move will bounce off the wall.
|
|
//
|
|
static void P_HitBounceLine(line_t *ld)
|
|
{
|
|
angle_t lineangle, moveangle, deltaangle;
|
|
fixed_t movelen;
|
|
|
|
if (ld->slopetype == ST_HORIZONTAL)
|
|
{
|
|
tmymove = -tmymove;
|
|
return;
|
|
}
|
|
|
|
if (ld->slopetype == ST_VERTICAL)
|
|
{
|
|
tmxmove = -tmxmove;
|
|
return;
|
|
}
|
|
|
|
lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
|
|
|
|
if (lineangle >= ANGLE_180)
|
|
lineangle -= ANGLE_180;
|
|
|
|
moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
|
deltaangle = moveangle + 2*(lineangle - moveangle);
|
|
|
|
lineangle >>= ANGLETOFINESHIFT;
|
|
deltaangle >>= ANGLETOFINESHIFT;
|
|
|
|
movelen = P_AproxDistance(tmxmove, tmymove);
|
|
|
|
tmxmove = FixedMul(movelen, FINECOSINE(deltaangle));
|
|
tmymove = FixedMul(movelen, FINESINE(deltaangle));
|
|
|
|
deltaangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
|
}
|
|
|
|
//
|
|
// PTR_SlideCameraTraverse
|
|
//
|
|
static boolean PTR_SlideCameraTraverse(intercept_t *in)
|
|
{
|
|
line_t *li;
|
|
|
|
I_Assert(in->isaline);
|
|
|
|
li = in->d.line;
|
|
|
|
// one-sided linedef
|
|
if (!li->backsector)
|
|
{
|
|
if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, li))
|
|
return true; // don't hit the back side
|
|
goto isblocking;
|
|
}
|
|
|
|
// set openrange, opentop, openbottom
|
|
P_CameraLineOpening(li);
|
|
|
|
if (openrange < mapcampointer->height)
|
|
goto isblocking; // doesn't fit
|
|
|
|
if (opentop - mapcampointer->z < mapcampointer->height)
|
|
goto isblocking; // mobj is too high
|
|
|
|
if (openbottom - mapcampointer->z > 0) // We don't want to make the camera step up.
|
|
goto isblocking; // too big a step up
|
|
|
|
// this line doesn't block movement
|
|
return true;
|
|
|
|
// the line does block movement,
|
|
// see if it is closer than best so far
|
|
isblocking:
|
|
{
|
|
if (in->frac < bestslidefrac)
|
|
{
|
|
secondslidefrac = bestslidefrac;
|
|
secondslideline = bestslideline;
|
|
bestslidefrac = in->frac;
|
|
bestslideline = li;
|
|
}
|
|
}
|
|
|
|
return false; // stop
|
|
}
|
|
|
|
//
|
|
// P_IsClimbingValid
|
|
//
|
|
static boolean P_IsClimbingValid(player_t *player, angle_t angle)
|
|
{
|
|
fixed_t platx, platy;
|
|
subsector_t *glidesector;
|
|
|
|
platx = P_ReturnThrustX(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
|
|
platy = P_ReturnThrustY(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
|
|
|
|
glidesector = R_PointInSubsector(player->mo->x + platx, player->mo->y + platy);
|
|
|
|
if (glidesector->sector != player->mo->subsector->sector)
|
|
{
|
|
boolean floorclimb = false;
|
|
|
|
if (glidesector->sector->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
|
|
continue;
|
|
|
|
floorclimb = true;
|
|
|
|
if (player->mo->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if ((*rover->topheight < player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) < *rover->topheight))
|
|
{
|
|
floorclimb = true;
|
|
}
|
|
if (*rover->topheight < player->mo->z) // Waaaay below the ledge.
|
|
{
|
|
floorclimb = false;
|
|
}
|
|
if (*rover->bottomheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale))
|
|
{
|
|
floorclimb = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((*rover->bottomheight > player->mo->z) && ((player->mo->z - player->mo->momz) > *rover->bottomheight))
|
|
{
|
|
floorclimb = true;
|
|
}
|
|
if (*rover->bottomheight > player->mo->z + player->mo->height) // Waaaay below the ledge.
|
|
{
|
|
floorclimb = false;
|
|
}
|
|
if (*rover->topheight < player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale))
|
|
{
|
|
floorclimb = false;
|
|
}
|
|
}
|
|
|
|
if (floorclimb)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (player->mo->eflags & MFE_VERTICALFLIP)
|
|
{
|
|
if ((glidesector->sector->floorheight <= player->mo->z + player->mo->height)
|
|
&& ((player->mo->z + player->mo->height - player->mo->momz) <= glidesector->sector->floorheight))
|
|
floorclimb = true;
|
|
|
|
if ((glidesector->sector->floorheight > player->mo->z)
|
|
&& glidesector->sector->floorpic == skyflatnum)
|
|
return false;
|
|
|
|
if ((player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale) > glidesector->sector->ceilingheight)
|
|
|| (player->mo->z + player->mo->height <= glidesector->sector->floorheight))
|
|
floorclimb = true;
|
|
}
|
|
else
|
|
{
|
|
if ((glidesector->sector->ceilingheight >= player->mo->z)
|
|
&& ((player->mo->z - player->mo->momz) >= glidesector->sector->ceilingheight))
|
|
floorclimb = true;
|
|
|
|
if ((glidesector->sector->ceilingheight < player->mo->z+player->mo->height)
|
|
&& glidesector->sector->ceilingpic == skyflatnum)
|
|
return false;
|
|
|
|
if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < glidesector->sector->floorheight)
|
|
|| (player->mo->z >= glidesector->sector->ceilingheight))
|
|
floorclimb = true;
|
|
}
|
|
|
|
if (!floorclimb)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// PTR_SlideTraverse
|
|
//
|
|
static boolean PTR_SlideTraverse(intercept_t *in)
|
|
{
|
|
line_t *li;
|
|
|
|
I_Assert(in->isaline);
|
|
|
|
li = in->d.line;
|
|
|
|
// one-sided linedefs are always solid to sliding movement.
|
|
// one-sided linedef
|
|
if (!li->backsector)
|
|
{
|
|
if (P_PointOnLineSide(slidemo->x, slidemo->y, li))
|
|
return true; // don't hit the back side
|
|
goto isblocking;
|
|
}
|
|
|
|
if (!(slidemo->flags & MF_MISSILE))
|
|
{
|
|
if (li->flags & ML_IMPASSIBLE)
|
|
goto isblocking;
|
|
|
|
if (((slidemo->flags & MF_ENEMY) || (slidemo->flags & MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
|
|
goto isblocking;
|
|
}
|
|
|
|
// set openrange, opentop, openbottom
|
|
P_LineOpening(li);
|
|
|
|
if (openrange < slidemo->height)
|
|
goto isblocking; // doesn't fit
|
|
|
|
if (opentop - slidemo->z < slidemo->height)
|
|
goto isblocking; // mobj is too high
|
|
|
|
if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
|
|
goto isblocking; // too big a step up
|
|
|
|
// this line doesn't block movement
|
|
return true;
|
|
|
|
// the line does block movement,
|
|
// see if it is closer than best so far
|
|
isblocking:
|
|
if (li->polyobj && slidemo->player)
|
|
{
|
|
if ((li->polyobj->lines[0]->backsector->flags & SF_TRIGGERSPECIAL_TOUCH) && !(li->polyobj->flags & POF_NOSPECIALS))
|
|
P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
|
|
}
|
|
|
|
if (slidemo->player && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)
|
|
&& slidemo->player->charability == CA_GLIDEANDCLIMB)
|
|
{
|
|
line_t *checkline = li;
|
|
sector_t *checksector;
|
|
ffloor_t *rover;
|
|
boolean fofline = false;
|
|
INT32 side = P_PointOnLineSide(slidemo->x, slidemo->y, li);
|
|
|
|
if (!side && li->backsector)
|
|
checksector = li->backsector;
|
|
else
|
|
checksector = li->frontsector;
|
|
|
|
if (checksector->ffloors)
|
|
{
|
|
for (rover = checksector->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
|
|
continue;
|
|
|
|
if (*rover->topheight < slidemo->z)
|
|
continue;
|
|
|
|
if (*rover->bottomheight > slidemo->z + slidemo->height)
|
|
continue;
|
|
|
|
// Got this far, so I guess it's climbable.
|
|
if (rover->master->flags & ML_TFERLINE)
|
|
{
|
|
size_t linenum = li-checksector->lines[0];
|
|
checkline = rover->master->frontsector->lines[0] + linenum;
|
|
fofline = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// see about climbing on the wall
|
|
if (!(checkline->flags & ML_NOCLIMB))
|
|
{
|
|
angle_t climbangle, climbline;
|
|
INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
|
|
|
|
climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
|
|
|
|
if (whichside) // on second side?
|
|
climbline += ANGLE_180;
|
|
|
|
climbangle += (ANGLE_90 * (whichside ? -1 : 1));
|
|
|
|
if (((!slidemo->player->climbing && abs(slidemo->angle - ANGLE_90 - climbline) < ANGLE_45)
|
|
|| (slidemo->player->climbing == 1 && abs(slidemo->angle - climbline) < ANGLE_135))
|
|
&& P_IsClimbingValid(slidemo->player, climbangle))
|
|
{
|
|
slidemo->angle = climbangle;
|
|
if (slidemo->player == &players[consoleplayer])
|
|
localangle = slidemo->angle;
|
|
else if (slidemo->player == &players[secondarydisplayplayer])
|
|
localangle2 = slidemo->angle;
|
|
|
|
if (!slidemo->player->climbing)
|
|
{
|
|
S_StartSound(slidemo->player->mo, sfx_s3k4a);
|
|
slidemo->player->climbing = 5;
|
|
}
|
|
|
|
slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_THOKKED);
|
|
slidemo->player->glidetime = 0;
|
|
slidemo->player->secondjump = 0;
|
|
|
|
if (slidemo->player->climbing > 1)
|
|
slidemo->momz = slidemo->momx = slidemo->momy = 0;
|
|
|
|
if (fofline)
|
|
whichside = 0;
|
|
|
|
if (!whichside)
|
|
{
|
|
slidemo->player->lastsidehit = checkline->sidenum[whichside];
|
|
slidemo->player->lastlinehit = (INT16)(checkline - lines);
|
|
}
|
|
|
|
P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing))
|
|
{
|
|
secondslidefrac = bestslidefrac;
|
|
secondslideline = bestslideline;
|
|
bestslidefrac = in->frac;
|
|
bestslideline = li;
|
|
}
|
|
|
|
return false; // stop
|
|
}
|
|
|
|
//
|
|
// P_SlideCameraMove
|
|
//
|
|
// Tries to slide the camera along a wall.
|
|
//
|
|
void P_SlideCameraMove(camera_t *thiscam)
|
|
{
|
|
fixed_t leadx, leady, trailx, traily, newx, newy;
|
|
INT32 hitcount = 0;
|
|
INT32 retval = 0;
|
|
|
|
bestslideline = NULL;
|
|
|
|
retry:
|
|
if (++hitcount == 3)
|
|
goto stairstep; // don't loop forever
|
|
|
|
// trace along the three leading corners
|
|
if (thiscam->momx > 0)
|
|
{
|
|
leadx = thiscam->x + thiscam->radius;
|
|
trailx = thiscam->x - thiscam->radius;
|
|
}
|
|
else
|
|
{
|
|
leadx = thiscam->x - thiscam->radius;
|
|
trailx = thiscam->x + thiscam->radius;
|
|
}
|
|
|
|
if (thiscam->momy > 0)
|
|
{
|
|
leady = thiscam->y + thiscam->radius;
|
|
traily = thiscam->y - thiscam->radius;
|
|
}
|
|
else
|
|
{
|
|
leady = thiscam->y - thiscam->radius;
|
|
traily = thiscam->y + thiscam->radius;
|
|
}
|
|
|
|
bestslidefrac = FRACUNIT+1;
|
|
|
|
mapcampointer = thiscam;
|
|
|
|
P_PathTraverse(leadx, leady, leadx + thiscam->momx, leady + thiscam->momy,
|
|
PT_ADDLINES, PTR_SlideCameraTraverse);
|
|
P_PathTraverse(trailx, leady, trailx + thiscam->momx, leady + thiscam->momy,
|
|
PT_ADDLINES, PTR_SlideCameraTraverse);
|
|
P_PathTraverse(leadx, traily, leadx + thiscam->momx, traily + thiscam->momy,
|
|
PT_ADDLINES, PTR_SlideCameraTraverse);
|
|
|
|
// move up to the wall
|
|
if (bestslidefrac == FRACUNIT+1)
|
|
{
|
|
retval = P_TryCameraMove(thiscam->x, thiscam->y + thiscam->momy, thiscam);
|
|
// the move must have hit the middle, so stairstep
|
|
stairstep:
|
|
if (!retval) // Allow things to drop off.
|
|
P_TryCameraMove(thiscam->x + thiscam->momx, thiscam->y, thiscam);
|
|
return;
|
|
}
|
|
|
|
// fudge a bit to make sure it doesn't hit
|
|
bestslidefrac -= 0x800;
|
|
if (bestslidefrac > 0)
|
|
{
|
|
newx = FixedMul(thiscam->momx, bestslidefrac);
|
|
newy = FixedMul(thiscam->momy, bestslidefrac);
|
|
|
|
retval = P_TryCameraMove(thiscam->x + newx, thiscam->y + newy, thiscam);
|
|
|
|
if (!retval)
|
|
goto stairstep;
|
|
}
|
|
|
|
// Now continue along the wall.
|
|
// First calculate remainder.
|
|
bestslidefrac = FRACUNIT - (bestslidefrac+0x800);
|
|
|
|
if (bestslidefrac > FRACUNIT)
|
|
bestslidefrac = FRACUNIT;
|
|
|
|
if (bestslidefrac <= 0)
|
|
return;
|
|
|
|
tmxmove = FixedMul(thiscam->momx, bestslidefrac);
|
|
tmymove = FixedMul(thiscam->momy, bestslidefrac);
|
|
|
|
P_HitCameraSlideLine(bestslideline, thiscam); // clip the moves
|
|
|
|
thiscam->momx = tmxmove;
|
|
thiscam->momy = tmymove;
|
|
|
|
retval = P_TryCameraMove(thiscam->x + tmxmove, thiscam->y + tmymove, thiscam);
|
|
|
|
if (!retval)
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// P_SlideMove
|
|
// The momx / momy move is bad, so try to slide
|
|
// along a wall.
|
|
// Find the first line hit, move flush to it,
|
|
// and slide along it
|
|
//
|
|
// This is a kludgy mess.
|
|
//
|
|
void P_SlideMove(mobj_t *mo)
|
|
{
|
|
fixed_t leadx, leady, trailx, traily, newx, newy;
|
|
INT16 hitcount = 0;
|
|
boolean success = false;
|
|
|
|
if (tmhitthing)
|
|
{
|
|
// Don't mess with your momentum if it's a pushable object. Pushables do their own crazy things already.
|
|
if (tmhitthing->flags & MF_PUSHABLE)
|
|
return;
|
|
|
|
// Thankfully box collisions are a lot simpler than arbitrary lines. There's only four possible cases.
|
|
if (mo->y + mo->radius <= tmhitthing->y - tmhitthing->radius)
|
|
{
|
|
mo->momy = 0;
|
|
P_TryMove(mo, mo->x + mo->momx, tmhitthing->y - tmhitthing->radius - mo->radius, true);
|
|
}
|
|
else if (mo->y - mo->radius >= tmhitthing->y + tmhitthing->radius)
|
|
{
|
|
mo->momy = 0;
|
|
P_TryMove(mo, mo->x + mo->momx, tmhitthing->y + tmhitthing->radius + mo->radius, true);
|
|
}
|
|
else if (mo->x + mo->radius <= tmhitthing->x - tmhitthing->radius)
|
|
{
|
|
mo->momx = 0;
|
|
P_TryMove(mo, tmhitthing->x - tmhitthing->radius - mo->radius, mo->y + mo->momy, true);
|
|
}
|
|
else if (mo->x - mo->radius >= tmhitthing->x + tmhitthing->radius)
|
|
{
|
|
mo->momx = 0;
|
|
P_TryMove(mo, tmhitthing->x + tmhitthing->radius + mo->radius, mo->y + mo->momy, true);
|
|
}
|
|
else
|
|
mo->momx = mo->momy = 0;
|
|
return;
|
|
}
|
|
|
|
slidemo = mo;
|
|
bestslideline = NULL;
|
|
|
|
retry:
|
|
if (++hitcount == 3)
|
|
goto stairstep; // don't loop forever
|
|
|
|
// trace along the three leading corners
|
|
if (mo->momx > 0)
|
|
{
|
|
leadx = mo->x + mo->radius;
|
|
trailx = mo->x - mo->radius;
|
|
}
|
|
else
|
|
{
|
|
leadx = mo->x - mo->radius;
|
|
trailx = mo->x + mo->radius;
|
|
}
|
|
|
|
if (mo->momy > 0)
|
|
{
|
|
leady = mo->y + mo->radius;
|
|
traily = mo->y - mo->radius;
|
|
}
|
|
else
|
|
{
|
|
leady = mo->y - mo->radius;
|
|
traily = mo->y + mo->radius;
|
|
}
|
|
|
|
bestslidefrac = FRACUNIT+1;
|
|
|
|
P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy,
|
|
PT_ADDLINES, PTR_SlideTraverse);
|
|
P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy,
|
|
PT_ADDLINES, PTR_SlideTraverse);
|
|
P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy,
|
|
PT_ADDLINES, PTR_SlideTraverse);
|
|
|
|
// Some walls are bouncy even if you're not
|
|
if (bestslideline && bestslideline->flags & ML_BOUNCY)
|
|
{
|
|
P_BounceMove(mo);
|
|
return;
|
|
}
|
|
|
|
// move up to the wall
|
|
if (bestslidefrac == FRACUNIT+1)
|
|
{
|
|
// the move must have hit the middle, so stairstep
|
|
stairstep:
|
|
if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true)) //Allow things to drop off.
|
|
P_TryMove(mo, mo->x + mo->momx, mo->y, true);
|
|
return;
|
|
}
|
|
|
|
// fudge a bit to make sure it doesn't hit
|
|
bestslidefrac -= 0x800;
|
|
if (bestslidefrac > 0)
|
|
{
|
|
newx = FixedMul(mo->momx, bestslidefrac);
|
|
newy = FixedMul(mo->momy, bestslidefrac);
|
|
|
|
if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
|
|
goto stairstep;
|
|
}
|
|
|
|
// Now continue along the wall.
|
|
// First calculate remainder.
|
|
bestslidefrac = FRACUNIT - (bestslidefrac+0x800);
|
|
|
|
if (bestslidefrac > FRACUNIT)
|
|
bestslidefrac = FRACUNIT;
|
|
|
|
if (bestslidefrac <= 0)
|
|
return;
|
|
|
|
tmxmove = FixedMul(mo->momx, bestslidefrac);
|
|
tmymove = FixedMul(mo->momy, bestslidefrac);
|
|
|
|
P_HitSlideLine(bestslideline); // clip the moves
|
|
|
|
if ((twodlevel || (mo->flags2 & MF2_TWOD)) && mo->player)
|
|
{
|
|
mo->momx = tmxmove;
|
|
tmymove = 0;
|
|
}
|
|
else
|
|
{
|
|
mo->momx = tmxmove;
|
|
mo->momy = tmymove;
|
|
}
|
|
|
|
do {
|
|
if (tmxmove > mo->radius) {
|
|
newx = mo->x + mo->radius;
|
|
tmxmove -= mo->radius;
|
|
} else if (tmxmove < -mo->radius) {
|
|
newx = mo->x - mo->radius;
|
|
tmxmove += mo->radius;
|
|
} else {
|
|
newx = mo->x + tmxmove;
|
|
tmxmove = 0;
|
|
}
|
|
if (tmymove > mo->radius) {
|
|
newy = mo->y + mo->radius;
|
|
tmymove -= mo->radius;
|
|
} else if (tmymove < -mo->radius) {
|
|
newy = mo->y - mo->radius;
|
|
tmymove += mo->radius;
|
|
} else {
|
|
newy = mo->y + tmymove;
|
|
tmymove = 0;
|
|
}
|
|
if (!P_TryMove(mo, newx, newy, true)) {
|
|
if (success)
|
|
return; // Good enough!!
|
|
else
|
|
goto retry;
|
|
}
|
|
success = true;
|
|
} while(tmxmove || tmymove);
|
|
}
|
|
|
|
//
|
|
// P_BounceMove
|
|
//
|
|
// The momx / momy move is bad, so try to bounce off a wall.
|
|
//
|
|
void P_BounceMove(mobj_t *mo)
|
|
{
|
|
fixed_t leadx, leady;
|
|
fixed_t trailx, traily;
|
|
fixed_t newx, newy;
|
|
INT32 hitcount;
|
|
fixed_t mmomx = 0, mmomy = 0;
|
|
|
|
slidemo = mo;
|
|
hitcount = 0;
|
|
|
|
retry:
|
|
if (++hitcount == 3)
|
|
goto bounceback; // don't loop forever
|
|
|
|
if (mo->player)
|
|
{
|
|
mmomx = mo->player->rmomx;
|
|
mmomy = mo->player->rmomy;
|
|
}
|
|
else
|
|
{
|
|
mmomx = mo->momx;
|
|
mmomy = mo->momy;
|
|
}
|
|
|
|
// trace along the three leading corners
|
|
if (mo->momx > 0)
|
|
{
|
|
leadx = mo->x + mo->radius;
|
|
trailx = mo->x - mo->radius;
|
|
}
|
|
else
|
|
{
|
|
leadx = mo->x - mo->radius;
|
|
trailx = mo->x + mo->radius;
|
|
}
|
|
|
|
if (mo->momy > 0)
|
|
{
|
|
leady = mo->y + mo->radius;
|
|
traily = mo->y - mo->radius;
|
|
}
|
|
else
|
|
{
|
|
leady = mo->y - mo->radius;
|
|
traily = mo->y + mo->radius;
|
|
}
|
|
|
|
bestslidefrac = FRACUNIT + 1;
|
|
|
|
P_PathTraverse(leadx, leady, leadx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
|
|
P_PathTraverse(trailx, leady, trailx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
|
|
P_PathTraverse(leadx, traily, leadx + mmomx, traily + mmomy, PT_ADDLINES, PTR_SlideTraverse);
|
|
|
|
// move up to the wall
|
|
if (bestslidefrac == FRACUNIT + 1)
|
|
{
|
|
// the move must have hit the middle, so bounce straight back
|
|
bounceback:
|
|
if (P_TryMove(mo, mo->x - mmomx, mo->y - mmomy, true))
|
|
{
|
|
mo->momx *= -1;
|
|
mo->momy *= -1;
|
|
mo->momx = FixedMul(mo->momx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
mo->momy = FixedMul(mo->momy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
|
|
if (mo->player)
|
|
{
|
|
mo->player->cmomx *= -1;
|
|
mo->player->cmomy *= -1;
|
|
mo->player->cmomx = FixedMul(mo->player->cmomx,
|
|
(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
mo->player->cmomy = FixedMul(mo->player->cmomy,
|
|
(FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// fudge a bit to make sure it doesn't hit
|
|
bestslidefrac -= 0x800;
|
|
if (bestslidefrac > 0)
|
|
{
|
|
newx = FixedMul(mmomx, bestslidefrac);
|
|
newy = FixedMul(mmomy, bestslidefrac);
|
|
|
|
if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
|
|
goto bounceback;
|
|
}
|
|
|
|
// Now continue along the wall.
|
|
// First calculate remainder.
|
|
bestslidefrac = FRACUNIT - bestslidefrac;
|
|
|
|
if (bestslidefrac > FRACUNIT)
|
|
bestslidefrac = FRACUNIT;
|
|
|
|
if (bestslidefrac <= 0)
|
|
return;
|
|
|
|
if (mo->type == MT_SHELL)
|
|
{
|
|
tmxmove = mmomx;
|
|
tmymove = mmomy;
|
|
}
|
|
else if (mo->type == MT_THROWNBOUNCE)
|
|
{
|
|
tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
|
|
tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
|
|
}
|
|
else if (mo->type == MT_THROWNGRENADE || mo->type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)
|
|
{
|
|
// Quickly decay speed as it bounces
|
|
tmxmove = FixedDiv(mmomx, 2*FRACUNIT);
|
|
tmymove = FixedDiv(mmomy, 2*FRACUNIT);
|
|
}
|
|
else
|
|
{
|
|
tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
|
}
|
|
|
|
P_HitBounceLine(bestslideline); // clip the moves
|
|
|
|
mo->momx = tmxmove;
|
|
mo->momy = tmymove;
|
|
|
|
if (mo->player)
|
|
{
|
|
mo->player->cmomx = tmxmove;
|
|
mo->player->cmomy = tmymove;
|
|
}
|
|
|
|
if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true))
|
|
goto retry;
|
|
}
|
|
|
|
//
|
|
// RADIUS ATTACK
|
|
//
|
|
static fixed_t bombdamage;
|
|
static mobj_t *bombsource;
|
|
static mobj_t *bombspot;
|
|
|
|
//
|
|
// PIT_RadiusAttack
|
|
// "bombsource" is the creature
|
|
// that caused the explosion at "bombspot".
|
|
//
|
|
static boolean PIT_RadiusAttack(mobj_t *thing)
|
|
{
|
|
fixed_t dx, dy, dz, dist;
|
|
|
|
if (thing == bombspot // ignore the bomb itself (Deton fix)
|
|
|| (bombsource && thing->type == bombsource->type)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
|
|
return true;
|
|
|
|
if (!(thing->flags & MF_SHOOTABLE))
|
|
return true;
|
|
|
|
if (thing->flags & MF_BOSS)
|
|
return true;
|
|
|
|
if (thing->flags & MF_MONITOR)
|
|
return true;
|
|
|
|
dx = abs(thing->x - bombspot->x);
|
|
dy = abs(thing->y - bombspot->y);
|
|
dz = abs(thing->z + (thing->height>>1) - bombspot->z);
|
|
|
|
dist = P_AproxDistance(P_AproxDistance(dx, dy), dz);
|
|
dist -= thing->radius;
|
|
|
|
if (dist < 0)
|
|
dist = 0;
|
|
|
|
if (dist >= bombdamage)
|
|
return true; // out of range
|
|
|
|
if (thing->floorz > bombspot->z && bombspot->ceilingz < thing->z)
|
|
return true;
|
|
|
|
if (thing->ceilingz < bombspot->z && bombspot->floorz > thing->z)
|
|
return true;
|
|
|
|
if (P_CheckSight(thing, bombspot))
|
|
{ // must be in direct path
|
|
P_DamageMobj(thing, bombspot, bombsource, 1); // Tails 01-11-2001
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// P_RadiusAttack
|
|
// Source is the creature that caused the explosion at spot.
|
|
//
|
|
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist)
|
|
{
|
|
INT32 x, y;
|
|
INT32 xl, xh, yl, yh;
|
|
fixed_t dist;
|
|
|
|
dist = FixedMul(damagedist, spot->scale) + MAXRADIUS;
|
|
yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xl = (unsigned)(spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
|
|
|
|
bombspot = spot;
|
|
bombsource = source;
|
|
bombdamage = FixedMul(damagedist, spot->scale);
|
|
|
|
for (y = yl; y <= yh; y++)
|
|
for (x = xl; x <= xh; x++)
|
|
P_BlockThingsIterator(x, y, PIT_RadiusAttack);
|
|
}
|
|
|
|
//
|
|
// SECTOR HEIGHT CHANGING
|
|
// After modifying a sectors floor or ceiling height,
|
|
// call this routine to adjust the positions
|
|
// of all things that touch the sector.
|
|
//
|
|
// If anything doesn't fit anymore, true will be returned.
|
|
// If crunch is true, they will take damage
|
|
// as they are being crushed.
|
|
// If Crunch is false, you should set the sector height back
|
|
// the way it was and call P_CheckSector (? was P_ChangeSector - Graue) again
|
|
// to undo the changes.
|
|
//
|
|
static boolean crushchange;
|
|
static boolean nofit;
|
|
|
|
//
|
|
// PIT_ChangeSector
|
|
//
|
|
static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
|
|
{
|
|
if (P_ThingHeightClip(thing))
|
|
{
|
|
// keep checking
|
|
return true;
|
|
}
|
|
|
|
if (!(thing->flags & (MF_SHOOTABLE|MF_PUSHABLE))
|
|
|| thing->flags & MF_NOCLIPHEIGHT)
|
|
{
|
|
// assume it is bloody gibs or something
|
|
return true;
|
|
}
|
|
|
|
// Crush the thing if necessary, and if it's a crumbling FOF that did it,
|
|
// reward the player who made it crumble!
|
|
if (thing->z + thing->height > thing->ceilingz && thing->z <= thing->ceilingz)
|
|
{
|
|
if (realcrush && thing->subsector->sector->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
fixed_t delta1, delta2;
|
|
INT32 thingtop = thing->z + thing->height;
|
|
|
|
for (rover = thing->subsector->sector->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(((rover->flags & FF_BLOCKPLAYER) && thing->player)
|
|
|| ((rover->flags & FF_BLOCKOTHERS) && !thing->player)) || !(rover->flags & FF_EXISTS))
|
|
continue;
|
|
|
|
delta1 = thing->z - (*rover->bottomheight + *rover->topheight)/2;
|
|
delta2 = thingtop - (*rover->bottomheight + *rover->topheight)/2;
|
|
if (*rover->bottomheight <= thing->ceilingz && abs(delta1) >= abs(delta2))
|
|
{
|
|
thinker_t *think;
|
|
elevator_t *crumbler;
|
|
|
|
for (think = thinkercap.next; think != &thinkercap; think = think->next)
|
|
{
|
|
if (think->function.acp1 != (actionf_p1)T_StartCrumble)
|
|
continue;
|
|
|
|
crumbler = (elevator_t *)think;
|
|
|
|
if (crumbler->player && crumbler->player->mo
|
|
&& crumbler->player->mo != thing
|
|
&& crumbler->actionsector == thing->subsector->sector
|
|
&& crumbler->sector == rover->master->frontsector
|
|
&& (crumbler->type == elevateBounce
|
|
|| crumbler->type == elevateContinuous))
|
|
{
|
|
if (netgame && thing->player && thing->player->spectator)
|
|
P_DamageMobj(thing, NULL, NULL, 42000); // Respawn crushed spectators
|
|
else
|
|
P_DamageMobj(thing, crumbler->player->mo, crumbler->player->mo, 10000);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thing->flags & MF_PUSHABLE)
|
|
{
|
|
nofit = true;
|
|
return false;
|
|
}
|
|
|
|
if (realcrush)
|
|
{
|
|
// Instant-death, but no one to blame
|
|
if (netgame && thing->player && thing->player->spectator)
|
|
P_DamageMobj(thing, NULL, NULL, 42000); // Respawn crushed spectators
|
|
else
|
|
{
|
|
// So give a generic crush death message
|
|
mobj_t *killer = P_SpawnMobj(thing->x, thing->y, thing->z, MT_NULL);
|
|
killer->threshold = 44; // Special flag for crushing
|
|
P_DamageMobj(thing, killer, killer, 10000);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (realcrush && crushchange)
|
|
P_DamageMobj(thing, NULL, NULL, 1);
|
|
|
|
// keep checking (crush other things)
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// P_CheckSector
|
|
//
|
|
boolean P_CheckSector(sector_t *sector, boolean crunch)
|
|
{
|
|
msecnode_t *n;
|
|
|
|
nofit = false;
|
|
crushchange = crunch;
|
|
|
|
// killough 4/4/98: scan list front-to-back until empty or exhausted,
|
|
// restarting from beginning after each thing is processed. Avoids
|
|
// crashes, and is sure to examine all things in the sector, and only
|
|
// the things which are in the sector, until a steady-state is reached.
|
|
// Things can arbitrarily be inserted and removed and it won't mess up.
|
|
//
|
|
// killough 4/7/98: simplified to avoid using complicated counter
|
|
|
|
|
|
// First, let's see if anything will keep it from crushing.
|
|
if (sector->numattached)
|
|
{
|
|
size_t i;
|
|
sector_t *sec;
|
|
for (i = 0; i < sector->numattached; i++)
|
|
{
|
|
sec = §ors[sector->attached[i]];
|
|
for (n = sec->touching_thinglist; n; n = n->m_snext)
|
|
n->visited = false;
|
|
|
|
sec->moved = true;
|
|
|
|
P_RecalcPrecipInSector(sec);
|
|
|
|
if (!sector->attachedsolid[i])
|
|
continue;
|
|
|
|
do
|
|
{
|
|
for (n = sec->touching_thinglist; n; n = n->m_snext)
|
|
if (!n->visited)
|
|
{
|
|
n->visited = true;
|
|
if (!(n->m_thing->flags & MF_NOBLOCKMAP))
|
|
{
|
|
if (!PIT_ChangeSector(n->m_thing, false))
|
|
{
|
|
nofit = true;
|
|
return nofit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} while (n);
|
|
}
|
|
}
|
|
|
|
// Mark all things invalid
|
|
sector->moved = true;
|
|
|
|
for (n = sector->touching_thinglist; n; n = n->m_snext)
|
|
n->visited = false;
|
|
|
|
do
|
|
{
|
|
for (n = sector->touching_thinglist; n; n = n->m_snext) // go through list
|
|
if (!n->visited) // unprocessed thing found
|
|
{
|
|
n->visited = true; // mark thing as processed
|
|
if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
|
|
{
|
|
if (!PIT_ChangeSector(n->m_thing, false)) // process it
|
|
{
|
|
nofit = true;
|
|
return nofit;
|
|
}
|
|
}
|
|
break; // exit and start over
|
|
}
|
|
} while (n); // repeat from scratch until all things left are marked valid
|
|
|
|
// Nothing blocked us, so lets crush for real!
|
|
if (sector->numattached)
|
|
{
|
|
size_t i;
|
|
sector_t *sec;
|
|
for (i = 0; i < sector->numattached; i++)
|
|
{
|
|
sec = §ors[sector->attached[i]];
|
|
for (n = sec->touching_thinglist; n; n = n->m_snext)
|
|
n->visited = false;
|
|
|
|
sec->moved = true;
|
|
|
|
P_RecalcPrecipInSector(sec);
|
|
|
|
if (!sector->attachedsolid[i])
|
|
continue;
|
|
|
|
do
|
|
{
|
|
for (n = sec->touching_thinglist; n; n = n->m_snext)
|
|
if (!n->visited)
|
|
{
|
|
n->visited = true;
|
|
if (!(n->m_thing->flags & MF_NOBLOCKMAP))
|
|
{
|
|
PIT_ChangeSector(n->m_thing, true);
|
|
return nofit;
|
|
}
|
|
break;
|
|
}
|
|
} while (n);
|
|
}
|
|
}
|
|
|
|
// Mark all things invalid
|
|
sector->moved = true;
|
|
|
|
for (n = sector->touching_thinglist; n; n = n->m_snext)
|
|
n->visited = false;
|
|
|
|
do
|
|
{
|
|
for (n = sector->touching_thinglist; n; n = n->m_snext) // go through list
|
|
if (!n->visited) // unprocessed thing found
|
|
{
|
|
n->visited = true; // mark thing as processed
|
|
if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
|
|
{
|
|
PIT_ChangeSector(n->m_thing, true); // process it
|
|
return nofit;
|
|
}
|
|
break; // exit and start over
|
|
}
|
|
} while (n); // repeat from scratch until all things left are marked valid
|
|
|
|
return nofit;
|
|
}
|
|
|
|
/*
|
|
SoM: 3/15/2000
|
|
Lots of new Boom functions that work faster and add functionality.
|
|
*/
|
|
|
|
static msecnode_t *headsecnode = NULL;
|
|
static mprecipsecnode_t *headprecipsecnode = NULL;
|
|
|
|
void P_Initsecnode(void)
|
|
{
|
|
headsecnode = NULL;
|
|
headprecipsecnode = NULL;
|
|
}
|
|
|
|
// P_GetSecnode() retrieves a node from the freelist. The calling routine
|
|
// should make sure it sets all fields properly.
|
|
|
|
static msecnode_t *P_GetSecnode(void)
|
|
{
|
|
msecnode_t *node;
|
|
|
|
if (headsecnode)
|
|
{
|
|
node = headsecnode;
|
|
headsecnode = headsecnode->m_snext;
|
|
}
|
|
else
|
|
node = Z_Calloc(sizeof (*node), PU_LEVEL, NULL);
|
|
return node;
|
|
}
|
|
|
|
static mprecipsecnode_t *P_GetPrecipSecnode(void)
|
|
{
|
|
mprecipsecnode_t *node;
|
|
|
|
if (headprecipsecnode)
|
|
{
|
|
node = headprecipsecnode;
|
|
headprecipsecnode = headprecipsecnode->m_snext;
|
|
}
|
|
else
|
|
node = Z_Calloc(sizeof (*node), PU_LEVEL, NULL);
|
|
return node;
|
|
}
|
|
|
|
// P_PutSecnode() returns a node to the freelist.
|
|
|
|
static inline void P_PutSecnode(msecnode_t *node)
|
|
{
|
|
node->m_snext = headsecnode;
|
|
headsecnode = node;
|
|
}
|
|
|
|
// Tails 08-25-2002
|
|
static inline void P_PutPrecipSecnode(mprecipsecnode_t *node)
|
|
{
|
|
node->m_snext = headprecipsecnode;
|
|
headprecipsecnode = node;
|
|
}
|
|
|
|
// P_AddSecnode() searches the current list to see if this sector is
|
|
// already there. If not, it adds a sector node at the head of the list of
|
|
// sectors this object appears in. This is called when creating a list of
|
|
// nodes that will get linked in later. Returns a pointer to the new node.
|
|
|
|
static msecnode_t *P_AddSecnode(sector_t *s, mobj_t *thing, msecnode_t *nextnode)
|
|
{
|
|
msecnode_t *node;
|
|
|
|
node = nextnode;
|
|
while (node)
|
|
{
|
|
if (node->m_sector == s) // Already have a node for this sector?
|
|
{
|
|
node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
|
|
return nextnode;
|
|
}
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
// Couldn't find an existing node for this sector. Add one at the head
|
|
// of the list.
|
|
|
|
node = P_GetSecnode();
|
|
|
|
// mark new nodes unvisited.
|
|
node->visited = 0;
|
|
|
|
node->m_sector = s; // sector
|
|
node->m_thing = thing; // mobj
|
|
node->m_tprev = NULL; // prev node on Thing thread
|
|
node->m_tnext = nextnode; // next node on Thing thread
|
|
if (nextnode)
|
|
nextnode->m_tprev = node; // set back link on Thing
|
|
|
|
// Add new node at head of sector thread starting at s->touching_thinglist
|
|
|
|
node->m_sprev = NULL; // prev node on sector thread
|
|
node->m_snext = s->touching_thinglist; // next node on sector thread
|
|
if (s->touching_thinglist)
|
|
node->m_snext->m_sprev = node;
|
|
s->touching_thinglist = node;
|
|
return node;
|
|
}
|
|
|
|
// More crazy crap Tails 08-25-2002
|
|
static mprecipsecnode_t *P_AddPrecipSecnode(sector_t *s, precipmobj_t *thing, mprecipsecnode_t *nextnode)
|
|
{
|
|
mprecipsecnode_t *node;
|
|
|
|
node = nextnode;
|
|
while (node)
|
|
{
|
|
if (node->m_sector == s) // Already have a node for this sector?
|
|
{
|
|
node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
|
|
return nextnode;
|
|
}
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
// Couldn't find an existing node for this sector. Add one at the head
|
|
// of the list.
|
|
|
|
node = P_GetPrecipSecnode();
|
|
|
|
// mark new nodes unvisited.
|
|
node->visited = 0;
|
|
|
|
node->m_sector = s; // sector
|
|
node->m_thing = thing; // mobj
|
|
node->m_tprev = NULL; // prev node on Thing thread
|
|
node->m_tnext = nextnode; // next node on Thing thread
|
|
if (nextnode)
|
|
nextnode->m_tprev = node; // set back link on Thing
|
|
|
|
// Add new node at head of sector thread starting at s->touching_thinglist
|
|
|
|
node->m_sprev = NULL; // prev node on sector thread
|
|
node->m_snext = s->touching_preciplist; // next node on sector thread
|
|
if (s->touching_preciplist)
|
|
node->m_snext->m_sprev = node;
|
|
s->touching_preciplist = node;
|
|
return node;
|
|
}
|
|
|
|
// P_DelSecnode() deletes a sector node from the list of
|
|
// sectors this object appears in. Returns a pointer to the next node
|
|
// on the linked list, or NULL.
|
|
|
|
static msecnode_t *P_DelSecnode(msecnode_t *node)
|
|
{
|
|
msecnode_t *tp; // prev node on thing thread
|
|
msecnode_t *tn; // next node on thing thread
|
|
msecnode_t *sp; // prev node on sector thread
|
|
msecnode_t *sn; // next node on sector thread
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
// Unlink from the Thing thread. The Thing thread begins at
|
|
// sector_list and not from mobj_t->touching_sectorlist.
|
|
|
|
tp = node->m_tprev;
|
|
tn = node->m_tnext;
|
|
if (tp)
|
|
tp->m_tnext = tn;
|
|
if (tn)
|
|
tn->m_tprev = tp;
|
|
|
|
// Unlink from the sector thread. This thread begins at
|
|
// sector_t->touching_thinglist.
|
|
|
|
sp = node->m_sprev;
|
|
sn = node->m_snext;
|
|
if (sp)
|
|
sp->m_snext = sn;
|
|
else
|
|
node->m_sector->touching_thinglist = sn;
|
|
if (sn)
|
|
sn->m_sprev = sp;
|
|
|
|
// Return this node to the freelist
|
|
|
|
P_PutSecnode(node);
|
|
return tn;
|
|
}
|
|
|
|
// Tails 08-25-2002
|
|
static mprecipsecnode_t *P_DelPrecipSecnode(mprecipsecnode_t *node)
|
|
{
|
|
mprecipsecnode_t *tp; // prev node on thing thread
|
|
mprecipsecnode_t *tn; // next node on thing thread
|
|
mprecipsecnode_t *sp; // prev node on sector thread
|
|
mprecipsecnode_t *sn; // next node on sector thread
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
// Unlink from the Thing thread. The Thing thread begins at
|
|
// sector_list and not from mobj_t->touching_sectorlist.
|
|
|
|
tp = node->m_tprev;
|
|
tn = node->m_tnext;
|
|
if (tp)
|
|
tp->m_tnext = tn;
|
|
if (tn)
|
|
tn->m_tprev = tp;
|
|
|
|
// Unlink from the sector thread. This thread begins at
|
|
// sector_t->touching_thinglist.
|
|
|
|
sp = node->m_sprev;
|
|
sn = node->m_snext;
|
|
if (sp)
|
|
sp->m_snext = sn;
|
|
else
|
|
node->m_sector->touching_preciplist = sn;
|
|
if (sn)
|
|
sn->m_sprev = sp;
|
|
|
|
// Return this node to the freelist
|
|
|
|
P_PutPrecipSecnode(node);
|
|
return tn;
|
|
}
|
|
|
|
// Delete an entire sector list
|
|
void P_DelSeclist(msecnode_t *node)
|
|
{
|
|
while (node)
|
|
node = P_DelSecnode(node);
|
|
}
|
|
|
|
// Tails 08-25-2002
|
|
void P_DelPrecipSeclist(mprecipsecnode_t *node)
|
|
{
|
|
while (node)
|
|
node = P_DelPrecipSecnode(node);
|
|
}
|
|
|
|
// PIT_GetSectors
|
|
// Locates all the sectors the object is in by looking at the lines that
|
|
// cross through it. You have already decided that the object is allowed
|
|
// at this location, so don't bother with checking impassable or
|
|
// blocking lines.
|
|
|
|
static inline boolean PIT_GetSectors(line_t *ld)
|
|
{
|
|
if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
|
|
tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
|
|
tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
|
|
tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
|
|
return true;
|
|
|
|
if (P_BoxOnLineSide(tmbbox, ld) != -1)
|
|
return true;
|
|
|
|
// This line crosses through the object.
|
|
|
|
// Collect the sector(s) from the line and add to the
|
|
// sector_list you're examining. If the Thing ends up being
|
|
// allowed to move to this position, then the sector_list
|
|
// will be attached to the Thing's mobj_t at touching_sectorlist.
|
|
|
|
sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list);
|
|
|
|
// Don't assume all lines are 2-sided, since some Things
|
|
// like MT_TFOG are allowed regardless of whether their radius takes
|
|
// them beyond an impassable linedef.
|
|
|
|
// Use sidedefs instead of 2s flag to determine two-sidedness.
|
|
if (ld->backsector)
|
|
sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Tails 08-25-2002
|
|
static inline boolean PIT_GetPrecipSectors(line_t *ld)
|
|
{
|
|
if (preciptmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
|
|
preciptmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
|
|
preciptmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
|
|
preciptmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
|
|
return true;
|
|
|
|
if (P_BoxOnLineSide(preciptmbbox, ld) != -1)
|
|
return true;
|
|
|
|
// This line crosses through the object.
|
|
|
|
// Collect the sector(s) from the line and add to the
|
|
// sector_list you're examining. If the Thing ends up being
|
|
// allowed to move to this position, then the sector_list
|
|
// will be attached to the Thing's mobj_t at touching_sectorlist.
|
|
|
|
precipsector_list = P_AddPrecipSecnode(ld->frontsector, tmprecipthing, precipsector_list);
|
|
|
|
// Don't assume all lines are 2-sided, since some Things
|
|
// like MT_TFOG are allowed regardless of whether their radius takes
|
|
// them beyond an impassable linedef.
|
|
|
|
// Use sidedefs instead of 2s flag to determine two-sidedness.
|
|
if (ld->backsector)
|
|
precipsector_list = P_AddPrecipSecnode(ld->backsector, tmprecipthing, precipsector_list);
|
|
|
|
return true;
|
|
}
|
|
|
|
// P_CreateSecNodeList alters/creates the sector_list that shows what sectors
|
|
// the object resides in.
|
|
|
|
void P_CreateSecNodeList(mobj_t *thing, fixed_t x, fixed_t y)
|
|
{
|
|
INT32 xl, xh, yl, yh, bx, by;
|
|
msecnode_t *node = sector_list;
|
|
mobj_t *saved_tmthing = tmthing; /* cph - see comment at func end */
|
|
fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */
|
|
|
|
// First, clear out the existing m_thing fields. As each node is
|
|
// added or verified as needed, m_thing will be set properly. When
|
|
// finished, delete all nodes where m_thing is still NULL. These
|
|
// represent the sectors the Thing has vacated.
|
|
|
|
while (node)
|
|
{
|
|
node->m_thing = NULL;
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
P_SetTarget(&tmthing, thing);
|
|
tmflags = thing->flags;
|
|
|
|
tmx = x;
|
|
tmy = y;
|
|
|
|
tmbbox[BOXTOP] = y + tmthing->radius;
|
|
tmbbox[BOXBOTTOM] = y - tmthing->radius;
|
|
tmbbox[BOXRIGHT] = x + tmthing->radius;
|
|
tmbbox[BOXLEFT] = x - tmthing->radius;
|
|
|
|
validcount++; // used to make sure we only process a line once
|
|
|
|
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
|
|
for (bx = xl; bx <= xh; bx++)
|
|
for (by = yl; by <= yh; by++)
|
|
P_BlockLinesIterator(bx, by, PIT_GetSectors);
|
|
|
|
// Add the sector of the (x, y) point to sector_list.
|
|
sector_list = P_AddSecnode(thing->subsector->sector, thing, sector_list);
|
|
|
|
// Now delete any nodes that won't be used. These are the ones where
|
|
// m_thing is still NULL.
|
|
node = sector_list;
|
|
while (node)
|
|
{
|
|
if (!node->m_thing)
|
|
{
|
|
if (node == sector_list)
|
|
sector_list = node->m_tnext;
|
|
node = P_DelSecnode(node);
|
|
}
|
|
else
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
/* cph -
|
|
* This is the strife we get into for using global variables. tmthing
|
|
* is being used by several different functions calling
|
|
* P_BlockThingIterator, including functions that can be called *from*
|
|
* P_BlockThingIterator. Using a global tmthing is not reentrant.
|
|
* OTOH for Boom/MBF demos we have to preserve the buggy behavior.
|
|
* Fun. We restore its previous value unless we're in a Boom/MBF demo.
|
|
*/
|
|
P_SetTarget(&tmthing, saved_tmthing);
|
|
|
|
/* And, duh, the same for tmx/y - cph 2002/09/22
|
|
* And for tmbbox - cph 2003/08/10 */
|
|
tmx = saved_tmx, tmy = saved_tmy;
|
|
|
|
if (tmthing)
|
|
{
|
|
tmbbox[BOXTOP] = tmy + tmthing->radius;
|
|
tmbbox[BOXBOTTOM] = tmy - tmthing->radius;
|
|
tmbbox[BOXRIGHT] = tmx + tmthing->radius;
|
|
tmbbox[BOXLEFT] = tmx - tmthing->radius;
|
|
}
|
|
}
|
|
|
|
// More crazy crap Tails 08-25-2002
|
|
void P_CreatePrecipSecNodeList(precipmobj_t *thing,fixed_t x,fixed_t y)
|
|
{
|
|
INT32 xl, xh, yl, yh, bx, by;
|
|
mprecipsecnode_t *node = precipsector_list;
|
|
precipmobj_t *saved_tmthing = tmprecipthing; /* cph - see comment at func end */
|
|
|
|
// First, clear out the existing m_thing fields. As each node is
|
|
// added or verified as needed, m_thing will be set properly. When
|
|
// finished, delete all nodes where m_thing is still NULL. These
|
|
// represent the sectors the Thing has vacated.
|
|
|
|
while (node)
|
|
{
|
|
node->m_thing = NULL;
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
tmprecipthing = thing;
|
|
|
|
preciptmbbox[BOXTOP] = y + 2*FRACUNIT;
|
|
preciptmbbox[BOXBOTTOM] = y - 2*FRACUNIT;
|
|
preciptmbbox[BOXRIGHT] = x + 2*FRACUNIT;
|
|
preciptmbbox[BOXLEFT] = x - 2*FRACUNIT;
|
|
|
|
validcount++; // used to make sure we only process a line once
|
|
|
|
xl = (unsigned)(preciptmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
xh = (unsigned)(preciptmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
|
|
yl = (unsigned)(preciptmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
yh = (unsigned)(preciptmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
|
|
|
|
for (bx = xl; bx <= xh; bx++)
|
|
for (by = yl; by <= yh; by++)
|
|
P_BlockLinesIterator(bx, by, PIT_GetPrecipSectors);
|
|
|
|
// Add the sector of the (x, y) point to sector_list.
|
|
precipsector_list = P_AddPrecipSecnode(thing->subsector->sector, thing, precipsector_list);
|
|
|
|
// Now delete any nodes that won't be used. These are the ones where
|
|
// m_thing is still NULL.
|
|
node = precipsector_list;
|
|
while (node)
|
|
{
|
|
if (!node->m_thing)
|
|
{
|
|
if (node == precipsector_list)
|
|
precipsector_list = node->m_tnext;
|
|
node = P_DelPrecipSecnode(node);
|
|
}
|
|
else
|
|
node = node->m_tnext;
|
|
}
|
|
|
|
/* cph -
|
|
* This is the strife we get into for using global variables. tmthing
|
|
* is being used by several different functions calling
|
|
* P_BlockThingIterator, including functions that can be called *from*
|
|
* P_BlockThingIterator. Using a global tmthing is not reentrant.
|
|
* OTOH for Boom/MBF demos we have to preserve the buggy behavior.
|
|
* Fun. We restore its previous value unless we're in a Boom/MBF demo.
|
|
*/
|
|
tmprecipthing = saved_tmthing;
|
|
}
|
|
|
|
/* cphipps 2004/08/30 -
|
|
* Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */
|
|
void P_MapStart(void)
|
|
{
|
|
if (tmthing)
|
|
I_Error("P_MapStart: tmthing set!");
|
|
}
|
|
|
|
void P_MapEnd(void)
|
|
{
|
|
P_SetTarget(&tmthing, NULL);
|
|
}
|
|
|
|
// P_FloorzAtPos
|
|
// Returns the floorz of the XYZ position
|
|
// Tails 05-26-2003
|
|
fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
|
|
{
|
|
sector_t *sec = R_PointInSubsector(x, y)->sector;
|
|
fixed_t floorz = sec->floorheight;
|
|
|
|
// Intercept the stupid 'fall through 3dfloors' bug Tails 03-17-2002
|
|
if (sec->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
fixed_t delta1, delta2, thingtop = z + height;
|
|
|
|
for (rover = sec->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (!(rover->flags & FF_EXISTS))
|
|
continue;
|
|
|
|
if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE)))
|
|
continue;
|
|
|
|
if (rover->flags & FF_QUICKSAND)
|
|
{
|
|
if (z < *rover->topheight && *rover->bottomheight < thingtop)
|
|
{
|
|
if (floorz < z)
|
|
floorz = z;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
delta1 = z - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2));
|
|
delta2 = thingtop - (*rover->bottomheight + ((*rover->topheight - *rover->bottomheight)/2));
|
|
if (*rover->topheight > floorz && abs(delta1) < abs(delta2))
|
|
floorz = *rover->topheight;
|
|
}
|
|
}
|
|
|
|
return floorz;
|
|
}
|