--Ascended Orbifiring actiton. This is a slight modification of A_Fireshot.
function A_AscendedOrbinaut(actor,var1,var2) 
	local z
	if not actor.target
		return
	end
	
	A_FaceTarget(actor)
	
	if (actor.eflags & MFE_VERTICALFLIP)
		z = actor.z + actor.height - FixedMul(48*FRACUNIT + var2*FRACUNIT, actor.scale)
	else
		z = actor.z + FixedMul(48*FRACUNIT + var2*FRACUNIT, actor.scale)
	end
	
	local shot = P_SpawnXYZMissile(actor, actor.target, var1, actor.x, actor.y, z)
	shot.movefactor = 50*FRACUNIT
	shot.color = SKINCOLOR_PEACH
	
	if (not(actor.flags & MF_BOSS))
		if (ultimatemode)
			actor.reactiontime = actor.info.reactiontime*TICRATE
		else
			actor.reactiontime = actor.info.reactiontime*TICRATE*2
		end
	end
end


--Map Related Lua
local function BrightArrows(mo)
	if not mapheaderinfo[gamemap].brightsigns == true
		return
	end
	mo.frame = $1|FF_FULLBRIGHT
	--return true
end


local function HyuuPurification(l,mo,s)
	if mo.player.kartstuff[k_hyudorotimer] > 0
		return
	end
	P_DamageMobj(mo,nil,nil,10000)
end

--Lua port of K_DoThunderShield as it is not exposed to lua.
local function Thundershield(player)
	local mo
	local sx
	local sy
	local an
	local THUNDERRADIUS = 320
	

	S_StartSound(player.mo, sfx_zio3)
	P_NukeEnemies(player.mo, player.mo, RING_DIST/4)

	-- spawn vertical bolt
	mo = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_THOK)
	mo.target = player.mo
	mo.state = S_LZIO11
	mo.color = SKINCOLOR_TEAL
	mo.scale = player.mo.scale*3 + (player.mo.scale/2)

	mo = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_THOK)
	mo.target = player.mo
	mo.state = S_LZIO21
	mo.color = SKINCOLOR_CYAN
	mo.scale = player.mo.scale*3 + (player.mo.scale/2)

	-- spawn horizontal bolts
	for i=0,7
		mo = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_THOK)
		mo.angle = P_RandomRange(0, 359)*ANG1
		mo.fuse = P_RandomRange(20, 50)
		mo.target = player.mo
		mo.state = S_KLIT1
	end

	-- spawn the radius thing:
	an = ANGLE_22h
	for i=0,15
		sx = player.mo.x + FixedMul((player.mo.scale*THUNDERRADIUS), cos((an*i)))
		sy = player.mo.y + FixedMul((player.mo.scale*THUNDERRADIUS), sin((an*i)))
		mo = P_SpawnMobj(sx, sy, player.mo.z, MT_THOK)
		mo.angle = an*i
		mo.extravalue1 = THUNDERRADIUS	-- Used to know whether we should teleport by radius or something.
		mo.scale = player.mo.scale*3
		mo.target = player.mo
		mo.state = S_KSPARK1
	end
end


--Autodetonate shield upon attacking the core.
local function HitAssist(l,mo,s)
	if mo.player.kartstuff[k_curshield] == 1
		Thundershield(mo.player)
		mo.player.kartstuff[k_itemamount] = $1 - 1
		mo.player.powers[pw_flashing] = 50
		K_PlayAttackTaunt(mo.player.mo)
	end
end

local function EdenThankYou (l,mo,s)
	local thanksstring = "\x82Thank you for playing Abstraction Pack! \x86It has been a long road of development. It would not have been possible without STJr, Kart Krew and the support of the fantastic community!"
	if mo.player
		S_StartSound(nil,sfx_ncitem,mo.player)
		if multiplayer
			chatprintf(mo.player,thanksstring)
		else
			CONS_Printf(mo.player,thanksstring)
		end
	end
end

addHook("LinedefExecute",EdenThankYou,"EDTHANKS")
addHook("LinedefExecute",HitAssist,"HITASST")
addHook("LinedefExecute",HyuuPurification,"HYUUPURE")
freeslot(
"MT_COLORED_CC_SIGN"
)
addHook("MobjThinker",BrightArrows,MT_COLORED_CC_SIGN)

