//reveries shields

freeslot(
	"MT_REVI_TORB",
	"MT_REVI_THORN",
	"MT_REVI_ASH",
	"S_REVI_TORB",
	"S_REVI_STABS",
	"S_REVI_THORN1",
	"S_REVI_THORN2",
	"S_REVI_ASH",
	"SPR_THORNSHIELD",
	"sfx_tswtch")

sfxinfo[sfx_shield].caption = "Shield"
sfxinfo[sfx_s3k71].caption = "Thorn Shield"
sfxinfo[sfx_s3kcfs].caption = "Elemental Rise"
sfxinfo[sfx_tswtch].caption = "Thorn Switch"
	
mobjinfo[MT_REVI_TORB] = {
	doomednum = -1,
	spawnhealth = 1000,
	spawnstate = S_REVI_TORB,
	speed = SH_THORN,
	radius = 64<<FRACBITS,
	height = 64<<FRACBITS,
	flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY,
	dispoffset = 4}
	
mobjinfo[MT_REVI_THORN] = {
	doomednum = -1,
	spawnhealth = 1,
	spawnstate = S_REVI_THORN1,
	deathstate = S_NULL,
	speed = 40<<FRACBITS,
	radius = 32<<FRACBITS,
	height = 32<<FRACBITS,
	mass = DMG_SPIKE,
	flags = MF_NOGRAVITY|MF_NOBLOCKMAP|MF_MISSILE}
	
mobjinfo[MT_REVI_ASH] = {
	doomednum = -1,
	spawnhealth = 1000,
	spawnstate = S_REVI_ASH,
	speed = SH_ASH,
	radius = 64<<FRACBITS,
	height = 64<<FRACBITS,
	flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY,
	dispoffset = 4}

states[S_REVI_TORB] = {SPR_PITY, 0|FF_ANIMATE|TR_TRANS30, -1, NULL, 11, 2, S_NULL}
states[S_REVI_STABS] = {SPR_THORNSHIELD, 0|TR_TRANS40, -1, NULL, 0, 0, S_NULL}
states[S_REVI_THORN1] = {SPR_THORNSHIELD, 1, -1, NULL, 0, 0, S_NULL}
states[S_REVI_THORN2] = {SPR_THORNSHIELD, 2, -1, NULL, 0, 0, S_NULL}
states[S_REVI_ASH] = {SPR_ARMA, 0|FF_ANIMATE|TR_TRANS40, -1, NULL, 15, 2, S_NULL}

//globals

rawset(_G, "SH_ASH", 62)
rawset(_G, "SH_THORN", SH_ARMAGEDDON|SH_PROTECTSPIKE)

function P_DoJumpShield(player)
	if (player.pflags & PF_THOKKED)
		return
	end
	local pmo = player.mo
	player.pflags = $ & ~PF_JUMPED & ~PF_NOJUMPDAMAGE & ~PF_STARTJUMP
	player.secondjump = 0
	P_DoJump(player, false)
	player.pflags = $|PF_JUMPED|PF_THOKKED|PF_SHIELDABILITY & ~PF_STARTJUMP & ~PF_SPINNING & ~PF_NOJUMPDAMAGE & ~PF_BOUNCING
	player.secondjump = 1
	if ((player.powers[pw_shield] & SH_PROTECTELECTRIC) == SH_PROTECTELECTRIC)
		pmo.state = S_PLAY_ROLL
		for i = 0, 8 do
			local spark = P_SpawnMobjFromMobj(pmo, 0, 0, 0, MT_THUNDERCOIN_SPARK)
			P_InstaThrust(spark, (pmo.angle+ANGLE_45) + (i*ANGLE_90), FixedMul(4<<FRACBITS, spark.scale))
			if (i < 5)
				P_SetObjectMomZ(spark, 4<<FRACBITS, false)
			else
				P_SetObjectMomZ(spark, -4<<FRACBITS, false)
			end
			spark.fuse = 18
		end
		S_StartSound(pmo, sfx_s3k45)
	else
		pmo.state = S_PLAY_FALL
		player.pflags = $|PF_NOJUMPDAMAGE
		S_StartSound(pmo, sfx_wdjump)
	end
