// content by Luigifan

//----------- ability definition -----------

--actions
freeslot(
"S_PLAY_SONJA_SLOWTIME",
"S_PLAY_SONJA_REWINDA",
"S_PLAY_SONJA_REWINDB",
"S_PLAY_SONJA_REWINDC",
"S_PLAY_SONJA_CHRONOEVASION",
"S_PLAY_SONJA_CHRONOEVASIONCOUNTERPREP",
"S_PLAY_SONJA_CHRONOEVASIONCOUNTER"
)

--passive abilities
freeslot(
"S_PLAY_SONJA_DANGERRADAR",
"S_PLAY_SONJA_DELAYENEMIES",
"S_PLAY_SONJA_ADVANCENOTICE"
)

--sound effects

// main thinker
addHook("ThinkFrame", function()
	for player in players.iterate do
		if player.mo.skin ~= "sonja_belnades" then
			continue
		end
	end
end)

//Slow Time (Code to slow down everything but Sonja on command)
--mobjthinker to store momentum-related variables in order to return them after setting them to zero during time slow
addHook("MobjThinker", function(mobj)
	if mobj and mobj.valid
	and not ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
		if mobj.preslowxmom == nil then
			mobj.preslowxmom = 0
		end
		if mobj.preslowymom == nil then
			mobj.preslowymom = 0
		end
		if mobj.preslowzmom == nil then
			mobj.preslowzmom = 0
		end
		if mobj.timefrozen == nil then
			mobj.timefrozen = 0
		end
		if mobj.timefrozen > 0 then
			if not (leveltime%TICRATE) then
				mobj.timefrozen = $ - 1
			end
		end
		if mobj.timefrozen == 1 then --failsafe against time slow being released on the exact tic that everything stops moving
			mobj.xmom = mobj.preslowxmom
			mobj.ymom = mobj.preslowymom
			mobj.zmom = mobj.preslowzmom
		end
	end
end, MT_NULL) --run this for everything