--Pickups
freeslot(
"MT_EDEN_CORE",
"MT_ASCENDEDSOUL",
"MT_THUNDERSHIELDSPAWNER",
"S_ASCENDEDSOULFLIGHT",
"S_ASCENDEDSOULFLIGHT1",
"S_ASCENDEDSOULFLIGHT2",
"S_ASCENDEDSOULESCAPE",
"S_ASCENDEDSOULESCAPE1",
"S_ASCENDEDSOULESCAPE2",
"S_ASCENDEDSOULESCAPE3",
"S_ASCENDEDSOULESCAPE4",
"S_ASCENDEDSOULESCAPE5"
)

states[S_ASCENDEDSOULFLIGHT] = {SPR_BBAL, A, 16, A_ZThrust, 2,(1<<16)+1 , S_ASCENDEDSOULFLIGHT1}
states[S_ASCENDEDSOULFLIGHT1] = {SPR_BBAL, A, 16, A_ZThrust, -2,(1<<16)+1 , S_ASCENDEDSOULFLIGHT2}
states[S_ASCENDEDSOULFLIGHT2] = {SPR_BBAL, A, 0, A_Repeat, 11, S_ASCENDEDSOULFLIGHT, S_ASCENDEDSOULESCAPE}

states[S_ASCENDEDSOULESCAPE] = {SPR_PITY, C, 25, A_ZThrust, 15, (1<<16)+1, S_ASCENDEDSOULESCAPE1}
states[S_ASCENDEDSOULESCAPE1] = {SPR_PITY, C, 2, A_ZThrust, 0, (1<<16)+1, S_ASCENDEDSOULESCAPE2}
states[S_ASCENDEDSOULESCAPE2] = {SPR_PITY, C, 1, A_FindTarget, MT_EDEN_CORE, 0, S_ASCENDEDSOULESCAPE3}
states[S_ASCENDEDSOULESCAPE3] = {SPR_PITY, C, 1, A_SkullAttack, 1, 0, S_ASCENDEDSOULESCAPE4}
states[S_ASCENDEDSOULESCAPE4] = {SPR_PITY, A, 0, A_SetObjectFlags, MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, 2, S_ASCENDEDSOULESCAPE5}
states[S_ASCENDEDSOULESCAPE5] = {SPR_PITY, A|FF_ANIMATE, 140, None, 5, 3, S_NULL}

--Gives 1 hyuus on touch (you can get more)
--Ideally autodetonate all shields the player has.
--itemtype 
--itemamount
addHook("TouchSpecial", function(special,toucher)
	if toucher.player.kartstuff[k_itemtype] == KITEM_HYUDORO --give more ghosts
		toucher.player.kartstuff[k_itemamount] = $1 + 1
	elseif toucher.player.kartstuff[k_itemtype] == KITEM_THUNDERSHIELD --Detonate and replace with ghosts
		Thundershield(toucher.player)
		toucher.player.kartstuff[k_itemtype] = KITEM_HYUDORO
		toucher.player.kartstuff[k_itemamount] = 1
	else
		toucher.player.kartstuff[k_itemtype] = KITEM_HYUDORO
		toucher.player.kartstuff[k_itemamount] = 1
	end
	return false
end
,MT_ASCENDEDSOUL)


mobjinfo[MT_ASCENDEDSOUL] = {
        doomednum = -1,
        spawnstate = S_ASCENDEDSOULFLIGHT,
        spawnhealth = 1000,
        seestate = S_NULL,
        seesound = sfx_None,
        reactiontime = 8,
        attacksound = sfx_None,
        painstate = S_NULL,
        painchance = MT_THOK,
        painsound = sfx_None,
        meleestate = S_NULL,
        missilestate = S_NULL,
        deathstate = S_NULL,
        xdeathstate = S_NULL,
        deathsound = sfx_s3k9c,
        speed = 1,
        radius = 48*FRACUNIT,
        height = 128*FRACUNIT,
        dispoffset = 0,
        mass = 0,
        damage = 0,
        activesound = sfx_None,
        flags = MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP|MF_RUNSPAWNFUNC,
        raisestate = S_NULL
}


addHook("TouchSpecial", function(special,toucher)
		toucher.player.kartstuff[k_stealingtimer] = 0
	if toucher.player.kartstuff[k_itemtype] == KITEM_HYUDORO --auto ghost?
		S_StartSound(toucher.player.mo, sfx_s3k92)
		toucher.player.kartstuff[k_hyudorotimer] = 7*TICRATE
		toucher.player.kartstuff[k_itemtype] = KITEM_THUNDERSHIELD
		toucher.player.kartstuff[k_itemamount] = 2
			return false
	elseif (toucher.player.kartstuff[k_itemtype] == KITEM_THUNDERSHIELD and (toucher.player.kartstuff[k_itemamount] < 2)) 
	or (toucher.player.kartstuff[k_itemtype] == KITEM_NONE)--Replenish shields
		toucher.player.kartstuff[k_itemtype] = KITEM_THUNDERSHIELD
		toucher.player.kartstuff[k_itemamount] = 2
		return false
	else
		return true
	end
end
,MT_THUNDERSHIELDSPAWNER)



