2012-11-26 18:58:24 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
2022-09-05 20:25:33 +00:00
|
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Precompiled.h"
|
|
|
|
#include "globaldata.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "m_bbox.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
|
|
|
|
#include "s_sound.h"
|
|
|
|
|
|
|
|
// State.
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "r_state.h"
|
|
|
|
// Data.
|
|
|
|
#include "sounds.h"
|
|
|
|
|
|
|
|
#include "Main.h"
|
|
|
|
|
|
|
|
|
|
|
|
// If "floatok" true, move would be ok
|
|
|
|
// if within "tmfloorz - tmceilingz".
|
|
|
|
|
|
|
|
|
|
|
|
// keep track of the line that lowers the ceiling,
|
|
|
|
// so missiles don't explode against sky hack walls
|
|
|
|
|
|
|
|
// keep track of special ::g->lines as they are hit,
|
|
|
|
// but don't process them until the move is proven valid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// TELEPORT MOVE
|
2022-09-05 20:25:33 +00:00
|
|
|
//
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// PIT_StompThing
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PIT_StompThing( mobj_t* thing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t blockdist;
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
blockdist = thing->radius + ::g->tmthing->radius;
|
|
|
|
|
|
|
|
if( abs( thing->x - ::g->tmx ) >= blockdist
|
|
|
|
|| abs( thing->y - ::g->tmy ) >= blockdist )
|
|
|
|
{
|
|
|
|
// didn't hit it
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't clip against self
|
|
|
|
if( thing == ::g->tmthing )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// monsters don't stomp things except on boss level
|
|
|
|
if( !::g->tmthing->player && ::g->gamemap != 30 )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
P_DamageMobj( thing, ::g->tmthing, ::g->tmthing, 10000 );
|
|
|
|
|
2012-11-26 18:58:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_TeleportMove
|
|
|
|
//
|
|
|
|
qboolean
|
|
|
|
P_TeleportMove
|
|
|
|
( mobj_t* thing,
|
|
|
|
fixed_t x,
|
|
|
|
fixed_t y )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int xl;
|
|
|
|
int xh;
|
|
|
|
int yl;
|
|
|
|
int yh;
|
|
|
|
int bx;
|
|
|
|
int by;
|
|
|
|
|
|
|
|
subsector_t* newsubsec;
|
|
|
|
|
|
|
|
// kill anything occupying the position
|
|
|
|
::g->tmthing = thing;
|
|
|
|
::g->tmflags = thing->flags;
|
|
|
|
|
|
|
|
::g->tmx = x;
|
|
|
|
::g->tmy = y;
|
|
|
|
|
|
|
|
::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius;
|
|
|
|
|
|
|
|
newsubsec = R_PointInSubsector( x, y );
|
|
|
|
::g->ceilingline = NULL;
|
|
|
|
|
|
|
|
// The base floor/ceiling is from the subsector
|
|
|
|
// that contains the point.
|
|
|
|
// Any contacted ::g->lines the step closer together
|
|
|
|
// will adjust them.
|
|
|
|
::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight;
|
|
|
|
::g->tmceilingz = newsubsec->sector->ceilingheight;
|
|
|
|
|
|
|
|
::g->validcount++;
|
|
|
|
::g->numspechit = 0;
|
|
|
|
|
|
|
|
// stomp on any things contacted
|
|
|
|
xl = ( ::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
xh = ( ::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
yl = ( ::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
yh = ( ::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
|
|
|
|
for( bx = xl ; bx <= xh ; bx++ )
|
|
|
|
for( by = yl ; by <= yh ; by++ )
|
|
|
|
if( !P_BlockThingsIterator( bx, by, PIT_StompThing ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// the move is ok,
|
|
|
|
// so link the thing into its new position
|
|
|
|
P_UnsetThingPosition( thing );
|
|
|
|
|
|
|
|
thing->floorz = ::g->tmfloorz;
|
|
|
|
thing->ceilingz = ::g->tmceilingz;
|
|
|
|
thing->x = x;
|
|
|
|
thing->y = y;
|
|
|
|
|
|
|
|
P_SetThingPosition( thing );
|
|
|
|
|
|
|
|
return true;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// MOVEMENT ITERATOR FUNCTIONS
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PIT_CheckLine
|
|
|
|
// Adjusts ::g->tmfloorz and ::g->tmceilingz as ::g->lines are contacted
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PIT_CheckLine( line_t* ld )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
if( ::g->tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
|
|
|
|
|| ::g->tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
|
|
|
|
|| ::g->tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
|
|
|
|
|| ::g->tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( P_BoxOnLineSide( ::g->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 ::g->lines that are only 8 pixels apart
|
|
|
|
// could be crossed in either order.
|
|
|
|
|
|
|
|
if( !ld->backsector )
|
|
|
|
{
|
|
|
|
return false; // one sided line
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( ::g->tmthing->flags & MF_MISSILE ) )
|
|
|
|
{
|
|
|
|
if( ld->flags & ML_BLOCKING )
|
|
|
|
{
|
|
|
|
return false; // explicitly blocking everything
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !::g->tmthing->player && ld->flags & ML_BLOCKMONSTERS )
|
|
|
|
{
|
|
|
|
return false; // block monsters only
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set ::g->openrange, ::g->opentop, ::g->openbottom
|
|
|
|
P_LineOpening( ld );
|
|
|
|
|
|
|
|
// adjust floor / ceiling heights
|
|
|
|
if( ::g->opentop < ::g->tmceilingz )
|
|
|
|
{
|
|
|
|
::g->tmceilingz = ::g->opentop;
|
|
|
|
::g->ceilingline = ld;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->openbottom > ::g->tmfloorz )
|
|
|
|
{
|
|
|
|
::g->tmfloorz = ::g->openbottom;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->lowfloor < ::g->tmdropoffz )
|
|
|
|
{
|
|
|
|
::g->tmdropoffz = ::g->lowfloor;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if contacted a special line, add it to the list
|
|
|
|
if( ld->special && ::g->numspechit < MAXSPECIALCROSS )
|
|
|
|
{
|
|
|
|
::g->spechit[::g->numspechit] = ld;
|
|
|
|
::g->numspechit++;
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// PIT_CheckThing
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PIT_CheckThing( mobj_t* thing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t blockdist;
|
|
|
|
qboolean solid;
|
|
|
|
int damage;
|
|
|
|
|
|
|
|
if( !( thing->flags & ( MF_SOLID | MF_SPECIAL | MF_SHOOTABLE ) ) )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
return true;
|
2022-09-05 20:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
blockdist = thing->radius + ::g->tmthing->radius;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( abs( thing->x - ::g->tmx ) >= blockdist
|
|
|
|
|| abs( thing->y - ::g->tmy ) >= blockdist )
|
|
|
|
{
|
|
|
|
// didn't hit it
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't clip against self
|
|
|
|
if( thing == ::g->tmthing )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for skulls slamming into things
|
|
|
|
if( ::g->tmthing->flags & MF_SKULLFLY )
|
|
|
|
{
|
|
|
|
damage = ( ( P_Random() % 8 ) + 1 )*::g->tmthing->info->damage;
|
|
|
|
|
|
|
|
P_DamageMobj( thing, ::g->tmthing, ::g->tmthing, damage );
|
|
|
|
|
|
|
|
::g->tmthing->flags &= ~MF_SKULLFLY;
|
|
|
|
::g->tmthing->momx = ::g->tmthing->momy = ::g->tmthing->momz = 0;
|
|
|
|
|
|
|
|
P_SetMobjState( ::g->tmthing, ( statenum_t )::g->tmthing->info->spawnstate );
|
|
|
|
|
|
|
|
return false; // stop moving
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// missiles can hit other things
|
|
|
|
if( ::g->tmthing->flags & MF_MISSILE )
|
|
|
|
{
|
|
|
|
// see if it went over / under
|
|
|
|
if( ::g->tmthing->z > thing->z + thing->height )
|
|
|
|
{
|
|
|
|
return true; // overhead
|
|
|
|
}
|
|
|
|
if( ::g->tmthing->z +::g->tmthing->height < thing->z )
|
|
|
|
{
|
|
|
|
return true; // underneath
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->tmthing->target && (
|
|
|
|
::g->tmthing->target->type == thing->type ||
|
|
|
|
( ::g->tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER ) ||
|
|
|
|
( ::g->tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT ) ) )
|
|
|
|
{
|
|
|
|
// Don't hit same species as originator.
|
|
|
|
if( thing == ::g->tmthing->target )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( thing->type != MT_PLAYER )
|
|
|
|
{
|
|
|
|
// Explode, but do no damage.
|
|
|
|
// Let ::g->players missile other ::g->players.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
// didn't do any damage
|
|
|
|
return !( thing->flags & MF_SOLID );
|
|
|
|
}
|
|
|
|
|
|
|
|
// damage / explode
|
|
|
|
damage = ( ( P_Random() % 8 ) + 1 )*::g->tmthing->info->damage;
|
|
|
|
P_DamageMobj( thing, ::g->tmthing, ::g->tmthing->target, damage );
|
|
|
|
|
|
|
|
// don't traverse any more
|
2012-11-26 18:58:24 +00:00
|
|
|
return false;
|
2022-09-05 20:25:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// check for special pickup
|
|
|
|
if( thing->flags & MF_SPECIAL )
|
|
|
|
{
|
|
|
|
solid = thing->flags & MF_SOLID;
|
|
|
|
if( ::g->tmflags & MF_PICKUP )
|
|
|
|
{
|
|
|
|
// can remove thing
|
|
|
|
P_TouchSpecialThing( thing, ::g->tmthing );
|
|
|
|
}
|
|
|
|
return !solid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !( thing->flags & MF_SOLID );
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// MOVEMENT CLIPPING
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_CheckPosition
|
|
|
|
// This is purely informative, nothing is modified
|
|
|
|
// (except things picked up).
|
2022-09-05 20:25:33 +00:00
|
|
|
//
|
2012-11-26 18:58:24 +00:00
|
|
|
// 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
|
|
|
|
// floorz
|
|
|
|
// ceilingz
|
|
|
|
// ::g->tmdropoffz
|
|
|
|
// the lowest point contacted
|
|
|
|
// (monsters won't move to a dropoff)
|
|
|
|
// speciallines[]
|
|
|
|
// numspeciallines
|
|
|
|
//
|
|
|
|
qboolean
|
|
|
|
P_CheckPosition
|
|
|
|
( mobj_t* thing,
|
|
|
|
fixed_t x,
|
|
|
|
fixed_t y )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int xl;
|
|
|
|
int xh;
|
|
|
|
int yl;
|
|
|
|
int yh;
|
|
|
|
int bx;
|
|
|
|
int by;
|
|
|
|
subsector_t* newsubsec;
|
|
|
|
|
|
|
|
::g->tmthing = thing;
|
|
|
|
::g->tmflags = thing->flags;
|
|
|
|
|
|
|
|
::g->tmx = x;
|
|
|
|
::g->tmy = y;
|
|
|
|
|
|
|
|
::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius;
|
|
|
|
::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius;
|
|
|
|
|
|
|
|
newsubsec = R_PointInSubsector( x, y );
|
|
|
|
::g->ceilingline = NULL;
|
|
|
|
|
|
|
|
// The base floor / ceiling is from the subsector
|
|
|
|
// that contains the point.
|
|
|
|
// Any contacted ::g->lines the step closer together
|
|
|
|
// will adjust them.
|
|
|
|
::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight;
|
|
|
|
::g->tmceilingz = newsubsec->sector->ceilingheight;
|
|
|
|
|
|
|
|
::g->validcount++;
|
|
|
|
::g->numspechit = 0;
|
|
|
|
|
|
|
|
if( ::g->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 = ( ::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
xh = ( ::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
yl = ( ::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
yh = ( ::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS ) >> MAPBLOCKSHIFT;
|
|
|
|
|
|
|
|
for( bx = xl ; bx <= xh ; bx++ )
|
|
|
|
for( by = yl ; by <= yh ; by++ )
|
|
|
|
if( !P_BlockThingsIterator( bx, by, PIT_CheckThing ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
// check ::g->lines
|
|
|
|
xl = ( ::g->tmbbox[BOXLEFT] - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
|
|
xh = ( ::g->tmbbox[BOXRIGHT] - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
|
|
yl = ( ::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
yh = ( ::g->tmbbox[BOXTOP] - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
|
|
|
|
for( bx = xl ; bx <= xh ; bx++ )
|
|
|
|
for( by = yl ; by <= yh ; by++ )
|
|
|
|
if( !P_BlockLinesIterator( bx, by, PIT_CheckLine ) )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_TryMove
|
|
|
|
// Attempt to move to a new position,
|
|
|
|
// crossing special ::g->lines unless MF_TELEPORT is set.
|
|
|
|
//
|
|
|
|
qboolean
|
|
|
|
P_TryMove
|
|
|
|
( mobj_t* thing,
|
|
|
|
fixed_t x,
|
|
|
|
fixed_t y )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t oldx;
|
|
|
|
fixed_t oldy;
|
|
|
|
int side;
|
|
|
|
int oldside;
|
|
|
|
line_t* ld;
|
|
|
|
|
|
|
|
::g->floatok = false;
|
|
|
|
if( !P_CheckPosition( thing, x, y ) )
|
|
|
|
{
|
|
|
|
return false; // solid wall or thing
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_NOCLIP ) )
|
|
|
|
{
|
|
|
|
if( ::g->tmceilingz - ::g->tmfloorz < thing->height )
|
|
|
|
{
|
|
|
|
return false; // doesn't fit
|
|
|
|
}
|
|
|
|
|
|
|
|
::g->floatok = true;
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_TELEPORT )
|
|
|
|
&&::g->tmceilingz - thing->z < thing->height )
|
|
|
|
{
|
|
|
|
return false; // mobj must lower itself to fit
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_TELEPORT )
|
|
|
|
&& ::g->tmfloorz - thing->z > 24 * FRACUNIT )
|
|
|
|
{
|
|
|
|
return false; // too big a step up
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( thing->flags & ( MF_DROPOFF | MF_FLOAT ) )
|
|
|
|
&& ::g->tmfloorz - ::g->tmdropoffz > 24 * FRACUNIT )
|
|
|
|
{
|
|
|
|
return false; // don't stand over a dropoff
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// the move is ok,
|
|
|
|
// so link the thing into its new position
|
|
|
|
P_UnsetThingPosition( thing );
|
|
|
|
|
|
|
|
oldx = thing->x;
|
|
|
|
oldy = thing->y;
|
|
|
|
thing->floorz = ::g->tmfloorz;
|
|
|
|
thing->ceilingz = ::g->tmceilingz;
|
|
|
|
thing->x = x;
|
|
|
|
thing->y = y;
|
|
|
|
|
|
|
|
P_SetThingPosition( thing );
|
|
|
|
|
|
|
|
// if any special ::g->lines were hit, do the effect
|
|
|
|
if( !( thing->flags & ( MF_TELEPORT | MF_NOCLIP ) ) )
|
|
|
|
{
|
|
|
|
while( ::g->numspechit-- )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
|
|
|
// see if the line was crossed
|
|
|
|
ld = ::g->spechit[::g->numspechit];
|
2022-09-05 20:25:33 +00:00
|
|
|
side = P_PointOnLineSide( thing->x, thing->y, ld );
|
|
|
|
oldside = P_PointOnLineSide( oldx, oldy, ld );
|
|
|
|
if( side != oldside )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
if( ld->special )
|
|
|
|
{
|
|
|
|
P_CrossSpecialLine( ld -::g->lines, oldside, thing );
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-05 20:25:33 +00:00
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
return true;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean P_ThingHeightClip( mobj_t* thing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean onfloor;
|
|
|
|
|
|
|
|
onfloor = ( thing->z == thing->floorz );
|
|
|
|
|
|
|
|
P_CheckPosition( thing, thing->x, thing->y );
|
|
|
|
// what about stranding a monster partially off an edge?
|
|
|
|
|
|
|
|
thing->floorz = ::g->tmfloorz;
|
|
|
|
thing->ceilingz = ::g->tmceilingz;
|
|
|
|
|
|
|
|
if( onfloor )
|
|
|
|
{
|
|
|
|
// walking monsters rise and fall with the floor
|
|
|
|
thing->z = thing->floorz;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// don't adjust a floating monster unless forced to
|
|
|
|
if( thing->z + thing->height > thing->ceilingz )
|
|
|
|
{
|
|
|
|
thing->z = thing->ceilingz - thing->height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( thing->ceilingz - thing->floorz < thing->height )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// SLIDE MOVE
|
|
|
|
// Allows the player to slide along any angled walls.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_HitSlideLine
|
|
|
|
// Adjusts the xmove / ymove
|
|
|
|
// so that the next move will slide along the wall.
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
void P_HitSlideLine( line_t* ld )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int side;
|
|
|
|
|
|
|
|
angle_t lineangle;
|
|
|
|
angle_t moveangle;
|
|
|
|
angle_t deltaangle;
|
|
|
|
|
|
|
|
fixed_t movelen;
|
|
|
|
fixed_t newlen;
|
|
|
|
|
|
|
|
|
|
|
|
if( ld->slopetype == ST_HORIZONTAL )
|
|
|
|
{
|
|
|
|
::g->tmymove = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ld->slopetype == ST_VERTICAL )
|
|
|
|
{
|
|
|
|
::g->tmxmove = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
side = P_PointOnLineSide( ::g->slidemo->x, ::g->slidemo->y, ld );
|
|
|
|
|
|
|
|
lineangle = R_PointToAngle2( 0, 0, ld->dx, ld->dy );
|
|
|
|
|
|
|
|
if( side == 1 )
|
|
|
|
{
|
|
|
|
lineangle += ANG180;
|
|
|
|
}
|
|
|
|
|
|
|
|
moveangle = R_PointToAngle2( 0, 0, ::g->tmxmove, ::g->tmymove );
|
|
|
|
deltaangle = moveangle - lineangle;
|
|
|
|
|
|
|
|
if( deltaangle > ANG180 )
|
|
|
|
{
|
|
|
|
deltaangle += ANG180;
|
|
|
|
}
|
|
|
|
// I_Error ("SlideLine: ang>ANG180");
|
|
|
|
|
|
|
|
lineangle >>= ANGLETOFINESHIFT;
|
|
|
|
deltaangle >>= ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
movelen = P_AproxDistance( ::g->tmxmove, ::g->tmymove );
|
|
|
|
newlen = FixedMul( movelen, finecosine[deltaangle] );
|
|
|
|
|
|
|
|
::g->tmxmove = FixedMul( newlen, finecosine[lineangle] );
|
|
|
|
::g->tmymove = FixedMul( newlen, finesine[lineangle] );
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PTR_SlideTraverse
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PTR_SlideTraverse( intercept_t* in )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
line_t* li;
|
|
|
|
|
|
|
|
if( !in->isaline )
|
|
|
|
{
|
|
|
|
I_Error( "PTR_SlideTraverse: not a line?" );
|
|
|
|
}
|
|
|
|
|
|
|
|
li = in->d.line;
|
|
|
|
|
|
|
|
if( !( li->flags & ML_TWOSIDED ) )
|
|
|
|
{
|
|
|
|
if( P_PointOnLineSide( ::g->slidemo->x, ::g->slidemo->y, li ) )
|
|
|
|
{
|
|
|
|
// don't hit the back side
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
goto isblocking;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set ::g->openrange, ::g->opentop, ::g->openbottom
|
|
|
|
P_LineOpening( li );
|
|
|
|
|
|
|
|
if( ::g->openrange < ::g->slidemo->height )
|
|
|
|
{
|
|
|
|
goto isblocking; // doesn't fit
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->opentop - ::g->slidemo->z < ::g->slidemo->height )
|
|
|
|
{
|
|
|
|
goto isblocking; // mobj is too high
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->openbottom - ::g->slidemo->z > 24 * FRACUNIT )
|
|
|
|
{
|
|
|
|
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 < ::g->bestslidefrac )
|
|
|
|
{
|
|
|
|
::g->secondslidefrac = ::g->bestslidefrac;
|
|
|
|
::g->secondslideline = ::g->bestslideline;
|
|
|
|
::g->bestslidefrac = in->frac;
|
|
|
|
::g->bestslideline = li;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false; // stop
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
void P_SlideMove( mobj_t* mo )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t leadx;
|
|
|
|
fixed_t leady;
|
|
|
|
fixed_t trailx;
|
|
|
|
fixed_t traily;
|
|
|
|
fixed_t newx;
|
|
|
|
fixed_t newy;
|
|
|
|
int hitcount;
|
|
|
|
|
|
|
|
::g->slidemo = mo;
|
|
|
|
hitcount = 0;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
if( ++hitcount == 3 )
|
|
|
|
{
|
|
|
|
goto stairstep; // don't loop forever
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ::g->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;
|
|
|
|
}
|
|
|
|
|
|
|
|
::g->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 );
|
|
|
|
|
|
|
|
// move up to the wall
|
|
|
|
if( ::g->bestslidefrac == FRACUNIT + 1 )
|
|
|
|
{
|
|
|
|
// the move most have hit the middle, so stairstep
|
|
|
|
stairstep:
|
|
|
|
if( !P_TryMove( mo, mo->x, mo->y + mo->momy ) )
|
|
|
|
{
|
|
|
|
P_TryMove( mo, mo->x + mo->momx, mo->y );
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fudge a bit to make sure it doesn't hit
|
|
|
|
::g->bestslidefrac -= 0x800;
|
|
|
|
if( ::g->bestslidefrac > 0 )
|
|
|
|
{
|
|
|
|
newx = FixedMul( mo->momx, ::g->bestslidefrac );
|
|
|
|
newy = FixedMul( mo->momy, ::g->bestslidefrac );
|
|
|
|
|
|
|
|
if( !P_TryMove( mo, mo->x + newx, mo->y + newy ) )
|
|
|
|
{
|
|
|
|
goto stairstep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now continue along the wall.
|
|
|
|
// First calculate remainder.
|
|
|
|
::g->bestslidefrac = FRACUNIT - ( ::g->bestslidefrac + 0x800 );
|
|
|
|
|
|
|
|
if( ::g->bestslidefrac > FRACUNIT )
|
|
|
|
{
|
|
|
|
::g->bestslidefrac = FRACUNIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->bestslidefrac <= 0 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
::g->tmxmove = FixedMul( mo->momx, ::g->bestslidefrac );
|
|
|
|
::g->tmymove = FixedMul( mo->momy, ::g->bestslidefrac );
|
|
|
|
|
|
|
|
P_HitSlideLine( ::g->bestslideline ); // clip the moves
|
|
|
|
|
|
|
|
mo->momx = ::g->tmxmove;
|
|
|
|
mo->momy = ::g->tmymove;
|
|
|
|
|
|
|
|
if( !P_TryMove( mo, mo->x +::g->tmxmove, mo->y +::g->tmymove ) )
|
|
|
|
{
|
|
|
|
goto retry;
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_LineAttack
|
|
|
|
//
|
|
|
|
|
|
|
|
// Height if not aiming up or down
|
|
|
|
// ???: use slope for monsters?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// slopes to top and bottom of target
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PTR_AimTraverse
|
|
|
|
// Sets linetaget and ::g->aimslope when a target is aimed at.
|
|
|
|
//
|
|
|
|
qboolean
|
2022-09-05 20:25:33 +00:00
|
|
|
PTR_AimTraverse( intercept_t* in )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
line_t* li;
|
|
|
|
mobj_t* th;
|
|
|
|
fixed_t slope;
|
|
|
|
fixed_t thingtopslope;
|
|
|
|
fixed_t thingbottomslope;
|
|
|
|
fixed_t dist;
|
|
|
|
|
|
|
|
if( in->isaline )
|
|
|
|
{
|
|
|
|
li = in->d.line;
|
|
|
|
|
|
|
|
if( !( li->flags & ML_TWOSIDED ) )
|
|
|
|
{
|
|
|
|
return false; // stop
|
|
|
|
}
|
|
|
|
|
|
|
|
// Crosses a two sided line.
|
|
|
|
// A two sided line will restrict
|
|
|
|
// the possible target ranges.
|
|
|
|
P_LineOpening( li );
|
|
|
|
|
|
|
|
if( ::g->openbottom >= ::g->opentop )
|
|
|
|
{
|
|
|
|
return false; // stop
|
|
|
|
}
|
|
|
|
|
|
|
|
dist = FixedMul( ::g->attackrange, in->frac );
|
|
|
|
|
|
|
|
if( li->frontsector->floorheight != li->backsector->floorheight )
|
|
|
|
{
|
|
|
|
slope = FixedDiv( ::g->openbottom - ::g->shootz , dist );
|
|
|
|
if( slope > ::g->bottomslope )
|
|
|
|
{
|
|
|
|
::g->bottomslope = slope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( li->frontsector->ceilingheight != li->backsector->ceilingheight )
|
|
|
|
{
|
|
|
|
slope = FixedDiv( ::g->opentop - ::g->shootz , dist );
|
|
|
|
if( slope < ::g->topslope )
|
|
|
|
{
|
|
|
|
::g->topslope = slope;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( ::g->topslope <= ::g->bottomslope )
|
|
|
|
{
|
|
|
|
return false; // stop
|
|
|
|
}
|
|
|
|
|
|
|
|
return true; // shot continues
|
|
|
|
}
|
|
|
|
|
|
|
|
// shoot a thing
|
|
|
|
th = in->d.thing;
|
|
|
|
if( th == ::g->shootthing )
|
|
|
|
{
|
|
|
|
return true; // can't shoot self
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( th->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
return true; // corpse or something
|
|
|
|
}
|
|
|
|
|
|
|
|
// check angles to see if the thing can be aimed at
|
|
|
|
dist = FixedMul( ::g->attackrange, in->frac );
|
|
|
|
thingtopslope = FixedDiv( th->z + th->height - ::g->shootz , dist );
|
|
|
|
|
|
|
|
if( thingtopslope < ::g->bottomslope )
|
|
|
|
{
|
|
|
|
return true; // shot over the thing
|
|
|
|
}
|
|
|
|
|
|
|
|
thingbottomslope = FixedDiv( th->z - ::g->shootz, dist );
|
|
|
|
|
|
|
|
if( thingbottomslope > ::g->topslope )
|
|
|
|
{
|
|
|
|
return true; // shot under the thing
|
|
|
|
}
|
|
|
|
|
|
|
|
// this thing can be hit!
|
|
|
|
if( thingtopslope > ::g->topslope )
|
|
|
|
{
|
|
|
|
thingtopslope = ::g->topslope;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( thingbottomslope < ::g->bottomslope )
|
|
|
|
{
|
|
|
|
thingbottomslope = ::g->bottomslope;
|
|
|
|
}
|
|
|
|
|
|
|
|
::g->aimslope = ( thingtopslope + thingbottomslope ) / 2;
|
|
|
|
::g->linetarget = th;
|
|
|
|
|
|
|
|
return false; // don't go any farther
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PTR_ShootTraverse
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PTR_ShootTraverse( intercept_t* in )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t x;
|
|
|
|
fixed_t y;
|
|
|
|
fixed_t z;
|
|
|
|
fixed_t frac;
|
|
|
|
|
|
|
|
line_t* li;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
mobj_t* th;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t slope;
|
|
|
|
fixed_t dist;
|
|
|
|
fixed_t thingtopslope;
|
|
|
|
fixed_t thingbottomslope;
|
|
|
|
|
|
|
|
if( in->isaline )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
li = in->d.line;
|
|
|
|
|
|
|
|
if( li->special )
|
|
|
|
{
|
|
|
|
P_ShootSpecialLine( ::g->shootthing, li );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( li->flags & ML_TWOSIDED ) )
|
|
|
|
{
|
|
|
|
goto hitline;
|
|
|
|
}
|
|
|
|
|
|
|
|
// crosses a two sided line
|
|
|
|
P_LineOpening( li );
|
|
|
|
|
|
|
|
dist = FixedMul( ::g->attackrange, in->frac );
|
|
|
|
|
|
|
|
if( li->frontsector->floorheight != li->backsector->floorheight )
|
|
|
|
{
|
|
|
|
slope = FixedDiv( ::g->openbottom - ::g->shootz , dist );
|
|
|
|
if( slope > ::g->aimslope )
|
|
|
|
{
|
|
|
|
goto hitline;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( li->frontsector->ceilingheight != li->backsector->ceilingheight )
|
|
|
|
{
|
|
|
|
slope = FixedDiv( ::g->opentop - ::g->shootz , dist );
|
|
|
|
if( slope < ::g->aimslope )
|
|
|
|
{
|
|
|
|
goto hitline;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// shot continues
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
// hit line
|
|
|
|
hitline:
|
|
|
|
// position a bit closer
|
|
|
|
frac = in->frac - FixedDiv( 4 * FRACUNIT, ::g->attackrange );
|
|
|
|
x = ::g->trace.x + FixedMul( ::g->trace.dx, frac );
|
|
|
|
y = ::g->trace.y + FixedMul( ::g->trace.dy, frac );
|
|
|
|
z = ::g->shootz + FixedMul( ::g->aimslope, FixedMul( frac, ::g->attackrange ) );
|
|
|
|
|
|
|
|
if( li->frontsector->ceilingpic == ::g->skyflatnum )
|
|
|
|
{
|
|
|
|
// don't shoot the sky!
|
|
|
|
if( z > li->frontsector->ceilingheight )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// it's a sky hack wall
|
|
|
|
if( li->backsector && li->backsector->ceilingpic == ::g->skyflatnum )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mobj_t* sourceObject = ::g->shootthing;
|
|
|
|
if( sourceObject )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( ( sourceObject->player ) == &( ::g->players[DoomLib::GetPlayer()] ) )
|
|
|
|
{
|
|
|
|
|
|
|
|
// Fist Punch.
|
|
|
|
if( ::g->attackrange == MELEERANGE )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spawn bullet puffs.
|
|
|
|
P_SpawnPuff( x, y, z );
|
|
|
|
|
|
|
|
// don't go any farther
|
|
|
|
return false;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
2022-09-05 20:25:33 +00:00
|
|
|
|
|
|
|
// shoot a thing
|
|
|
|
th = in->d.thing;
|
|
|
|
if( th == ::g->shootthing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
return true; // can't shoot self
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( !( th->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
return true; // corpse or something
|
|
|
|
}
|
|
|
|
|
|
|
|
// check angles to see if the thing can be aimed at
|
|
|
|
dist = FixedMul( ::g->attackrange, in->frac );
|
|
|
|
thingtopslope = FixedDiv( th->z + th->height - ::g->shootz , dist );
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( thingtopslope < ::g->aimslope )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
return true; // shot over the thing
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
thingbottomslope = FixedDiv( th->z - ::g->shootz, dist );
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( thingbottomslope > ::g->aimslope )
|
|
|
|
{
|
|
|
|
return true; // shot under the thing
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
|
|
|
|
// hit thing
|
|
|
|
// position a bit closer
|
|
|
|
frac = in->frac - FixedDiv( 10 * FRACUNIT, ::g->attackrange );
|
|
|
|
|
|
|
|
x = ::g->trace.x + FixedMul( ::g->trace.dx, frac );
|
|
|
|
y = ::g->trace.y + FixedMul( ::g->trace.dy, frac );
|
|
|
|
z = ::g->shootz + FixedMul( ::g->aimslope, FixedMul( frac, ::g->attackrange ) );
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
// check for friendly fire.
|
|
|
|
#ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
|
2022-09-05 20:25:33 +00:00
|
|
|
if( th && gameLocal->GetMatchParms().GetGameType() != GAME_TYPE_PVP )
|
|
|
|
{
|
|
|
|
player_t* hitPlayer = th->player;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( hitPlayer )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
mobj_t* sourceObject = ::g->shootthing;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( sourceObject )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
player_t* sourcePlayer = sourceObject->player;
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( sourcePlayer != NULL && sourcePlayer != hitPlayer && !gameLocal->GetMatchParms().AllowFriendlyFire() )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
mobj_t* sourceObject = ::g->shootthing;
|
|
|
|
if( sourceObject )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( ( sourceObject->player ) == &( ::g->players[DoomLib::GetPlayer()] ) )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
// Fist Punch.
|
2022-09-05 20:25:33 +00:00
|
|
|
if( ::g->attackrange == MELEERANGE )
|
|
|
|
{
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
// Spawn bullet puffs or blod spots,
|
|
|
|
// depending on target type.
|
|
|
|
if( in->d.thing->flags & MF_NOBLOOD )
|
|
|
|
{
|
|
|
|
P_SpawnPuff( x, y, z );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
P_SpawnBlood( x, y, z, ::g->la_damage );
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( ::g->la_damage )
|
|
|
|
{
|
|
|
|
P_DamageMobj( th, ::g->shootthing, ::g->shootthing, ::g->la_damage );
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't go any farther
|
|
|
|
return false;
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_AimLineAttack
|
|
|
|
//
|
|
|
|
fixed_t
|
|
|
|
P_AimLineAttack
|
|
|
|
( mobj_t* t1,
|
|
|
|
angle_t angle,
|
|
|
|
fixed_t distance )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t x2;
|
|
|
|
fixed_t y2;
|
|
|
|
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
::g->shootthing = t1;
|
|
|
|
|
|
|
|
x2 = t1->x + ( distance >> FRACBITS ) * finecosine[angle];
|
|
|
|
y2 = t1->y + ( distance >> FRACBITS ) * finesine[angle];
|
|
|
|
::g->shootz = t1->z + ( t1->height >> 1 ) + 8 * FRACUNIT;
|
|
|
|
|
|
|
|
// can't shoot outside view angles
|
|
|
|
::g->topslope = 100 * FRACUNIT / 160;
|
|
|
|
::g->bottomslope = -100 * FRACUNIT / 160;
|
|
|
|
|
|
|
|
::g->attackrange = distance;
|
|
|
|
::g->linetarget = NULL;
|
|
|
|
|
|
|
|
P_PathTraverse( t1->x, t1->y,
|
|
|
|
x2, y2,
|
|
|
|
PT_ADDLINES | PT_ADDTHINGS,
|
|
|
|
PTR_AimTraverse );
|
|
|
|
|
|
|
|
if( ::g->linetarget )
|
|
|
|
{
|
|
|
|
return ::g->aimslope;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
2022-09-05 20:25:33 +00:00
|
|
|
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// P_LineAttack
|
|
|
|
// If damage == 0, it is just a test ::g->trace
|
|
|
|
// that will leave ::g->linetarget set.
|
|
|
|
//
|
|
|
|
void
|
|
|
|
P_LineAttack
|
|
|
|
( mobj_t* t1,
|
|
|
|
angle_t angle,
|
|
|
|
fixed_t distance,
|
|
|
|
fixed_t slope,
|
|
|
|
int damage )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t x2;
|
|
|
|
fixed_t y2;
|
|
|
|
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
::g->shootthing = t1;
|
|
|
|
::g->la_damage = damage;
|
|
|
|
x2 = t1->x + ( distance >> FRACBITS ) * finecosine[angle];
|
|
|
|
y2 = t1->y + ( distance >> FRACBITS ) * finesine[angle];
|
|
|
|
::g->shootz = t1->z + ( t1->height >> 1 ) + 8 * FRACUNIT;
|
|
|
|
::g->attackrange = distance;
|
|
|
|
::g->aimslope = slope;
|
|
|
|
|
|
|
|
P_PathTraverse( t1->x, t1->y,
|
|
|
|
x2, y2,
|
|
|
|
PT_ADDLINES | PT_ADDTHINGS,
|
|
|
|
PTR_ShootTraverse );
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
2022-09-05 20:25:33 +00:00
|
|
|
|
2012-11-26 18:58:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// USE LINES
|
|
|
|
//
|
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PTR_UseTraverse( intercept_t* in )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int side;
|
|
|
|
|
|
|
|
if( !in->d.line->special )
|
|
|
|
{
|
|
|
|
P_LineOpening( in->d.line );
|
|
|
|
if( ::g->openrange <= 0 )
|
|
|
|
{
|
|
|
|
S_StartSound( ::g->usething, sfx_noway );
|
|
|
|
|
|
|
|
// can't use through a wall
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// not a special line, but keep checking
|
|
|
|
return true ;
|
|
|
|
}
|
|
|
|
|
|
|
|
side = 0;
|
|
|
|
if( P_PointOnLineSide( ::g->usething->x, ::g->usething->y, in->d.line ) == 1 )
|
|
|
|
{
|
|
|
|
side = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return false; // don't use back side
|
|
|
|
|
|
|
|
P_UseSpecialLine( ::g->usething, in->d.line, side );
|
|
|
|
|
|
|
|
// can't use for than one special line in a row
|
|
|
|
return false;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_UseLines
|
|
|
|
// Looks for special ::g->lines in front of the player to activate.
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
void P_UseLines( player_t* player )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int angle;
|
|
|
|
fixed_t x1;
|
|
|
|
fixed_t y1;
|
|
|
|
fixed_t x2;
|
|
|
|
fixed_t y2;
|
|
|
|
|
|
|
|
::g->usething = player->mo;
|
|
|
|
|
|
|
|
angle = player->mo->angle >> ANGLETOFINESHIFT;
|
|
|
|
|
|
|
|
x1 = player->mo->x;
|
|
|
|
y1 = player->mo->y;
|
|
|
|
x2 = x1 + ( USERANGE >> FRACBITS ) * finecosine[angle];
|
|
|
|
y2 = y1 + ( USERANGE >> FRACBITS ) * finesine[angle];
|
|
|
|
|
|
|
|
P_PathTraverse( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// RADIUS ATTACK
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PIT_RadiusAttack
|
|
|
|
// "bombsource" is the creature
|
|
|
|
// that caused the explosion at "bombspot".
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PIT_RadiusAttack( mobj_t* thing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
fixed_t dx;
|
|
|
|
fixed_t dy;
|
|
|
|
fixed_t dist;
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Boss spider and cyborg
|
|
|
|
// take no damage from concussion.
|
|
|
|
if( thing->type == MT_CYBORG
|
|
|
|
|| thing->type == MT_SPIDER )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dx = abs( thing->x - ::g->bombspot->x );
|
|
|
|
dy = abs( thing->y - ::g->bombspot->y );
|
|
|
|
|
|
|
|
dist = dx > dy ? dx : dy;
|
|
|
|
dist = ( dist - thing->radius ) >> FRACBITS;
|
|
|
|
|
|
|
|
if( dist < 0 )
|
|
|
|
{
|
|
|
|
dist = 0;
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
|
2022-09-05 20:25:33 +00:00
|
|
|
if( dist >= ::g->bombdamage )
|
|
|
|
{
|
|
|
|
return true; // out of range
|
|
|
|
}
|
|
|
|
|
|
|
|
if( P_CheckSight( thing, ::g->bombspot ) )
|
|
|
|
{
|
|
|
|
// must be in direct path
|
|
|
|
P_DamageMobj( thing, ::g->bombspot, ::g->bombsource, ::g->bombdamage - dist );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_RadiusAttack
|
|
|
|
// Source is the creature that caused the explosion at spot.
|
|
|
|
//
|
|
|
|
void
|
|
|
|
P_RadiusAttack
|
|
|
|
( mobj_t* spot,
|
|
|
|
mobj_t* source,
|
|
|
|
int damage )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
|
|
|
|
int xl;
|
|
|
|
int xh;
|
|
|
|
int yl;
|
|
|
|
int yh;
|
|
|
|
|
|
|
|
fixed_t dist;
|
|
|
|
|
|
|
|
dist = ( damage + MAXRADIUS ) << FRACBITS;
|
|
|
|
yh = ( spot->y + dist - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
yl = ( spot->y - dist - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
|
|
|
|
xh = ( spot->x + dist - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
|
|
xl = ( spot->x - dist - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
|
|
|
|
::g->bombspot = spot;
|
|
|
|
::g->bombsource = source;
|
|
|
|
::g->bombdamage = damage;
|
|
|
|
|
|
|
|
for( y = yl ; y <= yh ; y++ )
|
|
|
|
for( x = xl ; x <= xh ; x++ )
|
|
|
|
{
|
|
|
|
P_BlockThingsIterator( x, y, PIT_RadiusAttack );
|
|
|
|
}
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// SECTOR HEIGHT CHANGING
|
|
|
|
// After modifying a ::g->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_ChangeSector again
|
|
|
|
// to undo the changes.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// PIT_ChangeSector
|
|
|
|
//
|
2022-09-05 20:25:33 +00:00
|
|
|
qboolean PIT_ChangeSector( mobj_t* thing )
|
2012-11-26 18:58:24 +00:00
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
mobj_t* mo;
|
|
|
|
|
|
|
|
if( P_ThingHeightClip( thing ) )
|
|
|
|
{
|
|
|
|
// keep checking
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// crunch bodies to giblets
|
|
|
|
if( thing->health <= 0 )
|
|
|
|
{
|
|
|
|
P_SetMobjState( thing, S_GIBS );
|
|
|
|
|
|
|
|
thing->flags &= ~MF_SOLID;
|
|
|
|
thing->height = 0;
|
|
|
|
thing->radius = 0;
|
|
|
|
|
|
|
|
// keep checking
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// crunch dropped items
|
|
|
|
if( thing->flags & MF_DROPPED )
|
|
|
|
{
|
|
|
|
P_RemoveMobj( thing );
|
|
|
|
|
|
|
|
// keep checking
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( thing->flags & MF_SHOOTABLE ) )
|
|
|
|
{
|
|
|
|
// assume it is bloody gibs or something
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
::g->nofit = true;
|
|
|
|
|
|
|
|
if( ::g->crushchange && !( ::g->leveltime & 3 ) )
|
|
|
|
{
|
|
|
|
P_DamageMobj( thing, NULL, NULL, 10 );
|
|
|
|
|
|
|
|
// spray blood in a random direction
|
|
|
|
mo = P_SpawnMobj( thing->x,
|
|
|
|
thing->y,
|
|
|
|
thing->z + thing->height / 2, MT_BLOOD );
|
|
|
|
|
|
|
|
mo->momx = ( P_Random() - P_Random() ) << 12;
|
|
|
|
mo->momy = ( P_Random() - P_Random() ) << 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
// keep checking (crush other things)
|
2012-11-26 18:58:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// P_ChangeSector
|
|
|
|
//
|
|
|
|
qboolean
|
|
|
|
P_ChangeSector
|
|
|
|
( sector_t* sector,
|
|
|
|
qboolean crunch )
|
|
|
|
{
|
2022-09-05 20:25:33 +00:00
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
|
|
|
|
::g->nofit = false;
|
|
|
|
::g->crushchange = crunch;
|
|
|
|
|
|
|
|
// re-check heights for all things near the moving sector
|
|
|
|
for( x = sector->blockbox[BOXLEFT] ; x <= sector->blockbox[BOXRIGHT] ; x++ )
|
|
|
|
for( y = sector->blockbox[BOXBOTTOM]; y <= sector->blockbox[BOXTOP] ; y++ )
|
|
|
|
{
|
|
|
|
P_BlockThingsIterator( x, y, PIT_ChangeSector );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ::g->nofit;
|
2012-11-26 18:58:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|