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-2021 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  r_things.c
12 /// \brief Refresh of things, i.e. objects represented by sprites
13 
14 #include "doomdef.h"
15 #include "console.h"
16 #include "g_game.h"
17 #include "r_local.h"
18 #include "st_stuff.h"
19 #include "w_wad.h"
20 #include "z_zone.h"
21 #include "m_menu.h" // character select
22 #include "m_misc.h"
23 #include "info.h" // spr2names
24 #include "i_video.h" // rendermode
25 #include "i_system.h"
26 #include "r_things.h"
27 #include "r_patch.h"
28 #include "r_patchrotation.h"
29 #include "r_picformats.h"
30 #include "r_plane.h"
31 #include "r_portal.h"
32 #include "r_splats.h"
33 #include "p_tick.h"
34 #include "p_local.h"
35 #include "p_slopes.h"
36 #include "d_netfil.h" // blargh. for nameonly().
37 #include "m_cheat.h" // objectplace
38 #ifdef HWRENDER
39 #include "hardware/hw_md2.h"
40 #include "hardware/hw_glob.h"
41 #include "hardware/hw_light.h"
42 #include "hardware/hw_drv.h"
43 #endif
44 
45 #define MINZ (FRACUNIT*4)
46 #define BASEYCENTER (BASEVIDHEIGHT/2)
47 
48 typedef struct
49 {
50 	INT32 x1, x2;
51 	INT32 column;
52 	INT32 topclip, bottomclip;
53 } maskdraw_t;
54 
55 //
56 // Sprite rotation 0 is facing the viewer,
57 //  rotation 1 is one angle turn CLOCKWISE around the axis.
58 // This is not the same as the angle,
59 //  which increases counter clockwise (protractor).
60 // There was a lot of stuff grabbed wrong, so I changed it...
61 //
62 static lighttable_t **spritelights;
63 
64 // constant arrays used for psprite clipping and initializing clipping
65 INT16 negonearray[MAXVIDWIDTH];
66 INT16 screenheightarray[MAXVIDWIDTH];
67 
68 spriteinfo_t spriteinfo[NUMSPRITES];
69 
70 //
71 // INITIALIZATION FUNCTIONS
72 //
73 
74 // variables used to look up and range check thing_t sprites patches
75 spritedef_t *sprites;
76 size_t numsprites;
77 
78 static spriteframe_t sprtemp[64];
79 static size_t maxframe;
80 static const char *spritename;
81 
82 // ==========================================================================
83 //
84 // Sprite loading routines: support sprites in pwad, dehacked sprite renaming,
85 // replacing not all frames of an existing sprite, add sprites at run-time,
86 // add wads at run-time.
87 //
88 // ==========================================================================
89 
90 //
91 //
92 //
R_InstallSpriteLump(UINT16 wad,UINT16 lump,size_t lumpid,UINT8 frame,UINT8 rotation,UINT8 flipped)93 static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
94                                 UINT16 lump,
95                                 size_t lumpid,      // identifier
96                                 UINT8 frame,
97                                 UINT8 rotation,
98                                 UINT8 flipped)
99 {
100 	char cn = R_Frame2Char(frame), cr = R_Rotation2Char(rotation); // for debugging
101 
102 	INT32 r;
103 	lumpnum_t lumppat = wad;
104 	lumppat <<= 16;
105 	lumppat += lump;
106 
107 	if (maxframe ==(size_t)-1 || frame > maxframe)
108 		maxframe = frame;
109 
110 #ifdef ROTSPRITE
111 	for (r = 0; r < 16; r++)
112 	{
113 		sprtemp[frame].rotated[0][r] = NULL;
114 		sprtemp[frame].rotated[1][r] = NULL;
115 	}
116 #endif
117 
118 	if (rotation == 0)
119 	{
120 		// the lump should be used for all rotations
121 		if (sprtemp[frame].rotate == SRF_SINGLE)
122 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple rot = 0 lump\n", spritename, cn);
123 		else if (sprtemp[frame].rotate != SRF_NONE) // Let's bundle 1-8/16 and L/R rotations into one debug message.
124 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has rotations and a rot = 0 lump\n", spritename, cn);
125 
126 		sprtemp[frame].rotate = SRF_SINGLE;
127 		for (r = 0; r < 16; r++)
128 		{
129 			sprtemp[frame].lumppat[r] = lumppat;
130 			sprtemp[frame].lumpid[r] = lumpid;
131 		}
132 		sprtemp[frame].flip = flipped ? 0xFFFF : 0; // 1111111111111111 in binary
133 		return;
134 	}
135 
136 	if (rotation == ROT_L || rotation == ROT_R)
137 	{
138 		UINT8 rightfactor = ((rotation == ROT_R) ? 4 : 0);
139 
140 		// the lump should be used for half of all rotations
141 		if (sprtemp[frame].rotate == SRF_NONE)
142 			sprtemp[frame].rotate = SRF_SINGLE;
143 		else if (sprtemp[frame].rotate == SRF_SINGLE)
144 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has L/R rotations and a rot = 0 lump\n", spritename, cn);
145 		else if (sprtemp[frame].rotate == SRF_3D)
146 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8 rotations\n", spritename, cn);
147 		else if (sprtemp[frame].rotate == SRF_3DGE)
148 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-G rotations\n", spritename, cn);
149 		else if ((sprtemp[frame].rotate & SRF_LEFT) && (rotation == ROT_L))
150 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple L rotations\n", spritename, cn);
151 		else if ((sprtemp[frame].rotate & SRF_RIGHT) && (rotation == ROT_R))
152 			CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has multiple R rotations\n", spritename, cn);
153 
154 		sprtemp[frame].rotate |= ((rotation == ROT_R) ? SRF_RIGHT : SRF_LEFT);
155 		if ((sprtemp[frame].rotate & SRF_2D) == SRF_2D)
156 			sprtemp[frame].rotate &= ~SRF_3DMASK; // SRF_3D|SRF_2D being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen.
157 
158 		// load into every relevant angle, including the front one
159 		for (r = 0; r < 4; r++)
160 		{
161 			sprtemp[frame].lumppat[r + rightfactor] = lumppat;
162 			sprtemp[frame].lumpid[r + rightfactor] = lumpid;
163 			sprtemp[frame].lumppat[r + rightfactor + 8] = lumppat;
164 			sprtemp[frame].lumpid[r + rightfactor + 8] = lumpid;
165 
166 		}
167 
168 		if (flipped)
169 			sprtemp[frame].flip |= (0x0F0F<<rightfactor); // 0000111100001111 or 1111000011110000 in binary, depending on rotation being ROT_L or ROT_R
170 		else
171 			sprtemp[frame].flip &= ~(0x0F0F<<rightfactor); // ditto
172 
173 		return;
174 	}
175 
176 	if (sprtemp[frame].rotate == SRF_NONE)
177 		sprtemp[frame].rotate = SRF_SINGLE;
178 	else if (sprtemp[frame].rotate == SRF_SINGLE)
179 		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has 1-8/G rotations and a rot = 0 lump\n", spritename, cn);
180 	else if (sprtemp[frame].rotate & SRF_2D)
181 		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s frame %c has both L/R and 1-8/G rotations\n", spritename, cn);
182 
183 	// make 0 based
184 	rotation--;
185 
186 	{
187 		// SRF_3D|SRF_3DGE being enabled at the same time doesn't HURT in the current sprite angle implementation, but it DOES mean more to check in some of the helper functions. Let's not allow this scenario to happen.
188 		UINT8 threedrot = (rotation > 7) ? SRF_3DGE : (sprtemp[frame].rotate & SRF_3DMASK);
189 		if (!threedrot)
190 			threedrot = SRF_3D;
191 
192 		if (rotation == 0 || rotation == 4) // Front or back...
193 			sprtemp[frame].rotate = threedrot; // Prevent L and R changeover
194 		else if ((rotation & 7) > 3) // Right side
195 			sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_LEFT)); // Continue allowing L frame changeover
196 		else // if ((rotation & 7) <= 3) // Left side
197 			sprtemp[frame].rotate = (threedrot | (sprtemp[frame].rotate & SRF_RIGHT)); // Continue allowing R frame changeover
198 	}
199 
200 	if (sprtemp[frame].lumppat[rotation] != LUMPERROR)
201 		CONS_Debug(DBG_SETUP, "R_InitSprites: Sprite %s: %c%c has two lumps mapped to it\n", spritename, cn, cr);
202 
203 	// lumppat & lumpid are the same for original Doom, but different
204 	// when using sprites in pwad : the lumppat points the new graphics
205 	sprtemp[frame].lumppat[rotation] = lumppat;
206 	sprtemp[frame].lumpid[rotation] = lumpid;
207 	if (flipped)
208 		sprtemp[frame].flip |= (1<<rotation);
209 	else
210 		sprtemp[frame].flip &= ~(1<<rotation);
211 }
212 
213 // Install a single sprite, given its identifying name (4 chars)
214 //
215 // (originally part of R_AddSpriteDefs)
216 //
217 // Pass: name of sprite : 4 chars
218 //       spritedef_t
219 //       wadnum         : wad number, indexes wadfiles[], where patches
220 //                        for frames are found
221 //       startlump      : first lump to search for sprite frames
222 //       endlump        : AFTER the last lump to search
223 //
224 // Returns true if the sprite was succesfully added
225 //
R_AddSingleSpriteDef(const char * sprname,spritedef_t * spritedef,UINT16 wadnum,UINT16 startlump,UINT16 endlump)226 boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 wadnum, UINT16 startlump, UINT16 endlump)
227 {
228 	UINT16 l;
229 	UINT8 frame;
230 	UINT8 rotation;
231 	lumpinfo_t *lumpinfo;
232 	softwarepatch_t patch;
233 	UINT16 numadded = 0;
234 
235 	memset(sprtemp,0xFF, sizeof (sprtemp));
236 	maxframe = (size_t)-1;
237 
238 	spritename = sprname;
239 
240 	// are we 'patching' a sprite already loaded ?
241 	// if so, it might patch only certain frames, not all
242 	if (spritedef->numframes) // (then spriteframes is not null)
243 	{
244 		// copy the already defined sprite frames
245 		M_Memcpy(sprtemp, spritedef->spriteframes,
246 		 spritedef->numframes * sizeof (spriteframe_t));
247 		maxframe = spritedef->numframes - 1;
248 	}
249 
250 	// scan the lumps,
251 	//  filling in the frames for whatever is found
252 	lumpinfo = wadfiles[wadnum]->lumpinfo;
253 	if (endlump > wadfiles[wadnum]->numlumps)
254 		endlump = wadfiles[wadnum]->numlumps;
255 
256 	for (l = startlump; l < endlump; l++)
257 	{
258 		if (memcmp(lumpinfo[l].name,sprname,4)==0)
259 		{
260 			INT32 width, height;
261 			INT16 topoffset, leftoffset;
262 #ifndef NO_PNG_LUMPS
263 			boolean isPNG = false;
264 #endif
265 
266 			frame = R_Char2Frame(lumpinfo[l].name[4]);
267 			rotation = R_Char2Rotation(lumpinfo[l].name[5]);
268 
269 			if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-...
270 			{
271 				CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l));
272 				continue;
273 			}
274 
275 			// skip NULL sprites from very old dmadds pwads
276 			if (W_LumpLengthPwad(wadnum,l)<=8)
277 				continue;
278 
279 			// store sprite info in lookup tables
280 			//FIXME : numspritelumps do not duplicate sprite replacements
281 
282 #ifndef NO_PNG_LUMPS
283 			{
284 				softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
285 				size_t len = W_LumpLengthPwad(wadnum, l);
286 
287 				if (Picture_IsLumpPNG((UINT8 *)png, len))
288 				{
289 					Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len);
290 					isPNG = true;
291 				}
292 
293 				Z_Free(png);
294 			}
295 
296 			if (!isPNG)
297 #endif
298 			{
299 				W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof(INT16) * 4, 0);
300 				width = (INT32)(SHORT(patch.width));
301 				height = (INT32)(SHORT(patch.height));
302 				topoffset = (INT16)(SHORT(patch.topoffset));
303 				leftoffset = (INT16)(SHORT(patch.leftoffset));
304 			}
305 
306 			spritecachedinfo[numspritelumps].width = width<<FRACBITS;
307 			spritecachedinfo[numspritelumps].offset = leftoffset<<FRACBITS;
308 			spritecachedinfo[numspritelumps].topoffset = topoffset<<FRACBITS;
309 			spritecachedinfo[numspritelumps].height = height<<FRACBITS;
310 
311 			// BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
312 			spritecachedinfo[numspritelumps].topoffset += FEETADJUST;
313 
314 			//----------------------------------------------------
315 
316 			R_InstallSpriteLump(wadnum, l, numspritelumps, frame, rotation, 0);
317 
318 			if (lumpinfo[l].name[6])
319 			{
320 				frame = R_Char2Frame(lumpinfo[l].name[6]);
321 				rotation = R_Char2Rotation(lumpinfo[l].name[7]);
322 
323 				if (frame >= 64 || rotation == 255) // Give an actual NAME error -_-...
324 				{
325 					CONS_Alert(CONS_WARNING, M_GetText("Bad sprite name: %s\n"), W_CheckNameForNumPwad(wadnum,l));
326 					continue;
327 				}
328 				R_InstallSpriteLump(wadnum, l, numspritelumps, frame, rotation, 1);
329 			}
330 
331 			if (++numspritelumps >= max_spritelumps)
332 			{
333 				max_spritelumps *= 2;
334 				Z_Realloc(spritecachedinfo, max_spritelumps*sizeof(*spritecachedinfo), PU_STATIC, &spritecachedinfo);
335 			}
336 
337 			++numadded;
338 		}
339 	}
340 
341 	//
342 	// if no frames found for this sprite
343 	//
344 	if (maxframe == (size_t)-1)
345 	{
346 		// the first time (which is for the original wad),
347 		// all sprites should have their initial frames
348 		// and then, patch wads can replace it
349 		// we will skip non-replaced sprite frames, only if
350 		// they have already have been initially defined (original wad)
351 
352 		//check only after all initial pwads added
353 		//if (spritedef->numframes == 0)
354 		//    I_Error("R_AddSpriteDefs: no initial frames found for sprite %s\n",
355 		//             namelist[i]);
356 
357 		// sprite already has frames, and is not replaced by this wad
358 		return false;
359 	}
360 	else if (!numadded)
361 	{
362 		// Nothing related to this spritedef has been changed
363 		// so there is no point going back through these checks again.
364 		return false;
365 	}
366 
367 	maxframe++;
368 
369 	//
370 	//  some checks to help development
371 	//
372 	for (frame = 0; frame < maxframe; frame++)
373 	{
374 		switch (sprtemp[frame].rotate)
375 		{
376 			case SRF_NONE:
377 			// no rotations were found for that frame at all
378 			I_Error("R_AddSingleSpriteDef: No patches found for %.4s frame %c", sprname, R_Frame2Char(frame));
379 			break;
380 
381 			case SRF_SINGLE:
382 			// only the first rotation is needed
383 			break;
384 
385 			case SRF_2D: // both Left and Right rotations
386 			// we test to see whether the left and right slots are present
387 			if ((sprtemp[frame].lumppat[2] == LUMPERROR) || (sprtemp[frame].lumppat[6] == LUMPERROR))
388 				I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (L-R mode)",
389 				sprname, R_Frame2Char(frame));
390 			break;
391 
392 			default:
393 			// must have all 8/16 frames
394 				rotation = ((sprtemp[frame].rotate & SRF_3DGE) ? 16 : 8);
395 				while (rotation--)
396 				// we test the patch lump, or the id lump whatever
397 				// if it was not loaded the two are LUMPERROR
398 				if (sprtemp[frame].lumppat[rotation] == LUMPERROR)
399 					I_Error("R_AddSingleSpriteDef: Sprite %.4s frame %c is missing rotations (1-%c mode)",
400 					        sprname, R_Frame2Char(frame), ((sprtemp[frame].rotate & SRF_3DGE) ? 'G' : '8'));
401 			break;
402 		}
403 	}
404 
405 	// allocate space for the frames present and copy sprtemp to it
406 	if (spritedef->numframes &&             // has been allocated
407 		spritedef->numframes < maxframe)   // more frames are defined ?
408 	{
409 		Z_Free(spritedef->spriteframes);
410 		spritedef->spriteframes = NULL;
411 	}
412 
413 	// allocate this sprite's frames
414 	if (!spritedef->spriteframes)
415 		spritedef->spriteframes =
416 		 Z_Malloc(maxframe * sizeof (*spritedef->spriteframes), PU_STATIC, NULL);
417 
418 	spritedef->numframes = maxframe;
419 	M_Memcpy(spritedef->spriteframes, sprtemp, maxframe*sizeof (spriteframe_t));
420 
421 	return true;
422 }
423 
424 //
425 // Search for sprites replacements in a wad whose names are in namelist
426 //
R_AddSpriteDefs(UINT16 wadnum)427 void R_AddSpriteDefs(UINT16 wadnum)
428 {
429 	size_t i, addsprites = 0;
430 	UINT16 start, end;
431 	char wadname[MAX_WADPATH];
432 
433 	// Find the sprites section in this resource file.
434 	switch (wadfiles[wadnum]->type)
435 	{
436 	case RET_WAD:
437 		start = W_CheckNumForMarkerStartPwad("S_START", wadnum, 0);
438 		if (start == INT16_MAX)
439 			start = W_CheckNumForMarkerStartPwad("SS_START", wadnum, 0); //deutex compatib.
440 
441 		end = W_CheckNumForNamePwad("S_END",wadnum,start);
442 		if (end == INT16_MAX)
443 			end = W_CheckNumForNamePwad("SS_END",wadnum,start);     //deutex compatib.
444 		break;
445 	case RET_PK3:
446 		start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0);
447 		end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start);
448 		break;
449 	default:
450 		return;
451 	}
452 
453 	if (start == INT16_MAX)
454 	{
455 		// ignore skin wads (we don't want skin sprites interfering with vanilla sprites)
456 		if (W_CheckNumForNamePwad("S_SKIN", wadnum, 0) != UINT16_MAX)
457 			return;
458 
459 		start = 0; //let say S_START is lump 0
460 	}
461 
462 	if (end == INT16_MAX || start >= end)
463 	{
464 		CONS_Debug(DBG_SETUP, "no sprites in pwad %d\n", wadnum);
465 		return;
466 	}
467 
468 
469 	//
470 	// scan through lumps, for each sprite, find all the sprite frames
471 	//
472 	for (i = 0; i < numsprites; i++)
473 	{
474 		if (sprnames[i][4] && wadnum >= (UINT16)sprnames[i][4])
475 			continue;
476 
477 		if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end))
478 		{
479 #ifdef HWRENDER
480 			if (rendermode == render_opengl)
481 				HWR_AddSpriteModel(i);
482 #endif
483 			// if a new sprite was added (not just replaced)
484 			addsprites++;
485 #ifndef ZDEBUG
486 			CONS_Debug(DBG_SETUP, "sprite %s set in pwad %d\n", sprnames[i], wadnum);
487 #endif
488 		}
489 	}
490 
491 	nameonly(strcpy(wadname, wadfiles[wadnum]->filename));
492 	CONS_Printf(M_GetText("%s added %d frames in %s sprites\n"), wadname, end-start, sizeu1(addsprites));
493 }
494 
495 //
496 // GAME FUNCTIONS
497 //
498 UINT32 visspritecount;
499 static UINT32 clippedvissprites;
500 static vissprite_t *visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL};
501 
502 //
503 // R_InitSprites
504 // Called at program start.
505 //
R_InitSprites(void)506 void R_InitSprites(void)
507 {
508 	size_t i;
509 #ifdef ROTSPRITE
510 	INT32 angle;
511 	float fa;
512 #endif
513 
514 	for (i = 0; i < MAXVIDWIDTH; i++)
515 		negonearray[i] = -1;
516 
517 #ifdef ROTSPRITE
518 	for (angle = 1; angle < ROTANGLES; angle++)
519 	{
520 		fa = ANG2RAD(FixedAngle((ROTANGDIFF * angle)<<FRACBITS));
521 		rollcosang[angle] = FLOAT_TO_FIXED(cos(-fa));
522 		rollsinang[angle] = FLOAT_TO_FIXED(sin(-fa));
523 	}
524 #endif
525 
526 	//
527 	// count the number of sprite names, and allocate sprites table
528 	//
529 	numsprites = 0;
530 	for (i = 0; i < NUMSPRITES + 1; i++)
531 		if (sprnames[i][0] != '\0') numsprites++;
532 
533 	if (!numsprites)
534 		I_Error("R_AddSpriteDefs: no sprites in namelist\n");
535 
536 	sprites = Z_Calloc(numsprites * sizeof (*sprites), PU_STATIC, NULL);
537 
538 	// find sprites in each -file added pwad
539 	for (i = 0; i < numwadfiles; i++)
540 		R_AddSpriteDefs((UINT16)i);
541 
542 	//
543 	// now check for skins
544 	//
545 
546 	// it can be is do before loading config for skin cvar possible value
547 	R_InitSkins();
548 	for (i = 0; i < numwadfiles; i++)
549 	{
550 		R_AddSkins((UINT16)i);
551 		R_PatchSkins((UINT16)i);
552 		R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
553 	}
554 	ST_ReloadSkinFaceGraphics();
555 
556 	//
557 	// check if all sprites have frames
558 	//
559 	/*
560 	for (i = 0; i < numsprites; i++)
561 		if (sprites[i].numframes < 1)
562 			CONS_Debug(DBG_SETUP, "R_InitSprites: sprite %s has no frames at all\n", sprnames[i]);
563 	*/
564 }
565 
566 //
567 // R_ClearSprites
568 // Called at frame start.
569 //
R_ClearSprites(void)570 void R_ClearSprites(void)
571 {
572 	visspritecount = clippedvissprites = 0;
573 }
574 
575 //
576 // R_NewVisSprite
577 //
578 static vissprite_t overflowsprite;
579 
R_GetVisSprite(UINT32 num)580 static vissprite_t *R_GetVisSprite(UINT32 num)
581 {
582 		UINT32 chunk = num >> VISSPRITECHUNKBITS;
583 
584 		// Allocate chunk if necessary
585 		if (!visspritechunks[chunk])
586 			Z_Malloc(sizeof(vissprite_t) * VISSPRITESPERCHUNK, PU_LEVEL, &visspritechunks[chunk]);
587 
588 		return visspritechunks[chunk] + (num & VISSPRITEINDEXMASK);
589 }
590 
R_NewVisSprite(void)591 static vissprite_t *R_NewVisSprite(void)
592 {
593 	if (visspritecount == MAXVISSPRITES)
594 		return &overflowsprite;
595 
596 	return R_GetVisSprite(visspritecount++);
597 }
598 
599 //
600 // R_DrawMaskedColumn
601 // Used for sprites and masked mid textures.
602 // Masked means: partly transparent, i.e. stored
603 //  in posts/runs of opaque pixels.
604 //
605 INT16 *mfloorclip;
606 INT16 *mceilingclip;
607 
608 fixed_t spryscale = 0, sprtopscreen = 0, sprbotscreen = 0;
609 fixed_t windowtop = 0, windowbottom = 0;
610 
R_DrawMaskedColumn(column_t * column)611 void R_DrawMaskedColumn(column_t *column)
612 {
613 	INT32 topscreen;
614 	INT32 bottomscreen;
615 	fixed_t basetexturemid;
616 	INT32 topdelta, prevdelta = 0;
617 
618 	basetexturemid = dc_texturemid;
619 
620 	for (; column->topdelta != 0xff ;)
621 	{
622 		// calculate unclipped screen coordinates
623 		// for post
624 		topdelta = column->topdelta;
625 		if (topdelta <= prevdelta)
626 			topdelta += prevdelta;
627 		prevdelta = topdelta;
628 		topscreen = sprtopscreen + spryscale*topdelta;
629 		bottomscreen = topscreen + spryscale*column->length;
630 
631 		dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
632 		dc_yh = (bottomscreen-1)>>FRACBITS;
633 
634 		if (windowtop != INT32_MAX && windowbottom != INT32_MAX)
635 		{
636 			if (windowtop > topscreen)
637 				dc_yl = (windowtop + FRACUNIT - 1)>>FRACBITS;
638 			if (windowbottom < bottomscreen)
639 				dc_yh = (windowbottom - 1)>>FRACBITS;
640 		}
641 
642 		if (dc_yh >= mfloorclip[dc_x])
643 			dc_yh = mfloorclip[dc_x]-1;
644 		if (dc_yl <= mceilingclip[dc_x])
645 			dc_yl = mceilingclip[dc_x]+1;
646 		if (dc_yl < 0)
647 			dc_yl = 0;
648 		if (dc_yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
649 			dc_yh = vid.height - 1;
650 
651 		if (dc_yl <= dc_yh && dc_yh > 0)
652 		{
653 			dc_source = (UINT8 *)column + 3;
654 			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
655 
656 			// Drawn by R_DrawColumn.
657 			// This stuff is a likely cause of the splitscreen water crash bug.
658 			// FIXTHIS: Figure out what "something more proper" is and do it.
659 			// quick fix... something more proper should be done!!!
660 			if (ylookup[dc_yl])
661 				colfunc();
662 #ifdef PARANOIA
663 			else
664 				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
665 #endif
666 		}
667 		column = (column_t *)((UINT8 *)column + column->length + 4);
668 	}
669 
670 	dc_texturemid = basetexturemid;
671 }
672 
673 INT32 lengthcol; // column->length : for flipped column function pointers and multi-patch on 2sided wall = texture->height
674 
R_DrawFlippedMaskedColumn(column_t * column)675 void R_DrawFlippedMaskedColumn(column_t *column)
676 {
677 	INT32 topscreen;
678 	INT32 bottomscreen;
679 	fixed_t basetexturemid = dc_texturemid;
680 	INT32 topdelta, prevdelta = -1;
681 	UINT8 *d,*s;
682 
683 	for (; column->topdelta != 0xff ;)
684 	{
685 		// calculate unclipped screen coordinates
686 		// for post
687 		topdelta = column->topdelta;
688 		if (topdelta <= prevdelta)
689 			topdelta += prevdelta;
690 		prevdelta = topdelta;
691 		topdelta = lengthcol-column->length-topdelta;
692 		topscreen = sprtopscreen + spryscale*topdelta;
693 		bottomscreen = sprbotscreen == INT32_MAX ? topscreen + spryscale*column->length
694 		                                      : sprbotscreen + spryscale*column->length;
695 
696 		dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS;
697 		dc_yh = (bottomscreen-1)>>FRACBITS;
698 
699 		if (windowtop != INT32_MAX && windowbottom != INT32_MAX)
700 		{
701 			if (windowtop > topscreen)
702 				dc_yl = (windowtop + FRACUNIT - 1)>>FRACBITS;
703 			if (windowbottom < bottomscreen)
704 				dc_yh = (windowbottom - 1)>>FRACBITS;
705 		}
706 
707 		if (dc_yh >= mfloorclip[dc_x])
708 			dc_yh = mfloorclip[dc_x]-1;
709 		if (dc_yl <= mceilingclip[dc_x])
710 			dc_yl = mceilingclip[dc_x]+1;
711 		if (dc_yl < 0)
712 			dc_yl = 0;
713 		if (dc_yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
714 			dc_yh = vid.height - 1;
715 
716 		if (dc_yl <= dc_yh && dc_yh > 0)
717 		{
718 			dc_source = ZZ_Alloc(column->length);
719 			for (s = (UINT8 *)column+2+column->length, d = dc_source; d < dc_source+column->length; --s)
720 				*d++ = *s;
721 			dc_texturemid = basetexturemid - (topdelta<<FRACBITS);
722 
723 			// Still drawn by R_DrawColumn.
724 			if (ylookup[dc_yl])
725 				colfunc();
726 #ifdef PARANOIA
727 			else
728 				I_Error("R_DrawMaskedColumn: Invalid ylookup for dc_yl %d", dc_yl);
729 #endif
730 			Z_Free(dc_source);
731 		}
732 		column = (column_t *)((UINT8 *)column + column->length + 4);
733 	}
734 
735 	dc_texturemid = basetexturemid;
736 }
737 
R_SpriteIsFlashing(vissprite_t * vis)738 boolean R_SpriteIsFlashing(vissprite_t *vis)
739 {
740 	return (!(vis->cut & SC_PRECIP)
741 	&& (vis->mobj->flags & (MF_ENEMY|MF_BOSS))
742 	&& (vis->mobj->flags2 & MF2_FRET)
743 	&& !(vis->mobj->flags & MF_GRENADEBOUNCE)
744 	&& (leveltime & 1));
745 }
746 
R_GetSpriteTranslation(vissprite_t * vis)747 UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
748 {
749 	if (R_SpriteIsFlashing(vis)) // Bosses "flash"
750 	{
751 		if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
752 			return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
753 		else if (vis->mobj->type == MT_METALSONIC_BATTLE)
754 			return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
755 		else
756 			return R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE);
757 	}
758 	else if (vis->mobj->color)
759 	{
760 		// New colormap stuff for skins Tails 06-07-2002
761 		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
762 			return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
763 		else if (!(vis->cut & SC_PRECIP)
764 			&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
765 			&& (vis->mobj->player->charflags & SF_DASHMODE)
766 			&& ((leveltime/2) & 1))
767 		{
768 			if (vis->mobj->player->charflags & SF_MACHINE)
769 				return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
770 			else
771 				return R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
772 		}
773 		else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player!
774 		{
775 			size_t skinnum = (skin_t*)vis->mobj->skin-skins;
776 			return R_GetTranslationColormap((INT32)skinnum, vis->mobj->color, GTC_CACHE);
777 		}
778 		else // Use the defaults
779 			return R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color, GTC_CACHE);
780 	}
781 	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
782 		return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
783 
784 	return NULL;
785 }
786 
787 //
788 // R_DrawVisSprite
789 //  mfloorclip and mceilingclip should also be set.
790 //
R_DrawVisSprite(vissprite_t * vis)791 static void R_DrawVisSprite(vissprite_t *vis)
792 {
793 	column_t *column;
794 	void (*localcolfunc)(column_t *);
795 	INT32 texturecolumn;
796 	INT32 pwidth;
797 	fixed_t frac;
798 	patch_t *patch = vis->patch;
799 	fixed_t this_scale = vis->thingscale;
800 	INT32 x1, x2;
801 	INT64 overflow_test;
802 
803 	if (!patch)
804 		return;
805 
806 	// Check for overflow
807 	overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*vis->scale)>>FRACBITS);
808 	if (overflow_test < 0) overflow_test = -overflow_test;
809 	if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
810 
811 	if (vis->scalestep) // handles right edge too
812 	{
813 		overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*(vis->scale + (vis->scalestep*(vis->x2 - vis->x1))))>>FRACBITS);
814 		if (overflow_test < 0) overflow_test = -overflow_test;
815 		if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
816 	}
817 
818 	colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
819 	dc_colormap = vis->colormap;
820 	dc_translation = R_GetSpriteTranslation(vis);
821 
822 	if (R_SpriteIsFlashing(vis)) // Bosses "flash"
823 		colfunc = colfuncs[COLDRAWFUNC_TRANS]; // translate certain pixels to white
824 	else if (vis->mobj->color && vis->transmap) // Color mapping
825 	{
826 		colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS];
827 		dc_transmap = vis->transmap;
828 	}
829 	else if (vis->transmap)
830 	{
831 		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
832 		dc_transmap = vis->transmap;    //Fab : 29-04-98: translucency table
833 	}
834 	else if (vis->mobj->color) // translate green skin to another color
835 		colfunc = colfuncs[COLDRAWFUNC_TRANS];
836 	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
837 		colfunc = colfuncs[COLDRAWFUNC_TRANS];
838 
839 	if (vis->extra_colormap && !(vis->renderflags & RF_NOCOLORMAPS))
840 	{
841 		if (!dc_colormap)
842 			dc_colormap = vis->extra_colormap->colormap;
843 		else
844 			dc_colormap = &vis->extra_colormap->colormap[dc_colormap - colormaps];
845 	}
846 	if (!dc_colormap)
847 		dc_colormap = colormaps;
848 
849 	dc_texturemid = vis->texturemid;
850 	dc_texheight = 0;
851 
852 	frac = vis->startfrac;
853 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
854 
855 	if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
856 		this_scale = FixedMul(this_scale, ((skin_t *)vis->mobj->skin)->highresscale);
857 	if (this_scale <= 0)
858 		this_scale = 1;
859 	if (this_scale != FRACUNIT)
860 	{
861 		if (!(vis->cut & SC_ISSCALED))
862 		{
863 			vis->scale = FixedMul(vis->scale, this_scale);
864 			vis->scalestep = FixedMul(vis->scalestep, this_scale);
865 			vis->xiscale = FixedDiv(vis->xiscale,this_scale);
866 			vis->cut |= SC_ISSCALED;
867 		}
868 		dc_texturemid = FixedDiv(dc_texturemid,this_scale);
869 	}
870 
871 	spryscale = vis->scale;
872 
873 	if (!(vis->scalestep))
874 	{
875 		sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
876 		sprtopscreen += vis->shear.tan * vis->shear.offset;
877 		dc_iscale = FixedDiv(FRACUNIT, vis->scale);
878 	}
879 
880 	x1 = vis->x1;
881 	x2 = vis->x2;
882 
883 	if (vis->x1 < 0)
884 	{
885 		spryscale += vis->scalestep*(-vis->x1);
886 		vis->x1 = 0;
887 	}
888 
889 	if (vis->x2 >= vid.width)
890 		vis->x2 = vid.width-1;
891 
892 	localcolfunc = (vis->cut & SC_VFLIP) ? R_DrawFlippedMaskedColumn : R_DrawMaskedColumn;
893 	lengthcol = patch->height;
894 
895 	// Split drawing loops for paper and non-paper to reduce conditional checks per sprite
896 	if (vis->scalestep)
897 	{
898 		fixed_t horzscale = FixedMul(vis->spritexscale, this_scale);
899 		fixed_t scalestep = FixedMul(vis->scalestep, vis->spriteyscale);
900 
901 		pwidth = patch->width;
902 
903 		// Papersprite drawing loop
904 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += scalestep)
905 		{
906 			angle_t angle = ((vis->centerangle + xtoviewangle[dc_x]) >> ANGLETOFINESHIFT) & 0xFFF;
907 			texturecolumn = (vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)) / horzscale;
908 
909 			if (texturecolumn < 0 || texturecolumn >= pwidth)
910 				continue;
911 
912 			if (vis->xiscale < 0) // Flipped sprite
913 				texturecolumn = pwidth - 1 - texturecolumn;
914 
915 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
916 			dc_iscale = (0xffffffffu / (unsigned)spryscale);
917 
918 			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
919 
920 			localcolfunc (column);
921 		}
922 	}
923 	else if (vis->cut & SC_SHEAR)
924 	{
925 #ifdef RANGECHECK
926 		pwidth = patch->width;
927 #endif
928 
929 		// Vertically sheared sprite
930 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, dc_texturemid -= vis->shear.tan)
931 		{
932 #ifdef RANGECHECK
933 			texturecolumn = frac>>FRACBITS;
934 			if (texturecolumn < 0 || texturecolumn >= pwidth)
935 				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
936 			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
937 #else
938 			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
939 #endif
940 
941 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
942 			localcolfunc (column);
943 		}
944 	}
945 	else
946 	{
947 #ifdef RANGECHECK
948 		pwidth = patch->width;
949 #endif
950 
951 		// Non-paper drawing loop
952 		for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
953 		{
954 #ifdef RANGECHECK
955 			texturecolumn = frac>>FRACBITS;
956 			if (texturecolumn < 0 || texturecolumn >= pwidth)
957 				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
958 			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
959 #else
960 			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
961 #endif
962 			localcolfunc (column);
963 		}
964 	}
965 
966 	colfunc = colfuncs[BASEDRAWFUNC];
967 	dc_hires = 0;
968 
969 	vis->x1 = x1;
970 	vis->x2 = x2;
971 }
972 
973 // Special precipitation drawer Tails 08-18-2002
R_DrawPrecipitationVisSprite(vissprite_t * vis)974 static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
975 {
976 	column_t *column;
977 #ifdef RANGECHECK
978 	INT32 texturecolumn;
979 #endif
980 	fixed_t frac;
981 	patch_t *patch;
982 	INT64 overflow_test;
983 
984 	//Fab : R_InitSprites now sets a wad lump number
985 	patch = vis->patch;
986 	if (!patch)
987 		return;
988 
989 	// Check for overflow
990 	overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*vis->scale)>>FRACBITS);
991 	if (overflow_test < 0) overflow_test = -overflow_test;
992 	if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
993 
994 	if (vis->transmap)
995 	{
996 		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
997 		dc_transmap = vis->transmap;    //Fab : 29-04-98: translucency table
998 	}
999 
1000 	dc_colormap = colormaps;
1001 
1002 	dc_iscale = FixedDiv(FRACUNIT, vis->scale);
1003 	dc_texturemid = vis->texturemid;
1004 	dc_texheight = 0;
1005 
1006 	frac = vis->startfrac;
1007 	spryscale = vis->scale;
1008 	sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale);
1009 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
1010 
1011 	if (vis->x1 < 0)
1012 		vis->x1 = 0;
1013 
1014 	if (vis->x2 >= vid.width)
1015 		vis->x2 = vid.width-1;
1016 
1017 	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
1018 	{
1019 #ifdef RANGECHECK
1020 		texturecolumn = frac>>FRACBITS;
1021 
1022 		if (texturecolumn < 0 || texturecolumn >= patch->width)
1023 			I_Error("R_DrawPrecipitationSpriteRange: bad texturecolumn");
1024 
1025 		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
1026 #else
1027 		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
1028 #endif
1029 		R_DrawMaskedColumn(column);
1030 	}
1031 
1032 	colfunc = colfuncs[BASEDRAWFUNC];
1033 }
1034 
1035 //
1036 // R_SplitSprite
1037 // runs through a sector's lightlist and Knuckles
R_SplitSprite(vissprite_t * sprite)1038 static void R_SplitSprite(vissprite_t *sprite)
1039 {
1040 	INT32 i, lightnum, lindex;
1041 	INT16 cutfrac;
1042 	sector_t *sector;
1043 	vissprite_t *newsprite;
1044 
1045 	sector = sprite->sector;
1046 
1047 	for (i = 1; i < sector->numlights; i++)
1048 	{
1049 		fixed_t testheight;
1050 
1051 		if (!(sector->lightlist[i].caster->flags & FF_CUTSPRITES))
1052 			continue;
1053 
1054 		testheight = P_GetLightZAt(&sector->lightlist[i], sprite->gx, sprite->gy);
1055 
1056 		if (testheight >= sprite->gzt)
1057 			continue;
1058 		if (testheight <= sprite->gz)
1059 			return;
1060 
1061 		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->sortscale))>>FRACBITS);
1062 		if (cutfrac < 0)
1063 			continue;
1064 		if (cutfrac > viewheight)
1065 			return;
1066 
1067 		// Found a split! Make a new sprite, copy the old sprite to it, and
1068 		// adjust the heights.
1069 		newsprite = M_Memcpy(R_NewVisSprite(), sprite, sizeof (vissprite_t));
1070 
1071 		newsprite->cut |= (sprite->cut & SC_FLAGMASK);
1072 
1073 		sprite->cut |= SC_BOTTOM;
1074 		sprite->gz = testheight;
1075 
1076 		newsprite->gzt = sprite->gz;
1077 
1078 		sprite->sz = cutfrac;
1079 		newsprite->szt = (INT16)(sprite->sz - 1);
1080 
1081 		if (testheight < sprite->pzt && testheight > sprite->pz)
1082 			sprite->pz = newsprite->pzt = testheight;
1083 		else
1084 		{
1085 			newsprite->pz = newsprite->gz;
1086 			newsprite->pzt = newsprite->gzt;
1087 		}
1088 
1089 		newsprite->szt -= 8;
1090 
1091 		newsprite->cut |= SC_TOP;
1092 		if (!(sector->lightlist[i].caster->flags & FF_NOSHADE))
1093 		{
1094 			lightnum = (*sector->lightlist[i].lightlevel >> LIGHTSEGSHIFT);
1095 
1096 			if (lightnum < 0)
1097 				spritelights = scalelight[0];
1098 			else if (lightnum >= LIGHTLEVELS)
1099 				spritelights = scalelight[LIGHTLEVELS-1];
1100 			else
1101 				spritelights = scalelight[lightnum];
1102 
1103 			newsprite->extra_colormap = *sector->lightlist[i].extra_colormap;
1104 
1105 			if (!(newsprite->cut & SC_FULLBRIGHT)
1106 				|| (newsprite->extra_colormap && (newsprite->extra_colormap->flags & CMF_FADEFULLBRIGHTSPRITES)))
1107 			{
1108 				lindex = FixedMul(sprite->xscale, LIGHTRESOLUTIONFIX)>>(LIGHTSCALESHIFT);
1109 
1110 				if (lindex >= MAXLIGHTSCALE)
1111 					lindex = MAXLIGHTSCALE-1;
1112 				newsprite->colormap = spritelights[lindex];
1113 			}
1114 		}
1115 		sprite = newsprite;
1116 	}
1117 }
1118 
1119 //
1120 // R_GetShadowZ(thing, shadowslope)
1121 // Get the first visible floor below the object for shadows
1122 // shadowslope is filled with the floor's slope, if provided
1123 //
R_GetShadowZ(mobj_t * thing,pslope_t ** shadowslope)1124 fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope)
1125 {
1126 	boolean isflipped = thing->eflags & MFE_VERTICALFLIP;
1127 	fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN;
1128 	pslope_t *slope, *groundslope = NULL;
1129 	msecnode_t *node;
1130 	sector_t *sector;
1131 	ffloor_t *rover;
1132 #define CHECKZ (isflipped ? z > thing->z+thing->height/2 && z < groundz : z < thing->z+thing->height/2 && z > groundz)
1133 
1134 	for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next)
1135 	{
1136 		sector = node->m_sector;
1137 
1138 		slope = sector->heightsec != -1 ? NULL : (isflipped ? sector->c_slope : sector->f_slope);
1139 
1140 		if (sector->heightsec != -1)
1141 			z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight;
1142 		else
1143 			z = isflipped ? P_GetSectorCeilingZAt(sector, thing->x, thing->y) : P_GetSectorFloorZAt(sector, thing->x, thing->y);
1144 
1145 		if CHECKZ
1146 		{
1147 			groundz = z;
1148 			groundslope = slope;
1149 		}
1150 
1151 		if (sector->ffloors)
1152 			for (rover = sector->ffloors; rover; rover = rover->next)
1153 			{
1154 				if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE)))
1155 					continue;
1156 
1157 				z = isflipped ? P_GetFFloorBottomZAt(rover, thing->x, thing->y) : P_GetFFloorTopZAt(rover, thing->x, thing->y);
1158 				if CHECKZ
1159 				{
1160 					groundz = z;
1161 					groundslope = isflipped ? *rover->b_slope : *rover->t_slope;
1162 				}
1163 			}
1164 	}
1165 
1166 	if (isflipped ? (thing->ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))
1167 		: (thing->floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))))
1168 	{
1169 		groundz = isflipped ? thing->ceilingz : thing->floorz;
1170 		groundslope = NULL;
1171 	}
1172 
1173 #if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7.
1174 	// NOTE: this section was not updated to reflect reverse gravity support
1175 	// Check polyobjects and see if groundz needs to be altered, for rings only because they don't update floorz
1176 	if (thing->type == MT_RING)
1177 	{
1178 		INT32 xl, xh, yl, yh, bx, by;
1179 
1180 		xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
1181 		xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
1182 		yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
1183 		yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
1184 
1185 		BMBOUNDFIX(xl, xh, yl, yh);
1186 
1187 		validcount++;
1188 
1189 		for (by = yl; by <= yh; by++)
1190 			for (bx = xl; bx <= xh; bx++)
1191 			{
1192 				INT32 offset;
1193 				polymaplink_t *plink; // haleyjd 02/22/06
1194 
1195 				if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
1196 					continue;
1197 
1198 				offset = by*bmapwidth + bx;
1199 
1200 				// haleyjd 02/22/06: consider polyobject lines
1201 				plink = polyblocklinks[offset];
1202 
1203 				while (plink)
1204 				{
1205 					polyobj_t *po = plink->po;
1206 
1207 					if (po->validcount != validcount) // if polyobj hasn't been checked
1208 					{
1209 						po->validcount = validcount;
1210 
1211 						if (!P_MobjInsidePolyobj(po, thing) || !(po->flags & POF_RENDERPLANES))
1212 						{
1213 							plink = (polymaplink_t *)(plink->link.next);
1214 							continue;
1215 						}
1216 
1217 						// We're inside it! Yess...
1218 						z = po->lines[0]->backsector->ceilingheight;
1219 
1220 						if (z < thing->z+thing->height/2 && z > groundz)
1221 						{
1222 							groundz = z;
1223 							groundslope = NULL;
1224 						}
1225 					}
1226 					plink = (polymaplink_t *)(plink->link.next);
1227 				}
1228 			}
1229 	}
1230 #endif
1231 
1232 	if (shadowslope != NULL)
1233 		*shadowslope = groundslope;
1234 
1235 	return groundz;
1236 #undef CHECKZ
1237 }
1238 
1239 static void R_SkewShadowSprite(
1240 			mobj_t *thing, pslope_t *groundslope,
1241 			fixed_t groundz, INT32 spriteheight, fixed_t scalemul,
1242 			fixed_t *shadowyscale, fixed_t *shadowskew)
1243 {
1244 	// haha let's try some dumb stuff
1245 	fixed_t xslope, zslope;
1246 	angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT;
1247 
1248 	xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta);
1249 	zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta);
1250 
1251 	//CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope);
1252 
1253 	if (viewz < groundz)
1254 		*shadowyscale += FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
1255 	else
1256 		*shadowyscale -= FixedMul(FixedMul(thing->radius*2 / spriteheight, scalemul), zslope);
1257 
1258 	*shadowyscale = abs((*shadowyscale));
1259 	*shadowskew = xslope;
1260 }
1261 
1262 static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz)
1263 {
1264 	vissprite_t *shadow;
1265 	patch_t *patch;
1266 	fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2;
1267 	INT32 light = 0;
1268 	fixed_t scalemul; UINT8 trans;
1269 	fixed_t floordiff;
1270 	fixed_t groundz;
1271 	pslope_t *groundslope;
1272 	boolean isflipped = thing->eflags & MFE_VERTICALFLIP;
1273 
1274 	groundz = R_GetShadowZ(thing, &groundslope);
1275 
1276 	if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes
1277 
1278 	floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz);
1279 
1280 	trans = floordiff / (100*FRACUNIT) + 3;
1281 	if (trans >= 9) return;
1282 
1283 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
1284 
1285 	patch = W_CachePatchName("DSHADOW", PU_SPRITE);
1286 	xscale = FixedDiv(projection, tz);
1287 	yscale = FixedDiv(projectiony, tz);
1288 	shadowxscale = FixedMul(thing->radius*2, scalemul);
1289 	shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz));
1290 	shadowyscale = min(shadowyscale, shadowxscale) / patch->height;
1291 	shadowxscale /= patch->width;
1292 	shadowskew = 0;
1293 
1294 	if (groundslope)
1295 		R_SkewShadowSprite(thing, groundslope, groundz, patch->height, scalemul, &shadowyscale, &shadowskew);
1296 
1297 	tx -= patch->width * shadowxscale/2;
1298 	x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
1299 	if (x1 >= viewwidth) return;
1300 
1301 	tx += patch->width * shadowxscale;
1302 	x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
1303 	if (x2 < 0 || x2 <= x1) return;
1304 
1305 	if (shadowyscale < FRACUNIT/patch->height) return; // fix some crashes?
1306 
1307 	shadow = R_NewVisSprite();
1308 	shadow->patch = patch;
1309 	shadow->heightsec = vis->heightsec;
1310 
1311 	shadow->thingheight = FRACUNIT;
1312 	shadow->pz = groundz + (isflipped ? -shadow->thingheight : 0);
1313 	shadow->pzt = shadow->pz + shadow->thingheight;
1314 
1315 	shadow->mobjflags = 0;
1316 	shadow->sortscale = vis->sortscale;
1317 	shadow->dispoffset = vis->dispoffset - 5;
1318 	shadow->gx = thing->x;
1319 	shadow->gy = thing->y;
1320 	shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + patch->height * shadowyscale / 2;
1321 	shadow->gz = shadow->gzt - patch->height * shadowyscale;
1322 	shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale));
1323 	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
1324 		shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale);
1325 	shadow->scalestep = 0;
1326 	shadow->shear.tan = shadowskew; // repurposed variable
1327 
1328 	shadow->mobj = thing; // Easy access! Tails 06-07-2002
1329 
1330 	shadow->x1 = x1 < portalclipstart ? portalclipstart : x1;
1331 	shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
1332 
1333 	shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000
1334 	shadow->scale = FixedMul(yscale, shadowyscale);
1335 	shadow->thingscale = thing->scale;
1336 	shadow->sector = vis->sector;
1337 	shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS);
1338 	shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS);
1339 	shadow->cut = SC_ISSCALED|SC_SHADOW; //check this
1340 
1341 	shadow->startfrac = 0;
1342 	//shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2);
1343 	shadow->xiscale = (patch->width<<FRACBITS)/(x2-x1+1); // fuck it
1344 
1345 	if (shadow->x1 > x1)
1346 		shadow->startfrac += shadow->xiscale*(shadow->x1-x1);
1347 
1348 	// reusing x1 variable
1349 	x1 += (x2-x1)/2;
1350 	shadow->shear.offset = shadow->x1-x1;
1351 
1352 	if (thing->renderflags & RF_NOCOLORMAPS)
1353 		shadow->extra_colormap = NULL;
1354 	else
1355 	{
1356 		if (thing->subsector->sector->numlights)
1357 		{
1358 			INT32 lightnum;
1359 			light = thing->subsector->sector->numlights - 1;
1360 
1361 			// R_GetPlaneLight won't work on sloped lights!
1362 			for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
1363 				fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], thing->x, thing->y);
1364 				if (h <= shadow->gzt) {
1365 					light = lightnum - 1;
1366 					break;
1367 				}
1368 			}
1369 		}
1370 
1371 		if (thing->subsector->sector->numlights)
1372 			shadow->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
1373 		else
1374 			shadow->extra_colormap = thing->subsector->sector->extra_colormap;
1375 	}
1376 
1377 	shadow->transmap = R_GetTranslucencyTable(trans + 1);
1378 	shadow->colormap = scalelight[0][0]; // full dark!
1379 
1380 	objectsdrawn++;
1381 }
1382 
1383 //
1384 // R_ProjectSprite
1385 // Generates a vissprite for a thing
1386 // if it might be visible.
1387 //
1388 static void R_ProjectSprite(mobj_t *thing)
1389 {
1390 	mobj_t *oldthing = thing;
1391 	fixed_t tr_x, tr_y;
1392 	fixed_t tx, tz;
1393 	fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
1394 	fixed_t sortscale, sortsplat = 0;
1395 	fixed_t sort_x = 0, sort_y = 0, sort_z;
1396 
1397 	INT32 x1, x2;
1398 
1399 	spritedef_t *sprdef;
1400 	spriteframe_t *sprframe;
1401 #ifdef ROTSPRITE
1402 	spriteinfo_t *sprinfo;
1403 #endif
1404 	size_t lump;
1405 
1406 	size_t frame, rot;
1407 	UINT16 flip;
1408 	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
1409 	boolean mirrored = thing->mirrored;
1410 	boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
1411 
1412 	INT32 lindex;
1413 	INT32 trans;
1414 
1415 	vissprite_t *vis;
1416 	patch_t *patch;
1417 
1418 	spritecut_e cut = SC_NONE;
1419 
1420 	angle_t ang = 0; // compiler complaints
1421 	fixed_t iscale;
1422 	fixed_t scalestep;
1423 	fixed_t offset, offset2;
1424 
1425 	fixed_t sheartan = 0;
1426 	fixed_t shadowscale = FRACUNIT;
1427 	fixed_t basetx, basetz; // drop shadows
1428 
1429 	boolean shadowdraw, shadoweffects, shadowskew;
1430 	boolean splat = R_ThingIsFloorSprite(thing);
1431 	boolean papersprite = (R_ThingIsPaperSprite(thing) && !splat);
1432 	fixed_t paperoffset = 0, paperdistance = 0;
1433 	angle_t centerangle = 0;
1434 
1435 	INT32 dispoffset = thing->info->dispoffset;
1436 
1437 	//SoM: 3/17/2000
1438 	fixed_t gz = 0, gzt = 0;
1439 	INT32 heightsec, phs;
1440 	INT32 light = 0;
1441 	fixed_t this_scale = thing->scale;
1442 	fixed_t spritexscale, spriteyscale;
1443 
1444 	// rotsprite
1445 	fixed_t spr_width, spr_height;
1446 	fixed_t spr_offset, spr_topoffset;
1447 
1448 #ifdef ROTSPRITE
1449 	patch_t *rotsprite = NULL;
1450 	INT32 rollangle = 0;
1451 #endif
1452 
1453 	// transform the origin point
1454 	tr_x = thing->x - viewx;
1455 	tr_y = thing->y - viewy;
1456 
1457 	basetz = tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance
1458 
1459 	// thing is behind view plane?
1460 	if (!papersprite && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later
1461 		return;
1462 
1463 	basetx = tx = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos); // sideways distance
1464 
1465 	// too far off the side?
1466 	if (!papersprite && abs(tx) > FixedMul(tz, fovtan)<<2) // papersprite clipping is handled later
1467 		return;
1468 
1469 	// aspect ratio stuff
1470 	xscale = FixedDiv(projection, tz);
1471 	sortscale = FixedDiv(projectiony, tz);
1472 
1473 	// decide which patch to use for sprite relative to player
1474 #ifdef RANGECHECK
1475 	if ((size_t)(thing->sprite) >= numsprites)
1476 		I_Error("R_ProjectSprite: invalid sprite number %d ", thing->sprite);
1477 #endif
1478 
1479 	frame = thing->frame&FF_FRAMEMASK;
1480 
1481 	//Fab : 02-08-98: 'skin' override spritedef currently used for skin
1482 	if (thing->skin && thing->sprite == SPR_PLAY)
1483 	{
1484 		sprdef = &((skin_t *)thing->skin)->sprites[thing->sprite2];
1485 #ifdef ROTSPRITE
1486 		sprinfo = &((skin_t *)thing->skin)->sprinfo[thing->sprite2];
1487 #endif
1488 		if (frame >= sprdef->numframes) {
1489 			CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid skins[\"%s\"].sprites[%sSPR2_%s] frame %s\n"), ((skin_t *)thing->skin)->name, ((thing->sprite2 & FF_SPR2SUPER) ? "FF_SPR2SUPER|": ""), spr2names[(thing->sprite2 & ~FF_SPR2SUPER)], sizeu5(frame));
1490 			thing->sprite = states[S_UNKNOWN].sprite;
1491 			thing->frame = states[S_UNKNOWN].frame;
1492 			sprdef = &sprites[thing->sprite];
1493 #ifdef ROTSPRITE
1494 			sprinfo = &spriteinfo[thing->sprite];
1495 #endif
1496 			frame = thing->frame&FF_FRAMEMASK;
1497 		}
1498 	}
1499 	else
1500 	{
1501 		sprdef = &sprites[thing->sprite];
1502 #ifdef ROTSPRITE
1503 		sprinfo = &spriteinfo[thing->sprite];
1504 #endif
1505 
1506 		if (frame >= sprdef->numframes)
1507 		{
1508 			CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
1509 				sizeu1(frame), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
1510 			if (thing->sprite == thing->state->sprite && thing->frame == thing->state->frame)
1511 			{
1512 				thing->state->sprite = states[S_UNKNOWN].sprite;
1513 				thing->state->frame = states[S_UNKNOWN].frame;
1514 			}
1515 			thing->sprite = states[S_UNKNOWN].sprite;
1516 			thing->frame = states[S_UNKNOWN].frame;
1517 			sprdef = &sprites[thing->sprite];
1518 			sprinfo = &spriteinfo[thing->sprite];
1519 			frame = thing->frame&FF_FRAMEMASK;
1520 		}
1521 	}
1522 
1523 	sprframe = &sprdef->spriteframes[frame];
1524 
1525 #ifdef PARANOIA
1526 	if (!sprframe)
1527 		I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
1528 #endif
1529 
1530 	if (sprframe->rotate != SRF_SINGLE || papersprite)
1531 	{
1532 		ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle);
1533 		if (mirrored)
1534 			ang = InvAngle(ang);
1535 	}
1536 
1537 	if (sprframe->rotate == SRF_SINGLE)
1538 	{
1539 		// use single rotation for all views
1540 		rot = 0;                        //Fab: for vis->patch below
1541 		lump = sprframe->lumpid[0];     //Fab: see note above
1542 		flip = sprframe->flip; 			// Will only be 0 or 0xFFFF
1543 	}
1544 	else
1545 	{
1546 		// choose a different rotation based on player view
1547 		//ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
1548 
1549 		if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
1550 			rot = 6; // F7 slot
1551 		else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
1552 			rot = 2; // F3 slot
1553 		else if (sprframe->rotate & SRF_3DGE) // 16-angle mode
1554 		{
1555 			rot = (ang+ANGLE_180+ANGLE_11hh)>>28;
1556 			rot = ((rot & 1)<<3)|(rot>>1);
1557 		}
1558 		else // Normal behaviour
1559 			rot = (ang+ANGLE_202h)>>29;
1560 
1561 		//Fab: lumpid is the index for spritewidth,spriteoffset... tables
1562 		lump = sprframe->lumpid[rot];
1563 		flip = sprframe->flip & (1<<rot);
1564 	}
1565 
1566 	I_Assert(lump < max_spritelumps);
1567 
1568 	if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES)
1569 		this_scale = FixedMul(this_scale, ((skin_t *)thing->skin)->highresscale);
1570 
1571 	spr_width = spritecachedinfo[lump].width;
1572 	spr_height = spritecachedinfo[lump].height;
1573 	spr_offset = spritecachedinfo[lump].offset;
1574 	spr_topoffset = spritecachedinfo[lump].topoffset;
1575 
1576 	//Fab: lumppat is the lump number of the patch to use, this is different
1577 	//     than lumpid for sprites-in-pwad : the graphics are patched
1578 	patch = W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
1579 
1580 #ifdef ROTSPRITE
1581 	if (thing->rollangle
1582 	&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
1583 	{
1584 		rollangle = R_GetRollAngle(thing->rollangle);
1585 		rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle);
1586 
1587 		if (rotsprite != NULL)
1588 		{
1589 			patch = rotsprite;
1590 			cut |= SC_ISROTATED;
1591 
1592 			spr_width = rotsprite->width << FRACBITS;
1593 			spr_height = rotsprite->height << FRACBITS;
1594 			spr_offset = rotsprite->leftoffset << FRACBITS;
1595 			spr_topoffset = rotsprite->topoffset << FRACBITS;
1596 			spr_topoffset += FEETADJUST;
1597 
1598 			// flip -> rotate, not rotate -> flip
1599 			flip = 0;
1600 		}
1601 	}
1602 #endif
1603 
1604 	flip = !flip != !hflip;
1605 
1606 	// calculate edges of the shape
1607 	spritexscale = thing->spritexscale;
1608 	spriteyscale = thing->spriteyscale;
1609 	if (spritexscale < 1 || spriteyscale < 1)
1610 		return;
1611 
1612 	if (thing->renderflags & RF_ABSOLUTEOFFSETS)
1613 	{
1614 		spr_offset = thing->spritexoffset;
1615 		spr_topoffset = thing->spriteyoffset;
1616 	}
1617 	else
1618 	{
1619 		SINT8 flipoffset = 1;
1620 
1621 		if ((thing->renderflags & RF_FLIPOFFSETS) && flip)
1622 			flipoffset = -1;
1623 
1624 		spr_offset += thing->spritexoffset * flipoffset;
1625 		spr_topoffset += thing->spriteyoffset * flipoffset;
1626 	}
1627 
1628 	if (flip)
1629 		offset = spr_offset - spr_width;
1630 	else
1631 		offset = -spr_offset;
1632 
1633 	offset = FixedMul(offset, FixedMul(spritexscale, this_scale));
1634 	offset2 = FixedMul(spr_width, FixedMul(spritexscale, this_scale));
1635 
1636 	if (papersprite)
1637 	{
1638 		fixed_t xscale2, yscale2, cosmul, sinmul, tx2, tz2;
1639 		INT32 range;
1640 
1641 		if (ang >= ANGLE_180)
1642 		{
1643 			offset *= -1;
1644 			offset2 *= -1;
1645 		}
1646 
1647 		cosmul = FINECOSINE(thing->angle>>ANGLETOFINESHIFT);
1648 		sinmul = FINESINE(thing->angle>>ANGLETOFINESHIFT);
1649 
1650 		tr_x += FixedMul(offset, cosmul);
1651 		tr_y += FixedMul(offset, sinmul);
1652 		tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
1653 
1654 		tx = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
1655 
1656 		// Get paperoffset (offset) and paperoffset (distance)
1657 		paperoffset = -FixedMul(tr_x, cosmul) - FixedMul(tr_y, sinmul);
1658 		paperdistance = -FixedMul(tr_x, sinmul) + FixedMul(tr_y, cosmul);
1659 		if (paperdistance < 0)
1660 		{
1661 			paperoffset = -paperoffset;
1662 			paperdistance = -paperdistance;
1663 		}
1664 		centerangle = viewangle - thing->angle;
1665 
1666 		tr_x += FixedMul(offset2, cosmul);
1667 		tr_y += FixedMul(offset2, sinmul);
1668 		tz2 = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
1669 
1670 		tx2 = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
1671 
1672 		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
1673 			return;
1674 
1675 		// Needs partially clipped
1676 		if (tz < FixedMul(MINZ, this_scale))
1677 		{
1678 			fixed_t div = FixedDiv(tz2-tz, FixedMul(MINZ, this_scale)-tz);
1679 			tx += FixedDiv(tx2-tx, div);
1680 			tz = FixedMul(MINZ, this_scale);
1681 		}
1682 		else if (tz2 < FixedMul(MINZ, this_scale))
1683 		{
1684 			fixed_t div = FixedDiv(tz-tz2, FixedMul(MINZ, this_scale)-tz2);
1685 			tx2 += FixedDiv(tx-tx2, div);
1686 			tz2 = FixedMul(MINZ, this_scale);
1687 		}
1688 
1689 		if (tx2 < -(FixedMul(tz2, fovtan)<<2) || tx > FixedMul(tz, fovtan)<<2) // too far off the side?
1690 			return;
1691 
1692 		yscale = FixedDiv(projectiony, tz);
1693 		xscale = FixedDiv(projection, tz);
1694 
1695 		x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
1696 
1697 		// off the right side?
1698 		if (x1 > viewwidth)
1699 			return;
1700 
1701 		yscale2 = FixedDiv(projectiony, tz2);
1702 		xscale2 = FixedDiv(projection, tz2);
1703 
1704 		x2 = (centerxfrac + FixedMul(tx2,xscale2))>>FRACBITS;
1705 
1706 		// off the left side
1707 		if (x2 < 0)
1708 			return;
1709 
1710 		if ((range = x2 - x1) <= 0)
1711 			return;
1712 
1713 		range++; // fencepost problem
1714 
1715 		scalestep = ((yscale2 - yscale)/range) ?: 1;
1716 		xscale = FixedDiv(range<<FRACBITS, abs(offset2));
1717 
1718 		// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
1719 		// sortscale = max(yscale, yscale2);
1720 		// sortscale = min(yscale, yscale2);
1721 	}
1722 	else
1723 	{
1724 		scalestep = 0;
1725 		yscale = sortscale;
1726 		tx += offset;
1727 		x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
1728 
1729 		// off the right side?
1730 		if (x1 > viewwidth)
1731 			return;
1732 
1733 		tx += offset2;
1734 		x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
1735 
1736 		// off the left side
1737 		if (x2 < 0)
1738 			return;
1739 	}
1740 
1741 	// Adjust the sort scale if needed
1742 	if (splat)
1743 	{
1744 		sort_z = (patch->height - patch->topoffset) * FRACUNIT;
1745 		ang = (viewangle >> ANGLETOFINESHIFT);
1746 		sort_x = FixedMul(FixedMul(FixedMul(spritexscale, this_scale), sort_z), FINECOSINE(ang));
1747 		sort_y = FixedMul(FixedMul(FixedMul(spriteyscale, this_scale), sort_z), FINESINE(ang));
1748 	}
1749 
1750 	if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
1751 	{
1752 		fixed_t linkscale;
1753 
1754 		thing = thing->tracer;
1755 
1756 		if (! R_ThingVisible(thing))
1757 			return;
1758 
1759 		tr_x = (thing->x + sort_x) - viewx;
1760 		tr_y = (thing->y + sort_y) - viewy;
1761 		tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
1762 		linkscale = FixedDiv(projectiony, tz);
1763 
1764 		if (tz < FixedMul(MINZ, this_scale))
1765 			return;
1766 
1767 		if (sortscale < linkscale)
1768 			dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
1769 
1770 		sortscale = linkscale; // now make sure it's linked
1771 		cut |= SC_LINKDRAW;
1772 	}
1773 	else if (splat)
1774 	{
1775 		tr_x = (thing->x + sort_x) - viewx;
1776 		tr_y = (thing->y + sort_y) - viewy;
1777 		sort_z = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
1778 		sortscale = FixedDiv(projectiony, sort_z);
1779 	}
1780 
1781 	// Calculate the splat's sortscale
1782 	if (splat)
1783 	{
1784 		tr_x = (thing->x - sort_x) - viewx;
1785 		tr_y = (thing->y - sort_y) - viewy;
1786 		sort_z = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
1787 		sortsplat = FixedDiv(projectiony, sort_z);
1788 	}
1789 
1790 	// PORTAL SPRITE CLIPPING
1791 	if (portalrender && portalclipline)
1792 	{
1793 		if (x2 < portalclipstart || x1 >= portalclipend)
1794 			return;
1795 
1796 		if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0)
1797 			return;
1798 	}
1799 
1800 	// Determine the translucency value.
1801 	if (oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility)
1802 		trans = tr_trans80; // because now the translucency is set through FF_TRANSMASK
1803 	else if (oldthing->frame & FF_TRANSMASK)
1804 	{
1805 		trans = (oldthing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
1806 		if (!R_BlendLevelVisible(oldthing->blendmode, trans))
1807 			return;
1808 	}
1809 	else
1810 		trans = 0;
1811 
1812 	// Check if this sprite needs to be rendered like a shadow
1813 	shadowdraw = (!!(thing->renderflags & RF_SHADOWDRAW) && !(papersprite || splat));
1814 	shadoweffects = (thing->renderflags & RF_SHADOWEFFECTS);
1815 	shadowskew = (shadowdraw && thing->standingslope);
1816 
1817 	if (shadowdraw || shadoweffects)
1818 	{
1819 		fixed_t groundz = R_GetShadowZ(thing, NULL);
1820 		boolean isflipped = (thing->eflags & MFE_VERTICALFLIP);
1821 
1822 		if (shadoweffects)
1823 		{
1824 			mobj_t *caster = thing->target;
1825 
1826 			if (caster && !P_MobjWasRemoved(caster))
1827 			{
1828 				fixed_t floordiff;
1829 
1830 				if (abs(groundz-viewz)/tz > 4)
1831 					return; // Prevent stretchy shadows and possible crashes
1832 
1833 				floordiff = abs((isflipped ? caster->height : 0) + caster->z - groundz);
1834 				trans += ((floordiff / (100*FRACUNIT)) + 3);
1835 				shadowscale = FixedMul(FRACUNIT - floordiff/640, caster->scale);
1836 			}
1837 			else
1838 				trans += 3;
1839 
1840 			if (trans >= NUMTRANSMAPS)
1841 				return;
1842 
1843 			trans--;
1844 		}
1845 
1846 		if (shadowdraw)
1847 		{
1848 			spritexscale = FixedMul(thing->radius * 2, FixedMul(shadowscale, spritexscale));
1849 			spriteyscale = FixedMul(thing->radius * 2, FixedMul(shadowscale, spriteyscale));
1850 			spriteyscale = FixedMul(spriteyscale, FixedDiv(abs(groundz - viewz), tz));
1851 			spriteyscale = min(spriteyscale, spritexscale) / patch->height;
1852 			spritexscale /= patch->width;
1853 		}
1854 		else
1855 		{
1856 			spritexscale = FixedMul(shadowscale, spritexscale);
1857 			spriteyscale = FixedMul(shadowscale, spriteyscale);
1858 		}
1859 
1860 		if (shadowskew)
1861 		{
1862 			R_SkewShadowSprite(thing, thing->standingslope, groundz, patch->height, shadowscale, &spriteyscale, &sheartan);
1863 
1864 			gzt = (isflipped ? (thing->z + thing->height) : thing->z) + patch->height * spriteyscale / 2;
1865 			gz = gzt - patch->height * spriteyscale;
1866 
1867 			cut |= SC_SHEAR;
1868 		}
1869 	}
1870 
1871 	if (!shadowskew)
1872 	{
1873 		//SoM: 3/17/2000: Disregard sprites that are out of view..
1874 		if (vflip)
1875 		{
1876 			// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
1877 			// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
1878 			// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
1879 			gz = oldthing->z + oldthing->height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
1880 			gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
1881 		}
1882 		else
1883 		{
1884 			gzt = oldthing->z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
1885 			gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
1886 		}
1887 	}
1888 
1889 	if (thing->subsector->sector->cullheight)
1890 	{
1891 		if (R_DoCulling(thing->subsector->sector->cullheight, viewsector->cullheight, viewz, gz, gzt))
1892 			return;
1893 	}
1894 
1895 	if (thing->subsector->sector->numlights)
1896 	{
1897 		INT32 lightnum;
1898 		fixed_t top = (splat) ? gz : gzt;
1899 		light = thing->subsector->sector->numlights - 1;
1900 
1901 		// R_GetPlaneLight won't work on sloped lights!
1902 		for (lightnum = 1; lightnum < thing->subsector->sector->numlights; lightnum++) {
1903 			fixed_t h = P_GetLightZAt(&thing->subsector->sector->lightlist[lightnum], thing->x, thing->y);
1904 			if (h <= top) {
1905 				light = lightnum - 1;
1906 				break;
1907 			}
1908 		}
1909 		//light = R_GetPlaneLight(thing->subsector->sector, gzt, false);
1910 		lightnum = (*thing->subsector->sector->lightlist[light].lightlevel >> LIGHTSEGSHIFT);
1911 
1912 		if (lightnum < 0)
1913 			spritelights = scalelight[0];
1914 		else if (lightnum >= LIGHTLEVELS)
1915 			spritelights = scalelight[LIGHTLEVELS-1];
1916 		else
1917 			spritelights = scalelight[lightnum];
1918 	}
1919 
1920 	heightsec = thing->subsector->sector->heightsec;
1921 	if (viewplayer->mo && viewplayer->mo->subsector)
1922 		phs = viewplayer->mo->subsector->sector->heightsec;
1923 	else
1924 		phs = -1;
1925 
1926 	if (heightsec != -1 && phs != -1) // only clip things which are in special sectors
1927 	{
1928 		if (viewz < sectors[phs].floorheight ?
1929 		thing->z >= sectors[heightsec].floorheight :
1930 		gzt < sectors[heightsec].floorheight)
1931 			return;
1932 		if (viewz > sectors[phs].ceilingheight ?
1933 		gzt < sectors[heightsec].ceilingheight && viewz >= sectors[heightsec].ceilingheight :
1934 		thing->z >= sectors[heightsec].ceilingheight)
1935 			return;
1936 	}
1937 
1938 	// store information in a vissprite
1939 	vis = R_NewVisSprite();
1940 	vis->renderflags = thing->renderflags;
1941 	vis->rotateflags = sprframe->rotate;
1942 	vis->heightsec = heightsec; //SoM: 3/17/2000
1943 	vis->mobjflags = thing->flags;
1944 	vis->sortscale = sortscale;
1945 	vis->sortsplat = sortsplat;
1946 	vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15
1947 	vis->gx = thing->x;
1948 	vis->gy = thing->y;
1949 	vis->gz = gz;
1950 	vis->gzt = gzt;
1951 	vis->thingheight = thing->height;
1952 	vis->pz = thing->z;
1953 	vis->pzt = vis->pz + vis->thingheight;
1954 	vis->texturemid = FixedDiv(gzt - viewz, spriteyscale);
1955 	vis->scalestep = scalestep;
1956 	vis->paperoffset = paperoffset;
1957 	vis->paperdistance = paperdistance;
1958 	vis->centerangle = centerangle;
1959 	vis->viewangle = viewangle;
1960 	vis->shear.tan = sheartan;
1961 	vis->shear.offset = 0;
1962 
1963 	vis->mobj = thing; // Easy access! Tails 06-07-2002
1964 
1965 	vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
1966 	vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
1967 
1968 	vis->sector = thing->subsector->sector;
1969 	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
1970 	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
1971 	vis->cut = cut;
1972 
1973 	if (thing->subsector->sector->numlights)
1974 		vis->extra_colormap = *thing->subsector->sector->lightlist[light].extra_colormap;
1975 	else
1976 		vis->extra_colormap = thing->subsector->sector->extra_colormap;
1977 
1978 	vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000
1979 	vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift;
1980 	vis->thingscale = oldthing->scale;
1981 
1982 	vis->spritexscale = spritexscale;
1983 	vis->spriteyscale = spriteyscale;
1984 	vis->spritexoffset = spr_offset;
1985 	vis->spriteyoffset = spr_topoffset;
1986 
1987 	if (shadowdraw || shadoweffects)
1988 	{
1989 		iscale = (patch->width<<FRACBITS)/(x2-x1+1); // fuck it
1990 		x1 += (x2-x1)/2; // reusing x1 variable
1991 		vis->shear.offset = vis->x1-x1;
1992 	}
1993 	else
1994 		iscale = FixedDiv(FRACUNIT, vis->xscale);
1995 
1996 	vis->shadowscale = shadowscale;
1997 
1998 	if (flip)
1999 	{
2000 		vis->startfrac = spr_width-1;
2001 		vis->xiscale = -iscale;
2002 	}
2003 	else
2004 	{
2005 		vis->startfrac = 0;
2006 		vis->xiscale = iscale;
2007 	}
2008 
2009 	if (vis->x1 > x1)
2010 	{
2011 		vis->startfrac += FixedDiv(vis->xiscale, this_scale) * (vis->x1 - x1);
2012 		vis->scale += FixedMul(scalestep, spriteyscale) * (vis->x1 - x1);
2013 	}
2014 
2015 	if ((oldthing->blendmode != AST_COPY) && cv_translucency.value)
2016 		vis->transmap = R_GetBlendTable(oldthing->blendmode, trans);
2017 	else
2018 		vis->transmap = NULL;
2019 
2020 	if (R_ThingIsFullBright(oldthing) || oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW)
2021 		vis->cut |= SC_FULLBRIGHT;
2022 	else if (R_ThingIsFullDark(oldthing))
2023 		vis->cut |= SC_FULLDARK;
2024 
2025 	//
2026 	// determine the colormap (lightlevel & special effects)
2027 	//
2028 	if (vis->cut & SC_FULLBRIGHT
2029 		&& (!vis->extra_colormap || !(vis->extra_colormap->flags & CMF_FADEFULLBRIGHTSPRITES)))
2030 	{
2031 		// full bright: goggles
2032 		vis->colormap = colormaps;
2033 	}
2034 	else if (vis->cut & SC_FULLDARK)
2035 		vis->colormap = scalelight[0][0];
2036 	else
2037 	{
2038 		// diminished light
2039 		lindex = FixedMul(xscale, LIGHTRESOLUTIONFIX)>>(LIGHTSCALESHIFT);
2040 
2041 		if (lindex >= MAXLIGHTSCALE)
2042 			lindex = MAXLIGHTSCALE-1;
2043 
2044 		vis->colormap = spritelights[lindex];
2045 	}
2046 
2047 	if (vflip)
2048 		vis->cut |= SC_VFLIP;
2049 	if (splat)
2050 		vis->cut |= SC_SPLAT; // I like ya cut g
2051 
2052 	vis->patch = patch;
2053 
2054 	if (thing->subsector->sector->numlights && !(shadowdraw || splat))
2055 		R_SplitSprite(vis);
2056 
2057 	if (oldthing->shadowscale && cv_shadow.value)
2058 		R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz);
2059 
2060 	// Debug
2061 	++objectsdrawn;
2062 }
2063 
2064 static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
2065 {
2066 	fixed_t tr_x, tr_y;
2067 	fixed_t tx, tz;
2068 	fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
2069 
2070 	INT32 x1, x2;
2071 
2072 	spritedef_t *sprdef;
2073 	spriteframe_t *sprframe;
2074 	size_t lump;
2075 
2076 	vissprite_t *vis;
2077 
2078 	fixed_t iscale;
2079 
2080 	//SoM: 3/17/2000
2081 	fixed_t gz, gzt;
2082 
2083 	// transform the origin point
2084 	tr_x = thing->x - viewx;
2085 	tr_y = thing->y - viewy;
2086 
2087 	tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance
2088 
2089 	// thing is behind view plane?
2090 	if (tz < MINZ)
2091 		return;
2092 
2093 	tx = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos); // sideways distance
2094 
2095 	// too far off the side?
2096 	if (abs(tx) > FixedMul(tz, fovtan)<<2)
2097 		return;
2098 
2099 	// aspect ratio stuff :
2100 	xscale = FixedDiv(projection, tz);
2101 	yscale = FixedDiv(projectiony, tz);
2102 
2103 	// decide which patch to use for sprite relative to player
2104 #ifdef RANGECHECK
2105 	if ((unsigned)thing->sprite >= numsprites)
2106 		I_Error("R_ProjectPrecipitationSprite: invalid sprite number %d ",
2107 			thing->sprite);
2108 #endif
2109 
2110 	sprdef = &sprites[thing->sprite];
2111 
2112 #ifdef RANGECHECK
2113 	if ((UINT8)(thing->frame&FF_FRAMEMASK) >= sprdef->numframes)
2114 		I_Error("R_ProjectPrecipitationSprite: invalid sprite frame %d : %d for %s",
2115 			thing->sprite, thing->frame, sprnames[thing->sprite]);
2116 #endif
2117 
2118 	sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK];
2119 
2120 #ifdef PARANOIA
2121 	if (!sprframe)
2122 		I_Error("R_ProjectPrecipitationSprite: sprframes NULL for sprite %d\n", thing->sprite);
2123 #endif
2124 
2125 	// use single rotation for all views
2126 	lump = sprframe->lumpid[0];     //Fab: see note above
2127 
2128 	// calculate edges of the shape
2129 	tx -= spritecachedinfo[lump].offset;
2130 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
2131 
2132 	// off the right side?
2133 	if (x1 > viewwidth)
2134 		return;
2135 
2136 	tx += spritecachedinfo[lump].width;
2137 	x2 = ((centerxfrac + FixedMul (tx,xscale)) >>FRACBITS) - 1;
2138 
2139 	// off the left side
2140 	if (x2 < 0)
2141 		return;
2142 
2143 	// PORTAL SPRITE CLIPPING
2144 	if (portalrender && portalclipline)
2145 	{
2146 		if (x2 < portalclipstart || x1 >= portalclipend)
2147 			return;
2148 
2149 		if (P_PointOnLineSide(thing->x, thing->y, portalclipline) != 0)
2150 			return;
2151 	}
2152 
2153 
2154 	//SoM: 3/17/2000: Disregard sprites that are out of view..
2155 	gzt = thing->z + spritecachedinfo[lump].topoffset;
2156 	gz = gzt - spritecachedinfo[lump].height;
2157 
2158 	if (thing->subsector->sector->cullheight)
2159 	{
2160 		if (R_DoCulling(thing->subsector->sector->cullheight, viewsector->cullheight, viewz, gz, gzt))
2161 			goto weatherthink;
2162 	}
2163 
2164 	// store information in a vissprite
2165 	vis = R_NewVisSprite();
2166 	vis->scale = vis->sortscale = yscale; //<<detailshift;
2167 	vis->dispoffset = 0; // Monster Iestyn: 23/11/15
2168 	vis->gx = thing->x;
2169 	vis->gy = thing->y;
2170 	vis->gz = gz;
2171 	vis->gzt = gzt;
2172 	vis->thingheight = 4*FRACUNIT;
2173 	vis->pz = thing->z;
2174 	vis->pzt = vis->pz + vis->thingheight;
2175 	vis->texturemid = vis->gzt - viewz;
2176 	vis->scalestep = 0;
2177 	vis->paperdistance = 0;
2178 	vis->shear.tan = 0;
2179 	vis->shear.offset = 0;
2180 
2181 	vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
2182 	vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
2183 
2184 	vis->xscale = xscale; //SoM: 4/17/2000
2185 	vis->sector = thing->subsector->sector;
2186 	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS);
2187 	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, yscale))>>FRACBITS);
2188 
2189 	iscale = FixedDiv(FRACUNIT, xscale);
2190 
2191 	vis->startfrac = 0;
2192 	vis->xiscale = iscale;
2193 
2194 	if (vis->x1 > x1)
2195 		vis->startfrac += vis->xiscale*(vis->x1-x1);
2196 
2197 	//Fab: lumppat is the lump number of the patch to use, this is different
2198 	//     than lumpid for sprites-in-pwad : the graphics are patched
2199 	vis->patch = W_CachePatchNum(sprframe->lumppat[0], PU_SPRITE);
2200 
2201 	// specific translucency
2202 	if (thing->frame & FF_TRANSMASK)
2203 		vis->transmap = R_GetTranslucencyTable((thing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT);
2204 	else
2205 		vis->transmap = NULL;
2206 
2207 	vis->mobj = (mobj_t *)thing;
2208 	vis->mobjflags = 0;
2209 	vis->cut = SC_PRECIP;
2210 	vis->extra_colormap = thing->subsector->sector->extra_colormap;
2211 	vis->heightsec = thing->subsector->sector->heightsec;
2212 
2213 	// Fullbright
2214 	vis->colormap = colormaps;
2215 
2216 weatherthink:
2217 	// okay... this is a hack, but weather isn't networked, so it should be ok
2218 	if (!(thing->precipflags & PCF_THUNK))
2219 	{
2220 		if (thing->precipflags & PCF_RAIN)
2221 			P_RainThinker(thing);
2222 		else
2223 			P_SnowThinker(thing);
2224 		thing->precipflags |= PCF_THUNK;
2225 	}
2226 }
2227 
2228 // R_AddSprites
2229 // During BSP traversal, this adds sprites by sector.
2230 //
2231 void R_AddSprites(sector_t *sec, INT32 lightlevel)
2232 {
2233 	mobj_t *thing;
2234 	precipmobj_t *precipthing; // Tails 08-25-2002
2235 	INT32 lightnum;
2236 	fixed_t limit_dist, hoop_limit_dist;
2237 
2238 	if (rendermode != render_soft)
2239 		return;
2240 
2241 	// BSP is traversed by subsector.
2242 	// A sector might have been split into several
2243 	//  subsectors during BSP building.
2244 	// Thus we check whether its already added.
2245 	if (sec->validcount == validcount)
2246 		return;
2247 
2248 	// Well, now it will be done.
2249 	sec->validcount = validcount;
2250 
2251 	if (!sec->numlights)
2252 	{
2253 		if (sec->heightsec == -1) lightlevel = sec->lightlevel;
2254 
2255 		lightnum = (lightlevel >> LIGHTSEGSHIFT);
2256 
2257 		if (lightnum < 0)
2258 			spritelights = scalelight[0];
2259 		else if (lightnum >= LIGHTLEVELS)
2260 			spritelights = scalelight[LIGHTLEVELS-1];
2261 		else
2262 			spritelights = scalelight[lightnum];
2263 	}
2264 
2265 	// Handle all things in sector.
2266 	// If a limit exists, handle things a tiny bit different.
2267 	limit_dist = (fixed_t)(cv_drawdist.value) << FRACBITS;
2268 	hoop_limit_dist = (fixed_t)(cv_drawdist_nights.value) << FRACBITS;
2269 	for (thing = sec->thinglist; thing; thing = thing->snext)
2270 	{
2271 		if (R_ThingVisibleWithinDist(thing, limit_dist, hoop_limit_dist))
2272 			R_ProjectSprite(thing);
2273 	}
2274 
2275 	// no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off
2276 	if ((limit_dist = (fixed_t)cv_drawdist_precip.value << FRACBITS))
2277 	{
2278 		for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext)
2279 		{
2280 			if (R_PrecipThingVisible(precipthing, limit_dist))
2281 				R_ProjectPrecipitationSprite(precipthing);
2282 		}
2283 	}
2284 }
2285 
2286 //
2287 // R_SortVisSprites
2288 //
2289 static void R_SortVisSprites(vissprite_t* vsprsortedhead, UINT32 start, UINT32 end)
2290 {
2291 	UINT32       i, linkedvissprites = 0;
2292 	vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
2293 	vissprite_t *best = NULL;
2294 	vissprite_t  unsorted;
2295 	fixed_t      bestscale;
2296 	INT32        bestdispoffset;
2297 
2298 	unsorted.next = unsorted.prev = &unsorted;
2299 
2300 	dsfirst = R_GetVisSprite(start);
2301 
2302 	// The first's prev and last's next will be set to
2303 	// nonsense, but are fixed in a moment
2304 	for (i = start, dsnext = dsfirst, ds = NULL; i < end; i++)
2305 	{
2306 		dsprev = ds;
2307 		ds = dsnext;
2308 		if (i < end - 1) dsnext = R_GetVisSprite(i + 1);
2309 
2310 		ds->next = dsnext;
2311 		ds->prev = dsprev;
2312 		ds->linkdraw = NULL;
2313 	}
2314 
2315 	// Fix first and last. ds still points to the last one after the loop
2316 	dsfirst->prev = &unsorted;
2317 	unsorted.next = dsfirst;
2318 	if (ds)
2319 	{
2320 		ds->next = &unsorted;
2321 		ds->linkdraw = NULL;
2322 	}
2323 	unsorted.prev = ds;
2324 
2325 	// bundle linkdraw
2326 	for (ds = unsorted.prev; ds != &unsorted; ds = ds->prev)
2327 	{
2328 		if (!(ds->cut & SC_LINKDRAW))
2329 			continue;
2330 
2331 		if (ds->cut & SC_SHADOW)
2332 			continue;
2333 
2334 		// reuse dsfirst...
2335 		for (dsfirst = unsorted.prev; dsfirst != &unsorted; dsfirst = dsfirst->prev)
2336 		{
2337 			// don't connect if it's also a link
2338 			if (dsfirst->cut & SC_LINKDRAW)
2339 				continue;
2340 
2341 			// don't connect to your shadow!
2342 			if (dsfirst->cut & SC_SHADOW)
2343 				continue;
2344 
2345 			// don't connect if it's not the tracer
2346 			if (dsfirst->mobj != ds->mobj)
2347 				continue;
2348 
2349 			// don't connect if the tracer's top is cut off, but lower than the link's top
2350 			if ((dsfirst->cut & SC_TOP)
2351 			&& dsfirst->szt > ds->szt)
2352 				continue;
2353 
2354 			// don't connect if the tracer's bottom is cut off, but higher than the link's bottom
2355 			if ((dsfirst->cut & SC_BOTTOM)
2356 			&& dsfirst->sz < ds->sz)
2357 				continue;
2358 
2359 			break;
2360 		}
2361 
2362 		// remove from chain
2363 		ds->next->prev = ds->prev;
2364 		ds->prev->next = ds->next;
2365 		linkedvissprites++;
2366 
2367 		if (dsfirst != &unsorted)
2368 		{
2369 			if (!(ds->cut & SC_FULLBRIGHT))
2370 				ds->colormap = dsfirst->colormap;
2371 			ds->extra_colormap = dsfirst->extra_colormap;
2372 
2373 			// reusing dsnext...
2374 			dsnext = dsfirst->linkdraw;
2375 
2376 			if (!dsnext || ds->dispoffset < dsnext->dispoffset)
2377 			{
2378 				ds->next = dsnext;
2379 				dsfirst->linkdraw = ds;
2380 			}
2381 			else
2382 			{
2383 				for (; dsnext->next != NULL; dsnext = dsnext->next)
2384 					if (ds->dispoffset < dsnext->next->dispoffset)
2385 						break;
2386 				ds->next = dsnext->next;
2387 				dsnext->next = ds;
2388 			}
2389 		}
2390 	}
2391 
2392 	// pull the vissprites out by scale
2393 	vsprsortedhead->next = vsprsortedhead->prev = vsprsortedhead;
2394 	for (i = start; i < end-linkedvissprites; i++)
2395 	{
2396 		bestscale = bestdispoffset = INT32_MAX;
2397 		for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
2398 		{
2399 #ifdef PARANOIA
2400 			if (ds->cut & SC_LINKDRAW)
2401 				I_Error("R_SortVisSprites: no link or discardal made for linkdraw!");
2402 #endif
2403 
2404 			if (ds->sortscale < bestscale)
2405 			{
2406 				bestscale = ds->sortscale;
2407 				bestdispoffset = ds->dispoffset;
2408 				best = ds;
2409 			}
2410 			// order visprites of same scale by dispoffset, smallest first
2411 			else if (ds->sortscale == bestscale && ds->dispoffset < bestdispoffset)
2412 			{
2413 				bestdispoffset = ds->dispoffset;
2414 				best = ds;
2415 			}
2416 		}
2417 		best->next->prev = best->prev;
2418 		best->prev->next = best->next;
2419 		best->next = vsprsortedhead;
2420 		best->prev = vsprsortedhead->prev;
2421 		vsprsortedhead->prev->next = best;
2422 		vsprsortedhead->prev = best;
2423 	}
2424 }
2425 
2426 //
2427 // R_CreateDrawNodes
2428 // Creates and sorts a list of drawnodes for the scene being rendered.
2429 static drawnode_t *R_CreateDrawNode(drawnode_t *link);
2430 
2431 static drawnode_t nodebankhead;
2432 
2433 static void R_CreateDrawNodes(maskcount_t* mask, drawnode_t* head, boolean tempskip)
2434 {
2435 	drawnode_t *entry;
2436 	drawseg_t *ds;
2437 	INT32 i, p, best, x1, x2;
2438 	fixed_t bestdelta, delta;
2439 	vissprite_t *rover;
2440 	static vissprite_t vsprsortedhead;
2441 	drawnode_t *r2;
2442 	visplane_t *plane;
2443 	INT32 sintersect;
2444 	fixed_t scale = 0;
2445 
2446 	// Add the 3D floors, thicksides, and masked textures...
2447 	for (ds = drawsegs + mask->drawsegs[1]; ds-- > drawsegs + mask->drawsegs[0];)
2448 	{
2449 		if (ds->numthicksides)
2450 		{
2451 			for (i = 0; i < ds->numthicksides; i++)
2452 			{
2453 				entry = R_CreateDrawNode(head);
2454 				entry->thickseg = ds;
2455 				entry->ffloor = ds->thicksides[i];
2456 			}
2457 		}
2458 		// Check for a polyobject plane, but only if this is a front line
2459 		if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
2460 			plane = ds->curline->polyseg->visplane;
2461 			R_PlaneBounds(plane);
2462 
2463 			if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low)
2464 				;
2465 			else {
2466 				// Put it in!
2467 				entry = R_CreateDrawNode(head);
2468 				entry->plane = plane;
2469 				entry->seg = ds;
2470 			}
2471 			ds->curline->polyseg->visplane = NULL;
2472 		}
2473 		if (ds->maskedtexturecol)
2474 		{
2475 			entry = R_CreateDrawNode(head);
2476 			entry->seg = ds;
2477 		}
2478 		if (ds->numffloorplanes)
2479 		{
2480 			for (i = 0; i < ds->numffloorplanes; i++)
2481 			{
2482 				best = -1;
2483 				bestdelta = 0;
2484 				for (p = 0; p < ds->numffloorplanes; p++)
2485 				{
2486 					if (!ds->ffloorplanes[p])
2487 						continue;
2488 					plane = ds->ffloorplanes[p];
2489 					R_PlaneBounds(plane);
2490 
2491 					if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low || plane->polyobj)
2492 					{
2493 						ds->ffloorplanes[p] = NULL;
2494 						continue;
2495 					}
2496 
2497 					delta = abs(plane->height - viewz);
2498 					if (delta > bestdelta)
2499 					{
2500 						best = p;
2501 						bestdelta = delta;
2502 					}
2503 				}
2504 				if (best != -1)
2505 				{
2506 					entry = R_CreateDrawNode(head);
2507 					entry->plane = ds->ffloorplanes[best];
2508 					entry->seg = ds;
2509 					ds->ffloorplanes[best] = NULL;
2510 				}
2511 				else
2512 					break;
2513 			}
2514 		}
2515 	}
2516 
2517 	if (tempskip)
2518 		return;
2519 
2520 	// find all the remaining polyobject planes and add them on the end of the list
2521 	// probably this is a terrible idea if we wanted them to be sorted properly
2522 	// but it works getting them in for now
2523 	for (i = 0; i < numPolyObjects; i++)
2524 	{
2525 		if (!PolyObjects[i].visplane)
2526 			continue;
2527 		plane = PolyObjects[i].visplane;
2528 		R_PlaneBounds(plane);
2529 
2530 		if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low)
2531 		{
2532 			PolyObjects[i].visplane = NULL;
2533 			continue;
2534 		}
2535 		entry = R_CreateDrawNode(head);
2536 		entry->plane = plane;
2537 		// note: no seg is set, for what should be obvious reasons
2538 		PolyObjects[i].visplane = NULL;
2539 	}
2540 
2541 	// No vissprites in this mask?
2542 	if (mask->vissprites[1] - mask->vissprites[0] == 0)
2543 		return;
2544 
2545 	R_SortVisSprites(&vsprsortedhead, mask->vissprites[0], mask->vissprites[1]);
2546 
2547 	for (rover = vsprsortedhead.prev; rover != &vsprsortedhead; rover = rover->prev)
2548 	{
2549 		if (rover->szt > vid.height || rover->sz < 0)
2550 			continue;
2551 
2552 		sintersect = (rover->x1 + rover->x2) / 2;
2553 
2554 		for (r2 = head->next; r2 != head; r2 = r2->next)
2555 		{
2556 			if (r2->plane)
2557 			{
2558 				fixed_t planeobjectz, planecameraz;
2559 				if (r2->plane->minx > rover->x2 || r2->plane->maxx < rover->x1)
2560 					continue;
2561 				if (rover->szt > r2->plane->low || rover->sz < r2->plane->high)
2562 					continue;
2563 
2564 				// Effective height may be different for each comparison in the case of slopes
2565 				planeobjectz = P_GetZAt(r2->plane->slope, rover->gx, rover->gy, r2->plane->height);
2566 				planecameraz = P_GetZAt(r2->plane->slope,     viewx,     viewy, r2->plane->height);
2567 
2568 				if (rover->mobjflags & MF_NOCLIPHEIGHT)
2569 				{
2570 					//Objects with NOCLIPHEIGHT can appear halfway in.
2571 					if (planecameraz < viewz && rover->pz+(rover->thingheight/2) >= planeobjectz)
2572 						continue;
2573 					if (planecameraz > viewz && rover->pzt-(rover->thingheight/2) <= planeobjectz)
2574 						continue;
2575 				}
2576 				else
2577 				{
2578 					if (planecameraz < viewz && rover->pz >= planeobjectz)
2579 						continue;
2580 					if (planecameraz > viewz && rover->pzt <= planeobjectz)
2581 						continue;
2582 				}
2583 
2584 				// SoM: NOTE: Because a visplane's shape and scale is not directly
2585 				// bound to any single linedef, a simple poll of it's frontscale is
2586 				// not adequate. We must check the entire frontscale array for any
2587 				// part that is in front of the sprite.
2588 
2589 				x1 = rover->x1;
2590 				x2 = rover->x2;
2591 				if (x1 < r2->plane->minx) x1 = r2->plane->minx;
2592 				if (x2 > r2->plane->maxx) x2 = r2->plane->maxx;
2593 
2594 				if (r2->seg) // if no seg set, assume the whole thing is in front or something stupid
2595 				{
2596 					for (i = x1; i <= x2; i++)
2597 					{
2598 						if (r2->seg->frontscale[i] > rover->sortscale)
2599 							break;
2600 					}
2601 					if (i > x2)
2602 						continue;
2603 				}
2604 
2605 				entry = R_CreateDrawNode(NULL);
2606 				(entry->prev = r2->prev)->next = entry;
2607 				(entry->next = r2)->prev = entry;
2608 				entry->sprite = rover;
2609 				break;
2610 			}
2611 			else if (r2->thickseg)
2612 			{
2613 				fixed_t topplaneobjectz, topplanecameraz, botplaneobjectz, botplanecameraz;
2614 				if (rover->x1 > r2->thickseg->x2 || rover->x2 < r2->thickseg->x1)
2615 					continue;
2616 
2617 				scale = r2->thickseg->scale1 > r2->thickseg->scale2 ? r2->thickseg->scale1 : r2->thickseg->scale2;
2618 				if (scale <= rover->sortscale)
2619 					continue;
2620 				scale = r2->thickseg->scale1 + (r2->thickseg->scalestep * (sintersect - r2->thickseg->x1));
2621 				if (scale <= rover->sortscale)
2622 					continue;
2623 
2624 				topplaneobjectz = P_GetFFloorTopZAt   (r2->ffloor, rover->gx, rover->gy);
2625 				topplanecameraz = P_GetFFloorTopZAt   (r2->ffloor,     viewx,     viewy);
2626 				botplaneobjectz = P_GetFFloorBottomZAt(r2->ffloor, rover->gx, rover->gy);
2627 				botplanecameraz = P_GetFFloorBottomZAt(r2->ffloor,     viewx,     viewy);
2628 
2629 				if ((topplanecameraz > viewz && botplanecameraz < viewz) ||
2630 				    (topplanecameraz < viewz && rover->gzt < topplaneobjectz) ||
2631 				    (botplanecameraz > viewz && rover->gz > botplaneobjectz))
2632 				{
2633 					entry = R_CreateDrawNode(NULL);
2634 					(entry->prev = r2->prev)->next = entry;
2635 					(entry->next = r2)->prev = entry;
2636 					entry->sprite = rover;
2637 					break;
2638 				}
2639 			}
2640 			else if (r2->seg)
2641 			{
2642 				if (rover->x1 > r2->seg->x2 || rover->x2 < r2->seg->x1)
2643 					continue;
2644 
2645 				scale = r2->seg->scale1 > r2->seg->scale2 ? r2->seg->scale1 : r2->seg->scale2;
2646 				if (scale <= rover->sortscale)
2647 					continue;
2648 				scale = r2->seg->scale1 + (r2->seg->scalestep * (sintersect - r2->seg->x1));
2649 
2650 				if (rover->sortscale < scale)
2651 				{
2652 					entry = R_CreateDrawNode(NULL);
2653 					(entry->prev = r2->prev)->next = entry;
2654 					(entry->next = r2)->prev = entry;
2655 					entry->sprite = rover;
2656 					break;
2657 				}
2658 			}
2659 			else if (r2->sprite)
2660 			{
2661 				boolean infront = (r2->sprite->sortscale > rover->sortscale
2662 								|| (r2->sprite->sortscale == rover->sortscale && r2->sprite->dispoffset > rover->dispoffset));
2663 
2664 				if (rover->cut & SC_SPLAT || r2->sprite->cut & SC_SPLAT)
2665 				{
2666 					fixed_t scale1 = (rover->cut & SC_SPLAT ? rover->sortsplat : rover->sortscale);
2667 					fixed_t scale2 = (r2->sprite->cut & SC_SPLAT ? r2->sprite->sortsplat : r2->sprite->sortscale);
2668 					boolean behind = (scale2 > scale1 || (scale2 == scale1 && r2->sprite->dispoffset > rover->dispoffset));
2669 
2670 					if (!behind)
2671 					{
2672 						fixed_t z1 = 0, z2 = 0;
2673 
2674 						if (rover->mobj->z - viewz > 0)
2675 						{
2676 							z1 = rover->pz;
2677 							z2 = r2->sprite->pz;
2678 						}
2679 						else
2680 						{
2681 							z1 = r2->sprite->pz;
2682 							z2 = rover->pz;
2683 						}
2684 
2685 						z1 -= viewz;
2686 						z2 -= viewz;
2687 
2688 						infront = (z1 >= z2);
2689 					}
2690 				}
2691 				else
2692 				{
2693 					if (r2->sprite->x1 > rover->x2 || r2->sprite->x2 < rover->x1)
2694 						continue;
2695 					if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
2696 						continue;
2697 				}
2698 
2699 				if (infront)
2700 				{
2701 					entry = R_CreateDrawNode(NULL);
2702 					(entry->prev = r2->prev)->next = entry;
2703 					(entry->next = r2)->prev = entry;
2704 					entry->sprite = rover;
2705 					break;
2706 				}
2707 			}
2708 		}
2709 		if (r2 == head)
2710 		{
2711 			entry = R_CreateDrawNode(head);
2712 			entry->sprite = rover;
2713 		}
2714 	}
2715 }
2716 
2717 static drawnode_t *R_CreateDrawNode(drawnode_t *link)
2718 {
2719 	drawnode_t *node = nodebankhead.next;
2720 
2721 	if (node == &nodebankhead)
2722 	{
2723 		node = malloc(sizeof (*node));
2724 		if (!node)
2725 			I_Error("No more free memory to CreateDrawNode");
2726 	}
2727 	else
2728 		(nodebankhead.next = node->next)->prev = &nodebankhead;
2729 
2730 	if (link)
2731 	{
2732 		node->next = link;
2733 		node->prev = link->prev;
2734 		link->prev->next = node;
2735 		link->prev = node;
2736 	}
2737 
2738 	node->plane = NULL;
2739 	node->seg = NULL;
2740 	node->thickseg = NULL;
2741 	node->ffloor = NULL;
2742 	node->sprite = NULL;
2743 
2744 	ps_numdrawnodes++;
2745 	return node;
2746 }
2747 
2748 static void R_DoneWithNode(drawnode_t *node)
2749 {
2750 	(node->next->prev = node->prev)->next = node->next;
2751 	(node->next = nodebankhead.next)->prev = node;
2752 	(node->prev = &nodebankhead)->next = node;
2753 }
2754 
2755 static void R_ClearDrawNodes(drawnode_t* head)
2756 {
2757 	drawnode_t *rover;
2758 	drawnode_t *next;
2759 
2760 	for (rover = head->next; rover != head;)
2761 	{
2762 		next = rover->next;
2763 		R_DoneWithNode(rover);
2764 		rover = next;
2765 	}
2766 
2767 	head->next = head->prev = head;
2768 }
2769 
2770 void R_InitDrawNodes(void)
2771 {
2772 	nodebankhead.next = nodebankhead.prev = &nodebankhead;
2773 }
2774 
2775 //
2776 // R_DrawSprite
2777 //
2778 //Fab : 26-04-98:
2779 // NOTE : uses con_clipviewtop, so that when console is on,
2780 //        don't draw the part of sprites hidden under the console
2781 static void R_DrawSprite(vissprite_t *spr)
2782 {
2783 	mfloorclip = spr->clipbot;
2784 	mceilingclip = spr->cliptop;
2785 
2786 	if (spr->cut & SC_SPLAT)
2787 		R_DrawFloorSplat(spr);
2788 	else
2789 		R_DrawVisSprite(spr);
2790 }
2791 
2792 // Special drawer for precipitation sprites Tails 08-18-2002
2793 static void R_DrawPrecipitationSprite(vissprite_t *spr)
2794 {
2795 	mfloorclip = spr->clipbot;
2796 	mceilingclip = spr->cliptop;
2797 	R_DrawPrecipitationVisSprite(spr);
2798 }
2799 
2800 // R_ClipVisSprite
2801 // Clips vissprites without drawing, so that portals can work. -Red
2802 void R_ClipVisSprite(vissprite_t *spr, INT32 x1, INT32 x2, drawseg_t* dsstart, portal_t* portal)
2803 {
2804 	drawseg_t *ds;
2805 	INT32		x;
2806 	INT32		r1;
2807 	INT32		r2;
2808 	fixed_t		scale;
2809 	fixed_t		lowscale;
2810 	INT32		silhouette;
2811 
2812 	for (x = x1; x <= x2; x++)
2813 		spr->clipbot[x] = spr->cliptop[x] = -2;
2814 
2815 	// Scan drawsegs from end to start for obscuring segs.
2816 	// The first drawseg that has a greater scale
2817 	//  is the clip seg.
2818 	//SoM: 4/8/2000:
2819 	// Pointer check was originally nonportable
2820 	// and buggy, by going past LEFT end of array:
2821 
2822 	//    for (ds = ds_p-1; ds >= drawsegs; ds--)    old buggy code
2823 	for (ds = ds_p; ds-- > dsstart;)
2824 	{
2825 		// determine if the drawseg obscures the sprite
2826 		if (ds->x1 > x2 ||
2827 			ds->x2 < x1 ||
2828 			(!ds->silhouette
2829 			 && !ds->maskedtexturecol))
2830 		{
2831 			// does not cover sprite
2832 			continue;
2833 		}
2834 
2835 		if (ds->portalpass != 66)
2836 		{
2837 			if (ds->portalpass > 0 && ds->portalpass <= portalrender)
2838 				continue; // is a portal
2839 
2840 			if (ds->scale1 > ds->scale2)
2841 			{
2842 				lowscale = ds->scale2;
2843 				scale = ds->scale1;
2844 			}
2845 			else
2846 			{
2847 				lowscale = ds->scale1;
2848 				scale = ds->scale2;
2849 			}
2850 
2851 			if (scale < spr->sortscale ||
2852 				(lowscale < spr->sortscale &&
2853 				 !R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
2854 			{
2855 				// masked mid texture?
2856 				/*if (ds->maskedtexturecol)
2857 					R_RenderMaskedSegRange (ds, r1, r2);*/
2858 				// seg is behind sprite
2859 				continue;
2860 			}
2861 		}
2862 
2863 		r1 = ds->x1 < x1 ? x1 : ds->x1;
2864 		r2 = ds->x2 > x2 ? x2 : ds->x2;
2865 
2866 		// clip this piece of the sprite
2867 		silhouette = ds->silhouette;
2868 
2869 		if (spr->gz >= ds->bsilheight)
2870 			silhouette &= ~SIL_BOTTOM;
2871 
2872 		if (spr->gzt <= ds->tsilheight)
2873 			silhouette &= ~SIL_TOP;
2874 
2875 		if (silhouette == SIL_BOTTOM)
2876 		{
2877 			// bottom sil
2878 			for (x = r1; x <= r2; x++)
2879 				if (spr->clipbot[x] == -2)
2880 					spr->clipbot[x] = ds->sprbottomclip[x];
2881 		}
2882 		else if (silhouette == SIL_TOP)
2883 		{
2884 			// top sil
2885 			for (x = r1; x <= r2; x++)
2886 				if (spr->cliptop[x] == -2)
2887 					spr->cliptop[x] = ds->sprtopclip[x];
2888 		}
2889 		else if (silhouette == (SIL_TOP|SIL_BOTTOM))
2890 		{
2891 			// both
2892 			for (x = r1; x <= r2; x++)
2893 			{
2894 				if (spr->clipbot[x] == -2)
2895 					spr->clipbot[x] = ds->sprbottomclip[x];
2896 				if (spr->cliptop[x] == -2)
2897 					spr->cliptop[x] = ds->sprtopclip[x];
2898 			}
2899 		}
2900 	}
2901 	//SoM: 3/17/2000: Clip sprites in water.
2902 	if (spr->heightsec != -1)  // only things in specially marked sectors
2903 	{
2904 		fixed_t mh, h;
2905 		INT32 phs = viewplayer->mo->subsector->sector->heightsec;
2906 		if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
2907 			(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
2908 			(h >>= FRACBITS) < viewheight)
2909 		{
2910 			if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
2911 			{                          // clip bottom
2912 				for (x = x1; x <= x2; x++)
2913 					if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
2914 						spr->clipbot[x] = (INT16)h;
2915 			}
2916 			else						// clip top
2917 			{
2918 				for (x = x1; x <= x2; x++)
2919 					if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
2920 						spr->cliptop[x] = (INT16)h;
2921 			}
2922 		}
2923 
2924 		if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
2925 			(h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
2926 			(h >>= FRACBITS) < viewheight)
2927 		{
2928 			if (phs != -1 && viewz >= sectors[phs].ceilingheight)
2929 			{                         // clip bottom
2930 				for (x = x1; x <= x2; x++)
2931 					if (spr->clipbot[x] == -2 || h < spr->clipbot[x])
2932 						spr->clipbot[x] = (INT16)h;
2933 			}
2934 			else                       // clip top
2935 			{
2936 				for (x = x1; x <= x2; x++)
2937 					if (spr->cliptop[x] == -2 || h > spr->cliptop[x])
2938 						spr->cliptop[x] = (INT16)h;
2939 			}
2940 		}
2941 	}
2942 	if (spr->cut & SC_TOP && spr->cut & SC_BOTTOM)
2943 	{
2944 		for (x = x1; x <= x2; x++)
2945 		{
2946 			if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
2947 				spr->cliptop[x] = spr->szt;
2948 
2949 			if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
2950 				spr->clipbot[x] = spr->sz;
2951 		}
2952 	}
2953 	else if (spr->cut & SC_TOP)
2954 	{
2955 		for (x = x1; x <= x2; x++)
2956 		{
2957 			if (spr->cliptop[x] == -2 || spr->szt > spr->cliptop[x])
2958 				spr->cliptop[x] = spr->szt;
2959 		}
2960 	}
2961 	else if (spr->cut & SC_BOTTOM)
2962 	{
2963 		for (x = x1; x <= x2; x++)
2964 		{
2965 			if (spr->clipbot[x] == -2 || spr->sz < spr->clipbot[x])
2966 				spr->clipbot[x] = spr->sz;
2967 		}
2968 	}
2969 
2970 	// all clipping has been performed, so store the values - what, did you think we were drawing them NOW?
2971 
2972 	// check for unclipped columns
2973 	for (x = x1; x <= x2; x++)
2974 	{
2975 		if (spr->clipbot[x] == -2)
2976 			spr->clipbot[x] = (INT16)viewheight;
2977 
2978 		if (spr->cliptop[x] == -2)
2979 			//Fab : 26-04-98: was -1, now clips against console bottom
2980 			spr->cliptop[x] = (INT16)con_clipviewtop;
2981 	}
2982 
2983 	if (portal)
2984 	{
2985 		for (x = x1; x <= x2; x++)
2986 		{
2987 			if (spr->clipbot[x] > portal->floorclip[x - portal->start])
2988 				spr->clipbot[x] = portal->floorclip[x - portal->start];
2989 			if (spr->cliptop[x] < portal->ceilingclip[x - portal->start])
2990 				spr->cliptop[x] = portal->ceilingclip[x - portal->start];
2991 		}
2992 	}
2993 }
2994 
2995 void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
2996 {
2997 	for (; clippedvissprites < visspritecount; clippedvissprites++)
2998 	{
2999 		vissprite_t *spr = R_GetVisSprite(clippedvissprites);
3000 		INT32 x1 = (spr->cut & SC_SPLAT) ? 0 : spr->x1;
3001 		INT32 x2 = (spr->cut & SC_SPLAT) ? viewwidth : spr->x2;
3002 		R_ClipVisSprite(spr, x1, x2, dsstart, portal);
3003 	}
3004 }
3005 
3006 /* Check if thing may be drawn from our current view. */
3007 boolean R_ThingVisible (mobj_t *thing)
3008 {
3009 	return (!(
3010 				thing->sprite == SPR_NULL ||
3011 				( thing->flags2 & (MF2_DONTDRAW) ) ||
3012 				(r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
3013 	));
3014 }
3015 
3016 boolean R_ThingVisibleWithinDist (mobj_t *thing,
3017 		fixed_t      limit_dist,
3018 		fixed_t hoop_limit_dist)
3019 {
3020 	fixed_t approx_dist;
3021 
3022 	if (! R_ThingVisible(thing))
3023 		return false;
3024 
3025 	approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y);
3026 
3027 	if (thing->sprite == SPR_HOOP)
3028 	{
3029 		if (hoop_limit_dist && approx_dist > hoop_limit_dist)
3030 			return false;
3031 	}
3032 	else
3033 	{
3034 		if (limit_dist && approx_dist > limit_dist)
3035 			return false;
3036 	}
3037 
3038 	return true;
3039 }
3040 
3041 /* Check if precipitation may be drawn from our current view. */
3042 boolean R_PrecipThingVisible (precipmobj_t *precipthing,
3043 		fixed_t limit_dist)
3044 {
3045 	fixed_t approx_dist;
3046 
3047 	if (( precipthing->precipflags & PCF_INVISIBLE ))
3048 		return false;
3049 
3050 	approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y);
3051 
3052 	return ( approx_dist <= limit_dist );
3053 }
3054 
3055 boolean R_ThingHorizontallyFlipped(mobj_t *thing)
3056 {
3057 	return (thing->frame & FF_HORIZONTALFLIP || thing->renderflags & RF_HORIZONTALFLIP);
3058 }
3059 
3060 boolean R_ThingVerticallyFlipped(mobj_t *thing)
3061 {
3062 	return (thing->frame & FF_VERTICALFLIP || thing->renderflags & RF_VERTICALFLIP);
3063 }
3064 
3065 boolean R_ThingIsPaperSprite(mobj_t *thing)
3066 {
3067 	return (thing->frame & FF_PAPERSPRITE || thing->renderflags & RF_PAPERSPRITE);
3068 }
3069 
3070 boolean R_ThingIsFloorSprite(mobj_t *thing)
3071 {
3072 	return (thing->flags2 & MF2_SPLAT || thing->renderflags & RF_FLOORSPRITE);
3073 }
3074 
3075 boolean R_ThingIsFullBright(mobj_t *thing)
3076 {
3077 	return (thing->frame & FF_FULLBRIGHT || thing->renderflags & RF_FULLBRIGHT);
3078 }
3079 
3080 boolean R_ThingIsFullDark(mobj_t *thing)
3081 {
3082 	return (thing->renderflags & RF_FULLDARK);
3083 }
3084 
3085 //
3086 // R_DrawMasked
3087 //
3088 static void R_DrawMaskedList (drawnode_t* head)
3089 {
3090 	drawnode_t *r2;
3091 	drawnode_t *next;
3092 
3093 	for (r2 = head->next; r2 != head; r2 = r2->next)
3094 	{
3095 		if (r2->plane)
3096 		{
3097 			next = r2->prev;
3098 			R_DrawSinglePlane(r2->plane);
3099 			R_DoneWithNode(r2);
3100 			r2 = next;
3101 		}
3102 		else if (r2->seg && r2->seg->maskedtexturecol != NULL)
3103 		{
3104 			next = r2->prev;
3105 			R_RenderMaskedSegRange(r2->seg, r2->seg->x1, r2->seg->x2);
3106 			r2->seg->maskedtexturecol = NULL;
3107 			R_DoneWithNode(r2);
3108 			r2 = next;
3109 		}
3110 		else if (r2->thickseg)
3111 		{
3112 			next = r2->prev;
3113 			R_RenderThickSideRange(r2->thickseg, r2->thickseg->x1, r2->thickseg->x2, r2->ffloor);
3114 			R_DoneWithNode(r2);
3115 			r2 = next;
3116 		}
3117 		else if (r2->sprite)
3118 		{
3119 			next = r2->prev;
3120 
3121 			// Tails 08-18-2002
3122 			if (r2->sprite->cut & SC_PRECIP)
3123 				R_DrawPrecipitationSprite(r2->sprite);
3124 			else if (!r2->sprite->linkdraw)
3125 				R_DrawSprite(r2->sprite);
3126 			else // unbundle linkdraw
3127 			{
3128 				vissprite_t *ds = r2->sprite->linkdraw;
3129 
3130 				for (;
3131 				(ds != NULL && r2->sprite->dispoffset > ds->dispoffset);
3132 				ds = ds->next)
3133 					R_DrawSprite(ds);
3134 
3135 				R_DrawSprite(r2->sprite);
3136 
3137 				for (; ds != NULL; ds = ds->next)
3138 					R_DrawSprite(ds);
3139 			}
3140 
3141 			R_DoneWithNode(r2);
3142 			r2 = next;
3143 		}
3144 	}
3145 }
3146 
3147 void R_DrawMasked(maskcount_t* masks, UINT8 nummasks)
3148 {
3149 	drawnode_t *heads;	/**< Drawnode lists; as many as number of views/portals. */
3150 	SINT8 i;
3151 
3152 	heads = calloc(nummasks, sizeof(drawnode_t));
3153 
3154 	for (i = 0; i < nummasks; i++)
3155 	{
3156 		heads[i].next = heads[i].prev = &heads[i];
3157 
3158 		viewx = masks[i].viewx;
3159 		viewy = masks[i].viewy;
3160 		viewz = masks[i].viewz;
3161 		viewsector = masks[i].viewsector;
3162 
3163 		R_CreateDrawNodes(&masks[i], &heads[i], false);
3164 	}
3165 
3166 	//for (i = 0; i < nummasks; i++)
3167 	//	CONS_Printf("Mask no.%d:\ndrawsegs: %d\n vissprites: %d\n\n", i, masks[i].drawsegs[1] - masks[i].drawsegs[0], masks[i].vissprites[1] - masks[i].vissprites[0]);
3168 
3169 	for (; nummasks > 0; nummasks--)
3170 	{
3171 		viewx = masks[nummasks - 1].viewx;
3172 		viewy = masks[nummasks - 1].viewy;
3173 		viewz = masks[nummasks - 1].viewz;
3174 		viewsector = masks[nummasks - 1].viewsector;
3175 
3176 		R_DrawMaskedList(&heads[nummasks - 1]);
3177 		R_ClearDrawNodes(&heads[nummasks - 1]);
3178 	}
3179 
3180 	free(heads);
3181 }
3182