diff --git a/README.md b/README.md index c2fd064..a957cc6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # figura-skin -my figura avatar!! this currently targets version **0.1.5** +my figura avatar!! this currently targets version **0.15** `textures/main.png` contains the skins base, which can be used as your default minecraft skin if you so choose @@ -8,4 +8,4 @@ my figura avatar!! this currently targets version **0.1.5** to get proper typings, find the correct branch for your figura version [on this repository](github.com/GrandpaScout/FiguraRewriteVSDocs/). then, copy everything from the `src/` folder into your current working folder here -unfortunately, 0.1.5 typings aren't done yet, so just make sure it works :) +unfortunately, 0.15 typings aren't done yet, so just make sure it works :) diff --git a/avatar.json b/avatar.json index 2cb9aad..4802630 100644 --- a/avatar.json +++ b/avatar.json @@ -4,8 +4,7 @@ "authors": [ "reidlab", "mrsirsquishy: Squishy's API", - "Agapurnis: Gradient and nameplate script", - "skyevg: Oklab script", + "purpledeni + Manuel_: Gradient Scroll Nickname", "adristel: Wet Clothes/Fur Script" ], "color": "#d87b5a", diff --git a/scripts/libs/gradient.lua b/scripts/libs/gradient.lua deleted file mode 100644 index 6af0dc3..0000000 --- a/scripts/libs/gradient.lua +++ /dev/null @@ -1,370 +0,0 @@ --- work in pewgross --- do not share !! - ----@class UnitRGBA: Vector4 [r, g, b, a]; all values are 0-1. -local UnitRGBA = {}; - ----@param vector Vector4 a Vector4 matching the format of UnitRGBA ([r, g, b, a]; all values are 0-1) ----@return UnitRGBA? output nil the vector with the new metatable if valid, otherwise nil ----@return string? error a string describing the error if invalid, otherwise nil ----@nodiscard ----@see UnitRGBA.fromUnitStrict to throw an error instead of returning nil -function UnitRGBA.fromUnit(vector) - for i, v in ipairs({vector:unpack()}) do - if type(v) ~= "number" then - return nil, "Vector component " .. i .. " is not a number"; - end - if v < 0 or v > 1 then - return nil, "Vector component " .. i .. " is not in the range 0-1; got " .. v; - end - end - return setmetatable({ vector = vector }, UnitRGBA), nil; -end ----@param vector Vector4 a Vector4 matching the format of UnitRGBA ([r, g, b, a]; all values are 0-1) ----@return UnitRGBA vector the vector with the new metatable if valid. otherwise, an error is thrown ----@nodiscard ----@see UnitRGBA.fromUnit to return nil instead of throwing an error -function UnitRGBA.fromUnitStrict(vector) - local result, why = UnitRGBA.fromUnit(vector); - if result == nil then - error("Invalid vector: " .. why); - end - return result; -end ----@param vector Vector4 a Vector4 matching the format of UnitRGBA ([r, g, b, a]; all values are 0-255) ----@return UnitRGBA? output nil the vector with the new metatable if valid, otherwise nil ----@return string? error a string describing the error if invalid, otherwise nil ----@nodiscard ----@see UnitRGBA.fromU8Strict to throw an error instead of returning nil -function UnitRGBA.fromU8(vector) - for i, v in ipairs(vector) do - if type(v) ~= "number" then - return nil, "Vector component " .. i .. " is not a number"; - end - if v < 0 or v > 255 then - return nil, "Vector component " .. i .. " is not in the range 0-255"; - end - end - return setmetatable({ vector = vector / 255 }, UnitRGBA), nil; -end ----@param vector Vector4 a Vector4 matching the format of UnitRGBA ([r, g, b, a]; all values are 0-255) ----@return UnitRGBA vector the vector with the new metatable if valid. otherwise, an error is thrown ----@nodiscard ----@see UnitRGBA.fromU8 to return nil instead of throwing an error -function UnitRGBA.fromU8Strict(vector) - local result, why = UnitRGBA.fromU8(vector); - if result == nil then - error("Invalid vector: " .. why); - end - return result; -end - ----@param vector Vector4 a Vector4 matching the format of UnitRGBA ([r, g, b, a]; all values are 0-1) ----@return UnitRGBA vector the vector with the new metatable ----@nodiscard -function UnitRGBA.fromUnitClamping(vector) - vector[1] = math.clamp(vector[1], 0, 1); - vector[2] = math.clamp(vector[2], 0, 1); - vector[3] = math.clamp(vector[3], 0, 1); - vector[4] = math.clamp(vector[4], 0, 1); - return setmetatable({ vector = vector }, UnitRGBA); -end - ----@param hex string a color in hexadecimal format (e.g. "#FF00FF") ----@return UnitRGBA? output the color with the new metatable if valid, otherwise nil ----@return string? error a string describing the error if invalid, otherwise nil ----@nodiscard ----@see UnitRGBA.fromHexStrict to throw an error instead of returning nil -function UnitRGBA.fromHex(hex) - if type(hex) ~= "string" then - return nil, "not a string"; - end - if hex:sub(1, 1) == "#" then - hex = hex:sub(2); - end - if hex:len() ~= 6 and hex:len() ~= 8 then - return nil, "not 6 or 8 characters long"; - end - local vector = vec(0, 0, 0, 1); - for i = 1, 3 do - local value = tonumber(hex:sub(i * 2 - 1, i * 2), 16); - if value == nil then - return nil, "contains non-hex characters"; - end - vector[i] = value / 255; - end - if hex:len() == 8 then - local value = tonumber(hex:sub(7, 8), 16); - if value == nil then - return nil, "contains non-hex characters"; - end - vector[4] = value / 255; - else - vector[4] = 1; - end - return setmetatable({ vector = vector }, UnitRGBA), nil; -end ----@param hex string a color in hexadecimal format (e.g. "#FF00FF") ----@return UnitRGBA color the color with the new metatable if valid. otherwise, an error is thrown ----@nodiscard ----@see UnitRGBA.fromHex to return nil instead of throwing an error -function UnitRGBA.fromHexStrict(hex) - local result, why = UnitRGBA.fromHex(hex); - if result == nil then - error("Invalid hex color: " .. why); - end - return result; -end - ----@return boolean opaque whether the color is *fully opaque* (as in, a == 1, not just a > 0). ----@see UnitRGBA:isTranslucent to see if the color is partially or fully transparent. ----@see UnitRGBA:isTransparent to see if the color is fully transparent. ----@nodiscard -function UnitRGBA:isOpaque() - return self.vector[4] == 1; -end ----@return boolean transparent whether the color is *fully transparent* (as in, a == 0, not just a < 1). ----@see UnitRGBA:isTranslucent to see if the color is partially transparent. ----@see UnitRGBA:isOpaque to see if the color is fully opaque. ----@nodiscard -function UnitRGBA:isTransparent() - return self.vector[4] == 0; -end ----@return boolean translucent whether the color is *translucent* (as in, a < 1). ----@see UnitRGBA:isTransparent to see if the color is fully transparent. ----@see UnitRGBA:isOpaque to see if the color is fully opaque. ----@nodiscard -function UnitRGBA:isTranslucent() - return self.vector[4] < 1; -end - -function UnitRGBA:toVec3() - return vec(self.vector[1], self.vector[2], self.vector[3]); -end - - ----@return Vector4 vector Vector4 with format ([r, g, b, a]; all values are 0-255) ----@nodiscard -function UnitRGBA:toU8vec4() - return vec(self.vector[1] * 255, self.vector[2] * 255, self.vector[3] * 255, self.vector[4] * 255); -end ----@return Vector3 vector Vector3 with format ([r, g, b]; all values are 0-255) ----@nodiscard -function UnitRGBA:toU8vec3() - local a = vec(self.vector[1] * 255, self.vector[2] * 255, self.vector[3] * 255) - return a -end - - ----@param hash? boolean? whether to include the hash symbol (#) in the output. defaults to true ----@param alpha? boolean? whether to include the alpha channel in the output. defaults to true ----@return string hex the color in RGBA hexadecimal format (e.g. "#FF00FF") ----@nodiscard -function UnitRGBA:toHex(hash, alpha) - local hex = (hash == nil or hash) and "#" or ""; - local iter = (alpha == nil or alpha) and 4 or 3; - for i = 1, iter do - local value = math.round(self.vector[i] * 255); - hex = hex .. string.format("%02X", value); - end - return hex; -end - -function UnitRGBA:__index(key) - return rawget(UnitRGBA, key) or self.vector[key]; -end -function UnitRGBA:__tostring() - return "UnitRGBA(" .. table.concat(self.vector, ", ") .. ")"; -end - -function UnitRGBA:__sub(other) - return UnitRGBA.fromUnitClamping(vec( - self.vector[1] - other.vector[1], - self.vector[2] - other.vector[2], - self.vector[3] - other.vector[3], - self.vector[4] - other.vector[4] - )); -end - ---- Transparent white. -UnitRGBA.TRANSPARENT_WHITE = UnitRGBA.fromUnitStrict(vec(1, 1, 1, 0)); ---- Transparent black. -UnitRGBA.TRANSPARENT_BLACK = UnitRGBA.fromUnitStrict(vec(0, 0, 0, 0)); ---- Opaque white. -UnitRGBA.WHITE = UnitRGBA.fromUnitStrict(vec(1, 1, 1, 1)); ---- Opaque black. -UnitRGBA.BLACK = UnitRGBA.fromUnitStrict(vec(0, 0, 0, 1)); - ----@class Keypoint - A keypoint (interpolation point definition) in a gradient. ----@field public color UnitRGBA color of the keypoint ----@field public time number a number between 0 and 1 (inclusive on both ends) -local Keypoint = {}; - ----Returns a new Keypoint object. ----@param color UnitRGBA color of the keypoint ----@param time number a number between 0 and 1 (inclusive on both ends) ----@return Keypoint ----@nodiscard -function Keypoint.new(color, time) - return setmetatable({ - color = color, - time = time - }, Keypoint); -end - ----@class Gradient A gradient definition. ----@field public keypoints Keypoint[] a list of keypoint objects ----@field public loop boolean whether the gradient should loop when given out-of-bounds time values. if false, an error is instead thrown. -local Gradient = {}; - ----Returns a new Gradient object. ----@param keypoints Keypoint[] a list of keypoint objects ----@param loop boolean? whether the gradient should loop when given out-of-bounds time values. if false, an error is instead thrown. defaults to true. ----@return Gradient ----@nodiscard -function Gradient.new(keypoints, loop) - return setmetatable({ - keypoints = keypoints, - loop = loop == nil or loop - }, Gradient); -end - -local function easeInOutQuad(t) - return t < 0.5 and 2 * t * t or 1 - (-2 * t + 2)^2 / 2 -end - -local oklab = require("scripts.libs.oklab") ----Returns the color of the gradient at a given time. ----@param time number a number between 0 and 1 (inclusive on both ends) (can extend beyond this range if Gradient.loop is true) ----@return UnitRGBA? color the color of the gradient at the given time, or nil if the time is out of bounds and Gradient.loop is false -function Gradient:at(time) - if time < 0 or time > 1 then - if not self.loop then - return nil; - end - time = time % 1; - end - - if time == 0 then return self.keypoints[1].color - elseif time == 1 then return self.keypoints[#self.keypoints].color - end - - for i = 1, #self.keypoints - 1 do - local this = self.keypoints[i] - local next = self.keypoints[i + 1] - if time >= this.time and time < next.time then - local t = easeInOutQuad((time - this.time) / (next.time - this.time)); - - local alpha = math.lerp(this.color.a, next.color.a, t) - local La, Aa, Ba = oklab.srgbToLinear(this.color.xyz):unpack() - local Lb, Ab, Bb = oklab.srgbToLinear(next.color.xyz):unpack() - local mixed = vec( - math.lerp(La, Lb, easeInOutQuad(t)), - math.lerp(Aa, Ab, t), - math.lerp(Ba, Bb, t) - ) - - mixed = oklab.linearToSrgb(mixed); - mixed = vec(mixed.x, mixed.y, mixed.z, alpha) - return UnitRGBA.fromUnitClamping(mixed); - end - end - - if #self.keypoints == 1 then - return self.keypoints[1].color; - end - - error("Gradient.at: time " .. time .. " is out of bounds"); -end - ----@param colors string[] a list of colors in hexadecimal format (e.g. "#FF00FF") ----@param loop boolean? whether the gradient should loop when given out-of-bounds time values. if false, an error is instead thrown. defaults to true. ----@return Gradient gradient the gradient with the new metatable if valid. otherwise, an error is thrown -function Gradient.distributedHex(colors, loop) - local keypoints = {}; - for i, hex in ipairs(colors) do - local time = (i - 1) / (#colors - 1); - local color = UnitRGBA.fromHexStrict(hex); - table.insert(keypoints, Keypoint.new(color, time)); - end - return Gradient.new(keypoints, loop); -end - -function Gradient:__index(key) - return rawget(Gradient, key) or self[key]; -end - - ----@class SimpleGradientBuilder ----@field public colors UnitRGBA[] a list of colors -local SimpleGradientBuilder = {}; ----@return SimpleGradientBuilder builder a simple gradient builder -function SimpleGradientBuilder.new() - return setmetatable({ colors = {} }, { __index = SimpleGradientBuilder }); -end ----Reflect the colors of the gradient; this makes it nicer when looping. ----@param reflect_last boolean? whether to also reflect the final color, such that it would appear twice in a row at the middle. if false, it'll only be present once ----@return SimpleGradientBuilder self builder for chaining -function SimpleGradientBuilder:reflect(reflect_last) - ---@type UnitRGBA[] - local reflected = {}; - for i = 1, #self.colors - 1 do - table.insert(reflected, self.colors[i]); - end - table.insert(reflected, self.colors[#self.colors]) - if reflect_last then - table.insert(reflected, self.colors[#self.colors]) - end - for i = #self.colors - 1, 1, -1 do - table.insert(reflected, self.colors[i]) - end - self.colors = reflected - return self; -end ----"Thicken" the gradient by duplicating every keypoint. This means that the actual colors themselves will have as much presence as the transitions. ----@param amount? integer how many times to duplicate each keypoint. defaults to 1. ----@return SimpleGradientBuilder self builder for chaining -function SimpleGradientBuilder:thicken(amount) - amount = amount or 1; - local thickened = {}; - for i = 1, #self.colors do - for _ = 0, amount do - table.insert(thickened, self.colors[i]) - end - end - self.colors = thickened; - return self; -end ----@param loop boolean? whether the gradient should loop when given out-of-bounds time values. if false, an error is instead thrown. defaults to true. ----@return Gradient gradient the constructed gradient -function SimpleGradientBuilder:build(loop) - loop = loop == nil or loop; - - ---@type Keypoint[] - local keypoints = {}; - for i, color in ipairs(self.colors) do - table.insert(keypoints, Keypoint.new(color, (i - 1) / (#self.colors - 1))); - end - - return setmetatable({ - keypoints = keypoints, - loop = loop - }, Gradient); -end ----@param colors string[] | string hexadecimal color(s) to add -function SimpleGradientBuilder:add(colors) - if type(colors) == "string" then - colors = {colors} - end - for _, color in ipairs(colors) do - table.insert(self.colors, UnitRGBA.fromHexStrict(color)) - end - return self; -end - -return { - UnitRGBA = UnitRGBA, - Keypoint = Keypoint, - Gradient = Gradient, - SimpleGradientBuilder = SimpleGradientBuilder, -} diff --git a/scripts/libs/oklab.lua b/scripts/libs/oklab.lua deleted file mode 100644 index f12a95a..0000000 --- a/scripts/libs/oklab.lua +++ /dev/null @@ -1,113 +0,0 @@ -local lib = {} - ----@param x number ----@return number -local function fromLinear(x) - if x >= 0.0031308 then - return 1.055 * math.pow(x, 1.0/2.4) - 0.055 - else - return 12.92 * x - end -end - ----@param x number ----@return number -local function toLinear(x) - if x >= 0.04045 then - return math.pow((x + 0.055)/(1 + 0.055), 2.4) - else - return x / 12.92 - end -end - ----Converts from sRGB to Linear sRGB ----@param color Vector3 ----@return Vector3 -function lib.srgbToLinear(color) - return vec(toLinear(color.x), toLinear(color.y), toLinear(color.z)) -end - ----Converts from Linear sRGB to sRGB ----@param color Vector3 ----@return Vector3 -function lib.linearToSrgb(color) - return vec(fromLinear(color.x), fromLinear(color.y), fromLinear(color.z)) -end - ----Converts from Linear sRGB to OKLAB ----@param color Vector3 ----@return Vector3 -function lib.linearToOklab(color) - local l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b - local m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b - local s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b - - local l_ = math.pow(l, 1/3) - local m_ = math.pow(m, 1/3) - local s_ = math.pow(s, 1/3) - - return vec( - 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_, - 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_, - 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_ - ) -end - ----Converts from OKLAB to Linear sRGB ----@param color Vector3 ----@return Vector3 -function lib.oklabToLinear(color) - local l_ = color.x + 0.3963377774 * color.y + 0.2158037573 * color.z; - local m_ = color.x - 0.1055613458 * color.y - 0.0638541728 * color.z; - local s_ = color.x - 0.0894841775 * color.y - 1.2914855480 * color.z; - - local l = l_*l_*l_; - local m = m_*m_*m_; - local s = s_*s_*s_; - - return vec( - 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, - -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, - -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s - ) -end - ----Converts from LAB to LCh ----@param color Vector3 ----@return Vector3 -function lib.labToLch(color) - return vec( - color.x, - math.sqrt(color.y * color.y + color.z * color.z), - math.deg(math.atan2(color.z, color.y)) - ) -end - ----Converts from LCh to LAB ----@param color Vector3 ----@return Vector3 -function lib.lchToLab(color) - return vec( - color.x, - color.y * math.cos(math.rad(color.z)), - color.y * math.sin(math.rad(color.z)) - ) -end - - - ----Converts from sRGB to OKLCh ----@param color Vector3 ----@return Vector3 -function lib.srgbToOklch(color) - return lib.labToLch(lib.linearToOklab(lib.srgbToLinear(color))) -end - ----Converts from OKLCh to sRGB ----@param color Vector3 ----@return Vector3 -function lib.oklchToSrgb(color) - return lib.linearToSrgb(lib.oklabToLinear(lib.lchToLab(color))) -end - -return lib \ No newline at end of file diff --git a/scripts/nameplate.lua b/scripts/nameplate.lua index 95cb7b4..93c749b 100644 --- a/scripts/nameplate.lua +++ b/scripts/nameplate.lua @@ -1,110 +1,22 @@ -local g = require("scripts.libs.gradient") - ----@class ColoredText ----@field text string ----@field color string - ----@param name string ----@param gradient Gradient ----@param nameplates Nameplate | Nameplate[] where to use the animated name ----@param count number number of gradients to be present in the span ----@param phase_shift_rate number how fast to shift the gradient ----@returns fun():void # function to unregister -local function animate_gradient_name(name, gradient, nameplates, count, phase_shift_rate) - if type(nameplates) ~= "table" then - nameplates = {nameplates} - end - - local string_width = client.getTextWidth(name) / count; - local string_width_chars = {}; - for i = 1, #name do - string_width_chars[i] = client.getTextWidth(name:sub(i, i)) - end - - ---@type ColoredText[] - local result = {} - - local phase_shift = 0; - local function render() - if client.isPaused() then - return - end - - local acc = 0; - for i = 1, #name do - local offset = acc + string_width_chars[i]; - local color = gradient:at((offset / string_width) + phase_shift); - acc = offset - - result[i] = { - text = name:sub(i, i), - color = color:toHex(true, false) - } - end - - phase_shift = phase_shift + phase_shift_rate - - local json = toJson(result); - for _, nameplate in pairs(nameplates) do - nameplate:setText(json) - end - end - - events.render:register(render) - return function () - events.render:remove(render) - end -end - ----@param name string ----@param gradient Gradient ----@param nameplates Nameplate | Nameplate[] where to use the static name ----@param count number number of gradients to be present in the span ----@param phase_shift number shift of the gradient -local function static_gradient_name(name, gradient, nameplates, count, phase_shift) - if type(nameplates) ~= "table" then - nameplates = {nameplates} - end - - local string_width = client.getTextWidth(name) / count; - local string_width_chars = {}; - for i = 1, #name do - string_width_chars[i] = client.getTextWidth(name:sub(i, i)) - end - - ---@type ColoredText[] - local result = {} - - local acc = 0; - for i = 1, #name do - local offset = acc + string_width_chars[i]; - local color = gradient:at((offset / string_width) + phase_shift); - acc = offset - - result[i] = { - text = name:sub(i, i), - color = color:toHex(true, false) - } - end - - local json = toJson(result); - for _, nameplate in pairs(nameplates) do - nameplate:setText(json) - end -end - local name = "reidlab!" -local gradient_count = 1 -local gradient = g.SimpleGradientBuilder.new() - :add({ "#d87b5a", "#e0ab91" }) - :reflect(false) - :build() +local colors = { "#d87b5a", "#e0ab91" } +local offset = 0.05 +local speed = 0.05 -animate_gradient_name(name, gradient, { - nameplate.ENTITY, - nameplate.LIST -}, gradient_count, 0.005) +colors[#colors + 1] = colors[1] +offset = offset / speed -static_gradient_name(name, gradient, { - nameplate.CHAT -}, gradient_count, 0) +events.TICK:register(function () + local newName = "[" + + for i = 1, #name, 1 do + local counter = (((world.getTime() + offset * i) * speed) % (#colors - 1)) + 1 + local counterFloored = math.floor(counter) + local color = math.lerp(vectors.hexToRGB(colors[counterFloored]), vectors.hexToRGB(colors[counterFloored + 1]), counter - counterFloored) + newName = newName .. '{"text":"' .. name:sub(i,i) .. '","color":"#' .. vectors.rgbToHex(color) .. '"},' + avatar:setColor(color) + end + + newName = newName:sub(1, #newName - 1) .. "]" + nameplate.ALL:setText(newName) +end) diff --git a/scripts/soggy.lua b/scripts/soggy.lua index 2d8c2eb..3367573 100644 --- a/scripts/soggy.lua +++ b/scripts/soggy.lua @@ -24,6 +24,7 @@ events.TICK:register(function () if world.getTime() % driptime == 0 and tw ~= 1 and not (player:isUnderwater()) then for _ = 0, driptime*0.5 do particles:newParticle("falling_dripstone_water",player:getPos():add(offset+vec(0,0.7,0)),offset2) end end if world.getTime() % driptime == 0 and bw ~= 1 and not (player:isInWater()) then for _ = 0, driptime*0.5 do particles:newParticle("falling_dripstone_water",player:getPos():add(offset),offset2:mul(0.5)) end end + utils.forEachNonGroup(models.models.main.LeftArm, function (part) part:setColor(tw,tw,tw) end) utils.forEachNonGroup(models.models.main.RightArm, function (part) part:setColor(tw,tw,tw) end) utils.forEachNonGroup(models.models.main.Head, function (part) part:setColor(tw,tw,tw) end) diff --git a/scripts/vanilla_model.lua b/scripts/vanilla_model.lua index f6c7877..1e0ffda 100644 --- a/scripts/vanilla_model.lua +++ b/scripts/vanilla_model.lua @@ -1,4 +1,4 @@ -for _, vanillaModel in pairs({ +for _, vanillaModel in ipairs({ vanilla_model.PLAYER, vanilla_model.ARMOR }) do