--Basic time slow
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		/*if player.custom1down == nil then
			player.custom1down = 0
		end
		
		if player.cmd.buttons & BT_CUSTOM1 > 0 then
			player.custom1down = $1 + 1
		else
			player.custom1down = 0
		end*/
		
		if player.slowedtime == nil then
			player.slowedtime = 0
		end
		
		if player.slowedtimecheckpoint == nil then
			player.slowedtimecheckpoint = 0
		end
		
		if player.custom1down > 1 --if Custom 1 is being held
		and player.custom2down == 0 --if Custom 2 is not being held
		and player.tossflagdown == 0 --if Toss Flag is not being held
		and player.mo.energy >= 3*FRACUNIT then
			if not (player.custom1down%TICRATE) then --every second
				player.mo.energy = $ - 3*FRACUNIT --reduce energy by 3
			end
			if not (player.custom1down%(15)-1) then --on the tic before every 15th tic (14, 29, 44, 59, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.preslowxmom = mobj.xmom --store x-axis momentum
						mobj.preslowymom = mobj.ymom --store y-axis momentum
						mobj.preslowzmom = mobj.zmom --store z-axis momentum
					end
				end
			end
			if not (player.custom1down%15) then --every 15th tic
				player.slowedtime = $ + 1 --remove a tic from the player's HUD time
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue --don't slow down Sonja herself
					else
						mobj.xmom = 0 --remove x-axis momentum
						mobj.ymom = 0 --remove y-axis momentum
						mobj.zmom = 0 --remove z-axis momentum
						mobj.tics = $ + 1 --delay next action
						mobj.timefrozen = 2 --set failsafe
						mobj.reactiontime = $ + 1 --delay next action
						mobj.movecount = $ + 1 --delay next action
						mobj.threshold = $ + 1 --delay next action
					end
				end
			end
			if not (player.custom1down%(15)+1) then --on the tic after every 15th tic (16, 31, 46, 61, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.xmom = mobj.preslowxmom --return x-axis momentum
						mobj.ymom = mobj.preslowymom --return y-axis momentum
						mobj.zmom = mobj.preslowzmom --return z-axis momentum
					end
				end
			end
		end
	end
end)

--Stronger time slow
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if player.custom1down > 1 --if Custom 1 is being held
		and player.custom2down > 1 --if Custom 2 is being held
		and player.custom1down > player.custom2down --if Custom 1 was pressed before Custom 2
		and player.tossflagdown == 0 --if Toss Flag is not being held
		and player.mo.energy >= 6*FRACUNIT then
			if not (player.custom1down%TICRATE) then --every second
				player.mo.energy = $ - 6*FRACUNIT --reduce energy by 6
			end
			if not (player.custom1down%(12)-1) then --on the tic before every 12th tic (11, 23, 35, 47, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.preslowxmom = mobj.xmom --store x-axis momentum
						mobj.preslowymom = mobj.ymom --store y-axis momentum
						mobj.preslowzmom = mobj.zmom --store z-axis momentum
					end
				end
			end
			if not (player.custom1down%12) then --every 12th tic
				player.slowedtime = $ + 1 --remove a tic from the player's HUD time
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue --don't slow down Sonja herself
					else
						mobj.xmom = 0 --remove x-axis momentum
						mobj.ymom = 0 --remove y-axis momentum
						mobj.zmom = 0 --remove z-axis momentum
						mobj.timefrozen = 2 --set failsafe
						mobj.tics = $ + 1 --delay next action
						mobj.reactiontime = $ + 1 --delay next action
						mobj.movecount = $ + 1 --delay next action
						mobj.threshold = $ + 1 --delay next action
					end
				end
			end
			if not (player.custom1down%(12)+1) then --on the tic after every 12th tic (13, 25, 37, 49, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.xmom = mobj.preslowxmom --return x-axis momentum
						mobj.ymom = mobj.preslowymom --return y-axis momentum
						mobj.zmom = mobj.preslowzmom --return z-axis momentum
					end
				end
			end
		end
	end
end)

--Weaker time slow
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if player.custom1down > 1 --if Custom 1 is being held
		and player.custom2down == 0 --if Custom 2 is not being held
		and player.tossflagdown > 1 --if Toss Flag is being held
		and player.custom1down > player.tossflagdown --if Custom 1 was pressed before Toss Flag
		and player.mo.energy >= 3*FRACUNIT/2 then
			if not (player.custom1down%TICRATE) then --every second
				player.mo.energy = $ - 3*FRACUNIT/2 --reduce energy by 1.5
			end
			if not (player.custom1down%(20)-1) then --on the tic before every 20th tic (19, 39, 59, 79, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.preslowxmom = mobj.xmom --store x-axis momentum
						mobj.preslowymom = mobj.ymom --store y-axis momentum
						mobj.preslowzmom = mobj.zmom --store z-axis momentum
					end
				end
			end
			if not (player.custom1down%20) then --every 20th tic
				player.slowedtime = $ + 1 --remove a tic from the player's HUD time
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue --don't slow down Sonja herself
					else
						mobj.xmom = 0 --remove x-axis momentum
						mobj.ymom = 0 --remove y-axis momentum
						mobj.zmom = 0 --remove z-axis momentum
						mobj.timefrozen = 2 --set failsafe
						mobj.tics = $ + 1 --delay next action
						mobj.reactiontime = $ + 1 --delay next action
						mobj.movecount = $ + 1 --delay next action
						mobj.threshold = $ + 1 --delay next action
					end
				end
			end
			if not (player.custom1down%(20)+1) then --on the tic after every 20th tic (21, 41, 61, 81, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.xmom = mobj.preslowxmom --return x-axis momentum
						mobj.ymom = mobj.preslowymom --return y-axis momentum
						mobj.zmom = mobj.preslowzmom --return z-axis momentum
					end
				end
			end
		end
	end
end)

--Even stronger time slow
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if player.custom1down > 1 --if Custom 1 is being held
		and player.custom2down > 1 --if Custom 2 is being held
		and player.custom1down > player.custom2down --if Custom 1 was pressed before Custom 2
		and player.tossflagdown > 1 --if Toss Flag is being held
		and player.custom1down > player.tossflagdown --if Custom 1 was pressed before Toss Flag
		and player.tossflagdown > player.custom2down --if Toss Flag was pressed before Custom 2
		and player.mo.energy >= 10*FRACUNIT then
			if not (player.custom1down%TICRATE) then --every second
				player.mo.energy = $ - 10*FRACUNIT --reduce energy by 10
			end
			if not (player.custom1down%(8)-1) then --on the tic before every 8th tic (7, 15, 23, 31, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.preslowxmom = mobj.xmom --store x-axis momentum
						mobj.preslowymom = mobj.ymom --store y-axis momentum
						mobj.preslowzmom = mobj.zmom --store z-axis momentum
					end
				end
			end
			if not (player.custom1down%8) then --every 8th tic
				player.slowedtime = $ + 1 --remove a tic from the player's HUD time
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue --don't slow down Sonja herself
					else
						mobj.xmom = 0 --remove x-axis momentum
						mobj.ymom = 0 --remove y-axis momentum
						mobj.zmom = 0 --remove z-axis momentum
						mobj.timefrozen = 2 --set failsafe
						mobj.tics = $ + 1 --delay next action
						mobj.reactiontime = $ + 1 --delay next action
						mobj.movecount = $ + 1 --delay next action
						mobj.threshold = $ + 1 --delay next action
					end
				end
			end
			if not (player.custom1down%(8)+1) then --on the tic after every 8th tic (9, 17, 25, 33, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo -- exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.xmom = mobj.preslowxmom --return x-axis momentum
						mobj.ymom = mobj.preslowymom --return y-axis momentum
						mobj.zmom = mobj.preslowzmom --return z-axis momentum
					end
				end
			end
		end
	end
end)

--High-power time slow
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if player.custom1down > 1 --if Custom 1 is being held
		and player.custom2down > 1 --if Custom 2 is being held
		and player.custom1down > player.custom2down --if Custom 1 was pressed before Custom 2
		and player.tossflagdown > 1 --if Toss Flag is being held
		and player.custom1down > player.tossflagdown --if Custom 1 was pressed before Toss Flag
		and player.custom2down > player.tossflagdown --if Custom 2 was pressed before Toss Flag
		and player.mo.energy >= 15*FRACUNIT then
			if not (player.custom1down%TICRATE) then --every second
				player.mo.energy = $ - 15*FRACUNIT --reduce energy by 15
			end
			if not (player.custom1down%(4)-1) then --on the tic before every 4th tic (3, 7, 11, 15, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.preslowxmom = mobj.xmom --store x-axis momentum
						mobj.preslowymom = mobj.ymom --store y-axis momentum
						mobj.preslowzmom = mobj.zmom --store z-axis momentum
					end
				end
			end
			if not (player.custom1down%4) then --every 4th tic
				player.slowedtime = $ + 1 --remove a tic from the player's HUD time
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue --don't slow down Sonja herself
					else
						mobj.xmom = 0 --remove x-axis momentum
						mobj.ymom = 0 --remove y-axis momentum
						mobj.zmom = 0 --remove z-axis momentum
						mobj.timefrozen = 2 --set failsafe
						mobj.tics = $ + 1 --delay next action
						mobj.reactiontime = $ + 1 --delay next action
						mobj.movecount = $ + 1 --delay next action
						mobj.threshold = $ + 1 --delay next action
					end
				end
			end
			if not (player.custom1down%(4)+1) then --on the tic after every 4th tic (5, 9, 13, 17, etc.)
				for mobj in mobjs.iterate() do
					if mobj == consoleplayer.mo --exclude Sonja
					or ((mobj.flags & MF_AMBIENT) or (mobj.flags & MF_BOXICON) or (mobj.flags & MF_MONITOR) or (mobj.flags & MF_NOTHINK) or (mobj.flags & MF_SCENERY)) then --exclude some mobjs in order to preserve processing power
						continue
					else
						mobj.xmom = mobj.preslowxmom --return x-axis momentum
						mobj.ymom = mobj.preslowymom --return y-axis momentum
						mobj.zmom = mobj.preslowzmom --return z-axis momentum
					end
				end
			end
		end
	end
end)

//Rewind (Code to rewind time)
--Code to keep a log of previous states and positions of objects and level elements for the past several seconds

--Version 1 - lowest energy consumption, Sonja cannot move
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		/*if player.custom2down == nil then
			player.custom2down = 0
		end
		
		if player.cmd.buttons & BT_CUSTOM2 > 0 then
			player.custom2down = $1 + 1
		else
			player.custom2down = 0
		end*/
		
		if player.rewoundtime == nil then
			player.rewoundtime = 0
		end
		
		if player.rewoundtimecheckpoint == nil then
			player.rewoundtimecheckpoint = 0
		end
		
		if player.rewindabletics == nil then
			player.rewindabletics = 0
		end
		
		if player.custom2down > 1
		and player.tossflagdown == 0
		and player.custom1down == 0
		//and player.rewindabletics > 0 --if the log of rewindable states and positions is not exhausted
		and player.mo.energy >= 20*FRACUNIT then
			P_ResetPlayer(player)
			player.pflags = $1|PF_FULLSTASIS
			if not (player.custom2down%TICRATE) then
				player.mo.energy = $ - 20*FRACUNIT
			end
			if (player.custom2down%TICRATE) then
				player.rewoundtime = $ + 2
				//player.rewindabletics = $ - 1				
			end
		end
	end
end)

--Version 2 - higher energy consumption, Sonja's state is rewound along with everything else
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		/*if player.custom2down == nil then
			player.custom2down = 0
		end
		
		if player.cmd.buttons & BT_CUSTOM2 > 0 then
			player.custom2down = $1 + 1
		else
			player.custom2down = 0
		end
		
		if player.tossflagdown == nil then
			player.tossflagdown = 0
		end
		
		if player.cmd.buttons & BT_TOSSFLAG > 0 then
			player.tossflagdown = $1 + 1
		else
			player.tossflagdown = 0
		end*/
		
		if player.custom2down > 1
		and player.tossflagdown > 1
		and player.custom1down == 0
		//and player.rewindabletics > 0 --if the log of rewindable states and positions is not exhausted
		and player.mo.energy >= 45*FRACUNIT/2 then
			if not (player.custom2down%TICRATE) then
				player.mo.energy = $ - 45*FRACUNIT/2
			end
			if (player.custom2down%TICRATE) then
				player.rewoundtime = $ + 2
				//player.rewindabletics = $ - 1				
			end
		end
	end
end)

--Version 3 - highest energy consumption, Sonja can move freely while everything else rewinds
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		/*if player.custom2down == nil then
			player.custom2down = 0
		end
		
		if player.cmd.buttons & BT_CUSTOM2 > 0 then
			player.custom2down = $1 + 1
		else
			player.custom2down = 0
		end*/
		
		if player.custom2down > 1
		and player.tossflagdown == 0
		and player.custom1down > 1
		and player.custom2down > player.custom1down
		//and player.rewindabletics > 0 --if the log of rewindable states and positions is not exhausted
		and player.mo.energy >= 25*FRACUNIT then
			if not (player.custom2down%TICRATE) then
				player.mo.energy = $ - 25*FRACUNIT
			end
			if (player.custom2down%TICRATE) then
				player.rewoundtime = $ + 2
				//player.rewindabletics = $ - 1
			end
		end
	end
end)

//Time manipulation affecting the player's level time
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if (gametype == GT_RACE or gametype == GT_COMPETITION) then
			if player.finaltime == 0 then
				player.realtime = leveltime - (player.slowedtime + player.rewoundtime + (4*TICRATE)) --allow Sonja's time slow and rewind to modify her level timer
			else
				player.realtime = player.finaltime --lock in player.realtime when level is finished
			end
			if (player.pflags & PF_FINISHED) then
				stoppedclock = true
				player.finaltime = player.realtime --stop timer from changing when level is completed
			end
		else
			if player.finaltime == 0 then
				player.realtime = leveltime - (player.slowedtime + player.rewoundtime) --allow Sonja's time slow and rewind to modify her level timer
			else
				player.realtime = player.finaltime --lock in player.realtime when level is finished
			end
			if (player.pflags & PF_FINISHED) then
				stoppedclock = true
				player.finaltime = player.realtime --stop timer from changing when level is completed
			end
		end
		
		if player.tossflagdown == 1 --If Toss Flag is pressed on its own
		and player.custom1down == 0 --Toss Flag is also used to modify Time Slow and Rewind, so this print command can't be used during time manipulation
		and player.custom2down == 0
		and player.gotflag == 0 then --Don't overwrite Toss Flag's default function
			print("Full leveltime (tics) = "..leveltime)
			print("Adjusted leveltime (tics) = "..player.realtime)
			print("Difference in leveltime (tics) = "..(leveltime - player.realtime))
			print("Full leveltime (seconds) = "..G_TicsToHours(leveltime)..":"..G_TicsToMinutes(leveltime, false)..":"..G_TicsToSeconds(leveltime)..":"..G_TicsToCentiseconds(leveltime))
			print("Adjusted leveltime (seconds) = "..G_TicsToHours(player.realtime)..":"..G_TicsToMinutes(player.realtime, false)..":"..G_TicsToSeconds(player.realtime)..":"..G_TicsToCentiseconds(player.realtime))
			print("Difference in leveltime (seconds) = "..G_TicsToHours((leveltime - player.realtime))..":"..G_TicsToMinutes((leveltime - player.realtime), false)..":"..G_TicsToSeconds((leveltime - player.realtime))..":"..G_TicsToCentiseconds((leveltime - player.realtime))) --Allow the player to see how much they've modified the leveltime
			chatprint("Full leveltime (tics) = "..leveltime, false)
			chatprint("Adjusted leveltime (tics) = "..player.realtime, false)
			chatprint("Difference in leveltime (tics) = "..(leveltime - player.realtime), false)
			chatprint("Full leveltime (seconds) = "..G_TicsToHours(leveltime)..":"..G_TicsToMinutes(leveltime, false)..":"..G_TicsToSeconds(leveltime)..":"..G_TicsToCentiseconds(leveltime), false)
			chatprint("Adjusted leveltime (seconds) = "..G_TicsToHours(player.realtime)..":"..G_TicsToMinutes(player.realtime, false)..":"..G_TicsToSeconds(player.realtime)..":"..G_TicsToCentiseconds(player.realtime), false)
			chatprint("Difference in leveltime (seconds) = "..G_TicsToHours((leveltime - player.realtime))..":"..G_TicsToMinutes((leveltime - player.realtime), false)..":"..G_TicsToSeconds((leveltime - player.realtime))..":"..G_TicsToCentiseconds((leveltime - player.realtime)), false) --Show everyone else how much the leveltime has been tweaked, too
		end
	end
end)

