p_slopes.c 25.4 KB
Newer Older
1
// SONIC ROBO BLAST 2
2
//-----------------------------------------------------------------------------
3
// Copyright (C) 2004      by Stephen McGranahan
James R. committed
4
// Copyright (C) 2015-2020 by Sonic Team Junior.
5
//
6 7 8
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
9
//-----------------------------------------------------------------------------
10 11
/// \file  p_slopes.c
/// \brief ZDoom + Eternity Engine Slopes, ported and enhanced by Kalaron
12 13 14 15 16 17

#include "doomdef.h"
#include "r_defs.h"
#include "r_state.h"
#include "m_bbox.h"
#include "z_zone.h"
18
#include "p_local.h"
19 20
#include "p_spec.h"
#include "p_slopes.h"
RedEnchilada committed
21
#include "p_setup.h"
22 23 24 25
#include "r_main.h"
#include "p_maputl.h"
#include "w_wad.h"

Nev3r committed
26 27
pslope_t *slopelist = NULL;
UINT16 slopecount = 0;
28 29

// Calculate line normal
wolfs committed
30
void P_CalculateSlopeNormal(pslope_t *slope) {
31
	slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT);
Nev3r committed
32 33
	slope->normal.x = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x);
	slope->normal.y = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y);
34 35
}

Nev3r committed
36 37
/// Setup slope via 3 vertexes.
static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const vector3_t v2, const vector3_t v3)
RedEnchilada committed
38 39 40
{
	vector3_t vec1, vec2;

Nev3r committed
41 42 43 44 45 46 47 48 49 50
	// Set origin.
	FV3_Copy(&slope->o, &v1);

	// Get slope's normal.
	FV3_SubEx(&v2, &v1, &vec1);
	FV3_SubEx(&v3, &v1, &vec2);

	// Set some defaults for a non-sloped "slope"
	if (vec1.z == 0 && vec2.z == 0)
	{
RedEnchilada committed
51 52
		slope->zangle = slope->xydirection = 0;
		slope->zdelta = slope->d.x = slope->d.y = 0;
53 54
		slope->normal.x = slope->normal.y = 0;
		slope->normal.z = FRACUNIT;
Nev3r committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	}
	else
	{
		/// \note Using fixed point for vectorial products easily leads to overflows so we work around by downscaling them.
		fixed_t m = max(
			max(max(abs(vec1.x), abs(vec1.y)), abs(vec1.z)),
			max(max(abs(vec2.x), abs(vec2.y)), abs(vec2.z))
		) >> 5; // shifting right by 5 is good enough.

		FV3_Cross(
				FV3_Divide(&vec1, m),
				FV3_Divide(&vec2, m),
				&slope->normal
				);

		// NOTE: FV3_Magnitude() doesn't work properly in some cases, and chaining FixedHypot() seems to give worse results.
		m = R_PointToDist2(0, 0, R_PointToDist2(0, 0, slope->normal.x, slope->normal.y), slope->normal.z);

		// Invert normal if it's facing down.
		if (slope->normal.z < 0)
			m = -m;

		FV3_Divide(&slope->normal, m);

RedEnchilada committed
79
		// Get direction vector
Nev3r committed
80 81 82
		m = FixedHypot(slope->normal.x, slope->normal.y);
		slope->d.x = -FixedDiv(slope->normal.x, m);
		slope->d.y = -FixedDiv(slope->normal.y, m);
RedEnchilada committed
83 84

		// Z delta
Nev3r committed
85
		slope->zdelta = FixedDiv(m, slope->normal.z);
RedEnchilada committed
86 87 88

		// Get angles
		slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180;
Alam Ed Arias committed
89
		slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta));
RedEnchilada committed
90 91 92
	}
}

