-- Allows creating different appearances for same character (for example, -- different sonic sprites) without taking skin slot -- -- Idea by legion local appearance_version = 1 if appearance_loaded then if appearance_loaded < appearance_version then error("Older version of appearance loaded, this may lead to problems") end print("\131NOTICE:\128 Appearance already loaded (version #"..appearance_loaded..")") return end rawset(_G, "appearance_loaded", appearance_version) local appearance = {} -- CONFIG local config = "appearance.cfg" local config_loaded = false local config_tosave = false local function loadConfig() if config_loaded or not consoleplayer then return end local file = io.open(config, "r") -- No config :( if not file then config_loaded = true return end for line in file:lines() do COM_BufInsertText(consoleplayer, line) end config_loaded = true file:close() end local function saveConfig() if not consoleplayer then return end config_tosave = false local file = io.open(config, "w") if not file then return end if consoleplayer.appearance ~= nil then for skin, choice in pairs(consoleplayer.appearance) do file:write("appear "..skin.." "..choice..'\n') end end file:close() end addHook("ThinkFrame", function() if not config_loaded then loadConfig() end if config_tosave then saveConfig() end end) -- CONFIG rawset(_G, "APPEAR_AddAppearance", function(skin, appearname, spritename) if appearname == "default" then error("appearance name 'default' is reserved") end skin = skin:lower() appearname = appearname:lower() spritename = "SPR_"..spritename:upper() freeslot(spritename) if not appearance[skin] then appearance[skin] = {} end appearance[skin][appearname] = _G[spritename] end) COM_AddCommand("appear", function(player, skin, appearname) if not player.appearance then player.appearance = {} end if skin then skin = skin:lower() end if appearname then appearname = appearname:lower() end if not skin then CONS_Printf(player, "Usage: \129setappearance\128 <\130skin\128> [\132name\128]") CONS_Printf(player, "Without appearance name, will print all available variants for it") return end if not appearname then if not appearance[skin] then CONS_Printf(player, "\134This skin doesn't have any custom appearances") else local buff = {"Available appearances for skin \130"..skin.."\128:", "default"} for name, _ in pairs(appearance[skin]) do table.insert(buff, name) end CONS_Printf(player, table.concat(buff, " \132")) end return end if appearname == "default" then player.appearance[skin] = nil config_tosave = player == consoleplayer and config_loaded CONS_Printf(player, "Set \132default\128 appearance for skin \130"..skin) return end if not appearance[skin] then CONS_Printf(player, "\134This skin doesn't have any custom appearances") return end if not appearance[skin][appearname] then CONS_Printf(player, "Appearance \132"..appearname.."\132 \133does not exist\128 for this skin") return end player.appearance[skin] = appearname CONS_Printf(player, "Set appearance \132"..appearname.."\128 for skin \130"..skin) config_tosave = player == consoleplayer and config_loaded end) addHook("PostThinkFrame", function() for player in players.iterate do if player.mo then local pmo = player.mo if player.appearance and player.appearance[pmo.skin] then local sprite = appearance[pmo.skin][player.appearance[pmo.skin]] pmo.sprite = sprite if player.signoverlay then local delete_overlays = {} -- pairs iterator is not synch-safe, but there aren't any -- code that relies on execution order anyway for overlay, _ in pairs(player.signoverlay) do if overlay.valid then overlay.sprite = sprite else -- afaik removing table entry while iterating over -- it is not safe so imma do this instead table.insert(delete_overlays, overlay) end end -- Now we can safely remove everything for _, overlay in ipairs(delete_overlays) do player.signoverlay[overlay] = nil end end end end end end) addHook("MobjThinker", function(mo) if not (mo.target and mo.target.valid) then return end local sign = mo.target if sign.target and sign.target.valid and sign.target.player then local pmo = sign.target local player = pmo.player if not player.signoverlay then player.signoverlay = {} end -- Using table as a set player.signoverlay[mo] = true end end, MT_OVERLAY)