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 = &sectors[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 = &sectors[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