Nev3r committed
93 94 95 96 97
/// Recalculate dynamic slopes.
void T_DynamicSlopeLine (dynplanethink_t* th)
{
	pslope_t* slope = th->slope;
	line_t* srcline = th->sourceline;
98

Nev3r committed
99
	fixed_t zdelta;
100

Nev3r committed
101 102 103 104 105
	switch(th->type) {
	case DP_FRONTFLOOR:
		zdelta = srcline->backsector->floorheight - srcline->frontsector->floorheight;
		slope->o.z = srcline->frontsector->floorheight;
		break;
106

Nev3r committed
107 108 109 110
	case DP_FRONTCEIL:
		zdelta = srcline->backsector->ceilingheight - srcline->frontsector->ceilingheight;
		slope->o.z = srcline->frontsector->ceilingheight;
		break;
111

Nev3r committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
	case DP_BACKFLOOR:
		zdelta = srcline->frontsector->floorheight - srcline->backsector->floorheight;
		slope->o.z = srcline->backsector->floorheight;
		break;

	case DP_BACKCEIL:
		zdelta = srcline->frontsector->ceilingheight - srcline->backsector->ceilingheight;
		slope->o.z = srcline->backsector->ceilingheight;
		break;

	default:
		return;
	}

	if (slope->zdelta != FixedDiv(zdelta, th->extent)) {
		slope->zdelta = FixedDiv(zdelta, th->extent);
		slope->zangle = R_PointToAngle2(0, 0, th->extent, -zdelta);
		P_CalculateSlopeNormal(slope);
	}
}

/// Mapthing-defined
void T_DynamicSlopeVert (dynplanethink_t* th)
{
	pslope_t* slope = th->slope;

	size_t i;
	INT32 l;
140

Nev3r committed
141
	for (i = 0; i < 3; i++) {
142
		l = Tag_FindLineSpecial(799, th->tags[i]);
Nev3r committed
143 144
		if (l != -1) {
			th->vex[i].z = lines[l].frontsector->floorheight;
145
		}
Nev3r committed
146 147
		else
			th->vex[i].z = 0;
148
	}
Nev3r committed
149 150

	ReconfigureViaVertexes(slope, th->vex[0], th->vex[1], th->vex[2]);
151 152
}

Nev3r committed
153
static inline void P_AddDynSlopeThinker (pslope_t* slope, dynplanetype_t type, line_t* sourceline, fixed_t extent, const INT16 tags[3], const vector3_t vx[3])
154
{
Nev3r committed
155 156 157 158 159 160 161 162 163 164 165 166 167
	dynplanethink_t* th = Z_Calloc(sizeof (*th), PU_LEVSPEC, NULL);
	switch (type)
	{
	case DP_VERTEX:
		th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeVert;
		memcpy(th->tags, tags, sizeof(th->tags));
		memcpy(th->vex, vx, sizeof(th->vex));
		break;
	default:
		th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeLine;
		th->sourceline = sourceline;
		th->extent = extent;
	}
168

Nev3r committed
169 170
	th->slope = slope;
	th->type = type;
171

172
	P_AddThinker(THINK_DYNSLOPE, &th->thinker);
Nev3r committed
173
}
174 175


Nev3r committed
176 177 178 179
/// Create a new slope and add it to the slope list.
static inline pslope_t* Slope_Add (const UINT8 flags)
{
	pslope_t *ret = Z_Calloc(sizeof(pslope_t), PU_LEVEL, NULL);
180 181 182 183 184 185 186
	ret->flags = flags;

	ret->next = slopelist;
	slopelist = ret;

	slopecount++;
	ret->id = slopecount;
187 188

	return ret;
189 190
}

Nev3r committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
/// Alocates and fill the contents of a slope structure.
static pslope_t *MakeViaVectors(const vector3_t *o, const vector2_t *d,
                             const fixed_t zdelta, UINT8 flags)
{
	pslope_t *ret = Slope_Add(flags);

	FV3_Copy(&ret->o, o);
	FV2_Copy(&ret->d, d);

	ret->zdelta = zdelta;

	ret->flags = flags;

	return ret;
}

/// Get furthest perpendicular distance from all vertexes in a sector for a given line.
static fixed_t GetExtent(sector_t *sector, line_t *line)
209 210
{
	// ZDoom code reference: v3float_t = vertex_t
211 212
	fixed_t fardist = -FRACUNIT;
	size_t i;
213 214 215

	// Find furthest vertex from the reference line. It, along with the two ends
	// of the line, will define the plane.
216 217 218 219 220 221 222 223 224 225 226 227 228 229
	for(i = 0; i < sector->linecount; i++)
	{
		line_t *li = sector->lines[i];
		vertex_t tempv;
		fixed_t dist;

		// Don't compare to the slope line.
		if(li == line)
			continue;

		P_ClosestPointOnLine(li->v1->x, li->v1->y, line, &tempv);
		dist = R_PointToDist2(tempv.x, tempv.y, li->v1->x, li->v1->y);
		if(dist > fardist)
			fardist = dist;
230

231
		// Okay, maybe do it for v2 as well?
232 233 234 235 236
		P_ClosestPointOnLine(li->v2->x, li->v2->y, line, &tempv);
		dist = R_PointToDist2(tempv.x, tempv.y, li->v2->x, li->v2->y);
		if(dist > fardist)
			fardist = dist;
	}
RedEnchilada committed
237

238 239 240
	return fardist;
}