--Hud magic
local EdenMonitor

addHook("MobjSpawn", function(mo)
	EdenMonitor = mo
end
,MT_EDEN_CORE)

local edendeathtime = (270*TICRATE)
local vulnticker = 0*TICRATE
local vulnratio =0
local vulncharge = 0 
local chargeratio = 0

local pipstationaryincrement = 75
local pipshimmerincrement = 1
local pipwaveincrement = 2
local piptotincrement = pipstationaryincrement + 3 * (pipwaveincrement + pipshimmerincrement)

addHook("MobjThinker",function(mo)
	if mo == EdenMonitor
		if mo.state == S_EDEN_CORE or mo.state == S_EDEN_CORE2 or mo.state == S_EDEN_CORE3 or mo.state == S_EDEN_RAIN3
			vulncharge = 0
			vulnticker = $1 + 1
			vulnratio = (vulnticker*FRACUNIT)/(30*TICRATE)
		else 
			if vulnticker != 0
				vulnticker = 0
			end
		end
		if mo.state == S_EDEN_RAIN or mo.state == S_EDEN_RAIN1 or mo.state == S_EDEN_RAIN2 
			vulncharge = $1 + 1
			vulnratio = (vulncharge*FRACUNIT)/(344)
		else
			if vulncharge != 0
				vulncharge = 0
			end
		end
		if leveltime >= (edendeathtime+(6*TICRATE + (3*TICRATE/4))) and mo.fuse == 0
			mo.fuse = 280
		end
	end
end
,MT_EDEN_CORE)

local function getPipFrame(edenpip, i)
	if (edenpip[i] > pipstationaryincrement) and (edenpip[i] <= (pipstationaryincrement + pipwaveincrement))
		return 2
	elseif (edenpip[i] > (pipstationaryincrement + pipwaveincrement + pipshimmerincrement)) and (edenpip[i] <= (pipstationaryincrement + 2*pipwaveincrement + pipshimmerincrement)) 
		return 3
	elseif (edenpip[i] > (pipstationaryincrement + 2*pipwaveincrement + pipshimmerincrement)) and (edenpip[i] <= (pipstationaryincrement + 3*pipwaveincrement + 2*pipshimmerincrement)) 
		return 4
	else 
		return 1
	end
end

local prevleveltime
local edenpip = {0,0,0,0}

local function EdenBossBar(v, player, cam)
	if not(mapheaderinfo[gamemap].lvlttl == "Flesh Eden") 
		hud.enable("gametypeinfo")
		hud.enable("freeplay")
		return
	end
	
	local edenbar = v.cachePatch("EDENBAR")
	local pip = {}
	for i = 1, 4
		pip[i] = v.cachePatch("SPBPIP"..i)
	end
	local edeninit = {5,25,45,65}
	if not (leveltime == prevleveltime)
		for i = 1, #edenpip
			if edenpip[i] > piptotincrement
				edenpip[i] = edeninit[i]
			end
			edenpip[i] = $1+1
		end
		prevleveltime = leveltime
	end
	local slice = v.cachePatch("EDENSLI")	
	local basex = 13
	local basey = 159
	if splitscreen 
		basex = 81
		basey = 80
	end
	
	hud.disable("gametypeinfo")
	hud.disable("freeplay")
	
	if leveltime < (6*TICRATE + (3*TICRATE/4)) or player.awayviewtics > 0 or player.spectator == true
		return
	end
	v.draw(basex, basey, edenbar,V_HUDTRANS)
	
	if EdenMonitor.valid
		for i = 1, EdenMonitor.health
			v.draw(basex + 76 - (23*(i-1)), basey + 14,pip[getPipFrame(edenpip,i)],V_HUDTRANS)
		end
		local timedifference = (leveltime*FRACUNIT)/edendeathtime
		if EdenMonitor.health >= 1
			if leveltime < edendeathtime
				for i = 0, FixedInt(timedifference *36)
					v.draw(basex + 103 + i, basey + 31,slice,V_HUDTRANS,v.getColormap(0,SKINCOLOR_BLACK))
				end
			end
			--Bar increase while attacking.
			if EdenMonitor.state == S_EDEN_RAIN or EdenMonitor.state == S_EDEN_RAIN1 or EdenMonitor.state == S_EDEN_RAIN2
				for i = 0, FixedInt(vulnratio *36)
					v.draw(basex + 103 + i, basey + 2,slice,V_HUDTRANS,v.getColormap(0,SKINCOLOR_MAUVE))
				end
			end
			--Bar decrease while waiting.
			if EdenMonitor.state == S_EDEN_CORE or EdenMonitor.state == S_EDEN_CORE2 or EdenMonitor.state == S_EDEN_CORE3 or EdenMonitor.state == S_EDEN_RAIN3
				for i = 0,(36 - FixedInt(vulnratio *36))
					if vulnticker >= 25*TICRATE 
						v.draw(basex + 103 + i, basey + 2,slice,V_HUDTRANS,v.getColormap(0,SKINCOLOR_JET))
					else
						v.draw(basex + 103 + i, basey + 2,slice,V_HUDTRANS,v.getColormap(0,SKINCOLOR_NOVA))
					end
				end
			end
		end
		if leveltime >= edendeathtime
			for i = 0, 36
				v.draw(basex + 103 + i, basey + 31,slice,V_HUDTRANS,v.getColormap(0,SKINCOLOR_BLACK))
			end
		end
	end
