2 //**************************************************************************
4 //** p_map.c : Heretic 2 : Raven Software, Corp.
11 //**************************************************************************
17 static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj);
20 ===============================================================================
25 ===============================================================================
29 ===============================================================================
33 mobj_ts are used to tell the refresh where to draw an image, tell the world simulation when objects are contacted, and tell the sound driver how to position a sound.
35 The refresh uses the next and prev links to follow lists of things in sectors as they are being drawn. The sprite, frame, and angle elements determine which patch_t is used to draw the sprite if it is visible. The sprite and frame values are allmost allways set from state_t structures. The statescr.exe utility generates the states.h and states.c files that contain the sprite/frame numbers from the statescr.txt source file. The xyz origin point represents a point at the bottom middle of the sprite (between the feet of a biped). This is the default origin position for patch_ts grabbed with lumpy.exe. A walking creature will have its z equal to the floor it is standing on.
37 The sound code uses the x,y, and subsector fields to do stereo positioning of any sound effited by the mobj_t.
39 The play simulation uses the blocklinks, x,y,z, radius, height to determine when mobj_ts are touching each other, touching lines in the map, or hit by trace lines (gunshots, lines of sight, etc). The mobj_t->flags element has various bit flags used by the simulation.
42 Every mobj_t is linked into a single sector based on it's origin coordinates.
43 The subsector_t is found with R_PointInSubsector(x,y), and the sector_t can be found with subsector->sector. The sector links are only used by the rendering code, the play simulation does not care about them at all.
45 Any mobj_t that needs to be acted upon be something else in the play world (block movement, be shot, etc) will also need to be linked into the blockmap. If the thing has the MF_NOBLOCK flag set, it will not use the block links. It can still interact with other things, but only as the instigator (missiles will run into other things, but nothing can run into a missile). Each block in the grid is 128*128 units, and knows about every line_t that it contains a piece of, and every interactable mobj_t that has it's origin contained.
47 A valid mobj_t is a mobj_t that has the proper subsector_t filled in for it's xy coordinates and is linked into the subsector's sector or has the MF_NOSECTOR flag set (the subsector_t needs to be valid even if MF_NOSECTOR is set), and is linked into a blockmap block or has the MF_NOBLOCKMAP flag set. Links should only be modified by the P_[Un]SetThingPosition () functions. Do not change the MF_NO? flags while a thing is valid.
50 ===============================================================================
59 boolean floatok; // if true, move would be ok if
60 // within tmfloorz - tmceilingz
62 fixed_t tmfloorz, tmceilingz, tmdropoffz;
65 // keep track of the line that lowers the ceiling, so missiles don't explode
66 // against sky hack walls
69 // keep track of special lines as they are hit, but don't process them
70 // until the move is proven valid
71 #define MAXSPECIALCROSS 8
72 line_t *spechit[MAXSPECIALCROSS];
75 mobj_t *onmobj; // generic global onmobj...used for landing on pods/players
79 ===============================================================================
83 ===============================================================================
94 boolean PIT_StompThing (mobj_t *thing)
98 if (!(thing->flags & MF_SHOOTABLE) )
101 blockdist = thing->radius + tmthing->radius;
102 if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist )
103 return true; // didn't hit it
105 if (thing == tmthing)
106 return true; // don't clip against self
108 if(!(tmthing->flags2&MF2_TELESTOMP))
109 { // Not allowed to stomp things
113 P_DamageMobj (thing, tmthing, tmthing, 10000);
127 boolean P_TeleportMove (mobj_t *thing, fixed_t x, fixed_t y)
129 int xl,xh,yl,yh,bx,by;
130 subsector_t *newsubsec;
133 // kill anything occupying the position
137 tmflags = thing->flags;
142 tmbbox[BOXTOP] = y + tmthing->radius;
143 tmbbox[BOXBOTTOM] = y - tmthing->radius;
144 tmbbox[BOXRIGHT] = x + tmthing->radius;
145 tmbbox[BOXLEFT] = x - tmthing->radius;
147 newsubsec = R_PointInSubsector (x,y);
151 // the base floor / ceiling is from the subsector that contains the
152 // point. Any contacted lines the step closer together will adjust them
154 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
155 tmceilingz = newsubsec->sector->ceilingheight;
156 tmfloorpic = newsubsec->sector->floorpic;
162 // stomp on any things contacted
164 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
165 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
166 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
167 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
169 for (bx=xl ; bx<=xh ; bx++)
170 for (by=yl ; by<=yh ; by++)
171 if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
175 // the move is ok, so link the thing into its new position
177 P_UnsetThingPosition (thing);
179 thing->floorz = tmfloorz;
180 thing->ceilingz = tmceilingz;
184 P_SetThingPosition (thing);
190 boolean PIT_ThrustStompThing (mobj_t *thing)
194 if (!(thing->flags & MF_SHOOTABLE) )
197 blockdist = thing->radius + tsthing->radius;
198 if ( abs(thing->x - tsthing->x) >= blockdist ||
199 abs(thing->y - tsthing->y) >= blockdist ||
200 (thing->z > tsthing->z+tsthing->height) )
201 return true; // didn't hit it
203 if (thing == tsthing)
204 return true; // don't clip against self
206 P_DamageMobj (thing, tsthing, tsthing, 10001);
207 tsthing->args[1] = 1; // Mark thrust thing as bloody
214 void PIT_ThrustSpike(mobj_t *actor)
216 int xl,xh,yl,yh,bx,by;
221 x0 = actor->x - actor->info->radius;
222 x2 = actor->x + actor->info->radius;
223 y0 = actor->y - actor->info->radius;
224 y2 = actor->y + actor->info->radius;
226 xl = (x0 - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
227 xh = (x2 - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
228 yl = (y0 - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
229 yh = (y2 - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
231 // stomp on any things contacted
232 for (bx=xl ; bx<=xh ; bx++)
233 for (by=yl ; by<=yh ; by++)
234 P_BlockThingsIterator(bx,by,PIT_ThrustStompThing);
240 ===============================================================================
242 MOVEMENT ITERATOR FUNCTIONS
244 ===============================================================================
252 = Adjusts tmfloorz and tmceilingz as lines are contacted
256 boolean PIT_CheckLine(line_t *ld)
258 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
259 || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
260 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
261 || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
265 if(P_BoxOnLineSide(tmbbox, ld) != -1)
270 // a line has been hit
273 = The moving thing's destination position will cross the given line.
274 = If this should not be allowed, return false.
275 = If the line is special, keep track of it to process later if the move
276 = is proven ok. NOTE: specials are NOT sorted by order, so two special lines
277 = that are only 8 pixels apart could be crossed in either order.
282 if (tmthing->flags2&MF2_BLASTED)
284 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
286 CheckForPushSpecial(ld, 0, tmthing);
289 if(!(tmthing->flags&MF_MISSILE))
291 if(ld->flags&ML_BLOCKING)
292 { // Explicitly blocking everything
293 if (tmthing->flags2&MF2_BLASTED)
295 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
297 CheckForPushSpecial(ld, 0, tmthing);
300 if(!tmthing->player && ld->flags&ML_BLOCKMONSTERS)
301 { // Block monsters only
302 if (tmthing->flags2&MF2_BLASTED)
304 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
309 P_LineOpening(ld); // set openrange, opentop, openbottom
310 // adjust floor / ceiling heights
311 if(opentop < tmceilingz)
313 tmceilingz = opentop;
316 if(openbottom > tmfloorz)
318 tmfloorz = openbottom;
320 if(lowfloor < tmdropoffz)
322 tmdropoffz = lowfloor;
325 { // Contacted a special line, add it to the list
326 spechit[numspechit] = ld;
332 //---------------------------------------------------------------------------
334 // FUNC PIT_CheckThing
336 //---------------------------------------------------------------------------
338 boolean PIT_CheckThing(mobj_t *thing)
344 if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE)))
348 blockdist = thing->radius+tmthing->radius;
349 if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist)
350 { // Didn't hit thing
354 { // Don't clip against self
357 BlockingMobj = thing;
358 if(tmthing->flags2&MF2_PASSMOBJ)
359 { // check if a mobj passed over/under another object
360 if(tmthing->type == MT_BISHOP && thing->type == MT_BISHOP)
361 { // don't let bishops fly over other bishops
364 if(tmthing->z >= thing->z+thing->height
365 && !(thing->flags&MF_SPECIAL))
369 else if(tmthing->z+tmthing->height < thing->z
370 && !(thing->flags&MF_SPECIAL))
375 // Check for skulls slamming into things
376 if(tmthing->flags&MF_SKULLFLY)
378 if(tmthing->type == MT_MINOTAUR)
380 // Slamming minotaurs shouldn't move non-creatures
381 if (!(thing->flags&MF_COUNTKILL))
386 else if(tmthing->type == MT_HOLY_FX)
388 if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
390 if(netgame && !deathmatch && thing->player)
391 { // don't attack other co-op players
394 if(thing->flags2&MF2_REFLECTIVE
395 && (thing->player || thing->flags2&MF2_BOSS))
397 tmthing->special1 = (int)tmthing->target;
398 tmthing->target = thing;
401 if(thing->flags&MF_COUNTKILL || thing->player)
403 tmthing->special1 = (int)thing;
408 if(thing->player || thing->flags2&MF2_BOSS)
411 // ghost burns out faster when attacking players/bosses
412 tmthing->health -= 6;
414 P_DamageMobj(thing, tmthing, tmthing->target, damage);
417 P_SpawnMobj(tmthing->x, tmthing->y, tmthing->z,
419 S_StartSound(tmthing, SFX_SPIRIT_ATTACK);
420 if(thing->flags&MF_COUNTKILL && P_Random() < 128
421 && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT))
423 if ((thing->type == MT_CENTAUR) ||
424 (thing->type == MT_CENTAURLEADER) ||
425 (thing->type == MT_ETTIN))
427 S_StartSound(thing, SFX_PUPPYBEAT);
432 if(thing->health <= 0)
434 tmthing->special1 = 0;
439 damage = ((P_Random()%8)+1)*tmthing->damage;
440 P_DamageMobj(thing, tmthing, tmthing, damage);
441 tmthing->flags &= ~MF_SKULLFLY;
442 tmthing->momx = tmthing->momy = tmthing->momz = 0;
443 P_SetMobjState(tmthing, tmthing->info->seestate);
446 // Check for blasted thing running into another
447 if(tmthing->flags2&MF2_BLASTED && thing->flags&MF_SHOOTABLE)
449 if (!(thing->flags2&MF2_BOSS) &&
450 (thing->flags&MF_COUNTKILL))
452 thing->momx += tmthing->momx;
453 thing->momy += tmthing->momy;
454 if ((thing->momx + thing->momy) > 3*FRACUNIT)
456 damage = (tmthing->info->mass/100)+1;
457 P_DamageMobj(thing, tmthing, tmthing, damage);
458 damage = (thing->info->mass/100)+1;
459 P_DamageMobj(tmthing, thing, thing, damage>>2);
465 if(tmthing->flags&MF_MISSILE)
467 // Check for a non-shootable mobj
468 if(thing->flags2&MF2_NONSHOOTABLE)
472 // Check if it went over / under
473 if(tmthing->z > thing->z+thing->height)
477 if(tmthing->z+tmthing->height < thing->z)
481 if(tmthing->flags2&MF2_FLOORBOUNCE)
483 if(tmthing->target == thing || !(thing->flags&MF_SOLID))
492 if(tmthing->type == MT_LIGHTNING_FLOOR
493 || tmthing->type == MT_LIGHTNING_CEILING)
495 if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
497 if(thing->info->mass != MAXINT)
499 thing->momx += tmthing->momx>>4;
500 thing->momy += tmthing->momy>>4;
502 if((!thing->player && !(thing->flags2&MF2_BOSS))
505 if(thing->type == MT_CENTAUR
506 || thing->type == MT_CENTAURLEADER)
507 { // Lightning does more damage to centaurs
508 P_DamageMobj(thing, tmthing, tmthing->target, 9);
512 P_DamageMobj(thing, tmthing, tmthing->target, 3);
514 if(!(S_GetSoundPlayingInfo(tmthing,
515 SFX_MAGE_LIGHTNING_ZAP)))
517 S_StartSound(tmthing, SFX_MAGE_LIGHTNING_ZAP);
519 if(thing->flags&MF_COUNTKILL && P_Random() < 64
520 && !S_GetSoundPlayingInfo(thing, SFX_PUPPYBEAT))
522 if ((thing->type == MT_CENTAUR) ||
523 (thing->type == MT_CENTAURLEADER) ||
524 (thing->type == MT_ETTIN))
526 S_StartSound(thing, SFX_PUPPYBEAT);
531 if(tmthing->health <= 0 || thing->health <= 0)
535 if(tmthing->type == MT_LIGHTNING_FLOOR)
538 && !((mobj_t *)tmthing->special2)->special1)
540 ((mobj_t *)tmthing->special2)->special1 =
544 else if(!tmthing->special1)
546 tmthing->special1 = (int)thing;
549 return true; // lightning zaps through all sprites
551 else if(tmthing->type == MT_LIGHTNING_ZAP)
555 if(thing->flags&MF_SHOOTABLE && thing != tmthing->target)
557 lmo = (mobj_t *)tmthing->special2;
560 if(lmo->type == MT_LIGHTNING_FLOOR)
563 && !((mobj_t *)lmo->special2)->special1)
565 ((mobj_t *)lmo->special2)->special1 = (int)thing;
568 else if(!lmo->special1)
570 lmo->special1 = (int)thing;
579 else if(tmthing->type == MT_MSTAFF_FX2 && thing != tmthing->target)
581 if(!thing->player && !(thing->flags2&MF2_BOSS))
585 case MT_FIGHTER_BOSS: // these not flagged boss
586 case MT_CLERIC_BOSS: // so they can be blasted
590 P_DamageMobj(thing, tmthing, tmthing->target, 10);
596 if(tmthing->target && tmthing->target->type == thing->type)
597 { // Don't hit same species as originator
598 if(thing == tmthing->target)
599 { // Don't missile self
603 { // Hit same species as originator, explode, no damage
607 if(!(thing->flags&MF_SHOOTABLE))
608 { // Didn't do any damage
609 return!(thing->flags&MF_SOLID);
611 if(tmthing->flags2&MF2_RIP)
613 if (!(thing->flags&MF_NOBLOOD) &&
614 !(thing->flags2&MF2_REFLECTIVE) &&
615 !(thing->flags2&MF2_INVULNERABLE))
616 { // Ok to spawn some blood
617 P_RipperBlood(tmthing);
619 //S_StartSound(tmthing, sfx_ripslop);
620 damage = ((P_Random()&3)+2)*tmthing->damage;
621 P_DamageMobj(thing, tmthing, tmthing->target, damage);
622 if(thing->flags2&MF2_PUSHABLE
623 && !(tmthing->flags2&MF2_CANNOTPUSH))
625 thing->momx += tmthing->momx>>2;
626 thing->momy += tmthing->momy>>2;
632 damage = ((P_Random()%8)+1)*tmthing->damage;
635 if (!(thing->flags&MF_NOBLOOD) &&
636 !(thing->flags2&MF2_REFLECTIVE) &&
637 !(thing->flags2&MF2_INVULNERABLE) &&
638 !(tmthing->type == MT_TELOTHER_FX1) &&
639 !(tmthing->type == MT_TELOTHER_FX2) &&
640 !(tmthing->type == MT_TELOTHER_FX3) &&
641 !(tmthing->type == MT_TELOTHER_FX4) &&
642 !(tmthing->type == MT_TELOTHER_FX5) &&
645 P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing);
647 P_DamageMobj(thing, tmthing, tmthing->target, damage);
651 if(thing->flags2&MF2_PUSHABLE && !(tmthing->flags2&MF2_CANNOTPUSH))
653 thing->momx += tmthing->momx>>2;
654 thing->momy += tmthing->momy>>2;
656 // Check for special thing
657 if(thing->flags&MF_SPECIAL)
659 solid = thing->flags&MF_SOLID;
660 if(tmflags&MF_PICKUP)
661 { // Can be picked up by tmthing
662 P_TouchSpecialThing(thing, tmthing); // Can remove thing
666 return(!(thing->flags&MF_SOLID));
669 //---------------------------------------------------------------------------
673 //---------------------------------------------------------------------------
675 boolean PIT_CheckOnmobjZ(mobj_t *thing)
679 if(!(thing->flags&(MF_SOLID|MF_SPECIAL|MF_SHOOTABLE)))
683 blockdist = thing->radius+tmthing->radius;
684 if(abs(thing->x-tmx) >= blockdist || abs(thing->y-tmy) >= blockdist)
685 { // Didn't hit thing
689 { // Don't clip against self
692 if(tmthing->z > thing->z+thing->height)
696 else if(tmthing->z+tmthing->height < thing->z)
700 if(thing->flags&MF_SOLID)
704 return(!(thing->flags&MF_SOLID));
708 ===============================================================================
712 ===============================================================================
715 //----------------------------------------------------------------------------
717 // FUNC P_TestMobjLocation
719 // Returns true if the mobj is not blocked by anything at its current
720 // location, otherwise returns false.
722 //----------------------------------------------------------------------------
724 boolean P_TestMobjLocation(mobj_t *mobj)
729 mobj->flags &= ~MF_PICKUP;
730 if(P_CheckPosition(mobj, mobj->x, mobj->y))
731 { // XY is ok, now check Z
733 if((mobj->z < mobj->floorz)
734 || (mobj->z+mobj->height > mobj->ceilingz))
749 = This is purely informative, nothing is modified (except things picked up)
752 a mobj_t (can be valid or invalid)
753 a position to be checked (doesn't need to be related to the mobj_t->x,y)
756 special things are touched if MF_PICKUP
757 early out on solid lines?
763 tmdropoffz = the lowest point contacted (monsters won't move to a dropoff)
766 mobj_t *BlockingMobj = pointer to thing that blocked position (NULL if not
767 blocked, or blocked by a line).
772 boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y)
774 int xl,xh,yl,yh,bx,by;
775 subsector_t *newsubsec;
778 tmflags = thing->flags;
783 tmbbox[BOXTOP] = y + tmthing->radius;
784 tmbbox[BOXBOTTOM] = y - tmthing->radius;
785 tmbbox[BOXRIGHT] = x + tmthing->radius;
786 tmbbox[BOXLEFT] = x - tmthing->radius;
788 newsubsec = R_PointInSubsector (x,y);
792 // the base floor / ceiling is from the subsector that contains the
793 // point. Any contacted lines the step closer together will adjust them
795 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
796 tmceilingz = newsubsec->sector->ceilingheight;
797 tmfloorpic = newsubsec->sector->floorpic;
802 if(tmflags&MF_NOCLIP && !(tmflags&MF_SKULLFLY))
808 // check things first, possibly picking things up
809 // the bounding box is extended by MAXRADIUS because mobj_ts are grouped
810 // into mapblocks based on their origin point, and can overlap into adjacent
811 // blocks by up to MAXRADIUS units
813 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
814 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
815 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
816 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
819 for (bx=xl ; bx<=xh ; bx++)
820 for (by=yl ; by<=yh ; by++)
821 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
826 if(tmflags&MF_NOCLIP)
832 xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
833 xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
834 yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
835 yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
837 for (bx=xl ; bx<=xh ; bx++)
838 for (by=yl ; by<=yh ; by++)
839 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
844 //=============================================================================
846 // P_CheckOnmobj(mobj_t *thing)
848 // Checks if the new Z position is legal
849 //=============================================================================
851 mobj_t *P_CheckOnmobj(mobj_t *thing)
853 int xl,xh,yl,yh,bx,by;
854 subsector_t *newsubsec;
862 tmflags = thing->flags;
863 oldmo = *thing; // save the old mobj before the fake zmovement
864 P_FakeZMovement(tmthing);
869 tmbbox[BOXTOP] = y + tmthing->radius;
870 tmbbox[BOXBOTTOM] = y - tmthing->radius;
871 tmbbox[BOXRIGHT] = x + tmthing->radius;
872 tmbbox[BOXLEFT] = x - tmthing->radius;
874 newsubsec = R_PointInSubsector (x,y);
878 // the base floor / ceiling is from the subsector that contains the
879 // point. Any contacted lines the step closer together will adjust them
881 tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
882 tmceilingz = newsubsec->sector->ceilingheight;
883 tmfloorpic = newsubsec->sector->floorpic;
888 if ( tmflags & MF_NOCLIP )
892 // check things first, possibly picking things up
893 // the bounding box is extended by MAXRADIUS because mobj_ts are grouped
894 // into mapblocks based on their origin point, and can overlap into adjacent
895 // blocks by up to MAXRADIUS units
897 xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
898 xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
899 yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
900 yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
902 for (bx=xl ; bx<=xh ; bx++)
903 for (by=yl ; by<=yh ; by++)
904 if (!P_BlockThingsIterator(bx,by,PIT_CheckOnmobjZ))
913 //=============================================================================
917 // Fake the zmovement so that we can check if a move is legal
918 //=============================================================================
920 void P_FakeZMovement(mobj_t *mo)
928 if(mo->flags&MF_FLOAT && mo->target)
929 { // float down towards target if too close
930 if(!(mo->flags&MF_SKULLFLY) && !(mo->flags&MF_INFLOAT))
932 dist = P_AproxDistance(mo->x-mo->target->x, mo->y-mo->target->y);
933 delta =( mo->target->z+(mo->height>>1))-mo->z;
934 if (delta < 0 && dist < -(delta*3))
936 else if (delta > 0 && dist < (delta*3))
940 if(mo->player && mo->flags2&MF2_FLY && !(mo->z <= mo->floorz)
943 mo->z += finesine[(FINEANGLES/20*leveltime>>2)&FINEMASK];
949 if(mo->z <= mo->floorz)
956 if(mo->flags&MF_SKULLFLY)
957 { // The skull slammed into something
958 mo->momz = -mo->momz;
960 if(mo->info->crashstate && (mo->flags&MF_CORPSE))
965 else if(mo->flags2&MF2_LOGRAV)
968 mo->momz = -(GRAVITY>>3)*2;
970 mo->momz -= GRAVITY>>3;
972 else if (! (mo->flags & MF_NOGRAVITY) )
975 mo->momz = -GRAVITY*2;
980 if (mo->z + mo->height > mo->ceilingz)
984 mo->z = mo->ceilingz - mo->height;
985 if (mo->flags & MF_SKULLFLY)
986 { // the skull slammed into something
987 mo->momz = -mo->momz;
992 //===========================================================================
994 // CheckForPushSpecial
996 //===========================================================================
998 static void CheckForPushSpecial(line_t *line, int side, mobj_t *mobj)
1002 if(mobj->flags2&MF2_PUSHWALL)
1004 P_ActivateLine(line, mobj, side, SPAC_PUSH);
1006 else if(mobj->flags2&MF2_IMPACT)
1008 P_ActivateLine(line, mobj, side, SPAC_IMPACT);
1018 = Attempt to move to a new position, crossing special lines unless MF_TELEPORT
1024 boolean P_TryMove (mobj_t *thing, fixed_t x, fixed_t y)
1031 if(!P_CheckPosition(thing, x, y))
1032 { // Solid wall or thing
1033 if(!BlockingMobj || BlockingMobj->player
1038 else if (BlockingMobj->z+BlockingMobj->height-thing->z
1040 || (BlockingMobj->subsector->sector->ceilingheight
1041 -(BlockingMobj->z+BlockingMobj->height) < thing->height)
1042 || (tmceilingz-(BlockingMobj->z+BlockingMobj->height)
1048 if(!(thing->flags&MF_NOCLIP))
1050 if(tmceilingz-tmfloorz < thing->height)
1055 if(!(thing->flags&MF_TELEPORT)
1056 && tmceilingz-thing->z < thing->height
1057 && thing->type != MT_LIGHTNING_CEILING
1058 && !(thing->flags2&MF2_FLY))
1059 { // mobj must lower itself to fit
1062 if(thing->flags2&MF2_FLY)
1064 if(thing->z+thing->height > tmceilingz)
1066 thing->momz = -8*FRACUNIT;
1069 else if(thing->z < tmfloorz && tmfloorz-tmdropoffz > 24*FRACUNIT)
1071 thing->momz = 8*FRACUNIT;
1075 if(!(thing->flags&MF_TELEPORT)
1076 // The Minotaur floor fire (MT_MNTRFX2) can step up any amount
1077 && thing->type != MT_MNTRFX2 && thing->type != MT_LIGHTNING_FLOOR
1078 && tmfloorz-thing->z > 24*FRACUNIT)
1082 if (!(thing->flags&(MF_DROPOFF|MF_FLOAT)) &&
1083 (tmfloorz-tmdropoffz > 24*FRACUNIT) &&
1084 !(thing->flags2&MF2_BLASTED))
1085 { // Can't move over a dropoff unless it's been blasted
1088 if(thing->flags2&MF2_CANTLEAVEFLOORPIC
1089 && (tmfloorpic != thing->subsector->sector->floorpic
1090 || tmfloorz-thing->z != 0))
1091 { // must stay within a sector of a certain floor type
1097 // the move is ok, so link the thing into its new position
1099 P_UnsetThingPosition (thing);
1103 thing->floorz = tmfloorz;
1104 thing->ceilingz = tmceilingz;
1105 thing->floorpic = tmfloorpic;
1109 P_SetThingPosition (thing);
1111 if(thing->flags2&MF2_FLOORCLIP)
1113 if(thing->z == thing->subsector->sector->floorheight
1114 && P_GetThingFloorType(thing) >= FLOOR_LIQUID)
1116 thing->floorclip = 10*FRACUNIT;
1120 thing->floorclip = 0;
1125 // if any special lines were hit, do the effect
1127 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
1129 while (numspechit > 0)
1132 // see if the line was crossed
1133 ld = spechit[numspechit];
1134 side = P_PointOnLineSide (thing->x, thing->y, ld);
1135 oldside = P_PointOnLineSide (oldx, oldy, ld);
1136 if (side != oldside)
1142 P_ActivateLine(ld, thing, oldside, SPAC_CROSS);
1144 else if(thing->flags2&MF2_MCROSS)
1146 P_ActivateLine(ld, thing, oldside, SPAC_MCROSS);
1148 else if(thing->flags2&MF2_PCROSS)
1150 P_ActivateLine(ld, thing, oldside, SPAC_PCROSS);
1159 if(!(thing->flags&(MF_TELEPORT|MF_NOCLIP)))
1163 if (tmthing->flags2&MF2_BLASTED)
1165 P_DamageMobj(tmthing, NULL, NULL, tmthing->info->mass>>5);
1167 numSpecHitTemp = numspechit;
1168 while (numSpecHitTemp > 0)
1171 // see if the line was crossed
1172 ld = spechit[numSpecHitTemp];
1173 side = P_PointOnLineSide (thing->x, thing->y, ld);
1174 CheckForPushSpecial(ld, side, thing);
1185 = Takes a valid thing and adjusts the thing->floorz, thing->ceilingz,
1186 = anf possibly thing->z
1188 = This is called for all nearby monsters whenever a sector changes height
1190 = If the thing doesn't fit, the z will be set to the lowest value and
1191 = false will be returned
1195 boolean P_ThingHeightClip (mobj_t *thing)
1199 onfloor = (thing->z == thing->floorz);
1201 P_CheckPosition (thing, thing->x, thing->y);
1202 // what about stranding a monster partially off an edge?
1204 thing->floorz = tmfloorz;
1205 thing->ceilingz = tmceilingz;
1206 thing->floorpic = tmfloorpic;
1209 { // walking monsters rise and fall with the floor
1210 if((thing->z-thing->floorz < 9*FRACUNIT)
1211 || (thing->flags&MF_NOGRAVITY))
1213 thing->z = thing->floorz;
1217 { // don't adjust a floating monster unless forced to
1218 if (thing->z+thing->height > thing->ceilingz)
1219 thing->z = thing->ceilingz - thing->height;
1222 if (thing->ceilingz - thing->floorz < thing->height)
1230 ==============================================================================
1234 Allows the player to slide along any angled walls
1236 ==============================================================================
1239 fixed_t bestslidefrac, secondslidefrac;
1240 line_t *bestslideline, *secondslideline;
1243 fixed_t tmxmove, tmymove;
1250 = Adjusts the xmove / ymove so that the next move will slide along the wall
1254 void P_HitSlideLine (line_t *ld)
1257 angle_t lineangle, moveangle, deltaangle;
1258 fixed_t movelen, newlen;
1261 if (ld->slopetype == ST_HORIZONTAL)
1266 if (ld->slopetype == ST_VERTICAL)
1272 side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
1274 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
1276 lineangle += ANG180;
1277 moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
1278 deltaangle = moveangle-lineangle;
1279 if (deltaangle > ANG180)
1280 deltaangle += ANG180;
1281 // I_Error ("SlideLine: ang>ANG180");
1283 lineangle >>= ANGLETOFINESHIFT;
1284 deltaangle >>= ANGLETOFINESHIFT;
1286 movelen = P_AproxDistance (tmxmove, tmymove);
1287 newlen = FixedMul (movelen, finecosine[deltaangle]);
1288 tmxmove = FixedMul (newlen, finecosine[lineangle]);
1289 tmymove = FixedMul (newlen, finesine[lineangle]);
1300 boolean PTR_SlideTraverse (intercept_t *in)
1305 I_Error ("PTR_SlideTraverse: not a line?");
1308 if ( ! (li->flags & ML_TWOSIDED) )
1310 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1311 return true; // don't hit the back side
1315 P_LineOpening (li); // set openrange, opentop, openbottom
1316 if (openrange < slidemo->height)
1317 goto isblocking; // doesn't fit
1319 if (opentop - slidemo->z < slidemo->height)
1320 goto isblocking; // mobj is too high
1322 if (openbottom - slidemo->z > 24*FRACUNIT )
1323 goto isblocking; // too big a step up
1325 return true; // this line doesn't block movement
1327 // the line does block movement, see if it is closer than best so far
1329 if (in->frac < bestslidefrac)
1331 secondslidefrac = bestslidefrac;
1332 secondslideline = bestslideline;
1333 bestslidefrac = in->frac;
1337 return false; // stop
1346 = The momx / momy move is bad, so try to slide along a wall
1348 = Find the first line hit, move flush to it, and slide along it
1350 = This is a kludgy mess.
1354 void P_SlideMove (mobj_t *mo)
1356 fixed_t leadx, leady;
1357 fixed_t trailx, traily;
1364 if (++hitcount == 3)
1365 goto stairstep; // don't loop forever
1368 // trace along the three leading corners
1372 leadx = mo->x + mo->radius;
1373 trailx = mo->x - mo->radius;
1377 leadx = mo->x - mo->radius;
1378 trailx = mo->x + mo->radius;
1383 leady = mo->y + mo->radius;
1384 traily = mo->y - mo->radius;
1388 leady = mo->y - mo->radius;
1389 traily = mo->y + mo->radius;
1392 bestslidefrac = FRACUNIT+1;
1394 P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
1395 PT_ADDLINES, PTR_SlideTraverse );
1396 P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
1397 PT_ADDLINES, PTR_SlideTraverse );
1398 P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
1399 PT_ADDLINES, PTR_SlideTraverse );
1402 // move up to the wall
1404 if(bestslidefrac == FRACUNIT+1)
1405 { // the move must have hit the middle, so stairstep
1407 if(!P_TryMove(mo, mo->x, mo->y+mo->momy))
1409 P_TryMove(mo, mo->x+mo->momx, mo->y);
1414 bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit
1415 if (bestslidefrac > 0)
1417 newx = FixedMul (mo->momx, bestslidefrac);
1418 newy = FixedMul (mo->momy, bestslidefrac);
1419 if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
1424 // now continue along the wall
1426 bestslidefrac = FRACUNIT-(bestslidefrac+0x800); // remainder
1427 if (bestslidefrac > FRACUNIT)
1428 bestslidefrac = FRACUNIT;
1429 if (bestslidefrac <= 0)
1432 tmxmove = FixedMul (mo->momx, bestslidefrac);
1433 tmymove = FixedMul (mo->momy, bestslidefrac);
1435 P_HitSlideLine (bestslideline); // clip the moves
1440 if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
1446 //============================================================================
1448 // PTR_BounceTraverse
1450 //============================================================================
1452 boolean PTR_BounceTraverse(intercept_t *in)
1457 I_Error ("PTR_BounceTraverse: not a line?");
1460 if (!(li->flags&ML_TWOSIDED))
1462 if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
1463 return true; // don't hit the back side
1464 goto bounceblocking;
1467 P_LineOpening (li); // set openrange, opentop, openbottom
1468 if (openrange < slidemo->height)
1469 goto bounceblocking; // doesn't fit
1471 if (opentop - slidemo->z < slidemo->height)
1472 goto bounceblocking; // mobj is too high
1473 return true; // this line doesn't block movement
1475 // the line does block movement, see if it is closer than best so far
1477 if (in->frac < bestslidefrac)
1479 secondslidefrac = bestslidefrac;
1480 secondslideline = bestslideline;
1481 bestslidefrac = in->frac;
1484 return false; // stop
1487 //============================================================================
1491 //============================================================================
1493 void P_BounceWall(mobj_t *mo)
1495 fixed_t leadx, leady;
1497 angle_t lineangle, moveangle, deltaangle;
1503 // trace along the three leading corners
1507 leadx = mo->x+mo->radius;
1511 leadx = mo->x-mo->radius;
1515 leady = mo->y+mo->radius;
1519 leady = mo->y-mo->radius;
1521 bestslidefrac = FRACUNIT+1;
1522 P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy,
1523 PT_ADDLINES, PTR_BounceTraverse);
1525 side = P_PointOnLineSide(mo->x, mo->y, bestslideline);
1526 lineangle = R_PointToAngle2(0, 0, bestslideline->dx,
1529 lineangle += ANG180;
1530 moveangle = R_PointToAngle2(0, 0, mo->momx, mo->momy);
1531 deltaangle = (2*lineangle)-moveangle;
1532 // if (deltaangle > ANG180)
1533 // deltaangle += ANG180;
1534 // I_Error ("SlideLine: ang>ANG180");
1536 lineangle >>= ANGLETOFINESHIFT;
1537 deltaangle >>= ANGLETOFINESHIFT;
1539 movelen = P_AproxDistance(mo->momx, mo->momy);
1540 movelen = FixedMul(movelen, 0.75*FRACUNIT); // friction
1541 if (movelen < FRACUNIT) movelen = 2*FRACUNIT;
1542 mo->momx = FixedMul(movelen, finecosine[deltaangle]);
1543 mo->momy = FixedMul(movelen, finesine[deltaangle]);
1548 ==============================================================================
1552 ==============================================================================
1556 mobj_t *PuffSpawned;
1557 mobj_t *linetarget; // who got hit (or NULL)
1559 fixed_t shootz; // height if not aiming up or down
1560 // ???: use slope for monsters?
1562 fixed_t attackrange;
1566 extern fixed_t topslope, bottomslope; // slopes to top and bottom of target
1569 ===============================================================================
1573 = Sets linetaget and aimslope when a target is aimed at
1574 ===============================================================================
1577 boolean PTR_AimTraverse (intercept_t *in)
1581 fixed_t slope, thingtopslope, thingbottomslope;
1587 if ( !(li->flags & ML_TWOSIDED) )
1588 return false; // stop
1590 // crosses a two sided line
1591 // a two sided line will restrict the possible target ranges
1594 if (openbottom >= opentop)
1595 return false; // stop
1597 dist = FixedMul (attackrange, in->frac);
1599 if (li->frontsector->floorheight != li->backsector->floorheight)
1601 slope = FixedDiv (openbottom - shootz , dist);
1602 if (slope > bottomslope)
1603 bottomslope = slope;
1606 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1608 slope = FixedDiv (opentop - shootz , dist);
1609 if (slope < topslope)
1613 if (topslope <= bottomslope)
1614 return false; // stop
1616 return true; // shot continues
1623 if (th == shootthing)
1624 return true; // can't shoot self
1625 if(!(th->flags&MF_SHOOTABLE))
1626 { // corpse or something
1629 if(th->player && netgame && !deathmatch)
1630 { // don't aim at fellow co-op players
1634 // check angles to see if the thing can be aimed at
1636 dist = FixedMul (attackrange, in->frac);
1637 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1638 if (thingtopslope < bottomslope)
1639 return true; // shot over the thing
1640 thingbottomslope = FixedDiv (th->z - shootz, dist);
1641 if (thingbottomslope > topslope)
1642 return true; // shot under the thing
1645 // this thing can be hit!
1647 if (thingtopslope > topslope)
1648 thingtopslope = topslope;
1649 if (thingbottomslope < bottomslope)
1650 thingbottomslope = bottomslope;
1652 aimslope = (thingtopslope+thingbottomslope)/2;
1655 return false; // don't go any farther
1660 ==============================================================================
1664 ==============================================================================
1667 boolean PTR_ShootTraverse (intercept_t *in)
1675 fixed_t thingtopslope, thingbottomslope;
1677 extern mobj_t LavaInflictor;
1684 P_ActivateLine(li, shootthing, 0, SPAC_IMPACT);
1685 // P_ShootSpecialLine (shootthing, li);
1687 if ( !(li->flags & ML_TWOSIDED) )
1691 // crosses a two sided line
1695 dist = FixedMul (attackrange, in->frac);
1697 if (li->frontsector->floorheight != li->backsector->floorheight)
1699 slope = FixedDiv (openbottom - shootz , dist);
1700 if (slope > aimslope)
1704 if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1706 slope = FixedDiv (opentop - shootz , dist);
1707 if (slope < aimslope)
1711 return true; // shot continues
1716 // position a bit closer
1717 frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1718 x = trace.x + FixedMul (trace.dx, frac);
1719 y = trace.y + FixedMul (trace.dy, frac);
1720 z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1722 if (li->frontsector->ceilingpic == skyflatnum)
1724 if (z > li->frontsector->ceilingheight)
1725 return false; // don't shoot the sky!
1726 if (li->backsector && li->backsector->ceilingpic == skyflatnum)
1727 return false; // it's a sky hack wall
1730 P_SpawnPuff (x,y,z);
1731 return false; // don't go any farther
1738 if (th == shootthing)
1739 return true; // can't shoot self
1740 if (!(th->flags&MF_SHOOTABLE))
1741 return true; // corpse or something
1744 // check for physical attacks on a ghost
1746 /* FIX: Impliment Heretic 2 weapons here
1747 if(th->flags&MF_SHADOW && shootthing->player->readyweapon == wp_staff)
1753 // check angles to see if the thing can be aimed at
1754 dist = FixedMul (attackrange, in->frac);
1755 thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
1756 if (thingtopslope < aimslope)
1757 return true; // shot over the thing
1758 thingbottomslope = FixedDiv (th->z - shootz, dist);
1759 if (thingbottomslope > aimslope)
1760 return true; // shot under the thing
1765 // position a bit closer
1766 frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1767 x = trace.x + FixedMul(trace.dx, frac);
1768 y = trace.y + FixedMul(trace.dy, frac);
1769 z = shootz + FixedMul(aimslope, FixedMul(frac, attackrange));
1770 P_SpawnPuff(x, y, z);
1773 if (!(in->d.thing->flags&MF_NOBLOOD) &&
1774 !(in->d.thing->flags2&MF2_INVULNERABLE))
1776 if(PuffType == MT_AXEPUFF || PuffType == MT_AXEPUFF_GLOW)
1778 P_BloodSplatter2(x, y, z, in->d.thing);
1780 if(P_Random() < 192)
1782 P_BloodSplatter(x, y, z, in->d.thing);
1785 if(PuffType == MT_FLAMEPUFF2)
1786 { // Cleric FlameStrike does fire damage
1787 P_DamageMobj(th, &LavaInflictor, shootthing, la_damage);
1791 P_DamageMobj(th, shootthing, shootthing, la_damage);
1794 return(false); // don't go any farther
1805 fixed_t P_AimLineAttack (mobj_t *t1, angle_t angle, fixed_t distance)
1809 angle >>= ANGLETOFINESHIFT;
1811 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1812 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1813 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1814 topslope = 100*FRACUNIT/160; // can't shoot outside view angles
1815 bottomslope = -100*FRACUNIT/160;
1816 attackrange = distance;
1819 P_PathTraverse ( t1->x, t1->y, x2, y2
1820 , PT_ADDLINES|PT_ADDTHINGS, PTR_AimTraverse );
1834 = if damage == 0, it is just a test trace that will leave linetarget set
1839 void P_LineAttack (mobj_t *t1, angle_t angle, fixed_t distance, fixed_t slope, int damage)
1843 angle >>= ANGLETOFINESHIFT;
1846 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1847 y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1848 shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1849 shootz -= t1->floorclip;
1850 attackrange = distance;
1853 if(P_PathTraverse(t1->x, t1->y, x2, y2, PT_ADDLINES|PT_ADDTHINGS,
1859 S_StartSound(t1, SFX_FIGHTER_PUNCH_MISS);
1863 case MT_AXEPUFF_GLOW:
1864 S_StartSound(t1, SFX_FIGHTER_HAMMER_MISS);
1867 P_SpawnPuff(x2, y2, shootz+FixedMul(slope, distance));
1876 ==============================================================================
1880 ==============================================================================
1885 boolean PTR_UseTraverse (intercept_t *in)
1890 if (!in->d.line->special)
1892 P_LineOpening (in->d.line);
1895 if(usething->player)
1897 switch(usething->player->class)
1899 case PCLASS_FIGHTER:
1900 sound = SFX_PLAYER_FIGHTER_FAILED_USE;
1903 sound = SFX_PLAYER_CLERIC_FAILED_USE;
1906 sound = SFX_PLAYER_MAGE_FAILED_USE;
1909 sound = SFX_PIG_ACTIVE1;
1915 S_StartSound(usething, sound);
1917 return false; // can't use through a wall
1919 if(usething->player)
1921 pheight = usething->z + (usething->height/2);
1922 if ((opentop < pheight) || (openbottom > pheight))
1924 switch(usething->player->class)
1926 case PCLASS_FIGHTER:
1927 sound = SFX_PLAYER_FIGHTER_FAILED_USE;
1930 sound = SFX_PLAYER_CLERIC_FAILED_USE;
1933 sound = SFX_PLAYER_MAGE_FAILED_USE;
1936 sound = SFX_PIG_ACTIVE1;
1942 S_StartSound(usething, sound);
1945 return true ; // not a special line, but keep checking
1948 if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1949 return false; // don't use back sides
1951 // P_UseSpecialLine (usething, in->d.line);
1952 P_ActivateLine(in->d.line, usething, 0, SPAC_USE);
1954 return false; // can't use for than one special line in a row
1963 = Looks for special lines in front of the player to activate
1967 void P_UseLines (player_t *player)
1970 fixed_t x1, y1, x2, y2;
1972 usething = player->mo;
1974 angle = player->mo->angle >> ANGLETOFINESHIFT;
1977 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1978 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1980 P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1983 //==========================================================================
1985 // PTR_PuzzleItemTraverse
1987 //==========================================================================
1989 #define USE_PUZZLE_ITEM_SPECIAL 129
1991 static mobj_t *PuzzleItemUser;
1992 static int PuzzleItemType;
1993 static boolean PuzzleActivated;
1995 boolean PTR_PuzzleItemTraverse(intercept_t *in)
2002 if(in->d.line->special != USE_PUZZLE_ITEM_SPECIAL)
2004 P_LineOpening(in->d.line);
2008 if(PuzzleItemUser->player)
2010 switch(PuzzleItemUser->player->class)
2012 case PCLASS_FIGHTER:
2013 sound = SFX_PUZZLE_FAIL_FIGHTER;
2016 sound = SFX_PUZZLE_FAIL_CLERIC;
2019 sound = SFX_PUZZLE_FAIL_MAGE;
2026 S_StartSound(PuzzleItemUser, sound);
2027 return false; // can't use through a wall
2029 return true; // Continue searching
2031 if(P_PointOnLineSide(PuzzleItemUser->x, PuzzleItemUser->y,
2033 { // Don't use back sides
2036 if(PuzzleItemType != in->d.line->arg1)
2037 { // Item type doesn't match
2040 P_StartACS(in->d.line->arg2, 0, &in->d.line->arg3,
2041 PuzzleItemUser, in->d.line, 0);
2042 in->d.line->special = 0;
2043 PuzzleActivated = true;
2044 return false; // Stop searching
2048 if(mobj->special != USE_PUZZLE_ITEM_SPECIAL)
2052 if(PuzzleItemType != mobj->args[0])
2053 { // Item type doesn't match
2056 P_StartACS(mobj->args[1], 0, &mobj->args[2], PuzzleItemUser, NULL, 0);
2058 PuzzleActivated = true;
2059 return false; // Stop searching
2062 //==========================================================================
2066 // Returns true if the puzzle item was used on a line or a thing.
2068 //==========================================================================
2070 boolean P_UsePuzzleItem(player_t *player, int itemType)
2073 fixed_t x1, y1, x2, y2;
2075 PuzzleItemType = itemType;
2076 PuzzleItemUser = player->mo;
2077 PuzzleActivated = false;
2078 angle = player->mo->angle>>ANGLETOFINESHIFT;
2081 x2 = x1+(USERANGE>>FRACBITS)*finecosine[angle];
2082 y2 = y1+(USERANGE>>FRACBITS)*finesine[angle];
2083 P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS,
2084 PTR_PuzzleItemTraverse);
2085 return PuzzleActivated;
2089 ==============================================================================
2093 ==============================================================================
2100 boolean DamageSource;
2107 = Source is the creature that casued the explosion at spot
2111 boolean PIT_RadiusAttack (mobj_t *thing)
2113 fixed_t dx, dy, dist;
2116 if(!(thing->flags&MF_SHOOTABLE))
2120 // if(thing->flags2&MF2_BOSS)
2121 // { // Bosses take no damage from PIT_RadiusAttack
2124 if(!DamageSource && thing == bombsource)
2125 { // don't damage the source of the explosion
2128 if(abs((thing->z-bombspot->z)>>FRACBITS) > 2*bombdistance)
2132 dx = abs(thing->x-bombspot->x);
2133 dy = abs(thing->y-bombspot->y);
2134 dist = dx > dy ? dx : dy;
2135 dist = (dist-thing->radius)>>FRACBITS;
2140 if(dist >= bombdistance)
2144 if(P_CheckSight(thing, bombspot))
2145 { // OK to damage, target is in direct path
2146 damage = (bombdamage*(bombdistance-dist)/bombdistance)+1;
2151 P_DamageMobj(thing, bombspot, bombsource, damage);
2161 = Source is the creature that caused the explosion at spot
2165 void P_RadiusAttack (mobj_t *spot, mobj_t *source, int damage, int distance,
2166 boolean damageSource)
2168 int x,y, xl, xh, yl, yh;
2171 dist = (distance+MAXRADIUS)<<FRACBITS;
2172 yh = (spot->y+dist-bmaporgy)>>MAPBLOCKSHIFT;
2173 yl = (spot->y-dist-bmaporgy)>>MAPBLOCKSHIFT;
2174 xh = (spot->x+dist-bmaporgx)>>MAPBLOCKSHIFT;
2175 xl = (spot->x-dist-bmaporgx)>>MAPBLOCKSHIFT;
2177 bombsource = source;
2178 bombdamage = damage;
2179 bombdistance = distance;
2180 DamageSource = damageSource;
2181 for (y = yl; y <= yh; y++)
2183 for (x = xl; x <= xh; x++)
2185 P_BlockThingsIterator(x, y, PIT_RadiusAttack);
2191 ==============================================================================
2193 SECTOR HEIGHT CHANGING
2195 = After modifying a sectors floor or ceiling height, call this
2196 = routine to adjust the positions of all things that touch the
2199 = If anything doesn't fit anymore, true will be returned.
2200 = If crunch is true, they will take damage as they are being crushed
2201 = If Crunch is false, you should set the sector height back the way it
2202 = was and call P_ChangeSector again to undo the changes
2203 ==============================================================================
2217 boolean PIT_ChangeSector (mobj_t *thing)
2221 if (P_ThingHeightClip (thing))
2222 return true; // keep checking
2224 // crunch bodies to giblets
2225 if ((thing->flags&MF_CORPSE) && (thing->health <= 0))
2227 if (thing->flags&MF_NOBLOOD)
2229 P_RemoveMobj (thing);
2233 if (thing->state != &states[S_GIBS1])
2235 P_SetMobjState (thing, S_GIBS1);
2238 S_StartSound(thing, SFX_PLAYER_FALLING_SPLAT);
2241 return true; // keep checking
2244 // crunch dropped items
2245 if (thing->flags2&MF2_DROPPED)
2247 P_RemoveMobj (thing);
2248 return true; // keep checking
2251 if (! (thing->flags & MF_SHOOTABLE) )
2252 return true; // assume it is bloody gibs or something
2255 if (crushchange && !(leveltime&3))
2257 P_DamageMobj(thing, NULL, NULL, crushchange);
2258 // spray blood in a random direction
2259 if ((!(thing->flags&MF_NOBLOOD)) &&
2260 (!(thing->flags2&MF2_INVULNERABLE)))
2262 mo = P_SpawnMobj (thing->x, thing->y, thing->z + thing->height/2,
2264 mo->momx = (P_Random() - P_Random ())<<12;
2265 mo->momy = (P_Random() - P_Random ())<<12;
2269 return true; // keep checking (crush other things)
2280 boolean P_ChangeSector (sector_t *sector, int crunch)
2285 crushchange = crunch;
2287 // recheck heights for all things near the moving sector
2289 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
2290 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
2291 P_BlockThingsIterator (x, y, PIT_ChangeSector);