Nev3r committed
241 242
/// Creates one or more slopes based on the given line type and front/back sectors.
static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
243 244 245 246 247 248
{
	// With dynamic slopes, it's fine to just leave this function as normal,
	// because checking to see if a slope had changed will waste more memory than
	// if the slope was just updated when called
	line_t *line = lines + linenum;
	pslope_t *fslope = NULL, *cslope = NULL;
249 250
	vector3_t origin, point;
	vector2_t direction;
251
	fixed_t nx, ny, dz, extent;
252

253 254 255 256
	boolean frontfloor = line->args[0] == TMS_FRONT;
	boolean backfloor = line->args[0] == TMS_BACK;
	boolean frontceil = line->args[1] == TMS_FRONT;
	boolean backceil = line->args[1] == TMS_BACK;
257
	UINT8 flags = 0; // Slope flags
258
	if (line->args[2] & TMSL_NOPHYSICS)
259
		flags |= SL_NOPHYSICS;
260
	if (line->args[2] & TMSL_DYNAMIC)
261
		flags |= SL_DYNAMIC;
262

263 264
	if(!frontfloor && !backfloor && !frontceil && !backceil)
	{
265
		CONS_Printf("line_SpawnViaLine: Slope special with nothing to do.\n");
266 267
		return;
	}
268

269 270
	if(!line->frontsector || !line->backsector)
	{
271
		CONS_Debug(DBG_SETUP, "line_SpawnViaLine: Slope special used on a line without two sides. (line number %i)\n", linenum);
272 273
		return;
	}
274

275 276 277 278 279 280
	{
		fixed_t len = R_PointToDist2(0, 0, line->dx, line->dy);
		nx = FixedDiv(line->dy, len);
		ny = -FixedDiv(line->dx, len);
	}

Nev3r committed
281
	// Set origin to line's center.
282 283
	origin.x = line->v1->x + (line->v2->x - line->v1->x)/2;
	origin.y = line->v1->y + (line->v2->y - line->v1->y)/2;
284

285 286 287
	// For FOF slopes, make a special function to copy to the xy origin & direction relative to the position of the FOF on the map!
	if(frontfloor || frontceil)
	{
288 289
		line->frontsector->hasslope = true; // Tell the software renderer that we're sloped

290 291 292
		origin.z = line->backsector->floorheight;
		direction.x = nx;
		direction.y = ny;
293

Nev3r committed
294
		extent = GetExtent(line->frontsector, line);
295

296
		if(extent < 0)
297
		{
298
			CONS_Printf("line_SpawnViaLine failed to get frontsector extent on line number %i\n", linenum);
299 300
			return;
		}
301

302
		// reposition the origin according to the extent
303 304
		point.x = origin.x + FixedMul(direction.x, extent);
		point.y = origin.y + FixedMul(direction.y, extent);
305 306
		direction.x = -direction.x;
		direction.y = -direction.y;
307

308
		// TODO: We take origin and point 's xy values and translate them to the center of an FOF!
309

310 311
		if(frontfloor)
		{
312
			point.z = line->frontsector->floorheight; // Startz
313
			dz = FixedDiv(origin.z - point.z, extent); // Destinationz
314

315
			// In P_SpawnSlopeLine the origin is the centerpoint of the sourcelinedef
316 317

			fslope = line->frontsector->f_slope =
Nev3r committed
318
            MakeViaVectors(&point, &direction, dz, flags);
319

320 321 322 323
			// Now remember that f_slope IS a vector
			// fslope->o = origin      3D point 1 of the vector
			// fslope->d = destination 3D point 2 of the vector
			// fslope->normal is a 3D line perpendicular to the 3D vector
324

325
			fslope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
326
			fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
327 328

			P_CalculateSlopeNormal(fslope);
Nev3r committed
329

Nev3r committed
330
			if (spawnthinker && (flags & SL_DYNAMIC))
Nev3r committed
331
				P_AddDynSlopeThinker(fslope, DP_FRONTFLOOR, line, extent, NULL, NULL);
332 333 334
		}
		if(frontceil)
		{
335
			origin.z = line->backsector->ceilingheight;
336
			point.z = line->frontsector->ceilingheight;
337
			dz = FixedDiv(origin.z - point.z, extent);
338 339

			cslope = line->frontsector->c_slope =
Nev3r committed
340
            MakeViaVectors(&point, &direction, dz, flags);
341

342
			cslope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
343
			cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
344 345

			P_CalculateSlopeNormal(cslope);
Nev3r committed
346

Nev3r committed
347
			if (spawnthinker && (flags & SL_DYNAMIC))
Nev3r committed
348
				P_AddDynSlopeThinker(cslope, DP_FRONTCEIL, line, extent, NULL, NULL);
349 350 351 352
		}
	}
	if(backfloor || backceil)
	{
353 354
		line->backsector->hasslope = true; // Tell the software renderer that we're sloped

355
		origin.z = line->frontsector->floorheight;
356
		// Backsector
357 358
		direction.x = -nx;
		direction.y = -ny;
359

Nev3r committed
360
		extent = GetExtent(line->backsector, line);
361

362
		if(extent < 0)
363
		{
364
			CONS_Printf("line_SpawnViaLine failed to get backsector extent on line number %i\n", linenum);
365 366
			return;
		}
367

368
		// reposition the origin according to the extent
369 370
		point.x = origin.x + FixedMul(direction.x, extent);
		point.y = origin.y + FixedMul(direction.y, extent);
371 372
		direction.x = -direction.x;
		direction.y = -direction.y;
373

374 375
		if(backfloor)
		{
376
			point.z = line->backsector->floorheight;
377
			dz = FixedDiv(origin.z - point.z, extent);
378 379

			fslope = line->backsector->f_slope =
Nev3r committed
380
            MakeViaVectors(&point, &direction, dz, flags);
381

382 383
			fslope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
			fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
384 385

			P_CalculateSlopeNormal(fslope);
Nev3r committed
386

Nev3r committed
387
			if (spawnthinker && (flags & SL_DYNAMIC))
Nev3r committed
388
				P_AddDynSlopeThinker(fslope, DP_BACKFLOOR, line, extent, NULL, NULL);
389 390 391
		}
		if(backceil)
		{
392
			origin.z = line->frontsector->ceilingheight;
393
			point.z = line->backsector->ceilingheight;
394
			dz = FixedDiv(origin.z - point.z, extent);
395 396

			cslope = line->backsector->c_slope =
Nev3r committed
397
            MakeViaVectors(&point, &direction, dz, flags);
398

399
			cslope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
400
			cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
401 402

			P_CalculateSlopeNormal(cslope);
Nev3r committed
403

Nev3r committed
404
			if (spawnthinker && (flags & SL_DYNAMIC))
Nev3r committed
405
				P_AddDynSlopeThinker(cslope, DP_BACKCEIL, line, extent, NULL, NULL);
406 407 408 409
		}
	}
}