end

hud.add(EdenBossBar, "game")

local neweden = false --Boss dependent toggle for meat mode.
local meatmaps = 1 --Maps to meat mode afterthe boss.
local function edenvars(net)
	neweden = net($)
	meatmaps = net($)
end

addHook("MobjFuse",function(mo)
	if (mo.health > 0)
		if not CV_FindVar("paradiseillusion") or paradiseillusion.value == 0 or paradiseillusion.value == 2 or not modeattacking
			neweden = true
			meatmaps = 1
		end
		G_ExitLevel()
	end
	return true
end
,MT_EDEN_CORE)

--Replaces strings with numerical indices for texture swapping
local function TextureNameNumSwap(table)
	for i =1  , #table
		table[i] = R_TextureNumForName(table[i])
	end
end

local function blacklistcheck(val, bl)
	for i = 1, #bl
		if val == bl[i]
			return true
		end
	end
	return false
end

local cominit = false --So the message will not pop up when load Abstraction Pack.
local oldvalue = 0 --Prevents redundant prints
--Flesh paradise flat and texture swapping. This will occur on the next non flesh
--map on failure of Flesh Eden or if infiniteparadise is enabled.
local infiniteparadise = CV_RegisterVar({
	name = "infiniteparadise",
	defaultvalue = "Off",
	flags = CV_NETVAR|CV_CALL|CV_NOSHOWHELP,
	possiblevalue = CV_OnOff,
	func = function(v)
		if  cominit == false
			cominit = true
			return 
		end
		if gamestate == GS_LEVEL and (v.value != oldvalue)
			if v.value == 1 
				print("\130INFINITE FLESH PARADISE ENABLED:\128 You are filled with visions of an undying world.")
				S_StartSound(nil,sfx_menu1, p)
				G_SetCustomExitVars(gamemap,1)
				G_ExitLevel()
			elseif v.value == 0
				print("\130INFINITE FLESH PARADISE DISABLED:\128 The madness subsides.")
				S_StartSound(nil,sfx_menu1, p)
				G_SetCustomExitVars(gamemap,1)
				G_ExitLevel()
			else
			end
			oldvalue = v.value
		end
		end
})


--Tables that all unique textures are added to
local texturetable = {}
local flattable = {}

--Flat Pools
local fmeattable = {"NTFLAT10","NTFLAT9","OFLSHB02","OFLSHF01",
"VISNAKF3","VSNAKF41","VSNAKF42","OHELLA02","DCZFLR09","OHELLA03"}
--,"OLAVAD01"

local ffoliagetable = {"FLPDFLWR","FLPDGRS","FLPDLCBF","FLPDWDF"}

local fcastletable = {"ADEL_J15", "DEM2_6", "OBRCKM04", "OFADEA04", "OFNCYA23", "OFNCYA32",
					"OHEXAK03", "OMBLRA44", "OMRBLD01", "OMRBLG33", "OMRBLH01", "OMRBLL01",
					"OMRBLL44", "PG_ROAD3"}

--Texture pools
local tmeattable = {"OFLSHB01","OFLSHC01","OFLSHE01","OFLSHF01","OFLSHG01",
					"SCYPFWL2","SKIN2","SKINFLE1","SKINFLE2","VISNAKE3",
					"VSNAKE41","VSNAKE42","DCWHITEW"}
