---@type table> local PALETTE_DATA local CURRENT_PALETTE = "default" local DEFAULT_COLOR = vmath.vector4(1, 1, 1, 1) local COLOR_X = hash("color.x") local COLOR_Y = hash("color.y") local COLOR_Z = hash("color.z") local M = {} ---Get color color by id ---@param color_id string ---@return vector4 function M.get(color_id) -- Check is it hex: starts with "#" or contains only 3 or 6 hex symbols if type(color_id) == "string" then if string.sub(color_id, 1, 1) == "#" or string.match(color_id, "^[0-9a-fA-F]+$") then return M.hex2vector4(color_id) end end return PALETTE_DATA[CURRENT_PALETTE] and PALETTE_DATA[CURRENT_PALETTE][color_id] or DEFAULT_COLOR end ---Add palette to palette data ---@param palette_name string ---@param palette_data table function M.add_palette(palette_name, palette_data) PALETTE_DATA[palette_name] = PALETTE_DATA[palette_name] or {} local palette = PALETTE_DATA[palette_name] for color_id, color in pairs(palette_data) do if type(color) == "string" then palette[color_id] = M.hex2vector4(color) else palette[color_id] = color end end end function M.set_palette(palette_name) if PALETTE_DATA[palette_name] then CURRENT_PALETTE = palette_name end end function M.get_palette() return CURRENT_PALETTE end ---Set color of gui node without changing alpha ---@param gui_node node ---@param color vector4|vector3|string Color in vector4, vector3 or color id from palette function M.set_color(gui_node, color) if type(color) == "string" then color = M.get(color) end gui.set(gui_node, COLOR_X, color.x) gui.set(gui_node, COLOR_Y, color.y) gui.set(gui_node, COLOR_Z, color.z) end function M.get_random_color() return vmath.vector4(math.random(), math.random(), math.random(), 1) end ---Lerp colors via color HSB values function M.lerp(t, color1, color2) local h1, s1, v1 = M.rgb2hsb(color1.x, color1.y, color1.z) local h2, s2, v2 = M.rgb2hsb(color2.x, color2.y, color2.z) local h = h1 + (h2 - h1) * t local s = s1 + (s2 - s1) * t local v = v1 + (v2 - v1) * t local r, g, b, a = M.hsb2rgb(h, s, v) a = a or 1 return vmath.vector4(r, g, b, a) end ---@param hex string ---@return number, number, number function M.hex2rgb(hex) if not hex or #hex < 3 then return 0, 0, 0 end hex = hex:gsub("^#", "") if #hex == 3 then hex = hex:gsub("(.)", "%1%1") end return tonumber("0x" .. hex:sub(1,2)) / 255, tonumber("0x" .. hex:sub(3,4)) / 255, tonumber("0x" .. hex:sub(5,6)) / 255 end ---@param hex string ---@param alpha number|nil ---@return vector4 function M.hex2vector4(hex, alpha) local r, g, b = M.hex2rgb(hex) return vmath.vector4(r, g, b, alpha or 1) end ---Convert hsb color to rgb color ---@param r number @Red value ---@param g number @Green value ---@param b number @Blue value ---@param alpha number|nil @Alpha value. Default is 1 function M.rgb2hsb(r, g, b, alpha) alpha = alpha or 1 local min, max = math.min(r, g, b), math.max(r, g, b) local delta = max - min local h, s, v = 0, max, max s = max ~= 0 and delta / max or 0 if delta ~= 0 then if r == max then h = (g - b) / delta elseif g == max then h = 2 + (b - r) / delta else h = 4 + (r - g) / delta end h = (h / 6) % 1 end alpha = alpha > 1 and alpha / 100 or alpha return h, s, v, alpha end ---Convert hsb color to rgb color ---@param h number @Hue ---@param s number @Saturation ---@param v number @Value ---@param alpha number|nil @Alpha value. Default is 1 function M.hsb2rgb(h, s, v, alpha) local r, g, b local i = math.floor(h * 6) local f = h * 6 - i local p = v * (1 - s) local q = v * (1 - f * s) local t = v * (1 - (1 - f) * s) i = i % 6 if i == 0 then r, g, b = v, t, p elseif i == 1 then r, g, b = q, v, p elseif i == 2 then r, g, b = p, v, t elseif i == 3 then r, g, b = p, q, v elseif i == 4 then r, g, b = t, p, v elseif i == 5 then r, g, b = v, p, q end return r, g, b, alpha end ---Convert rgb color to hex color ---@param red number @Red value ---@param green number @Green value ---@param blue number @Blue value function M.rgb2hex(red, green, blue) local r = string.format("%x", math.floor(red * 255)) local g = string.format("%x", math.floor(green * 255)) local b = string.format("%x", math.floor(blue * 255)) return string.upper((#r == 1 and "0" or "") .. r .. (#g == 1 and "0" or "") .. g .. (#b == 1 and "0" or "") .. b) end function M.load_palette() local PALETTE_PATH = sys.get_config_string("druid.palette") if PALETTE_PATH then PALETTE_DATA = M.load_json(PALETTE_PATH) --[[@as table>]] end PALETTE_DATA = PALETTE_DATA or {} for _, palette_data in pairs(PALETTE_DATA) do for color_id, color in pairs(palette_data) do if type(color) == "string" then palette_data[color_id] = M.hex2vector4(color) end end end end ---Load JSON file from game resources folder (by relative path to game.project) ---Return nil if file not found or error ---@param json_path string ---@return table|nil function M.load_json(json_path) local resource, is_error = sys.load_resource(json_path) if is_error or not resource then return nil end return json.decode(resource) end M.load_palette() return M