Nev3r committed
410 411
/// Creates a new slope from three mapthings with the specified IDs
static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flags, const boolean spawnthinker)
RedEnchilada committed
412 413
{
	size_t i;
Nev3r committed
414 415 416
	mapthing_t* mt = mapthings;
	mapthing_t* vertices[3] = {0};
	INT16 tags[3] = {tag1, tag2, tag3};
RedEnchilada committed
417

Nev3r committed
418 419
	vector3_t vx[3];
	pslope_t* ret = Slope_Add(flags);
RedEnchilada committed
420 421 422 423 424 425

	// And... look for the vertices in question.
	for (i = 0; i < nummapthings; i++, mt++) {
		if (mt->type != 750) // Haha, I'm hijacking the old Chaos Spawn thingtype for something!
			continue;

426
		if (!vertices[0] && Tag_Find(&mt->tags, tag1))
Nev3r committed
427
			vertices[0] = mt;
428
		else if (!vertices[1] && Tag_Find(&mt->tags, tag2))
Nev3r committed
429
			vertices[1] = mt;
430
		else if (!vertices[2] && Tag_Find(&mt->tags, tag3))
Nev3r committed
431
			vertices[2] = mt;
RedEnchilada committed
432 433 434 435
	}

	// Now set heights for each vertex, because they haven't been set yet
	for (i = 0; i < 3; i++) {
Nev3r committed
436
		mt = vertices[i];
437
		if (!mt) // If a vertex wasn't found, it's game over. There's nothing you can do to recover (except maybe try and kill the slope instead - TODO?)
Nev3r committed
438 439 440
			I_Error("MakeViaMapthings: Slope vertex %s (for linedef tag %d) not found!", sizeu1(i), tag1);
		vx[i].x = mt->x << FRACBITS;
		vx[i].y = mt->y << FRACBITS;
441 442 443
		vx[i].z = mt->z << FRACBITS;
		if (!mt->extrainfo)
			vx[i].z += R_PointInSubsector(vx[i].x, vx[i].y)->sector->floorheight;
RedEnchilada committed
444 445
	}

Nev3r committed
446
	ReconfigureViaVertexes(ret, vx[0], vx[1], vx[2]);
RedEnchilada committed
447

Nev3r committed
448
	if (spawnthinker && (flags & SL_DYNAMIC))
Nev3r committed
449
		P_AddDynSlopeThinker(ret, DP_VERTEX, NULL, 0, tags, vx);
RedEnchilada committed
450 451 452 453

	return ret;
}