end

//tables

local FRACUNIT = FRACUNIT
local FRACBITS = FRACBITS
local TICRATE = TICRATE
local pw_shield = pw_shield
local SH_NOSTACK = SH_NOSTACK
local SH_ELEMENTAL = SH_ELEMENTAL
local SH_WHIRLWIND = SH_WHIRLWIND
local SH_FORCE = SH_FORCE
local SH_PITY = SH_PITY
local SH_ARMAGEDDON = SH_ARMAGEDDON
local SH_ASH = SH_ASH
local SH_THORN = SH_THORN
local SH_FLAMEAURA = SH_FLAMEAURA
local SH_THUNDERCOIN = SH_THUNDERCOIN

local marios = {}
marios["mario"] = true
marios["luigi"] = true

//functions

local function isplayer(mo)
	if mo.player
		return true
	else
		return false
	end
end

local function controlbits(player)
	if (player.pflags & PF_ANALOGMODE)
		return FixedAngle((9<<FRACBITS)/2)
	else
		return FixedAngle(7<<FRACBITS)
	end
end

//main

addHook("PlayerSpawn", function(player)
	player.sonictrick = 0
	player.amytrick = false
	player.rsonicpeel = 0
	player.rfakedropdash = false
	player.metalsalt = 0
	player.reveriesFshield = 0
	player.reveriesEshield = 0
end)

