Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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