--"SKINRAIL"

local tfoliagetable = {"FLPDGRSW", "FLPDLCBW", "FLPDWD"}

local tcastletable = {"GSTONE1",
"OMRBLB19","OMRBLB30","OMRBLD39","OMRBLD50","OMRBLD59",
"OMRBLE43","OMRBLF18","OMRBLG22","OMRBLP90","OPALCA01",
"OPALCA02","OPALCA40","OPALCC23","OPALCE23","SP_HOT1"}

--Meta pools
local fswappool = {fmeattable, ffoliagetable, fmeattable}

--Flat force pools
local grasstable = {
	"JNGGRS01",
	"JNGGRS02",
	"JNGGRS03",
	"JNGGRS04",
	"JNGGRS05",
	"JNGGRS06",
	"JNGGRS07",
	"JNGGRS08",
	"GFZFLR02",
	"CEZFLR03",
	"CEZFLR05",
	"CEZFLR13",
	"GRASS1",
	"GRASS3",
	"GRASS7",
	"GRASSY",
	"GRASZY",
	"PG_GRAS1",
	"PG_GRAS2"
	
}

local flowertable = {
	"JNGGRS09",
	"JNGGRS10",
	"JNGGRS11",
	"JNGGRS12",
	"CEZFLR02",
	"CEZFLR12",
	"GRASS2",
	"GRASS2L",
	"GRASZP",
	"GRAS3F",
	"GRASS2F",
	"GFZFLR10",
	"PG_GRAS3"
}
local cherryblossom = {
	"JNGFLR09",
	"JNGFLR10",
	"JNGFLR11",
	"JNGFLR12",
	"DEM1_2",
	"DKLEAFLR",
	"EC_VINES",
	"SFZLEAF",
	"SCZLEAF1",
	"SCZLEAF2"
}
local woodtable = {
	"JNGWDF5",
	"JNGWDF6",
	"JNGWDF7",
	"JNGWDF8",
	"JNGWDF9",
	"JNGWDFA",
	"JNGWDFB",
	"JNGWDFC",
	"GFZFLR11",
	"WOODFLR",
	"WOODFLR2",
	"WOODFLR3",
	"WOODFLR4"
}
--Texture force pools
local grasswalltable = {
	"JNGGRW1",
	"JNGGRW2",
	"JNGGRW3",
	"JNGGRW4",
	"CEZGRSW",
	"GFZGRSW"
}

local grassedgetable = {
	"JNGGRW5",
	"JNGGRW6",
	"JNGGRW7",
	"JNGGRW8",
	"JNGGRW5S",
	"JNGGRW6S",
	"JNGGRW7S",
	"JNGGRW8S",
	"GRSEDG1",
	"GRSEDG2",
	"GRSEDG3",
	"GRSEDG4",
	"GRSWALL",
	"GRSWALL2",
	"GRSWALL3",
	"GRSWALL4",
	"GFZGRASS",
	"WEEDWALL",
	"PG_GRASW"
}

local cherrywalltable = {
	"JNGWL09",
	"JNGWL10",
	"JNGWL11",
	"JNGWL12",
	"ECVINESW",
	"SCZLEFW1",
	"SCZLEFW2",
}

local woodwalltable = {
	"JNGWD1",
	"JNGWD2",
	"JNGWD3",
	"JNGWD4",
	"JNGWD5",
	"JNGWD6",
	"JNGWD7",
	"JNGWD8",
	"WOODWALL",
	"WOODWLL2",
	"WOODWLL3",
	"WOODWLL4",
	"OWOODW"
}

