local P={}local z={}P.enums=z function P.createEnum(P,g)assert(typeof(P)=="\115\116\114\105\110\103","\098\097\100 \097\114\103\117\109\101\110\116 \035\049 - \101\110\117\109\115 \109\117\115\116 \098\101 \099\114\101\097\116\101\100 \117\115\105\110\103 \097 \115\116\114\105\110\103 \110\097\109\101\033")assert(typeof(g)=="\116\097\098\108\101","\098\097\100 \097\114\103\117\109\101\110\116 \035\050 - \101\110\117\109\115 \109\117\115\116 \098\101 \099\114\101\097\116\101\100 \117\115\105\110\103 \097 \116\097\098\108\101\033")assert(not z[P],("\101\110\117\109 \'\037\115\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033"):format(P))local q={}local e={}local k={}local A={}local t={getName=function(P)P=tostring(P)local z=k[P]if not z then z=A[P]end if z then return g[z][1]end end;getValue=function(P)P=tostring(P)local z=e[P]if not z then z=A[P]end if z then return g[z][2]end end;getProperty=function(P)P=tostring(P)local z=e[P]if not z then z=k[P]end if z then return g[z][3]end end}for P,z in pairs(g)do assert(typeof(z)=="\116\097\098\108\101",("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115 - \100\101\116\097\105\108\115 \109\117\115\116 \111\110\108\121 \098\101 \099\111\109\112\114\105\115\101\100 \111\102 \116\097\098\108\101\115\033"):format(P))local g=z[1]assert(typeof(g)=="\115\116\114\105\110\103",("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115\046\049 - \100\101\116\097\105\108 \110\097\109\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103\033"):format(P))assert(typeof(not e[g]),("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115\046\049 - \116\104\101 \100\101\116\097\105\108 \110\097\109\101 \'\037\115\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033"):format(P,g))assert(typeof(not t[g]),("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115\046\049 - \116\104\097\116 \110\097\109\101 \105\115 \114\101\115\101\114\118\101\100\046"):format(P,g))e[tostring(g)]=P local E=z[2]local F=tostring(E)assert(typeof(not k[F]),("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115\046\050 - \116\104\101 \100\101\116\097\105\108 \118\097\108\117\101 \'\037\115\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033"):format(P,F))k[F]=P local Z=z[3]if Z then assert(typeof(not A[Z]),("\098\097\100 \097\114\103\117\109\101\110\116 \035\050\046\037\115\046\051 - \116\104\101 \100\101\116\097\105\108 \112\114\111\112\101\114\116\121 \'\037\115\' \097\108\114\101\097\100\121 \101\120\105\115\116\115\033"):format(P,tostring(Z)))A[tostring(Z)]=P end q[g]=E setmetatable(q,{__index=function(P,z)return t[z]end})end z[P]=q return q end function P.getEnums()return z end local g=P.createEnum for P,z in pairs(script:GetChildren())do if z:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then local P=require(z)g(z.Name,P)end end return P
-- Custom enum implementation that provides an effective way to compare, send
-- and store values. Instead of returning a userdata value, enum items return
-- their corresponding itemValue (an integer) when indexed. Enum items can
-- also associate a 'property', specified as the third element, which can be
-- retrieved by doing ``enum.getProperty(ITEM_NAME_OR_VALUE)``
-- This ultimately means groups of data can be easily categorised, efficiently
-- transmitted over networks and saved without throwing errors.
-- Ben Horton (ForeverHD)

-- LOCAL
local Enum = {}
local enums = {}
Enum.enums = enums

-- METHODS
function Enum.createEnum(enumName, details)
	assert(
		typeof(enumName) == "string",
		"bad argument #1 - enums must be created using a string name!"
	)
	assert(typeof(details) == "table", "bad argument #2 - enums must be created using a table!")
	assert(not enums[enumName], ("enum '%s' already exists!"):format(enumName))

	local enum = {}
	local usedNames = {}
	local usedValues = {}
	local usedProperties = {}
	local enumMetaFunctions = {
		getName = function(valueOrProperty)
			valueOrProperty = tostring(valueOrProperty)
			local index = usedValues[valueOrProperty]
			if not index then
				index = usedProperties[valueOrProperty]
			end
			if index then
				return details[index][1]
			end
		end,
		getValue = function(nameOrProperty)
			nameOrProperty = tostring(nameOrProperty)
			local index = usedNames[nameOrProperty]
			if not index then
				index = usedProperties[nameOrProperty]
			end
			if index then
				return details[index][2]
			end
		end,
		getProperty = function(nameOrValue)
			nameOrValue = tostring(nameOrValue)
			local index = usedNames[nameOrValue]
			if not index then
				index = usedValues[nameOrValue]
			end
			if index then
				return details[index][3]
			end
		end,
	}
	for i, detail in pairs(details) do
		assert(
			typeof(detail) == "table",
			("bad argument #2.%s - details must only be comprised of tables!"):format(i)
		)
		local name = detail[1]
		assert(
			typeof(name) == "string",
			("bad argument #2.%s.1 - detail name must be a string!"):format(i)
		)
		assert(
			typeof(not usedNames[name]),
			("bad argument #2.%s.1 - the detail name '%s' already exists!"):format(i, name)
		)
		assert(
			typeof(not enumMetaFunctions[name]),
			("bad argument #2.%s.1 - that name is reserved."):format(i, name)
		)
		usedNames[tostring(name)] = i
		local value = detail[2]
		local valueString = tostring(value)
		--assert(typeof(value) == "number" and math.ceil(value)/value == 1, ("bad argument #2.%s.2 - detail value must be an integer!"):format(i))
		assert(
			typeof(not usedValues[valueString]),
			("bad argument #2.%s.2 - the detail value '%s' already exists!"):format(i, valueString)
		)
		usedValues[valueString] = i
		local property = detail[3]
		if property then
			assert(
				typeof(not usedProperties[property]),
				("bad argument #2.%s.3 - the detail property '%s' already exists!"):format(
					i,
					tostring(property)
				)
			)
			usedProperties[tostring(property)] = i
		end
		enum[name] = value
		setmetatable(enum, {
			__index = function(_, index)
				return enumMetaFunctions[index]
			end,
		})
	end

	enums[enumName] = enum
	return enum
end

function Enum.getEnums()
	return enums
end

-- SETUP ENUMS
local createEnum = Enum.createEnum
for _, childModule in pairs(script:GetChildren()) do
	if childModule:IsA("ModuleScript") then
		local enumDetail = require(childModule)
		createEnum(childModule.Name, enumDetail)
	end
end

--[[
-- Example enum
createEnum("Color", {
	{"White", 1, Color3.fromRGB(255, 255, 255)},
	{"Black", 2, Color3.fromRGB(0, 0, 0)},
})
--]]

return Enum