/*addHook("MapChange", function(mapnum)
	for player in players.iterate() do
		//if player.skin ~= "sonja_belnades" then
			//continue
		//else
			player.slowedtimecheckpoint = 0
			player.rewoundtimecheckpoint = 0
		//end
	end
end)*/

addHook("TouchSpecial", function(special, toucher)
	if special and special.valid then
		if toucher.player and toucher.player.valid then
			if toucher.player.mo.skin ~= "sonja_belnades" then
				return
			end
		end
		
		if (special.spawnpoint.extrainfo) + ((special.spawnpoint.angle)/360) == toucher.player.starpostnum then --if the player is touching a new Star Post
			print("Checkpoint ID: "..((special.spawnpoint.extrainfo) + ((special.spawnpoint.angle)/360)),"Checkpoint # "..toucher.player.starpostnum.." reached!")
			toucher.player.slowedtimecheckpoint = toucher.player.slowedtime --store current slowed time
			toucher.player.rewoundtimecheckpoint = toucher.player.rewoundtime --store current rewound time
		end
	end
end, MT_STARPOST)

addHook("PlayerSpawn", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		else
			if player.starpostnum == 0 then
				player.slowedtime = 0
				player.rewoundtime = 0 --Reset manipulated time on a map change
				player.slowedtimecheckpoint = 0
				player.rewoundtimecheckpoint = 0
				player.finaltime = 0
			else
				player.slowedtime = player.slowedtimecheckpoint
				player.rewoundtime = player.rewoundtimecheckpoint --Reset manipulated time to its value upon touching the most recent checkpoint upon respawn
			end
		end
	end
end)

