1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file p_map.c
12 /// \brief Movement, collision handling
13 ///
14 /// Shooting and aiming
15
16 #include "doomdef.h"
17 #include "g_game.h"
18 #include "m_bbox.h"
19 #include "m_random.h"
20 #include "p_local.h"
21 #include "p_setup.h" // NiGHTS stuff
22 #include "r_state.h"
23 #include "r_main.h"
24 #include "r_sky.h"
25 #include "s_sound.h"
26 #include "w_wad.h"
27
28 #include "r_splats.h"
29
30 #include "p_slopes.h"
31
32 #include "z_zone.h"
33
34 #include "lua_hook.h"
35
36 #include "m_perfstats.h" // ps_checkposition_calls
37
38 fixed_t tmbbox[4];
39 mobj_t *tmthing;
40 static INT32 tmflags;
41 fixed_t tmx;
42 fixed_t tmy;
43
44 static precipmobj_t *tmprecipthing;
45 static fixed_t preciptmbbox[4];
46
47 // If "floatok" true, move would be ok
48 // if within "tmfloorz - tmceilingz".
49 boolean floatok;
50
51 fixed_t tmfloorz, tmceilingz;
52 static fixed_t tmdropoffz, tmdrpoffceilz; // drop-off floor/ceiling heights
53 mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz is from a sector
54 mobj_t *tmhitthing; // the solid thing you bumped into (for collisions)
55 ffloor_t *tmfloorrover, *tmceilingrover;
56 pslope_t *tmfloorslope, *tmceilingslope;
57
58 // keep track of the line that lowers the ceiling,
59 // so missiles don't explode against sky hack walls
60 line_t *ceilingline;
61
62 // set by PIT_CheckLine() for any line that stopped the PIT_CheckLine()
63 // that is, for any line which is 'solid'
64 line_t *blockingline;
65
66 msecnode_t *sector_list = NULL;
67 mprecipsecnode_t *precipsector_list = NULL;
68 camera_t *mapcampointer;
69
70 //
71 // TELEPORT MOVE
72 //
73
74 //
75 // P_TeleportMove
76 //
P_TeleportMove(mobj_t * thing,fixed_t x,fixed_t y,fixed_t z)77 boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z)
78 {
79 // the move is ok,
80 // so link the thing into its new position
81 P_UnsetThingPosition(thing);
82
83 // Remove touching_sectorlist from mobj.
84 if (sector_list)
85 {
86 P_DelSeclist(sector_list);
87 sector_list = NULL;
88 }
89
90 thing->x = x;
91 thing->y = y;
92 thing->z = z;
93
94 P_SetThingPosition(thing);
95
96 P_CheckPosition(thing, thing->x, thing->y);
97
98 if (P_MobjWasRemoved(thing))
99 return true;
100
101 thing->floorz = tmfloorz;
102 thing->ceilingz = tmceilingz;
103 thing->floorrover = tmfloorrover;
104 thing->ceilingrover = tmceilingrover;
105
106 return true;
107 }
108
109 // =========================================================================
110 // MOVEMENT ITERATOR FUNCTIONS
111 // =========================================================================
112
113 // P_DoSpring
114 //
115 // MF_SPRING does some weird, mildly hacky stuff sometimes.
116 // mass = vertical speed
117 // damage = horizontal speed
118 // raisestate = state to change spring to on collision
119 // reactiontime = number of times it can give 10 points (0 is standard)
120 // painchance = spring mode:
121 // 0 = standard vanilla spring behaviour
122 // Positive spring modes are minor variants of vanilla spring behaviour.
123 // 1 = launch players in jump
124 // 2 = don't modify player at all, just add momentum
125 // 3 = speed-booster mode (force onto ground, MF_AMBUSH causes auto-spin)
126 // Negative spring modes are mildly-related gimmicks with customisation.
127 // -1 = pinball bumper
128 // Any other spring mode defaults to standard vanilla spring behaviour,
129 // ****** but forward compatibility is not guaranteed for these. ******
130 //
131
P_DoSpring(mobj_t * spring,mobj_t * object)132 boolean P_DoSpring(mobj_t *spring, mobj_t *object)
133 {
134 fixed_t vertispeed = spring->info->mass;
135 fixed_t horizspeed = spring->info->damage;
136 boolean final = false;
137 UINT8 strong = 0;
138
139 // Object was already sprung this tic
140 if (object->eflags & MFE_SPRUNG)
141 return false;
142
143 // Spectators don't trigger springs.
144 if (object->player && object->player->spectator)
145 return false;
146
147 // "Even in Death" is a song from Volume 8, not a command.
148 if (!spring->health || !object->health)
149 return false;
150
151 if (object->player)
152 {
153 if (spring->info->painchance == 3)
154 ;
155 else if (object->player->charability == CA_TWINSPIN && object->player->panim == PA_ABILITY)
156 strong = 1;
157 else if (object->player->charability2 == CA2_MELEE && object->player->panim == PA_ABILITY2)
158 strong = 2;
159 }
160
161 if (spring->info->painchance == -1) // Pinball bumper mode.
162 {
163 // The first of the entirely different spring modes!
164 // Some of the attributes mean different things here.
165 // mass = default strength (can be controlled by mapthing's spawnangle)
166 // damage = unused
167 angle_t horizangle, vertiangle;
168
169 if (!vertispeed)
170 return false;
171
172 if (object->player && object->player->homing) // Sonic Heroes and Shadow the Hedgehog are the only games to contain homing-attackable bumpers!
173 {
174 horizangle = 0;
175 vertiangle = ((object->eflags & MFE_VERTICALFLIP) ? ANGLE_270 : ANGLE_90) >> ANGLETOFINESHIFT;
176 object->player->pflags &= ~PF_THOKKED;
177 if (spring->eflags & MFE_VERTICALFLIP)
178 object->z = spring->z - object->height - 1;
179 else
180 object->z = spring->z + spring->height + 1;
181 }
182 else
183 {
184 horizangle = R_PointToAngle2(spring->x, spring->y, object->x, object->y);
185 vertiangle = (R_PointToAngle2(
186 0,
187 spring->z + spring->height/2,
188 FixedHypot(object->x - spring->x, object->y - spring->y),
189 object->z + object->height/2)
190 >> ANGLETOFINESHIFT) & FINEMASK;
191 }
192
193 if (spring->spawnpoint && spring->spawnpoint->angle > 0)
194 vertispeed = (spring->spawnpoint->angle<<(FRACBITS-1))/5;
195 vertispeed = FixedMul(vertispeed, FixedMul(object->scale, spring->scale));
196
197 if (object->player)
198 {
199 fixed_t playervelocity;
200
201 if (strong)
202 vertispeed <<= 1;
203
204 if (!(object->player->pflags & PF_THOKKED) && !(object->player->homing)
205 && ((playervelocity = FixedDiv(9*FixedHypot(object->player->speed, object->momz), 10<<FRACBITS)) > vertispeed))
206 vertispeed = playervelocity;
207
208 if (object->player->powers[pw_carry] == CR_NIGHTSMODE) // THIS has NiGHTS support, at least...
209 {
210 angle_t nightsangle = 0;
211
212 if (object->player->bumpertime > (TICRATE/2)-5)
213 return false;
214
215 if ((object->player->pflags & PF_TRANSFERTOCLOSEST) && object->player->axis1 && object->player->axis2)
216 {
217 nightsangle = R_PointToAngle2(object->player->axis1->x, object->player->axis1->y, object->player->axis2->x, object->player->axis2->y);
218 nightsangle += ANGLE_90;
219 }
220 else if (object->target)
221 {
222 if (object->target->flags2 & MF2_AMBUSH)
223 nightsangle = R_PointToAngle2(object->target->x, object->target->y, object->x, object->y);
224 else
225 nightsangle = R_PointToAngle2(object->x, object->y, object->target->x, object->target->y);
226 }
227
228 object->player->flyangle = AngleFixed(R_PointToAngle2(
229 0,
230 spring->z + spring->height/2,
231 FixedMul(
232 FINESINE(((nightsangle - horizangle) >> ANGLETOFINESHIFT) & FINEMASK),
233 FixedHypot(object->x - spring->x, object->y - spring->y)),
234 object->z + object->height/2))>>FRACBITS;
235
236 object->player->bumpertime = TICRATE/2;
237 }
238 else
239 {
240 INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // Not identical to below...
241 UINT8 secondjump = object->player->secondjump;
242 if (object->player->pflags & PF_GLIDING)
243 P_SetPlayerMobjState(object, S_PLAY_FALL);
244 P_ResetPlayer(object->player);
245 object->player->pflags |= pflags;
246 object->player->secondjump = secondjump;
247 }
248 }
249
250 if (!P_IsObjectOnGround(object)) // prevents uncurling when spinning due to "landing"
251 object->momz = FixedMul(vertispeed, FINESINE(vertiangle));
252 P_InstaThrust(object, horizangle, FixedMul(vertispeed, FINECOSINE(vertiangle)));
253
254 object->eflags |= MFE_SPRUNG; // apply this flag asap!
255
256 goto springstate;
257 }
258
259 // Does nothing?
260 if (!vertispeed && !horizspeed)
261 return false;
262
263 object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you.
264
265 if (spring->eflags & MFE_VERTICALFLIP)
266 vertispeed *= -1;
267
268 if (object->player && (object->player->powers[pw_carry] == CR_NIGHTSMODE))
269 {
270 /*Someone want to make these work like bumpers?*/
271 return false;
272 }
273
274 if (strong)
275 {
276 if (horizspeed)
277 horizspeed = FixedMul(horizspeed, (4*FRACUNIT)/3);
278 if (vertispeed)
279 vertispeed = FixedMul(vertispeed, (6*FRACUNIT)/5); // aprox square root of above
280 }
281
282 object->eflags |= MFE_SPRUNG; // apply this flag asap!
283 spring->flags &= ~(MF_SPRING|MF_SPECIAL); // De-solidify
284
285 if (spring->info->painchance != 2)
286 {
287 if (object->player)
288 {
289 object->player->pflags &= ~PF_APPLYAUTOBRAKE;
290 #ifndef SPRINGSPIN
291 object->player->powers[pw_justsprung] = 5;
292 if (horizspeed)
293 object->player->powers[pw_noautobrake] = ((horizspeed*TICRATE)>>(FRACBITS+3))/9; // TICRATE at 72*FRACUNIT
294 else if (P_MobjFlip(object) == P_MobjFlip(spring))
295 object->player->powers[pw_justsprung] |= (1<<15);
296 #else
297 object->player->powers[pw_justsprung] = 15;
298 if (horizspeed)
299 object->player->powers[pw_noautobrake] = ((horizspeed*TICRATE)>>(FRACBITS+3))/9; // TICRATE at 72*FRACUNIT
300 else
301 {
302 if (abs(object->player->rmomx) > object->scale || abs(object->player->rmomy) > object->scale)
303 object->player->drawangle = R_PointToAngle2(0, 0, object->player->rmomx, object->player->rmomy);
304 if (P_MobjFlip(object) == P_MobjFlip(spring))
305 object->player->powers[pw_justsprung] |= (1<<15);
306 }
307 #endif
308 }
309
310 if ((horizspeed && vertispeed) || (object->player && object->player->homing)) // Mimic SA
311 {
312 object->momx = object->momy = 0;
313 P_TryMove(object, spring->x, spring->y, true);
314 }
315
316 if (vertispeed > 0)
317 object->z = spring->z + spring->height + 1;
318 else if (vertispeed < 0)
319 object->z = spring->z - object->height - 1;
320 else
321 {
322 fixed_t offx, offy;
323 // Horizontal springs teleport you in FRONT of them.
324 object->momx = object->momy = 0;
325
326 // Overestimate the distance to position you at
327 offx = P_ReturnThrustX(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
328 offy = P_ReturnThrustY(spring, spring->angle, (spring->radius + object->radius + 1) * 2);
329
330 // Make it square by clipping
331 if (offx > (spring->radius + object->radius + 1))
332 offx = spring->radius + object->radius + 1;
333 else if (offx < -(spring->radius + object->radius + 1))
334 offx = -(spring->radius + object->radius + 1);
335
336 if (offy > (spring->radius + object->radius + 1))
337 offy = spring->radius + object->radius + 1;
338 else if (offy < -(spring->radius + object->radius + 1))
339 offy = -(spring->radius + object->radius + 1);
340
341 // Set position!
342 P_TryMove(object, spring->x + offx, spring->y + offy, true);
343
344 if ((spring->info->painchance == 3))
345 {
346 object->z = spring->z;
347 if (spring->eflags & MFE_VERTICALFLIP)
348 object->z -= object->height;
349 object->momz = 0;
350 }
351 }
352 }
353
354 if (vertispeed)
355 object->momz = FixedMul(vertispeed,FixedSqrt(FixedMul(object->scale, spring->scale)));
356
357 if (horizspeed)
358 P_InstaThrustEvenIn2D(object, spring->angle, FixedMul(horizspeed,FixedSqrt(FixedMul(object->scale, spring->scale))));
359
360 // Re-solidify
361 spring->flags |= (spring->info->flags & (MF_SPRING|MF_SPECIAL));
362
363 if (object->player)
364 {
365 INT32 pflags;
366 UINT8 secondjump;
367 boolean washoming;
368
369 if (spring->flags & MF_ENEMY) // Spring shells
370 P_SetTarget(&spring->target, object);
371
372 if (horizspeed)
373 {
374 object->angle = object->player->drawangle = spring->angle;
375
376 if (!demoplayback || P_ControlStyle(object->player) == CS_LMAOGALOG)
377 P_SetPlayerAngle(object->player, spring->angle);
378 }
379
380 if (object->player->pflags & PF_GLIDING)
381 P_SetPlayerMobjState(object, S_PLAY_FALL);
382 if ((spring->info->painchance == 3))
383 {
384 if (!(pflags = (object->player->pflags & PF_SPINNING)) &&
385 (((object->player->charability2 == CA2_SPINDASH) && (object->player->cmd.buttons & BT_SPIN))
386 || (spring->flags2 & MF2_AMBUSH)))
387 {
388 pflags = PF_SPINNING;
389 P_SetPlayerMobjState(object, S_PLAY_ROLL);
390 S_StartSound(object, sfx_spin);
391 }
392 else
393 P_SetPlayerMobjState(object, S_PLAY_ROLL);
394 }
395 else
396 pflags = object->player->pflags & (PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // I still need these.
397 secondjump = object->player->secondjump;
398 washoming = object->player->homing;
399 P_ResetPlayer(object->player);
400
401 if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities.
402 {
403 object->player->pflags |= P_GetJumpFlags(object->player);
404 P_SetPlayerMobjState(object, S_PLAY_JUMP);
405 }
406 else if ((spring->info->painchance == 2) || ((spring->info->painchance != 3) && (pflags & PF_BOUNCING))) // Adding momentum only.
407 {
408 object->player->pflags |= (pflags &~ PF_STARTJUMP);
409 object->player->secondjump = secondjump;
410 if (washoming)
411 object->player->pflags &= ~PF_THOKKED;
412 }
413 else if (!vertispeed)
414 {
415 if (pflags & (PF_JUMPED|PF_SPINNING))
416 {
417 object->player->pflags |= pflags;
418 object->player->secondjump = secondjump;
419 }
420 else if (object->player->dashmode >= DASHMODE_THRESHOLD)
421 P_SetPlayerMobjState(object, S_PLAY_DASH);
422 else if (P_IsObjectOnGround(object) && horizspeed >= FixedMul(object->player->runspeed, object->scale))
423 P_SetPlayerMobjState(object, S_PLAY_RUN);
424 else
425 P_SetPlayerMobjState(object, S_PLAY_WALK);
426 }
427 else if (P_MobjFlip(object)*vertispeed > 0)
428 P_SetPlayerMobjState(object, S_PLAY_SPRING);
429 else
430 P_SetPlayerMobjState(object, S_PLAY_FALL);
431 }
432
433 object->standingslope = NULL; // And again.
434
435 final = true;
436
437 springstate:
438 if ((statenum_t)(spring->state-states) < spring->info->raisestate)
439 {
440 P_SetMobjState(spring, spring->info->raisestate);
441 if (object->player && spring->reactiontime && !(spring->info->flags & MF_ENEMY))
442 {
443 if (object->player->powers[pw_carry] != CR_NIGHTSMODE) // don't make graphic in NiGHTS
444 P_SetMobjState(P_SpawnMobj(spring->x, spring->y, spring->z + (spring->height/2), MT_SCORE), mobjinfo[MT_SCORE].spawnstate+11);
445 P_AddPlayerScore(object->player, 10);
446 spring->reactiontime--;
447 }
448
449 if (strong)
450 {
451 P_TwinSpinRejuvenate(object->player, (strong == 1 ? object->player->thokitem : object->player->revitem));
452 S_StartSound(object, sfx_sprong); // strong spring. sprong.
453 }
454 }
455
456 return final;
457 }
458
P_DoFanAndGasJet(mobj_t * spring,mobj_t * object)459 static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
460 {
461 player_t *p = object->player; // will be NULL if not a player
462 fixed_t zdist; // distance between bottoms
463 fixed_t speed = spring->info->mass; // conveniently, both fans and gas jets use this for the vertical thrust
464 SINT8 flipval = P_MobjFlip(spring); // virtually everything here centers around the thruster's gravity, not the object's!
465
466 if (p && object->state == &states[object->info->painstate]) // can't use fans and gas jets when player is in pain!
467 return;
468
469 // is object's top below thruster's position? if not, calculate distance between their bottoms
470 if (spring->eflags & MFE_VERTICALFLIP)
471 {
472 if (object->z > spring->z + spring->height)
473 return;
474 zdist = (spring->z + spring->height) - (object->z + object->height);
475 }
476 else
477 {
478 if (object->z + object->height < spring->z)
479 return;
480 zdist = object->z - spring->z;
481 }
482
483 object->standingslope = NULL; // No launching off at silly angles for you.
484
485 switch (spring->type)
486 {
487 case MT_FAN: // fan
488 if (zdist > (spring->health << FRACBITS)) // max z distance determined by health (set by map thing angle)
489 break;
490 if (flipval*object->momz >= FixedMul(speed, spring->scale)) // if object's already moving faster than your best, don't bother
491 break;
492 if (p && (p->climbing || p->pflags & PF_GLIDING)) // doesn't affect Knux when he's using his abilities!
493 break;
494
495 object->momz += flipval*FixedMul(speed/4, spring->scale);
496
497 // limit the speed if too high
498 if (flipval*object->momz > FixedMul(speed, spring->scale))
499 object->momz = flipval*FixedMul(speed, spring->scale);
500
501 if (p && !p->powers[pw_tailsfly]) // doesn't reset anim for Tails' flight
502 {
503 P_ResetPlayer(p);
504 if (p->panim != PA_FALL)
505 P_SetPlayerMobjState(object, S_PLAY_FALL);
506 }
507 break;
508 case MT_STEAM: // Steam
509 if (zdist > FixedMul(16*FRACUNIT, spring->scale))
510 break;
511 if (spring->state != &states[S_STEAM1]) // Only when it bursts
512 break;
513
514 object->momz = flipval*FixedMul(speed, FixedSqrt(FixedMul(spring->scale, object->scale))); // scale the speed with both objects' scales, just like with springs!
515
516 if (p)
517 {
518 P_ResetPlayer(p);
519 if (p->panim != PA_FALL)
520 P_SetPlayerMobjState(object, S_PLAY_FALL);
521 }
522 break;
523 default:
524 break;
525 }
526 }
527
P_DoPterabyteCarry(player_t * player,mobj_t * ptera)528 static void P_DoPterabyteCarry(player_t *player, mobj_t *ptera)
529 {
530 if (player->powers[pw_carry] && player->powers[pw_carry] != CR_ROLLOUT)
531 return;
532 if (player->powers[pw_ignorelatch] & (1<<15))
533 return;
534 if (ptera->extravalue1 != 1)
535 return; // Not swooping
536 if (ptera->target != player->mo)
537 return; // Not swooping for you!
538
539 if (player->spectator)
540 return;
541
542 if ((player->mo->eflags & MFE_VERTICALFLIP) != (ptera->eflags & MFE_VERTICALFLIP))
543 return; // Both should be in same gravity
544
545 if (ptera->eflags & MFE_VERTICALFLIP)
546 {
547 if (ptera->ceilingz - (ptera->z + ptera->height) < player->mo->height - FixedMul(2*FRACUNIT, player->mo->scale))
548 return;
549 }
550 else if (ptera->z - ptera->floorz < player->mo->height - FixedMul(2*FRACUNIT, player->mo->scale))
551 return; // No room to pick up this guy!
552
553 P_ResetPlayer(player);
554 P_SetTarget(&player->mo->tracer, ptera);
555 player->pflags &= ~PF_APPLYAUTOBRAKE;
556 player->powers[pw_carry] = CR_PTERABYTE;
557 S_StartSound(player->mo, sfx_s3k4a);
558 P_UnsetThingPosition(player->mo);
559 player->mo->x = ptera->x;
560 player->mo->y = ptera->y;
561 P_SetThingPosition(player->mo);
562 ptera->movefactor = 3*TICRATE;
563 ptera->watertop = ptera->waterbottom = ptera->cusval = 0;
564 }
565
P_DoTailsCarry(player_t * sonic,player_t * tails)566 static void P_DoTailsCarry(player_t *sonic, player_t *tails)
567 {
568 INT32 p;
569 fixed_t zdist; // z distance between the two players' bottoms
570
571 if (tails->powers[pw_carry])
572 return;
573 if (sonic->powers[pw_carry])
574 return;
575
576 if (tails->spectator)
577 return;
578 if (sonic->spectator)
579 return;
580
581 if (!(tails->pflags & PF_CANCARRY))
582 return;
583
584 if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
585 return; // Both should be in same gravity
586
587 if (tails->mo->eflags & MFE_VERTICALFLIP)
588 {
589 if (tails->mo->ceilingz - (tails->mo->z + tails->mo->height) < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
590 return;
591 }
592 else if (tails->mo->z - tails->mo->floorz < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
593 return; // No room to pick up this guy!
594
595 // Search in case another player is already being carried by this fox.
596 for (p = 0; p < MAXPLAYERS; p++)
597 if (playeringame[p] && players[p].mo
598 && players[p].powers[pw_carry] == CR_PLAYER && players[p].mo->tracer == tails->mo)
599 return;
600
601 // Why block opposing teams from tailsflying each other?
602 // Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows.
603 /*
604 if ((gametyperules & GTR_RACE)
605 || (netgame && (tails->spectator || sonic->spectator))
606 || (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT)))
607 || (gametype == GT_MATCH)
608 || (G_GametypeHasTeams() && tails->ctfteam != sonic->ctfteam))
609 return; */
610
611 if (tails->mo->eflags & MFE_VERTICALFLIP)
612 zdist = (sonic->mo->z + sonic->mo->height) - (tails->mo->z + tails->mo->height);
613 else
614 zdist = tails->mo->z - sonic->mo->z;
615
616 if (zdist <= sonic->mo->height + sonic->mo->scale // FixedMul(FRACUNIT, sonic->mo->scale), but scale == FRACUNIT by default
617 && zdist > sonic->mo->height*2/3
618 && P_MobjFlip(tails->mo)*sonic->mo->momz <= 0
619 && !(sonic->powers[pw_ignorelatch] & (1<<15)))
620 {
621 if (sonic-players == consoleplayer && botingame)
622 CV_SetValue(&cv_analog[1], false);
623 P_ResetPlayer(sonic);
624 P_SetTarget(&sonic->mo->tracer, tails->mo);
625 sonic->powers[pw_carry] = CR_PLAYER;
626 S_StartSound(sonic->mo, sfx_s3k4a);
627 P_UnsetThingPosition(sonic->mo);
628 sonic->mo->x = tails->mo->x;
629 sonic->mo->y = tails->mo->y;
630 P_SetThingPosition(sonic->mo);
631 }
632 else {
633 if (sonic-players == consoleplayer && botingame)
634 CV_SetValue(&cv_analog[1], true);
635 P_SetTarget(&sonic->mo->tracer, NULL);
636 sonic->powers[pw_carry] = CR_NONE;
637 }
638 }
639
640 // Boss 5 post-defeat comedy
P_SlapStick(mobj_t * fang,mobj_t * pole)641 static void P_SlapStick(mobj_t *fang, mobj_t *pole)
642 {
643 fixed_t momx1, momx2, momy1, momy2;
644
645 #define dist 3
646 momx1 = pole->momx/dist;
647 momy1 = pole->momy/dist;
648 momx2 = fang->momx/dist;
649 momy2 = fang->momy/dist;
650
651 pole->tracer->tracer->momx = momx1 + (dist-1)*momx2;
652 pole->tracer->tracer->momy = momy1 + (dist-1)*momy2;
653 fang->momx = (dist-1)*momx1 + momx2;
654 fang->momy = (dist-1)*momy1 + momy2;
655 #undef dist
656
657 P_SetObjectMomZ(pole->tracer->tracer, 6*FRACUNIT, false);
658 pole->tracer->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP);
659 pole->tracer->tracer->movedir = ANGLE_67h;
660 if ((R_PointToAngle(fang->x - pole->tracer->tracer->x, fang->y - pole->tracer->tracer->y) - pole->angle) > ANGLE_180)
661 pole->tracer->tracer->movedir = InvAngle(pole->tracer->movedir);
662
663 P_SetObjectMomZ(fang, 14*FRACUNIT, false);
664 fang->flags |= MF_NOGRAVITY|MF_NOCLIP;
665 P_SetMobjState(fang, fang->info->xdeathstate);
666
667 pole->tracer->tracer->tics = pole->tracer->tics = pole->tics = fang->tics;
668
669 var1 = var2 = 0;
670 A_Scream(pole->tracer->tracer);
671 S_StartSound(fang, sfx_altdi1);
672
673 P_SetTarget(&pole->tracer->tracer, NULL);
674 P_SetMobjState(pole->tracer, pole->info->xdeathstate);
675 P_SetTarget(&pole->tracer, NULL);
676 P_SetMobjState(pole, pole->info->deathstate);
677 }
678
P_PlayerBarrelCollide(mobj_t * toucher,mobj_t * barrel)679 static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel)
680 {
681 if (toucher->momz < 0)
682 {
683 if (toucher->z + toucher->momz > barrel->z + barrel->height)
684 return;
685 }
686 else
687 {
688 if (toucher->z > barrel->z + barrel->height)
689 return;
690 }
691
692 if (toucher->momz > 0)
693 {
694 if (toucher->z + toucher->height + toucher->momz < barrel->z)
695 return;
696 }
697 else
698 {
699 if (toucher->z + toucher->height < barrel->z)
700 return;
701 }
702
703 if (P_PlayerCanDamage(toucher->player, barrel))
704 P_DamageMobj(barrel, toucher, toucher, 1, 0);
705 }
706
707 //
708 // PIT_CheckThing
709 //
PIT_CheckThing(mobj_t * thing)710 static boolean PIT_CheckThing(mobj_t *thing)
711 {
712 fixed_t blockdist;
713
714 // don't clip against self
715 if (thing == tmthing)
716 return true;
717
718 // Ignore... things.
719 if (!tmthing || !thing || P_MobjWasRemoved(thing))
720 return true;
721
722 I_Assert(!P_MobjWasRemoved(tmthing));
723 I_Assert(!P_MobjWasRemoved(thing));
724
725 // Ignore spectators
726 if ((tmthing->player && tmthing->player->spectator)
727 || (thing->player && thing->player->spectator))
728 return true;
729
730 // Do name checks all the way up here
731 // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
732 if (tmthing->type == MT_NAMECHECK)
733 {
734 // Ignore things that aren't players, ignore spectators, ignore yourself.
735 if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
736 return true;
737
738 // Now check that you actually hit them.
739 blockdist = thing->radius + tmthing->radius;
740 if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
741 return true; // didn't hit it
742 // see if it went over / under
743 if (tmthing->z > thing->z + thing->height)
744 return true; // overhead
745 if (tmthing->z + tmthing->height < thing->z)
746 return true; // underneath
747
748 // REX HAS SEEN YOU
749 if (!LUAh_SeenPlayer(tmthing->target->player, thing->player))
750 return false;
751
752 seenplayer = thing->player;
753 return false;
754 }
755
756 // Metal Sonic destroys tiny baby objects.
757 if (tmthing->type == MT_METALSONIC_RACE
758 && (thing->flags & (MF_MISSILE|MF_ENEMY|MF_BOSS)
759 || (thing->type == MT_SPIKE
760 || thing->type == MT_WALLSPIKE)))
761 {
762 if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
763 return true;
764 blockdist = thing->radius + tmthing->radius;
765 if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
766 return true; // didn't hit it
767 // see if it went over / under
768 if (tmthing->z > thing->z + thing->height)
769 return true; // overhead
770 if (tmthing->z + tmthing->height < thing->z)
771 return true; // underneath
772 if (thing->type == MT_SPIKE
773 || thing->type == MT_WALLSPIKE)
774 {
775 mobj_t *iter;
776 if (thing->flags & MF_SOLID)
777 S_StartSound(tmthing, thing->info->deathsound);
778 for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
779 if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
780 P_KillMobj(iter, tmthing, tmthing, 0);
781 }
782 else
783 {
784 thing->health = 0;
785 P_KillMobj(thing, tmthing, tmthing, 0);
786 }
787 return true;
788 }
789
790 // SF_DASHMODE users destroy spikes and monitors, CA_TWINSPIN users and CA2_MELEE users destroy spikes.
791 if ((tmthing->player)
792 && ((((tmthing->player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)) && (tmthing->player->dashmode >= DASHMODE_THRESHOLD)
793 && (thing->flags & (MF_MONITOR)
794 || (thing->type == MT_SPIKE
795 || thing->type == MT_WALLSPIKE)))
796 || ((((tmthing->player->charability == CA_TWINSPIN) && (tmthing->player->panim == PA_ABILITY))
797 || (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2))
798 && (thing->type == MT_SPIKE
799 || thing->type == MT_WALLSPIKE))))
800 {
801 if ((thing->flags & (MF_MONITOR)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
802 return true;
803 blockdist = thing->radius + tmthing->radius;
804 if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
805 return true; // didn't hit it
806 // see if it went over / under
807 if (tmthing->z > thing->z + thing->height)
808 return true; // overhead
809 if (tmthing->z + tmthing->height < thing->z)
810 return true; // underneath
811 if (thing->type == MT_SPIKE
812 || thing->type == MT_WALLSPIKE)
813 {
814 mobj_t *iter;
815 if (thing->flags & MF_SOLID)
816 S_StartSound(tmthing, thing->info->deathsound);
817 for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
818 if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
819 P_KillMobj(iter, tmthing, tmthing, 0);
820 return true;
821 }
822 else
823 {
824 if (P_DamageMobj(thing, tmthing, tmthing, 1, 0))
825 return true;
826 }
827 }
828
829 // vectorise metal - done in a special case as at this point neither has the right flags for touching
830 if (thing->type == MT_METALSONIC_BATTLE
831 && (tmthing->flags & MF_MISSILE)
832 && tmthing->target != thing
833 && thing->state == &states[thing->info->spawnstate])
834 {
835 blockdist = thing->radius + tmthing->radius;
836
837 if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
838 return true; // didn't hit it
839
840 if (tmthing->z > thing->z + thing->height)
841 return true; // overhead
842 if (tmthing->z + tmthing->height < thing->z)
843 return true; // underneath
844
845 thing->flags2 |= MF2_CLASSICPUSH;
846
847 return true;
848 }
849
850 if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
851 return true;
852
853 // Don't collide with your buddies while NiGHTS-flying.
854 if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
855 && ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE)))
856 return true;
857
858 blockdist = thing->radius + tmthing->radius;
859
860 if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
861 return true; // didn't hit it
862
863 if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing.
864 {
865 fixed_t cosradius, sinradius;
866 vertex_t v1, v2; // fake vertexes
867 line_t junk; // fake linedef
868
869 cosradius = FixedMul(thing->radius, FINECOSINE(thing->angle>>ANGLETOFINESHIFT));
870 sinradius = FixedMul(thing->radius, FINESINE(thing->angle>>ANGLETOFINESHIFT));
871
872 v1.x = thing->x - cosradius;
873 v1.y = thing->y - sinradius;
874 v2.x = thing->x + cosradius;
875 v2.y = thing->y + sinradius;
876
877 junk.v1 = &v1;
878 junk.v2 = &v2;
879 junk.dx = 2*cosradius; // v2.x - v1.x;
880 junk.dy = 2*sinradius; // v2.y - v1.y;
881
882 if (tmthing->flags & MF_PAPERCOLLISION) // more strenuous checking to prevent clipping issues
883 {
884 INT32 check1, check2, check3, check4;
885 fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
886 fixed_t tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
887 if (abs(thing->x - tmx) >= (abs(tmcosradius) + abs(cosradius)) || abs(thing->y - tmy) >= (abs(tmsinradius) + abs(sinradius)))
888 return true; // didn't hit it
889 check1 = P_PointOnLineSide(tmx - tmcosradius, tmy - tmsinradius, &junk);
890 check2 = P_PointOnLineSide(tmx + tmcosradius, tmy + tmsinradius, &junk);
891 check3 = P_PointOnLineSide(tmx + tmthing->momx - tmcosradius, tmy + tmthing->momy - tmsinradius, &junk);
892 check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk);
893 if ((check1 == check2) && (check2 == check3) && (check3 == check4))
894 return true; // the line doesn't cross between collider's start or end
895 }
896 else
897 {
898 if (abs(thing->x - tmx) >= (tmthing->radius + abs(cosradius)) || abs(thing->y - tmy) >= (tmthing->radius + abs(sinradius)))
899 return true; // didn't hit it
900 if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk)
901 == P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk))
902 && (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk)
903 == P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk)))
904 return true; // the line doesn't cross between either pair of opposite corners
905 }
906 }
907 else if (tmthing->flags & MF_PAPERCOLLISION)
908 {
909 fixed_t tmcosradius, tmsinradius;
910 vertex_t v1, v2; // fake vertexes
911 line_t junk; // fake linedef
912
913 tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
914 tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
915
916 if (abs(thing->x - tmx) >= (thing->radius + abs(tmcosradius)) || abs(thing->y - tmy) >= (thing->radius + abs(tmsinradius)))
917 return true; // didn't hit it
918
919 v1.x = tmx - tmcosradius;
920 v1.y = tmy - tmsinradius;
921 v2.x = tmx + tmcosradius;
922 v2.y = tmy + tmsinradius;
923
924 junk.v1 = &v1;
925 junk.v2 = &v2;
926 junk.dx = 2*tmcosradius; // v2.x - v1.x;
927 junk.dy = 2*tmsinradius; // v2.y - v1.y;
928
929 // no need to check whether other thing has MF_PAPERCOLLISION, since would fall under other condition
930 if ((P_PointOnLineSide(thing->x - thing->radius, thing->y - thing->radius, &junk)
931 == P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
932 && (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk)
933 == P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk)))
934 return true; // the line doesn't cross between either pair of opposite corners
935 }
936
937 {
938 UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
939 if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
940 return true; // one of them was removed???
941 if (shouldCollide == 1)
942 return false; // force collide
943 else if (shouldCollide == 2)
944 return true; // force no collide
945
946 shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type
947 if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
948 return true; // one of them was removed???
949 if (shouldCollide == 1)
950 return false; // force collide
951 else if (shouldCollide == 2)
952 return true; // force no collide
953 }
954
955 if (tmthing->type == MT_LAVAFALL_LAVA && (thing->type == MT_RING || thing->type == MT_REDTEAMRING || thing->type == MT_BLUETEAMRING || thing->type == MT_FLINGRING))
956 {
957 //height check
958 if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
959 return true;
960
961 P_KillMobj(thing, tmthing, tmthing, DMG_FIRE);
962 }
963
964 if (tmthing->type == MT_MINECART)
965 {
966 //height check
967 if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
968 return true;
969
970 if (thing->type == MT_TNTBARREL)
971 P_KillMobj(thing, tmthing, tmthing->target, 0);
972 else if ((thing->flags & MF_MONITOR) || (thing->flags & MF_ENEMY))
973 {
974 P_KillMobj(thing, tmthing, tmthing->target, 0);
975 if (tmthing->momz*P_MobjFlip(tmthing) < 0)
976 tmthing->momz = abs(tmthing->momz)*P_MobjFlip(tmthing);
977 }
978 }
979
980 if (thing->type == MT_SALOONDOOR && tmthing->player)
981 {
982 mobj_t *ref = (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)) ? tmthing->tracer : tmthing;
983 if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z))
984 || ref != tmthing)
985 {
986 fixed_t dm = min(FixedHypot(ref->momx, ref->momy), 16*FRACUNIT);
987 angle_t ang = R_PointToAngle2(0, 0, ref->momx, ref->momy) - thing->angle;
988 fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK);
989 S_StartSound(tmthing, thing->info->activesound);
990 thing->extravalue2 += 2*FixedMul(s, dm)/3;
991 return true;
992 }
993 }
994
995 if (thing->type == MT_SALOONDOORCENTER && tmthing->player)
996 {
997 if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z))
998 || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)))
999 return true;
1000 }
1001
1002 if (thing->type == MT_ROLLOUTROCK && tmthing->player && tmthing->health)
1003 {
1004 if (tmthing->player->powers[pw_carry] == CR_ROLLOUT)
1005 {
1006 return true;
1007 }
1008 if ((thing->flags & MF_PUSHABLE) // not carrying a player
1009 && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
1010 && !(tmthing->player->powers[pw_ignorelatch] & (1<<15))
1011 && ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP))
1012 && (P_MobjFlip(tmthing)*tmthing->momz <= 0)
1013 && ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2))
1014 || (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2))))
1015 {
1016 thing->flags &= ~MF_PUSHABLE; // prevent riding player from applying pushable movement logic
1017 thing->flags2 &= ~MF2_DONTDRAW; // don't leave the rock invisible if it was flashing prior to boarding
1018 P_SetTarget(&thing->tracer, tmthing);
1019 P_ResetPlayer(tmthing->player);
1020 P_SetPlayerMobjState(tmthing, S_PLAY_WALK);
1021 tmthing->player->powers[pw_carry] = CR_ROLLOUT;
1022 P_SetTarget(&tmthing->tracer, thing);
1023 if (!P_IsObjectOnGround(thing))
1024 thing->momz += tmthing->momz;
1025 return true;
1026 }
1027 }
1028 else if (tmthing->type == MT_ROLLOUTROCK)
1029 {
1030 if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !thing->health)
1031 return true;
1032
1033 if (thing == tmthing->tracer) // don't collide with rider
1034 return true;
1035
1036 if (thing->flags & MF_SPRING) // bounce on springs
1037 {
1038 P_DoSpring(thing, tmthing);
1039 return true;
1040 }
1041 else if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) == (MF_MONITOR|MF_SHOOTABLE) && !(tmthing->flags & MF_PUSHABLE)) // pop monitors while carrying a player
1042 {
1043 P_KillMobj(thing, tmthing, tmthing->tracer, 0);
1044 return true;
1045 }
1046
1047 if (thing->type == tmthing->type // bounce against other rollout rocks
1048 && (tmthing->momx || tmthing->momy || thing->momx || thing->momy))
1049 {
1050 fixed_t tempmomx = thing->momx, tempmomy = thing->momy;
1051 thing->momx = tmthing->momx;
1052 thing->momy = tmthing->momy;
1053 tmthing->momx = tempmomx;
1054 tmthing->momy = tempmomy;
1055 S_StartSound(thing, thing->info->painsound);
1056 }
1057 }
1058
1059 if (thing->type == MT_PTERABYTE && tmthing->player)
1060 P_DoPterabyteCarry(tmthing->player, thing);
1061
1062 if (thing->type == MT_TNTBARREL && tmthing->player)
1063 P_PlayerBarrelCollide(tmthing, thing);
1064
1065 if (thing->type == MT_VULTURE && tmthing->type == MT_VULTURE)
1066 {
1067 fixed_t dx = thing->x - tmthing->x;
1068 fixed_t dy = thing->y - tmthing->y;
1069 fixed_t dz = thing->z - tmthing->z;
1070 fixed_t dm = FixedHypot(dz, FixedHypot(dx, dy));
1071 thing->momx += FixedDiv(dx, dm);
1072 thing->momy += FixedDiv(dy, dm);
1073 thing->momz += FixedDiv(dz, dm);
1074 }
1075
1076 if (tmthing->type == MT_FANG && thing->type == MT_FSGNB)
1077 {
1078 if (thing->z > tmthing->z + tmthing->height)
1079 return true; // overhead
1080 if (thing->z + thing->height < tmthing->z)
1081 return true; // underneath
1082 if (!thing->tracer || !thing->tracer->tracer)
1083 return true;
1084 P_SlapStick(tmthing, thing);
1085 // no return value was used in the original prototype script at this point,
1086 // so I'm assuming we fall back on the solid code to determine how it all ends?
1087 // -- Monster Iestyn
1088 }
1089
1090 // Billiards mines!
1091 if (thing->type == MT_BIGMINE)
1092 {
1093 if (tmthing->type == MT_BIGMINE)
1094 {
1095 if (!tmthing->momx && !tmthing->momy)
1096 return true;
1097 if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
1098 return true;
1099 if (thing->z > tmthing->z + tmthing->height)
1100 return true; // overhead
1101 if (thing->z + thing->height < tmthing->z)
1102 return true; // underneath
1103
1104 thing->momx = tmthing->momx/3;
1105 thing->momy = tmthing->momy/3;
1106 thing->momz = tmthing->momz/3;
1107 tmthing->momx /= -8;
1108 tmthing->momy /= -8;
1109 tmthing->momz /= -8;
1110 if (thing->info->activesound)
1111 S_StartSound(thing, thing->info->activesound);
1112 P_SetMobjState(thing, thing->info->meleestate);
1113 P_SetTarget(&thing->tracer, tmthing->tracer);
1114 return true;
1115 }
1116 else if (tmthing->type == MT_CRUSHCLAW)
1117 {
1118 if (tmthing->extravalue1 <= 0)
1119 return true;
1120 if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
1121 return true;
1122 if (thing->z > tmthing->z + tmthing->height)
1123 return true; // overhead
1124 if (thing->z + thing->height < tmthing->z)
1125 return true; // underneath
1126
1127 thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
1128 thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
1129 if (thing->info->activesound)
1130 S_StartSound(thing, thing->info->activesound);
1131 P_SetMobjState(thing, thing->info->meleestate);
1132 if (tmthing->tracer)
1133 P_SetTarget(&thing->tracer, tmthing->tracer->target);
1134 return false;
1135 }
1136 }
1137
1138 // When solid spikes move, assume they just popped up and teleport things on top of them to hurt.
1139 if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID)
1140 {
1141 if (thing->z > tmthing->z + tmthing->height)
1142 return true; // overhead
1143 if (thing->z + thing->height < tmthing->z)
1144 return true; // underneath
1145
1146 if (tmthing->eflags & MFE_VERTICALFLIP)
1147 thing->z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale);
1148 else
1149 thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale);
1150 if (thing->flags & MF_SHOOTABLE)
1151 P_DamageMobj(thing, tmthing, tmthing, 1, 0);
1152 return true;
1153 }
1154
1155 if (thing->flags & MF_PAIN && tmthing->player)
1156 { // Player touches painful thing sitting on the floor
1157 // see if it went over / under
1158 if (thing->z > tmthing->z + tmthing->height)
1159 return true; // overhead
1160 if (thing->z + thing->height < tmthing->z)
1161 return true; // underneath
1162 if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
1163 {
1164 UINT32 damagetype = (thing->info->mass & 0xFF);
1165 if (!damagetype && thing->flags & MF_FIRE) // BURN!
1166 damagetype = DMG_FIRE;
1167 if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
1168 S_StartSound(thing, damagetype);
1169 }
1170 return true;
1171 }
1172 else if (tmthing->flags & MF_PAIN && thing->player)
1173 { // Painful thing splats player in the face
1174 // see if it went over / under
1175 if (tmthing->z > thing->z + thing->height)
1176 return true; // overhead
1177 if (tmthing->z + tmthing->height < thing->z)
1178 return true; // underneath
1179 if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
1180 {
1181 UINT32 damagetype = (tmthing->info->mass & 0xFF);
1182 if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
1183 damagetype = DMG_FIRE;
1184 if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
1185 S_StartSound(tmthing, damagetype);
1186 }
1187 return true;
1188 }
1189
1190 if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player)
1191 {
1192 P_TouchSpecialThing(thing, tmthing, true);
1193 return true;
1194 }
1195
1196 // check for skulls slamming into things
1197 if (tmthing->flags2 & MF2_SKULLFLY)
1198 {
1199 if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop!
1200 return true; // Let him RUN YOU RIGHT OVER. >:3
1201 else
1202 {
1203 // see if it went over / under
1204 if (tmthing->z > thing->z + thing->height)
1205 return true; // overhead
1206 if (tmthing->z + tmthing->height < thing->z)
1207 return true; // underneath
1208
1209 tmthing->flags2 &= ~MF2_SKULLFLY;
1210 tmthing->momx = tmthing->momy = tmthing->momz = 0;
1211 return false; // stop moving
1212 }
1213 }
1214
1215 if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0
1216 && (tmthing->player || (tmthing->flags & MF_PUSHABLE)) && tmthing->health > 0)
1217 {
1218 // Multiplying by -1 inherently flips "less than" and "greater than"
1219 fixed_t tmz = ((thing->eflags & MFE_VERTICALFLIP) ? -(tmthing->z + tmthing->height) : tmthing->z);
1220 fixed_t tmznext = ((thing->eflags & MFE_VERTICALFLIP) ? -tmthing->momz : tmthing->momz) + tmz;
1221 fixed_t thzh = ((thing->eflags & MFE_VERTICALFLIP) ? -thing->z : thing->z + thing->height);
1222 fixed_t sprarea = FixedMul(8*FRACUNIT, thing->scale) * P_MobjFlip(thing);
1223
1224 if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh))
1225 {
1226 P_DoSpring(thing, tmthing);
1227 return true;
1228 }
1229 else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
1230 return true;
1231 }
1232
1233 // missiles can hit other things
1234 if (tmthing->flags & MF_MISSILE || tmthing->type == MT_SHELL)
1235 {
1236 // see if it went over / under
1237 if (tmthing->z > thing->z + thing->height)
1238 return true; // overhead
1239 if (tmthing->z + tmthing->height < thing->z)
1240 return true; // underneath
1241
1242 if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
1243 {
1244 // Don't hit same species as originator.
1245 if (thing == tmthing->target)
1246 return true;
1247
1248 if (thing->type != MT_PLAYER)
1249 {
1250 // Explode, but do no damage.
1251 // Let players missile other players.
1252 return false;
1253 }
1254 }
1255
1256 // Special case for bounce rings so they don't get caught behind solid objects.
1257 if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID)
1258 return true;
1259
1260 // Missiles ignore Brak's helper.
1261 if (thing->type == MT_BLACKEGGMAN_HELPER)
1262 return true;
1263
1264 // Hurting Brak
1265 if (tmthing->type == MT_BLACKEGGMAN_MISSILE
1266 && thing->type == MT_BLACKEGGMAN && tmthing->target != thing)
1267 {
1268 // Not if Brak's already in pain
1269 if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35]))
1270 P_SetMobjState(thing, thing->info->painstate);
1271 return false;
1272 }
1273
1274 if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD))
1275 {
1276 // didn't do any damage
1277 return !(thing->flags & MF_SOLID);
1278 }
1279
1280 if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player
1281 && thing->player->ctfteam == tmthing->target->player->ctfteam
1282 && thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target)
1283 return true; // Don't give rings to your carry player by accident.
1284
1285 if (thing->type == MT_EGGSHIELD)
1286 {
1287 angle_t angle = (R_PointToAngle2(thing->x, thing->y, tmthing->x - tmthing->momx, tmthing->y - tmthing->momy) - thing->angle) - ANGLE_90;
1288
1289 if (angle < ANGLE_180) // hit shield from behind, shield is destroyed!
1290 P_KillMobj(thing, tmthing, tmthing, 0);
1291 return false;
1292 }
1293
1294 // damage / explode
1295 if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
1296 P_DamageMobj(thing, tmthing, tmthing, 1, 0);
1297 else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player
1298 && (thing->player->pflags & PF_JUMPED)
1299 && !thing->player->powers[pw_flashing]
1300 && !thing->player->powers[pw_ignorelatch]
1301 && thing->tracer != tmthing
1302 && tmthing->target != thing)
1303 {
1304 // Hop on the missile for a ride!
1305 thing->player->powers[pw_carry] = CR_GENERIC;
1306 thing->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
1307 P_SetTarget(&thing->tracer, tmthing);
1308 P_SetTarget(&tmthing->target, thing); // Set owner to the player
1309 P_SetTarget(&tmthing->tracer, NULL); // Disable homing-ness
1310 tmthing->momz = 0;
1311
1312 thing->angle = tmthing->angle;
1313
1314 if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
1315 P_SetPlayerAngle(thing->player, thing->angle);
1316
1317 return true;
1318 }
1319 else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED)))
1320 {
1321 // Ignore
1322 }
1323 else if (tmthing->type == MT_BLACKEGGMAN_GOOPFIRE)
1324 {
1325 P_UnsetThingPosition(tmthing);
1326 tmthing->x = thing->x;
1327 tmthing->y = thing->y;
1328 P_SetThingPosition(tmthing);
1329 }
1330 else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial for shell
1331 {
1332 UINT8 damagetype = tmthing->info->mass;
1333 if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
1334 damagetype = DMG_FIRE;
1335 P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype);
1336 }
1337
1338 // Fireball touched an enemy
1339 // Don't bounce though, just despawn right there
1340 if ((tmthing->type == MT_FIREBALL) && (thing->flags & MF_ENEMY))
1341 P_KillMobj(tmthing, NULL, NULL, 0);
1342
1343 // don't traverse any more
1344
1345 if (tmthing->type == MT_SHELL)
1346 return true;
1347 else
1348 return false;
1349 }
1350
1351 if (thing->flags & MF_PUSHABLE && (tmthing->player || tmthing->flags & MF_PUSHABLE)
1352 && tmthing->z + tmthing->height > thing->z && tmthing->z < thing->z + thing->height
1353 && !(netgame && tmthing->player && tmthing->player->spectator)) // Push thing!
1354 {
1355 if (thing->flags2 & MF2_SLIDEPUSH) // Make it slide
1356 {
1357 if (tmthing->momy > 0 && tmthing->momy > FixedMul(4*FRACUNIT, thing->scale) && tmthing->momy > thing->momy)
1358 {
1359 thing->momy += FixedMul(PUSHACCEL, thing->scale);
1360 tmthing->momy -= FixedMul(PUSHACCEL, thing->scale);
1361 }
1362 else if (tmthing->momy < 0 && tmthing->momy < FixedMul(-4*FRACUNIT, thing->scale)
1363 && tmthing->momy < thing->momy)
1364 {
1365 thing->momy -= FixedMul(PUSHACCEL, thing->scale);
1366 tmthing->momy += FixedMul(PUSHACCEL, thing->scale);
1367 }
1368 if (tmthing->momx > 0 && tmthing->momx > FixedMul(4*FRACUNIT, thing->scale)
1369 && tmthing->momx > thing->momx)
1370 {
1371 thing->momx += FixedMul(PUSHACCEL, thing->scale);
1372 tmthing->momx -= FixedMul(PUSHACCEL, thing->scale);
1373 }
1374 else if (tmthing->momx < 0 && tmthing->momx < FixedMul(-4*FRACUNIT, thing->scale)
1375 && tmthing->momx < thing->momx)
1376 {
1377 thing->momx -= FixedMul(PUSHACCEL, thing->scale);
1378 tmthing->momx += FixedMul(PUSHACCEL, thing->scale);
1379 }
1380
1381 if (thing->momx > FixedMul(thing->info->speed, thing->scale))
1382 thing->momx = FixedMul(thing->info->speed, thing->scale);
1383 else if (thing->momx < -FixedMul(thing->info->speed, thing->scale))
1384 thing->momx = -FixedMul(thing->info->speed, thing->scale);
1385 if (thing->momy > FixedMul(thing->info->speed, thing->scale))
1386 thing->momy = FixedMul(thing->info->speed, thing->scale);
1387 else if (thing->momy < -FixedMul(thing->info->speed, thing->scale))
1388 thing->momy = -FixedMul(thing->info->speed, thing->scale);
1389 }
1390 else
1391 {
1392 if (tmthing->momx > FixedMul(4*FRACUNIT, thing->scale))
1393 tmthing->momx = FixedMul(4*FRACUNIT, thing->scale);
1394 else if (tmthing->momx < FixedMul(-4*FRACUNIT, thing->scale))
1395 tmthing->momx = FixedMul(-4*FRACUNIT, thing->scale);
1396 if (tmthing->momy > FixedMul(4*FRACUNIT, thing->scale))
1397 tmthing->momy = FixedMul(4*FRACUNIT, thing->scale);
1398 else if (tmthing->momy < FixedMul(-4*FRACUNIT, thing->scale))
1399 tmthing->momy = FixedMul(-4*FRACUNIT, thing->scale);
1400
1401 thing->momx = tmthing->momx;
1402 thing->momy = tmthing->momy;
1403 }
1404
1405 if (thing->type != MT_GARGOYLE || P_IsObjectOnGround(thing))
1406 S_StartSound(thing, thing->info->activesound);
1407
1408 P_SetTarget(&thing->target, tmthing);
1409 }
1410
1411 // NiGHTS lap logic
1412 if ((tmthing->type == MT_NIGHTSDRONE || thing->type == MT_NIGHTSDRONE)
1413 && (tmthing->player || thing->player))
1414 {
1415 mobj_t *droneobj = (tmthing->type == MT_NIGHTSDRONE) ? tmthing : thing;
1416 player_t *pl = (droneobj == thing) ? tmthing->player : thing->player;
1417
1418 // Must be NiGHTS, must wait about a second
1419 // must be flying in the SAME DIRECTION as the last time you came through.
1420 // not (your direction) xor (stored direction)
1421 // In other words, you can't u-turn and respawn rings near the drone.
1422 if ((pl->powers[pw_carry] == CR_NIGHTSMODE) && (INT32)leveltime > droneobj->extravalue2 && (
1423 !(pl->flyangle > 90 && pl->flyangle < 270)
1424 ^ (droneobj->extravalue1 > 90 && droneobj->extravalue1 < 270)
1425 ))
1426 {
1427 pl->marelap++;
1428 pl->totalmarelap++;
1429 pl->lapbegunat = leveltime;
1430 pl->lapstartedtime = pl->nightstime;
1431
1432 if (pl->bonustime)
1433 {
1434 pl->marebonuslap++;
1435 pl->totalmarebonuslap++;
1436
1437 // Respawn rings and items
1438 P_ReloadRings();
1439 }
1440
1441 P_RunNightsLapExecutors(pl->mo);
1442 }
1443 droneobj->extravalue1 = pl->flyangle;
1444 droneobj->extravalue2 = (INT32)leveltime + TICRATE;
1445 }
1446
1447 // check for special pickup
1448 if (thing->flags & MF_SPECIAL && tmthing->player)
1449 {
1450 P_TouchSpecialThing(thing, tmthing, true); // can remove thing
1451 return true;
1452 }
1453 // check again for special pickup
1454 if (tmthing->flags & MF_SPECIAL && thing->player)
1455 {
1456 P_TouchSpecialThing(tmthing, thing, true); // can remove thing
1457 return true;
1458 }
1459
1460 // Sprite Spikes!
1461 // Do not return because solidity code comes below.
1462 if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID && thing->player) // moving spike rams into player?!
1463 {
1464 if (tmthing->eflags & MFE_VERTICALFLIP)
1465 {
1466 if (thing->z + thing->height <= tmthing->z + FixedMul(FRACUNIT, tmthing->scale)
1467 && thing->z + thing->height + thing->momz >= tmthing->z + FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
1468 && !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && thing->eflags & MFE_VERTICALFLIP))
1469 P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
1470 }
1471 else if (thing->z >= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale)
1472 && thing->z + thing->momz <= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
1473 && !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && !(thing->eflags & MFE_VERTICALFLIP)))
1474 P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
1475 }
1476 else if (thing->type == MT_SPIKE && thing->flags & MF_SOLID && tmthing->player) // unfortunate player falls into spike?!
1477 {
1478 if (thing->eflags & MFE_VERTICALFLIP)
1479 {
1480 if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
1481 && tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)
1482 && !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && tmthing->eflags & MFE_VERTICALFLIP))
1483 P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
1484 }
1485 else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
1486 && tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
1487 && !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && !(tmthing->eflags & MFE_VERTICALFLIP)))
1488 P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
1489 }
1490
1491 if (tmthing->type == MT_WALLSPIKE && tmthing->flags & MF_SOLID && thing->player) // wall spike impales player
1492 {
1493 fixed_t bottomz, topz;
1494 bottomz = tmthing->z;
1495 topz = tmthing->z + tmthing->height;
1496 if (tmthing->eflags & MFE_VERTICALFLIP)
1497 bottomz -= FixedMul(FRACUNIT, tmthing->scale);
1498 else
1499 topz += FixedMul(FRACUNIT, tmthing->scale);
1500
1501 if (thing->z + thing->height > bottomz // above bottom
1502 && thing->z < topz) // below top
1503 // don't check angle, the player was clearly in the way in this case
1504 P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
1505 }
1506 else if (thing->type == MT_WALLSPIKE && thing->flags & MF_SOLID && tmthing->player)
1507 {
1508 fixed_t bottomz, topz;
1509 angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tmthing->x, tmthing->y);
1510
1511 if (P_PlayerInPain(tmthing->player) && (tmthing->momx || tmthing->momy))
1512 {
1513 angle_t playerangle = R_PointToAngle2(0, 0, tmthing->momx, tmthing->momy) - touchangle;
1514 if (playerangle > ANGLE_180)
1515 playerangle = InvAngle(playerangle);
1516 if (playerangle < ANGLE_90)
1517 return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
1518 }
1519
1520 bottomz = thing->z;
1521 topz = thing->z + thing->height;
1522
1523 if (thing->eflags & MFE_VERTICALFLIP)
1524 bottomz -= FixedMul(FRACUNIT, thing->scale);
1525 else
1526 topz += FixedMul(FRACUNIT, thing->scale);
1527
1528 if (tmthing->z + tmthing->height > bottomz // above bottom
1529 && tmthing->z < topz // below top
1530 && !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer
1531 { // use base as a reference point to determine what angle you touched the spike at
1532 touchangle = thing->angle - touchangle;
1533 if (touchangle > ANGLE_180)
1534 touchangle = InvAngle(touchangle);
1535 if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked!
1536 P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
1537 }
1538 }
1539
1540 if (thing->flags & MF_PUSHABLE)
1541 {
1542 if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
1543 P_DoFanAndGasJet(tmthing, thing);
1544 }
1545
1546 if (tmthing->flags & MF_PUSHABLE)
1547 {
1548 if (thing->type == MT_FAN || thing->type == MT_STEAM)
1549 {
1550 P_DoFanAndGasJet(thing, tmthing);
1551 return true;
1552 }
1553 else if (thing->flags & MF_SPRING)
1554 {
1555 if ( thing->z <= tmthing->z + tmthing->height
1556 && tmthing->z <= thing->z + thing->height)
1557 if (P_DoSpring(thing, tmthing))
1558 return false;
1559 return true;
1560 }
1561 }
1562
1563 // thanks to sal for solidenemies dot lua
1564 if (thing->flags & (MF_ENEMY|MF_BOSS) && tmthing->flags & (MF_ENEMY|MF_BOSS))
1565 {
1566 if ((thing->z + thing->height >= tmthing->z)
1567 && (tmthing->z + tmthing->height >= thing->z))
1568 return false;
1569 }
1570
1571 // Damage other players when invincible
1572 if (tmthing->player && thing->player
1573 // Make sure they aren't able to damage you ANYWHERE along the Z axis, you have to be TOUCHING the person.
1574 && !(thing->z + thing->height < tmthing->z || thing->z > tmthing->z + tmthing->height))
1575 {
1576 if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam))
1577 {
1578 if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super] || (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (tmthing->player->pflags & PF_SHIELDABILITY)))
1579 && !thing->player->powers[pw_super])
1580 P_DamageMobj(thing, tmthing, tmthing, 1, 0);
1581 else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super] || (((thing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (thing->player->pflags & PF_SHIELDABILITY)))
1582 && !tmthing->player->powers[pw_super])
1583 P_DamageMobj(tmthing, thing, thing, 1, 0);
1584 }
1585
1586 // If players are using touch tag, seekers damage hiders.
1587 if (G_TagGametype() && cv_touchtag.value &&
1588 ((thing->player->pflags & PF_TAGIT) != (tmthing->player->pflags & PF_TAGIT)))
1589 {
1590 if ((tmthing->player->pflags & PF_TAGIT) && !(thing->player->pflags & PF_TAGIT))
1591 P_DamageMobj(thing, tmthing, tmthing, 1, 0);
1592 else if ((thing->player->pflags & PF_TAGIT) && !(tmthing->player->pflags & PF_TAGIT))
1593 P_DamageMobj(tmthing, thing, tmthing, 1, 0);
1594 }
1595 }
1596
1597 // Force solid players in hide and seek to avoid corner stacking.
1598 if (cv_tailspickup.value && !(gametyperules & GTR_HIDEFROZEN))
1599 {
1600 if (tmthing->player && thing->player)
1601 {
1602 P_DoTailsCarry(thing->player, tmthing->player);
1603 return true;
1604 }
1605 }
1606 else if (thing->player) {
1607 if (thing->player-players == consoleplayer && botingame)
1608 CV_SetValue(&cv_analog[1], true);
1609 if (thing->player->powers[pw_carry] == CR_PLAYER)
1610 {
1611 P_SetTarget(&thing->tracer, NULL);
1612 thing->player->powers[pw_carry] = CR_NONE;
1613 }
1614 }
1615
1616 if (thing->player)
1617 {
1618 // Doesn't matter what gravity player's following! Just do your stuff in YOUR direction only
1619 if (tmthing->eflags & MFE_VERTICALFLIP
1620 && (tmthing->z + tmthing->height + tmthing->momz < thing->z
1621 || tmthing->z + tmthing->height + tmthing->momz >= thing->z + thing->height))
1622 ;
1623 else if (!(tmthing->eflags & MFE_VERTICALFLIP)
1624 && (tmthing->z + tmthing->momz > thing->z + thing->height
1625 || tmthing->z + tmthing->momz <= thing->z))
1626 ;
1627 else if (P_IsObjectOnGround(thing)
1628 && !P_IsObjectOnGround(tmthing) // Don't crush if the monitor is on the ground...
1629 && (tmthing->flags & MF_SOLID))
1630 {
1631 if (tmthing->flags & (MF_MONITOR|MF_PUSHABLE))
1632 {
1633 // Objects kill you if it falls from above.
1634 if (thing != tmthing->target)
1635 P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_CRUSHED);
1636
1637 tmthing->momz = -tmthing->momz/2; // Bounce, just for fun!
1638 // The tmthing->target allows the pusher of the object
1639 // to get the point if he topples it on an opponent.
1640 }
1641 }
1642
1643 if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
1644 P_DoFanAndGasJet(tmthing, thing);
1645 }
1646
1647 if (tmthing->player) // Is the moving/interacting object the player?
1648 {
1649 if (!tmthing->health)
1650 return true;
1651
1652 if (thing->type == MT_FAN || thing->type == MT_STEAM)
1653 P_DoFanAndGasJet(thing, tmthing);
1654 else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART)
1655 {
1656 if ( thing->z <= tmthing->z + tmthing->height
1657 && tmthing->z <= thing->z + thing->height)
1658 if (P_DoSpring(thing, tmthing))
1659 return false;
1660 return true;
1661 }
1662 // Monitor?
1663 else if (thing->flags & MF_MONITOR
1664 && !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2))
1665 && (!(thing->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing)))
1666 {
1667 if (thing->z - thing->scale <= tmthing->z + tmthing->height
1668 && thing->z + thing->height + thing->scale >= tmthing->z)
1669 {
1670 player_t *player = tmthing->player;
1671 // 0 = none, 1 = elemental pierce, 2 = bubble bounce
1672 UINT8 elementalpierce = (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY)
1673 ? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
1674 : 0);
1675 SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
1676 fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
1677 fixed_t *z = &tmthing->z; // aau.
1678 // Going down? Then bounce back up.
1679 if (P_DamageMobj(thing, tmthing, tmthing, 1, 0) // break the monitor
1680 && (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
1681 && (elementalpierce != 1)) // you're not piercing through the monitor...
1682 {
1683 if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
1684 {
1685 fixed_t setmomz = -*momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
1686
1687 if (elementalpierce == 2) // Reset bubblewrap, part 1
1688 P_DoBubbleBounce(player);
1689 *momz = setmomz; // Therefore, you should be thrust in the opposite direction, vertically.
1690 if (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)
1691 P_TwinSpinRejuvenate(player, player->thokitem);
1692 if (elementalpierce == 2) // Reset bubblewrap, part 2
1693 {
1694 boolean underwater = tmthing->eflags & MFE_UNDERWATER;
1695
1696 if (underwater)
1697 *momz /= 2;
1698 *momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
1699 }
1700 }
1701 }
1702 if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
1703 {
1704 if (player->pflags & PF_BOUNCING)
1705 P_DoAbilityBounce(player, false);
1706 return false;
1707 }
1708 else
1709 *z -= *momz; // to ensure proper collision.
1710 }
1711
1712 return true;
1713 }
1714 }
1715
1716 if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
1717 ; // springs, gas jets and springs should never be able to step up onto a player
1718 // z checking at last
1719 // Treat noclip things as non-solid!
1720 else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
1721 && (tmthing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID)
1722 {
1723 fixed_t topz, tmtopz;
1724
1725 if (tmthing->eflags & MFE_VERTICALFLIP)
1726 {
1727 // pass under
1728 tmtopz = tmthing->z;
1729
1730 if (tmtopz > thing->z + thing->height)
1731 {
1732 if (thing->z + thing->height > tmfloorz)
1733 {
1734 tmfloorz = thing->z + thing->height;
1735 tmfloorrover = NULL;
1736 tmfloorslope = NULL;
1737 }
1738 return true;
1739 }
1740
1741 topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
1742
1743 // block only when jumping not high enough,
1744 // (dont climb max. 24units while already in air)
1745 // since return false doesn't handle momentum properly,
1746 // we lie to P_TryMove() so it's always too high
1747 if (tmthing->player && tmthing->z + tmthing->height > topz
1748 && tmthing->z + tmthing->height < tmthing->ceilingz)
1749 {
1750 if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
1751 return false;
1752
1753 tmfloorz = tmceilingz = topz; // block while in air
1754 tmceilingrover = NULL;
1755 tmceilingslope = NULL;
1756 tmfloorthing = thing; // needed for side collision
1757 }
1758 else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
1759 {
1760 tmceilingz = topz;
1761 tmceilingrover = NULL;
1762 tmceilingslope = NULL;
1763 tmfloorthing = thing; // thing we may stand on
1764 }
1765 }
1766 else
1767 {
1768 // pass under
1769 tmtopz = tmthing->z + tmthing->height;
1770
1771 if (tmtopz < thing->z)
1772 {
1773 if (thing->z < tmceilingz)
1774 {
1775 tmceilingz = thing->z;
1776 tmceilingrover = NULL;
1777 tmceilingslope = NULL;
1778 }
1779 return true;
1780 }
1781
1782 topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
1783
1784 // block only when jumping not high enough,
1785 // (dont climb max. 24units while already in air)
1786 // since return false doesn't handle momentum properly,
1787 // we lie to P_TryMove() so it's always too high
1788 if (tmthing->player && tmthing->z < topz
1789 && tmthing->z > tmthing->floorz)
1790 {
1791 if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
1792 return false;
1793
1794 tmfloorz = tmceilingz = topz; // block while in air
1795 tmfloorrover = NULL;
1796 tmfloorslope = NULL;
1797 tmfloorthing = thing; // needed for side collision
1798 }
1799 else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
1800 {
1801 tmfloorz = topz;
1802 tmfloorrover = NULL;
1803 tmfloorslope = NULL;
1804 tmfloorthing = thing; // thing we may stand on
1805 }
1806 }
1807 }
1808
1809 // not solid not blocked
1810 return true;
1811 }
1812
1813 // PIT_CheckCameraLine
1814 // Adjusts tmfloorz and tmceilingz as lines are contacted - FOR CAMERA ONLY
PIT_CheckCameraLine(line_t * ld)1815 static boolean PIT_CheckCameraLine(line_t *ld)
1816 {
1817 if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID))
1818 return true;
1819
1820 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
1821 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
1822 {
1823 return true;
1824 }
1825
1826 if (P_BoxOnLineSide(tmbbox, ld) != -1)
1827 return true;
1828
1829 // A line has been hit
1830
1831 // The moving thing's destination position will cross
1832 // the given line.
1833 // If this should not be allowed, return false.
1834 // If the line is special, keep track of it
1835 // to process later if the move is proven ok.
1836 // NOTE: specials are NOT sorted by order,
1837 // so two special lines that are only 8 pixels apart
1838 // could be crossed in either order.
1839
1840 // this line is out of the if so upper and lower textures can be hit by a splat
1841 blockingline = ld;
1842 if (!ld->backsector) // one sided line
1843 {
1844 if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld))
1845 return true; // don't hit the back side
1846 return false;
1847 }
1848
1849 // set openrange, opentop, openbottom
1850 P_CameraLineOpening(ld);
1851
1852 // adjust floor / ceiling heights
1853 if (opentop < tmceilingz)
1854 {
1855 tmceilingz = opentop;
1856 ceilingline = ld;
1857 }
1858
1859 if (openbottom > tmfloorz)
1860 {
1861 tmfloorz = openbottom;
1862 }
1863
1864 if (highceiling > tmdrpoffceilz)
1865 tmdrpoffceilz = highceiling;
1866
1867 if (lowfloor < tmdropoffz)
1868 tmdropoffz = lowfloor;
1869
1870 return true;
1871 }
1872
1873 //
1874 // PIT_CheckLine
1875 // Adjusts tmfloorz and tmceilingz as lines are contacted
1876 //
PIT_CheckLine(line_t * ld)1877 static boolean PIT_CheckLine(line_t *ld)
1878 {
1879 if (ld->polyobj && !(ld->polyobj->flags & POF_SOLID))
1880 return true;
1881
1882 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
1883 || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
1884 return true;
1885
1886 if (P_BoxOnLineSide(tmbbox, ld) != -1)
1887 return true;
1888
1889 if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
1890 {
1891 fixed_t cosradius, sinradius;
1892 cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
1893 sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
1894 if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld)
1895 == P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld))
1896 return true; // the line doesn't cross between collider's start or end
1897 #ifdef PAPER_COLLISIONCORRECTION
1898 {
1899 fixed_t dist;
1900 vertex_t result;
1901 angle_t langle;
1902 P_ClosestPointOnLine(tmx, tmy, ld, &result);
1903 langle = R_PointToAngle2(ld->v1->x, ld->v1->y, ld->v2->x, ld->v2->y);
1904 langle += ANGLE_90*(P_PointOnLineSide(tmx, tmy, ld) ? -1 : 1);
1905 dist = abs(FixedMul(tmthing->radius, FINECOSINE((tmthing->angle - langle)>>ANGLETOFINESHIFT)));
1906 cosradius = FixedMul(dist, FINECOSINE(langle>>ANGLETOFINESHIFT));
1907 sinradius = FixedMul(dist, FINESINE(langle>>ANGLETOFINESHIFT));
1908 tmthing->flags |= MF_NOCLIP;
1909 P_TeleportMove(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z);
1910 tmthing->flags &= ~MF_NOCLIP;
1911 }
1912 #endif
1913 }
1914
1915 // A line has been hit
1916
1917 // The moving thing's destination position will cross
1918 // the given line.
1919 // If this should not be allowed, return false.
1920 // If the line is special, keep track of it
1921 // to process later if the move is proven ok.
1922 // NOTE: specials are NOT sorted by order,
1923 // so two special lines that are only 8 pixels apart
1924 // could be crossed in either order.
1925
1926 // this line is out of the if so upper and lower textures can be hit by a splat
1927 blockingline = ld;
1928
1929 {
1930 UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type
1931 if (P_MobjWasRemoved(tmthing))
1932 return true; // one of them was removed???
1933 if (shouldCollide == 1)
1934 return false; // force collide
1935 else if (shouldCollide == 2)
1936 return true; // force no collide
1937 }
1938
1939 if (!ld->backsector) // one sided line
1940 {
1941 if (P_PointOnLineSide(tmthing->x, tmthing->y, ld))
1942 return true; // don't hit the back side
1943 return false;
1944 }
1945
1946 // missiles can cross uncrossable lines
1947 if (!(tmthing->flags & MF_MISSILE))
1948 {
1949 if (ld->flags & ML_IMPASSIBLE) // block objects from moving through this linedef.
1950 return false;
1951 if ((tmthing->flags & (MF_ENEMY|MF_BOSS)) && ld->flags & ML_BLOCKMONSTERS)
1952 return false; // block monsters only
1953 }
1954
1955 // set openrange, opentop, openbottom
1956 P_LineOpening(ld, tmthing);
1957
1958 // adjust floor / ceiling heights
1959 if (opentop < tmceilingz)
1960 {
1961 tmceilingz = opentop;
1962 ceilingline = ld;
1963 tmceilingrover = openceilingrover;
1964 tmceilingslope = opentopslope;
1965 }
1966
1967 if (openbottom > tmfloorz)
1968 {
1969 tmfloorz = openbottom;
1970 tmfloorrover = openfloorrover;
1971 tmfloorslope = openbottomslope;
1972 }
1973
1974 if (highceiling > tmdrpoffceilz)
1975 tmdrpoffceilz = highceiling;
1976
1977 if (lowfloor < tmdropoffz)
1978 tmdropoffz = lowfloor;
1979
1980 return true;
1981 }
1982
1983 // =========================================================================
1984 // MOVEMENT CLIPPING
1985 // =========================================================================
1986
1987 //
1988 // P_CheckPosition
1989 // This is purely informative, nothing is modified
1990 // (except things picked up).
1991 //
1992 // in:
1993 // a mobj_t (can be valid or invalid)
1994 // a position to be checked
1995 // (doesn't need to be related to the mobj_t->x,y)
1996 //
1997 // during:
1998 // special things are touched if MF_PICKUP
1999 // early out on solid lines?
2000 //
2001 // out:
2002 // newsubsec
2003 // tmfloorz
2004 // tmceilingz
2005 // tmdropoffz
2006 // tmdrpoffceilz
2007 // the lowest point contacted
2008 // (monsters won't move to a dropoff)
2009 // speciallines[]
2010 // numspeciallines
2011 //
2012
2013 // tmfloorz
2014 // the nearest floor or thing's top under tmthing
2015 // tmceilingz
2016 // the nearest ceiling or thing's bottom over tmthing
2017 //
P_CheckPosition(mobj_t * thing,fixed_t x,fixed_t y)2018 boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
2019 {
2020 INT32 xl, xh, yl, yh, bx, by;
2021 subsector_t *newsubsec;
2022 boolean blockval = true;
2023
2024 ps_checkposition_calls++;
2025
2026 I_Assert(thing != NULL);
2027 #ifdef PARANOIA
2028 if (P_MobjWasRemoved(thing))
2029 I_Error("Previously-removed Thing of type %u crashes P_CheckPosition!", thing->type);
2030 #endif
2031
2032 P_SetTarget(&tmthing, thing);
2033 tmflags = thing->flags;
2034
2035 tmx = x;
2036 tmy = y;
2037
2038 tmbbox[BOXTOP] = y + tmthing->radius;
2039 tmbbox[BOXBOTTOM] = y - tmthing->radius;
2040 tmbbox[BOXRIGHT] = x + tmthing->radius;
2041 tmbbox[BOXLEFT] = x - tmthing->radius;
2042
2043 newsubsec = R_PointInSubsector(x, y);
2044 ceilingline = blockingline = NULL;
2045
2046 // The base floor / ceiling is from the subsector
2047 // that contains the point.
2048 // Any contacted lines the step closer together
2049 // will adjust them.
2050 tmfloorz = tmdropoffz = P_GetFloorZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->floorheight;
2051 tmceilingz = P_GetCeilingZ(thing, newsubsec->sector, x, y, NULL); //newsubsec->sector->ceilingheight;
2052 tmfloorrover = NULL;
2053 tmceilingrover = NULL;
2054 tmfloorslope = newsubsec->sector->f_slope;
2055 tmceilingslope = newsubsec->sector->c_slope;
2056
2057 // Check list of fake floors and see if tmfloorz/tmceilingz need to be altered.
2058 if (newsubsec->sector->ffloors)
2059 {
2060 ffloor_t *rover;
2061 fixed_t delta1, delta2;
2062 INT32 thingtop = thing->z + thing->height;
2063
2064 for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
2065 {
2066 fixed_t topheight, bottomheight;
2067
2068 if (!(rover->flags & FF_EXISTS))
2069 continue;
2070
2071 topheight = P_GetFOFTopZ(thing, newsubsec->sector, rover, x, y, NULL);
2072 bottomheight = P_GetFOFBottomZ(thing, newsubsec->sector, rover, x, y, NULL);
2073
2074 if ((rover->flags & (FF_SWIMMABLE|FF_GOOWATER)) == (FF_SWIMMABLE|FF_GOOWATER) && !(thing->flags & MF_NOGRAVITY))
2075 {
2076 // If you're inside goowater and slowing down
2077 fixed_t sinklevel = FixedMul(thing->info->height/6, thing->scale);
2078 fixed_t minspeed = FixedMul(thing->info->height/9, thing->scale);
2079 if (thing->z < topheight && bottomheight < thingtop
2080 && abs(thing->momz) < minspeed)
2081 {
2082 // Oh no! The object is stick in between the surface of the goo and sinklevel! help them out!
2083 if (!(thing->eflags & MFE_VERTICALFLIP) && thing->z > topheight - sinklevel
2084 && thing->momz >= 0 && thing->momz < (minspeed>>2))
2085 thing->momz += minspeed>>2;
2086 else if (thing->eflags & MFE_VERTICALFLIP && thingtop < bottomheight + sinklevel
2087 && thing->momz <= 0 && thing->momz > -(minspeed>>2))
2088 thing->momz -= minspeed>>2;
2089
2090 // Land on the top or the bottom, depending on gravity flip.
2091 if (!(thing->eflags & MFE_VERTICALFLIP) && thing->z >= topheight - sinklevel && thing->momz <= 0)
2092 {
2093 if (tmfloorz < topheight - sinklevel) {
2094 tmfloorz = topheight - sinklevel;
2095 tmfloorrover = rover;
2096 tmfloorslope = *rover->t_slope;
2097 }
2098 }
2099 else if (thing->eflags & MFE_VERTICALFLIP && thingtop <= bottomheight + sinklevel && thing->momz >= 0)
2100 {
2101 if (tmceilingz > bottomheight + sinklevel) {
2102 tmceilingz = bottomheight + sinklevel;
2103 tmceilingrover = rover;
2104 tmceilingslope = *rover->b_slope;
2105 }
2106 }
2107 }
2108 continue;
2109 }
2110
2111 if (thing->player && (P_CheckSolidLava(rover) || P_CanRunOnWater(thing->player, rover)))
2112 ;
2113 else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE))
2114 ;
2115 else if (!((rover->flags & FF_BLOCKPLAYER && thing->player)
2116 || (rover->flags & FF_BLOCKOTHERS && !thing->player)
2117 || rover->flags & FF_QUICKSAND))
2118 continue;
2119
2120 if (rover->flags & FF_QUICKSAND)
2121 {
2122 if (thing->z < topheight && bottomheight < thingtop)
2123 {
2124 if (tmfloorz < thing->z) {
2125 tmfloorz = thing->z;
2126 tmfloorrover = rover;
2127 tmfloorslope = NULL;
2128 }
2129 }
2130 // Quicksand blocks never change heights otherwise.
2131 continue;
2132 }
2133
2134 delta1 = thing->z - (bottomheight
2135 + ((topheight - bottomheight)/2));
2136 delta2 = thingtop - (bottomheight
2137 + ((topheight - bottomheight)/2));
2138
2139 if (topheight > tmfloorz && abs(delta1) < abs(delta2)
2140 && !(rover->flags & FF_REVERSEPLATFORM))
2141 {
2142 tmfloorz = tmdropoffz = topheight;
2143 tmfloorrover = rover;
2144 tmfloorslope = *rover->t_slope;
2145 }
2146 if (bottomheight < tmceilingz && abs(delta1) >= abs(delta2)
2147 && !(rover->flags & FF_PLATFORM)
2148 && !(thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE)))
2149 {
2150 tmceilingz = tmdrpoffceilz = bottomheight;
2151 tmceilingrover = rover;
2152 tmceilingslope = *rover->b_slope;
2153 }
2154 }
2155 }
2156
2157 // The bounding box is extended by MAXRADIUS
2158 // because mobj_ts are grouped into mapblocks
2159 // based on their origin point, and can overlap
2160 // into adjacent blocks by up to MAXRADIUS units.
2161
2162 xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
2163 xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
2164 yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
2165 yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
2166
2167 BMBOUNDFIX(xl, xh, yl, yh);
2168
2169 // Check polyobjects and see if tmfloorz/tmceilingz need to be altered
2170 {
2171 validcount++;
2172
2173 for (by = yl; by <= yh; by++)
2174 for (bx = xl; bx <= xh; bx++)
2175 {
2176 INT32 offset;
2177 polymaplink_t *plink; // haleyjd 02/22/06
2178
2179 if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
2180 continue;
2181
2182 offset = by*bmapwidth + bx;
2183
2184 // haleyjd 02/22/06: consider polyobject lines
2185 plink = polyblocklinks[offset];
2186
2187 while (plink)
2188 {
2189 polyobj_t *po = plink->po;
2190
2191 if (po->validcount != validcount) // if polyobj hasn't been checked
2192 {
2193 sector_t *polysec;
2194 fixed_t delta1, delta2, thingtop;
2195 fixed_t polytop, polybottom;
2196
2197 po->validcount = validcount;
2198
2199 if (!P_BBoxInsidePolyobj(po, tmbbox)
2200 || !(po->flags & POF_SOLID))
2201 {
2202 plink = (polymaplink_t *)(plink->link.next);
2203 continue;
2204 }
2205
2206 // We're inside it! Yess...
2207 polysec = po->lines[0]->backsector;
2208
2209 if (po->flags & POF_CLIPPLANES)
2210 {
2211 polytop = polysec->ceilingheight;
2212 polybottom = polysec->floorheight;
2213 }
2214 else
2215 {
2216 polytop = INT32_MAX;
2217 polybottom = INT32_MIN;
2218 }
2219
2220 thingtop = thing->z + thing->height;
2221 delta1 = thing->z - (polybottom + ((polytop - polybottom)/2));
2222 delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
2223
2224 if (polytop > tmfloorz && abs(delta1) < abs(delta2)) {
2225 tmfloorz = tmdropoffz = polytop;
2226 tmfloorslope = NULL;
2227 tmfloorrover = NULL;
2228 }
2229
2230 if (polybottom < tmceilingz && abs(delta1) >= abs(delta2)) {
2231 tmceilingz = tmdrpoffceilz = polybottom;
2232 tmceilingslope = NULL;
2233 tmceilingrover = NULL;
2234 }
2235 }
2236 plink = (polymaplink_t *)(plink->link.next);
2237 }
2238 }
2239 }
2240
2241 // tmfloorthing is set when tmfloorz comes from a thing's top
2242 tmfloorthing = NULL;
2243 tmhitthing = NULL;
2244
2245 validcount++;
2246
2247 if (tmflags & MF_NOCLIP)
2248 return true;
2249
2250 // Check things first, possibly picking things up.
2251
2252 // MF_NOCLIPTHING: used by camera to not be blocked by things
2253 if (!(thing->flags & MF_NOCLIPTHING))
2254 {
2255 for (bx = xl; bx <= xh; bx++)
2256 for (by = yl; by <= yh; by++)
2257 {
2258 if (!P_BlockThingsIterator(bx, by, PIT_CheckThing))
2259 blockval = false;
2260 else
2261 tmhitthing = tmfloorthing;
2262 if (P_MobjWasRemoved(tmthing))
2263 return false;
2264 }
2265 }
2266
2267 validcount++;
2268
2269 // check lines
2270 for (bx = xl; bx <= xh; bx++)
2271 for (by = yl; by <= yh; by++)
2272 if (!P_BlockLinesIterator(bx, by, PIT_CheckLine))
2273 blockval = false;
2274
2275 return blockval;
2276 }
2277
2278 static const fixed_t hoopblockdist = 16*FRACUNIT + 8*FRACUNIT;
2279 static const fixed_t hoophalfheight = (56*FRACUNIT)/2;
2280
2281 // P_CheckPosition optimized for the MT_HOOPCOLLIDE object. This needs to be as fast as possible!
P_CheckHoopPosition(mobj_t * hoopthing,fixed_t x,fixed_t y,fixed_t z,fixed_t radius)2282 void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius)
2283 {
2284 INT32 i;
2285
2286 (void)radius; //unused
2287 for (i = 0; i < MAXPLAYERS; i++)
2288 {
2289 if (!playeringame[i] || !players[i].mo || players[i].spectator)
2290 continue;
2291
2292 if (abs(players[i].mo->x - x) >= hoopblockdist ||
2293 abs(players[i].mo->y - y) >= hoopblockdist ||
2294 abs((players[i].mo->z+hoophalfheight) - z) >= hoopblockdist)
2295 continue; // didn't hit it
2296
2297 // can remove thing
2298 P_TouchSpecialThing(hoopthing, players[i].mo, false);
2299 break;
2300 }
2301
2302 return;
2303 }
2304
2305 //
2306 // P_CheckCameraPosition
2307 //
P_CheckCameraPosition(fixed_t x,fixed_t y,camera_t * thiscam)2308 boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
2309 {
2310 INT32 xl, xh, yl, yh, bx, by;
2311 subsector_t *newsubsec;
2312
2313 tmx = x;
2314 tmy = y;
2315
2316 tmbbox[BOXTOP] = y + thiscam->radius;
2317 tmbbox[BOXBOTTOM] = y - thiscam->radius;
2318 tmbbox[BOXRIGHT] = x + thiscam->radius;
2319 tmbbox[BOXLEFT] = x - thiscam->radius;
2320
2321 newsubsec = R_PointInSubsector(x, y);
2322 ceilingline = blockingline = NULL;
2323
2324 mapcampointer = thiscam;
2325
2326 if (GETSECSPECIAL(newsubsec->sector->special, 4) == 12)
2327 { // Camera noclip on entire sector.
2328 tmfloorz = tmdropoffz = thiscam->z;
2329 tmceilingz = tmdrpoffceilz = thiscam->z + thiscam->height;
2330 return true;
2331 }
2332
2333 // The base floor / ceiling is from the subsector
2334 // that contains the point.
2335 // Any contacted lines the step closer together
2336 // will adjust them.
2337 tmfloorz = tmdropoffz = P_CameraGetFloorZ(thiscam, newsubsec->sector, x, y, NULL);
2338
2339 tmceilingz = P_CameraGetCeilingZ(thiscam, newsubsec->sector, x, y, NULL);
2340
2341 // Cameras use the heightsec's heights rather then the actual sector heights.
2342 // If you can see through it, why not move the camera through it too?
2343 if (newsubsec->sector->heightsec >= 0)
2344 {
2345 tmfloorz = tmdropoffz = sectors[newsubsec->sector->heightsec].floorheight;
2346 tmceilingz = tmdrpoffceilz = sectors[newsubsec->sector->heightsec].ceilingheight;
2347 }
2348
2349 // Use preset camera clipping heights if set with Sector Special Parameters whose control sector has Camera Intangible special -Red
2350 if (newsubsec->sector->camsec >= 0)
2351 {
2352 tmfloorz = tmdropoffz = sectors[newsubsec->sector->camsec].floorheight;
2353 tmceilingz = tmdrpoffceilz = sectors[newsubsec->sector->camsec].ceilingheight;
2354 }
2355
2356 // Check list of fake floors and see if tmfloorz/tmceilingz need to be altered.
2357 if (newsubsec->sector->ffloors)
2358 {
2359 ffloor_t *rover;
2360 fixed_t delta1, delta2;
2361 INT32 thingtop = thiscam->z + thiscam->height;
2362
2363 for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
2364 {
2365 fixed_t topheight, bottomheight;
2366 if (!(rover->flags & FF_BLOCKOTHERS) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERALL) || GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
2367 continue;
2368
2369 topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, x, y, NULL);
2370 bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, x, y, NULL);
2371
2372 delta1 = thiscam->z - (bottomheight
2373 + ((topheight - bottomheight)/2));
2374 delta2 = thingtop - (bottomheight
2375 + ((topheight - bottomheight)/2));
2376 if (topheight > tmfloorz && abs(delta1) < abs(delta2))
2377 {
2378 tmfloorz = tmdropoffz = topheight;
2379 }
2380 if (bottomheight < tmceilingz && abs(delta1) >= abs(delta2))
2381 {
2382 tmceilingz = tmdrpoffceilz = bottomheight;
2383 }
2384 }
2385 }
2386
2387 // The bounding box is extended by MAXRADIUS
2388 // because mobj_ts are grouped into mapblocks
2389 // based on their origin point, and can overlap
2390 // into adjacent blocks by up to MAXRADIUS units.
2391
2392 xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
2393 xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
2394 yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
2395 yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
2396
2397 BMBOUNDFIX(xl, xh, yl, yh);
2398
2399 // Check polyobjects and see if tmfloorz/tmceilingz need to be altered
2400 {
2401 validcount++;
2402
2403 for (by = yl; by <= yh; by++)
2404 for (bx = xl; bx <= xh; bx++)
2405 {
2406 INT32 offset;
2407 polymaplink_t *plink; // haleyjd 02/22/06
2408
2409 if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
2410 continue;
2411
2412 offset = by*bmapwidth + bx;
2413
2414 // haleyjd 02/22/06: consider polyobject lines
2415 plink = polyblocklinks[offset];
2416
2417 while (plink)
2418 {
2419 polyobj_t *po = plink->po;
2420
2421 if (po->validcount != validcount) // if polyobj hasn't been checked
2422 {
2423 sector_t *polysec;
2424 fixed_t delta1, delta2, thingtop;
2425 fixed_t polytop, polybottom;
2426
2427 po->validcount = validcount;
2428
2429 if (!P_PointInsidePolyobj(po, x, y) || !(po->flags & POF_SOLID))
2430 {
2431 plink = (polymaplink_t *)(plink->link.next);
2432 continue;
2433 }
2434
2435 // We're inside it! Yess...
2436 polysec = po->lines[0]->backsector;
2437
2438 if (GETSECSPECIAL(polysec->special, 4) == 12)
2439 { // Camera noclip polyobj.
2440 plink = (polymaplink_t *)(plink->link.next);
2441 continue;
2442 }
2443
2444 if (po->flags & POF_CLIPPLANES)
2445 {
2446 polytop = polysec->ceilingheight;
2447 polybottom = polysec->floorheight;
2448 }
2449 else
2450 {
2451 polytop = INT32_MAX;
2452 polybottom = INT32_MIN;
2453 }
2454
2455 thingtop = thiscam->z + thiscam->height;
2456 delta1 = thiscam->z - (polybottom + ((polytop - polybottom)/2));
2457 delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
2458
2459 if (polytop > tmfloorz && abs(delta1) < abs(delta2))
2460 tmfloorz = tmdropoffz = polytop;
2461
2462 if (polybottom < tmceilingz && abs(delta1) >= abs(delta2))
2463 tmceilingz = tmdrpoffceilz = polybottom;
2464 }
2465 plink = (polymaplink_t *)(plink->link.next);
2466 }
2467 }
2468 }
2469
2470 // check lines
2471 for (bx = xl; bx <= xh; bx++)
2472 for (by = yl; by <= yh; by++)
2473 if (!P_BlockLinesIterator(bx, by, PIT_CheckCameraLine))
2474 return false;
2475
2476 return true;
2477 }
2478
2479 // The highest the camera will "step up" onto another floor.
2480 #define MAXCAMERASTEPMOVE MAXSTEPMOVE
2481
2482 //
2483 // P_TryCameraMove
2484 //
2485 // Attempt to move the camera to a new position
2486 //
2487 // Return true if the move succeeded and no sliding should be done.
2488 //
P_TryCameraMove(fixed_t x,fixed_t y,camera_t * thiscam)2489 boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
2490 {
2491 subsector_t *s = R_PointInSubsector(x, y);
2492 boolean retval = true;
2493 boolean itsatwodlevel = false;
2494
2495 floatok = false;
2496
2497 if (twodlevel
2498 || (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
2499 || (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
2500 itsatwodlevel = true;
2501
2502 if (!itsatwodlevel && players[displayplayer].mo)
2503 {
2504 fixed_t tryx = thiscam->x;
2505 fixed_t tryy = thiscam->y;
2506
2507 if ((thiscam == &camera && (players[displayplayer].pflags & PF_NOCLIP))
2508 || (thiscam == &camera2 && (players[secondarydisplayplayer].pflags & PF_NOCLIP)))
2509 { // Noclipping player camera noclips too!!
2510 floatok = true;
2511 thiscam->floorz = thiscam->z;
2512 thiscam->ceilingz = thiscam->z + thiscam->height;
2513 thiscam->x = x;
2514 thiscam->y = y;
2515 thiscam->subsector = s;
2516 return true;
2517 }
2518
2519 do {
2520 if (x-tryx > MAXRADIUS)
2521 tryx += MAXRADIUS;
2522 else if (x-tryx < -MAXRADIUS)
2523 tryx -= MAXRADIUS;
2524 else
2525 tryx = x;
2526 if (y-tryy > MAXRADIUS)
2527 tryy += MAXRADIUS;
2528 else if (y-tryy < -MAXRADIUS)
2529 tryy -= MAXRADIUS;
2530 else
2531 tryy = y;
2532
2533 if (!P_CheckCameraPosition(tryx, tryy, thiscam))
2534 return false; // solid wall or thing
2535
2536 if (tmceilingz - tmfloorz < thiscam->height)
2537 return false; // doesn't fit
2538
2539 floatok = true;
2540
2541 if (tmceilingz - thiscam->z < thiscam->height)
2542 {
2543 if (s == thiscam->subsector && tmceilingz >= thiscam->z)
2544 {
2545 floatok = true;
2546 thiscam->floorz = tmfloorz;
2547 thiscam->ceilingz = tmfloorz + thiscam->height;
2548 thiscam->x = x;
2549 thiscam->y = y;
2550 thiscam->subsector = s;
2551 return true;
2552 }
2553 else
2554 return false; // mobj must lower itself to fit
2555 }
2556
2557 if ((tmfloorz - thiscam->z > MAXCAMERASTEPMOVE))
2558 return false; // too big a step up
2559 } while(tryx != x || tryy != y);
2560 }
2561 else
2562 {
2563 tmfloorz = P_CameraGetFloorZ(thiscam, thiscam->subsector->sector, x, y, NULL);
2564 tmceilingz = P_CameraGetCeilingZ(thiscam, thiscam->subsector->sector, x, y, NULL);
2565 }
2566
2567 // the move is ok,
2568 // so link the thing into its new position
2569
2570 thiscam->floorz = tmfloorz;
2571 thiscam->ceilingz = tmceilingz;
2572 thiscam->x = x;
2573 thiscam->y = y;
2574 thiscam->subsector = s;
2575
2576 return retval;
2577 }
2578
2579 //
2580 // PIT_PushableMoved
2581 //
2582 // Move things standing on top
2583 // of pushable things being pushed.
2584 //
2585 static mobj_t *stand;
2586 static fixed_t standx, standy;
2587
PIT_PushableMoved(mobj_t * thing)2588 boolean PIT_PushableMoved(mobj_t *thing)
2589 {
2590 fixed_t blockdist;
2591
2592 if (!(thing->flags & MF_SOLID)
2593 || (thing->flags & MF_NOGRAVITY))
2594 return true; // Don't move something non-solid!
2595
2596 // Only pushables are supported... in 2.0. Now players can be moved too!
2597 if (!(thing->flags & MF_PUSHABLE || thing->player))
2598 return true;
2599
2600 if (thing == stand)
2601 return true;
2602
2603 blockdist = stand->radius + thing->radius;
2604
2605 if (abs(thing->x - stand->x) >= blockdist || abs(thing->y - stand->y) >= blockdist)
2606 return true; // didn't hit it
2607
2608 if ((!(stand->eflags & MFE_VERTICALFLIP) && thing->z != stand->z + stand->height + FixedMul(FRACUNIT, stand->scale))
2609 || ((stand->eflags & MFE_VERTICALFLIP) && thing->z + thing->height != stand->z - FixedMul(FRACUNIT, stand->scale)))
2610 return true; // Not standing on top
2611
2612 if (!stand->momx && !stand->momy)
2613 return true;
2614
2615 // Move this guy!
2616 if (thing->player)
2617 {
2618 // Monster Iestyn - 29/11/13
2619 // Ridiculous amount of newly declared stuff so players can't get stuck in walls AND so gargoyles don't break themselves at the same time either
2620 // These are all non-static map variables that are changed for each and every single mobj
2621 // See, changing player's momx/y would possibly trigger stuff as if the player were running somehow, so this must be done to keep the player standing
2622 // All this so players can ride gargoyles!
2623 boolean oldfltok = floatok;
2624 fixed_t oldflrz = tmfloorz;
2625 fixed_t oldceilz = tmceilingz;
2626 mobj_t *oldflrthing = tmfloorthing;
2627 mobj_t *oldthing = tmthing;
2628 line_t *oldceilline = ceilingline;
2629 line_t *oldblockline = blockingline;
2630 ffloor_t *oldflrrover = tmfloorrover;
2631 ffloor_t *oldceilrover = tmceilingrover;
2632 pslope_t *oldfslope = tmfloorslope;
2633 pslope_t *oldcslope = tmceilingslope;
2634
2635 // Move the player
2636 P_TryMove(thing, thing->x+stand->momx, thing->y+stand->momy, true);
2637
2638 // Now restore EVERYTHING so the gargoyle doesn't keep the player's tmstuff and break
2639 floatok = oldfltok;
2640 tmfloorz = oldflrz;
2641 tmceilingz = oldceilz;
2642 tmfloorthing = oldflrthing;
2643 P_SetTarget(&tmthing, oldthing);
2644 ceilingline = oldceilline;
2645 blockingline = oldblockline;
2646 tmfloorrover = oldflrrover;
2647 tmceilingrover = oldceilrover;
2648 tmfloorslope = oldfslope;
2649 tmceilingslope = oldcslope;
2650 thing->momz = stand->momz;
2651 }
2652 else
2653 {
2654 thing->momx = stand->momx;
2655 thing->momy = stand->momy;
2656 thing->momz = stand->momz;
2657 }
2658 return true;
2659 }
2660
2661 //
2662 // P_TryMove
2663 // Attempt to move to a new position.
2664 //
P_TryMove(mobj_t * thing,fixed_t x,fixed_t y,boolean allowdropoff)2665 boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
2666 {
2667 fixed_t tryx = thing->x;
2668 fixed_t tryy = thing->y;
2669 fixed_t radius = thing->radius;
2670 fixed_t thingtop;
2671 fixed_t startingonground = P_IsObjectOnGround(thing);
2672 floatok = false;
2673
2674 if (radius < MAXRADIUS/2)
2675 radius = MAXRADIUS/2;
2676
2677 do {
2678 if (thing->flags & MF_NOCLIP) {
2679 tryx = x;
2680 tryy = y;
2681 } else {
2682 if (x-tryx > radius)
2683 tryx += radius;
2684 else if (x-tryx < -radius)
2685 tryx -= radius;
2686 else
2687 tryx = x;
2688 if (y-tryy > radius)
2689 tryy += radius;
2690 else if (y-tryy < -radius)
2691 tryy -= radius;
2692 else
2693 tryy = y;
2694 }
2695
2696 if (!P_CheckPosition(thing, tryx, tryy))
2697 return false; // solid wall or thing
2698
2699 if (!(thing->flags & MF_NOCLIP))
2700 {
2701 //All things are affected by their scale.
2702 fixed_t maxstep = FixedMul(MAXSTEPMOVE, thing->scale);
2703
2704 if (thing->player)
2705 {
2706 // If using type Section1:13, double the maxstep.
2707 if (P_PlayerTouchingSectorSpecial(thing->player, 1, 13)
2708 || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13)
2709 maxstep <<= 1;
2710
2711 // If using type Section1:14, no maxstep.
2712 if (P_PlayerTouchingSectorSpecial(thing->player, 1, 14)
2713 || GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14)
2714 maxstep = 0;
2715
2716 // Don't 'step up' while springing,
2717 // Only step up "if needed".
2718 if (thing->player->panim == PA_SPRING
2719 && P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale))
2720 maxstep = 0;
2721 }
2722
2723 if (thing->type == MT_SKIM)
2724 maxstep = 0;
2725
2726 if (tmceilingz - tmfloorz < thing->height
2727 || (thing->player
2728 && tmceilingz - tmfloorz < P_GetPlayerHeight(thing->player)
2729 && !P_PlayerCanEnterSpinGaps(thing->player)))
2730 {
2731 if (tmfloorthing)
2732 tmhitthing = tmfloorthing;
2733 return false; // doesn't fit
2734 }
2735
2736 floatok = true;
2737
2738 thingtop = thing->z + thing->height;
2739
2740 // Step up
2741 if (thing->z < tmfloorz)
2742 {
2743 if (maxstep > 0 && tmfloorz - thing->z <= maxstep)
2744 {
2745 thing->z = thing->floorz = tmfloorz;
2746 thing->floorrover = tmfloorrover;
2747 thing->eflags |= MFE_JUSTSTEPPEDDOWN;
2748 }
2749 else
2750 {
2751 return false; // mobj must raise itself to fit
2752 }
2753 }
2754 else if (tmceilingz < thingtop)
2755 {
2756 if (maxstep > 0 && thingtop - tmceilingz <= maxstep)
2757 {
2758 thing->z = ( thing->ceilingz = tmceilingz ) - thing->height;
2759 thing->ceilingrover = tmceilingrover;
2760 thing->eflags |= MFE_JUSTSTEPPEDDOWN;
2761 }
2762 else
2763 {
2764 return false; // mobj must lower itself to fit
2765 }
2766 }
2767 else if (maxstep > 0) // Step down
2768 {
2769 // If the floor difference is MAXSTEPMOVE or less, and the sector isn't Section1:14, ALWAYS
2770 // step down! Formerly required a Section1:13 sector for the full MAXSTEPMOVE, but no more.
2771
2772 if (thingtop == thing->ceilingz && tmceilingz > thingtop && tmceilingz - thingtop <= maxstep)
2773 {
2774 thing->z = (thing->ceilingz = tmceilingz) - thing->height;
2775 thing->ceilingrover = tmceilingrover;
2776 thing->eflags |= MFE_JUSTSTEPPEDDOWN;
2777 }
2778 else if (thing->z == thing->floorz && tmfloorz < thing->z && thing->z - tmfloorz <= maxstep)
2779 {
2780 thing->z = thing->floorz = tmfloorz;
2781 thing->floorrover = tmfloorrover;
2782 thing->eflags |= MFE_JUSTSTEPPEDDOWN;
2783 }
2784 }
2785
2786 if (!allowdropoff && !(thing->flags & MF_FLOAT) && thing->type != MT_SKIM && !tmfloorthing)
2787 {
2788 if (thing->eflags & MFE_VERTICALFLIP)
2789 {
2790 if (tmdrpoffceilz - tmceilingz > maxstep)
2791 return false;
2792 }
2793 else if (tmfloorz - tmdropoffz > maxstep)
2794 return false; // don't stand over a dropoff
2795 }
2796 }
2797 } while (tryx != x || tryy != y);
2798
2799 // The move is ok!
2800
2801 // If it's a pushable object, check if anything is
2802 // standing on top and move it, too.
2803 if (thing->flags & MF_PUSHABLE)
2804 {
2805 INT32 bx, by, xl, xh, yl, yh;
2806
2807 yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
2808 yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
2809 xh = (unsigned)(thing->x + MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
2810 xl = (unsigned)(thing->x - MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
2811
2812 BMBOUNDFIX(xl, xh, yl, yh);
2813
2814 stand = thing;
2815 standx = x;
2816 standy = y;
2817
2818 for (by = yl; by <= yh; by++)
2819 for (bx = xl; bx <= xh; bx++)
2820 P_BlockThingsIterator(bx, by, PIT_PushableMoved);
2821 }
2822
2823 // Link the thing into its new position
2824 P_UnsetThingPosition(thing);
2825
2826 thing->floorz = tmfloorz;
2827 thing->ceilingz = tmceilingz;
2828 thing->floorrover = tmfloorrover;
2829 thing->ceilingrover = tmceilingrover;
2830
2831 if (!(thing->flags & MF_NOCLIPHEIGHT))
2832 {
2833 // Assign thing's standingslope if needed
2834 if (thing->z <= tmfloorz && !(thing->eflags & MFE_VERTICALFLIP)) {
2835 if (!startingonground && tmfloorslope)
2836 P_HandleSlopeLanding(thing, tmfloorslope);
2837
2838 if (thing->momz <= 0)
2839 {
2840 thing->standingslope = tmfloorslope;
2841 if (thing->momz == 0 && thing->player && !startingonground)
2842 P_PlayerHitFloor(thing->player, true);
2843 }
2844 }
2845 else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) {
2846 if (!startingonground && tmceilingslope)
2847 P_HandleSlopeLanding(thing, tmceilingslope);
2848
2849 if (thing->momz >= 0)
2850 {
2851 thing->standingslope = tmceilingslope;
2852 if (thing->momz == 0 && thing->player && !startingonground)
2853 P_PlayerHitFloor(thing->player, true);
2854 }
2855 }
2856 }
2857 else // don't set standingslope if you're not going to clip against it
2858 thing->standingslope = NULL;
2859
2860 thing->x = x;
2861 thing->y = y;
2862
2863 if (tmfloorthing)
2864 thing->eflags &= ~MFE_ONGROUND; // not on real floor
2865 else
2866 thing->eflags |= MFE_ONGROUND;
2867
2868 P_SetThingPosition(thing);
2869 return true;
2870 }
2871
P_SceneryTryMove(mobj_t * thing,fixed_t x,fixed_t y)2872 boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
2873 {
2874 fixed_t tryx, tryy;
2875
2876 tryx = thing->x;
2877 tryy = thing->y;
2878 do {
2879 if (x-tryx > MAXRADIUS)
2880 tryx += MAXRADIUS;
2881 else if (x-tryx < -MAXRADIUS)
2882 tryx -= MAXRADIUS;
2883 else
2884 tryx = x;
2885 if (y-tryy > MAXRADIUS)
2886 tryy += MAXRADIUS;
2887 else if (y-tryy < -MAXRADIUS)
2888 tryy -= MAXRADIUS;
2889 else
2890 tryy = y;
2891
2892 if (!P_CheckPosition(thing, tryx, tryy))
2893 return false; // solid wall or thing
2894
2895 if (!(thing->flags & MF_NOCLIP))
2896 {
2897 const fixed_t maxstep = MAXSTEPMOVE;
2898
2899 if (tmceilingz - tmfloorz < thing->height)
2900 return false; // doesn't fit
2901
2902 if (tmceilingz - thing->z < thing->height)
2903 return false; // mobj must lower itself to fit
2904
2905 if (tmfloorz - thing->z > maxstep)
2906 return false; // too big a step up
2907 }
2908 } while(tryx != x || tryy != y);
2909
2910 // the move is ok,
2911 // so link the thing into its new position
2912 P_UnsetThingPosition(thing);
2913
2914 thing->floorz = tmfloorz;
2915 thing->ceilingz = tmceilingz;
2916 thing->floorrover = tmfloorrover;
2917 thing->ceilingrover = tmceilingrover;
2918 thing->x = x;
2919 thing->y = y;
2920
2921 if (tmfloorthing)
2922 thing->eflags &= ~MFE_ONGROUND; // not on real floor
2923 else
2924 thing->eflags |= MFE_ONGROUND;
2925
2926 P_SetThingPosition(thing);
2927 return true;
2928 }
2929
2930 //
2931 // P_ThingHeightClip
2932 // Takes a valid thing and adjusts the thing->floorz,
2933 // thing->ceilingz, and possibly thing->z.
2934 // This is called for all nearby monsters
2935 // whenever a sector changes height.
2936 // If the thing doesn't fit,
2937 // the z will be set to the lowest value
2938 // and false will be returned.
2939 //
P_ThingHeightClip(mobj_t * thing)2940 static boolean P_ThingHeightClip(mobj_t *thing)
2941 {
2942 boolean floormoved;
2943 fixed_t oldfloorz = thing->floorz, oldz = thing->z;
2944 ffloor_t *oldfloorrover = thing->floorrover;
2945 ffloor_t *oldceilingrover = thing->ceilingrover;
2946 boolean onfloor = P_IsObjectOnGround(thing);//(thing->z <= thing->floorz);
2947 ffloor_t *rover = NULL;
2948 boolean bouncing;
2949 boolean hitfloor = false;
2950
2951 if (thing->flags & MF_NOCLIPHEIGHT)
2952 return true;
2953
2954 P_CheckPosition(thing, thing->x, thing->y);
2955
2956 if (P_MobjWasRemoved(thing))
2957 return true;
2958
2959 floormoved = (thing->eflags & MFE_VERTICALFLIP && tmceilingz != thing->ceilingz)
2960 || (!(thing->eflags & MFE_VERTICALFLIP) && tmfloorz != thing->floorz);
2961
2962 thing->floorz = tmfloorz;
2963 thing->ceilingz = tmceilingz;
2964 thing->floorrover = tmfloorrover;
2965 thing->ceilingrover = tmceilingrover;
2966
2967 // Ugly hack?!?! As long as just ceilingz is the lowest,
2968 // you'll still get crushed, right?
2969 if (tmfloorz > oldfloorz+thing->height)
2970 return true;
2971
2972 bouncing = thing->player && thing->state-states == S_PLAY_BOUNCE_LANDING && P_IsObjectOnGround(thing);
2973
2974 if ((onfloor || bouncing) && !(thing->flags & MF_NOGRAVITY) && floormoved)
2975 {
2976 rover = (thing->eflags & MFE_VERTICALFLIP) ? oldceilingrover : oldfloorrover;
2977
2978 // Match the Thing's old floorz to an FOF and check for FF_EXISTS
2979 // If ~FF_EXISTS, don't set mobj Z.
2980 if (!rover || ((rover->flags & FF_EXISTS) && (rover->flags & FF_SOLID)))
2981 {
2982 hitfloor = bouncing;
2983 if (thing->eflags & MFE_VERTICALFLIP)
2984 thing->pmomz = thing->ceilingz - (thing->z + thing->height);
2985 else
2986 thing->pmomz = thing->floorz - thing->z;
2987 thing->eflags |= MFE_APPLYPMOMZ;
2988
2989 if (thing->eflags & MFE_VERTICALFLIP)
2990 thing->z = thing->ceilingz - thing->height;
2991 else
2992 thing->z = thing->floorz;
2993 }
2994 }
2995 else if (!tmfloorthing)
2996 {
2997 // don't adjust a floating monster unless forced to
2998 if (thing->eflags & MFE_VERTICALFLIP)
2999 {
3000 if (!onfloor && thing->z < tmfloorz)
3001 thing->z = thing->floorz;
3002 }
3003 else if (!onfloor && thing->z + thing->height > tmceilingz)
3004 thing->z = thing->ceilingz - thing->height;
3005 }
3006
3007 if ((P_MobjFlip(thing)*(thing->z - oldz) > 0 || hitfloor) && thing->player)
3008 P_PlayerHitFloor(thing->player, !onfloor);
3009
3010 // debug: be sure it falls to the floor
3011 thing->eflags &= ~MFE_ONGROUND;
3012
3013 if (thing->ceilingz - thing->floorz < thing->height && thing->z >= thing->floorz)
3014 // BP: i know that this code cause many trouble but this also fixes
3015 // a lot of problems, mainly this is implementation of the stepping
3016 // for mobj (walk on solid corpse without jumping or fake 3d bridge)
3017 // problem is imp into imp at map01 and monster going at top of others
3018 return false;
3019
3020 return true;
3021 }
3022
3023 //
3024 // SLIDE MOVE
3025 // Allows the player to slide along any angled walls.
3026 //
3027 static fixed_t bestslidefrac, secondslidefrac;
3028 static line_t *bestslideline;
3029 static line_t *secondslideline;
3030 static mobj_t *slidemo;
3031 static fixed_t tmxmove, tmymove;
3032
3033 //
3034 // P_HitCameraSlideLine
3035 //
P_HitCameraSlideLine(line_t * ld,camera_t * thiscam)3036 static void P_HitCameraSlideLine(line_t *ld, camera_t *thiscam)
3037 {
3038 INT32 side;
3039 angle_t lineangle, moveangle, deltaangle;
3040 fixed_t movelen, newlen;
3041
3042 if (ld->slopetype == ST_HORIZONTAL)
3043 {
3044 tmymove = 0;
3045 return;
3046 }
3047
3048 if (ld->slopetype == ST_VERTICAL)
3049 {
3050 tmxmove = 0;
3051 return;
3052 }
3053
3054 side = P_PointOnLineSide(thiscam->x, thiscam->y, ld);
3055 lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
3056
3057 if (side == 1)
3058 lineangle += ANGLE_180;
3059
3060 moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
3061 deltaangle = moveangle-lineangle;
3062
3063 if (deltaangle > ANGLE_180)
3064 deltaangle += ANGLE_180;
3065
3066 lineangle >>= ANGLETOFINESHIFT;
3067 deltaangle >>= ANGLETOFINESHIFT;
3068
3069 movelen = P_AproxDistance(tmxmove, tmymove);
3070 newlen = FixedMul(movelen, FINECOSINE(deltaangle));
3071
3072 tmxmove = FixedMul(newlen, FINECOSINE(lineangle));
3073 tmymove = FixedMul(newlen, FINESINE(lineangle));
3074 }
3075
3076 //
3077 // P_HitSlideLine
3078 // Adjusts the xmove / ymove
3079 // so that the next move will slide along the wall.
3080 //
P_HitSlideLine(line_t * ld)3081 static void P_HitSlideLine(line_t *ld)
3082 {
3083 INT32 side;
3084 angle_t lineangle, moveangle, deltaangle;
3085 fixed_t movelen, newlen;
3086
3087 if (ld->slopetype == ST_HORIZONTAL)
3088 {
3089 tmymove = 0;
3090 return;
3091 }
3092
3093 if (ld->slopetype == ST_VERTICAL)
3094 {
3095 tmxmove = 0;
3096 return;
3097 }
3098
3099 side = P_PointOnLineSide(slidemo->x, slidemo->y, ld);
3100
3101 lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
3102
3103 if (side == 1)
3104 lineangle += ANGLE_180;
3105
3106 moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
3107 deltaangle = moveangle-lineangle;
3108
3109 if (deltaangle > ANGLE_180)
3110 deltaangle += ANGLE_180;
3111
3112 lineangle >>= ANGLETOFINESHIFT;
3113 deltaangle >>= ANGLETOFINESHIFT;
3114
3115 movelen = R_PointToDist2(0, 0, tmxmove, tmymove);
3116 newlen = FixedMul(movelen, FINECOSINE(deltaangle));
3117
3118 tmxmove = FixedMul(newlen, FINECOSINE(lineangle));
3119 tmymove = FixedMul(newlen, FINESINE(lineangle));
3120 }
3121
3122 //
3123 // P_HitBounceLine
3124 //
3125 // Adjusts the xmove / ymove so that the next move will bounce off the wall.
3126 //
P_HitBounceLine(line_t * ld)3127 static void P_HitBounceLine(line_t *ld)
3128 {
3129 angle_t lineangle, moveangle, deltaangle;
3130 fixed_t movelen;
3131
3132 if (ld->slopetype == ST_HORIZONTAL)
3133 {
3134 tmymove = -tmymove;
3135 return;
3136 }
3137
3138 if (ld->slopetype == ST_VERTICAL)
3139 {
3140 tmxmove = -tmxmove;
3141 return;
3142 }
3143
3144 lineangle = R_PointToAngle2(0, 0, ld->dx, ld->dy);
3145
3146 if (lineangle >= ANGLE_180)
3147 lineangle -= ANGLE_180;
3148
3149 moveangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
3150 deltaangle = moveangle + 2*(lineangle - moveangle);
3151
3152 lineangle >>= ANGLETOFINESHIFT;
3153 deltaangle >>= ANGLETOFINESHIFT;
3154
3155 movelen = P_AproxDistance(tmxmove, tmymove);
3156
3157 tmxmove = FixedMul(movelen, FINECOSINE(deltaangle));
3158 tmymove = FixedMul(movelen, FINESINE(deltaangle));
3159
3160 deltaangle = R_PointToAngle2(0, 0, tmxmove, tmymove);
3161 }
3162
3163 //
3164 // PTR_SlideCameraTraverse
3165 //
PTR_SlideCameraTraverse(intercept_t * in)3166 static boolean PTR_SlideCameraTraverse(intercept_t *in)
3167 {
3168 line_t *li;
3169
3170 I_Assert(in->isaline);
3171
3172 li = in->d.line;
3173
3174 // one-sided linedef
3175 if (!li->backsector)
3176 {
3177 if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, li))
3178 return true; // don't hit the back side
3179 goto isblocking;
3180 }
3181
3182 // set openrange, opentop, openbottom
3183 P_CameraLineOpening(li);
3184
3185 if (openrange < mapcampointer->height)
3186 goto isblocking; // doesn't fit
3187
3188 if (opentop - mapcampointer->z < mapcampointer->height)
3189 goto isblocking; // mobj is too high
3190
3191 if (openbottom - mapcampointer->z > 0) // We don't want to make the camera step up.
3192 goto isblocking; // too big a step up
3193
3194 // this line doesn't block movement
3195 return true;
3196
3197 // the line does block movement,
3198 // see if it is closer than best so far
3199 isblocking:
3200 {
3201 if (in->frac < bestslidefrac)
3202 {
3203 secondslidefrac = bestslidefrac;
3204 secondslideline = bestslideline;
3205 bestslidefrac = in->frac;
3206 bestslideline = li;
3207 }
3208 }
3209
3210 return false; // stop
3211 }
3212
3213 //
3214 // P_IsClimbingValid
3215 //
3216 // Unlike P_DoClimbing, don't use when up against a one-sided linedef.
3217 //
P_IsClimbingValid(player_t * player,angle_t angle)3218 static boolean P_IsClimbingValid(player_t *player, angle_t angle)
3219 {
3220 fixed_t platx, platy;
3221 sector_t *glidesector;
3222 fixed_t floorz, ceilingz;
3223 mobj_t *mo = player->mo;
3224 ffloor_t *rover;
3225
3226 platx = P_ReturnThrustX(mo, angle, mo->radius + FixedMul(8*FRACUNIT, mo->scale));
3227 platy = P_ReturnThrustY(mo, angle, mo->radius + FixedMul(8*FRACUNIT, mo->scale));
3228
3229 glidesector = R_PointInSubsector(mo->x + platx, mo->y + platy)->sector;
3230
3231 floorz = P_GetSectorFloorZAt (glidesector, mo->x, mo->y);
3232 ceilingz = P_GetSectorCeilingZAt(glidesector, mo->x, mo->y);
3233
3234 if (glidesector != mo->subsector->sector)
3235 {
3236 boolean floorclimb = false;
3237 fixed_t topheight, bottomheight;
3238
3239 for (rover = glidesector->ffloors; rover; rover = rover->next)
3240 {
3241 if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
3242 continue;
3243
3244 topheight = P_GetFFloorTopZAt (rover, mo->x, mo->y);
3245 bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
3246
3247 floorclimb = true;
3248
3249 if (mo->eflags & MFE_VERTICALFLIP)
3250 {
3251 if ((topheight < mo->z + mo->height) && ((mo->z + mo->height + mo->momz) < topheight))
3252 floorclimb = true;
3253 if (topheight < mo->z) // Waaaay below the ledge.
3254 floorclimb = false;
3255 if (bottomheight > mo->z + mo->height - FixedMul(16*FRACUNIT,mo->scale))
3256 floorclimb = false;
3257 }
3258 else
3259 {
3260 if ((bottomheight > mo->z) && ((mo->z - mo->momz) > bottomheight))
3261 floorclimb = true;
3262 if (bottomheight > mo->z + mo->height) // Waaaay below the ledge.
3263 floorclimb = false;
3264 if (topheight < mo->z + FixedMul(16*FRACUNIT,mo->scale))
3265 floorclimb = false;
3266 }
3267
3268 if (floorclimb)
3269 break;
3270 }
3271
3272 if (mo->eflags & MFE_VERTICALFLIP)
3273 {
3274 if ((floorz <= mo->z + mo->height)
3275 && ((mo->z + mo->height - mo->momz) <= floorz))
3276 floorclimb = true;
3277
3278 if ((floorz > mo->z)
3279 && glidesector->floorpic == skyflatnum)
3280 return false;
3281
3282 if ((mo->z + mo->height - FixedMul(16*FRACUNIT,mo->scale) > ceilingz)
3283 || (mo->z + mo->height <= floorz))
3284 floorclimb = true;
3285 }
3286 else
3287 {
3288 if ((ceilingz >= mo->z)
3289 && ((mo->z - mo->momz) >= ceilingz))
3290 floorclimb = true;
3291
3292 if ((ceilingz < mo->z+mo->height)
3293 && glidesector->ceilingpic == skyflatnum)
3294 return false;
3295
3296 if ((mo->z + FixedMul(16*FRACUNIT,mo->scale) < floorz)
3297 || (mo->z >= ceilingz))
3298 floorclimb = true;
3299 }
3300
3301 if (!floorclimb)
3302 return false;
3303
3304 return true;
3305 }
3306
3307 return false;
3308 }
3309
PTR_LineIsBlocking(line_t * li)3310 static boolean PTR_LineIsBlocking(line_t *li)
3311 {
3312 // one-sided linedefs are always solid to sliding movement.
3313 if (!li->backsector)
3314 return !P_PointOnLineSide(slidemo->x, slidemo->y, li);
3315
3316 if (!(slidemo->flags & MF_MISSILE))
3317 {
3318 if (li->flags & ML_IMPASSIBLE)
3319 return true;
3320
3321 if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
3322 return true;
3323 }
3324
3325 // set openrange, opentop, openbottom
3326 P_LineOpening(li, slidemo);
3327
3328 if (openrange < slidemo->height)
3329 return true; // doesn't fit
3330
3331 if (opentop - slidemo->z < slidemo->height)
3332 return true; // mobj is too high
3333
3334 if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
3335 return true; // too big a step up
3336
3337 if (slidemo->player
3338 && openrange < P_GetPlayerHeight(slidemo->player)
3339 && !P_PlayerCanEnterSpinGaps(slidemo->player))
3340 return true; // nonspin character should not take this path
3341
3342 return false;
3343 }
3344
PTR_GlideClimbTraverse(line_t * li)3345 static void PTR_GlideClimbTraverse(line_t *li)
3346 {
3347 line_t *checkline = li;
3348 ffloor_t *rover;
3349 fixed_t topheight, bottomheight;
3350 boolean fofline = false;
3351 sector_t *checksector = (li->backsector && !P_PointOnLineSide(slidemo->x, slidemo->y, li)) ? li->backsector : li->frontsector;
3352
3353 if (checksector->ffloors)
3354 {
3355 for (rover = checksector->ffloors; rover; rover = rover->next)
3356 {
3357 if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || ((rover->flags & FF_BUSTUP) && (slidemo->player->charflags & SF_CANBUSTWALLS)))
3358 continue;
3359
3360 topheight = P_GetFFloorTopZAt (rover, slidemo->x, slidemo->y);
3361 bottomheight = P_GetFFloorBottomZAt(rover, slidemo->x, slidemo->y);
3362
3363 if (topheight < slidemo->z)
3364 continue;
3365
3366 if (bottomheight > slidemo->z + slidemo->height)
3367 continue;
3368
3369 // Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
3370 if (rover->master->flags & ML_TFERLINE)
3371 {
3372 size_t linenum = li-checksector->lines[0];
3373 checkline = rover->master->frontsector->lines[0] + linenum;
3374 fofline = true;
3375 }
3376
3377 break;
3378 }
3379 }
3380
3381 // see about climbing on the wall
3382 if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
3383 {
3384 boolean canclimb;
3385 angle_t climbangle, climbline;
3386 INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
3387
3388 climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
3389
3390 if (whichside) // on second side?
3391 climbline += ANGLE_180;
3392
3393 climbangle += (ANGLE_90 * (whichside ? -1 : 1));
3394
3395 canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
3396
3397 if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
3398 || (slidemo->player->climbing == 1 && abs((signed)(slidemo->angle - climbline)) < ANGLE_135))
3399 && canclimb)
3400 {
3401 slidemo->angle = climbangle;
3402 /*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
3403 P_SetPlayerAngle(slidemo->player, slidemo->angle);*/
3404
3405 if (!slidemo->player->climbing)
3406 {
3407 S_StartSound(slidemo, sfx_s3k4a);
3408 slidemo->player->climbing = 5;
3409 if (slidemo->player->powers[pw_super])
3410 {
3411 P_Earthquake(slidemo, slidemo, 256*FRACUNIT);
3412 S_StartSound(slidemo, sfx_s3k49);
3413 }
3414 }
3415
3416 slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
3417 slidemo->player->glidetime = 0;
3418 slidemo->player->secondjump = 0;
3419
3420 if (slidemo->player->climbing > 1)
3421 slidemo->momz = slidemo->momx = slidemo->momy = 0;
3422
3423 if (fofline)
3424 whichside = 0;
3425
3426 if (!whichside)
3427 {
3428 slidemo->player->lastsidehit = checkline->sidenum[whichside];
3429 slidemo->player->lastlinehit = (INT16)(checkline - lines);
3430 }
3431
3432 P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
3433 }
3434 }
3435 }
3436
PTR_SlideTraverse(intercept_t * in)3437 static boolean PTR_SlideTraverse(intercept_t *in)
3438 {
3439 line_t *li;
3440
3441 I_Assert(in->isaline);
3442
3443 li = in->d.line;
3444
3445 if (!PTR_LineIsBlocking(li))
3446 return true;
3447
3448 // the line blocks movement,
3449 // see if it is closer than best so far
3450 if (li->polyobj && slidemo->player)
3451 {
3452 if ((li->polyobj->lines[0]->backsector->flags & SF_TRIGGERSPECIAL_TOUCH) && !(li->polyobj->flags & POF_NOSPECIALS))
3453 P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
3454 }
3455
3456 if (slidemo->player)
3457 {
3458 if (slidemo->player->charability == CA_GLIDEANDCLIMB
3459 && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing))
3460 PTR_GlideClimbTraverse(li);
3461 else
3462 {
3463 slidemo->player->lastsidehit = li->sidenum[P_PointOnLineSide(slidemo->x, slidemo->y, li)];
3464 slidemo->player->lastlinehit = (INT16)(li - lines);
3465 }
3466 }
3467
3468 if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing))
3469 {
3470 secondslidefrac = bestslidefrac;
3471 secondslideline = bestslideline;
3472 bestslidefrac = in->frac;
3473 bestslideline = li;
3474 }
3475
3476 return false; // stop
3477 }
3478
3479 //
3480 // P_SlideCameraMove
3481 //
3482 // Tries to slide the camera along a wall.
3483 //
P_SlideCameraMove(camera_t * thiscam)3484 void P_SlideCameraMove(camera_t *thiscam)
3485 {
3486 fixed_t leadx, leady, trailx, traily, newx, newy;
3487 INT32 hitcount = 0;
3488 INT32 retval = 0;
3489
3490 bestslideline = NULL;
3491
3492 retry:
3493 if (++hitcount == 3)
3494 goto stairstep; // don't loop forever
3495
3496 // trace along the three leading corners
3497 if (thiscam->momx > 0)
3498 {
3499 leadx = thiscam->x + thiscam->radius;
3500 trailx = thiscam->x - thiscam->radius;
3501 }
3502 else
3503 {
3504 leadx = thiscam->x - thiscam->radius;
3505 trailx = thiscam->x + thiscam->radius;
3506 }
3507
3508 if (thiscam->momy > 0)
3509 {
3510 leady = thiscam->y + thiscam->radius;
3511 traily = thiscam->y - thiscam->radius;
3512 }
3513 else
3514 {
3515 leady = thiscam->y - thiscam->radius;
3516 traily = thiscam->y + thiscam->radius;
3517 }
3518
3519 bestslidefrac = FRACUNIT+1;
3520
3521 mapcampointer = thiscam;
3522
3523 P_PathTraverse(leadx, leady, leadx + thiscam->momx, leady + thiscam->momy,
3524 PT_ADDLINES, PTR_SlideCameraTraverse);
3525 P_PathTraverse(trailx, leady, trailx + thiscam->momx, leady + thiscam->momy,
3526 PT_ADDLINES, PTR_SlideCameraTraverse);
3527 P_PathTraverse(leadx, traily, leadx + thiscam->momx, traily + thiscam->momy,
3528 PT_ADDLINES, PTR_SlideCameraTraverse);
3529
3530 // move up to the wall
3531 if (bestslidefrac == FRACUNIT+1)
3532 {
3533 retval = P_TryCameraMove(thiscam->x, thiscam->y + thiscam->momy, thiscam);
3534 // the move must have hit the middle, so stairstep
3535 stairstep:
3536 if (!retval) // Allow things to drop off.
3537 P_TryCameraMove(thiscam->x + thiscam->momx, thiscam->y, thiscam);
3538 return;
3539 }
3540
3541 // fudge a bit to make sure it doesn't hit
3542 bestslidefrac -= 0x800;
3543 if (bestslidefrac > 0)
3544 {
3545 newx = FixedMul(thiscam->momx, bestslidefrac);
3546 newy = FixedMul(thiscam->momy, bestslidefrac);
3547
3548 retval = P_TryCameraMove(thiscam->x + newx, thiscam->y + newy, thiscam);
3549
3550 if (!retval)
3551 goto stairstep;
3552 }
3553
3554 // Now continue along the wall.
3555 // First calculate remainder.
3556 bestslidefrac = FRACUNIT - (bestslidefrac+0x800);
3557
3558 if (bestslidefrac > FRACUNIT)
3559 bestslidefrac = FRACUNIT;
3560
3561 if (bestslidefrac <= 0)
3562 return;
3563
3564 tmxmove = FixedMul(thiscam->momx, bestslidefrac);
3565 tmymove = FixedMul(thiscam->momy, bestslidefrac);
3566
3567 P_HitCameraSlideLine(bestslideline, thiscam); // clip the moves
3568
3569 thiscam->momx = tmxmove;
3570 thiscam->momy = tmymove;
3571
3572 retval = P_TryCameraMove(thiscam->x + tmxmove, thiscam->y + tmymove, thiscam);
3573
3574 if (!retval)
3575 goto retry;
3576 }
3577
P_CheckLavaWall(mobj_t * mo,sector_t * sec)3578 static void P_CheckLavaWall(mobj_t *mo, sector_t *sec)
3579 {
3580 ffloor_t *rover;
3581 fixed_t topheight, bottomheight;
3582
3583 for (rover = sec->ffloors; rover; rover = rover->next)
3584 {
3585 if (!(rover->flags & FF_EXISTS))
3586 continue;
3587
3588 if (!(rover->flags & FF_SWIMMABLE))
3589 continue;
3590
3591 if (GETSECSPECIAL(rover->master->frontsector->special, 1) != 3)
3592 continue;
3593
3594 if (rover->master->flags & ML_BLOCKMONSTERS)
3595 continue;
3596
3597 topheight = P_GetFFloorTopZAt(rover, mo->x, mo->y);
3598
3599 if (mo->eflags & MFE_VERTICALFLIP)
3600 {
3601 if (topheight < mo->z - mo->height)
3602 continue;
3603 }
3604 else
3605 {
3606 if (topheight < mo->z)
3607 continue;
3608 }
3609
3610 bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
3611
3612 if (mo->eflags & MFE_VERTICALFLIP)
3613 {
3614 if (bottomheight > mo->z)
3615 continue;
3616 }
3617 else
3618 {
3619 if (bottomheight > mo->z + mo->height)
3620 continue;
3621 }
3622
3623 P_DamageMobj(mo, NULL, NULL, 1, DMG_FIRE);
3624 return;
3625 }
3626 }
3627
3628 //
3629 // P_SlideMove
3630 // The momx / momy move is bad, so try to slide
3631 // along a wall.
3632 // Find the first line hit, move flush to it,
3633 // and slide along it
3634 //
3635 // This is a kludgy mess.
3636 //
P_SlideMove(mobj_t * mo)3637 void P_SlideMove(mobj_t *mo)
3638 {
3639 fixed_t leadx, leady, trailx, traily, newx, newy;
3640 INT16 hitcount = 0;
3641 boolean success = false;
3642
3643 boolean papercol = false;
3644 vertex_t v1, v2; // fake vertexes
3645 line_t junk; // fake linedef
3646
3647 if (tmhitthing && mo->z + mo->height > tmhitthing->z && mo->z < tmhitthing->z + tmhitthing->height)
3648 {
3649 // Don't mess with your momentum if it's a pushable object. Pushables do their own crazy things already.
3650 if (tmhitthing->flags & MF_PUSHABLE)
3651 return;
3652
3653 if (tmhitthing->flags & MF_PAPERCOLLISION)
3654 {
3655 fixed_t cosradius, sinradius, num, den;
3656
3657 // trace along the three leading corners
3658 if (mo->momx > 0)
3659 {
3660 leadx = mo->x + mo->radius;
3661 trailx = mo->x - mo->radius;
3662 }
3663 else
3664 {
3665 leadx = mo->x - mo->radius;
3666 trailx = mo->x + mo->radius;
3667 }
3668
3669 if (mo->momy > 0)
3670 {
3671 leady = mo->y + mo->radius;
3672 traily = mo->y - mo->radius;
3673 }
3674 else
3675 {
3676 leady = mo->y - mo->radius;
3677 traily = mo->y + mo->radius;
3678 }
3679
3680 papercol = true;
3681 slidemo = mo;
3682 bestslideline = &junk;
3683
3684 cosradius = FixedMul(tmhitthing->radius, FINECOSINE(tmhitthing->angle>>ANGLETOFINESHIFT));
3685 sinradius = FixedMul(tmhitthing->radius, FINESINE(tmhitthing->angle>>ANGLETOFINESHIFT));
3686
3687 v1.x = tmhitthing->x - cosradius;
3688 v1.y = tmhitthing->y - sinradius;
3689 v2.x = tmhitthing->x + cosradius;
3690 v2.y = tmhitthing->y + sinradius;
3691
3692 // Can we box collision our way into smooth movement..?
3693 if (sinradius && mo->y + mo->radius <= min(v1.y, v2.y))
3694 {
3695 mo->momy = 0;
3696 P_TryMove(mo, mo->x + mo->momx, min(v1.y, v2.y) - mo->radius, true);
3697 return;
3698 }
3699 else if (sinradius && mo->y - mo->radius >= max(v1.y, v2.y))
3700 {
3701 mo->momy = 0;
3702 P_TryMove(mo, mo->x + mo->momx, max(v1.y, v2.y) + mo->radius, true);
3703 return;
3704 }
3705 else if (cosradius && mo->x + mo->radius <= min(v1.x, v2.x))
3706 {
3707 mo->momx = 0;
3708 P_TryMove(mo, min(v1.x, v2.x) - mo->radius, mo->y + mo->momy, true);
3709 return;
3710 }
3711 else if (cosradius && mo->x - mo->radius >= max(v1.x, v2.x))
3712 {
3713 mo->momx = 0;
3714 P_TryMove(mo, max(v1.x, v2.x) + mo->radius, mo->y + mo->momy, true);
3715 return;
3716 }
3717
3718 // nope, gotta fuck around with a fake linedef!
3719 junk.v1 = &v1;
3720 junk.v2 = &v2;
3721 junk.dx = 2*cosradius; // v2.x - v1.x;
3722 junk.dy = 2*sinradius; // v2.y - v1.y;
3723
3724 junk.slopetype = !cosradius ? ST_VERTICAL : !sinradius ? ST_HORIZONTAL :
3725 ((sinradius > 0) == (cosradius > 0)) ? ST_POSITIVE : ST_NEGATIVE;
3726
3727 bestslidefrac = FRACUNIT+1;
3728
3729 den = FixedMul(junk.dy>>8, mo->momx) - FixedMul(junk.dx>>8, mo->momy);
3730
3731 if (!den)
3732 bestslidefrac = 0;
3733 else
3734 {
3735 fixed_t frac;
3736 #define P_PaperTraverse(startx, starty) \
3737 num = FixedMul((v1.x - leadx)>>8, junk.dy) + FixedMul((leady - v1.y)>>8, junk.dx); \
3738 frac = FixedDiv(num, den); \
3739 if (frac < bestslidefrac) \
3740 bestslidefrac = frac
3741 P_PaperTraverse(leadx, leady);
3742 P_PaperTraverse(trailx, leady);
3743 P_PaperTraverse(leadx, traily);
3744 #undef dowork
3745 }
3746
3747 goto papercollision;
3748 }
3749
3750 // Thankfully box collisions are a lot simpler than arbitrary lines. There's only four possible cases.
3751 if (mo->y + mo->radius <= tmhitthing->y - tmhitthing->radius)
3752 {
3753 mo->momy = 0;
3754 P_TryMove(mo, mo->x + mo->momx, tmhitthing->y - tmhitthing->radius - mo->radius, true);
3755 }
3756 else if (mo->y - mo->radius >= tmhitthing->y + tmhitthing->radius)
3757 {
3758 mo->momy = 0;
3759 P_TryMove(mo, mo->x + mo->momx, tmhitthing->y + tmhitthing->radius + mo->radius, true);
3760 }
3761 else if (mo->x + mo->radius <= tmhitthing->x - tmhitthing->radius)
3762 {
3763 mo->momx = 0;
3764 P_TryMove(mo, tmhitthing->x - tmhitthing->radius - mo->radius, mo->y + mo->momy, true);
3765 }
3766 else if (mo->x - mo->radius >= tmhitthing->x + tmhitthing->radius)
3767 {
3768 mo->momx = 0;
3769 P_TryMove(mo, tmhitthing->x + tmhitthing->radius + mo->radius, mo->y + mo->momy, true);
3770 }
3771 else
3772 mo->momx = mo->momy = 0;
3773 return;
3774 }
3775
3776 slidemo = mo;
3777 bestslideline = NULL;
3778
3779 retry:
3780 if ((++hitcount == 3) || papercol)
3781 goto stairstep; // don't loop forever
3782
3783 // trace along the three leading corners
3784 if (mo->momx > 0)
3785 {
3786 leadx = mo->x + mo->radius;
3787 trailx = mo->x - mo->radius;
3788 }
3789 else
3790 {
3791 leadx = mo->x - mo->radius;
3792 trailx = mo->x + mo->radius;
3793 }
3794
3795 if (mo->momy > 0)
3796 {
3797 leady = mo->y + mo->radius;
3798 traily = mo->y - mo->radius;
3799 }
3800 else
3801 {
3802 leady = mo->y - mo->radius;
3803 traily = mo->y + mo->radius;
3804 }
3805
3806 bestslidefrac = FRACUNIT+1;
3807
3808 P_PathTraverse(leadx, leady, leadx + mo->momx, leady + mo->momy,
3809 PT_ADDLINES, PTR_SlideTraverse);
3810 P_PathTraverse(trailx, leady, trailx + mo->momx, leady + mo->momy,
3811 PT_ADDLINES, PTR_SlideTraverse);
3812 P_PathTraverse(leadx, traily, leadx + mo->momx, traily + mo->momy,
3813 PT_ADDLINES, PTR_SlideTraverse);
3814
3815 if (bestslideline && mo->player && bestslideline->sidenum[1] != 0xffff)
3816 {
3817 sector_t *sec = P_PointOnLineSide(mo->x, mo->y, bestslideline) ? bestslideline->frontsector : bestslideline->backsector;
3818 P_CheckLavaWall(mo, sec);
3819 }
3820
3821 // Some walls are bouncy even if you're not
3822 if (bestslideline && bestslideline->flags & ML_BOUNCY)
3823 {
3824 P_BounceMove(mo);
3825 return;
3826 }
3827
3828 papercollision:
3829 // move up to the wall
3830 if (bestslidefrac == FRACUNIT+1)
3831 {
3832 // the move must have hit the middle, so stairstep
3833 stairstep:
3834 if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true)) //Allow things to drop off.
3835 P_TryMove(mo, mo->x + mo->momx, mo->y, true);
3836 return;
3837 }
3838
3839 // fudge a bit to make sure it doesn't hit
3840 bestslidefrac -= 0x800;
3841 if (bestslidefrac > 0)
3842 {
3843 newx = FixedMul(mo->momx, bestslidefrac);
3844 newy = FixedMul(mo->momy, bestslidefrac);
3845
3846 if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
3847 goto stairstep;
3848 }
3849
3850 // Now continue along the wall.
3851 // First calculate remainder.
3852 bestslidefrac = FRACUNIT - (bestslidefrac+0x800);
3853
3854 if (bestslidefrac > FRACUNIT)
3855 bestslidefrac = FRACUNIT;
3856
3857 if (bestslidefrac <= 0)
3858 return;
3859
3860 tmxmove = FixedMul(mo->momx, bestslidefrac);
3861 tmymove = FixedMul(mo->momy, bestslidefrac);
3862
3863 P_HitSlideLine(bestslideline); // clip the moves
3864
3865 if ((twodlevel || (mo->flags2 & MF2_TWOD)) && mo->player)
3866 {
3867 mo->momx = tmxmove;
3868 tmymove = 0;
3869 }
3870 else
3871 {
3872 mo->momx = tmxmove;
3873 mo->momy = tmymove;
3874 }
3875
3876 do {
3877 if (tmxmove > mo->radius) {
3878 newx = mo->x + mo->radius;
3879 tmxmove -= mo->radius;
3880 } else if (tmxmove < -mo->radius) {
3881 newx = mo->x - mo->radius;
3882 tmxmove += mo->radius;
3883 } else {
3884 newx = mo->x + tmxmove;
3885 tmxmove = 0;
3886 }
3887 if (tmymove > mo->radius) {
3888 newy = mo->y + mo->radius;
3889 tmymove -= mo->radius;
3890 } else if (tmymove < -mo->radius) {
3891 newy = mo->y - mo->radius;
3892 tmymove += mo->radius;
3893 } else {
3894 newy = mo->y + tmymove;
3895 tmymove = 0;
3896 }
3897 if (!P_TryMove(mo, newx, newy, true)) {
3898 if (success)
3899 return; // Good enough!!
3900 else
3901 goto retry;
3902 }
3903 success = true;
3904 } while(tmxmove || tmymove);
3905 }
3906
3907 //
3908 // P_BounceMove
3909 //
3910 // The momx / momy move is bad, so try to bounce off a wall.
3911 //
P_BounceMove(mobj_t * mo)3912 void P_BounceMove(mobj_t *mo)
3913 {
3914 fixed_t leadx, leady;
3915 fixed_t trailx, traily;
3916 fixed_t newx, newy;
3917 INT32 hitcount;
3918 fixed_t mmomx = 0, mmomy = 0;
3919
3920 slidemo = mo;
3921 hitcount = 0;
3922
3923 retry:
3924 if (++hitcount == 3)
3925 goto bounceback; // don't loop forever
3926
3927 if (mo->player)
3928 {
3929 mmomx = mo->player->rmomx;
3930 mmomy = mo->player->rmomy;
3931 }
3932 else
3933 {
3934 mmomx = mo->momx;
3935 mmomy = mo->momy;
3936 }
3937
3938 // trace along the three leading corners
3939 if (mo->momx > 0)
3940 {
3941 leadx = mo->x + mo->radius;
3942 trailx = mo->x - mo->radius;
3943 }
3944 else
3945 {
3946 leadx = mo->x - mo->radius;
3947 trailx = mo->x + mo->radius;
3948 }
3949
3950 if (mo->momy > 0)
3951 {
3952 leady = mo->y + mo->radius;
3953 traily = mo->y - mo->radius;
3954 }
3955 else
3956 {
3957 leady = mo->y - mo->radius;
3958 traily = mo->y + mo->radius;
3959 }
3960
3961 bestslidefrac = FRACUNIT + 1;
3962
3963 P_PathTraverse(leadx, leady, leadx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
3964 P_PathTraverse(trailx, leady, trailx + mmomx, leady + mmomy, PT_ADDLINES, PTR_SlideTraverse);
3965 P_PathTraverse(leadx, traily, leadx + mmomx, traily + mmomy, PT_ADDLINES, PTR_SlideTraverse);
3966
3967 // move up to the wall
3968 if (bestslidefrac == FRACUNIT + 1)
3969 {
3970 // the move must have hit the middle, so bounce straight back
3971 bounceback:
3972 if (P_TryMove(mo, mo->x - mmomx, mo->y - mmomy, true))
3973 {
3974 mo->momx *= -1;
3975 mo->momy *= -1;
3976 mo->momx = FixedMul(mo->momx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
3977 mo->momy = FixedMul(mo->momy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
3978
3979 if (mo->player)
3980 {
3981 mo->player->cmomx *= -1;
3982 mo->player->cmomy *= -1;
3983 mo->player->cmomx = FixedMul(mo->player->cmomx,
3984 (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
3985 mo->player->cmomy = FixedMul(mo->player->cmomy,
3986 (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
3987 }
3988 }
3989 return;
3990 }
3991
3992 // fudge a bit to make sure it doesn't hit
3993 bestslidefrac -= 0x800;
3994 if (bestslidefrac > 0)
3995 {
3996 newx = FixedMul(mmomx, bestslidefrac);
3997 newy = FixedMul(mmomy, bestslidefrac);
3998
3999 if (!P_TryMove(mo, mo->x + newx, mo->y + newy, true))
4000 goto bounceback;
4001 }
4002
4003 // Now continue along the wall.
4004 // First calculate remainder.
4005 bestslidefrac = FRACUNIT - bestslidefrac;
4006
4007 if (bestslidefrac > FRACUNIT)
4008 bestslidefrac = FRACUNIT;
4009
4010 if (bestslidefrac <= 0)
4011 return;
4012
4013 if (mo->type == MT_SHELL)
4014 {
4015 tmxmove = mmomx;
4016 tmymove = mmomy;
4017 }
4018 else if (mo->type == MT_THROWNBOUNCE)
4019 {
4020 tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
4021 tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>6) - (FRACUNIT>>5)));
4022 }
4023 else if (mo->type == MT_THROWNGRENADE || mo->type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)
4024 {
4025 // Quickly decay speed as it bounces
4026 tmxmove = FixedDiv(mmomx, 2*FRACUNIT);
4027 tmymove = FixedDiv(mmomy, 2*FRACUNIT);
4028 }
4029 else
4030 {
4031 tmxmove = FixedMul(mmomx, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
4032 tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
4033 }
4034
4035 P_HitBounceLine(bestslideline); // clip the moves
4036
4037 mo->momx = tmxmove;
4038 mo->momy = tmymove;
4039
4040 if (mo->player)
4041 {
4042 mo->player->cmomx = tmxmove;
4043 mo->player->cmomy = tmymove;
4044 }
4045
4046 if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true))
4047 goto retry;
4048 }
4049
4050 //
4051 // RADIUS ATTACK
4052 //
4053 static fixed_t bombdamage;
4054 static mobj_t *bombsource;
4055 static mobj_t *bombspot;
4056 static UINT8 bombdamagetype;
4057 static boolean bombsightcheck;
4058
4059 //
4060 // PIT_RadiusAttack
4061 // "bombsource" is the creature
4062 // that caused the explosion at "bombspot".
4063 //
PIT_RadiusAttack(mobj_t * thing)4064 static boolean PIT_RadiusAttack(mobj_t *thing)
4065 {
4066 fixed_t dx, dy, dz, dist;
4067
4068 if (thing == bombspot) // ignore the bomb itself (Deton fix)
4069 return true;
4070
4071 if (bombsource && thing->type == bombsource->type && !(bombdamagetype & DMG_CANHURTSELF)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
4072 return true;
4073
4074 if (thing->type == MT_MINUS && !(thing->flags & (MF_SPECIAL|MF_SHOOTABLE)) && !bombsightcheck)
4075 thing->flags = (thing->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE;
4076
4077 if (thing->type == MT_EGGGUARD && thing->tracer) //nuke Egg Guard's shield!
4078 P_KillMobj(thing->tracer, bombspot, bombsource, bombdamagetype);
4079
4080 if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE)
4081 return true;
4082
4083 dx = abs(thing->x - bombspot->x);
4084 dy = abs(thing->y - bombspot->y);
4085 dz = abs(thing->z + (thing->height>>1) - bombspot->z);
4086
4087 dist = P_AproxDistance(P_AproxDistance(dx, dy), dz);
4088 dist -= thing->radius;
4089
4090 if (dist < 0)
4091 dist = 0;
4092
4093 if (dist >= bombdamage)
4094 return true; // out of range
4095
4096 if (thing->floorz > bombspot->z && bombspot->ceilingz < thing->z)
4097 return true;
4098
4099 if (thing->ceilingz < bombspot->z && bombspot->floorz > thing->z)
4100 return true;
4101
4102 if (!bombsightcheck || P_CheckSight(thing, bombspot))
4103 { // must be in direct path
4104 P_DamageMobj(thing, bombspot, bombsource, 1, bombdamagetype); // Tails 01-11-2001
4105 }
4106
4107 return true;
4108 }
4109
4110 //
4111 // P_RadiusAttack
4112 // Source is the creature that caused the explosion at spot.
4113 //
P_RadiusAttack(mobj_t * spot,mobj_t * source,fixed_t damagedist,UINT8 damagetype,boolean sightcheck)4114 void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck)
4115 {
4116 INT32 x, y;
4117 INT32 xl, xh, yl, yh;
4118 fixed_t dist;
4119
4120 dist = FixedMul(damagedist, spot->scale) + MAXRADIUS;
4121 yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
4122 yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
4123 xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
4124 xl = (unsigned)(spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT;
4125
4126 BMBOUNDFIX(xl, xh, yl, yh);
4127
4128 bombspot = spot;
4129 bombsource = source;
4130 bombdamage = FixedMul(damagedist, spot->scale);
4131 bombdamagetype = damagetype;
4132 bombsightcheck = sightcheck;
4133
4134 for (y = yl; y <= yh; y++)
4135 for (x = xl; x <= xh; x++)
4136 P_BlockThingsIterator(x, y, PIT_RadiusAttack);
4137 }
4138
4139 //
4140 // SECTOR HEIGHT CHANGING
4141 // After modifying a sectors floor or ceiling height,
4142 // call this routine to adjust the positions
4143 // of all things that touch the sector.
4144 //
4145 // If anything doesn't fit anymore, true will be returned.
4146 // If crunch is true, they will take damage
4147 // as they are being crushed.
4148 // If Crunch is false, you should set the sector height back
4149 // the way it was and call P_CheckSector (? was P_ChangeSector - Graue) again
4150 // to undo the changes.
4151 //
4152 static boolean crushchange;
4153 static boolean nofit;
4154
4155 //
4156 // PIT_ChangeSector
4157 //
PIT_ChangeSector(mobj_t * thing,boolean realcrush)4158 static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
4159 {
4160 mobj_t *killer = NULL;
4161 //If a thing is both pushable and vulnerable, it doesn't block the crusher because it gets killed.
4162 boolean immunepushable = ((thing->flags & (MF_PUSHABLE | MF_SHOOTABLE)) == MF_PUSHABLE);
4163
4164 if (P_ThingHeightClip(thing))
4165 {
4166 //thing fits, check next thing
4167 return true;
4168 }
4169
4170 if (!(thing->flags & (MF_SHOOTABLE|MF_PUSHABLE))
4171 || thing->flags & MF_NOCLIPHEIGHT)
4172 {
4173 //doesn't interact with the crusher, just ignore it
4174 return true;
4175 }
4176
4177 // Thing doesn't fit. If thing is vulnerable, that means it's being crushed. If thing is pushable, it might
4178 // just be blocked by another object - check if it's really a ceiling!
4179 if (thing->z + thing->height > thing->ceilingz && thing->z <= thing->ceilingz)
4180 {
4181 if (immunepushable && thing->z + thing->height > thing->subsector->sector->ceilingheight)
4182 {
4183 //Thing is a pushable and blocks the moving ceiling
4184 nofit = true;
4185 return false;
4186 }
4187
4188 //Check FOFs in the sector
4189 if (thing->subsector->sector->ffloors && (realcrush || immunepushable))
4190 {
4191 ffloor_t *rover;
4192 fixed_t topheight, bottomheight;
4193 fixed_t delta1, delta2;
4194 INT32 thingtop = thing->z + thing->height;
4195
4196 for (rover = thing->subsector->sector->ffloors; rover; rover = rover->next)
4197 {
4198 if (!(((rover->flags & FF_BLOCKPLAYER) && thing->player)
4199 || ((rover->flags & FF_BLOCKOTHERS) && !thing->player)) || !(rover->flags & FF_EXISTS))
4200 continue;
4201
4202 topheight = *rover->topheight;
4203 bottomheight = *rover->bottomheight;
4204 //topheight = P_GetFFloorTopZAt (rover, thing->x, thing->y);
4205 //bottomheight = P_GetFFloorBottomZAt(rover, thing->x, thing->y);
4206
4207 delta1 = thing->z - (bottomheight + topheight)/2;
4208 delta2 = thingtop - (bottomheight + topheight)/2;
4209 if (bottomheight <= thing->ceilingz && abs(delta1) >= abs(delta2))
4210 {
4211 if (immunepushable)
4212 {
4213 //FOF is blocked by pushable
4214 nofit = true;
4215 return false;
4216 }
4217 else
4218 {
4219 //If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
4220 thinker_t *think;
4221 crumble_t *crumbler;
4222
4223 for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
4224 {
4225 if (think->function.acp1 != (actionf_p1)T_StartCrumble)
4226 continue;
4227
4228 crumbler = (crumble_t *)think;
4229
4230 if (crumbler->player && crumbler->player->mo
4231 && crumbler->player->mo != thing
4232 && crumbler->actionsector == thing->subsector->sector
4233 && crumbler->sector == rover->master->frontsector)
4234 {
4235 killer = crumbler->player->mo;
4236 }
4237 }
4238 }
4239 }
4240 }
4241 }
4242
4243 if (realcrush)
4244 {
4245 // Crush the object
4246 if (netgame && thing->player && thing->player->spectator)
4247 P_DamageMobj(thing, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators
4248 else
4249 P_DamageMobj(thing, killer, killer, 1, DMG_CRUSHED);
4250 return true;
4251 }
4252 }
4253
4254 if (realcrush && crushchange)
4255 P_DamageMobj(thing, NULL, NULL, 1, 0);
4256
4257 // keep checking (crush other things)
4258 return true;
4259 }
4260
4261 //
4262 // P_CheckSector
4263 //
P_CheckSector(sector_t * sector,boolean crunch)4264 boolean P_CheckSector(sector_t *sector, boolean crunch)
4265 {
4266 msecnode_t *n;
4267 size_t i;
4268
4269 nofit = false;
4270 crushchange = crunch;
4271
4272 // killough 4/4/98: scan list front-to-back until empty or exhausted,
4273 // restarting from beginning after each thing is processed. Avoids
4274 // crashes, and is sure to examine all things in the sector, and only
4275 // the things which are in the sector, until a steady-state is reached.
4276 // Things can arbitrarily be inserted and removed and it won't mess up.
4277 //
4278 // killough 4/7/98: simplified to avoid using complicated counter
4279
4280
4281 // First, let's see if anything will keep it from crushing.
4282
4283 // Sal: This stupid function chain is required to fix polyobjects not being able to crush.
4284 // Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
4285 validcount++;
4286
4287 for (i = 0; i < sector->linecount; i++)
4288 {
4289 if (sector->lines[i]->polyobj)
4290 {
4291 polyobj_t *po = sector->lines[i]->polyobj;
4292 if (po->validcount == validcount)
4293 continue; // skip if already checked
4294 if (!(po->flags & POF_SOLID))
4295 continue;
4296 if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
4297 {
4298 INT32 x, y;
4299 po->validcount = validcount;
4300
4301 for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
4302 {
4303 for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
4304 {
4305 mobj_t *mo;
4306
4307 if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
4308 continue;
4309
4310 mo = blocklinks[y * bmapwidth + x];
4311
4312 for (; mo; mo = mo->bnext)
4313 {
4314 // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
4315
4316 if (!P_MobjInsidePolyobj(po, mo))
4317 continue;
4318
4319 if (!PIT_ChangeSector(mo, false))
4320 {
4321 nofit = true;
4322 return nofit;
4323 }
4324 }
4325 }
4326 }
4327 }
4328 }
4329 }
4330
4331 if (sector->numattached)
4332 {
4333 sector_t *sec;
4334 for (i = 0; i < sector->numattached; i++)
4335 {
4336 sec = §ors[sector->attached[i]];
4337 for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
4338 n->visited = false;
4339
4340 sec->moved = true;
4341
4342 P_RecalcPrecipInSector(sec);
4343
4344 if (!sector->attachedsolid[i])
4345 continue;
4346
4347 do
4348 {
4349 for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
4350 if (!n->visited)
4351 {
4352 n->visited = true;
4353 if (!(n->m_thing->flags & MF_NOBLOCKMAP))
4354 {
4355 if (!PIT_ChangeSector(n->m_thing, false))
4356 {
4357 nofit = true;
4358 return nofit;
4359 }
4360 }
4361 break;
4362 }
4363 } while (n);
4364 }
4365 }
4366
4367 // Mark all things invalid
4368 sector->moved = true;
4369
4370 for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
4371 n->visited = false;
4372
4373 do
4374 {
4375 for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
4376 if (!n->visited) // unprocessed thing found
4377 {
4378 n->visited = true; // mark thing as processed
4379 if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
4380 {
4381 if (!PIT_ChangeSector(n->m_thing, false)) // process it
4382 {
4383 nofit = true;
4384 return nofit;
4385 }
4386 }
4387 break; // exit and start over
4388 }
4389 } while (n); // repeat from scratch until all things left are marked valid
4390
4391 // Nothing blocked us, so lets crush for real!
4392
4393 // Sal: This stupid function chain is required to fix polyobjects not being able to crush.
4394 // Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
4395 validcount++;
4396
4397 for (i = 0; i < sector->linecount; i++)
4398 {
4399 if (sector->lines[i]->polyobj)
4400 {
4401 polyobj_t *po = sector->lines[i]->polyobj;
4402 if (po->validcount == validcount)
4403 continue; // skip if already checked
4404 if (!(po->flags & POF_SOLID))
4405 continue;
4406 if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
4407 {
4408 INT32 x, y;
4409 po->validcount = validcount;
4410
4411 for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
4412 {
4413 for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
4414 {
4415 mobj_t *mo;
4416
4417 if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
4418 continue;
4419
4420 mo = blocklinks[y * bmapwidth + x];
4421
4422 for (; mo; mo = mo->bnext)
4423 {
4424 // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
4425
4426 if (!P_MobjInsidePolyobj(po, mo))
4427 continue;
4428
4429 PIT_ChangeSector(mo, true);
4430 return nofit;
4431 }
4432 }
4433 }
4434 }
4435 }
4436 }
4437 if (sector->numattached)
4438 {
4439 sector_t *sec;
4440 for (i = 0; i < sector->numattached; i++)
4441 {
4442 sec = §ors[sector->attached[i]];
4443 for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
4444 n->visited = false;
4445
4446 sec->moved = true;
4447
4448 P_RecalcPrecipInSector(sec);
4449
4450 if (!sector->attachedsolid[i])
4451 continue;
4452
4453 do
4454 {
4455 for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
4456 if (!n->visited)
4457 {
4458 n->visited = true;
4459 if (!(n->m_thing->flags & MF_NOBLOCKMAP))
4460 {
4461 PIT_ChangeSector(n->m_thing, true);
4462 return nofit;
4463 }
4464 break;
4465 }
4466 } while (n);
4467 }
4468 }
4469
4470 // Mark all things invalid
4471 sector->moved = true;
4472
4473 for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
4474 n->visited = false;
4475
4476 do
4477 {
4478 for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
4479 if (!n->visited) // unprocessed thing found
4480 {
4481 n->visited = true; // mark thing as processed
4482 if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
4483 {
4484 PIT_ChangeSector(n->m_thing, true); // process it
4485 return nofit;
4486 }
4487 break; // exit and start over
4488 }
4489 } while (n); // repeat from scratch until all things left are marked valid
4490
4491 return nofit;
4492 }
4493
4494 /*
4495 SoM: 3/15/2000
4496 Lots of new Boom functions that work faster and add functionality.
4497 */
4498
4499 static msecnode_t *headsecnode = NULL;
4500 static mprecipsecnode_t *headprecipsecnode = NULL;
4501
P_Initsecnode(void)4502 void P_Initsecnode(void)
4503 {
4504 headsecnode = NULL;
4505 headprecipsecnode = NULL;
4506 }
4507
4508 // P_GetSecnode() retrieves a node from the freelist. The calling routine
4509 // should make sure it sets all fields properly.
4510
P_GetSecnode(void)4511 static msecnode_t *P_GetSecnode(void)
4512 {
4513 msecnode_t *node;
4514
4515 if (headsecnode)
4516 {
4517 node = headsecnode;
4518 headsecnode = headsecnode->m_thinglist_next;
4519 }
4520 else
4521 node = Z_Calloc(sizeof (*node), PU_LEVEL, NULL);
4522 return node;
4523 }
4524
P_GetPrecipSecnode(void)4525 static mprecipsecnode_t *P_GetPrecipSecnode(void)
4526 {
4527 mprecipsecnode_t *node;
4528
4529 if (headprecipsecnode)
4530 {
4531 node = headprecipsecnode;
4532 headprecipsecnode = headprecipsecnode->m_thinglist_next;
4533 }
4534 else
4535 node = Z_Calloc(sizeof (*node), PU_LEVEL, NULL);
4536 return node;
4537 }
4538
4539 // P_PutSecnode() returns a node to the freelist.
4540
P_PutSecnode(msecnode_t * node)4541 static inline void P_PutSecnode(msecnode_t *node)
4542 {
4543 node->m_thinglist_next = headsecnode;
4544 headsecnode = node;
4545 }
4546
4547 // Tails 08-25-2002
P_PutPrecipSecnode(mprecipsecnode_t * node)4548 static inline void P_PutPrecipSecnode(mprecipsecnode_t *node)
4549 {
4550 node->m_thinglist_next = headprecipsecnode;
4551 headprecipsecnode = node;
4552 }
4553
4554 // P_AddSecnode() searches the current list to see if this sector is
4555 // already there. If not, it adds a sector node at the head of the list of
4556 // sectors this object appears in. This is called when creating a list of
4557 // nodes that will get linked in later. Returns a pointer to the new node.
4558
P_AddSecnode(sector_t * s,mobj_t * thing,msecnode_t * nextnode)4559 static msecnode_t *P_AddSecnode(sector_t *s, mobj_t *thing, msecnode_t *nextnode)
4560 {
4561 msecnode_t *node;
4562
4563 node = nextnode;
4564 while (node)
4565 {
4566 if (node->m_sector == s) // Already have a node for this sector?
4567 {
4568 node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
4569 return nextnode;
4570 }
4571 node = node->m_sectorlist_next;
4572 }
4573
4574 // Couldn't find an existing node for this sector. Add one at the head
4575 // of the list.
4576
4577 node = P_GetSecnode();
4578
4579 // mark new nodes unvisited.
4580 node->visited = 0;
4581
4582 node->m_sector = s; // sector
4583 node->m_thing = thing; // mobj
4584 node->m_sectorlist_prev = NULL; // prev node on Thing thread
4585 node->m_sectorlist_next = nextnode; // next node on Thing thread
4586 if (nextnode)
4587 nextnode->m_sectorlist_prev = node; // set back link on Thing
4588
4589 // Add new node at head of sector thread starting at s->touching_thinglist
4590
4591 node->m_thinglist_prev = NULL; // prev node on sector thread
4592 node->m_thinglist_next = s->touching_thinglist; // next node on sector thread
4593 if (s->touching_thinglist)
4594 node->m_thinglist_next->m_thinglist_prev = node;
4595 s->touching_thinglist = node;
4596 return node;
4597 }
4598
4599 // More crazy crap Tails 08-25-2002
P_AddPrecipSecnode(sector_t * s,precipmobj_t * thing,mprecipsecnode_t * nextnode)4600 static mprecipsecnode_t *P_AddPrecipSecnode(sector_t *s, precipmobj_t *thing, mprecipsecnode_t *nextnode)
4601 {
4602 mprecipsecnode_t *node;
4603
4604 node = nextnode;
4605 while (node)
4606 {
4607 if (node->m_sector == s) // Already have a node for this sector?
4608 {
4609 node->m_thing = thing; // Yes. Setting m_thing says 'keep it'.
4610 return nextnode;
4611 }
4612 node = node->m_sectorlist_next;
4613 }
4614
4615 // Couldn't find an existing node for this sector. Add one at the head
4616 // of the list.
4617
4618 node = P_GetPrecipSecnode();
4619
4620 // mark new nodes unvisited.
4621 node->visited = 0;
4622
4623 node->m_sector = s; // sector
4624 node->m_thing = thing; // mobj
4625 node->m_sectorlist_prev = NULL; // prev node on Thing thread
4626 node->m_sectorlist_next = nextnode; // next node on Thing thread
4627 if (nextnode)
4628 nextnode->m_sectorlist_prev = node; // set back link on Thing
4629
4630 // Add new node at head of sector thread starting at s->touching_thinglist
4631
4632 node->m_thinglist_prev = NULL; // prev node on sector thread
4633 node->m_thinglist_next = s->touching_preciplist; // next node on sector thread
4634 if (s->touching_preciplist)
4635 node->m_thinglist_next->m_thinglist_prev = node;
4636 s->touching_preciplist = node;
4637 return node;
4638 }
4639
4640 // P_DelSecnode() deletes a sector node from the list of
4641 // sectors this object appears in. Returns a pointer to the next node
4642 // on the linked list, or NULL.
4643
P_DelSecnode(msecnode_t * node)4644 static msecnode_t *P_DelSecnode(msecnode_t *node)
4645 {
4646 msecnode_t *tp; // prev node on thing thread
4647 msecnode_t *tn; // next node on thing thread
4648 msecnode_t *sp; // prev node on sector thread
4649 msecnode_t *sn; // next node on sector thread
4650
4651 if (!node)
4652 return NULL;
4653
4654 // Unlink from the Thing thread. The Thing thread begins at
4655 // sector_list and not from mobj_t->touching_sectorlist.
4656
4657 tp = node->m_sectorlist_prev;
4658 tn = node->m_sectorlist_next;
4659 if (tp)
4660 tp->m_sectorlist_next = tn;
4661 if (tn)
4662 tn->m_sectorlist_prev = tp;
4663
4664 // Unlink from the sector thread. This thread begins at
4665 // sector_t->touching_thinglist.
4666
4667 sp = node->m_thinglist_prev;
4668 sn = node->m_thinglist_next;
4669 if (sp)
4670 sp->m_thinglist_next = sn;
4671 else
4672 node->m_sector->touching_thinglist = sn;
4673 if (sn)
4674 sn->m_thinglist_prev = sp;
4675
4676 // Return this node to the freelist
4677
4678 P_PutSecnode(node);
4679 return tn;
4680 }
4681
4682 // Tails 08-25-2002
P_DelPrecipSecnode(mprecipsecnode_t * node)4683 static mprecipsecnode_t *P_DelPrecipSecnode(mprecipsecnode_t *node)
4684 {
4685 mprecipsecnode_t *tp; // prev node on thing thread
4686 mprecipsecnode_t *tn; // next node on thing thread
4687 mprecipsecnode_t *sp; // prev node on sector thread
4688 mprecipsecnode_t *sn; // next node on sector thread
4689
4690 if (!node)
4691 return NULL;
4692
4693 // Unlink from the Thing thread. The Thing thread begins at
4694 // sector_list and not from mobj_t->touching_sectorlist.
4695
4696 tp = node->m_sectorlist_prev;
4697 tn = node->m_sectorlist_next;
4698 if (tp)
4699 tp->m_sectorlist_next = tn;
4700 if (tn)
4701 tn->m_sectorlist_prev = tp;
4702
4703 // Unlink from the sector thread. This thread begins at
4704 // sector_t->touching_thinglist.
4705
4706 sp = node->m_thinglist_prev;
4707 sn = node->m_thinglist_next;
4708 if (sp)
4709 sp->m_thinglist_next = sn;
4710 else
4711 node->m_sector->touching_preciplist = sn;
4712 if (sn)
4713 sn->m_thinglist_prev = sp;
4714
4715 // Return this node to the freelist
4716
4717 P_PutPrecipSecnode(node);
4718 return tn;
4719 }
4720
4721 // Delete an entire sector list
P_DelSeclist(msecnode_t * node)4722 void P_DelSeclist(msecnode_t *node)
4723 {
4724 while (node)
4725 node = P_DelSecnode(node);
4726 }
4727
4728 // Tails 08-25-2002
P_DelPrecipSeclist(mprecipsecnode_t * node)4729 void P_DelPrecipSeclist(mprecipsecnode_t *node)
4730 {
4731 while (node)
4732 node = P_DelPrecipSecnode(node);
4733 }
4734
4735 // PIT_GetSectors
4736 // Locates all the sectors the object is in by looking at the lines that
4737 // cross through it. You have already decided that the object is allowed
4738 // at this location, so don't bother with checking impassable or
4739 // blocking lines.
4740
PIT_GetSectors(line_t * ld)4741 static inline boolean PIT_GetSectors(line_t *ld)
4742 {
4743 if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
4744 tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
4745 tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
4746 tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
4747 return true;
4748
4749 if (P_BoxOnLineSide(tmbbox, ld) != -1)
4750 return true;
4751
4752 if (ld->polyobj) // line belongs to a polyobject, don't add it
4753 return true;
4754
4755 // This line crosses through the object.
4756
4757 // Collect the sector(s) from the line and add to the
4758 // sector_list you're examining. If the Thing ends up being
4759 // allowed to move to this position, then the sector_list
4760 // will be attached to the Thing's mobj_t at touching_sectorlist.
4761
4762 sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list);
4763
4764 // Don't assume all lines are 2-sided, since some Things
4765 // like MT_TFOG are allowed regardless of whether their radius takes
4766 // them beyond an impassable linedef.
4767
4768 // Use sidedefs instead of 2s flag to determine two-sidedness.
4769 if (ld->backsector)
4770 sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list);
4771
4772 return true;
4773 }
4774
4775 // Tails 08-25-2002
PIT_GetPrecipSectors(line_t * ld)4776 static inline boolean PIT_GetPrecipSectors(line_t *ld)
4777 {
4778 if (preciptmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
4779 preciptmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
4780 preciptmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
4781 preciptmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
4782 return true;
4783
4784 if (P_BoxOnLineSide(preciptmbbox, ld) != -1)
4785 return true;
4786
4787 if (ld->polyobj) // line belongs to a polyobject, don't add it
4788 return true;
4789
4790 // This line crosses through the object.
4791
4792 // Collect the sector(s) from the line and add to the
4793 // sector_list you're examining. If the Thing ends up being
4794 // allowed to move to this position, then the sector_list
4795 // will be attached to the Thing's mobj_t at touching_sectorlist.
4796
4797 precipsector_list = P_AddPrecipSecnode(ld->frontsector, tmprecipthing, precipsector_list);
4798
4799 // Don't assume all lines are 2-sided, since some Things
4800 // like MT_TFOG are allowed regardless of whether their radius takes
4801 // them beyond an impassable linedef.
4802
4803 // Use sidedefs instead of 2s flag to determine two-sidedness.
4804 if (ld->backsector)
4805 precipsector_list = P_AddPrecipSecnode(ld->backsector, tmprecipthing, precipsector_list);
4806
4807 return true;
4808 }
4809
4810 // P_CreateSecNodeList alters/creates the sector_list that shows what sectors
4811 // the object resides in.
4812
P_CreateSecNodeList(mobj_t * thing,fixed_t x,fixed_t y)4813 void P_CreateSecNodeList(mobj_t *thing, fixed_t x, fixed_t y)
4814 {
4815 INT32 xl, xh, yl, yh, bx, by;
4816 msecnode_t *node = sector_list;
4817 mobj_t *saved_tmthing = tmthing; /* cph - see comment at func end */
4818 fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */
4819
4820 // First, clear out the existing m_thing fields. As each node is
4821 // added or verified as needed, m_thing will be set properly. When
4822 // finished, delete all nodes where m_thing is still NULL. These
4823 // represent the sectors the Thing has vacated.
4824
4825 while (node)
4826 {
4827 node->m_thing = NULL;
4828 node = node->m_sectorlist_next;
4829 }
4830
4831 P_SetTarget(&tmthing, thing);
4832 tmflags = thing->flags;
4833
4834 tmx = x;
4835 tmy = y;
4836
4837 tmbbox[BOXTOP] = y + tmthing->radius;
4838 tmbbox[BOXBOTTOM] = y - tmthing->radius;
4839 tmbbox[BOXRIGHT] = x + tmthing->radius;
4840 tmbbox[BOXLEFT] = x - tmthing->radius;
4841
4842 validcount++; // used to make sure we only process a line once
4843
4844 xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
4845 xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
4846 yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
4847 yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
4848
4849 BMBOUNDFIX(xl, xh, yl, yh);
4850
4851 for (bx = xl; bx <= xh; bx++)
4852 for (by = yl; by <= yh; by++)
4853 P_BlockLinesIterator(bx, by, PIT_GetSectors);
4854
4855 // Add the sector of the (x, y) point to sector_list.
4856 sector_list = P_AddSecnode(thing->subsector->sector, thing, sector_list);
4857
4858 // Now delete any nodes that won't be used. These are the ones where
4859 // m_thing is still NULL.
4860 node = sector_list;
4861 while (node)
4862 {
4863 if (!node->m_thing)
4864 {
4865 if (node == sector_list)
4866 sector_list = node->m_sectorlist_next;
4867 node = P_DelSecnode(node);
4868 }
4869 else
4870 node = node->m_sectorlist_next;
4871 }
4872
4873 /* cph -
4874 * This is the strife we get into for using global variables. tmthing
4875 * is being used by several different functions calling
4876 * P_BlockThingIterator, including functions that can be called *from*
4877 * P_BlockThingIterator. Using a global tmthing is not reentrant.
4878 * OTOH for Boom/MBF demos we have to preserve the buggy behavior.
4879 * Fun. We restore its previous value unless we're in a Boom/MBF demo.
4880 */
4881 P_SetTarget(&tmthing, saved_tmthing);
4882
4883 /* And, duh, the same for tmx/y - cph 2002/09/22
4884 * And for tmbbox - cph 2003/08/10 */
4885 tmx = saved_tmx, tmy = saved_tmy;
4886
4887 if (tmthing)
4888 {
4889 tmbbox[BOXTOP] = tmy + tmthing->radius;
4890 tmbbox[BOXBOTTOM] = tmy - tmthing->radius;
4891 tmbbox[BOXRIGHT] = tmx + tmthing->radius;
4892 tmbbox[BOXLEFT] = tmx - tmthing->radius;
4893 }
4894 }
4895
4896 // More crazy crap Tails 08-25-2002
P_CreatePrecipSecNodeList(precipmobj_t * thing,fixed_t x,fixed_t y)4897 void P_CreatePrecipSecNodeList(precipmobj_t *thing,fixed_t x,fixed_t y)
4898 {
4899 INT32 xl, xh, yl, yh, bx, by;
4900 mprecipsecnode_t *node = precipsector_list;
4901 precipmobj_t *saved_tmthing = tmprecipthing; /* cph - see comment at func end */
4902
4903 // First, clear out the existing m_thing fields. As each node is
4904 // added or verified as needed, m_thing will be set properly. When
4905 // finished, delete all nodes where m_thing is still NULL. These
4906 // represent the sectors the Thing has vacated.
4907
4908 while (node)
4909 {
4910 node->m_thing = NULL;
4911 node = node->m_sectorlist_next;
4912 }
4913
4914 tmprecipthing = thing;
4915
4916 preciptmbbox[BOXTOP] = y + 2*FRACUNIT;
4917 preciptmbbox[BOXBOTTOM] = y - 2*FRACUNIT;
4918 preciptmbbox[BOXRIGHT] = x + 2*FRACUNIT;
4919 preciptmbbox[BOXLEFT] = x - 2*FRACUNIT;
4920
4921 validcount++; // used to make sure we only process a line once
4922
4923 xl = (unsigned)(preciptmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
4924 xh = (unsigned)(preciptmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
4925 yl = (unsigned)(preciptmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
4926 yh = (unsigned)(preciptmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
4927
4928 BMBOUNDFIX(xl, xh, yl, yh);
4929
4930 for (bx = xl; bx <= xh; bx++)
4931 for (by = yl; by <= yh; by++)
4932 P_BlockLinesIterator(bx, by, PIT_GetPrecipSectors);
4933
4934 // Add the sector of the (x, y) point to sector_list.
4935 precipsector_list = P_AddPrecipSecnode(thing->subsector->sector, thing, precipsector_list);
4936
4937 // Now delete any nodes that won't be used. These are the ones where
4938 // m_thing is still NULL.
4939 node = precipsector_list;
4940 while (node)
4941 {
4942 if (!node->m_thing)
4943 {
4944 if (node == precipsector_list)
4945 precipsector_list = node->m_sectorlist_next;
4946 node = P_DelPrecipSecnode(node);
4947 }
4948 else
4949 node = node->m_sectorlist_next;
4950 }
4951
4952 /* cph -
4953 * This is the strife we get into for using global variables. tmthing
4954 * is being used by several different functions calling
4955 * P_BlockThingIterator, including functions that can be called *from*
4956 * P_BlockThingIterator. Using a global tmthing is not reentrant.
4957 * OTOH for Boom/MBF demos we have to preserve the buggy behavior.
4958 * Fun. We restore its previous value unless we're in a Boom/MBF demo.
4959 */
4960 tmprecipthing = saved_tmthing;
4961 }
4962
4963 /* cphipps 2004/08/30 -
4964 * Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */
P_MapStart(void)4965 void P_MapStart(void)
4966 {
4967 if (tmthing)
4968 I_Error("P_MapStart: tmthing set!");
4969 }
4970
P_MapEnd(void)4971 void P_MapEnd(void)
4972 {
4973 P_SetTarget(&tmthing, NULL);
4974 }
4975
4976 // P_FloorzAtPos
4977 // Returns the floorz of the XYZ position
4978 // Tails 05-26-2003
P_FloorzAtPos(fixed_t x,fixed_t y,fixed_t z,fixed_t height)4979 fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
4980 {
4981 sector_t *sec = R_PointInSubsector(x, y)->sector;
4982 fixed_t floorz = P_GetSectorFloorZAt(sec, x, y);
4983
4984 // Intercept the stupid 'fall through 3dfloors' bug Tails 03-17-2002
4985 if (sec->ffloors)
4986 {
4987 ffloor_t *rover;
4988 fixed_t delta1, delta2, thingtop = z + height;
4989
4990 for (rover = sec->ffloors; rover; rover = rover->next)
4991 {
4992 fixed_t topheight, bottomheight;
4993 if (!(rover->flags & FF_EXISTS))
4994 continue;
4995
4996 if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE)))
4997 continue;
4998
4999 topheight = P_GetFFloorTopZAt (rover, x, y);
5000 bottomheight = P_GetFFloorBottomZAt(rover, x, y);
5001
5002 if (rover->flags & FF_QUICKSAND)
5003 {
5004 if (z < topheight && bottomheight < thingtop)
5005 {
5006 if (floorz < z)
5007 floorz = z;
5008 }
5009 continue;
5010 }
5011
5012 delta1 = z - (bottomheight + ((topheight - bottomheight)/2));
5013 delta2 = thingtop - (bottomheight + ((topheight - bottomheight)/2));
5014 if (topheight > floorz && abs(delta1) < abs(delta2))
5015 floorz = topheight;
5016 }
5017 }
5018
5019 return floorz;
5020 }
5021
5022 // P_CeilingZAtPos
5023 // Returns the ceilingz of the XYZ position
P_CeilingzAtPos(fixed_t x,fixed_t y,fixed_t z,fixed_t height)5024 fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
5025 {
5026 sector_t *sec = R_PointInSubsector(x, y)->sector;
5027 fixed_t ceilingz = P_GetSectorCeilingZAt(sec, x, y);
5028
5029 if (sec->ffloors)
5030 {
5031 ffloor_t *rover;
5032 fixed_t delta1, delta2, thingtop = z + height;
5033
5034 for (rover = sec->ffloors; rover; rover = rover->next)
5035 {
5036 fixed_t topheight, bottomheight;
5037 if (!(rover->flags & FF_EXISTS))
5038 continue;
5039
5040 if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE)))
5041 continue;
5042
5043 topheight = P_GetFFloorTopZAt (rover, x, y);
5044 bottomheight = P_GetFFloorBottomZAt(rover, x, y);
5045
5046 if (rover->flags & FF_QUICKSAND)
5047 {
5048 if (thingtop > bottomheight && topheight > z)
5049 {
5050 if (ceilingz > z)
5051 ceilingz = z;
5052 }
5053 continue;
5054 }
5055
5056 delta1 = z - (bottomheight + ((topheight - bottomheight)/2));
5057 delta2 = thingtop - (bottomheight + ((topheight - bottomheight)/2));
5058 if (bottomheight < ceilingz && abs(delta1) > abs(delta2))
5059 ceilingz = bottomheight;
5060 }
5061 }
5062
5063 return ceilingz;
5064 }
5065