--Linedef special blacklist tables; Credit to Yoshimo
--define the texture blacklists per linedef type
local lineTypeFrontTopBlacklistArray = {
    4,   --Speed Pad
    9,   --Chain Parameters
    14,  --Bustable Block Parameters
    15,  --Fan Particle Spawner Parameters
    102, --Translucent Solid FOF
    121, --Translucent Water FOF
    123, --Sideless Translucent Water FOF
    124, --Translucent Goo Water FOF
    125, --Sideless Translucent Goo Water FOF
    141, --Intangible-Bottomed Translucent FOF
    142, --Intangible-Bottomed Sideless Translucent FOF
    144, --Intangible-Topped Translucent FOF
    145, --Intangible-Topped Sideless Translucent FOF
    174, --Crumbling Respawning Intangible-Bottomed Translucent FOF
    175, --Crumbling Respawnless Intangible-Bottomed Translucent FOF
    192, --Translucent Solid Rising Platform FOF
    195, --Intangible-Bottomed Translucent Rising Platform FOF
    221, --Intangible Translucent FOF
    253, --Translucent Shatter Block FOF
    256, --Translucent Spin-Bustable Block FOF
    331, --Continuous Player Skin Executor Trigger
    332, --Each-Time Player Skin Executor Trigger
    333, --Once Player Skin Executor Trigger
    334, --Continuous Object Dye Executor Trigger
    335, --Each-Time Object Dye Executor Trigger
    336, --Once Object Dye Executor Trigger
    413, --Change Music
    414, --Play Sound Effect
    425, --Change Object State
    434, --Award Power-Up
    442, --Change Object Type State
    443, --Call Lua Function
    447, --Change Tagged Sector's Colormap
    455, --Fade Tagged Sector's Colormap
    461, --Spawn Object
    463, --Dye Object
    606  --Colormap
}
local lineTypeFrontBottomBlacklistArray = {9, 14, 15, 331, 332, 333, 413, 425, 434, 442, 443, 447,
    455, 461, 606}
	
local lineTypeFrontMidBlacklistArray = {9, 14, 15, 331, 332, 333, 413, 425, 434, 442, 443, 447, 455,
    461, 606}
local lineTypeBackMidBlacklistArray = {9, 442}

local lineTypeBackTopBlacklistArray = {
    9,
    259, --Custom FOF
    442
}
local lineTypeBackBottomBlacklistArray = {9, 442}


local lineTypeFrontBlacklistArray = {lineTypeFrontTopBlacklistArray,lineTypeFrontBottomBlacklistArray}
local lineTypeMidBlacklistArray = {lineTypeFrontMidBlacklistArray, lineTypeBackMidBlacklistArray}
local lineTypeBackBlacklistArray = {lineTypeBackTopBlacklistArray,lineTypeBackBottomBlacklistArray}

--Duplicates of the above which change value on level load..
local grasswalltable2 = {}
local grassedgetable2 = {}
local cherrywalltable2 = {}
local woodwalltable2 = {}

local function tablelookup(search,table)
	for k,v in ipairs(table)
		if search == v
			return true
		end
	end
	return false
end
local Flatpool = {swap = "GFZFLR01"}

function Flatpool:new (o,swap)
	local o = o or {}
	local self = setmetatable(o, Flatpool)
	self.swap = swap or "GFZFLR01"
	return self
end