//Chrono Evasion (Code to become briefly immune to damage)
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		/*if player.custom3down == nil then
			player.custom3down = 0
		end
		
		if player.cmd.buttons & BT_CUSTOM3 > 0 then
			player.custom3down = $1 + 1
		else
			player.custom3down = 0
		end*/
		
		if player.evasion == nil then
			player.evasion = 0
		end
		
		if player.flashprep == nil then
			player.flashprep = 0
		end
		
		if player.counterready == nil then
			player.counterready = 0
		end
		
		if player.attacker == nil then
			player.attacker = 0
		end
		
		if player.counter == nil then
			player.counter = 0
		end
		
		if player.custom3down == 1
		and player.mo.energy >= 10*FRACUNIT then
			player.evasion = $ + 3*TICRATE/4 --enter Chrono Evasion state for 3/4 of a second
			player.mo.energy = $ - 10*FRACUNIT
		end
		
		if player.evasion > 0 then
			/*if player.skincolor == SKINCOLOR_PERIDOT then --if the player has set their skincolor to naturally be peridot
				(player.mo.state.frame & FF_FULLBRIGHT) --make the player's sprite brightly glow
			else
				(player.mo.state.frame & FF_SEMIBRIGHT) --make the player's sprite faintly glow
			end*/
			player.mo.color = SKINCOLOR_PERIDOT --give the player a faint green hue to notify them that Chrono Evasion is active
			if (leveltime%TICRATE) then
				player.evasion = $ - 1 --drain Chrono Evasion timer
				if player.evasion == 0 then
					player.mo.color = player.skincolor --switch back to standard color when Chrono Evasion ends
				end
			end	
		end
	end
