Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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