local function flatswapinit(table,flat,pool)
	if flat == "F_SKY1" 
	or flat == R_TextureNumForName("ROZLITW")
	or flat == 0
	or R_TextureNumForName(flat) == 0 
		return flat
	end
	
		
	local swapset = P_RandomRange(1,#pool)
	local randomtx = P_RandomRange(1,#pool[swapset])
	if not table[flat]
		--Special cases
		if tablelookup(flat,grasstable)
			table[flat] = Flatpool:new(nil,"FLPDGRS")
		elseif tablelookup(flat,flowertable)
			table[flat] = Flatpool:new(nil,"FLPDFLWR")
		elseif tablelookup(flat,cherryblossom)
			table[flat] = Flatpool:new(nil,"FLPDLCBF")
		elseif tablelookup(flat,woodtable)
			table[flat] = Flatpool:new(nil,"FLPDWDF")
		elseif tablelookup(flat,grasswalltable2)
			table[flat] = Flatpool:new(nil, R_TextureNumForName("FLPDGRSW"))
		elseif tablelookup(flat,grassedgetable2)
			table[flat] = Flatpool:new(nil, R_TextureNumForName("FLPDGRW"))
		elseif tablelookup(flat,cherrywalltable2)
			table[flat] = Flatpool:new(nil, R_TextureNumForName("FLPDLCBW"))
		elseif tablelookup(flat,woodwalltable2)
			table[flat] = Flatpool:new(nil, R_TextureNumForName("FLPDWD"))
		else
			table[flat] = Flatpool:new(nil,pool[swapset][randomtx])
		end
	end
	return table[flat].swap
end

local function tabledupe(new, base)
	for k,v in ipairs(base)
		new[k] = v
	end
end

--Poolcheck tests if a water sector is completely submerged in water.
--This avoids odd replacements if the texture was never to be seen.
local function poolcheck(l)
	local visiblesec = 0
	for s in sectors.iterate
		if not (s.tag == l.tag) 
			continue 
		end
		for i = 0, #s.lines -1
			local line = s.lines[i]
			if line.backside == nil continue end --This is the only sector.
			if #line.frontsector == #s and (#line.backsector != l.tag)
				if line.backsector.floorheight >= l.frontsector.ceilingheight 
					continue 
				else
					for rover in line.backsector.ffloors()
						if (rover.valid == false) visiblesec = $1 + 1 end
						--Check for visibility
						if not ((rover.flags&FF_RENDERALL) or (rover.flags&FF_RENDERSIDES) 
						or (rover.flags&FF_RENDERPLANES)) continue end
						if rover.sector.ceilingheight < l.frontsector.ceilingheight
							visiblesec = $1+1
						end
					end
				end
			else
			if (#line.backsector != l.tag)
				if line.frontsector.floorheight >= l.frontsector.ceilingheight 
					continue 
				else
					for rover in line.frontsector.ffloors()
						if (rover.valid == false) visiblesec = $1 + 1 end
						--Check for visibility
						if not ((rover.flags&FF_RENDERALL) or (rover.flags&FF_RENDERSIDES) 
						or (rover.flags&FF_RENDERPLANES)) continue end
						if rover.sector.ceilingheight < l.frontsector.ceilingheight
							visiblesec = $1+1
						end
					end
				end
			end
		end
		end
	end
	return (visiblesec > 0)
end

--waterfallcheck determines if a water block's ceiling is important for
--replacement.

local function waterfallcheck(l) 
	for s in sectors.iterate
			if not (s.tag == l.tag) 
				continue 
			end
			if s.ceilingheight < l.frontsector.ceilingheight
				return true
			end
	end
	return false
end

--Makes the next level that is not Flesh Eden/Paradise 
--get a texture/music swap to look like Paradise on failure of Eden.
--This is disabled in Record attack or if Paradiseillusion is enabled.
addHook("ThinkFrame", function()
	if neweden == true or (infiniteparadise.value == 1) and not
	((mapheaderinfo[gamemap].lvlttl == "Flesh Eden") 
	or (mapheaderinfo[gamemap].lvlttl == "Flesh Paradise"))
	
	if leveltime == 1
		if (infiniteparadise.value == 0) or neweden == true
			meatmaps = $1 - 1
			if meatmaps <= -1
				neweden = false
				return 
			end
		else
			if meatmaps != -1
				meatmaps = -1
			end
		end
		local tmeattable2 = {}
		tabledupe(tmeattable2,tmeattable)
		local tfoliagetable2 = {}
		tabledupe(tfoliagetable2,tfoliagetable)
		local tcastletable2 = {}
		tabledupe(tcastletable2,tcastletable)
		
		TextureNameNumSwap(tmeattable2)
		TextureNameNumSwap(tfoliagetable2)
		TextureNameNumSwap(tcastletable2)
		local tswappool = {tmeattable2, tfoliagetable2, tmeattable2}
		
		tabledupe(grasswalltable2,grasswalltable)
		TextureNameNumSwap(grasswalltable2)

		tabledupe(grassedgetable2,grassedgetable)
		TextureNameNumSwap(grassedgetable2)

		tabledupe(cherrywalltable2,cherrywalltable)
		TextureNameNumSwap(cherrywalltable2)

		tabledupe(woodwalltable2,woodwalltable)
		TextureNameNumSwap(woodwalltable2)
		
		local sectorblacklist = {}

		for p in players.iterate
			if infiniteparadise.value == 0
				S_StartSound(nil,sfx_menu1,p)
				chatprintf(p,"\xA5*You are blessed with a vision of the new world.*\xA6 Complete or exit the stage to free yourself of it.")
			end
		end
		P_SetupLevelSky(867, true)
		P_SetSkyboxMobj(nil,nil,nil)
		local unique_flats = {}
		local unique_textures = {}
		local waterfalltable = {}
		for l in lines.iterate  --Water is replaced in paradise.
			if not(l.special == 120 or l.special == 121 or l.special == 122 or l.special == 123 
			or l.special == 124 or l.special == 125) continue end
				if not (l.special == 122 or l.special == 123 or l.special == 125)
					if not unique_textures[l.frontside.midtexture] and poolcheck(l)
							unique_textures[l.frontside.midtexture] = Flatpool:new(nil,R_TextureNumForName("OFALLM01"))
							table.insert(waterfalltable,l.frontside.midtexture)
					end
				end
				if not waterfallcheck(l)
					if not unique_flats[l.frontside.sector.ceilingpic]
						unique_flats[l.frontside.sector.ceilingpic] = Flatpool:new(nil,"OLAVAD01")
					end
				end
				l.frontside.sector.lightlevel = 303 
		end
		for l in lines.iterate
			if l.special == 253 or l.special == 252 continue end--Ignore Shatter Blocks
			if (l.special == 102 or l.special == 141 or l.special == 142 or l.special == 144
			or l.special == 145 or l.special == 174 or l.special == 192 or l.special == 195 or
			l.special == 221 or l.special == 256) and (l.frontside.toptexture == 256)  --Ignore opaque translucent FoFs
				table.insert(sectorblacklist,l.frontsector)
				continue
			end
			if l.special == 423 --preserve the sky.
				l.frontside.textureoffset = 867 * FRACUNIT
				continue
			end
			for i = 0,1
				if (sides[l.sidenum[i]] == nil) or (sides[l.sidenum[i]].valid == false)
					continue
				end
				if not (sides[l.sidenum[i]].toptexture == 0) and not tablelookup(l.special,lineTypeFrontBlacklistArray[i+1])
					sides[l.sidenum[i]].toptexture = flatswapinit(unique_textures,sides[l.sidenum[i]].toptexture,tswappool)
				end
				if not (sides[l.sidenum[i]].midtexture == 0)
				and not  tablelookup(l.special,lineTypeMidBlacklistArray[i+1])
				and not !(l.flags&ML_DONTPEGTOP) 
				and not !(l.flags&ML_DONTPEGBOTTOM) 
				and not !(l.flags&ML_EFFECT3)
				or (l.flags&ML_IMPASSIBLE and (l.backside == nil))
					sides[l.sidenum[i]].midtexture = flatswapinit(unique_textures,sides[l.sidenum[i]].midtexture,tswappool)
				else
					if not (sides[l.sidenum[i]].midtexture == 0) and (tablelookup(sides[l.sidenum[i]].midtexture,grasswalltable2) or
					tablelookup(sides[l.sidenum[i]].midtexture,grassedgetable2) or 
					tablelookup(sides[l.sidenum[i]].midtexture,cherrywalltable2) or
					tablelookup(sides[l.sidenum[i]].midtexture,woodwalltable2) or
					tablelookup(sides[l.sidenum[i]].midtexture,waterfalltable))
					and not  tablelookup(l.special,lineTypeMidBlacklistArray[i+1])
						sides[l.sidenum[i]].midtexture = flatswapinit(unique_textures,sides[l.sidenum[i]].midtexture,tswappool)
					end
				end
				if not (sides[l.sidenum[i]].bottomtexture == 0) and not tablelookup(l.special,lineTypeBackBlacklistArray[i+1])
					sides[l.sidenum[i]].bottomtexture = flatswapinit(unique_textures,sides[l.sidenum[i]].bottomtexture,tswappool)
				end
			end
		end
		
		for s in sectors.iterate
			if (s.lightlevel - 48 > 144)
				s.lightlevel = $1-48
			elseif (s.lightlevel - 48  < 144) and (s.lightlevel > 144) 
				s.lightlevel = 144
			else
			end
			if (GetSecSpecial(s.special, 4) == 4) or (GetSecSpecial(s.special, 4) == 6)
			or (GetSecSpecial(s.special, 3) == 3) or (GetSecSpecial(s.special, 3) == 1)
			or (GetSecSpecial(s.special, 3) == 5) or tablelookup(s,sectorblacklist)
				continue
			end
			s.floorpic = flatswapinit(unique_flats,s.floorpic,fswappool)
			s.ceilingpic = flatswapinit(unique_flats,s.ceilingpic,fswappool)
		end
	end
	
	if leveltime == (6*TICRATE + (3*TICRATE/4))
		for p in players.iterate
			COM_BufInsertText(p, "tunes NEWFLP")
		end
	end
	else
		return
	end
end
)

local function mysterywater(mo)
	--Mystery water sounds.
	if neweden == true or (infiniteparadise.value == 1) and not
	((mapheaderinfo[gamemap].lvlttl == "Flesh Eden") 
	or (mapheaderinfo[gamemap].lvlttl == "Flesh Paradise"))
		if S_SoundPlaying(mo,sfx_wslap) or S_SoundPlaying(mo,sfx_splish)
			S_StopSoundByID(mo, sfx_wslap)
			S_StopSoundByID(mo, sfx_splish)
			S_StartSound(mo, sfx_ghit)
		end
	end
end

addHook("MobjThinker",mysterywater,MT_PLAYER)
addHook("MobjThinker",mysterywater,MT_SPLISH)