1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2004      by Stephen McGranahan
4 // Copyright (C) 2015-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file  p_slopes.c
11 /// \brief ZDoom + Eternity Engine Slopes, ported and enhanced by Kalaron
12 
13 #include "doomdef.h"
14 #include "r_defs.h"
15 #include "r_state.h"
16 #include "m_bbox.h"
17 #include "z_zone.h"
18 #include "p_local.h"
19 #include "p_spec.h"
20 #include "p_slopes.h"
21 #include "p_setup.h"
22 #include "r_main.h"
23 #include "p_maputl.h"
24 #include "w_wad.h"
25 
26 pslope_t *slopelist = NULL;
27 UINT16 slopecount = 0;
28 
29 // Calculate line normal
P_CalculateSlopeNormal(pslope_t * slope)30 void P_CalculateSlopeNormal(pslope_t *slope) {
31 	slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT);
32 	slope->normal.x = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x);
33 	slope->normal.y = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y);
34 }
35 
36 /// Setup slope via 3 vertexes.
ReconfigureViaVertexes(pslope_t * slope,const vector3_t v1,const vector3_t v2,const vector3_t v3)37 static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const vector3_t v2, const vector3_t v3)
38 {
39 	vector3_t vec1, vec2;
40 
41 	// Set origin.
42 	FV3_Copy(&slope->o, &v1);
43 
44 	// Get slope's normal.
45 	FV3_SubEx(&v2, &v1, &vec1);
46 	FV3_SubEx(&v3, &v1, &vec2);
47 
48 	// Set some defaults for a non-sloped "slope"
49 	if (vec1.z == 0 && vec2.z == 0)
50 	{
51 		slope->zangle = slope->xydirection = 0;
52 		slope->zdelta = slope->d.x = slope->d.y = 0;
53 		slope->normal.x = slope->normal.y = 0;
54 		slope->normal.z = FRACUNIT;
55 	}
56 	else
57 	{
58 		/// \note Using fixed point for vectorial products easily leads to overflows so we work around by downscaling them.
59 		fixed_t m = max(
60 			max(max(abs(vec1.x), abs(vec1.y)), abs(vec1.z)),
61 			max(max(abs(vec2.x), abs(vec2.y)), abs(vec2.z))
62 		) >> 5; // shifting right by 5 is good enough.
63 
64 		FV3_Cross(
65 				FV3_Divide(&vec1, m),
66 				FV3_Divide(&vec2, m),
67 				&slope->normal
68 				);
69 
70 		// NOTE: FV3_Magnitude() doesn't work properly in some cases, and chaining FixedHypot() seems to give worse results.
71 		m = R_PointToDist2(0, 0, R_PointToDist2(0, 0, slope->normal.x, slope->normal.y), slope->normal.z);
72 
73 		// Invert normal if it's facing down.
74 		if (slope->normal.z < 0)
75 			m = -m;
76 
77 		FV3_Divide(&slope->normal, m);
78 
79 		// Get direction vector
80 		m = FixedHypot(slope->normal.x, slope->normal.y);
81 		slope->d.x = -FixedDiv(slope->normal.x, m);
82 		slope->d.y = -FixedDiv(slope->normal.y, m);
83 
84 		// Z delta
85 		slope->zdelta = FixedDiv(m, slope->normal.z);
86 
87 		// Get angles
88 		slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180;
89 		slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta));
90 	}
91 }
92 
93 /// Recalculate dynamic slopes.
T_DynamicSlopeLine(dynplanethink_t * th)94 void T_DynamicSlopeLine (dynplanethink_t* th)
95 {
96 	pslope_t* slope = th->slope;
97 	line_t* srcline = th->sourceline;
98 
99 	fixed_t zdelta;
100 
101 	switch(th->type) {
102 	case DP_FRONTFLOOR:
103 		zdelta = srcline->backsector->floorheight - srcline->frontsector->floorheight;
104 		slope->o.z = srcline->frontsector->floorheight;
105 		break;
106 
107 	case DP_FRONTCEIL:
108 		zdelta = srcline->backsector->ceilingheight - srcline->frontsector->ceilingheight;
109 		slope->o.z = srcline->frontsector->ceilingheight;
110 		break;
111 
112 	case DP_BACKFLOOR:
113 		zdelta = srcline->frontsector->floorheight - srcline->backsector->floorheight;
114 		slope->o.z = srcline->backsector->floorheight;
115 		break;
116 
117 	case DP_BACKCEIL:
118 		zdelta = srcline->frontsector->ceilingheight - srcline->backsector->ceilingheight;
119 		slope->o.z = srcline->backsector->ceilingheight;
120 		break;
121 
122 	default:
123 		return;
124 	}
125 
126 	if (slope->zdelta != FixedDiv(zdelta, th->extent)) {
127 		slope->zdelta = FixedDiv(zdelta, th->extent);
128 		slope->zangle = R_PointToAngle2(0, 0, th->extent, -zdelta);
129 		P_CalculateSlopeNormal(slope);
130 	}
131 }
132 
133 /// Mapthing-defined
T_DynamicSlopeVert(dynplanethink_t * th)134 void T_DynamicSlopeVert (dynplanethink_t* th)
135 {
136 	pslope_t* slope = th->slope;
137 
138 	size_t i;
139 	INT32 l;
140 
141 	for (i = 0; i < 3; i++) {
142 		l = Tag_FindLineSpecial(799, th->tags[i]);
143 		if (l != -1) {
144 			th->vex[i].z = lines[l].frontsector->floorheight;
145 		}
146 		else
147 			th->vex[i].z = 0;
148 	}
149 
150 	ReconfigureViaVertexes(slope, th->vex[0], th->vex[1], th->vex[2]);
151 }
152 
P_AddDynSlopeThinker(pslope_t * slope,dynplanetype_t type,line_t * sourceline,fixed_t extent,const INT16 tags[3],const vector3_t vx[3])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 {
155 	dynplanethink_t* th = Z_Calloc(sizeof (*th), PU_LEVSPEC, NULL);
156 	switch (type)
157 	{
158 	case DP_VERTEX:
159 		th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeVert;
160 		memcpy(th->tags, tags, sizeof(th->tags));
161 		memcpy(th->vex, vx, sizeof(th->vex));
162 		break;
163 	default:
164 		th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeLine;
165 		th->sourceline = sourceline;
166 		th->extent = extent;
167 	}
168 
169 	th->slope = slope;
170 	th->type = type;
171 
172 	P_AddThinker(THINK_DYNSLOPE, &th->thinker);
173 }
174 
175 
176 /// Create a new slope and add it to the slope list.
Slope_Add(const UINT8 flags)177 static inline pslope_t* Slope_Add (const UINT8 flags)
178 {
179 	pslope_t *ret = Z_Calloc(sizeof(pslope_t), PU_LEVEL, NULL);
180 	ret->flags = flags;
181 
182 	ret->next = slopelist;
183 	slopelist = ret;
184 
185 	slopecount++;
186 	ret->id = slopecount;
187 
188 	return ret;
189 }
190 
191 /// Alocates and fill the contents of a slope structure.
MakeViaVectors(const vector3_t * o,const vector2_t * d,const fixed_t zdelta,UINT8 flags)192 static pslope_t *MakeViaVectors(const vector3_t *o, const vector2_t *d,
193                              const fixed_t zdelta, UINT8 flags)
194 {
195 	pslope_t *ret = Slope_Add(flags);
196 
197 	FV3_Copy(&ret->o, o);
198 	FV2_Copy(&ret->d, d);
199 
200 	ret->zdelta = zdelta;
201 
202 	ret->flags = flags;
203 
204 	return ret;
205 }
206 
207 /// Get furthest perpendicular distance from all vertexes in a sector for a given line.
GetExtent(sector_t * sector,line_t * line)208 static fixed_t GetExtent(sector_t *sector, line_t *line)
209 {
210 	// ZDoom code reference: v3float_t = vertex_t
211 	fixed_t fardist = -FRACUNIT;
212 	size_t i;
213 
214 	// Find furthest vertex from the reference line. It, along with the two ends
215 	// of the line, will define the plane.
216 	for(i = 0; i < sector->linecount; i++)
217 	{
218 		line_t *li = sector->lines[i];
219 		vertex_t tempv;
220 		fixed_t dist;
221 
222 		// Don't compare to the slope line.
223 		if(li == line)
224 			continue;
225 
226 		P_ClosestPointOnLine(li->v1->x, li->v1->y, line, &tempv);
227 		dist = R_PointToDist2(tempv.x, tempv.y, li->v1->x, li->v1->y);
228 		if(dist > fardist)
229 			fardist = dist;
230 
231 		// Okay, maybe do it for v2 as well?
232 		P_ClosestPointOnLine(li->v2->x, li->v2->y, line, &tempv);
233 		dist = R_PointToDist2(tempv.x, tempv.y, li->v2->x, li->v2->y);
234 		if(dist > fardist)
235 			fardist = dist;
236 	}
237 
238 	return fardist;
239 }
240 
241 /// Creates one or more slopes based on the given line type and front/back sectors.
line_SpawnViaLine(const int linenum,const boolean spawnthinker)242 static void line_SpawnViaLine(const int linenum, const boolean spawnthinker)
243 {
244 	// With dynamic slopes, it's fine to just leave this function as normal,
245 	// because checking to see if a slope had changed will waste more memory than
246 	// if the slope was just updated when called
247 	line_t *line = lines + linenum;
248 	pslope_t *fslope = NULL, *cslope = NULL;
249 	vector3_t origin, point;
250 	vector2_t direction;
251 	fixed_t nx, ny, dz, extent;
252 
253 	boolean frontfloor = line->args[0] == TMS_FRONT;
254 	boolean backfloor = line->args[0] == TMS_BACK;
255 	boolean frontceil = line->args[1] == TMS_FRONT;
256 	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 	if(!frontfloor && !backfloor && !frontceil && !backceil)
264 	{
265 		CONS_Printf("line_SpawnViaLine: Slope special with nothing to do.\n");
266 		return;
267 	}
268 
269 	if(!line->frontsector || !line->backsector)
270 	{
271 		CONS_Debug(DBG_SETUP, "line_SpawnViaLine: Slope special used on a line without two sides. (line number %i)\n", linenum);
272 		return;
273 	}
274 
275 	{
276 		fixed_t len = R_PointToDist2(0, 0, line->dx, line->dy);
277 		nx = FixedDiv(line->dy, len);
278 		ny = -FixedDiv(line->dx, len);
279 	}
280 
281 	// Set origin to line's center.
282 	origin.x = line->v1->x + (line->v2->x - line->v1->x)/2;
283 	origin.y = line->v1->y + (line->v2->y - line->v1->y)/2;
284 
285 	// For FOF slopes, make a special function to copy to the xy origin & direction relative to the position of the FOF on the map!
286 	if(frontfloor || frontceil)
287 	{
288 		line->frontsector->hasslope = true; // Tell the software renderer that we're sloped
289 
290 		origin.z = line->backsector->floorheight;
291 		direction.x = nx;
292 		direction.y = ny;
293 
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 			return;
300 		}
301 
302 		// reposition the origin according to the extent
303 		point.x = origin.x + FixedMul(direction.x, extent);
304 		point.y = origin.y + FixedMul(direction.y, extent);
305 		direction.x = -direction.x;
306 		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 		if(frontfloor)
311 		{
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 =
318             MakeViaVectors(&point, &direction, dz, flags);
319 
320 			// Now remember that f_slope IS a vector
321 			// fslope->o = origin      3D point 1 of the vector
322 			// fslope->d = destination 3D point 2 of the vector
323 			// 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);
329 
330 			if (spawnthinker && (flags & SL_DYNAMIC))
331 				P_AddDynSlopeThinker(fslope, DP_FRONTFLOOR, line, extent, NULL, NULL);
332 		}
333 		if(frontceil)
334 		{
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 =
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);
346 
347 			if (spawnthinker && (flags & SL_DYNAMIC))
348 				P_AddDynSlopeThinker(cslope, DP_FRONTCEIL, line, extent, NULL, NULL);
349 		}
350 	}
351 	if(backfloor || backceil)
352 	{
353 		line->backsector->hasslope = true; // Tell the software renderer that we're sloped
354 
355 		origin.z = line->frontsector->floorheight;
356 		// Backsector
357 		direction.x = -nx;
358 		direction.y = -ny;
359 
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 			return;
366 		}
367 
368 		// reposition the origin according to the extent
369 		point.x = origin.x + FixedMul(direction.x, extent);
370 		point.y = origin.y + FixedMul(direction.y, extent);
371 		direction.x = -direction.x;
372 		direction.y = -direction.y;
373 
374 		if(backfloor)
375 		{
376 			point.z = line->backsector->floorheight;
377 			dz = FixedDiv(origin.z - point.z, extent);
378 
379 			fslope = line->backsector->f_slope =
380             MakeViaVectors(&point, &direction, dz, flags);
381 
382 			fslope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
383 			fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y);
384 
385 			P_CalculateSlopeNormal(fslope);
386 
387 			if (spawnthinker && (flags & SL_DYNAMIC))
388 				P_AddDynSlopeThinker(fslope, DP_BACKFLOOR, line, extent, NULL, NULL);
389 		}
390 		if(backceil)
391 		{
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 =
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);
403 
404 			if (spawnthinker && (flags & SL_DYNAMIC))
405 				P_AddDynSlopeThinker(cslope, DP_BACKCEIL, line, extent, NULL, NULL);
406 		}
407 	}
408 }
409 
410 /// Creates a new slope from three mapthings with the specified IDs
MakeViaMapthings(INT16 tag1,INT16 tag2,INT16 tag3,UINT8 flags,const boolean spawnthinker)411 static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flags, const boolean spawnthinker)
412 {
413 	size_t i;
414 	mapthing_t* mt = mapthings;
415 	mapthing_t* vertices[3] = {0};
416 	INT16 tags[3] = {tag1, tag2, tag3};
417 
418 	vector3_t vx[3];
419 	pslope_t* ret = Slope_Add(flags);
420 
421 	// And... look for the vertices in question.
422 	for (i = 0; i < nummapthings; i++, mt++) {
423 		if (mt->type != 750) // Haha, I'm hijacking the old Chaos Spawn thingtype for something!
424 			continue;
425 
426 		if (!vertices[0] && Tag_Find(&mt->tags, tag1))
427 			vertices[0] = mt;
428 		else if (!vertices[1] && Tag_Find(&mt->tags, tag2))
429 			vertices[1] = mt;
430 		else if (!vertices[2] && Tag_Find(&mt->tags, tag3))
431 			vertices[2] = mt;
432 	}
433 
434 	// Now set heights for each vertex, because they haven't been set yet
435 	for (i = 0; i < 3; i++) {
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?)
438 			I_Error("MakeViaMapthings: Slope vertex %s (for linedef tag %d) not found!", sizeu1(i), tag1);
439 		vx[i].x = mt->x << FRACBITS;
440 		vx[i].y = mt->y << FRACBITS;
441 		vx[i].z = mt->z << FRACBITS;
442 		if (!mt->extrainfo)
443 			vx[i].z += R_PointInSubsector(vx[i].x, vx[i].y)->sector->floorheight;
444 	}
445 
446 	ReconfigureViaVertexes(ret, vx[0], vx[1], vx[2]);
447 
448 	if (spawnthinker && (flags & SL_DYNAMIC))
449 		P_AddDynSlopeThinker(ret, DP_VERTEX, NULL, 0, tags, vx);
450 
451 	return ret;
452 }
453 
454 /// Create vertex based slopes using tagged mapthings.
line_SpawnViaMapthingVertexes(const int linenum,const boolean spawnthinker)455 static void line_SpawnViaMapthingVertexes(const int linenum, const boolean spawnthinker)
456 {
457 	line_t *line = lines + linenum;
458 	side_t *side;
459 	pslope_t **slopetoset;
460 	UINT16 tag1 = line->args[1];
461 	UINT16 tag2 = line->args[2];
462 	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;
468 
469 	switch(line->args[0])
470 	{
471 	case TMSP_FRONTFLOOR:
472 		slopetoset = &line->frontsector->f_slope;
473 		side = &sides[line->sidenum[0]];
474 		break;
475 	case TMSP_FRONTCEILING:
476 		slopetoset = &line->frontsector->c_slope;
477 		side = &sides[line->sidenum[0]];
478 		break;
479 	case TMSP_BACKFLOOR:
480 		slopetoset = &line->backsector->f_slope;
481 		side = &sides[line->sidenum[1]];
482 		break;
483 	case TMSP_BACKCEILING:
484 		slopetoset = &line->backsector->c_slope;
485 		side = &sides[line->sidenum[1]];
486 	default:
487 		return;
488 	}
489 
490 	*slopetoset = MakeViaMapthings(tag1, tag2, tag3, flags, spawnthinker);
491 
492 	side->sector->hasslope = true;
493 }
494 
495 /// Spawn textmap vertex slopes.
SpawnVertexSlopes(void)496 static void SpawnVertexSlopes(void)
497 {
498 	line_t *l1, *l2;
499 	sector_t* sc;
500 	vertex_t *v1, *v2, *v3;
501 	size_t i;
502 	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
503 	{
504 		// The vertex slopes only work for 3-vertex sectors (and thus 3-sided sectors).
505 		if (sc->linecount != 3)
506 			continue;
507 
508 		l1 = sc->lines[0];
509 		l2 = sc->lines[1];
510 
511 		// Determine the vertexes.
512 		v1 = l1->v1;
513 		v2 = l1->v2;
514 		if ((l2->v1 != v1) && (l2->v1 != v2))
515 			v3 = l2->v1;
516 		else
517 			v3 = l2->v2;
518 
519 		if (v1->floorzset || v2->floorzset || v3->floorzset)
520 		{
521 			vector3_t vtx[3] = {
522 				{v1->x, v1->y, v1->floorzset ? v1->floorz : sc->floorheight},
523 				{v2->x, v2->y, v2->floorzset ? v2->floorz : sc->floorheight},
524 				{v3->x, v3->y, v3->floorzset ? v3->floorz : sc->floorheight}};
525 			pslope_t *slop = Slope_Add(0);
526 			sc->f_slope = slop;
527 			sc->hasslope = true;
528 			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
529 		}
530 
531 		if (v1->ceilingzset || v2->ceilingzset || v3->ceilingzset)
532 		{
533 			vector3_t vtx[3] = {
534 				{v1->x, v1->y, v1->ceilingzset ? v1->ceilingz : sc->ceilingheight},
535 				{v2->x, v2->y, v2->ceilingzset ? v2->ceilingz : sc->ceilingheight},
536 				{v3->x, v3->y, v3->ceilingzset ? v3->ceilingz : sc->ceilingheight}};
537 			pslope_t *slop = Slope_Add(0);
538 			sc->c_slope = slop;
539 			sc->hasslope = true;
540 			ReconfigureViaVertexes(slop, vtx[0], vtx[1], vtx[2]);
541 		}
542 	}
543 }
544 
P_SetSlopeFromTag(sector_t * sec,INT32 tag,boolean ceiling)545 static boolean P_SetSlopeFromTag(sector_t *sec, INT32 tag, boolean ceiling)
546 {
547 	INT32 i;
548 	pslope_t **secslope = ceiling ? &sec->c_slope : &sec->f_slope;
549 	TAG_ITER_DECLARECOUNTER(0);
550 
551 	if (!tag || *secslope)
552 		return false;
553 	TAG_ITER_SECTORS(0, tag, i)
554 	{
555 		pslope_t *srcslope = ceiling ? sectors[i].c_slope : sectors[i].f_slope;
556 		if (srcslope)
557 		{
558 			*secslope = srcslope;
559 			return true;
560 		}
561 	}
562 	return false;
563 }
564 
P_CopySlope(pslope_t ** toslope,pslope_t * fromslope)565 static boolean P_CopySlope(pslope_t **toslope, pslope_t *fromslope)
566 {
567 	if (*toslope || !fromslope)
568 		return true;
569 
570 	*toslope = fromslope;
571 	return true;
572 }
573 
P_UpdateHasSlope(sector_t * sec)574 static void P_UpdateHasSlope(sector_t *sec)
575 {
576 	size_t i;
577 
578 	sec->hasslope = true;
579 
580 	// if this is an FOF control sector, make sure any target sectors also are marked as having slopes
581 	if (sec->numattached)
582 		for (i = 0; i < sec->numattached; i++)
583 			sectors[sec->attached[i]].hasslope = true;
584 }
585 
586 //
587 // P_CopySectorSlope
588 //
589 // Searches through tagged sectors and copies
590 //
P_CopySectorSlope(line_t * line)591 void P_CopySectorSlope(line_t *line)
592 {
593 	sector_t *fsec = line->frontsector;
594 	sector_t *bsec = line->backsector;
595 	boolean setfront = false;
596 	boolean setback = false;
597 
598 	setfront |= P_SetSlopeFromTag(fsec, line->args[0], false);
599 	setfront |= P_SetSlopeFromTag(fsec, line->args[1], true);
600 	if (bsec)
601 	{
602 		setback |= P_SetSlopeFromTag(bsec, line->args[2], false);
603 		setback |= P_SetSlopeFromTag(bsec, line->args[3], true);
604 
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 	if (setfront)
616 		P_UpdateHasSlope(fsec);
617 	if (setback)
618 		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 // P_SlopeById
625 //
626 // Looks in the slope list for a slope with a specified ID. Mostly useful for netgame sync
627 //
P_SlopeById(UINT16 id)628 pslope_t *P_SlopeById(UINT16 id)
629 {
630 	pslope_t *ret;
631 	for (ret = slopelist; ret && ret->id != id; ret = ret->next);
632 	return ret;
633 }
634 
635 /// Initializes and reads the slopes from the map data.
P_SpawnSlopes(const boolean fromsave)636 void P_SpawnSlopes(const boolean fromsave) {
637 	size_t i;
638 
639 	slopelist = NULL;
640 	slopecount = 0;
641 
642 	/// Generates vertex slopes.
643 	SpawnVertexSlopes();
644 
645 	/// Generates line special-defined slopes.
646 	for (i = 0; i < numlines; i++)
647 	{
648 		switch (lines[i].special)
649 		{
650 			case 700:
651 				line_SpawnViaLine(i, !fromsave);
652 				break;
653 
654 			case 704:
655 				line_SpawnViaMapthingVertexes(i, !fromsave);
656 				break;
657 
658 			default:
659 				break;
660 		}
661 	}
662 
663 	/// Copies slopes from tagged sectors via line specials.
664 	/// \note Doesn't actually copy, but instead they share the same pointers.
665 	for (i = 0; i < numlines; i++)
666 		switch (lines[i].special)
667 		{
668 			case 720:
669 				P_CopySectorSlope(&lines[i]);
670 			default:
671 				break;
672 		}
673 }
674 
675 // ============================================================================
676 //
677 // Various utilities related to slopes
678 //
679 
680 // Returns the height of the sloped plane at (x, y) as a fixed_t
P_GetSlopeZAt(const pslope_t * slope,fixed_t x,fixed_t y)681 fixed_t P_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
682 {
683 	fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) +
684 	               FixedMul(y - slope->o.y, slope->d.y);
685 
686 	return slope->o.z + FixedMul(dist, slope->zdelta);
687 }
688 
689 // Like P_GetSlopeZAt but falls back to z if slope is NULL
P_GetZAt(const pslope_t * slope,fixed_t x,fixed_t y,fixed_t z)690 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 // Returns the height of the sector floor at (x, y)
P_GetSectorFloorZAt(const sector_t * sector,fixed_t x,fixed_t y)696 fixed_t P_GetSectorFloorZAt(const sector_t *sector, fixed_t x, fixed_t y)
697 {
698 	return sector->f_slope ? P_GetSlopeZAt(sector->f_slope, x, y) : sector->floorheight;
699 }
700 
701 // Returns the height of the sector ceiling at (x, y)
P_GetSectorCeilingZAt(const sector_t * sector,fixed_t x,fixed_t y)702 fixed_t P_GetSectorCeilingZAt(const sector_t *sector, fixed_t x, fixed_t y)
703 {
704 	return sector->c_slope ? P_GetSlopeZAt(sector->c_slope, x, y) : sector->ceilingheight;
705 }
706 
707 // Returns the height of the FOF top at (x, y)
P_GetFFloorTopZAt(const ffloor_t * ffloor,fixed_t x,fixed_t y)708 fixed_t P_GetFFloorTopZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
709 {
710 	return *ffloor->t_slope ? P_GetSlopeZAt(*ffloor->t_slope, x, y) : *ffloor->topheight;
711 }
712 
713 // Returns the height of the FOF bottom  at (x, y)
P_GetFFloorBottomZAt(const ffloor_t * ffloor,fixed_t x,fixed_t y)714 fixed_t P_GetFFloorBottomZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
715 {
716 	return *ffloor->b_slope ? P_GetSlopeZAt(*ffloor->b_slope, x, y) : *ffloor->bottomheight;
717 }
718 
719 // Returns the height of the light list at (x, y)
P_GetLightZAt(const lightlist_t * light,fixed_t x,fixed_t y)720 fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y)
721 {
722 	return light->slope ? P_GetSlopeZAt(light->slope, x, y) : light->height;
723 }
724 
725 
726 //
727 // P_QuantizeMomentumToSlope
728 //
729 // When given a vector, rotates it and aligns it to a slope
P_QuantizeMomentumToSlope(vector3_t * momentum,pslope_t * slope)730 void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope)
731 {
732 	vector3_t axis; // Fuck you, C90.
733 
734 	if (slope->flags & SL_NOPHYSICS)
735 		return; // No physics, no quantizing.
736 
737 	axis.x = -slope->d.y;
738 	axis.y = slope->d.x;
739 	axis.z = 0;
740 
741 	FV3_Rotate(momentum, &axis, slope->zangle >> ANGLETOFINESHIFT);
742 }
743 
744 //
745 // P_ReverseQuantizeMomentumToSlope
746 //
747 // When given a vector, rotates and aligns it to a flat surface (from being relative to a given slope)
P_ReverseQuantizeMomentumToSlope(vector3_t * momentum,pslope_t * slope)748 void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope)
749 {
750 	slope->zangle = InvAngle(slope->zangle);
751 	P_QuantizeMomentumToSlope(momentum, slope);
752 	slope->zangle = InvAngle(slope->zangle);
753 }
754 
755 //
756 // P_SlopeLaunch
757 //
758 // Handles slope ejection for objects
P_SlopeLaunch(mobj_t * mo)759 void P_SlopeLaunch(mobj_t *mo)
760 {
761 	if (!(mo->standingslope->flags & SL_NOPHYSICS) // If there's physics, time for launching.
762 		&& (mo->standingslope->normal.x != 0
763 		||  mo->standingslope->normal.y != 0))
764 	{
765 		// Double the pre-rotation Z, then halve the post-rotation Z. This reduces the
766 		// vertical launch given from slopes while increasing the horizontal launch
767 		// given. Good for SRB2's gravity and horizontal speeds.
768 		vector3_t slopemom;
769 		slopemom.x = mo->momx;
770 		slopemom.y = mo->momy;
771 		slopemom.z = mo->momz*2;
772 		P_QuantizeMomentumToSlope(&slopemom, mo->standingslope);
773 
774 		mo->momx = slopemom.x;
775 		mo->momy = slopemom.y;
776 		mo->momz = slopemom.z/2;
777 	}
778 
779 	//CONS_Printf("Launched off of slope.\n");
780 	mo->standingslope = NULL;
781 
782 	if (mo->player)
783 		mo->player->powers[pw_justlaunched] = 1;
784 }
785 
786 //
787 // P_GetWallTransferMomZ
788 //
789 // It would be nice to have a single function that does everything necessary for slope-to-wall transfer.
790 // However, it needs to be seperated out in P_XYMovement to take into account momentum before and after hitting the wall.
791 // This just performs the necessary calculations for getting the base vertical momentum; the horizontal is already reasonably calculated by P_SlideMove.
P_GetWallTransferMomZ(mobj_t * mo,pslope_t * slope)792 fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
793 {
794 	vector3_t slopemom, axis;
795 	angle_t ang;
796 
797 	if (mo->standingslope->flags & SL_NOPHYSICS)
798 		return 0;
799 
800 	// If there's physics, time for launching.
801 	// Doesn't kill the vertical momentum as much as P_SlopeLaunch does.
802 	ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1);
803 	if (ang > ANGLE_90 && ang < ANGLE_180)
804 		ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards
805 
806 	slopemom.x = mo->momx;
807 	slopemom.y = mo->momy;
808 	slopemom.z = 3*(mo->momz/2);
809 
810 	axis.x = -slope->d.y;
811 	axis.y = slope->d.x;
812 	axis.z = 0;
813 
814 	FV3_Rotate(&slopemom, &axis, ang >> ANGLETOFINESHIFT);
815 
816 	return 2*(slopemom.z/3);
817 }
818 
819 // Function to help handle landing on slopes
P_HandleSlopeLanding(mobj_t * thing,pslope_t * slope)820 void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
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 		if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope
825 		{
826 			thing->standingslope = slope;
827 			if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
828 				thing->momz = -P_MobjFlip(thing);
829 		}
830 		return;
831 	}
832 
833 	mom.x = thing->momx;
834 	mom.y = thing->momy;
835 	mom.z = thing->momz*2;
836 
837 	P_ReverseQuantizeMomentumToSlope(&mom, slope);
838 
839 	if (P_MobjFlip(thing)*mom.z < 0) { // falling, land on slope
840 		thing->momx = mom.x;
841 		thing->momy = mom.y;
842 		thing->standingslope = slope;
843 		if (!thing->player || !(thing->player->pflags & PF_BOUNCING))
844 			thing->momz = -P_MobjFlip(thing);
845 	}
846 }
847 
848 // https://yourlogicalfallacyis.com/slippery-slope
849 // Handles sliding down slopes, like if they were made of butter :)
P_ButteredSlope(mobj_t * mo)850 void P_ButteredSlope(mobj_t *mo)
851 {
852 	fixed_t thrust;
853 
854 	if (!mo->standingslope)
855 		return;
856 
857 	if (mo->standingslope->flags & SL_NOPHYSICS)
858 		return; // No physics, no butter.
859 
860 	if (mo->flags & (MF_NOCLIPHEIGHT|MF_NOGRAVITY))
861 		return; // don't slide down slopes if you can't touch them or you're not affected by gravity
862 
863 	if (mo->player) {
864 		if (abs(mo->standingslope->zdelta) < FRACUNIT/4 && !(mo->player->pflags & PF_SPINNING))
865 			return; // Don't slide on non-steep slopes unless spinning
866 
867 		if (abs(mo->standingslope->zdelta) < FRACUNIT/2 && !(mo->player->rmomx || mo->player->rmomy))
868 			return; // Allow the player to stand still on slopes below a certain steepness
869 	}
870 
871 	thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 3 / 2 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1);
872 
873 	if (mo->player && (mo->player->pflags & PF_SPINNING)) {
874 		fixed_t mult = 0;
875 		if (mo->momx || mo->momy) {
876 			angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection;
877 
878 			if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0)
879 				angle ^= ANGLE_180;
880 
881 			mult = FINECOSINE(angle >> ANGLETOFINESHIFT);
882 		}
883 
884 		thrust = FixedMul(thrust, FRACUNIT*2/3 + mult/8);
885 	}
886 
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 	thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo)));
893 
894 	// ... and its friction against the ground for good measure (divided by original friction to keep behaviour for normal slopes the same).
895 	thrust = FixedMul(thrust, FixedDiv(mo->friction, ORIG_FRICTION));
896 
897 	P_Thrust(mo, mo->standingslope->xydirection, thrust);
898 }
899