--=============================GFX Functions 2=======================================
--Newer Visual Effects I created later. This one also contains HMS-specific special GFX.
--including his animation code.
--=================================================================================

--=============================================================================================
--								EFFECTS THAT STICK TO YOU
--=============================================================================================

--Start of SMS' boost combines multiple premade effects
rawset(_G, "SMS_BoostStart", function(s, sparkle)
	local GRAPHIX = SMS_Graphix(s)				
	if GRAPHIX == 2 return end --
	
	if GRAPHIX == 0
		local ghs = SMS_LightFlash(s, SKINCOLOR_SUPERSKY5, FRACUNIT-27000, 7, Z+3|TR_TRANS30)
		ghs.tracer = s
		ghs.fuse = 8	
		
		for i = 1,2
			ghs = HMS_BigBangVortex(s, 11, nil, nil, sparkle == "airboost" and T|FF_TRANS20 or T|FF_TRANS10, s.color)
			ghs.tracer = s
			ghs.spriteyoffset = $-30*FRACUNIT
			if i == 2
				if sparkle == "airboost"
					ghs.GSoffsetY = -54*s.scale
					ghs.GSoffsetX = -54*s.scale
					ghs.GSoffsetZ = -9*s.scale
					ghs.GSshiftoffsetXY = 108
					
					local ths = SMS_ElectricBody(s, 8, SKINCOLOR_RED)	
					ths.spriteyscale = $+8000
					ths.spritexscale = $+8000
					ths.blendmode = AST_TRANSLUCENT
					ths.GSoffsetY = 15*s.scale
					ths.GSoffsetX = 15*s.scale
					ths.GSoffsetZ = 0				
				else
					ghs.ditchtracer = 1
					ghs.momx = s.momx/2
					ghs.momy = s.momy/2					
				end
				ghs.color = SKINCOLOR_MINT
				ghs.spriteyscale = $/2
				ghs.spritexscale = $/2
				if s.player and s.player == displayplayer and CV_FindVar("renderer") == 1
					ghs.flags2 = MF2_DONTDRAW
				end
			else
				ghs.scalespeed = $+(FRACUNIT/9)
			end
		end
	end
	if sparkle == true
		local ghs = SMS_SparkleFlash(s)		
		ghs.fuse = $+3	
		if GRAPHIX != 2
			ghs = SMS_LightClash(s, SKINCOLOR_MYSTICAL2, s)
			ghs.frame = $+FF_TRANS30				
			for smsbdzone = 0,05
				P_SpawnParaloop(s.x, s.y, s.z+FixedMul(15*FRACUNIT, s.scale), FixedMul(84*FRACUNIT, s.scale), 8, MT_NIGHTSPARKLE, smsbdzone*ANGLE_22h, S_SMSTSPARK1, true)
			end
		end
	end	
end)

--Pulse Electricity from your body
rawset(_G, "SMS_ElectricBody", function(s, FUSE, COLOR)				
	local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX3
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = A|TR_TRANS10
	ghs.blendmode = AST_ADD
	ghs.fuse = (FUSE) or 6
	if (COLOR)
		ghs.color = COLOR
		ghs.colorized = true
	end
	ghs.sttspeed = 1 
	ghs.sttfirstframe = A
	ghs.sttfinalframe = E
	ghs.tracer = s
	return ghs
end)