addHook("PreThinkFrame", do
	for player in players.iterate
		local pmo = player.mo
		if not (pmo)
			continue
		end
		if (player.reveriesFshield)
			if not (player.powers[pw_shield] & SH_FORCE)
				player.reveriesFshield = 0
				continue
			end
			if (pmo.state != S_PLAY_JUMP)
				pmo.state = S_PLAY_JUMP
			end
			pmo.angle = player.cmd.angleturn<<FRACBITS
			if (player.cmd.sidemove)
				if (player.cmd.sidemove < -20)
					pmo.angle = $+controlbits(player)
				elseif (player.cmd.sidemove > 20)
					pmo.angle = $-controlbits(player)
				end
				player.cmd.sidemove = 0
			end
			player.drawangle = pmo.angle
			player.cmd.angleturn = pmo.angle>>FRACBITS
			if (player.pflags & PF_SHIELDDOWN)
				pmo.momx = 0
				pmo.momy = 0
				pmo.momz = 0
				player.pflags = $|PF_STASIS
				if (player.cmd.buttons & BT_JUMP) and not (player.pflags & PF_JUMPDOWN)
					P_SetObjectMomZ(pmo, -(player.reveriesFshield/3), false)
					player.reveriesFshield = 0
					player.pflags = $|PF_SHIELDABILITY & ~PF_STASIS
					S_StartSound(pmo, sfx_prloop)
					continue
				end
			else
				P_Thrust(pmo, pmo.angle, player.reveriesFshield)
				player.reveriesFshield = 0
				player.pflags = $|PF_SHIELDABILITY & ~PF_STASIS
				S_StartSound(pmo, sfx_prloop)
			end
			continue
		end
		if ((player.powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
			if P_IsObjectOnGround(pmo) and ((player.panim == PA_RUN) or (player.panim == PA_DASH)) and (player.speed >= FixedMul(player.runspeed, pmo.scale)) and not (leveltime%2)
				P_ElementalFire(player, false)
			end
			if (player.reveriesEshield)
				if (pmo.eflags & MFE_SPRUNG) or P_PlayerInPain(player) or (player.playerstate != PST_LIVE)
					player.reveriesEshield = 0
					continue
				end
				if not (leveltime%2)
					S_StartSound(pmo, sfx_s3kcfs)
				end
				local rocketfire = P_SpawnMobjFromMobj(pmo, 0, 0, 0, MT_SPINDUST)
				if (pmo.eflags & MFE_UNDERWATER) and not (pmo.eflags & MFE_TOUCHLAVA)
					rocketfire.state = S_SPINDUST_BUBBLE1
					P_SetObjectMomZ(pmo, FRACUNIT/2, true)
				else
					rocketfire.state = S_SPINDUST_FIRE1
					P_SetObjectMomZ(pmo, FRACUNIT, true)
				end
				P_InstaThrust(rocketfire, P_RandomRange(1, 360)*ANG1, FixedMul(10<<FRACBITS, rocketfire.scale))
				player.reveriesEshield = $-1
				pmo.momy = 0
				pmo.momx = 0
				pmo.state = S_PLAY_SPRING
				player.pflags = $|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED & ~PF_STARTJUMP & ~PF_SPINNING
				player.cmd.buttons = $ & ~BT_SPIN
				if (player.reveriesEshield < 2) or (P_MobjFlip(pmo)*pmo.momz < FixedMul(2<<FRACBITS, pmo.scale)) or ((player.cmd.buttons & BT_SHIELD) and not (player.pflags & PF_SHIELDDOWN))
					S_StopSoundByID(pmo, sfx_s3kcfl)
					pmo.momz = 0
					player.reveriesEshield = 0
					if (pmo.eflags & MFE_UNDERWATER) and not (pmo.eflags & MFE_TOUCHLAVA)
						A_OldRingExplode(pmo, MT_EXTRALARGEBUBBLE)
						P_RadiusAttack(pmo, pmo, 192<<FRACBITS, DMG_WATER, true)
						S_StartSound(pmo, sfx_s3k57)
					else
						A_OldRingExplode(pmo, MT_FIREBALLTRAIL)
						P_RadiusAttack(pmo, pmo, 192<<FRACBITS, DMG_FIRE, true)
						S_StartSound(pmo, sfx_s3k6c)
					end
					if (player.cmd.forwardmove != 0) or (player.cmd.sidemove != 0)
						P_InstaThrust(pmo, (player.cmd.angleturn<<FRACBITS + R_PointToAngle2(0, 0, player.cmd.forwardmove<<FRACBITS, -player.cmd.sidemove<<FRACBITS)), FixedMul(5<<FRACBITS, pmo.scale))
					end
				end
			end
			continue
		elseif (player.reveriesEshield)
			player.reveriesEshield = 0
		end
		if ((player.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND) and (player.pflags & PF_SHIELDABILITY) and (P_MobjFlip(pmo)*pmo.momz > 0)
			local r = FixedMul(4, pmo.radius)
			local wind = P_SpawnMobjFromMobj(pmo, P_RandomRange(-r,r)<<FRACBITS, P_RandomRange(-r,r)<<FRACBITS, -48<<FRACBITS, MT_PARTICLE)
			wind.frame = ($ & FF_FRAMEMASK)|TR_TRANS70
			wind.fuse = TICRATE/3
			wind.momx = pmo.momx
			wind.momy = pmo.momy
			wind.destscale = $/3
			P_SetObjectMomZ(wind, max(pmo.momz*3/2, 15<<FRACBITS), false)
			continue
		end
	end
end)

//damage

addHook("ShouldDamage", function(sonic, inflictor, source, damage, element)
	if (sonic.player.homing and ((sonic.player.powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT))
		return false
	end
end, MT_PLAYER)


addHook("MobjDamage", function(force, inflictor, source)
	if not (force.player)
		return nil
	end
	if (force.player.powers[pw_shield] & SH_FORCE)
		if not (force.player.powers[pw_shield] & SH_FORCEHP)
			return false
		end
		S_StartSound(force, sfx_frcssg)
		P_RemoveShield(force.player)
		force.player.powers[pw_flashing] = flashingtics-1
		return true
	end
	if (((force.player.powers[pw_shield] & SH_NOSTACK) == SH_THORN) and source)
		P_DamageMobj(source, force, force, 3, DMG_SPIKE)
	end
end, MT_PLAYER)

addHook("PlayerCanDamage", function(player, enemy)
	if (player.reveriesEshield)
		return true
	end
end)

//specials

addHook("ShieldSpecial", function(player)
	local pmo = player.mo
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND)
		P_DoJumpShield(player)
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_THORN)
		player.pflags = $|PF_THOKKED|PF_SHIELDABILITY & ~PF_STARTJUMP & ~PF_SPINNING
		player.secondjump = 1
		S_StartSound(pmo, sfx_tswtch)
		for t = 1, 9
			local snarb = P_SpawnMobjFromMobj(pmo, 0, 0, 0, MT_REVI_THORN)
			snarb.target = pmo
			if (t < 9)
				P_InstaThrust(snarb, t*ANGLE_45, FixedMul(7<<FRACBITS, snarb.scale))
			end
			if (pmo.momz < 0)
				if not (pmo.eflags & MFE_VERTICALFLIP)
					snarb.state = S_REVI_THORN2
				end
				snarb.momz = FixedMul(-9<<FRACBITS, snarb.scale)
			else
				if (pmo.eflags & MFE_VERTICALFLIP)
					snarb.state = S_REVI_THORN2
				end
				snarb.momz = FixedMul(9<<FRACBITS, snarb.scale)
			end
		end
		pmo.momz = -$
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN)
		P_DoJumpShield(player)
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA)
		player.secondjump = 1
		player.pflags = $|PF_THOKKED|PF_SHIELDABILITY & ~PF_NOJUMPDAMAGE
		P_Thrust(pmo, pmo.angle, max(FixedMul(61<<FRACBITS, pmo.scale)-player.speed, FixedMul(61<<FRACBITS - FixedSqrt(FixedDiv(player.speed*20, pmo.scale)), pmo.scale)))
		pmo.state = S_PLAY_ROLL
		if mariomode
			S_StartSound(pmo, sfx_mario7)
		else
			S_StartSound(pmo, sfx_s3k43)
		end
		return true
	end
	if (player.powers[pw_shield] & SH_FORCE)
		player.secondjump = 1
		S_StartSound(pmo, sfx_ngskid)
		player.pflags = $|PF_THOKKED|PF_SHIELDABILITY & ~PF_STARTJUMP & ~PF_SPINNING
		pmo.state = S_PLAY_JUMP
		player.reveriesFshield = player.speed+1
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
		player.secondjump = 1
		player.pflags = $|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED & ~PF_STARTJUMP & ~PF_SPINNING
		player.reveriesEshield = TICRATE*16/19
		if (pmo.eflags & MFE_UNDERWATER)
			P_SetObjectMomZ(pmo, 3<<FRACBITS, false)
		else
			P_SetObjectMomZ(pmo, 5<<FRACBITS, false)
		end
		pmo.state = S_PLAY_SPRING
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
		P_BlackOw(player)
		P_SwitchShield(player, SH_ASH)
		return true
	end
end)

