1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard, Andrey Budko
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 //	Movement, collision handling.
17 //	Shooting and aiming.
18 //
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 
23 #include "deh_misc.h"
24 
25 #include "m_bbox.h"
26 #include "m_random.h"
27 #include "i_system.h"
28 
29 #include "doomdef.h"
30 #include "m_argv.h"
31 #include "m_misc.h"
32 #include "p_local.h"
33 
34 #include "s_sound.h"
35 
36 // State.
37 #include "doomstat.h"
38 #include "r_state.h"
39 // Data.
40 #include "sounds.h"
41 
42 // Spechit overrun magic value.
43 //
44 // This is the value used by PrBoom-plus.  I think the value below is
45 // actually better and works with more demos.  However, I think
46 // it's better for the spechits emulation to be compatible with
47 // PrBoom-plus, at least so that the big spechits emulation list
48 // on Doomworld can also be used with Chocolate Doom.
49 
50 #define DEFAULT_SPECHIT_MAGIC 0x01C09C98
51 
52 // This is from a post by myk on the Doomworld forums,
53 // outputted from entryway's spechit_magic generator for
54 // s205n546.lmp.  The _exact_ value of this isn't too
55 // important; as long as it is in the right general
56 // range, it will usually work.  Otherwise, we can use
57 // the generator (hacked doom2.exe) and provide it
58 // with -spechit.
59 
60 //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8
61 
62 
63 fixed_t		tmbbox[4];
64 mobj_t*		tmthing;
65 int		tmflags;
66 fixed_t		tmx;
67 fixed_t		tmy;
68 
69 
70 // If "floatok" true, move would be ok
71 // if within "tmfloorz - tmceilingz".
72 boolean		floatok;
73 
74 fixed_t		tmfloorz;
75 fixed_t		tmceilingz;
76 fixed_t		tmdropoffz;
77 
78 // keep track of the line that lowers the ceiling,
79 // so missiles don't explode against sky hack walls
80 line_t*		ceilingline;
81 
82 // keep track of special lines as they are hit,
83 // but don't process them until the move is proven valid
84 
85 line_t**	spechit; // [crispy] remove SPECHIT limit
86 int		numspechit;
87 static int spechit_max; // [crispy] remove SPECHIT limit
88 
89 
90 
91 //
92 // TELEPORT MOVE
93 //
94 
95 //
96 // PIT_StompThing
97 //
PIT_StompThing(mobj_t * thing)98 boolean PIT_StompThing (mobj_t* thing)
99 {
100     fixed_t	blockdist;
101 
102     if (!(thing->flags & MF_SHOOTABLE) )
103 	return true;
104 
105     blockdist = thing->radius + tmthing->radius;
106 
107     if ( abs(thing->x - tmx) >= blockdist
108 	 || abs(thing->y - tmy) >= blockdist )
109     {
110 	// didn't hit it
111 	return true;
112     }
113 
114     // don't clip against self
115     if (thing == tmthing)
116 	return true;
117 
118     // monsters don't stomp things except on boss level
119     if ( !tmthing->player && gamemap != 30)
120 	return false;
121 
122     P_DamageMobj (thing, tmthing, tmthing, 10000);
123 
124     return true;
125 }
126 
127 
128 //
129 // P_TeleportMove
130 //
131 boolean
P_TeleportMove(mobj_t * thing,fixed_t x,fixed_t y)132 P_TeleportMove
133 ( mobj_t*	thing,
134   fixed_t	x,
135   fixed_t	y )
136 {
137     int			xl;
138     int			xh;
139     int			yl;
140     int			yh;
141     int			bx;
142     int			by;
143 
144     subsector_t*	newsubsec;
145 
146     // kill anything occupying the position
147     tmthing = thing;
148     tmflags = thing->flags;
149 
150     tmx = x;
151     tmy = y;
152 
153     tmbbox[BOXTOP] = y + tmthing->radius;
154     tmbbox[BOXBOTTOM] = y - tmthing->radius;
155     tmbbox[BOXRIGHT] = x + tmthing->radius;
156     tmbbox[BOXLEFT] = x - tmthing->radius;
157 
158     newsubsec = R_PointInSubsector (x,y);
159     ceilingline = NULL;
160 
161     // The base floor/ceiling is from the subsector
162     // that contains the point.
163     // Any contacted lines the step closer together
164     // will adjust them.
165     tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
166     tmceilingz = newsubsec->sector->ceilingheight;
167 
168     validcount++;
169     numspechit = 0;
170 
171     // stomp on any things contacted
172     xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
173     xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
174     yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
175     yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
176 
177     for (bx=xl ; bx<=xh ; bx++)
178 	for (by=yl ; by<=yh ; by++)
179 	    if (!P_BlockThingsIterator(bx,by,PIT_StompThing))
180 		return false;
181 
182     // the move is ok,
183     // so link the thing into its new position
184     P_UnsetThingPosition (thing);
185 
186     thing->floorz = tmfloorz;
187     thing->ceilingz = tmceilingz;
188     thing->x = x;
189     thing->y = y;
190 
191     // [AM] Don't interpolate mobjs that pass
192     //      through teleporters
193     thing->interp = false;
194 
195     P_SetThingPosition (thing);
196 
197     return true;
198 }
199 
200 
201 //
202 // MOVEMENT ITERATOR FUNCTIONS
203 //
204 
205 static void SpechitOverrun(line_t *ld);
206 
207 //
208 // PIT_CheckLine
209 // Adjusts tmfloorz and tmceilingz as lines are contacted
210 //
PIT_CheckLine(line_t * ld)211 boolean PIT_CheckLine (line_t* ld)
212 {
213     if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT]
214 	|| tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
215 	|| tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM]
216 	|| tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] )
217 	return true;
218 
219     if (P_BoxOnLineSide (tmbbox, ld) != -1)
220 	return true;
221 
222     // A line has been hit
223 
224     // The moving thing's destination position will cross
225     // the given line.
226     // If this should not be allowed, return false.
227     // If the line is special, keep track of it
228     // to process later if the move is proven ok.
229     // NOTE: specials are NOT sorted by order,
230     // so two special lines that are only 8 pixels apart
231     // could be crossed in either order.
232 
233     if (!ld->backsector)
234 	return false;		// one sided line
235 
236     if (!(tmthing->flags & MF_MISSILE) )
237     {
238 	if ( ld->flags & ML_BLOCKING )
239 	    return false;	// explicitly blocking everything
240 
241 	if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS )
242 	    return false;	// block monsters only
243     }
244 
245     // set openrange, opentop, openbottom
246     P_LineOpening (ld);
247 
248     // adjust floor / ceiling heights
249     if (opentop < tmceilingz)
250     {
251 	tmceilingz = opentop;
252 	ceilingline = ld;
253     }
254 
255     if (openbottom > tmfloorz)
256 	tmfloorz = openbottom;
257 
258     if (lowfloor < tmdropoffz)
259 	tmdropoffz = lowfloor;
260 
261     // if contacted a special line, add it to the list
262     if (ld->special)
263     {
264         // [crispy] remove SPECHIT limit
265         if (numspechit >= spechit_max)
266         {
267             spechit_max = spechit_max ? spechit_max * 2 : MAXSPECIALCROSS;
268             spechit = I_Realloc(spechit, sizeof(*spechit) * spechit_max);
269         }
270         spechit[numspechit] = ld;
271 	numspechit++;
272 
273         // fraggle: spechits overrun emulation code from prboom-plus
274         if (numspechit > MAXSPECIALCROSS_ORIGINAL)
275         {
276             // [crispy] print a warning
277             if (numspechit == MAXSPECIALCROSS_ORIGINAL + 1)
278                 fprintf(stderr, "PIT_CheckLine: Triggered SPECHITS overflow!\n");
279             SpechitOverrun(ld);
280         }
281     }
282 
283     return true;
284 }
285 
286 //
287 // PIT_CheckThing
288 //
PIT_CheckThing(mobj_t * thing)289 boolean PIT_CheckThing (mobj_t* thing)
290 {
291     fixed_t		blockdist;
292     boolean		solid;
293     boolean		unblocking = false;
294     int			damage;
295 
296     if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) ))
297 	return true;
298 
299     blockdist = thing->radius + tmthing->radius;
300 
301     if ( abs(thing->x - tmx) >= blockdist
302 	 || abs(thing->y - tmy) >= blockdist )
303     {
304 	// didn't hit it
305 	return true;
306     }
307 
308     // don't clip against self
309     if (thing == tmthing)
310 	return true;
311 
312     // check for skulls slamming into things
313     if (tmthing->flags & MF_SKULLFLY)
314     {
315 	// [crispy] check if attacking skull flies over player
316 	if (critical->overunder && thing->player)
317 	{
318 	    if (tmthing->z > thing->z + thing->height)
319 	    {
320 		return true;
321 	    }
322 	}
323 
324 	damage = ((P_Random()%8)+1)*tmthing->info->damage;
325 
326 	P_DamageMobj (thing, tmthing, tmthing, damage);
327 
328 	tmthing->flags &= ~MF_SKULLFLY;
329 	tmthing->momx = tmthing->momy = tmthing->momz = 0;
330 
331 	P_SetMobjState (tmthing, tmthing->info->spawnstate);
332 
333 	return false;		// stop moving
334     }
335 
336 
337     // missiles can hit other things
338     if (tmthing->flags & MF_MISSILE)
339     {
340 	// [crispy] mobj or actual sprite height
341 	const fixed_t thingheight = (tmthing->target && tmthing->target->player &&
342 	                            critical->freeaim == FREEAIM_DIRECT) ?
343 	                            thing->info->actualheight : thing->height;
344 	// see if it went over / under
345 	if (tmthing->z > thing->z + thingheight)
346 	    return true;		// overhead
347 	if (tmthing->z+tmthing->height < thing->z)
348 	    return true;		// underneath
349 
350 	if (tmthing->target
351          && (tmthing->target->type == thing->type ||
352 	    (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)||
353 	    (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) )
354 	{
355 	    // Don't hit same species as originator.
356 	    if (thing == tmthing->target)
357 		return true;
358 
359             // sdh: Add deh_species_infighting here.  We can override the
360             // "monsters of the same species cant hurt each other" behavior
361             // through dehacked patches
362 
363 	    if (thing->type != MT_PLAYER && !deh_species_infighting)
364 	    {
365 		// Explode, but do no damage.
366 		// Let players missile other players.
367 		return false;
368 	    }
369 	}
370 
371 	if (! (thing->flags & MF_SHOOTABLE) )
372 	{
373 	    // didn't do any damage
374 	    return !(thing->flags & MF_SOLID);
375 	}
376 
377 	// damage / explode
378 	damage = ((P_Random()%8)+1)*tmthing->info->damage;
379 	P_DamageMobj (thing, tmthing, tmthing->target, damage);
380 
381 	// don't traverse any more
382 	return false;
383     }
384 
385     // check for special pickup
386     if (thing->flags & MF_SPECIAL)
387     {
388 	solid = (thing->flags & MF_SOLID) != 0;
389 	if (tmflags&MF_PICKUP)
390 	{
391 	    // can remove thing
392 	    P_TouchSpecialThing (thing, tmthing);
393 	}
394 	return !solid;
395     }
396 
397 	if (critical->overunder)
398 	{
399 		// [crispy] a solid hanging body will allow sufficiently small things underneath it
400 		if (thing->flags & MF_SOLID && thing->flags & MF_SPAWNCEILING)
401 		{
402 			if (tmthing->z + tmthing->height <= thing->z)
403 			{
404 				if (thing->z < tmceilingz)
405 				{
406 					tmceilingz = thing->z;
407 				}
408 				return true;
409 			}
410 		}
411 
412 		// [crispy] allow players to walk over/under shootable objects
413 		if (tmthing->player && thing->flags & MF_SHOOTABLE)
414 		{
415 			// [crispy] allow the usual 24 units step-up even across monsters' heads,
416 			// only if the current height has not been reached by "low" jumping
417 			fixed_t step_up = tmthing->player->jumpTics > 7 ? 0 : 24*FRACUNIT;
418 
419 			if (tmthing->z + step_up >= thing->z + thing->height)
420 			{
421 				// player walks over object
422 				tmfloorz = MAX(thing->z + thing->height, tmfloorz);
423 				thing->ceilingz = MIN(tmthing->z, thing->ceilingz);
424 				return true;
425 			}
426 			else
427 			if (tmthing->z + tmthing->height <= thing->z)
428 			{
429 				// player walks underneath object
430 				tmceilingz = MIN(thing->z, tmceilingz);
431 				thing->floorz = MAX(tmthing->z + tmthing->height, thing->floorz);
432 				return true;
433 			}
434 
435 			// [crispy] check if things are stuck and allow them to move further apart
436 			// taken from doomretro/src/p_map.c:319-332
437 			if (tmx == tmthing->x && tmy == tmthing->y)
438 			{
439 				unblocking = true;
440 			}
441 			else
442 			{
443 				fixed_t newdist = P_AproxDistance(thing->x - tmx, thing->y - tmy);
444 				fixed_t olddist = P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y);
445 
446 				if (newdist > olddist)
447 				{
448 					unblocking = (tmthing->z < thing->z + thing->height
449 					           && tmthing->z + tmthing->height > thing->z);
450 				}
451 			}
452 		}
453 	}
454 
455     return !(thing->flags & MF_SOLID) || unblocking;
456 }
457 
458 
459 //
460 // MOVEMENT CLIPPING
461 //
462 
463 //
464 // P_CheckPosition
465 // This is purely informative, nothing is modified
466 // (except things picked up).
467 //
468 // in:
469 //  a mobj_t (can be valid or invalid)
470 //  a position to be checked
471 //   (doesn't need to be related to the mobj_t->x,y)
472 //
473 // during:
474 //  special things are touched if MF_PICKUP
475 //  early out on solid lines?
476 //
477 // out:
478 //  newsubsec
479 //  floorz
480 //  ceilingz
481 //  tmdropoffz
482 //   the lowest point contacted
483 //   (monsters won't move to a dropoff)
484 //  speciallines[]
485 //  numspeciallines
486 //
487 boolean
P_CheckPosition(mobj_t * thing,fixed_t x,fixed_t y)488 P_CheckPosition
489 ( mobj_t*	thing,
490   fixed_t	x,
491   fixed_t	y )
492 {
493     int			xl;
494     int			xh;
495     int			yl;
496     int			yh;
497     int			bx;
498     int			by;
499     subsector_t*	newsubsec;
500 
501     tmthing = thing;
502     tmflags = thing->flags;
503 
504     tmx = x;
505     tmy = y;
506 
507     tmbbox[BOXTOP] = y + tmthing->radius;
508     tmbbox[BOXBOTTOM] = y - tmthing->radius;
509     tmbbox[BOXRIGHT] = x + tmthing->radius;
510     tmbbox[BOXLEFT] = x - tmthing->radius;
511 
512     newsubsec = R_PointInSubsector (x,y);
513     ceilingline = NULL;
514 
515     // The base floor / ceiling is from the subsector
516     // that contains the point.
517     // Any contacted lines the step closer together
518     // will adjust them.
519     tmfloorz = tmdropoffz = newsubsec->sector->floorheight;
520     tmceilingz = newsubsec->sector->ceilingheight;
521 
522     validcount++;
523     numspechit = 0;
524 
525     if ( tmflags & MF_NOCLIP )
526 	return true;
527 
528     // Check things first, possibly picking things up.
529     // The bounding box is extended by MAXRADIUS
530     // because mobj_ts are grouped into mapblocks
531     // based on their origin point, and can overlap
532     // into adjacent blocks by up to MAXRADIUS units.
533     xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
534     xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
535     yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
536     yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
537 
538     for (bx=xl ; bx<=xh ; bx++)
539 	for (by=yl ; by<=yh ; by++)
540 	    if (!P_BlockThingsIterator(bx,by,PIT_CheckThing))
541 		return false;
542 
543     // check lines
544     xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
545     xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
546     yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
547     yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
548 
549     for (bx=xl ; bx<=xh ; bx++)
550 	for (by=yl ; by<=yh ; by++)
551 	    if (!P_BlockLinesIterator (bx,by,PIT_CheckLine))
552 		return false;
553 
554     return true;
555 }
556 
557 
558 //
559 // P_TryMove
560 // Attempt to move to a new position,
561 // crossing special lines unless MF_TELEPORT is set.
562 //
563 boolean
P_TryMove(mobj_t * thing,fixed_t x,fixed_t y)564 P_TryMove
565 ( mobj_t*	thing,
566   fixed_t	x,
567   fixed_t	y )
568 {
569     fixed_t	oldx;
570     fixed_t	oldy;
571     int		side;
572     int		oldside;
573     line_t*	ld;
574 
575     floatok = false;
576     if (!P_CheckPosition (thing, x, y))
577 	return false;		// solid wall or thing
578 
579     if ( !(thing->flags & MF_NOCLIP) )
580     {
581 	if (tmceilingz - tmfloorz < thing->height)
582 	    return false;	// doesn't fit
583 
584 	floatok = true;
585 
586 	if ( !(thing->flags&MF_TELEPORT)
587 	     &&tmceilingz - thing->z < thing->height)
588 	    return false;	// mobj must lower itself to fit
589 
590 	if ( !(thing->flags&MF_TELEPORT)
591 	     && tmfloorz - thing->z > 24*FRACUNIT )
592 	    return false;	// too big a step up
593 
594 	if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT))
595 	     && tmfloorz - tmdropoffz > 24*FRACUNIT )
596 	    return false;	// don't stand over a dropoff
597     }
598 
599     // the move is ok,
600     // so link the thing into its new position
601     P_UnsetThingPosition (thing);
602 
603     oldx = thing->x;
604     oldy = thing->y;
605     thing->floorz = tmfloorz;
606     thing->ceilingz = tmceilingz;
607     thing->x = x;
608     thing->y = y;
609 
610     P_SetThingPosition (thing);
611 
612     // if any special lines were hit, do the effect
613     if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) )
614     {
615 	while (numspechit--)
616 	{
617 	    // see if the line was crossed
618 	    ld = spechit[numspechit];
619 	    side = P_PointOnLineSide (thing->x, thing->y, ld);
620 	    oldside = P_PointOnLineSide (oldx, oldy, ld);
621 	    if (side != oldside)
622 	    {
623 		if (ld->special)
624 		    P_CrossSpecialLine (ld-lines, oldside, thing);
625 	    }
626 	}
627     }
628 
629     return true;
630 }
631 
632 
633 //
634 // P_ThingHeightClip
635 // Takes a valid thing and adjusts the thing->floorz,
636 // thing->ceilingz, and possibly thing->z.
637 // This is called for all nearby monsters
638 // whenever a sector changes height.
639 // If the thing doesn't fit,
640 // the z will be set to the lowest value
641 // and false will be returned.
642 //
P_ThingHeightClip(mobj_t * thing)643 boolean P_ThingHeightClip (mobj_t* thing)
644 {
645     boolean		onfloor;
646 
647     onfloor = (thing->z == thing->floorz);
648 
649     P_CheckPosition (thing, thing->x, thing->y);
650     // what about stranding a monster partially off an edge?
651 
652     thing->floorz = tmfloorz;
653     thing->ceilingz = tmceilingz;
654 
655     if (onfloor)
656     {
657 	// walking monsters rise and fall with the floor
658 	thing->z = thing->floorz;
659     }
660     else
661     {
662 	// don't adjust a floating monster unless forced to
663 	if (thing->z+thing->height > thing->ceilingz)
664 	    thing->z = thing->ceilingz - thing->height;
665     }
666 
667     if (thing->ceilingz - thing->floorz < thing->height)
668 	return false;
669 
670     return true;
671 }
672 
673 
674 
675 //
676 // SLIDE MOVE
677 // Allows the player to slide along any angled walls.
678 //
679 fixed_t		bestslidefrac;
680 fixed_t		secondslidefrac;
681 
682 line_t*		bestslideline;
683 line_t*		secondslideline;
684 
685 mobj_t*		slidemo;
686 
687 fixed_t		tmxmove;
688 fixed_t		tmymove;
689 
690 
691 
692 //
693 // P_HitSlideLine
694 // Adjusts the xmove / ymove
695 // so that the next move will slide along the wall.
696 //
P_HitSlideLine(line_t * ld)697 void P_HitSlideLine (line_t* ld)
698 {
699     int			side;
700 
701     angle_t		lineangle;
702     angle_t		moveangle;
703     angle_t		deltaangle;
704 
705     fixed_t		movelen;
706     fixed_t		newlen;
707 
708 
709     if (ld->slopetype == ST_HORIZONTAL)
710     {
711 	tmymove = 0;
712 	return;
713     }
714 
715     if (ld->slopetype == ST_VERTICAL)
716     {
717 	tmxmove = 0;
718 	return;
719     }
720 
721     side = P_PointOnLineSide (slidemo->x, slidemo->y, ld);
722 
723     lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy);
724 
725     if (side == 1)
726 	lineangle += ANG180;
727 
728     moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove);
729     deltaangle = moveangle-lineangle;
730 
731     if (deltaangle > ANG180)
732 	deltaangle += ANG180;
733     //	I_Error ("SlideLine: ang>ANG180");
734 
735     lineangle >>= ANGLETOFINESHIFT;
736     deltaangle >>= ANGLETOFINESHIFT;
737 
738     movelen = P_AproxDistance (tmxmove, tmymove);
739     newlen = FixedMul (movelen, finecosine[deltaangle]);
740 
741     tmxmove = FixedMul (newlen, finecosine[lineangle]);
742     tmymove = FixedMul (newlen, finesine[lineangle]);
743 }
744 
745 
746 //
747 // PTR_SlideTraverse
748 //
PTR_SlideTraverse(intercept_t * in)749 boolean PTR_SlideTraverse (intercept_t* in)
750 {
751     line_t*	li;
752 
753     if (!in->isaline)
754 	I_Error ("PTR_SlideTraverse: not a line?");
755 
756     li = in->d.line;
757 
758     if ( ! (li->flags & ML_TWOSIDED) )
759     {
760 	if (P_PointOnLineSide (slidemo->x, slidemo->y, li))
761 	{
762 	    // don't hit the back side
763 	    return true;
764 	}
765 	goto isblocking;
766     }
767 
768     // set openrange, opentop, openbottom
769     P_LineOpening (li);
770 
771     if (openrange < slidemo->height)
772 	goto isblocking;		// doesn't fit
773 
774     if (opentop - slidemo->z < slidemo->height)
775 	goto isblocking;		// mobj is too high
776 
777     if (openbottom - slidemo->z > 24*FRACUNIT )
778 	goto isblocking;		// too big a step up
779 
780     // this line doesn't block movement
781     return true;
782 
783     // the line does block movement,
784     // see if it is closer than best so far
785   isblocking:
786     if (in->frac < bestslidefrac)
787     {
788 	secondslidefrac = bestslidefrac;
789 	secondslideline = bestslideline;
790 	bestslidefrac = in->frac;
791 	bestslideline = li;
792     }
793 
794     return false;	// stop
795 }
796 
797 
798 
799 //
800 // P_SlideMove
801 // The momx / momy move is bad, so try to slide
802 // along a wall.
803 // Find the first line hit, move flush to it,
804 // and slide along it
805 //
806 // This is a kludgy mess.
807 //
P_SlideMove(mobj_t * mo)808 void P_SlideMove (mobj_t* mo)
809 {
810     fixed_t		leadx;
811     fixed_t		leady;
812     fixed_t		trailx;
813     fixed_t		traily;
814     fixed_t		newx;
815     fixed_t		newy;
816     int			hitcount;
817 
818     slidemo = mo;
819     hitcount = 0;
820 
821   retry:
822     if (++hitcount == 3)
823 	goto stairstep;		// don't loop forever
824 
825 
826     // trace along the three leading corners
827     if (mo->momx > 0)
828     {
829 	leadx = mo->x + mo->radius;
830 	trailx = mo->x - mo->radius;
831     }
832     else
833     {
834 	leadx = mo->x - mo->radius;
835 	trailx = mo->x + mo->radius;
836     }
837 
838     if (mo->momy > 0)
839     {
840 	leady = mo->y + mo->radius;
841 	traily = mo->y - mo->radius;
842     }
843     else
844     {
845 	leady = mo->y - mo->radius;
846 	traily = mo->y + mo->radius;
847     }
848 
849     bestslidefrac = FRACUNIT+1;
850 
851     P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy,
852 		     PT_ADDLINES, PTR_SlideTraverse );
853     P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy,
854 		     PT_ADDLINES, PTR_SlideTraverse );
855     P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy,
856 		     PT_ADDLINES, PTR_SlideTraverse );
857 
858     // move up to the wall
859     if (bestslidefrac == FRACUNIT+1)
860     {
861 	// the move most have hit the middle, so stairstep
862       stairstep:
863 	if (!P_TryMove (mo, mo->x, mo->y + mo->momy))
864 	    P_TryMove (mo, mo->x + mo->momx, mo->y);
865 	return;
866     }
867 
868     // fudge a bit to make sure it doesn't hit
869     bestslidefrac -= 0x800;
870     if (bestslidefrac > 0)
871     {
872 	newx = FixedMul (mo->momx, bestslidefrac);
873 	newy = FixedMul (mo->momy, bestslidefrac);
874 
875 	if (!P_TryMove (mo, mo->x+newx, mo->y+newy))
876 	    goto stairstep;
877     }
878 
879     // Now continue along the wall.
880     // First calculate remainder.
881     bestslidefrac = FRACUNIT-(bestslidefrac+0x800);
882 
883     if (bestslidefrac > FRACUNIT)
884 	bestslidefrac = FRACUNIT;
885 
886     if (bestslidefrac <= 0)
887 	return;
888 
889     tmxmove = FixedMul (mo->momx, bestslidefrac);
890     tmymove = FixedMul (mo->momy, bestslidefrac);
891 
892     P_HitSlideLine (bestslideline);	// clip the moves
893 
894     mo->momx = tmxmove;
895     mo->momy = tmymove;
896 
897     if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove))
898     {
899 	goto retry;
900     }
901 }
902 
903 
904 //
905 // P_LineAttack
906 //
907 mobj_t*		linetarget;	// who got hit (or NULL)
908 mobj_t*		shootthing;
909 
910 // Height if not aiming up or down
911 // ???: use slope for monsters?
912 fixed_t		shootz;
913 
914 int		la_damage;
915 fixed_t		attackrange;
916 
917 fixed_t		aimslope;
918 
919 // slopes to top and bottom of target
920 extern fixed_t	topslope;
921 extern fixed_t	bottomslope;
922 
923 extern degenmobj_t *laserspot;
924 
925 //
926 // PTR_AimTraverse
927 // Sets linetaget and aimslope when a target is aimed at.
928 //
929 boolean
PTR_AimTraverse(intercept_t * in)930 PTR_AimTraverse (intercept_t* in)
931 {
932     line_t*		li;
933     mobj_t*		th;
934     fixed_t		slope;
935     fixed_t		thingtopslope;
936     fixed_t		thingbottomslope;
937     fixed_t		dist;
938 
939     if (in->isaline)
940     {
941 	li = in->d.line;
942 
943 	if ( !(li->flags & ML_TWOSIDED) )
944 	    return false;		// stop
945 
946 	// Crosses a two sided line.
947 	// A two sided line will restrict
948 	// the possible target ranges.
949 	P_LineOpening (li);
950 
951 	if (openbottom >= opentop)
952 	    return false;		// stop
953 
954 	dist = FixedMul (attackrange, in->frac);
955 
956         if (li->backsector == NULL
957          || li->frontsector->floorheight != li->backsector->floorheight)
958 	{
959 	    slope = FixedDiv (openbottom - shootz , dist);
960 	    if (slope > bottomslope)
961 		bottomslope = slope;
962 	}
963 
964 	if (li->backsector == NULL
965          || li->frontsector->ceilingheight != li->backsector->ceilingheight)
966 	{
967 	    slope = FixedDiv (opentop - shootz , dist);
968 	    if (slope < topslope)
969 		topslope = slope;
970 	}
971 
972 	if (topslope <= bottomslope)
973 	    return false;		// stop
974 
975 	return true;			// shot continues
976     }
977 
978     // shoot a thing
979     th = in->d.thing;
980     if (th == shootthing)
981 	return true;			// can't shoot self
982 
983     if (!(th->flags&MF_SHOOTABLE))
984 	return true;			// corpse or something
985 
986     // check angles to see if the thing can be aimed at
987     dist = FixedMul (attackrange, in->frac);
988     thingtopslope = FixedDiv (th->z+th->height - shootz , dist);
989 
990     if (thingtopslope < bottomslope)
991 	return true;			// shot over the thing
992 
993     thingbottomslope = FixedDiv (th->z - shootz, dist);
994 
995     if (thingbottomslope > topslope)
996 	return true;			// shot under the thing
997 
998     // this thing can be hit!
999     if (thingtopslope > topslope)
1000 	thingtopslope = topslope;
1001 
1002     if (thingbottomslope < bottomslope)
1003 	thingbottomslope = bottomslope;
1004 
1005     aimslope = (thingtopslope+thingbottomslope)/2;
1006     linetarget = th;
1007 
1008     return false;			// don't go any farther
1009 }
1010 
1011 
1012 //
1013 // PTR_ShootTraverse
1014 //
PTR_ShootTraverse(intercept_t * in)1015 boolean PTR_ShootTraverse (intercept_t* in)
1016 {
1017     fixed_t		x;
1018     fixed_t		y;
1019     fixed_t		z;
1020     fixed_t		frac;
1021 
1022     line_t*		li;
1023 
1024     mobj_t*		th;
1025 
1026     fixed_t		slope;
1027     fixed_t		dist;
1028     fixed_t		thingtopslope;
1029     fixed_t		thingbottomslope;
1030     fixed_t		thingheight; // [crispy] mobj or actual sprite height
1031 
1032     if (in->isaline)
1033     {
1034 	boolean safe = false;
1035 	li = in->d.line;
1036 
1037 	// [crispy] laser spot does not shoot any line
1038 	if (li->special && la_damage > INT_MIN)
1039 	    P_ShootSpecialLine (shootthing, li);
1040 
1041 	if ( !(li->flags & ML_TWOSIDED) )
1042 	    goto hitline;
1043 
1044 	// crosses a two sided line
1045 	P_LineOpening (li);
1046 
1047 	dist = FixedMul (attackrange, in->frac);
1048 
1049         // e6y: emulation of missed back side on two-sided lines.
1050         // backsector can be NULL when emulating missing back side.
1051 
1052         if (li->backsector == NULL)
1053         {
1054             slope = FixedDiv (openbottom - shootz , dist);
1055             if (slope > aimslope)
1056                 goto hitline;
1057 
1058             slope = FixedDiv (opentop - shootz , dist);
1059             if (slope < aimslope)
1060                 goto hitline;
1061         }
1062         else
1063         {
1064             if (li->frontsector->floorheight != li->backsector->floorheight)
1065             {
1066                 slope = FixedDiv (openbottom - shootz , dist);
1067                 if (slope > aimslope)
1068                     goto hitline;
1069             }
1070 
1071             if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
1072             {
1073                 slope = FixedDiv (opentop - shootz , dist);
1074                 if (slope < aimslope)
1075                     goto hitline;
1076             }
1077         }
1078 
1079 	// shot continues
1080 	return true;
1081 
1082 
1083 	// hit line
1084       hitline:
1085 	// position a bit closer
1086 	frac = in->frac - FixedDiv (4*FRACUNIT,attackrange);
1087 	x = trace.x + FixedMul (trace.dx, frac);
1088 	y = trace.y + FixedMul (trace.dy, frac);
1089 	z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1090 
1091 	if (li->frontsector->ceilingpic == skyflatnum)
1092 	{
1093 	    // don't shoot the sky!
1094 	    if (z > li->frontsector->ceilingheight)
1095 		return false;
1096 
1097 	    // it's a sky hack wall
1098 	    if	(li->backsector && li->backsector->ceilingpic == skyflatnum)
1099 	    {
1100 	      // [crispy] fix bullet puffs and laser spot not appearing in outdoor areas
1101 	      if (li->backsector->ceilingheight < z)
1102 		return false;
1103 	      else
1104 		safe = true;
1105 	    }
1106 	}
1107 
1108 	// [crispy] check if the pullet puff's z-coordinate is below of above
1109 	// its spawning sector's floor or ceiling, respectively, and move its
1110 	// coordinates to the point where the trajectory hits the plane
1111 	if (aimslope)
1112 	{
1113 		const int lineside = P_PointOnLineSide(x, y, li);
1114 		int side;
1115 
1116 		if ((side = li->sidenum[lineside]) != NO_INDEX)
1117 		{
1118 			const sector_t *const sector = sides[side].sector;
1119 
1120 			if (z < sector->floorheight ||
1121 			    (z > sector->ceilingheight && sector->ceilingpic != skyflatnum))
1122 			{
1123 				z = BETWEEN(sector->floorheight, sector->ceilingheight, z);
1124 				frac = FixedDiv(z - shootz, FixedMul(aimslope, attackrange));
1125 				x = trace.x + FixedMul (trace.dx, frac);
1126 				y = trace.y + FixedMul (trace.dy, frac);
1127 			}
1128 		}
1129 	}
1130 
1131 	// [crispy] update laser spot position and return
1132 	if (la_damage == INT_MIN)
1133 	{
1134 	    laserspot->thinker.function.acv = (actionf_v) (1);
1135 	    laserspot->x = x;
1136 	    laserspot->y = y;
1137 	    laserspot->z = z;
1138 	    return false;
1139 	}
1140 
1141 	// Spawn bullet puffs.
1142 	P_SpawnPuffSafe (x, y, z, safe);
1143 
1144 	// don't go any farther
1145 	return false;
1146     }
1147 
1148     // shoot a thing
1149     th = in->d.thing;
1150     if (th == shootthing)
1151 	return true;		// can't shoot self
1152 
1153     if (!(th->flags&MF_SHOOTABLE))
1154 	return true;		// corpse or something
1155 
1156     // check angles to see if the thing can be aimed at
1157     dist = FixedMul (attackrange, in->frac);
1158     // [crispy] mobj or actual sprite height
1159     thingheight = (shootthing->player && critical->freeaim == FREEAIM_DIRECT) ?
1160                   th->info->actualheight : th->height;
1161     thingtopslope = FixedDiv (th->z+thingheight - shootz , dist);
1162 
1163     if (thingtopslope < aimslope)
1164 	return true;		// shot over the thing
1165 
1166     thingbottomslope = FixedDiv (th->z - shootz, dist);
1167 
1168     if (thingbottomslope > aimslope)
1169 	return true;		// shot under the thing
1170 
1171 
1172     // hit thing
1173     // position a bit closer
1174     frac = in->frac - FixedDiv (10*FRACUNIT,attackrange);
1175 
1176     x = trace.x + FixedMul (trace.dx, frac);
1177     y = trace.y + FixedMul (trace.dy, frac);
1178     z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange));
1179 
1180     // [crispy] update laser spot position and return
1181     if (la_damage == INT_MIN)
1182     {
1183 	// [crispy] pass through Spectres
1184 	if (th->flags & MF_SHADOW)
1185 	    return true;
1186 
1187 	laserspot->thinker.function.acv = (actionf_v) (1);
1188 	laserspot->x = th->x;
1189 	laserspot->y = th->y;
1190 	laserspot->z = z;
1191 	return false;
1192     }
1193 
1194     // Spawn bullet puffs or blod spots,
1195     // depending on target type.
1196     if (in->d.thing->flags & MF_NOBLOOD)
1197 	P_SpawnPuff (x,y,z);
1198     else
1199 	P_SpawnBlood (x,y,z, la_damage, th); // [crispy] pass thing type
1200 
1201     if (la_damage)
1202 	P_DamageMobj (th, shootthing, shootthing, la_damage);
1203 
1204     // don't go any farther
1205     return false;
1206 
1207 }
1208 
1209 
1210 //
1211 // P_AimLineAttack
1212 //
1213 fixed_t
P_AimLineAttack(mobj_t * t1,angle_t angle,fixed_t distance)1214 P_AimLineAttack
1215 ( mobj_t*	t1,
1216   angle_t	angle,
1217   fixed_t	distance )
1218 {
1219     fixed_t	x2;
1220     fixed_t	y2;
1221 
1222     t1 = P_SubstNullMobj(t1);
1223 
1224     angle >>= ANGLETOFINESHIFT;
1225     shootthing = t1;
1226 
1227     x2 = t1->x + (distance>>FRACBITS)*finecosine[angle];
1228     y2 = t1->y + (distance>>FRACBITS)*finesine[angle];
1229     shootz = t1->z + (t1->height>>1) + 8*FRACUNIT;
1230 
1231     // can't shoot outside view angles
1232     topslope = (ORIGHEIGHT/2)*FRACUNIT/(ORIGWIDTH/2);
1233     bottomslope = -(ORIGHEIGHT/2)*FRACUNIT/(ORIGWIDTH/2);
1234 
1235     attackrange = distance;
1236     linetarget = NULL;
1237 
1238     P_PathTraverse ( t1->x, t1->y,
1239 		     x2, y2,
1240 		     PT_ADDLINES|PT_ADDTHINGS,
1241 		     PTR_AimTraverse );
1242 
1243     if (linetarget)
1244 	return aimslope;
1245 
1246     return 0;
1247 }
1248 
1249 
1250 //
1251 // P_LineAttack
1252 // If damage == 0, it is just a test trace
1253 // that will leave linetarget set.
1254 // [crispy] if damage == INT_MIN, it is a trace
1255 // to update the laser spot position
1256 //
1257 void
P_LineAttack(mobj_t * t1,angle_t angle,fixed_t distance,fixed_t slope,int damage)1258 P_LineAttack
1259 ( mobj_t*	t1,
1260   angle_t	angle,
1261   fixed_t	distance,
1262   fixed_t	slope,
1263   int		damage )
1264 {
1265     // [crispy] smooth laser spot movement with uncapped framerate
1266     const fixed_t t1x = (damage == INT_MIN) ? viewx : t1->x;
1267     const fixed_t t1y = (damage == INT_MIN) ? viewy : t1->y;
1268     fixed_t	x2;
1269     fixed_t	y2;
1270 
1271     angle >>= ANGLETOFINESHIFT;
1272     shootthing = t1;
1273     la_damage = damage;
1274     x2 = t1x + (distance>>FRACBITS)*finecosine[angle];
1275     y2 = t1y + (distance>>FRACBITS)*finesine[angle];
1276     shootz = (damage == INT_MIN) ? viewz : t1->z + (t1->height>>1) + 8*FRACUNIT;
1277     attackrange = distance;
1278     aimslope = slope;
1279 
1280     P_PathTraverse ( t1x, t1y,
1281 		     x2, y2,
1282 		     PT_ADDLINES|PT_ADDTHINGS,
1283 		     PTR_ShootTraverse );
1284 }
1285 
1286 // [crispy] update laser spot position
1287 // call P_AimLineAttack() to check if a target is aimed at (linetarget)
1288 // then call P_LineAttack() with either aimslope or the passed slope
1289 void
P_LineLaser(mobj_t * t1,angle_t angle,fixed_t distance,fixed_t slope)1290 P_LineLaser
1291 ( mobj_t*	t1,
1292   angle_t	angle,
1293   fixed_t	distance,
1294   fixed_t	slope )
1295 {
1296     fixed_t	lslope;
1297 
1298     laserspot->thinker.function.acv = (actionf_v) (0);
1299 
1300     // [crispy] intercepts overflow guard
1301     crispy->crosshair |= CROSSHAIR_INTERCEPT;
1302 
1303     // [crispy] set the linetarget pointer
1304     lslope = P_AimLineAttack(t1, angle, distance);
1305 
1306     if (critical->freeaim == FREEAIM_DIRECT)
1307     {
1308 	lslope = slope;
1309     }
1310     else
1311     {
1312     // [crispy] increase accuracy
1313     if (!linetarget)
1314     {
1315 	angle_t an = angle;
1316 
1317 	an += 1<<26;
1318 	lslope = P_AimLineAttack(t1, an, distance);
1319 
1320 	if (!linetarget)
1321 	{
1322 	    an -= 2<<26;
1323 	    lslope = P_AimLineAttack(t1, an, distance);
1324 
1325 	    if (!linetarget && critical->freeaim == FREEAIM_BOTH)
1326 	    {
1327 		lslope = slope;
1328 	    }
1329 
1330 	}
1331     }
1332     }
1333 
1334     if ((crispy->crosshair & ~CROSSHAIR_INTERCEPT) == CROSSHAIR_PROJECTED)
1335     {
1336 	// [crispy] don't aim at Spectres
1337 	if (linetarget && !(linetarget->flags & MF_SHADOW) && (crispy->freeaim != FREEAIM_DIRECT))
1338 		P_LineAttack(t1, angle, distance, aimslope, INT_MIN);
1339 	else
1340 		// [crispy] double the auto aim distance
1341 		P_LineAttack(t1, angle, 2*distance, lslope, INT_MIN);
1342     }
1343 
1344     // [crispy] intercepts overflow guard
1345     crispy->crosshair &= ~CROSSHAIR_INTERCEPT;
1346 }
1347 
1348 
1349 //
1350 // USE LINES
1351 //
1352 mobj_t*		usething;
1353 
PTR_UseTraverse(intercept_t * in)1354 boolean	PTR_UseTraverse (intercept_t* in)
1355 {
1356     int		side;
1357 
1358     if (!in->d.line->special)
1359     {
1360 	P_LineOpening (in->d.line);
1361 	if (openrange <= 0)
1362 	{
1363 	    S_StartSound (usething, sfx_noway);
1364 
1365 	    // can't use through a wall
1366 	    return false;
1367 	}
1368 	// not a special line, but keep checking
1369 	return true ;
1370     }
1371 
1372     side = 0;
1373     if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1)
1374 	side = 1;
1375 
1376     //	return false;		// don't use back side
1377 
1378     P_UseSpecialLine (usething, in->d.line, side);
1379 
1380     // can't use for than one special line in a row
1381     return false;
1382 }
1383 
1384 
1385 //
1386 // P_UseLines
1387 // Looks for special lines in front of the player to activate.
1388 //
P_UseLines(player_t * player)1389 void P_UseLines (player_t*	player)
1390 {
1391     int		angle;
1392     fixed_t	x1;
1393     fixed_t	y1;
1394     fixed_t	x2;
1395     fixed_t	y2;
1396 
1397     usething = player->mo;
1398 
1399     angle = player->mo->angle >> ANGLETOFINESHIFT;
1400 
1401     x1 = player->mo->x;
1402     y1 = player->mo->y;
1403     x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle];
1404     y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle];
1405 
1406     P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse );
1407 }
1408 
1409 
1410 //
1411 // RADIUS ATTACK
1412 //
1413 mobj_t*		bombsource;
1414 mobj_t*		bombspot;
1415 int		bombdamage;
1416 
1417 
1418 //
1419 // PIT_RadiusAttack
1420 // "bombsource" is the creature
1421 // that caused the explosion at "bombspot".
1422 //
PIT_RadiusAttack(mobj_t * thing)1423 boolean PIT_RadiusAttack (mobj_t* thing)
1424 {
1425     fixed_t	dx;
1426     fixed_t	dy;
1427     fixed_t	dist;
1428 
1429     if (!(thing->flags & MF_SHOOTABLE) )
1430 	return true;
1431 
1432     // Boss spider and cyborg
1433     // take no damage from concussion.
1434     if (thing->type == MT_CYBORG
1435 	|| thing->type == MT_SPIDER)
1436 	return true;
1437 
1438     dx = abs(thing->x - bombspot->x);
1439     dy = abs(thing->y - bombspot->y);
1440 
1441     dist = dx>dy ? dx : dy;
1442     dist = (dist - thing->radius) >> FRACBITS;
1443 
1444     if (dist < 0)
1445 	dist = 0;
1446 
1447     if (dist >= bombdamage)
1448 	return true;	// out of range
1449 
1450     if ( P_CheckSight (thing, bombspot) )
1451     {
1452 	// must be in direct path
1453 	P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist);
1454     }
1455 
1456     return true;
1457 }
1458 
1459 
1460 //
1461 // P_RadiusAttack
1462 // Source is the creature that caused the explosion at spot.
1463 //
1464 void
P_RadiusAttack(mobj_t * spot,mobj_t * source,int damage)1465 P_RadiusAttack
1466 ( mobj_t*	spot,
1467   mobj_t*	source,
1468   int		damage )
1469 {
1470     int		x;
1471     int		y;
1472 
1473     int		xl;
1474     int		xh;
1475     int		yl;
1476     int		yh;
1477 
1478     fixed_t	dist;
1479 
1480     dist = (damage+MAXRADIUS)<<FRACBITS;
1481     yh = (spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
1482     yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
1483     xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
1484     xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
1485     bombspot = spot;
1486     bombsource = source;
1487     bombdamage = damage;
1488 
1489     for (y=yl ; y<=yh ; y++)
1490 	for (x=xl ; x<=xh ; x++)
1491 	    P_BlockThingsIterator (x, y, PIT_RadiusAttack );
1492 }
1493 
1494 
1495 
1496 //
1497 // SECTOR HEIGHT CHANGING
1498 // After modifying a sectors floor or ceiling height,
1499 // call this routine to adjust the positions
1500 // of all things that touch the sector.
1501 //
1502 // If anything doesn't fit anymore, true will be returned.
1503 // If crunch is true, they will take damage
1504 //  as they are being crushed.
1505 // If Crunch is false, you should set the sector height back
1506 //  the way it was and call P_ChangeSector again
1507 //  to undo the changes.
1508 //
1509 boolean		crushchange;
1510 boolean		nofit;
1511 
1512 
1513 //
1514 // PIT_ChangeSector
1515 //
PIT_ChangeSector(mobj_t * thing)1516 boolean PIT_ChangeSector (mobj_t*	thing)
1517 {
1518     mobj_t*	mo;
1519 
1520     if (P_ThingHeightClip (thing))
1521     {
1522 	// keep checking
1523 	return true;
1524     }
1525 
1526 
1527     // crunch bodies to giblets
1528     if (thing->health <= 0)
1529     {
1530 	P_SetMobjState (thing, S_GIBS);
1531 
1532 	// [crispy] no blood, no giblets
1533 	if (crispy->coloredblood && (thing->flags & MF_NOBLOOD))
1534 	{
1535 		thing->sprite = SPR_TNT1;
1536 	}
1537 
1538     if (gameversion > exe_doom_1_2)
1539 	    thing->flags &= ~MF_SOLID;
1540 	thing->height = 0;
1541 	thing->radius = 0;
1542 
1543 	// [crispy] connect giblet object with the crushed monster
1544 	thing->target = thing;
1545 
1546 	// keep checking
1547 	return true;
1548     }
1549 
1550     // crunch dropped items
1551     if (thing->flags & MF_DROPPED)
1552     {
1553 	P_RemoveMobj (thing);
1554 
1555 	// keep checking
1556 	return true;
1557     }
1558 
1559     if (! (thing->flags & MF_SHOOTABLE) )
1560     {
1561 	// assume it is bloody gibs or something
1562 	return true;
1563     }
1564 
1565     nofit = true;
1566 
1567     if (crushchange && !(leveltime&3) )
1568     {
1569 	P_DamageMobj(thing,NULL,NULL,10);
1570 
1571 	// spray blood in a random direction
1572 	mo = P_SpawnMobj (thing->x,
1573 			  thing->y,
1574 			  // [crispy] Lost Souls and Barrels bleed Puffs
1575 			  thing->z + thing->height/2, (thing->flags & MF_NOBLOOD) ? MT_PUFF : MT_BLOOD);
1576 
1577 	mo->momx = P_SubRandom() << 12;
1578 	mo->momy = P_SubRandom() << 12;
1579 
1580 	// [crispy] connect blood object with the monster that bleeds it
1581 	mo->target = thing;
1582 
1583 	// [crispy] Spectres bleed spectre blood
1584 	if (crispy->coloredblood)
1585 	    mo->flags |= (thing->flags & MF_SHADOW);
1586     }
1587 
1588     // keep checking (crush other things)
1589     return true;
1590 }
1591 
1592 
1593 
1594 //
1595 // P_ChangeSector
1596 //
1597 boolean
P_ChangeSector(sector_t * sector,boolean crunch)1598 P_ChangeSector
1599 ( sector_t*	sector,
1600   boolean	crunch )
1601 {
1602     int		x;
1603     int		y;
1604 
1605     nofit = false;
1606     crushchange = crunch;
1607 
1608     // re-check heights for all things near the moving sector
1609     for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++)
1610 	for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++)
1611 	    P_BlockThingsIterator (x, y, PIT_ChangeSector);
1612 
1613 
1614     return nofit;
1615 }
1616 
1617 // Code to emulate the behavior of Vanilla Doom when encountering an overrun
1618 // of the spechit array.  This is by Andrey Budko (e6y) and comes from his
1619 // PrBoom plus port.  A big thanks to Andrey for this.
1620 
SpechitOverrun(line_t * ld)1621 static void SpechitOverrun(line_t *ld)
1622 {
1623     static unsigned int baseaddr = 0;
1624     unsigned int addr;
1625 
1626     if (baseaddr == 0)
1627     {
1628         int p;
1629 
1630         // This is the first time we have had an overrun.  Work out
1631         // what base address we are going to use.
1632         // Allow a spechit value to be specified on the command line.
1633 
1634         //!
1635         // @category compat
1636         // @arg <n>
1637         //
1638         // Use the specified magic value when emulating spechit overruns.
1639         //
1640 
1641         p = M_CheckParmWithArgs("-spechit", 1);
1642 
1643         if (p > 0)
1644         {
1645             M_StrToInt(myargv[p+1], (int *) &baseaddr);
1646         }
1647         else
1648         {
1649             baseaddr = DEFAULT_SPECHIT_MAGIC;
1650         }
1651     }
1652 
1653     // Calculate address used in doom2.exe
1654 
1655     addr = baseaddr + (ld - lines) * 0x3E;
1656 
1657     switch(numspechit)
1658     {
1659         case 9:
1660         case 10:
1661         case 11:
1662         case 12:
1663             tmbbox[numspechit-9] = addr;
1664             break;
1665         case 13:
1666             crushchange = addr;
1667             break;
1668         case 14:
1669             nofit = addr;
1670             break;
1671         default:
1672             fprintf(stderr, "SpechitOverrun: Warning: unable to emulate"
1673                             "an overrun where numspechit=%i\n",
1674                             numspechit);
1675             break;
1676     }
1677 }
1678 
1679