DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

p_map.cpp (30931B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "Precompiled.h"
     30 #include "globaldata.h"
     31 
     32 #include <stdlib.h>
     33 
     34 #include "m_bbox.h"
     35 #include "m_random.h"
     36 #include "i_system.h"
     37 
     38 #include "doomdef.h"
     39 #include "p_local.h"
     40 
     41 #include "s_sound.h"
     42 
     43 // State.
     44 #include "doomstat.h"
     45 #include "r_state.h"
     46 // Data.
     47 #include "sounds.h"
     48 
     49 #include "Main.h"
     50 
     51 
     52 // If "floatok" true, move would be ok
     53 // if within "tmfloorz - tmceilingz".
     54 
     55 
     56 // keep track of the line that lowers the ceiling,
     57 // so missiles don't explode against sky hack walls
     58 
     59 // keep track of special ::g->lines as they are hit,
     60 // but don't process them until the move is proven valid
     61 
     62 
     63 
     64 
     65 //
     66 // TELEPORT MOVE
     67 // 
     68 
     69 //
     70 // PIT_StompThing
     71 //
     72 qboolean PIT_StompThing (mobj_t* thing)
     73 {
     74     fixed_t	blockdist;
     75 		
     76     if (!(thing->flags & MF_SHOOTABLE) )
     77 	return true;
     78 		
     79     blockdist = thing->radius + ::g->tmthing->radius;
     80     
     81     if ( abs(thing->x - ::g->tmx) >= blockdist
     82 	 || abs(thing->y - ::g->tmy) >= blockdist )
     83     {
     84 	// didn't hit it
     85 	return true;
     86     }
     87     
     88     // don't clip against self
     89     if (thing == ::g->tmthing)
     90 	return true;
     91     
     92     // monsters don't stomp things except on boss level
     93     if ( !::g->tmthing->player && ::g->gamemap != 30)
     94 	return false;	
     95 		
     96     P_DamageMobj (thing, ::g->tmthing, ::g->tmthing, 10000);
     97 	
     98     return true;
     99 }
    100 
    101 
    102 //
    103 // P_TeleportMove
    104 //
    105 qboolean
    106 P_TeleportMove
    107 ( mobj_t*	thing,
    108   fixed_t	x,
    109   fixed_t	y )
    110 {
    111     int			xl;
    112     int			xh;
    113     int			yl;
    114     int			yh;
    115     int			bx;
    116     int			by;
    117     
    118     subsector_t*	newsubsec;
    119     
    120     // kill anything occupying the position
    121     ::g->tmthing = thing;
    122     ::g->tmflags = thing->flags;
    123 	
    124     ::g->tmx = x;
    125     ::g->tmy = y;
    126 	
    127     ::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius;
    128     ::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius;
    129     ::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius;
    130     ::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius;
    131 
    132     newsubsec = R_PointInSubsector (x,y);
    133     ::g->ceilingline = NULL;
    134     
    135     // The base floor/ceiling is from the subsector
    136     // that contains the point.
    137     // Any contacted ::g->lines the step closer together
    138     // will adjust them.
    139     ::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight;
    140     ::g->tmceilingz = newsubsec->sector->ceilingheight;
    141 			
    142     ::g->validcount++;
    143     ::g->numspechit = 0;
    144     
    145     // stomp on any things contacted
    146     xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
    147     xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
    148     yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
    149     yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
    150 
    151     for (bx=xl ; bx<=xh ; bx++)
    152 	for (by=yl ; by<=yh ; by++)
    153 	    if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
    154 		return false;
    155     
    156     // the move is ok,
    157     // so link the thing into its new position
    158     P_UnsetThingPosition (thing);
    159 
    160     thing->floorz = ::g->tmfloorz;
    161     thing->ceilingz = ::g->tmceilingz;	
    162     thing->x = x;
    163     thing->y = y;
    164 
    165     P_SetThingPosition (thing);
    166 	
    167     return true;
    168 }
    169 
    170 
    171 //
    172 // MOVEMENT ITERATOR FUNCTIONS
    173 //
    174 
    175 
    176 //
    177 // PIT_CheckLine
    178 // Adjusts ::g->tmfloorz and ::g->tmceilingz as ::g->lines are contacted
    179 //
    180 qboolean PIT_CheckLine (line_t* ld)
    181 {
    182     if (::g->tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
    183 	|| ::g->tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
    184 	|| ::g->tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
    185 	|| ::g->tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
    186 	return true;
    187 
    188     if (P_BoxOnLineSide (::g->tmbbox, ld) != -1)
    189 	return true;
    190 		
    191     // A line has been hit
    192     
    193     // The moving thing's destination position will cross
    194     // the given line.
    195     // If this should not be allowed, return false.
    196     // If the line is special, keep track of it
    197     // to process later if the move is proven ok.
    198     // NOTE: specials are NOT sorted by order,
    199     // so two special ::g->lines that are only 8 pixels apart
    200     // could be crossed in either order.
    201     
    202     if (!ld->backsector)
    203 	return false;		// one sided line
    204 		
    205     if (!(::g->tmthing->flags & MF_MISSILE) )
    206     {
    207 	if ( ld->flags & ML_BLOCKING )
    208 	    return false;	// explicitly blocking everything
    209 
    210 	if ( !::g->tmthing->player && ld->flags & ML_BLOCKMONSTERS )
    211 	    return false;	// block monsters only
    212     }
    213 
    214     // set ::g->openrange, ::g->opentop, ::g->openbottom
    215     P_LineOpening (ld);	
    216 	
    217     // adjust floor / ceiling heights
    218     if (::g->opentop < ::g->tmceilingz)
    219     {
    220 	::g->tmceilingz = ::g->opentop;
    221 	::g->ceilingline = ld;
    222     }
    223 
    224     if (::g->openbottom > ::g->tmfloorz)
    225 	::g->tmfloorz = ::g->openbottom;	
    226 
    227     if (::g->lowfloor < ::g->tmdropoffz)
    228 	::g->tmdropoffz = ::g->lowfloor;
    229 
    230     // if contacted a special line, add it to the list
    231     if (ld->special && ::g->numspechit < MAXSPECIALCROSS )
    232     {
    233 	::g->spechit[::g->numspechit] = ld;
    234 	::g->numspechit++;
    235     }
    236 
    237     return true;
    238 }
    239 
    240 //
    241 // PIT_CheckThing
    242 //
    243 qboolean PIT_CheckThing (mobj_t* thing)
    244 {
    245     fixed_t		blockdist;
    246     qboolean		solid;
    247     int			damage;
    248 		
    249     if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
    250 	return true;
    251     
    252     blockdist = thing->radius + ::g->tmthing->radius;
    253 
    254     if ( abs(thing->x - ::g->tmx) >= blockdist
    255 	 || abs(thing->y - ::g->tmy) >= blockdist )
    256     {
    257 	// didn't hit it
    258 	return true;	
    259     }
    260     
    261     // don't clip against self
    262     if (thing == ::g->tmthing)
    263 	return true;
    264     
    265     // check for skulls slamming into things
    266     if (::g->tmthing->flags & MF_SKULLFLY)
    267     {
    268 	damage = ((P_Random()%8)+1)*::g->tmthing->info->damage;
    269 	
    270 	P_DamageMobj (thing, ::g->tmthing, ::g->tmthing, damage);
    271 	
    272 	::g->tmthing->flags &= ~MF_SKULLFLY;
    273 	::g->tmthing->momx = ::g->tmthing->momy = ::g->tmthing->momz = 0;
    274 	
    275 	P_SetMobjState (::g->tmthing, (statenum_t)::g->tmthing->info->spawnstate);
    276 	
    277 	return false;		// stop moving
    278     }
    279 
    280     
    281     // missiles can hit other things
    282     if (::g->tmthing->flags & MF_MISSILE)
    283     {
    284 	// see if it went over / under
    285 	if (::g->tmthing->z > thing->z + thing->height)
    286 	    return true;		// overhead
    287 	if (::g->tmthing->z+::g->tmthing->height < thing->z)
    288 	    return true;		// underneath
    289 		
    290 	if (::g->tmthing->target && (
    291 	    ::g->tmthing->target->type == thing->type || 
    292 	    (::g->tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
    293 	    (::g->tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
    294 	{
    295 	    // Don't hit same species as originator.
    296 	    if (thing == ::g->tmthing->target)
    297 		return true;
    298 
    299 	    if (thing->type != MT_PLAYER)
    300 	    {
    301 		// Explode, but do no damage.
    302 		// Let ::g->players missile other ::g->players.
    303 		return false;
    304 	    }
    305 	}
    306 	
    307 	if (! (thing->flags & MF_SHOOTABLE) )
    308 	{
    309 	    // didn't do any damage
    310 	    return !(thing->flags & MF_SOLID);	
    311 	}
    312 	
    313 	// damage / explode
    314 	damage = ((P_Random()%8)+1)*::g->tmthing->info->damage;
    315 	P_DamageMobj (thing, ::g->tmthing, ::g->tmthing->target, damage);
    316 
    317 	// don't traverse any more
    318 	return false;				
    319     }
    320     
    321     // check for special pickup
    322     if (thing->flags & MF_SPECIAL)
    323     {
    324 	solid = thing->flags&MF_SOLID;
    325 	if (::g->tmflags&MF_PICKUP)
    326 	{
    327 	    // can remove thing
    328 	    P_TouchSpecialThing (thing, ::g->tmthing);
    329 	}
    330 	return !solid;
    331     }
    332 	
    333     return !(thing->flags & MF_SOLID);
    334 }
    335 
    336 
    337 //
    338 // MOVEMENT CLIPPING
    339 //
    340 
    341 //
    342 // P_CheckPosition
    343 // This is purely informative, nothing is modified
    344 // (except things picked up).
    345 // 
    346 // in:
    347 //  a mobj_t (can be valid or invalid)
    348 //  a position to be checked
    349 //   (doesn't need to be related to the mobj_t->x,y)
    350 //
    351 // during:
    352 //  special things are touched if MF_PICKUP
    353 //  early out on solid lines?
    354 //
    355 // out:
    356 //  newsubsec
    357 //  floorz
    358 //  ceilingz
    359 //  ::g->tmdropoffz
    360 //   the lowest point contacted
    361 //   (monsters won't move to a dropoff)
    362 //  speciallines[]
    363 //  numspeciallines
    364 //
    365 qboolean
    366 P_CheckPosition
    367 ( mobj_t*	thing,
    368   fixed_t	x,
    369   fixed_t	y )
    370 {
    371     int			xl;
    372     int			xh;
    373     int			yl;
    374     int			yh;
    375     int			bx;
    376     int			by;
    377     subsector_t*	newsubsec;
    378 
    379     ::g->tmthing = thing;
    380     ::g->tmflags = thing->flags;
    381 	
    382     ::g->tmx = x;
    383     ::g->tmy = y;
    384 	
    385     ::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius;
    386     ::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius;
    387     ::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius;
    388     ::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius;
    389 
    390     newsubsec = R_PointInSubsector (x,y);
    391     ::g->ceilingline = NULL;
    392     
    393     // The base floor / ceiling is from the subsector
    394     // that contains the point.
    395     // Any contacted ::g->lines the step closer together
    396     // will adjust them.
    397     ::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight;
    398     ::g->tmceilingz = newsubsec->sector->ceilingheight;
    399 			
    400     ::g->validcount++;
    401     ::g->numspechit = 0;
    402 
    403     if ( ::g->tmflags & MF_NOCLIP )
    404 	return true;
    405     
    406     // Check things first, possibly picking things up.
    407     // The bounding box is extended by MAXRADIUS
    408     // because mobj_ts are grouped into mapblocks
    409     // based on their origin point, and can overlap
    410     // into adjacent blocks by up to MAXRADIUS units.
    411     xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
    412     xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
    413     yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
    414     yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
    415 
    416     for (bx=xl ; bx<=xh ; bx++)
    417 	for (by=yl ; by<=yh ; by++)
    418 	    if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
    419 		return false;
    420     
    421     // check ::g->lines
    422     xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx)>>MAPBLOCKSHIFT;
    423     xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx)>>MAPBLOCKSHIFT;
    424     yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy)>>MAPBLOCKSHIFT;
    425     yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy)>>MAPBLOCKSHIFT;
    426 
    427     for (bx=xl ; bx<=xh ; bx++)
    428 	for (by=yl ; by<=yh ; by++)
    429 	    if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
    430 		return false;
    431 
    432     return true;
    433 }
    434 
    435 
    436 //
    437 // P_TryMove
    438 // Attempt to move to a new position,
    439 // crossing special ::g->lines unless MF_TELEPORT is set.
    440 //
    441 qboolean
    442 P_TryMove
    443 ( mobj_t*	thing,
    444   fixed_t	x,
    445   fixed_t	y )
    446 {
    447     fixed_t	oldx;
    448     fixed_t	oldy;
    449     int		side;
    450     int		oldside;
    451     line_t*	ld;
    452 
    453     ::g->floatok = false;
    454     if (!P_CheckPosition (thing, x, y))
    455 	return false;		// solid wall or thing
    456     
    457     if ( !(thing->flags & MF_NOCLIP) )
    458     {
    459 	if (::g->tmceilingz - ::g->tmfloorz < thing->height)
    460 	    return false;	// doesn't fit
    461 
    462 	::g->floatok = true;
    463 	
    464 	if ( !(thing->flags&MF_TELEPORT) 
    465 	     &&::g->tmceilingz - thing->z < thing->height)
    466 	    return false;	// mobj must lower itself to fit
    467 
    468 	if ( !(thing->flags&MF_TELEPORT)
    469 	     && ::g->tmfloorz - thing->z > 24*FRACUNIT )
    470 	    return false;	// too big a step up
    471 
    472 	if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
    473 	     && ::g->tmfloorz - ::g->tmdropoffz > 24*FRACUNIT )
    474 	    return false;	// don't stand over a dropoff
    475     }
    476     
    477     // the move is ok,
    478     // so link the thing into its new position
    479     P_UnsetThingPosition (thing);
    480 
    481     oldx = thing->x;
    482     oldy = thing->y;
    483     thing->floorz = ::g->tmfloorz;
    484     thing->ceilingz = ::g->tmceilingz;	
    485     thing->x = x;
    486     thing->y = y;
    487 
    488     P_SetThingPosition (thing);
    489     
    490     // if any special ::g->lines were hit, do the effect
    491     if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
    492     {
    493 		while (::g->numspechit--)
    494 		{
    495 			// see if the line was crossed
    496 			ld = ::g->spechit[::g->numspechit];
    497 			side = P_PointOnLineSide (thing->x, thing->y, ld);
    498 			oldside = P_PointOnLineSide (oldx, oldy, ld);
    499 			if (side != oldside)
    500 			{
    501 			if (ld->special)
    502 				P_CrossSpecialLine (ld-::g->lines, oldside, thing);
    503 			}
    504 		}
    505     }
    506 
    507     return true;
    508 }
    509 
    510 
    511 //
    512 // P_ThingHeightClip
    513 // Takes a valid thing and adjusts the thing->floorz,
    514 // thing->ceilingz, and possibly thing->z.
    515 // This is called for all nearby monsters
    516 // whenever a sector changes height.
    517 // If the thing doesn't fit,
    518 // the z will be set to the lowest value
    519 // and false will be returned.
    520 //
    521 qboolean P_ThingHeightClip (mobj_t* thing)
    522 {
    523     qboolean		onfloor;
    524 	
    525     onfloor = (thing->z == thing->floorz);
    526 	
    527     P_CheckPosition (thing, thing->x, thing->y);	
    528     // what about stranding a monster partially off an edge?
    529 	
    530     thing->floorz = ::g->tmfloorz;
    531     thing->ceilingz = ::g->tmceilingz;
    532 	
    533     if (onfloor)
    534     {
    535 	// walking monsters rise and fall with the floor
    536 	thing->z = thing->floorz;
    537     }
    538     else
    539     {
    540 	// don't adjust a floating monster unless forced to
    541 	if (thing->z+thing->height > thing->ceilingz)
    542 	    thing->z = thing->ceilingz - thing->height;
    543     }
    544 	
    545     if (thing->ceilingz - thing->floorz < thing->height)
    546 	return false;
    547 		
    548     return true;
    549 }
    550 
    551 
    552 
    553 //
    554 // SLIDE MOVE
    555 // Allows the player to slide along any angled walls.
    556 //
    557 
    558 
    559 
    560 
    561 
    562 
    563 //
    564 // P_HitSlideLine
    565 // Adjusts the xmove / ymove
    566 // so that the next move will slide along the wall.
    567 //
    568 void P_HitSlideLine (line_t* ld)
    569 {
    570     int			side;
    571 
    572     angle_t		lineangle;
    573     angle_t		moveangle;
    574     angle_t		deltaangle;
    575     
    576     fixed_t		movelen;
    577     fixed_t		newlen;
    578 	
    579 	
    580     if (ld->slopetype == ST_HORIZONTAL)
    581     {
    582 	::g->tmymove = 0;
    583 	return;
    584     }
    585     
    586     if (ld->slopetype == ST_VERTICAL)
    587     {
    588 	::g->tmxmove = 0;
    589 	return;
    590     }
    591 	
    592     side = P_PointOnLineSide (::g->slidemo->x, ::g->slidemo->y, ld);
    593 	
    594     lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
    595 
    596     if (side == 1)
    597 	lineangle += ANG180;
    598 
    599     moveangle = R_PointToAngle2 (0,0, ::g->tmxmove, ::g->tmymove);
    600     deltaangle = moveangle-lineangle;
    601 
    602     if (deltaangle > ANG180)
    603 	deltaangle += ANG180;
    604     //	I_Error ("SlideLine: ang>ANG180");
    605 
    606     lineangle >>= ANGLETOFINESHIFT;
    607     deltaangle >>= ANGLETOFINESHIFT;
    608 	
    609     movelen = P_AproxDistance (::g->tmxmove, ::g->tmymove);
    610     newlen = FixedMul (movelen, finecosine[deltaangle]);
    611 
    612     ::g->tmxmove = FixedMul (newlen, finecosine[lineangle]);	
    613     ::g->tmymove = FixedMul (newlen, finesine[lineangle]);	
    614 }
    615 
    616 
    617 //
    618 // PTR_SlideTraverse
    619 //
    620 qboolean PTR_SlideTraverse (intercept_t* in)
    621 {
    622     line_t*	li;
    623 	
    624     if (!in->isaline)
    625 	I_Error ("PTR_SlideTraverse: not a line?");
    626 		
    627     li = in->d.line;
    628     
    629     if ( ! (li->flags & ML_TWOSIDED) )
    630     {
    631 	if (P_PointOnLineSide (::g->slidemo->x, ::g->slidemo->y, li))
    632 	{
    633 	    // don't hit the back side
    634 	    return true;		
    635 	}
    636 	goto isblocking;
    637     }
    638 
    639     // set ::g->openrange, ::g->opentop, ::g->openbottom
    640     P_LineOpening (li);
    641     
    642     if (::g->openrange < ::g->slidemo->height)
    643 	goto isblocking;		// doesn't fit
    644 		
    645     if (::g->opentop - ::g->slidemo->z < ::g->slidemo->height)
    646 	goto isblocking;		// mobj is too high
    647 
    648     if (::g->openbottom - ::g->slidemo->z > 24*FRACUNIT )
    649 	goto isblocking;		// too big a step up
    650 
    651     // this line doesn't block movement
    652     return true;		
    653 	
    654     // the line does block movement,
    655     // see if it is closer than best so far
    656   isblocking:		
    657     if (in->frac < ::g->bestslidefrac)
    658     {
    659 	::g->secondslidefrac = ::g->bestslidefrac;
    660 	::g->secondslideline = ::g->bestslideline;
    661 	::g->bestslidefrac = in->frac;
    662 	::g->bestslideline = li;
    663     }
    664 	
    665     return false;	// stop
    666 }
    667 
    668 
    669 
    670 //
    671 // P_SlideMove
    672 // The momx / momy move is bad, so try to slide
    673 // along a wall.
    674 // Find the first line hit, move flush to it,
    675 // and slide along it
    676 //
    677 // This is a kludgy mess.
    678 //
    679 void P_SlideMove (mobj_t* mo)
    680 {
    681     fixed_t		leadx;
    682     fixed_t		leady;
    683     fixed_t		trailx;
    684     fixed_t		traily;
    685     fixed_t		newx;
    686     fixed_t		newy;
    687     int			hitcount;
    688 		
    689     ::g->slidemo = mo;
    690     hitcount = 0;
    691     
    692   retry:
    693     if (++hitcount == 3)
    694 	goto stairstep;		// don't loop forever
    695 
    696     
    697     // ::g->trace along the three leading corners
    698     if (mo->momx > 0)
    699     {
    700 	leadx = mo->x + mo->radius;
    701 	trailx = mo->x - mo->radius;
    702     }
    703     else
    704     {
    705 	leadx = mo->x - mo->radius;
    706 	trailx = mo->x + mo->radius;
    707     }
    708 	
    709     if (mo->momy > 0)
    710     {
    711 	leady = mo->y + mo->radius;
    712 	traily = mo->y - mo->radius;
    713     }
    714     else
    715     {
    716 	leady = mo->y - mo->radius;
    717 	traily = mo->y + mo->radius;
    718     }
    719 		
    720     ::g->bestslidefrac = FRACUNIT+1;
    721 	
    722     P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
    723 		     PT_ADDLINES, PTR_SlideTraverse );
    724     P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
    725 		     PT_ADDLINES, PTR_SlideTraverse );
    726     P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
    727 		     PT_ADDLINES, PTR_SlideTraverse );
    728     
    729     // move up to the wall
    730     if (::g->bestslidefrac == FRACUNIT+1)
    731     {
    732 	// the move most have hit the middle, so stairstep
    733       stairstep:
    734 	if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
    735 	    P_TryMove (mo, mo->x + mo->momx, mo->y);
    736 	return;
    737     }
    738 
    739     // fudge a bit to make sure it doesn't hit
    740     ::g->bestslidefrac -= 0x800;	
    741     if (::g->bestslidefrac > 0)
    742     {
    743 	newx = FixedMul (mo->momx, ::g->bestslidefrac);
    744 	newy = FixedMul (mo->momy, ::g->bestslidefrac);
    745 	
    746 	if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
    747 	    goto stairstep;
    748     }
    749     
    750     // Now continue along the wall.
    751     // First calculate remainder.
    752     ::g->bestslidefrac = FRACUNIT-(::g->bestslidefrac+0x800);
    753     
    754     if (::g->bestslidefrac > FRACUNIT)
    755 	::g->bestslidefrac = FRACUNIT;
    756     
    757     if (::g->bestslidefrac <= 0)
    758 	return;
    759     
    760     ::g->tmxmove = FixedMul (mo->momx, ::g->bestslidefrac);
    761     ::g->tmymove = FixedMul (mo->momy, ::g->bestslidefrac);
    762 
    763     P_HitSlideLine (::g->bestslideline);	// clip the moves
    764 
    765     mo->momx = ::g->tmxmove;
    766     mo->momy = ::g->tmymove;
    767 		
    768     if (!P_TryMove (mo, mo->x+::g->tmxmove, mo->y+::g->tmymove))
    769     {
    770 	goto retry;
    771     }
    772 }
    773 
    774 
    775 //
    776 // P_LineAttack
    777 //
    778 
    779 // Height if not aiming up or down
    780 // ???: use slope for monsters?
    781 
    782 
    783 
    784 // slopes to top and bottom of target
    785 
    786 
    787 //
    788 // PTR_AimTraverse
    789 // Sets linetaget and ::g->aimslope when a target is aimed at.
    790 //
    791 qboolean
    792 PTR_AimTraverse (intercept_t* in)
    793 {
    794     line_t*		li;
    795     mobj_t*		th;
    796     fixed_t		slope;
    797     fixed_t		thingtopslope;
    798     fixed_t		thingbottomslope;
    799     fixed_t		dist;
    800 		
    801     if (in->isaline)
    802     {
    803 	li = in->d.line;
    804 	
    805 	if ( !(li->flags & ML_TWOSIDED) )
    806 	    return false;		// stop
    807 	
    808 	// Crosses a two sided line.
    809 	// A two sided line will restrict
    810 	// the possible target ranges.
    811 	P_LineOpening (li);
    812 	
    813 	if (::g->openbottom >= ::g->opentop)
    814 	    return false;		// stop
    815 	
    816 	dist = FixedMul (::g->attackrange, in->frac);
    817 
    818 	if (li->frontsector->floorheight != li->backsector->floorheight)
    819 	{
    820 	    slope = FixedDiv (::g->openbottom - ::g->shootz , dist);
    821 	    if (slope > ::g->bottomslope)
    822 		::g->bottomslope = slope;
    823 	}
    824 		
    825 	if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
    826 	{
    827 	    slope = FixedDiv (::g->opentop - ::g->shootz , dist);
    828 	    if (slope < ::g->topslope)
    829 		::g->topslope = slope;
    830 	}
    831 		
    832 	if (::g->topslope <= ::g->bottomslope)
    833 	    return false;		// stop
    834 			
    835 	return true;			// shot continues
    836     }
    837     
    838     // shoot a thing
    839     th = in->d.thing;
    840     if (th == ::g->shootthing)
    841 	return true;			// can't shoot self
    842     
    843     if (!(th->flags&MF_SHOOTABLE))
    844 	return true;			// corpse or something
    845 
    846     // check angles to see if the thing can be aimed at
    847     dist = FixedMul (::g->attackrange, in->frac);
    848     thingtopslope = FixedDiv (th->z+th->height - ::g->shootz , dist);
    849 
    850     if (thingtopslope < ::g->bottomslope)
    851 	return true;			// shot over the thing
    852 
    853     thingbottomslope = FixedDiv (th->z - ::g->shootz, dist);
    854 
    855     if (thingbottomslope > ::g->topslope)
    856 	return true;			// shot under the thing
    857     
    858     // this thing can be hit!
    859     if (thingtopslope > ::g->topslope)
    860 	thingtopslope = ::g->topslope;
    861     
    862     if (thingbottomslope < ::g->bottomslope)
    863 	thingbottomslope = ::g->bottomslope;
    864 
    865     ::g->aimslope = (thingtopslope+thingbottomslope)/2;
    866     ::g->linetarget = th;
    867 
    868     return false;			// don't go any farther
    869 }
    870 
    871 
    872 //
    873 // PTR_ShootTraverse
    874 //
    875 qboolean PTR_ShootTraverse (intercept_t* in)
    876 {
    877     fixed_t		x;
    878     fixed_t		y;
    879     fixed_t		z;
    880     fixed_t		frac;
    881     
    882     line_t*		li;
    883     
    884     mobj_t*		th;
    885 
    886     fixed_t		slope;
    887     fixed_t		dist;
    888     fixed_t		thingtopslope;
    889     fixed_t		thingbottomslope;
    890 		
    891     if (in->isaline)
    892     {
    893 	li = in->d.line;
    894 	
    895 	if (li->special)
    896 	    P_ShootSpecialLine (::g->shootthing, li);
    897 
    898 	if ( !(li->flags & ML_TWOSIDED) )
    899 	    goto hitline;
    900 	
    901 	// crosses a two sided line
    902 	P_LineOpening (li);
    903 		
    904 	dist = FixedMul (::g->attackrange, in->frac);
    905 
    906 	if (li->frontsector->floorheight != li->backsector->floorheight)
    907 	{
    908 	    slope = FixedDiv (::g->openbottom - ::g->shootz , dist);
    909 	    if (slope > ::g->aimslope)
    910 		goto hitline;
    911 	}
    912 		
    913 	if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
    914 	{
    915 	    slope = FixedDiv (::g->opentop - ::g->shootz , dist);
    916 	    if (slope < ::g->aimslope)
    917 		goto hitline;
    918 	}
    919 
    920 	// shot continues
    921 	return true;
    922 	
    923 	
    924 	// hit line
    925       hitline:
    926 	// position a bit closer
    927 	frac = in->frac - FixedDiv (4*FRACUNIT,::g->attackrange);
    928 	x = ::g->trace.x + FixedMul (::g->trace.dx, frac);
    929 	y = ::g->trace.y + FixedMul (::g->trace.dy, frac);
    930 	z = ::g->shootz + FixedMul (::g->aimslope, FixedMul(frac, ::g->attackrange));
    931 
    932 	if (li->frontsector->ceilingpic == ::g->skyflatnum)
    933 	{
    934 	    // don't shoot the sky!
    935 	    if (z > li->frontsector->ceilingheight)
    936 		return false;
    937 	    
    938 	    // it's a sky hack wall
    939 	    if	(li->backsector && li->backsector->ceilingpic == ::g->skyflatnum)
    940 		return false;		
    941 	}
    942 
    943 	mobj_t * sourceObject = ::g->shootthing;
    944 	if( sourceObject ) {
    945 
    946 		if( ( sourceObject->player) == &(::g->players[DoomLib::GetPlayer()]) ) {
    947 			
    948 			// Fist Punch.
    949 			if( ::g->attackrange == MELEERANGE ) {
    950 			}
    951 		}
    952 	}
    953 
    954 	// Spawn bullet puffs.
    955 	P_SpawnPuff (x,y,z);
    956 	
    957 	// don't go any farther
    958 	return false;	
    959     }
    960     
    961     // shoot a thing
    962     th = in->d.thing;
    963     if (th == ::g->shootthing)
    964 	return true;		// can't shoot self
    965     
    966     if (!(th->flags&MF_SHOOTABLE))
    967 	return true;		// corpse or something
    968 		
    969     // check angles to see if the thing can be aimed at
    970     dist = FixedMul (::g->attackrange, in->frac);
    971     thingtopslope = FixedDiv (th->z+th->height - ::g->shootz , dist);
    972 
    973     if (thingtopslope < ::g->aimslope)
    974 	return true;		// shot over the thing
    975 
    976     thingbottomslope = FixedDiv (th->z - ::g->shootz, dist);
    977 
    978     if (thingbottomslope > ::g->aimslope)
    979 	return true;		// shot under the thing
    980 
    981     
    982     // hit thing
    983     // position a bit closer
    984     frac = in->frac - FixedDiv (10*FRACUNIT,::g->attackrange);
    985 
    986     x = ::g->trace.x + FixedMul (::g->trace.dx, frac);
    987     y = ::g->trace.y + FixedMul (::g->trace.dy, frac);
    988     z = ::g->shootz + FixedMul (::g->aimslope, FixedMul(frac, ::g->attackrange));
    989 
    990 	// check for friendly fire.
    991 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING
    992 	if( th  && gameLocal->GetMatchParms().GetGameType() != GAME_TYPE_PVP ) {
    993 		player_t * hitPlayer = th->player;
    994 
    995 		if( hitPlayer ) {
    996 
    997 			mobj_t * sourceObject = ::g->shootthing;
    998 
    999 			if( sourceObject ) {
   1000 				player_t* sourcePlayer = sourceObject->player;
   1001 
   1002 				if( sourcePlayer != NULL && sourcePlayer != hitPlayer  && !gameLocal->GetMatchParms().AllowFriendlyFire() ) {
   1003 					return true;
   1004 				}
   1005 			}
   1006 		}
   1007 	}
   1008 #endif
   1009 
   1010 	mobj_t * sourceObject = ::g->shootthing;
   1011 	if( sourceObject ) {
   1012 
   1013 		if( ( sourceObject->player) == &(::g->players[DoomLib::GetPlayer()]) ) {
   1014 
   1015 			// Fist Punch.
   1016 			if( ::g->attackrange == MELEERANGE ) {
   1017 			}
   1018 		}
   1019 	}
   1020 
   1021 
   1022     // Spawn bullet puffs or blod spots,
   1023     // depending on target type.
   1024     if (in->d.thing->flags & MF_NOBLOOD)
   1025 	P_SpawnPuff (x,y,z);
   1026     else
   1027 	P_SpawnBlood (x,y,z, ::g->la_damage);
   1028 
   1029     if (::g->la_damage)
   1030 	P_DamageMobj (th, ::g->shootthing, ::g->shootthing, ::g->la_damage);
   1031 
   1032     // don't go any farther
   1033     return false;
   1034 	
   1035 }
   1036 
   1037 
   1038 //
   1039 // P_AimLineAttack
   1040 //
   1041 fixed_t
   1042 P_AimLineAttack
   1043 ( mobj_t*	t1,
   1044   angle_t	angle,
   1045   fixed_t	distance )
   1046 {
   1047     fixed_t	x2;
   1048     fixed_t	y2;
   1049 	
   1050     angle >>= ANGLETOFINESHIFT;
   1051     ::g->shootthing = t1;
   1052     
   1053     x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
   1054     y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
   1055     ::g->shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
   1056 
   1057     // can't shoot outside view angles
   1058     ::g->topslope = 100*FRACUNIT/160;	
   1059     ::g->bottomslope = -100*FRACUNIT/160;
   1060     
   1061     ::g->attackrange = distance;
   1062     ::g->linetarget = NULL;
   1063 	
   1064     P_PathTraverse ( t1->x, t1->y,
   1065 		     x2, y2,
   1066 		     PT_ADDLINES|PT_ADDTHINGS,
   1067 		     PTR_AimTraverse );
   1068 		
   1069     if (::g->linetarget)
   1070 	return ::g->aimslope;
   1071 
   1072     return 0;
   1073 }
   1074  
   1075 
   1076 //
   1077 // P_LineAttack
   1078 // If damage == 0, it is just a test ::g->trace
   1079 // that will leave ::g->linetarget set.
   1080 //
   1081 void
   1082 P_LineAttack
   1083 ( mobj_t*	t1,
   1084   angle_t	angle,
   1085   fixed_t	distance,
   1086   fixed_t	slope,
   1087   int		damage )
   1088 {
   1089     fixed_t	x2;
   1090     fixed_t	y2;
   1091 	
   1092     angle >>= ANGLETOFINESHIFT;
   1093     ::g->shootthing = t1;
   1094     ::g->la_damage = damage;
   1095     x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
   1096     y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
   1097     ::g->shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
   1098     ::g->attackrange = distance;
   1099     ::g->aimslope = slope;
   1100 		
   1101     P_PathTraverse ( t1->x, t1->y,
   1102 		     x2, y2,
   1103 		     PT_ADDLINES|PT_ADDTHINGS,
   1104 		     PTR_ShootTraverse );
   1105 }
   1106  
   1107 
   1108 
   1109 //
   1110 // USE LINES
   1111 //
   1112 
   1113 qboolean	PTR_UseTraverse (intercept_t* in)
   1114 {
   1115     int		side;
   1116 	
   1117     if (!in->d.line->special)
   1118     {
   1119 	P_LineOpening (in->d.line);
   1120 	if (::g->openrange <= 0)
   1121 	{
   1122 	    S_StartSound (::g->usething, sfx_noway);
   1123 	    
   1124 	    // can't use through a wall
   1125 	    return false;	
   1126 	}
   1127 	// not a special line, but keep checking
   1128 	return true ;		
   1129     }
   1130 	
   1131     side = 0;
   1132     if (P_PointOnLineSide (::g->usething->x, ::g->usething->y, in->d.line) == 1)
   1133 	side = 1;
   1134     
   1135     //	return false;		// don't use back side
   1136 	
   1137     P_UseSpecialLine (::g->usething, in->d.line, side);
   1138 
   1139     // can't use for than one special line in a row
   1140     return false;
   1141 }
   1142 
   1143 
   1144 //
   1145 // P_UseLines
   1146 // Looks for special ::g->lines in front of the player to activate.
   1147 //
   1148 void P_UseLines (player_t*	player) 
   1149 {
   1150     int		angle;
   1151     fixed_t	x1;
   1152     fixed_t	y1;
   1153     fixed_t	x2;
   1154     fixed_t	y2;
   1155 	
   1156     ::g->usething = player->mo;
   1157 		
   1158     angle = player->mo->angle >> ANGLETOFINESHIFT;
   1159 
   1160     x1 = player->mo->x;
   1161     y1 = player->mo->y;
   1162     x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
   1163     y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
   1164 	
   1165     P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
   1166 }
   1167 
   1168 
   1169 //
   1170 // RADIUS ATTACK
   1171 //
   1172 
   1173 
   1174 //
   1175 // PIT_RadiusAttack
   1176 // "bombsource" is the creature
   1177 // that caused the explosion at "bombspot".
   1178 //
   1179 qboolean PIT_RadiusAttack (mobj_t* thing)
   1180 {
   1181     fixed_t	dx;
   1182     fixed_t	dy;
   1183     fixed_t	dist;
   1184 	
   1185     if (!(thing->flags & MF_SHOOTABLE) )
   1186 	return true;
   1187 
   1188     // Boss spider and cyborg
   1189     // take no damage from concussion.
   1190     if (thing->type == MT_CYBORG
   1191 	|| thing->type == MT_SPIDER)
   1192 	return true;	
   1193 		
   1194     dx = abs(thing->x - ::g->bombspot->x);
   1195     dy = abs(thing->y - ::g->bombspot->y);
   1196     
   1197     dist = dx>dy ? dx : dy;
   1198     dist = (dist - thing->radius) >> FRACBITS;
   1199 
   1200     if (dist < 0)
   1201 	dist = 0;
   1202 
   1203     if (dist >= ::g->bombdamage)
   1204 	return true;	// out of range
   1205 
   1206     if ( P_CheckSight (thing, ::g->bombspot) )
   1207     {
   1208 	// must be in direct path
   1209 	P_DamageMobj (thing, ::g->bombspot, ::g->bombsource, ::g->bombdamage - dist);
   1210     }
   1211     
   1212     return true;
   1213 }
   1214 
   1215 
   1216 //
   1217 // P_RadiusAttack
   1218 // Source is the creature that caused the explosion at spot.
   1219 //
   1220 void
   1221 P_RadiusAttack
   1222 ( mobj_t*	spot,
   1223   mobj_t*	source,
   1224   int		damage )
   1225 {
   1226     int		x;
   1227     int		y;
   1228     
   1229     int		xl;
   1230     int		xh;
   1231     int		yl;
   1232     int		yh;
   1233     
   1234     fixed_t	dist;
   1235 	
   1236     dist = (damage+MAXRADIUS)<<FRACBITS;
   1237     yh = (spot->y + dist - ::g->bmaporgy)>>MAPBLOCKSHIFT;
   1238     yl = (spot->y - dist - ::g->bmaporgy)>>MAPBLOCKSHIFT;
   1239     xh = (spot->x + dist - ::g->bmaporgx)>>MAPBLOCKSHIFT;
   1240     xl = (spot->x - dist - ::g->bmaporgx)>>MAPBLOCKSHIFT;
   1241     ::g->bombspot = spot;
   1242     ::g->bombsource = source;
   1243     ::g->bombdamage = damage;
   1244 	
   1245     for (y=yl ; y<=yh ; y++)
   1246 	for (x=xl ; x<=xh ; x++)
   1247 	    P_BlockThingsIterator (x, y, PIT_RadiusAttack );
   1248 }
   1249 
   1250 
   1251 
   1252 //
   1253 // SECTOR HEIGHT CHANGING
   1254 // After modifying a ::g->sectors floor or ceiling height,
   1255 // call this routine to adjust the positions
   1256 // of all things that touch the sector.
   1257 //
   1258 // If anything doesn't fit anymore, true will be returned.
   1259 // If crunch is true, they will take damage
   1260 //  as they are being crushed.
   1261 // If Crunch is false, you should set the sector height back
   1262 //  the way it was and call P_ChangeSector again
   1263 //  to undo the changes.
   1264 //
   1265 
   1266 
   1267 //
   1268 // PIT_ChangeSector
   1269 //
   1270 qboolean PIT_ChangeSector (mobj_t*	thing)
   1271 {
   1272     mobj_t*	mo;
   1273 	
   1274     if (P_ThingHeightClip (thing))
   1275     {
   1276 	// keep checking
   1277 	return true;
   1278     }
   1279     
   1280 
   1281     // crunch bodies to giblets
   1282     if (thing->health <= 0)
   1283     {
   1284 	P_SetMobjState (thing, S_GIBS);
   1285 
   1286 	thing->flags &= ~MF_SOLID;
   1287 	thing->height = 0;
   1288 	thing->radius = 0;
   1289 
   1290 	// keep checking
   1291 	return true;		
   1292     }
   1293 
   1294     // crunch dropped items
   1295     if (thing->flags & MF_DROPPED)
   1296     {
   1297 	P_RemoveMobj (thing);
   1298 	
   1299 	// keep checking
   1300 	return true;		
   1301     }
   1302 
   1303     if (! (thing->flags & MF_SHOOTABLE) )
   1304     {
   1305 	// assume it is bloody gibs or something
   1306 	return true;			
   1307     }
   1308     
   1309     ::g->nofit = true;
   1310 
   1311     if (::g->crushchange && !(::g->leveltime&3) )
   1312     {
   1313 	P_DamageMobj(thing,NULL,NULL,10);
   1314 
   1315 	// spray blood in a random direction
   1316 	mo = P_SpawnMobj (thing->x,
   1317 			  thing->y,
   1318 			  thing->z + thing->height/2, MT_BLOOD);
   1319 	
   1320 	mo->momx = (P_Random() - P_Random ())<<12;
   1321 	mo->momy = (P_Random() - P_Random ())<<12;
   1322     }
   1323 
   1324     // keep checking (crush other things)	
   1325     return true;	
   1326 }
   1327 
   1328 
   1329 
   1330 //
   1331 // P_ChangeSector
   1332 //
   1333 qboolean
   1334 P_ChangeSector
   1335 ( sector_t*	sector,
   1336   qboolean	crunch )
   1337 {
   1338     int		x;
   1339     int		y;
   1340 	
   1341     ::g->nofit = false;
   1342     ::g->crushchange = crunch;
   1343 	
   1344     // re-check heights for all things near the moving sector
   1345     for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
   1346 	for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
   1347 	    P_BlockThingsIterator (x, y, PIT_ChangeSector);
   1348 	
   1349 	
   1350     return ::g->nofit;
   1351 }
   1352 
   1353