local prettyloaded = false

addHook("ShieldSpawn", function(player)
	local pmo = player.mo
	if (mobjinfo[MT_PITY_ORB].seestate != S_NULL) //pretty shield compat
		mobjinfo[MT_ELEMENTAL_ORB].spawnstate = S_ELEM1
		mobjinfo[MT_ELEMENTAL_ORB].seestate = S_ELEMF1
		mobjinfo[MT_ELEMENTAL_ORB].painstate = S_ELEM13
		mobjinfo[MT_ELEMENTAL_ORB].raisestate = S_ELEMF9
		mobjinfo[MT_ARMAGEDDON_ORB].spawnstate = S_ARMA1
		mobjinfo[MT_ARMAGEDDON_ORB].seestate = S_ARMF1
		mobjinfo[MT_PITY_ORB].seestate = S_NULL
		prettyloaded = true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_THORN)
		for shield in mobjs.iterate()
			if (shield.type != MT_REVI_TORB)
				continue
			end
			if (shield.target) and (shield.target == pmo)
				pcall(P_RemoveMobj, shield)
			end
		end
		local thorn = P_SpawnMobjFromMobj(pmo, 0, 0, 0, MT_REVI_TORB)
		thorn.threshold = SH_THORN
		thorn.target = pmo
		thorn.flags2 = $|MF2_SHIELD
		local stabs = P_SpawnMobjFromMobj(thorn, 0, 0, 0, MT_OVERLAY)
		stabs.state = S_REVI_STABS
		stabs.target = thorn
		stabs.spritexscale = FixedMul($, 11*FRACUNIT/16)
		stabs.spriteyscale = FixedMul($, 11*FRACUNIT/16)
		if (pmo.skin == "adventuresonic")
			thorn.color = SKINCOLOR_EMERALD
			thorn.colorized = true
			thorn.blendmode = AST_ADD
			stabs.blendmode = AST_REVERSESUBTRACT
		elseif (prettyloaded)
			stabs.frame = $ & ~TR_TRANS40
			stabs.color = SKINCOLOR_MINT
			stabs.colorized = true
			stabs.blendmode = AST_REVERSESUBTRACT
		end
		return true
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_ASH)
		for shield in mobjs.iterate()
			if (shield.type != MT_REVI_ASH)
				continue
			end
			if (shield.target) and (shield.target == pmo)
				pcall(P_RemoveMobj, shield)
			end
		end
		local ash = P_SpawnMobjFromMobj(pmo, 0, 0, 0, MT_REVI_ASH)
		ash.threshold = SH_ASH
		ash.target = pmo
		ash.flags2 = $|MF2_SHIELD
		ash.color = SKINCOLOR_CARBON
		ash.colorized = true
		if (pmo.skin == "adventuresonic")
			ash.frame = $|TR_TRANS70
			ash.blendmode = AST_TRANSLUCENT
			ash.color = SKINCOLOR_BLACK
		elseif (prettyloaded)
			ash.blendmode = AST_REVERSESUBTRACT
		end
		return true
	end
	if marios[pmo.skin]
		return true
	end
	if (player.powers[pw_shield] == SH_PITY)
		P_SwitchShield(player, SH_ASH)
		return true
	end
	if not (gametyperules & GTR_FRIENDLY)
		return false
	end
	if ((player.powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
		if (player.reveriesAshield == nil)
			return false
		end
		S_StartSound(NULL, sfx_bkpoof)
		P_StartQuake(62<<FRACBITS, TICRATE*6/5, {pmo.x, pmo.y, pmo.z}, FixedMul(5000<<FRACBITS, pmo.scale))
		for enemy in mobjs.iterate()
			if (enemy.type == MT_TARGET)
				P_AddPlayerScore(player, 2000)
				P_DamageMobj(enemy, pmo, pmo, 1000, DMG_NUKE)
				continue
			end
			if (enemy == pmo)
				P_FlashPal(enemy.player, PAL_NUKE, TICRATE*5/6)
				if not (player.reveriesAshield & SH_NOSTACK)
					P_SwitchShield(player, SH_ASH)
				end
				continue
			end
			if (enemy.player)
				if not (enemy.player.powers[pw_shield] & SH_NOSTACK)
					P_SwitchShield(enemy.player, SH_ASH)
				end
				continue
			end
			if (enemy.valid) and (enemy.health) and (enemy.flags & (MF_SHOOTABLE|MF_ENEMY|MF_BOSS)) and not (enemy.flags & MF_MONITOR)
				P_AddPlayerScore(player, 1)
			else
				continue
			end
			if R_PointToDist2(pmo.x, pmo.y, enemy.x, enemy.y) <= FixedMul(5000<<FRACBITS, pmo.scale)
				P_KillMobj(enemy, enemy, enemy)
				P_AddPlayerScore(player, 750)
				continue
			end
			if (enemy.flags & MF_ENEMY) and not (enemy.flags & MF_BOSS)
				if (enemy.spawnhealth == 0)
					P_KillMobj(enemy, enemy, enemy)
					P_AddPlayerScore(player, 350)
				elseif P_RandomChance(FRACUNIT/5)
					P_KillMobj(enemy, enemy, enemy)
					P_AddPlayerScore(player, 350)
				end
			end
		end
		if ((player.powers[pw_shield] & SH_NOSTACK) != SH_ASH)
			player.powers[pw_shield] = player.reveriesAshield
		end
		player.reveriesAshield = nil
		return true
	end
end)
	
//other

addHook("MobjThinker", function(shield)
	if (shield.target) and shield.target.valid
		if (shield.state == S_REVI_STABS) and not (leveltime%4)
			shield.rollangle = $+ANGLE_22h
		end
		if (shield.target.type == MT_ELEMENTAL_ORB) and pcall(isplayer, shield.target.target)
			if ((shield.target.target.eflags & MFE_UNDERWATER or shield.target.target.eflags & MFE_TOUCHWATER) and not (shield.target.target.eflags & MFE_TOUCHLAVA)) or (shield.target.target.player.reveriesEshield)
				shield.flags2 = $|MF2_DONTDRAW
			elseif (shield.flags2 & MF2_DONTDRAW)
				shield.flags2 = $ & ~MF2_DONTDRAW
			end
			if (shield.target.target.player.reveriesEshield) and not (shield.stomping) and not ((shield.target.target.eflags & MFE_UNDERWATER) and not (shield.target.target.eflags & MFE_TOUCHLAVA))
				shield.target.state = shield.target.info.raisestate
				shield.target.renderflags = $ ^^ RF_VERTICALFLIP
				shield.target.spriteyoffset = 8<<FRACBITS
				shield.stomping = true
			elseif (shield.stomping)
				shield.target.state = shield.target.info.spawnstate
				shield.target.renderflags = $ ^^ RF_VERTICALFLIP
				shield.target.spriteyoffset = 0
				shield.stomping = false
			end
		end
	end
end, MT_OVERLAY)

addHook("MobjThinker", function(shield)
	if (shield.target) and not (leveltime%13)
		A_BossScream(shield.target, 0, MT_SMOKE)
	end
end, MT_REVI_ASH)

addHook("MobjThinker", function(actor)
	if pcall(isplayer, actor.target) and (gametyperules & GTR_FRIENDLY)
		S_StopSoundByID(actor.target, sfx_armasg)
		if not (actor.foundplayer)
			actor.foundplayer = true
			actor.target.player.reveriesAshield = actor.target.player.powers[pw_shield]
		end
	end
end, MT_ARMAGEDDON_ICON)

addHook("MobjThinker", function(actor)
	if pcall(isplayer, actor.target) and (gametyperules & GTR_FRIENDLY)
		S_StopSoundByID(actor.target, sfx_shield)
		if (actor.state == S_PITY_ICON2) and not (actor.foundplayer)
			actor.foundplayer = true
			S_StartSound(actor.target, sfx_s3k71)
			P_SwitchShield(actor.target.player, SH_THORN)
		end
	end
end, MT_PITY_ICON)

addHook("MobjRemoved", function(thorn)
	for p = 1, 8
		local psn = P_SpawnMobjFromMobj(thorn, 0, 0, 0, MT_EXPLODE)
		psn.color = SKINCOLOR_CHERRY
		psn.colorized = true
		psn.flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT
		psn.fuse = TICRATE/2
		pcall(P_InstaThrust, psn, p*ANGLE_45, FixedMul(11<<FRACBITS, psn.scale))
		pcall(P_RadiusAttack, psn, thorn.target, 128<<FRACBITS, DMG_NUKE, true)
	end
end, MT_REVI_THORN)

addHook("MapThingSpawn", function(bomb)
	if (gametyperules & GTR_FRIENDLY)
		pcall(P_SpawnMobjFromMobj, bomb, 0, 0, 0, MT_ARMAGEDDON_BOX)
		pcall(P_RemoveMobj, bomb)
	end
end, MT_ARMAGEDDON_GOLDBOX)