454 455
/// Create vertex based slopes using tagged mapthings.
static void line_SpawnViaMapthingVertexes(const int linenum, const boolean spawnthinker)
Nev3r committed
456 457 458 459
{
	line_t *line = lines + linenum;
	side_t *side;
	pslope_t **slopetoset;
460 461 462
	UINT16 tag1 = line->args[1];
	UINT16 tag2 = line->args[2];
	UINT16 tag3 = line->args[3];
463
	UINT8 flags = 0; // Slope flags
464
	if (line->args[4] & TMSL_NOPHYSICS)
465
		flags |= SL_NOPHYSICS;
466
	if (line->args[4] & TMSL_DYNAMIC)
467
		flags |= SL_DYNAMIC;
Nev3r committed
468

469
	switch(line->args[0])
Nev3r committed
470
	{
471
	case TMSP_FRONTFLOOR:
Nev3r committed
472 473 474
		slopetoset = &line->frontsector->f_slope;
		side = &sides[line->sidenum[0]];
		break;
475
	case TMSP_FRONTCEILING:
Nev3r committed
476 477 478
		slopetoset = &line->frontsector->c_slope;
		side = &sides[line->sidenum[0]];
		break;
479
	case TMSP_BACKFLOOR:
Nev3r committed
480 481 482
		slopetoset = &line->backsector->f_slope;
		side = &sides[line->sidenum[1]];
		break;
483
	case TMSP_BACKCEILING:
Nev3r committed
484 485 486 487 488 489 490 491 492 493
		slopetoset = &line->backsector->c_slope;
		side = &sides[line->sidenum[1]];
	default:
		return;
	}

	*slopetoset = MakeViaMapthings(tag1, tag2, tag3, flags, spawnthinker);

	side->sector->hasslope = true;
}
494

495
/// Spawn textmap vertex slopes.
MascaraSnake committed
496
static void SpawnVertexSlopes(void)
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
{
	line_t *l1, *l2;
	sector_t* sc;
	vertex_t *v1, *v2, *v3;
	size_t i;
	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
	{
		// The vertex slopes only work for 3-vertex sectors (and thus 3-sided sectors).
		if (sc->linecount != 3)
			continue;

		l1 = sc->lines[0];
		l2 = sc->lines[1];

		// Determine the vertexes.
		v1 = l1->v1;
		v2 = l1->v2;
		if ((l2->v1 != v1) && (l2->v1 != v2))
			v3 = l2->v1;
		else
			v3 = l2->v2;

		if (v1->floorzset || v2->floorzset || v3->floorzset)
		{
			vector3_t vtx[3] = {
MascaraSnake committed
522 523 524 525
				{v1->x, v1->y, v1->floorzset ? v1->floorz : sc->floorheight},
				{v2->x, v2->y, v2->floorzset ? v2->floorz : sc->floorheight},
				{v3->x, v3->y, v3->floorzset ? v3->floorz : sc->floorheight}};
			pslope_t *slop = Slope_Add(0);
526 527 528 529 530 531 532 533
			sc->f_slope = slop;
			sc->hasslope = true;
			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
		}

		if (v1->ceilingzset || v2->ceilingzset || v3->ceilingzset)
		{
			vector3_t vtx[3] = {
MascaraSnake committed
534 535 536 537
				{v1->x, v1->y, v1->ceilingzset ? v1->ceilingz : sc->ceilingheight},
				{v2->x, v2->y, v2->ceilingzset ? v2->ceilingz : sc->ceilingheight},
				{v3->x, v3->y, v3->ceilingzset ? v3->ceilingz : sc->ceilingheight}};
			pslope_t *slop = Slope_Add(0);
538 539 540 541 542 543
			sc->c_slope = slop;
			sc->hasslope = true;
			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
		}
	}
}
544