--Engulf self with a quick boostdash" effect
rawset(_G, "SMS_DashGFX", function(s, COLOR)				
	local ghs = P_SpawnMobjFromMobj(s, s.momx, s.momy, s.momz, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX3
	ghs.renderflags = RF_PAPERSPRITE|RF_NOCOLORMAPS|RF_FULLBRIGHT
	ghs.blendmode = AST_ADD
	ghs.angle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	ghs.frame = F|TR_TRANS30
	ghs.color = (COLOR) or s.color
	ghs.fuse = 13
	ghs.destscale = $*99
	ghs.sttfade = true
	ghs.sttspeed = 2 
	ghs.sttfirstframe = F
	ghs.sttfinalframe = L
	ghs.tracer = s 
	return ghs
end)

--Leave behind a super-quick kickup of dust.
rawset(_G, "SMS_QuickDustGFX", function(s, COLOR, BLENDMODE, OFFSET, TRACER)
	local ANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	local ths,ghs
	for i = -1,1,2
		ghs = P_SpawnMobjFromMobj(s, s.momx+FixedMul(36*s.scale, cos(ANGLE+(ANGLE_90*i))), s.momy+FixedMul(36*s.scale, sin(ANGLE+(ANGLE_90*i))), 0, MT_SMSAUTOFRAME)	
		ghs.sprite = SPR_SMX3
		ghs.renderflags = RF_PAPERSPRITE|RF_SEMIBRIGHT
		ghs.angle = ANGLE-(ANG1*39*i)
		ghs.frame = (i != 1) and M|FF_TRANS10 or M|FF_TRANS10|FF_HORIZONTALFLIP	
		P_Thrust(ghs, ANGLE+(ANG1*39*i), (5*s.scale)) 
		ghs.scale = s.scale/3
		ghs.scalespeed = $*4
		ghs.destscale = FRACUNIT*99
		ghs.sttfade = true
		ghs.sttspeed = 3
		ghs.sttfinalframe = N
		ghs.spriteyoffset = $+((OFFSET) or 0)
		if COLOR
			ghs.color = COLOR
			ghs.colorized = true
		end
		ghs.blendmode = BLENDMODE or $
		ghs.tracer = TRACER or nil
		ghs.fuse = TRACER and 7 or 5
		if i == -1 ths = ghs end
	end
	return ths,ghs --
end)

--Leave behind a medium-sized kickup of dust.
rawset(_G, "SMS_MediumDustGFX", function(s, COLOR, BLENDMODE)
	local ANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	local ghs,ths
	for i = -1,1,2
		 ghs = P_SpawnMobjFromMobj(s, s.momx+FixedMul(36*s.scale, cos(ANGLE+(ANGLE_90*i))), s.momy+FixedMul(36*s.scale, sin(ANGLE+(ANGLE_90*i))), 0, MT_SMSAUTOFRAME)	
		ghs.sprite = SPR_SMX3
		ghs.renderflags = RF_PAPERSPRITE
		ghs.angle = ANGLE-(ANG1*39*i) //+(ANGLE_90-(ANG1*18*i))
		P_Thrust(ghs, ANGLE+(ANG1*39*i), 5*s.scale) 
		ghs.frame = (i == 1) and P|FF_TRANS10 or P|FF_TRANS10|FF_HORIZONTALFLIP	
		ghs.fuse = 17	
		ghs.scalespeed = $*3
		ghs.destscale = FRACUNIT*99
		ghs.sttfade = true
		ghs.sttspeed = 2
		ghs.sttfinalframe = V
		ghs.color = COLOR or $
		ghs.colorized = (COLOR) and true or false
		ghs.blendmode = BLENDMODE or $
		if i != 1
			ths = ghs
		end
	end
	return ghs,ths --
end)

--Engulf self in a cartoony twirl effect. 
--Function can either be called every frame, or once in a quick burst with NOLOOP
rawset(_G, "SMS_TwirlGFX", function(s, COLOR, NOLOOP)
	local ghs = P_SpawnMobjFromMobj(s, 0,0, 0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX3
	ghs.renderflags = RF_NOCOLORMAPS|RF_SEMIBRIGHT
	ghs.blendmode = AST_ADD
	ghs.tracer = s
	ghs.destscale = FRACUNIT*99	
	ghs.dispoffset = 7
	ghs.GSoffsetX = -3*s.scale
	ghs.GSoffsetY = -3*s.scale
	ghs.GSoffsetZ = 0
	if s.player and s.player.hypermysticsonic
		ghs.scale = s.scale*5/4 --Slightly bigger and higher-up, then?
		ghs.spriteyoffset = $+8*FRACUNIT
	end
	if (COLOR)
		ghs.color = COLOR
	else --Special SMS-colored twirl.
		local COLORTABLE = {SKINCOLOR_SMS1R, SKINCOLOR_SMS3R, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_RED, SKINCOLOR_LIME, SKINCOLOR_EMERALD, SKINCOLOR_MINT, SKINCOLOR_WHITE}
		ghs.color = COLORTABLE[SMS_TimeFrame(10, 1)]	
	end
	if NOLOOP --Want a quick twirl effect?
		ghs.frame = W|FF_TRANS10
		ghs.sttspeed = 1
		ghs.sttfirstframe = W
		ghs.sttfinalframe = Z+1
		ghs.fuse = NOLOOP --Noloop should be the fuse number.
		ghs.sttfade = true
	else --Main looping version!
		ghs.frame = (SMS_TimeFrame(5, W))|(SMSLocalRNG(1, 4, s)*FRACUNIT)
		ghs.fuse = 4
		ghs.rollangle = SMSLocalRNG(-240, 240, s)*(ANG1/13) --Add shaking to the effect!
		ghs.spriteyoffset = SMSLocalRNG(-200, 200, s)*(FRACUNIT/17)
		ghs.spritexoffset = SMSLocalRNG(-150, 150, s)*(FRACUNIT/75)
		ghs.sttfirstframe = (ghs.frame & FF_FRAMEMASK)
		ghs.sttfinalframe = ghs.sttfirstframe+5
		ghs.momx = s.momx/8
		ghs.momy = s.momy/8
		if leveltime % 5 == 0
			ghs.spritexscale = FRACUNIT+21007
		elseif leveltime % 3 == 0
			ghs.spriteyscale = FRACUNIT+13500
		end
		ghs.ditchtracer = 1
	end
	return ghs --
end)

--Keep an expanding circle around you, usually to signify impact or failure.
rawset(_G, "SMS_FollowCircle", function(s, COLOR, CSCALEDIV)
	local ghs = P_SpawnMobjFromMobj(s, 0,0, 0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX3
	ghs.frame = O|TR_TRANS10
	ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
	ghs.fuse = 20
	ghs.scale = s.scale/3
	ghs.scalespeed = $*3
	ghs.destscale = FRACUNIT*99
	ghs.sttfade = true
	ghs.tracer = s
	if (COLOR)
		ghs.color = COLOR 
		ghs.colorized = true
	end	
	if CSCALEDIV
		ghs.spritexscale = FRACUNIT/CSCALEDIV
		ghs.spriteyscale = FRACUNIT/CSCALEDIV
	end
	return ghs --
end)

--=============================================================================================
--								TRAIL EFFECT FUNCTIONS
--=============================================================================================

--Draws a trail of "t" copies from "t" to "s". All but "s" and "t" can be left nil.
--Be careful not to use RNG functions on trail-spawned items, it'll probably desync.
--Let me try to explain the process this time. Hopefully you can follow along if you're not Lua experienced yet!
rawset(_G, "SMS_DrawTrail", function(s, t, customITEM)
	--2 = OpenGL. 1 = Software. Use Software setting for players that aren't you to save performance.
	local RENDERER = ((s.player and ((s.player != displayplayer) or not CV_FindVar("renderer").value)) or not s.player) and 1 or 2

	--What item to use? MT_THOK might be better.
	local ITEMTYPE = (customITEM) and customITEM or t.type
	
	--SMS graphics settings. 1 is decreased graphics, 2 is shit graphics. Decreases sprite density.
	local GFX = SMS_Graphix(s)*2
	
	--How far apart to spawn the sprites? The further apart the sprites, the less dense our trail.	
	local density = (renderer != 1) and (6+(GFX*2))*FRACUNIT or (17+GFX)*FRACUNIT --Software is less powerful, so render less sprites!
	if (t.spinspecial != nil or SMS_Gore(s) > 190) density = min(34*FRACUNIT, $+14*FRACUNIT) end
	
	--Not really "momx/momy" in this case, but rather X/Y distance between "s" and "t". But eh, I'm used to writing it like this.
	local MOMX,MOMY = (s.x-t.x)+s.momx or s.momx,(s.y-t.y)+s.momy or s.momy
	
	--Calculate spawn location to horizontal distance, then that with vertical distance. 
	--Next divide it by density(amount of sprites renderered). Afterwards scale our values properly with the player's scale.
	--Lastly set a sanity cap. Only spawn 180 sprites at most, but no less than 1 to prevent calculation errors.
	local dist = FixedDiv(FixedMul((R_PointToDist2(0, 0, R_PointToDist2(0, 0, MOMX, MOMY), (s.z-t.z)) / density), FRACUNIT), s.scale)
	dist = max(1, min($, 180))
	
	--Original scaled distances to decide where to spawn each sprite. If it returns 0, use 1 instead.
	local xdistog = MOMX/dist
	local ydistog = MOMY/dist
	local zdistog = (((s.z-t.z) or 1)/dist) or 1
	local zdistog2 = t.z or 1
	
	--Auto adjust translucency for software.
	if renderer == 1 and (t.frame & FF_TRANSMASK) >= FF_TRANS20 --Auto adjust translucency for software.
		t.frame = $-FF_TRANS10
	end
	local xdist,ydist,zdist = 0,0,0 --3 locals to be manipulated later.
	
	--Count for a max of potentially 180 sprites down until we reach 1. Spawn a trail sprite everytime we count down!
	for i = dist,1,-1
		if t.scale != FRACUNIT --Complicated version
			xdist = FixedDiv(FixedMul((MOMX/dist)*i, FRACUNIT), s.scale)
			ydist = FixedDiv(FixedMul((MOMY/dist)*i, FRACUNIT), s.scale)
			zdist = FixedDiv(FixedMul(((s.z-t.z)/dist)*i, FRACUNIT), s.scale)
		else --Simple version.
			xdist = xdistog*i
			ydist = ydistog*i
			zdist = zdistog*i
		end
		--SPAWN IT!
		local ghs = P_SpawnMobjFromMobj(s, xdist-MOMX, ydist-MOMY, zdist-(s.z-t.z), ITEMTYPE)	
		ghs.colorized = t.colorized
		ghs.spriteyscale = t.spriteyscale
		ghs.spritexscale = t.spritexscale	
		ghs.rollangle = t.rollangle
		ghs.renderflags = t.renderflags	
		if t.COLORT_trail and t.COLORT_trailmax --Special color trail!
			ghs.color = t.COLORT_trail[SMSLocalRNG(1, t.COLORT_trailmax, ghs)]
		else
			ghs.color = t.color
		end
		
		if (customITEM == MT_THOK) or (t.sprite == SPR_THOK) --State to prevent mods from screwing with the vanilla thok sprite, which an obnoxious amount of mods do.
			ghs.state = S_SMS_THOK --Also prevents game from loading 500 thok MD2s if models are enabled.
			if t.spinspecial
				if (i % 4 == 0) --Extra cool spin effect!
					local RNG = SMSLocalRNG(1,360,ghs)
					ghs.frame = (RNG/129)|FF_TRANS70|(RNG < 86 and FF_HORIZONTALFLIP or 0)
					ghs.rollangle = (68-(RNG/3))*ANG1
					ghs.colorized = true
					ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
					if RNG > 50
						ghs.spriteyscale = ($*3 or 8) / max(4,RNG/50)
						ghs.spritexscale = ghs.spriteyscale
					end
				else
					ghs.frame = Z|(t.frame & ~FF_FRAMEMASK)
				end
			else
				ghs.frame = (t.frame & ~FF_FRAMEMASK)
			end
		else
			P_SetMobjStateNF(ghs, t.state)
			ghs.sprite = t.sprite
			if t.skin
				ghs.skin = t.skin
				ghs.sprite2 = t.sprite2
			end	
			ghs.frame = t.frame		
		end
		ghs.tics = t.tics
		ghs.fuse = t.fuse
		ghs.tracer = t.tracer 
		ghs.target = t.target
		ghs.blendmode = t.blendmode
		ghs.mirrored = t.mirrored
		ghs.scale = t.scale
		ghs.flags = (t.flags|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY) & ~(MF_NOSECTOR|MF_NOTHINK)
		ghs.flags2 = t.flags2 & ~MF2_DONTDRAW
		ghs.eflags = t.eflags
		ghs.scalespeed = t.scalespeed
		ghs.spriteyoffset = t.spriteyoffset
		ghs.spritexoffset = t.spritexoffset
		ghs.scalespeed = t.scalespeed
		if t.toscale --Instant downscale near the tip of the trail.
			if ghs.scale and (i > 1)
				ghs.scale = FixedDiv(FixedMul(ghs.scale, i), ((dist*3) or 1)/4)
			else
				ghs.scale = 8
			end
		end
		ghs.destscale = t.destscale
		
		--Do we need to put in possible autoframe options?
		if (customITEM == MT_SMSAUTOFRAME)
			ghs.sttspeed = t.sttspeed
			ghs.sttfirstframe = t.sttfirstframe
			ghs.sttfinalframe = t.sttfinalframe
			ghs.GSoffsetX = t.GSoffsetX
			ghs.GSoffsetY = t.GSoffsetY
			ghs.GSoffsetZ = t.GSoffsetZ		
			ghs.sttfade = t.sttfade
			ghs.ditchtracer = t.ditchtracer
			ghs.GSoffsetwait = t.GSoffsetwait
			ghs.GSoffsetang = t.GSoffsetang
			ghs.GSshiftoffsetXY = t.GSshiftoffsetXY
			ghs.GSshiftoffsetZ = t.GSshiftoffsetZ
		end
		
		--Don't blind 1st person players! Hide the 2 closest trail sprites from their sight.
		if (i < 3)
			if t.ENDtrans != nil	
				ghs.frame = ($ & ~FF_TRANSMASK)|t.ENDtrans
				ghs.color = s.color
				ghs.colorized = false
				ghs.destscale = FRACUNIT*99
				break --
			elseif s.player and (s.player == displayplayer) and not CV_FindVar("chasecam").value
				ghs.flags2 = $|MF2_DONTDRAW
			end
		end
	end
	return true -- Done!
end)

--Run through the s.SMS_DLtrail table to lay continuous trails.
rawset(_G, "SMS_ContinuousTrail", function(s, yu)
	if not yu.timer or SMS_Graphix(s) == 2 --Shitty graphics get no cool trails.
		s.SMS_DLtrail = nil
		return false --
	end
	--Have some speed to spawn a trail. Or be HMS, who can teleport.
	if not (s.player and s.player.hypermysticsonic)
		if (abs(s.momx) < 2*s.scale)
		and (abs(s.momy) < 2*s.scale)
		and (abs(s.momz) < 5*s.scale)
			yu.timer = max($-1,0)
			return false
		end
	end	
	if yu.init == nil --Set up stuff real quick?
		yu.init = true
		if yu.color == nil
			yu.color = SKINCOLOR_SKY
			yu.colorized = true
		end
		if yu.fuse == nil
			yu.fuse = 8
		end
		yu.tics = yu.fuse+1 or 9
		if yu.momx == nil
			yu.momx = s.momx/2
			yu.momy = s.momy/2
		end
		if yu.blendmode == nil
			yu.blendmode = AST_ADD
		end
	end
	
		local ghs = SMS_MomentumTrail(s, yu.itemtype)
		if ghs and ghs.valid
		
			if yu.spinspecial
				if (leveltime % 7 == 0) or (leveltime % 6 == 0)
					yu.COLORT_trail = {SKINCOLOR_EMERALD, SKINCOLOR_MINT, SKINCOLOR_LIME, SKINCOLOR_GREEN, SKINCOLOR_PERIDOT,SKINCOLOR_APPLE}
				elseif (leveltime % 5 == 0) or (leveltime % 4 == 0)
					yu.COLORT_trail = {SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_FLAME, SKINCOLOR_CRIMSON, SKINCOLOR_RUBY, SKINCOLOR_FLAMEBREAKER5}
				else
					if SMSisyellow(s)
						yu.COLORT_trail = {SKINCOLOR_SMS1R, SKINCOLOR_SMS2R, SKINCOLOR_SMS3R, SKINCOLOR_SMS4R, SKINCOLOR_SMS5R, SKINCOLOR_FLAMEBREAKER2}
					elseif s.player and s.player.shinyglosscoat and s.player.smscoatc5 and s.player.smscoatc1
						local p = s.player
						yu.COLORT_trail = {p.smscoatc5, p.smscoatc2, p.smscoatc3,p.smscoatc2,p.smscoatc1,p.smscoatc2}
					else
						yu.COLORT_trail = {s.color, s.color, s.color, s.color, s.color, s.color} --...ok then...
					end
				end
				yu.COLORT_trailmax = 6
				ghs.spinspecial = yu.spinspecial
			end
			
			if yu.COLORT_trail != nil
				ghs.COLORT_trail = yu.COLORT_trail
				ghs.COLORT_trailmax = yu.COLORT_trailmax
			else
				ghs.color = yu.color
			end
			if yu.colorized != nil
				ghs.colorized = yu.colorized
			end
			if yu.renderflags != nil
				ghs.renderflags = yu.renderflags
			end
			if yu.blendmode != nil
				ghs.blendmode = yu.blendmode
			end
			if yu.momx --Expecting both if either is filled in.
				ghs.momx = yu.momx
				ghs.momy = yu.momy
			end			
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS90
			ghs.fuse = (yu.fuse) or 8
			ghs.tics = (yu.tics) or $
			ghs.spriteyscale = (yu.spriteyscale) or $
			ghs.spritexscale = (yu.spritexscale) or $
			ghs.spriteyoffset = (yu.spriteyoffset) or $
			ghs.spritexoffset = (yu.spritexoffset) or $
			ghs.toscale = (yu.toscale) or $
			ghs.scale = (yu.scale) or max(50, min(s.scale*13/9, (s.scale*(yu.timer+2))/4) )
			ghs.scalespeed = (yu.scalespeed) or $
			ghs.destscale = (yu.destscale) or 1
			SMS_DrawTrail(s, ghs)
			ghs.state = S_NULL --Remove it after.
		end			
		yu.timer = max($-1,0)
		return true --
end)

--Shortcut function. Spawns a ghost if no itemtype is given.
rawset(_G, "SMS_MomentumTrail", function(s, ITEMTYPE, MODIFY, forceX,forceY,forceZ)
	local MOMX,MOMY,MOMZ
	if forceX --Must fill in forceY and forceZ too in this case.
		MOMX,MOMY,MOMZ = forceX, forceY, forceZ
	else
		MOMX,MOMY,MOMZ = s.momx, s.momy, s.momz
		if s.SMS_lastz != nil and (s.SMS_lastz != s.z)
			MOMZ = s.z-s.SMS_lastz
		end
	end
	if not (MOMX or MOMY or MOMZ)
		return false --We're not going anywhere? No trail, then!
	end
	--Modify momentum value? Going below 100 adds momentum, going above decreases. 50 would double it, 200 would half it.
	if MODIFY
		if MOMX
			MOMX = FixedDiv(FixedMul(MOMX, 100), MODIFY)
		end
		if MOMY
			MOMY = FixedDiv(FixedMul(MOMY, 100), MODIFY)
		end
		if MOMZ
			MOMZ = FixedDiv(FixedMul(MOMZ, 100), MODIFY)
		end
	end
	
	local ghs
	if (ITEMTYPE) --No item given, spawn a ghost!
		ghs = P_SpawnMobjFromMobj(s, 0,0,0, ITEMTYPE)
	else  --No item given, spawn a ghost!
		ghs = P_SpawnGhostMobj(s)
		ghs.skin = s.skin
	end
	
	if ghs and ghs.valid --Now bring it to the designated position behind you.
		ghs.angle = R_PointToAngle2(s.x-MOMX, s.y-MOMY, s.x, s.y) --Face your new angle!
	--	if (ghs.renderflags & RF_PAPERSPRITE) or (ghs.frame & FF_PAPERSPRITE)
	--		ghs.angle = $+ANGLE_90
	--	end
		P_SetOrigin(ghs, s.x-MOMX, s.y-MOMY, s.z-MOMZ)
	end
	return ghs
end)

--Spawns numerous lightning bolts that are angled and keep momentum in mind.
rawset(_G, "SMS_AngledLightning", function(s, COLOR, FUSE, NUM, SCALEADD)
	local BOLTTYPE
	local NO = (NUM) or 3
	for i = -NO,NO,1
		if i == 0 continue end
		if (i % 2 == 0)
			BOLTTYPE = R
		else
			BOLTTYPE = nil
		end
		local ghs = SMS_LightningStrike(s, (COLOR) or SKINCOLOR_MYSTICAL3, BOLTTYPE)
		P_SetOrigin(ghs, s.x+FixedMul(20*s.scale*i, cos(s.angle+ANGLE_90)), s.y+FixedMul(20*s.scale*i, sin(s.angle+ANGLE_90)), s.z)
		ghs.renderflags = $|RF_FULLBRIGHT|RF_PAPERSPRITE
		ghs.rollangle = ANGLE_90-(ANG10*abs(i))
		ghs.angle = s.angle-(ANG1*25*i)
		ghs.spriteyoffset = $-52*FRACUNIT
		ghs.momx = $+s.momx/4
		ghs.momy = $+s.momy/4
		ghs.fuse = ((FUSE) or 20)/3
		ghs.tics = ghs.fuse+9
		P_Thrust(ghs, s.angle-(ANG1*25*i), -(4+abs(i))*s.scale)
		ghs.momz = $+(5*s.scale*P_MobjFlip(s))
		if SCALEADD
			ghs.spriteyscale = $+SCALEADD
			ghs.spritexscale = $+SCALEADD
		end
		if BOLTTYPE == R
			ghs.sttspeed = ghs.fuse/3
		end
		if s.SMShomingspeed
		and s.SMShomingangle != nil
		and s.player and (abs(s.player.cmd.forwardmove) > 16 or abs(s.player.cmd.sidemove) > 16)
			P_Thrust(ghs, s.SMShomingangle, s.SMShomingspeed/2)
		end
	end
end)

--Lightning Trail used by SMS' Homing Bolt! "t" is optional and can simply be yourself.
rawset(_G, "SMS_LightningTrail", function(s, t, RedLightning)

		--Create a lightning trail!
		local SpawnMo = s
		local ghs
		
		for d = 1,2
			if d == 2 and t and t.valid
				SpawnMo = t
			else
				break --
			end
			
			for i = 1,4
			ghs = P_SpawnMobjFromMobj(SpawnMo, 
			FixedMul(SMSLocalRNG(-33*d, 33*d,s)*s.scale, cos(SpawnMo.angle+(d*ANGLE_22h)+(i*ANGLE_45))),
			FixedMul(SMSLocalRNG(33*d, -33*d,t)*s.scale, sin(SpawnMo.angle+(d*ANGLE_22h)+(i*ANGLE_45))),
			0, MT_HMSWAVEAURA)
				ghs.state = S_HMSWAVEAURAG
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs.blendmode = AST_ADD
				ghs.target = s
				ghs.scale = $*11/SMSLocalRNG(1, i+i,SpawnMo)
				P_HomingAttack(ghs, s)
			end
		end
		
		if not RedLightning return ghs end
		
		--Spawn Red Lightning to strike down your foes!
		for i = -1,1,1
			ghs = P_SpawnMobjFromMobj(SpawnMo, 0, 0, 0, MT_HMSLIGHTNING2) --Strike enemy with red lightning!
			if SpawnMo.player
				ghs.followsms = SpawnMo
			end
			ghs.blendmode = AST_ADD
			if i == 0
				ghs.spriteyoffset = -8*FRACUNIT
			else
				ghs.spritexscale = FRACUNIT/2
			end
			ghs.color = SKINCOLOR_CRIMSON
			ghs.fuse = 10
			ghs.rollangle = (ANG1*70)*i
		end
		
end)

--Strike an area with lightning! Can use custom coordinates and a smaller version.
rawset(_G, "SMS_LightningStrike", function(s, COLOR, SMALL)
	local x,y,z = s.x, s.y, s.z+(s.height/3)
	if (ex != nil) x,y,z = ex,ey,ez end
	
	local ghs = P_SpawnMobj(x,y,z, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
	ghs.eflags = s.eflags
	ghs.color = (COLOR) or SKINCOLOR_SMS4R
	ghs.sttfade = true
	ghs.scale = s.scale/2
	ghs.scalespeed = $*10
	ghs.destscale = s.scale*2	
	if SMALL --Shoot a smaller bolt? R is default.
		ghs.frame = SMALL
		ghs.sttfinalframe = (SMALL & FF_FRAMEMASK)+1 --S
		ghs.sttspeed = 4
		ghs.fuse = 10
	else
		ghs.frame = A //|TR_TRANS10
		ghs.sttfinalframe = D
		ghs.sttspeed = 3
		ghs.fuse = 15
	end
	return ghs --
end)

--Clash a bright light off an object or yourself. TRACER can be same as "S" if wanted!
rawset(_G, "SMS_LightClash", function(s, COLOR, TRACER)	
	local ghs = P_SpawnMobjFromMobj(s,0,0,s.height/5, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = I|TR_TRANS10 --SMSAutoframe remembers FF_FULLBRIGHT, btw. Renderflags is optional alot of times.
	ghs.fuse = 25
	ghs.color = (COLOR) or SKINCOLOR_SMS4R
	ghs.destscale = $*99
	ghs.sttspeed = 3
	ghs.sttfade = true
	ghs.sttfinalframe = O
	if (TRACER)
		ghs.tracer = TRACER
		ghs.renderflags = $|RF_PAPERSPRITE
		ghs.scalespeed = 24000
		ghs.frame = I|TR_TRANS30
		ghs.fuse = 21
		ghs.angle = TRACER.angle+ANGLE_90
		ghs.ditchtracer = 1
	end
	return ghs --
end)

--A quick, blinding flash of pure light.
rawset(_G, "SMS_LightFlash", function(s, COLOR, CSCALE, CSCALESPEED, ALTFRAME)	
	local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	if ALTFRAME --Alt light frame. Such as Z+3|TR_TRANS10
		if (ALTFRAME & FF_REVERSESUBTRACT)
			ghs.sprite = SPR_SMX5 --Switches to SMX5 sprites! Such as Z+8|FF_TRANS10|FF_REVERSESUBTRACT
			ALTFRAME = $ & ~FF_REVERSESUBTRACT			
		end	
		if (ALTFRAME & FF_ANIMATE) and (COLOR)
			ALTFRAME = $ & ~FF_ANIMATE --Colorize too?
			ghs.colorized = true
		end
		ghs.frame = ALTFRAME
	else
		ghs.frame = P|TR_TRANS10
	end
	ghs.fuse = 15
	ghs.color = (COLOR) or SKINCOLOR_SMS4R
	if (CSCALE)
		ghs.spritexscale = CSCALE
		ghs.spriteyscale = CSCALE
	end
	ghs.scalespeed = $*((CSCALESPEED) or 3)
	ghs.destscale = $*99
	ghs.sttfade = true
	if s.player and s.player == displayplayer and CV_FindVar("renderer") == 1 --Software doesn't look good with this anyway.
		ghs.flags2 = $|MF2_DONTDRAW
	end	
	return ghs --
end)

--Sparkle flash, usually good for adding an intensity to an upcoming attack
rawset(_G, "SMS_SparkleFlash", function(s, OFFSETXY, OFFSETZ, CSCALE)
	local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = Y|TR_TRANS10
	ghs.fuse = 7
	ghs.sttspeed = 2
	ghs.sttfirstframe = Y --Loop!
	ghs.sttfinalframe = Z+1
	ghs.dispoffset = 35
	if (CSCALE)
		ghs.spritexscale = CSCALE
		ghs.spriteyscale = CSCALE
	end	
	if OFFSETXY or OFFSETZ --Stick on your body!
		local ANGLE = (s.player) and s.player.drawangle or (s.momx or s.momy) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
		if OFFSETXY
			ghs.GSoffsetang = (s.mirrored or (s.frame & FF_HORIZONTALFLIP)) and ANGLE+ANGLE_90 or ANGLE-ANGLE_90	
		end
		ghs.tracer = s
		ghs.GSoffsetX = (OFFSETXY) or s.radius/2
		ghs.GSoffsetY = (OFFSETXY) or s.radius/2
		ghs.GSoffsetZ = (OFFSETZ) or s.height/2
	end	
	return ghs --	
end)

--A flat expanding wave effect. A splat that's good for jump effects.
rawset(_G, "SMS_FlatWave", function(s, COLOR, FUSE, CSCALEDIV, MAXHEIGHT)
	local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE
	ghs.flags2 = $|MF2_SPLAT
	ghs.frame = U
	ghs.fuse = (FUSE) or 9
	ghs.sttspeed = 1
	ghs.sttfirstframe = U
	ghs.sttfinalframe = X
	ghs.sttfade = true
	ghs.color = (COLOR) or SKINCOLOR_WHITE
	if MAXHEIGHT != 0
		SMS_AttachSplatToFloor(s, ghs, (MAXHEIGHT) or 40*s.scale)
	end
	ghs.scalespeed = FRACUNIT/4
	if (CSCALEDIV) --Custom scaler?
		ghs.radius = 0
		ghs.height = 0	
		ghs.spritexscale = FRACUNIT/CSCALEDIV
		ghs.spriteyscale = FRACUNIT/CSCALEDIV
		ghs.scalespeed = (FRACUNIT/2)*CSCALEDIV --Default scalespeed of objects is FRACUNIT/12 or 5446
	end
	ghs.destscale = FRACUNIT*50	
	return ghs --	
end)

--Rotates a Lens Flare in the specific position.
rawset(_G, "SMS_LensFlare", function(s, COLOR, CROLL, FUSE, CSCALE, OFFSETXY, OFFSETZ)
	local ghs = P_SpawnMobjFromMobj(s,-s.momx,-s.momy,s.momz, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = (Z+2)|TR_TRANS10
	ghs.fuse = (FUSE) or 28
	ghs.color = (COLOR) or SKINCOLOR_SMS2R
	ghs.dispoffset = 11
	ghs.scalespeed = $*3
	ghs.destscale = $*99
	ghs.sttfade = 20
	
	ghs.spritexoffset = -49*FRACUNIT --49
	ghs.sttXspeed = (110*FRACUNIT)/28 --110
	ghs.spriteyoffset = 10*FRACUNIT --10
	ghs.sttYspeed = (23*FRACUNIT)/28 --23

	if (CSCALE) --FRACUNIT
		ghs.spritexscale = CSCALE
		ghs.spriteyscale = CSCALE
	end
	if (CROLL) --ANG2*4?
		ghs.sttroll = CROLL
	else
		ghs.sttroll = ANG1*9
	end

	ghs.rollangle = ghs.sttroll/-2
	
	if OFFSETXY or OFFSETZ
		local ANGLE = (s.momx or s.momy) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
		if OFFSETXY
			ghs.GSoffsetang = (s.mirrored or (s.frame & FF_HORIZONTALFLIP)) and ANGLE+ANGLE_90 or ANGLE-ANGLE_90	
		end	
		ghs.tracer = s
		ghs.GSoffsetX = (OFFSETXY) or 1	
		ghs.GSoffsetY = (OFFSETXY) or 1	
		ghs.GSoffsetZ = (OFFSETZ) or s.height/2		
	end		
	return ghs --	
end)

--=============================================================================================
--						EFFECTS USUALLY USED FOR HYPER MYSTIC SONIC
--=============================================================================================

--Spawn HMS detransformation effects.
rawset(_G, "HMS_UntransformGFX", function(s)
	if s.sprst or not leveltime
		return --You JUST spawned!
	end
	local ghs = SMS_ElectricBody(s, 20)
	ghs.color = SKINCOLOR_ZEEJIN5
	ghs.blendmode = AST_TRANSLUCENT
	ghs.rollangle = ANG20	
	for i = 1,2
		ghs = SMS_ElectricBody(s, 14+(10*i))
		if (i == 1)
			ghs.blendmode = AST_TRANSLUCENT
			ghs.color = SKINCOLOR_CRIMSON
			ghs.colorized = true			
		else
			ghs.blendmode = AST_SUBTRACT
		end
		ghs.frame = $+1+i
		ghs.mirrored = true
					
		ghs = SMS_FollowCircle(s, nil, i)
		ghs.blendmode = AST_SUBTRACT
	end
	if SMS_Graphix(s) == 0	--Higher end graphics.
		SMS_RisingFlames(s)
		if not s.SMS_DLtrail
			s.SMS_DLtrail = {timer = 4, blendmode = AST_SUBTRACT, colorized = true, color = SKINCOLOR_RED}
		end
		for i = 0,1
			SMS_Embers(s, s, i)
		end
	end		
	
	if s.skin == "sms"
		s.sms_boostloss = 11
		if P_IsObjectOnGround(s)	
			s.sms_juststomped = 6
		end
	end
	return true --
end)

--A mighty vortex effect, used for Galaxy Storm and reflections.
rawset(_G, "HMS_BigBangVortex", function(s, FUSE, CSCALE, CROLL, ALTFRAME, COLOR)	
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.frame = (ALTFRAME) or E|TR_TRANS10 --Different frame? Such as H|TR_TRANS10
	ghs.fuse = (FUSE) or 14
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.color = (COLOR) or SKINCOLOR_ZEEJIN5
	ghs.destscale = $*99
	ghs.sttfade = true
	if (CSCALE) --Scale sprite, not actual scale! Use FRACUNITs
		ghs.spriteyscale = CSCALE
		ghs.spritexscale = CSCALE
	end	
	if (CROLL) --ANG20?
		ghs.sttroll = CROLL
	else
		ghs.sttroll = (leveltime % 2) and ANG20*3 or -ANG20*3
	end
	return ghs --
end)

--A circle of aura effects around HMS. Repeatedly call this effect for the best success
rawset(_G, "HMS_AuraStorm", function(s, CSCALE, COLOR, FUSE, ALTFRAME, FOLLOWME, YOFFSET)
	local SCALE = (CSCALE) or s.scale
	local ghs
	for i = 1,8
		ghs = P_SpawnMobjFromMobj(s, 
		FixedMul(128*SCALE, cos(s.angle+(ANGLE_45*i) )),
		FixedMul(128*SCALE, sin(s.angle+(ANGLE_45*i))),
		-s.height, MT_SMSAUTOFRAME)
		
		ghs.sprite = SPR_SMX4
		if ALTFRAME --Only supports same-size graphics.
			ghs.frame = ALTFRAME
		else
			ghs.frame = G|TR_TRANS90
		end
		ghs.renderflags = RF_PAPERSPRITE|RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.blendmode = AST_ADD
		ghs.angle = R_PointToAngle2(s.x, s.y, ghs.x, ghs.y)+ANGLE_90
		ghs.momz = 12*s.scale*P_MobjFlip(s) --Scale with player's size, not your own.
		ghs.scale = SCALE --It's also used to calculate spawn dist, see.
		ghs.color = (COLOR) or s.color
		ghs.fuse = (FUSE) or 16
		ghs.destscale = $*99
		ghs.sttfade = true
		if FOLLOWME
			ghs.momx = s.momx/FOLLOWME
			ghs.momy = s.momy/FOLLOWME
			ghs.momz = s.momz/FOLLOWME
		end
		if YOFFSET
			ghs.spriteyoffset = $+YOFFSET
		end
	end
end)

--Create storm of lightning death all around you? Extradist is in FixedInt.(Regular numbers)
rawset(_G, "HMS_BoltStorm", function(s, COLOR, FUSE, EXTRANG, EXTRADIST, MOMZ)
	local ANGLE = (s.momx or s.momy) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
	ANGLE = $+((EXTRAANG) or 1)
	local DIST = 199+((EXTRADIST) or 1)
	
	local ghs
	for i = 1,8
		local RNG = SMSLocalRNG(-10-i, 10+i, s)
		
		ghs = P_SpawnMobjFromMobj(s, 
		FixedMul((DIST+RNG)*s.scale, cos(ANGLE+((ANGLE_45*i)+(RNG*ANG1)))),
		FixedMul((DIST+RNG)*s.scale, sin(ANGLE+((ANGLE_45*i)+(RNG*ANG1)))),
		RNG*s.scale, MT_SMSAUTOFRAME)
		
		RNG = SMSLocalRNG(-10, 10, ghs) --Update RNG.
		
		ghs.sprite = SPR_SMX4
		ghs.frame = (Z+4)|(TR_TRANS10+abs((RNG or 1)/5)*FRACUNIT)
		ghs.blendmode = AST_ADD
		ghs.angle = R_PointToAngle2(s.x, s.y, ghs.x, ghs.y)+ANGLE_90
		ghs.renderflags = RF_PAPERSPRITE|RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.color = (COLOR) or max(SKINCOLOR_SUPERGOLD5, SKINCOLOR_SUPERGOLD2+abs((RNG or 1)/3))
		ghs.fuse = (FUSE) or max(6, abs(RNG))
		ghs.sttfade = true
		ghs.sttfinalframe = Z+4
		ghs.scalespeed = $*4
		ghs.destscale = $*99
		ghs.spritexscale = FRACUNIT+(-900*RNG)
		ghs.spriteyscale = FRACUNIT+(900*RNG)
		ghs.momz = (MOMZ) or 0
	end
end)

--A giant explosion of lightning, and optionally a storm of lightning!
rawset(_G, "HMS_GigaJetBolt", function(s, CSCALE, FUSE)		
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = F|TR_TRANS10
	ghs.fuse = (FUSE) or 16
	if (CSCALE)
		ghs.spriteyscale = CSCALE
		ghs.spritexscale = CSCALE
	end
	ghs.scale = s.scale/4
	ghs.scalespeed = FRACUNIT/3
	ghs.destscale = s.scale*2
	ghs.sttfade = true
	ghs.sttroll = (leveltime % 2) and ANG30 or -ANG30
	ghs.rollangle = ghs.sttroll
	return ghs --
end)

--After effect waves when HMS rolls during an airboost. Air Boost"
rawset(_G, "HMS_RollWaves", function(s, CROLL, FUSE, COLOR, CSCALE, CFRAME, TRACER)
	local ANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	local ghs = P_SpawnMobjFromMobj(s, s.momx,s.momy,s.momz, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_PAPERSPRITE|RF_NOCOLORMAPS
	ghs.angle = ANGLE+ANGLE_90
	ghs.frame = (CFRAME) or Q|TR_TRANS10
	ghs.fuse = (FUSE) or 9
	ghs.scale = s.scale/3
	ghs.scalespeed = FRACUNIT/5
	ghs.destscale = s.scale*3
	ghs.sttfade = true
	ghs.color = (COLOR) or s.color
	if CROLL != nil
		ghs.sttroll = CROLL
		ghs.rollangle = ghs.sttroll
	else
		ghs.sttroll = (s.hmsrotateright) and ANG1*104 or -ANG1*104
		ghs.rollangle = s.rollangle
	end
	if (CSCALE)
		ghs.spriteyscale = CSCALE
		ghs.spritexscale = CSCALE
	end
	if (TRACER)
		ghs.tracer = TRACER
		ghs.GSoffsetX = 48*s.scale
		ghs.GSoffsetY = 48*s.scale
		ghs.GSoffsetZ = 0
	end
	return ghs --			
end)

--HMS' Air Boost Effect. 
rawset(_G, "HMS_AirShatter", function(s, COLOR, CSCALE, FUSE, CFRAME)
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSAUTOFRAME)	
	ghs.sprite = SPR_SMX4
	ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = AST_ADD
	
	ghs.frame = (CFRAME) and CFRAME or T|TR_TRANS30
	ghs.fuse = (FUSE) or 7
	ghs.scale = s.scale/2
	ghs.destscale = FRACUNIT*99
	ghs.sttfade = true
	ghs.color = (COLOR) or s.color
	ghs.rollangle = s.rollangle+(leveltime*(ANG1*68))
	if (CSCALE)
		ghs.spriteyscale = CSCALE
		ghs.spritexscale = CSCALE
	end
	return ghs --			
end)

--Glowing rotating lights from HMS in dark areas.
rawset(_G, "HMS_DarkLight", function(s, p, COLOR)
		local ghs = P_SpawnMobjFromMobj(s, 0,0, (s.height/2)+(11*s.scale*P_MobjFlip(s)), MT_SMSAUTOFRAME)
		ghs.sprite = SPR_AURA
		ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
		ghs.frame = S|TR_TRANS90
		ghs.color = (COLOR) or SKINCOLOR_MYSTICAL1
		ghs.rollangle = leveltime*ANG2
		ghs.scale = s.scale*2
		ghs.tics,ghs.fuse = 2,2
		ghs.momx = s.momx
		ghs.momy = s.momy
		ghs.momz = s.momz
		ghs.scalespeed = $+SMSLocalRNG(-500, 1000, s) //P_RandomRange(-500, 1000)				  
		ghs.destscale = ghs.scalespeed+1
		return ghs
end)

--Create a dark zone for dramatic effect. Only visible to caster. "s" and "t" can be the same.
rawset(_G, "HMS_NegativeZone", function(s, t, FULLWALLS, PURE, ALTFRAME)
	local ghs
	if s.player and t.player
		if s.player.sms_sontransform and not (PURE)
			return --
		end
		ghs = P_SpawnMobjFromMobj(s,
		t.momx+FixedMul(2*t.scale, cos(t.angle)),
		t.momy+FixedMul(2*t.scale, sin(t.angle)),
		t.momz-1500*t.scale,
		MT_SMSSPLAT) //MT_THOK)
		ghs.tics = 2
	else
		if not (s.HMSN_background and s.HMSN_background.valid)
			s.HMSN_background = P_SpawnMobjFromMobj(s,
			t.momx+FixedMul(2*t.scale, cos(t.angle)),
			t.momy+FixedMul(2*t.scale, sin(t.angle)),
			t.momz-1500*t.scale,
			MT_SMSSPLAT) //MT_THOK)
		end
		ghs = s.HMSN_background
		ghs.tics = 3 
	end
	
	ghs.sprite = SPR_NUEX
	ghs.scale = 50*FRACUNIT
	if PURE
		ghs.color = (PURE) or SKINCOLOR_BLACK --Use SKINCOLOR_BLACK for RF_FULLDARK!
		ghs.colorized = true
	else
		ghs.blendmode = AST_SUBTRACT
	end
	ghs.frame = (ALTFRAME) or A|TR_TRANS30
	ghs.renderflags = (ghs.color == SKINCOLOR_BLACK) and RF_FULLDARK|RF_NOCOLORMAPS|RF_PAPERSPRITE or RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_PAPERSPRITE
	ghs.angle = t.angle+ANGLE_90
	ghs.fuse = 99
	ghs.dispoffset = -50
	ghs.height = 0
	ghs.radius = 0
		
	P_MoveOrigin(ghs, 
	t.x+t.momx+FixedMul(50*t.scale, cos(t.angle)), 
	t.y+t.momy+FixedMul(50*t.scale, sin(t.angle)),
	t.z+t.momz-1500*t.scale)
		
	if displayplayer and (displayplayer != t.player)
		ghs.flags2 = MF2_DONTDRAW
	end
	
/*	if FULLWALLS --Tries to encapsulate player in a wall of negative zone.
	local ths
		for i = -1,1,2
			ths = P_SpawnMobjFromMobj(t, 
			t.momx+FixedMul(50*t.scale, cos(t.angle+(ANGLE_90*i)))+
			FixedMul(14*t.scale, cos(t.angle)),
			t.momy+FixedMul(50*t.scale, sin(t.angle+(ANGLE_90*i)))+
			FixedMul(14*t.scale, sin(t.angle)),
			-1500*t.scale, MT_THOK)
			ths.scale = ghs.scale
			ths.angle = t.angle+(i*(ANG1*84))
			ths.tics = ghs.tics
			ths.colorized = ghs.colorized
			ths.blendmode = ghs.blendmode
			ths.color = ghs.color
			ths.frame = ghs.frame
			ths.renderflags = ghs.renderflags
		end 
		--Now splats if possible?
		for i = -1,1,2
			local ths = P_SpawnMobjFromMobj(t, 0,0,t.momz, MT_THOK)
			if (i == 2) and P_IsObjectOnGround(t)
				ths.z = $+FRACUNIT
			else
				ths.z = $+(120*t.scale*i)
			end
			ths.scale = ghs.scale
			ths.height = 0
			ths.radius = 0			
			ths.angle = t.angle+ANGLE_180
			ths.tics = ghs.tics
			ths.colorized = ghs.colorized
			ths.blendmode = ghs.blendmode
			ths.momz = 0
			ths.frame = ghs.frame
			ths.color = ghs.color 		
			ths.flags = $|MF2_SPLAT
			ths.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_FLOORSPRITE
		end --It's not. It doesn't render stuff that big.
	end */
	return ghs
end)

--Add a prism glow to the NovaBlast. mult is required.
rawset(_G, "HMS_NovaBlastShine", function(s, mult)
		local ghs = P_SpawnMobjFromMobj(s, 0,0,s.height/2, MT_THOK)
		ghs.sprite = SPR_AURA
		ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
		if s.player
			ghs.color = SMSLocalRNG(SKINCOLOR_SUPERSKY1, SKINCOLOR_SUPERSKY5, s)
			ghs.frame = (leveltime % 3 == 0) and S|TR_TRANS90 or S|TR_TRANS80
			ghs.momx = $+ ((leveltime % 4 == 0) and s.momx/4 or s.momx/3)
			ghs.momy = $+ ((leveltime % 4 == 0) and s.momy/4 or s.momy/3)
			ghs.scalespeed = max($*4-SMSLocalRNG(1, 65534, s), 5)
			ghs.fuse = s.tics+1
		else
			if (s.state == S_NOVABLAST)
				ghs.frame = S|TR_TRANS60
			else
				ghs.frame = (leveltime % 3 == 0) and S|TR_TRANS90 or S|TR_TRANS80
			end
			ghs.momx = $+s.momx*SMSLocalRNG(-1, 1,s)
			ghs.momy = $+s.momy*SMSLocalRNG(1, -1,s)
			ghs.momz = $+s.momz*SMSLocalRNG(-1, 1,ghs)
			ghs.color = s.color
			ghs.scalespeed = max($*5-SMSLocalRNG(1, 65534, ghs), 5)
			ghs.fuse = s.tics+3
		end
		ghs.tics = $+9		
		ghs.scale = FixedMul(FixedDiv(s.scale, 10), (mult) or 10)
		ghs.rollangle = leveltime*ANG1
		ghs.destscale = $*999
		return ghs --
end)

--Spreads small electric sparks across the ground
rawset(_G, "HMS_GroundSparks", function(s, p, NUM)
	if not A_HMSCheckDeathPit(s)
		NUM = (NUM) or 0
		local RNGANG = SMSLocalRNG(1-NUM,360+NUM,s)
		local ghs = P_SpawnMobjFromMobj(s, 
		FixedMul(SMSLocalRNG(5, 250+NUM,s)*s.scale, cos(RNGANG*ANG1)), 
		FixedMul(SMSLocalRNG(252, 1+NUM,s)*s.scale, sin(RNGANG*ANG1)), 
		s.height/2, MT_SMSAUTOFRAME)
		ghs.sprite = SPR_AURA
		ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
		ghs.blendmode = AST_ADD
		ghs.sttspeed = 1
		local FRAME = SMSLocalRNG(I,P,ghs)
		ghs.frame = FRAME|FF_TRANS10
		ghs.fuse = max(6, 14-FRAME+NUM)
		ghs.sttfade = 9
		ghs.sttfirstframe = I
		ghs.sttfinalframe = P
		P_Thrust(ghs, RNGANG*ANG1, s.scale*6)
		if leveltime % 6 == 0
			ghs.sttroll = -ANG10
			ghs.frame = $|FF_HORIZONTALFLIP
		else
			ghs.sttroll = ANG10
		end
		return ghs --
	end
end)

--HMS currently crashing through the skies like a meteor.
rawset(_G, "HMS_StompGFX", function(s, p)
	local ghs
		for i = 2,4,2
			if i == 2 
				ghs = SMS_FlatWave(s, (s.hmsstompsfx != true) and SKINCOLOR_SUPERSKY5 or SKINCOLOR_FLAME, 13, nil, 0)
				ghs.frame = I|FF_TRANS20
				ghs.spritexoffset = 21*FRACUNIT
				ghs.spriteyoffset = 23*FRACUNIT		
			else
				ghs = SMS_FlatWave(s, (s.hmsstompsfx != true) and SKINCOLOR_SILVER or SKINCOLOR_MYSTICAL5, (s.hmsstompsfx == true) and 20 or 14, 2, 0)
				ghs.frame = (s.hmsstompsfx == true) and Q|FF_TRANS30 or Q|FF_TRANS10
				ghs.spritexscale = (s.hmsstompsfx == true) and $/6 or $/4
				ghs.spriteyscale = (s.hmsstompsfx == true) and $/6 or $/4
				ghs.spritexoffset = 127*FRACUNIT
				ghs.spriteyoffset = 129*FRACUNIT					
			end
			ghs.tracer = s
			ghs.sttspeed = nil
			ghs.sttfade = 9
			ghs.GSoffsetX = -s.scale
			ghs.GSoffsetY = -s.scale
			ghs.GSoffsetZ = (s.hmsstompsfx == true) and -16*s.scale or SMSLocalRNG(-60,-1,s)*s.scale					
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS|RF_NOSPLATBILLBOARD
			ghs.destscale = 999*FRACUNIT
			ghs.GSoffsetang = s.angle
			ghs.sttroll = ANG1*(16+(i*21))
		end
		if (s.hmsstompsfx != true)						
			ghs = SMS_FootSweepSlash(s, FF_TRANS10, leveltime*ANG20)
			if ghs and ghs.valid
				ghs.momz = $+(s.momz/3)
				ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
			end
		end
end)

--When airdashing as HMS usually! This is the slow version. Fast one is specified in LUA_HYPERMYSTICSONIC.
rawset(_G, "HMS_airdashGFX", function(s, p, COLOR, DESTSCALE, ROLLSPEED)

	local SLOWDASHCOLORS = {SKINCOLOR_SMS5R, SKINCOLOR_MYSTICAL3, SKINCOLOR_SKY, SKINCOLOR_SUPERSKY5, SKINCOLOR_BLUEBELL, SKINCOLOR_SUPERSKY3, SKINCOLOR_SMS2R, SKINCOLOR_MINT, SKINCOLOR_SILVER, SKINCOLOR,SKINCOLOR_ORANGE}
	local RNGANG = SMSLocalRNG(0,360,s)
	local PICKCOLOR = COLOR
	local GORELVL = SMS_Gore(s)
	local ths,ghs
	ROLLSPEED = (ROLLSPEED) or ANG30
	
	for i = -1,1,2
		if COLOR == "prism"
			PICKCOLOR = SLOWDASHCOLORS[SMSLocalRNG(1,10+i,s)]
		end
		ghs = HMS_BigBangVortex(s, 10, FRACUNIT, ROLLSPEED, Z+5, PICKCOLOR)	
		ghs.rollangle = (RNGANG*ANG1)+(ANGLE_90*i)
		ghs.sttrollangle = ghs.rollangle
		ghs.sttfade = (leveltime % 4 == 0) and 8 or 6
		ghs.frame = ($ & ~FF_TRANSMASK)|FF_PAPERSPRITE|FF_TRANS70
		ghs.scalespeed = $+(RNGANG*25)
		if not DESTSCALE
			ghs.spritexscale = $+(RNGANG*90)
			ghs.spriteyscale = ghs.spritexscale
		else
			ghs.scale = $/2
			ghs.destscale = DESTSCALE
		end
		ghs.tracer = s
		ghs.GSoffsetX = (leveltime % 4 == 0) and 49*s.scale or -1
		ghs.GSoffsetY = (leveltime % 4 == 0) and 49*s.scale or -1
		ghs.GSoffsetZ = -s.height
		
		if (leveltime % 4 == 0) and COLOR != "prism"
			ghs.dispoffset = 2
			ghs.ditchtracer = 4
			if i == -1	
				if GORELVL > 220
					ghs.flags2 = $|MF2_DONTDRAW
				elseif (p == displayplayer) and CV_FindVar("renderer") == 1
					ghs.flags2 = $|MF2_DONTDRAW --Software nerf.
				end
			end
		else
			ghs.ditchtracer = 2
			ghs.spriteyoffset = $+13*FRACUNIT	
			ghs.momx = s.momx/2
			ghs.momy = s.momy/2
			ghs.momz = s.momz/3
			if i == 1
				if GORELVL > 220
					ghs.flags2 = $|MF2_DONTDRAW			
				elseif (p == displayplayer) and CV_FindVar("renderer") == 1
					ghs.flags2 = $|MF2_DONTDRAW
				end
			end
		end
		if i == 1
			return ths,ghs --Return both
		else
			ths = ghs
		end
	end		
end)

--=============================================================================================
--							HYPER MYSTIC SONIC ANIMATIONS" CODE
--=============================================================================================

--Animate HMS' frames correctly!
rawset(_G, "HMS_Animation", function(s, p, thinker)	
		if (s.state >= S_HMSTRANSFORM1) and (s.state <= S_HMSTRANSFORM9) 
			if not s.hmsspinpass
			and not s.nohmsspin
			and not s.hms_jumprisen
				return false --
			end
		end
		if s.forceHMSyellanim --Animation after popping up next to a player.
			if p.hypermysticsonic
			  	s.forceHMSyellanim = $-1
				
			  	s.sprite = SPR_HMSO
			   	s.frame = ($ & ~FF_FRAMEMASK)|B
			  	s.tics = 5 s.anim_duration = 5
				s.hmsoframe = nil
				s.hmsspinpass = 0
				
				if s.forceHMSyellanim % 6 == 0
					local ghs = SMS_ElectricBody(s, 5, SKINCOLOR_CRIMSON)
					ghs.blendmode = AST_TRANSLUCENT
					ghs.spriteyscale = $+29000
					ghs.spriteyoffset = 4*FRACUNIT
					ghs.spritexscale = $+12000
					ghs.rollangle = SMSLocalRNG(-20,20,s)*ANG1
					if s.tics % 12 == 0
						ghs.mirrored = true
						ghs.color = SKINCOLOR_SMS4R
					end
				end					
			else
				s.forceHMSyellanim = nil --...huh?
			end
			
		elseif (s.sprite == SPR_PLAY)
		or (s.sprite == SPR_SMSO)
		or (s.sprite == SPR_HMSO) and (s.state <= S_PLAY_MELEE_LANDING) and (s.state >= S_PLAY_STND)
				if thinker == 1 --thinkframe.
					local FRAME = (s.frame & FF_FRAMEMASK)
					if (FRAME > J)
						s.frame = ($ & ~FF_FRAMEMASK)|C
					end	
					if s.hmsspinpass and not s.nohmsspin
						s.hmsspinpass = $-1
					end
				end
				s.sprite = SPR_HMSO
						
				--Force a particular frame?
				if s.hmsoframe != nil and (s.nohmsspin or not s.hmsspinpass) 		
					s.frame = ($ & ~FF_FRAMEMASK)|s.hmsoframe
					s.hmsoframe = nil
				elseif s.hmsspinpass and not s.nohmsspin --Special case.
					s.frame = ($ & ~FF_FRAMEMASK)|H	
					if s.hmsspinang != nil
						p.drawangle = s.hmsspinang
					end
				elseif (s.state >= S_PLAY_STND) and (s.state <= S_PLAY_MELEE_LANDING)
					if (s.state == S_PLAY_SPRING) or (p.panim == PA_SPRING) --Spring.
						s.frame = ($ & ~FF_FRAMEMASK)|H
						if s.hmsroflstomped 
							s.hmsroflstomped = false
							s.rollangle = 0
							s.hmsspinpass = 0
						end
					elseif (s.state == S_PLAY_EDGE) or (p.panim == PA_EDGE) --Standing down frame.
						s.frame = ($ & ~FF_FRAMEMASK)|F
					elseif (s.state == S_PLAY_STUN) --Yelling frame
						s.frame = ($ & ~FF_FRAMEMASK)|B
					elseif (p.speed < 35*s.scale) --Standing frame
					or (s.state == S_PLAY_STND)
					or (p.panim == PA_IDLE)
					or p.skidtime
						if not s.hmsidleanim or (s.hmsidleanim < 6)
							s.frame = ($ & ~FF_FRAMEMASK)|C
						elseif (s.hmsidleanim < 12)
							s.frame = ($ & ~FF_FRAMEMASK)|K
						elseif (s.hmsidleanim < 18)
							s.frame = ($ & ~FF_FRAMEMASK)|L
						else
							s.frame = ($ & ~FF_FRAMEMASK)|K			
						end
					end
				end
		end
end)