end)

addHook("ShouldDamage", function(target, inflictor, source, damage, damagetype)
	if target.player and target.player.valid then
		if target.skin ~= "sonja_belnades" then
			return
		end
		
		if damagetype ~= DMG_DROWNED
		and damagetype ~= DMG_SPACEDROWN --can't "dodge" lack of oxygen
		and target.player.evasion > 0 then --if Chrono Evasion is active
			target.player.flashprep = TICRATE/2 --allow flash step
			target.player.attacker = source --make a note of which mobj (if any) attempted to attack Sonja so that she can counterattack them
			return false --ignore damage if Chrono Evasion state is active
		end
	end
end, MT_PLAYER)

addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
		
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
		
		if player.flashprep > 0 then
			if (leveltime%TICRATE) then
				player.flashprep = $ - 1
			end
		end
		
		if player.counterready > 0 then
			if (leveltime%TICRATE) then
				player.counterready = $ - 1
			end
		end
		
		if player.counter > 0 then
			if (leveltime%TICRATE) then
				player.counter = $ - 1
			end
		end
		
		if player.flashprep == 0
		and player.counterready == 0
		and player.counter == 0 then
			player.attacker = 0 --a mobj should only be designated as Sonja's attacker if that mobj recently attacked her
		end
		
		if player.flashprep >= 1
		and (player.cmd.forwardmove ~= 0 or player.cmd.sidemove ~= 0) then --if player is moving
			P_Move(player.mo, 250) --warp the player 250 fracunits ahead in the direction they're already moving
			P_SpawnGhostMobj(player.mo)
			print("Flash step!")
			player.counterready = TICRATE
		end
		
		if player.flashprep >= 1
		and player.cmd.buttons & BT_JUMP > 0 then
			P_MoveOrigin(player.mo, (player.mo.x + 0), (player.mo.y + 0), (player.mo.z + 250*FRACUNIT)) --warp the player 250 fracunits up in the air without adjustments to their x and y position
			P_SpawnGhostMobj(player.mo)
			print("Flash jump!")
			player.counterready = TICRATE
		end
		
		if player.counterready >= 1 --if Sonja recently flash-stepped away from danger
		and player.attacker ~= 0 --and that danger is a valid object (an enemy or boss)
		//and P_PlayerCanDamage(player, player.attacker) == true
		and player.mo.energy >= 5*FRACUNIT --and Sonja has energy
		and player.cmd.buttons & BT_SPIN == 1 then --when the player presses Spin
			player.mo.energy = $ - 5*FRACUNIT --energy cost
			P_SpawnGhostMobj(player.mo)
			player.counter = 2*TICRATE --set counterattack in motion
			P_HomingAttack(player.mo, player.attacker) --begin homing in on attacker
			player.pflags = $1|PF_SPINNING --put Sonja in spinning state
			player.pflags = $&~PF_NOJUMPDAMAGE --allow Sonja to damage enemy even if she used Vixen Vortex
			print("Counterattack!")
		end
		
		if player.counter > 0
		and player.cmd.buttons & BT_SPIN > 1
		and player.attacker ~= 0 then
			P_SpawnGhostMobj(player.mo)
			P_HomingAttack(player.mo, player.attacker) --continue homing attack until contact is made or counter timer ends
			if P_HomingAttack(player.mo, player.attacker) == true then --if the homing attack connects
				P_DamageMobj(player.attacker, player.mo, player.mo, 2) --deal 2 damage
			end
		end
	end
end)

//Danger Radar (Code to show the location of hazardous objects and level geometry within ~1200 fracunits)
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid then
	
		if player.mo.skin ~= "sonja_belnades" then
			return
		end
	end
end)

//Delay Enemies (Code to give Sonja a bit of extra time to react to enemy attacks even when not actively slowing time)

//Advance Notice (Code to add extra telegraphing to enemy attacks)