545 546 547 548
static boolean P_SetSlopeFromTag(sector_t *sec, INT32 tag, boolean ceiling)
{
	INT32 i;
	pslope_t **secslope = ceiling ? &sec->c_slope : &sec->f_slope;
549
	TAG_ITER_DECLARECOUNTER(0);
550 551 552

	if (!tag || *secslope)
		return false;
553
	TAG_ITER_SECTORS(0, tag, i)
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
	{
		pslope_t *srcslope = ceiling ? sectors[i].c_slope : sectors[i].f_slope;
		if (srcslope)
		{
			*secslope = srcslope;
			return true;
		}
	}
	return false;
}

static boolean P_CopySlope(pslope_t **toslope, pslope_t *fromslope)
{
	if (*toslope || !fromslope)
		return true;

	*toslope = fromslope;
	return true;
}

static void P_UpdateHasSlope(sector_t *sec)
{
	size_t i;

	sec->hasslope = true;

	// if this is an FOF control sector, make sure any target sectors also are marked as having slopes
	if (sec->numattached)
		for (i = 0; i < sec->numattached; i++)
			sectors[sec->attached[i]].hasslope = true;
}
585 586 587 588 589 590 591 592

//
// P_CopySectorSlope
//
// Searches through tagged sectors and copies
//
void P_CopySectorSlope(line_t *line)
{
593
	sector_t *fsec = line->frontsector;
594 595 596
	sector_t *bsec = line->backsector;
	boolean setfront = false;
	boolean setback = false;
597

598 599 600
	setfront |= P_SetSlopeFromTag(fsec, line->args[0], false);
	setfront |= P_SetSlopeFromTag(fsec, line->args[1], true);
	if (bsec)
601
	{
602 603 604
		setback |= P_SetSlopeFromTag(bsec, line->args[2], false);
		setback |= P_SetSlopeFromTag(bsec, line->args[3], true);

605
		if (line->args[4] & TMSC_FRONTTOBACKFLOOR)
606
			setback |= P_CopySlope(&bsec->f_slope, fsec->f_slope);
607
		if (line->args[4] & TMSC_BACKTOFRONTFLOOR)
608
			setfront |= P_CopySlope(&fsec->f_slope, bsec->f_slope);
609
		if (line->args[4] & TMSC_FRONTTOBACKCEILING)
610
			setback |= P_CopySlope(&bsec->c_slope, fsec->c_slope);
611
		if (line->args[4] & TMSC_BACKTOFRONTCEILING)
612
			setfront |= P_CopySlope(&fsec->c_slope, bsec->c_slope);
613
	}
614

615 616 617 618
	if (setfront)
		P_UpdateHasSlope(fsec);
	if (setback)
		P_UpdateHasSlope(bsec);
619

620
	line->special = 0; // Linedef was use to set slopes, it finished its job, so now make it a normal linedef
621 622
}

623 624 625 626 627 628 629 630 631 632 633 634
//
// P_SlopeById
//
// Looks in the slope list for a slope with a specified ID. Mostly useful for netgame sync
//
pslope_t *P_SlopeById(UINT16 id)
{
	pslope_t *ret;
	for (ret = slopelist; ret && ret->id != id; ret = ret->next);
	return ret;
}

635 636
/// Initializes and reads the slopes from the map data.
void P_SpawnSlopes(const boolean fromsave) {
637
	size_t i;
638

639 640
	slopelist = NULL;
	slopecount = 0;
641

642 643 644
	/// Generates vertex slopes.
	SpawnVertexSlopes();

645
	/// Generates line special-defined slopes.
646 647 648 649
	for (i = 0; i < numlines; i++)
	{
		switch (lines[i].special)
		{
650
			case 700:
651
				line_SpawnViaLine(i, !fromsave);
652 653
				break;

RedEnchilada committed
654
			case 704:
655
				line_SpawnViaMapthingVertexes(i, !fromsave);
RedEnchilada committed
656 657
				break;

658 659 660 661
			default:
				break;
		}
	}
662

663 664 665 666 667 668 669 670 671 672 673
	/// Copies slopes from tagged sectors via line specials.
	/// \note Doesn't actually copy, but instead they share the same pointers.
	for (i = 0; i < numlines; i++)
		switch (lines[i].special)
		{
			case 720:
				P_CopySectorSlope(&lines[i]);
			default:
				break;
		}
}
674 675 676 677 678 679 680

