/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 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 . 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 #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 // // // PIT_StompThing // qboolean PIT_StompThing (mobj_t* thing) { 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); return true; } // // P_TeleportMove // qboolean P_TeleportMove ( mobj_t* thing, fixed_t x, fixed_t y ) { 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; } // // MOVEMENT ITERATOR FUNCTIONS // // // PIT_CheckLine // Adjusts ::g->tmfloorz and ::g->tmceilingz as ::g->lines are contacted // qboolean PIT_CheckLine (line_t* ld) { 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++; } return true; } // // PIT_CheckThing // qboolean PIT_CheckThing (mobj_t* thing) { fixed_t blockdist; qboolean solid; int damage; if (!(thing->flags & (MF_SOLID|MF_SPECIAL|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; // 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 return false; } // 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); } // // 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 // 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 ) { 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; // 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; } // // 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 ) { 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--) { // see if the line was crossed ld = ::g->spechit[::g->numspechit]; side = P_PointOnLineSide (thing->x, thing->y, ld); oldside = P_PointOnLineSide (oldx, oldy, ld); if (side != oldside) { if (ld->special) P_CrossSpecialLine (ld-::g->lines, oldside, 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. // qboolean P_ThingHeightClip (mobj_t* thing) { 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; } // // 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. // void P_HitSlideLine (line_t* ld) { 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]); } // // PTR_SlideTraverse // qboolean PTR_SlideTraverse (intercept_t* in) { 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 } // // 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; 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; } } // // 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 PTR_AimTraverse (intercept_t* in) { 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 } // // PTR_ShootTraverse // qboolean PTR_ShootTraverse (intercept_t* in) { fixed_t x; fixed_t y; fixed_t z; fixed_t frac; line_t* li; mobj_t* th; fixed_t slope; fixed_t dist; fixed_t thingtopslope; fixed_t thingbottomslope; if (in->isaline) { 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; } // 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->aimslope) return true; // shot over the thing thingbottomslope = FixedDiv (th->z - ::g->shootz, dist); if (thingbottomslope > ::g->aimslope) return true; // shot under the thing // 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)); // check for friendly fire. #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING if( th && gameLocal->GetMatchParms().GetGameType() != GAME_TYPE_PVP ) { player_t * hitPlayer = th->player; if( hitPlayer ) { mobj_t * sourceObject = ::g->shootthing; if( sourceObject ) { player_t* sourcePlayer = sourceObject->player; if( sourcePlayer != NULL && sourcePlayer != hitPlayer && !gameLocal->GetMatchParms().AllowFriendlyFire() ) { return true; } } } } #endif mobj_t * sourceObject = ::g->shootthing; if( sourceObject ) { if( ( sourceObject->player) == &(::g->players[DoomLib::GetPlayer()]) ) { // Fist Punch. if( ::g->attackrange == MELEERANGE ) { } } } // 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); if (::g->la_damage) P_DamageMobj (th, ::g->shootthing, ::g->shootthing, ::g->la_damage); // don't go any farther return false; } // // P_AimLineAttack // fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ) { 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; } // // 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 ) { 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 ); } // // USE LINES // qboolean PTR_UseTraverse (intercept_t* in) { 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; } // // P_UseLines // Looks for special ::g->lines in front of the player to activate. // void P_UseLines (player_t* player) { 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 ); } // // RADIUS ATTACK // // // PIT_RadiusAttack // "bombsource" is the creature // that caused the explosion at "bombspot". // qboolean PIT_RadiusAttack (mobj_t* thing) { 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; 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; } // // P_RadiusAttack // Source is the creature that caused the explosion at spot. // void P_RadiusAttack ( mobj_t* spot, mobj_t* source, int damage ) { int x; int y; int xl; int xh; int yl; int yh; fixed_t dist; dist = (damage+MAXRADIUS)<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 ); } // // 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 // qboolean PIT_ChangeSector (mobj_t* thing) { 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) return true; } // // P_ChangeSector // qboolean P_ChangeSector ( sector_t* sector, qboolean crunch ) { 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; }