// ============================================================================
//
// Various utilities related to slopes
//

// Returns the height of the sloped plane at (x, y) as a fixed_t
681
fixed_t P_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
682
{
683 684 685 686 687 688
	fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) +
	               FixedMul(y - slope->o.y, slope->d.y);

	return slope->o.z + FixedMul(dist, slope->zdelta);
}

689 690
// Like P_GetSlopeZAt but falls back to z if slope is NULL
fixed_t P_GetZAt(const pslope_t *slope, fixed_t x, fixed_t y, fixed_t z)
691
{
692
	return slope ? P_GetSlopeZAt(slope, x, y) : z;
693 694 695 696 697
}

// Returns the height of the sector floor at (x, y)
fixed_t P_GetSectorFloorZAt(const sector_t *sector, fixed_t x, fixed_t y)
{
698
	return sector->f_slope ? P_GetSlopeZAt(sector->f_slope, x, y) : sector->floorheight;
699
}
700

701 702 703
// Returns the height of the sector ceiling at (x, y)
fixed_t P_GetSectorCeilingZAt(const sector_t *sector, fixed_t x, fixed_t y)
{
704
	return sector->c_slope ? P_GetSlopeZAt(sector->c_slope, x, y) : sector->ceilingheight;
705 706 707 708 709
}

// Returns the height of the FOF top at (x, y)
fixed_t P_GetFFloorTopZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
{
710
	return *ffloor->t_slope ? P_GetSlopeZAt(*ffloor->t_slope, x, y) : *ffloor->topheight;
711 712 713 714 715
}

// Returns the height of the FOF bottom  at (x, y)
fixed_t P_GetFFloorBottomZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
{
716
	return *ffloor->b_slope ? P_GetSlopeZAt(*ffloor->b_slope, x, y) : *ffloor->bottomheight;
717 718 719 720 721
}

// Returns the height of the light list at (x, y)
fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y)
{
722
	return light->slope ? P_GetSlopeZAt(light->slope, x, y) : light->height;
723
}
724

725

726 727 728 729
//
// P_QuantizeMomentumToSlope
//
// When given a vector, rotates it and aligns it to a slope
730
void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope)
731
{
732 733
	vector3_t axis; // Fuck you, C90.

734 735
	if (slope->flags & SL_NOPHYSICS)
		return; // No physics, no quantizing.
toaster committed
736

737 738 739 740
	axis.x = -slope->d.y;
	axis.y = slope->d.x;
	axis.z = 0;

741
	FV3_Rotate(momentum, &axis, slope->zangle >> ANGLETOFINESHIFT);
742 743
}

744 745 746 747 748 749 750 751 752 753 754
//
// P_ReverseQuantizeMomentumToSlope
//
// When given a vector, rotates and aligns it to a flat surface (from being relative to a given slope)
void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope)
{
	slope->zangle = InvAngle(slope->zangle);
	P_QuantizeMomentumToSlope(momentum, slope);
	slope->zangle = InvAngle(slope->zangle);
}

755 756 757 758 759 760
//
// P_SlopeLaunch
//
// Handles slope ejection for objects
void P_SlopeLaunch(mobj_t *mo)
{
761 762 763
	if (!(mo->standingslope->flags & SL_NOPHYSICS) // If there's physics, time for launching.
		&& (mo->standingslope->normal.x != 0
		||  mo->standingslope->normal.y != 0))
764 765 766 767 768 769 770 771 772 773 774 775 776 777
	{
		// Double the pre-rotation Z, then halve the post-rotation Z. This reduces the
		// vertical launch given from slopes while increasing the horizontal launch
		// given. Good for SRB2's gravity and horizontal speeds.
		vector3_t slopemom;
		slopemom.x = mo->momx;
		slopemom.y = mo->momy;
		slopemom.z = mo->momz*2;
		P_QuantizeMomentumToSlope(&slopemom, mo->standingslope);

		mo->momx = slopemom.x;
		mo->momy = slopemom.y;
		mo->momz = slopemom.z/2;
	}
toaster committed
778

779
	//CONS_Printf("Launched off of slope.\n");
780
	mo->standingslope = NULL;
781 782 783

	if (mo->player)
		mo->player->powers[pw_justlaunched] = 1;
784 785
}

toaster committed
786
//
787
// P_GetWallTransferMomZ
toaster committed
788
//
789 790 791 792
// It would be nice to have a single function that does everything necessary for slope-to-wall transfer.
// However, it needs to be seperated out in P_XYMovement to take into account momentum before and after hitting the wall.
// This just performs the necessary calculations for getting the base vertical momentum; the horizontal is already reasonably calculated by P_SlideMove.
fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
toaster committed
793
{
toaster committed
794 795
	vector3_t slopemom, axis;
	angle_t ang;
toaster committed
796

toaster committed
797 798
	if (mo->standingslope->flags & SL_NOPHYSICS)
		return 0;
toaster committed
799

toaster committed
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
	// If there's physics, time for launching.
	// Doesn't kill the vertical momentum as much as P_SlopeLaunch does.
	ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1);
	if (ang > ANGLE_90 && ang < ANGLE_180)
		ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards

	slopemom.x = mo->momx;
	slopemom.y = mo->momy;
	slopemom.z = 3*(mo->momz/2);

	axis.x = -slope->d.y;
	axis.y = slope->d.x;
	axis.z = 0;

	FV3_Rotate(&slopemom, &axis, ang >> ANGLETOFINESHIFT);

	return 2*(slopemom.z/3);
toaster committed
817 818
}

819 820
// Function to help handle landing on slopes
void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
toaster committed
821
{
822
	vector3_t mom; // Ditto.
823
	if (slope->flags & SL_NOPHYSICS || (slope->normal.x == 0 && slope->normal.y == 0)) { // No physics, no need to make anything complicated.
824 825
		if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope
		{
826
			thing->standingslope = slope;
827 828
			if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
				thing->momz = -P_MobjFlip(thing);
829 830 831 832
		}
		return;
	}

833 834 835 836
	mom.x = thing->momx;
	mom.y = thing->momy;
	mom.z = thing->momz*2;

837
	P_ReverseQuantizeMomentumToSlope(&mom, slope);
838 839 840 841

	if (P_MobjFlip(thing)*mom.z < 0) { // falling, land on slope
		thing->momx = mom.x;
		thing->momy = mom.y;
842
		thing->standingslope = slope;
843 844
		if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
			thing->momz = -P_MobjFlip(thing);
845 846 847
	}
}

848 849 850 851 852 853 854 855
// https://yourlogicalfallacyis.com/slippery-slope
// Handles sliding down slopes, like if they were made of butter :)
void P_ButteredSlope(mobj_t *mo)
{
	fixed_t thrust;

	if (!mo->standingslope)
		return;
toaster committed
856

857 858
	if (mo->standingslope->flags & SL_NOPHYSICS)
		return; // No physics, no butter.
859

860 861 862
	if (mo->flags & (MF_NOCLIPHEIGHT|MF_NOGRAVITY))
		return; // don't slide down slopes if you can't touch them or you're not affected by gravity

863 864 865 866 867 868 869
	if (mo->player) {
		if (abs(mo->standingslope->zdelta) < FRACUNIT/4 && !(mo->player->pflags & PF_SPINNING))
			return; // Don't slide on non-steep slopes unless spinning

		if (abs(mo->standingslope->zdelta) < FRACUNIT/2 && !(mo->player->rmomx || mo->player->rmomy))
			return; // Allow the player to stand still on slopes below a certain steepness
	}
870

871
	thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 3 / 2 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1);
872

873 874 875 876 877 878 879 880 881 882 883 884 885 886
	if (mo->player && (mo->player->pflags & PF_SPINNING)) {
		fixed_t mult = 0;
		if (mo->momx || mo->momy) {
			angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection;

			if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0)
				angle ^= ANGLE_180;

			mult = FINECOSINE(angle >> ANGLETOFINESHIFT);
		}

		thrust = FixedMul(thrust, FRACUNIT*2/3 + mult/8);
	}

887
	if (mo->momx || mo->momy) // Slightly increase thrust based on the object's speed
888
		thrust = FixedMul(thrust, FRACUNIT+P_AproxDistance(mo->momx, mo->momy)/16);
889
	// This makes it harder to zigzag up steep slopes, as well as allows greater top speed when rolling down
890

891
	// Let's get the gravity strength for the object...
892 893
	thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo)));

894 895
	// ... and its friction against the ground for good measure (divided by original friction to keep behaviour for normal slopes the same).
	thrust = FixedMul(thrust, FixedDiv(mo->friction, ORIG_FRICTION));
896

897 898
	P_Thrust(mo, mo->standingslope->xydirection, thrust);
}