init (again)
i accidentally [doxxed myself](https://github.com/JannisX11/blockbench/issues/1322), thanks blockbench!
This commit is contained in:
commit
d1809b35ee
20 changed files with 2363 additions and 0 deletions
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.nix]
|
||||
indent_size = 2
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# figura extension files
|
||||
.vscode
|
||||
.luarc.json
|
||||
avatar.code-workspace
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# figura-skin
|
||||
|
||||
my figura avatar!! this currently targets version **0.1.5**
|
||||
|
||||
`textures/main.png` contains the skins base, which can be used as your default minecraft skin if you so choose
|
||||
|
||||
## development
|
||||
|
||||
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 :)
|
14
avatar.json
Normal file
14
avatar.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "reidlab's avatar",
|
||||
"description": "meee",
|
||||
"authors": [
|
||||
"reidlab",
|
||||
"mrsirsquishy: Squishy's API",
|
||||
"Agapurnis: Gradient and nameplate script",
|
||||
"skyevg: Oklab script",
|
||||
"adristel: Wet Clothes/Fur Script"
|
||||
],
|
||||
"color": "#d87b5a",
|
||||
"version": "0.1.5",
|
||||
"autoScripts": [ "scripts/main.lua" ]
|
||||
}
|
BIN
avatar.png
Normal file
BIN
avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
1
models/main.bbmodel
Normal file
1
models/main.bbmodel
Normal file
File diff suppressed because one or more lines are too long
1178
scripts/libs/SquAPI.lua
Normal file
1178
scripts/libs/SquAPI.lua
Normal file
File diff suppressed because it is too large
Load diff
428
scripts/libs/SquAssets.lua
Normal file
428
scripts/libs/SquAssets.lua
Normal file
|
@ -0,0 +1,428 @@
|
|||
--[[--------------------------------------------------------------------------------------
|
||||
____ _ _ _ _
|
||||
/ ___| __ _ _ _(_)___| |__ _ _ / \ ___ ___ ___| |_ ___
|
||||
\___ \ / _` | | | | / __| '_ \| | | | / _ \ / __/ __|/ _ \ __/ __|
|
||||
___) | (_| | |_| | \__ \ | | | |_| | / ___ \\__ \__ \ __/ |_\__ \
|
||||
|____/ \__, |\__,_|_|___/_| |_|\__, | /_/ \_\___/___/\___|\__|___/
|
||||
|_| |___/
|
||||
--]]--------------------------------------------------------------------------------------Standard
|
||||
|
||||
--[[
|
||||
-- Author: Squishy
|
||||
-- Discord tag: @mrsirsquishy
|
||||
|
||||
-- Version: 1.0.0
|
||||
-- Legal: ARR
|
||||
|
||||
Framework Functions and classes for SquAPI.
|
||||
This contains some math functions, some simplified calls to figura features, some debugging scripts for convenience, and classes used in SquAPI or for debugging.
|
||||
|
||||
You can also make use of these functions, however it's for more advanced scripters. remember to call: local squassets = require("SquAssets")
|
||||
|
||||
|
||||
]]
|
||||
|
||||
|
||||
|
||||
local squassets = {}
|
||||
|
||||
--Useful Calls
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
--detects the fluid the player is in(air is nil), and if they are fully submerged in that fluid.
|
||||
--vanilla player has an eye height of 1.5 which is used by default for checking if it's submerged, but you can optionally modify this for different height avatars
|
||||
function squassets.getFluid(eyeHeight)
|
||||
local fluid
|
||||
local B = world.getBlockState(player:getPos() + vec(0, eyeHeight or 1.5, 0))
|
||||
local submerged = B.id == "minecraft:water" or B.id == "minecraft:lava"
|
||||
|
||||
if player:isInWater() then
|
||||
fluid = "WATER"
|
||||
elseif player:isInLava() then
|
||||
fluid = "LAVA"
|
||||
end
|
||||
return fluid, submerged
|
||||
end
|
||||
|
||||
--better isOnGround, taken from the figura wiki
|
||||
function squassets.isOnGround()
|
||||
return world.getBlockState(thisEntity:getPos():add(0, -0.1, 0)):isSolidBlock()
|
||||
end
|
||||
|
||||
-- returns how fast the player moves forward, negative means backward
|
||||
function squassets.forwardVel()
|
||||
return player:getVelocity():dot((player:getLookDir().x_z):normalize())
|
||||
end
|
||||
|
||||
-- returns y velocity(negative is down)
|
||||
function squassets.verticalVel()
|
||||
return player:getVelocity()[2]
|
||||
end
|
||||
|
||||
-- returns how fast player moves sideways, negative means left
|
||||
-- Courtesy of @auriafoxgirl on discord
|
||||
function squassets.sideVel()
|
||||
return (player:getVelocity() * matrices.rotation3(0, player:getRot().y, 0)).x
|
||||
end
|
||||
|
||||
--returns a cleaner vanilla head rotation value to use
|
||||
function squassets.getHeadRot()
|
||||
return (vanilla_model.HEAD:getOriginRot()+180)%360-180
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--Math Functions
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
--polar to cartesian coordiantes
|
||||
function squassets.PTOC(r, theta)
|
||||
return r*math.cos(theta), r*math.sin(theta)
|
||||
end
|
||||
|
||||
--cartesian to polar coordinates
|
||||
function squassets.CTOP(x, y)
|
||||
return squassets.pyth(x, y), math.atan(y/x)
|
||||
end
|
||||
|
||||
--3D polar to cartesian coordiantes(returns vec3)
|
||||
function squassets.PTOC3(R, theta, phi)
|
||||
local r, y = squassets.PTOC(R, phi)
|
||||
local x, z = squassets.PTOC(r, theta)
|
||||
return vec(x, y, z)
|
||||
end
|
||||
|
||||
--3D cartesian to polar coordinates
|
||||
function squassets.CTOP3(x, y, z)
|
||||
local v
|
||||
if type(x) == "Vector3" then
|
||||
v = x
|
||||
else
|
||||
v = vec(x, y, z)
|
||||
end
|
||||
local R = v:length()
|
||||
|
||||
return R, math.atan2(v.z, v.x), math.asin(v.y/R)
|
||||
end
|
||||
|
||||
--pythagorean theoremn
|
||||
function squassets.pyth(a, b)
|
||||
return math.sqrt(a^2 + b^2)
|
||||
end
|
||||
|
||||
|
||||
--checks if a point is within a box
|
||||
function squassets.pointInBox(point, corner1, corner2)
|
||||
if not (point and corner1 and corner2) then return false end
|
||||
return
|
||||
point.x >= corner1.x and point.x <= corner2.x and
|
||||
point.y >= corner1.y and point.y <= corner2.y and
|
||||
point.z >= corner1.z and point.z <= corner2.z
|
||||
end
|
||||
|
||||
--returns true if the number is within range, false otherwise
|
||||
function squassets.inRange(lower, num, upper)
|
||||
return lower <= num and num <= upper
|
||||
end
|
||||
|
||||
-- Linear graph
|
||||
-- locally generates a graph between two points, returns the y value at t on that graph
|
||||
function squassets.lineargraph(x1, y1, x2, y2, t)
|
||||
local slope = (y2-y1)/(x2-x1)
|
||||
local inter = y2 - slope*x2
|
||||
return slope*t + inter
|
||||
end
|
||||
|
||||
--Parabolic graph
|
||||
--locally generates a parabolic graph between three points, returns the y value at t on that graph
|
||||
function squassets.parabolagraph(x1, y1, x2, y2, x3, y3, t)
|
||||
local denom = (x1 - x2) * (x1 - x3) * (x2 - x3)
|
||||
|
||||
local a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / denom
|
||||
local b = (x3^2 * (y1 - y2) + x2^2 * (y3 - y1) + x1^2 * (y2 - y3)) / denom
|
||||
local c = (x2 * x3 * (x2 - x3) * y1 + x3 * x1 * (x3 - x1) * y2 + x1 * x2 * (x1 - x2) * y3) / denom
|
||||
|
||||
return a * t^2 + b * t + c
|
||||
end
|
||||
|
||||
--returns 1 if num is >= 0, returns -1 if less than 0
|
||||
function squassets.sign(num)
|
||||
if num < 0 then
|
||||
return -1
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
--returns a vector with the signs of each vector(shows the direction of each vector)
|
||||
function squassets.Vec3Dir(v)
|
||||
return vec(squassets.sign(v.x), squassets.sign(v.y), squassets.sign(v.z))
|
||||
end
|
||||
|
||||
--raises all values in a vector to a power
|
||||
function squassets.Vec3Pow(v, power)
|
||||
local power = power or 2
|
||||
return vec(math.pow(v.x, power), math.pow(v.y, power), math.pow(v.z, power))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--Debug/Display Functions
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
--displays the corners of a bounding box, good for debugging
|
||||
---@param corner1 vector coordinate of first corner
|
||||
---@param corner2 vector coordinate of second corner
|
||||
---@param color vector of the color, or a string of one of the preset colors
|
||||
function squassets.bbox(corner1, corner2, color)
|
||||
dx = corner2[1] - corner1[1]
|
||||
dy = corner2[2] - corner1[2]
|
||||
dz = corner2[3] - corner1[3]
|
||||
squassets.pointMarker(corner1, color)
|
||||
squassets.pointMarker(corner2, color)
|
||||
squassets.pointMarker(corner1 + vec(dx,0,0), color)
|
||||
squassets.pointMarker(corner1 + vec(dx,dy,0), color)
|
||||
squassets.pointMarker(corner1 + vec(dx,0,dz), color)
|
||||
squassets.pointMarker(corner1 + vec(0,dy,0), color)
|
||||
squassets.pointMarker(corner1 + vec(0,dy,dz), color)
|
||||
squassets.pointMarker(corner1 + vec(0,0,dz), color)
|
||||
end
|
||||
|
||||
|
||||
--draws a sphere
|
||||
function squassets.sphereMarker(pos, radius, color, colorCenter, quality)
|
||||
local pos = pos or vec(0, 0, 0)
|
||||
local r = radius or 1
|
||||
local quality = (quality or 1)*10
|
||||
local colorCenter = colorCenter or color
|
||||
|
||||
|
||||
-- Draw the center point
|
||||
squassets.pointMarker(pos, colorCenter)
|
||||
|
||||
-- Draw surface points
|
||||
for i = 1, quality do
|
||||
for j = 1, quality do
|
||||
local theta = (i / quality) * 2 * math.pi
|
||||
local phi = (j / quality) * math.pi
|
||||
|
||||
local x = pos.x + r * math.sin(phi) * math.cos(theta)
|
||||
local y = pos.y + r * math.sin(phi) * math.sin(theta)
|
||||
local z = pos.z + r * math.cos(phi)
|
||||
|
||||
squassets.pointMarker(vec(x, y, z), color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--draws a line between two points with particles, higher density is more particles
|
||||
function squassets.line(corner1, corner2, color, density)
|
||||
local l = (corner2 - corner1):length() -- Length of the line
|
||||
local direction = (corner2 - corner1):normalize() -- Direction vector
|
||||
local density = density or 10
|
||||
|
||||
for i = 0, l, 1/density do
|
||||
local pos = corner1 + direction * i -- Interpolate position
|
||||
squassets.pointMarker(pos, color) -- Create a particle at the interpolated position
|
||||
end
|
||||
end
|
||||
|
||||
--displays a particle at a point, good for debugging
|
||||
---@param pos vector coordinate where it will render
|
||||
---@param color vector of the color, or a string of one of the preset colors
|
||||
function squassets.pointMarker(pos, color)
|
||||
if type(color) == "string" then
|
||||
if color == "R" then color = vec(1, 0, 0)
|
||||
elseif color == "G" then color = vec(0, 1, 0)
|
||||
elseif color == "B" then color = vec(0, 0, 1)
|
||||
elseif color == "yellow" then color = vec(1, 1, 0)
|
||||
elseif color == "purple" then color = vec(1, 0, 1)
|
||||
elseif color == "cyan" then color = vec(0, 1, 1)
|
||||
elseif color == "black" then color = vec(0, 0, 0)
|
||||
else
|
||||
color = vec(1,1,1)
|
||||
end
|
||||
else
|
||||
color = color or vec(1,1,1)
|
||||
end
|
||||
particles:newParticle("minecraft:wax_on", pos):setSize(0.5):setLifetime(0):setColor(color)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--Classes
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
squassets.vanillaElement = {}
|
||||
squassets.vanillaElement.__index = squassets.vanillaElement
|
||||
function squassets.vanillaElement:new(element, strength, keepPosition)
|
||||
local self = setmetatable({}, squassets.vanillaElement)
|
||||
|
||||
-- INIT -------------------------------------------------------------------------
|
||||
self.keepPosition = keepPosition
|
||||
if keepPosition == nil then self.keepPosition = true end
|
||||
self.element = element
|
||||
self.element:setParentType("NONE")
|
||||
self.strength = strength or 1
|
||||
self.rot = vec(0,0,0)
|
||||
self.pos = vec(0,0,0)
|
||||
|
||||
-- CONTROL -------------------------------------------------------------------------
|
||||
|
||||
self.enabled = true
|
||||
function self:disable()
|
||||
self.enabled = false
|
||||
end
|
||||
function self:enable()
|
||||
self.enabled = true
|
||||
end
|
||||
function self:toggle()
|
||||
self.enabled = not self.enabled
|
||||
end
|
||||
--returns it to normal attributes
|
||||
function self:zero()
|
||||
self.element:setOffsetRot(0, 0, 0)
|
||||
self.element:setPos(0, 0, 0)
|
||||
end
|
||||
--get the current rot/pos
|
||||
function self:getPos()
|
||||
return self.pos
|
||||
end
|
||||
function self:getRot()
|
||||
return self.rot
|
||||
end
|
||||
|
||||
-- UPDATES -------------------------------------------------------------------------
|
||||
|
||||
function self:render(dt, context)
|
||||
if self.enabled then
|
||||
local rot, pos = self:getVanilla()
|
||||
self.element:setOffsetRot(rot*self.strength)
|
||||
if self.keepPosition then
|
||||
self.element:setPos(pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
squassets.BERP3D = {}
|
||||
squassets.BERP3D.__index = squassets.BERP3D
|
||||
function squassets.BERP3D:new(stiff, bounce, lowerLimit, upperLimit, initialPos, initialVel)
|
||||
local self = setmetatable({}, squassets.BERP3D)
|
||||
|
||||
self.stiff = stiff or 0.1
|
||||
self.bounce = bounce or 0.1
|
||||
self.pos = initialPos or vec(0, 0, 0)
|
||||
self.vel = initialVel or vec(0, 0, 0)
|
||||
self.acc = vec(0, 0, 0)
|
||||
self.lower = lowerLimit or {nil, nil, nil}
|
||||
self.upper = upperLimit or {nil, nil, nil}
|
||||
|
||||
--target is the target position
|
||||
--dt, or delta time, the time between now and the last update(delta from the events.update() function)
|
||||
--if you want it to have a different stiff or bounce when run input a different stiff bounce
|
||||
function self:berp(target, dt, stiff, bounce)
|
||||
local target = target or vec(0,0,0)
|
||||
local dt = dt or 1
|
||||
|
||||
for i = 1, 3 do
|
||||
--certified bouncy math
|
||||
local dif = (target[i]) - self.pos[i]
|
||||
self.acc[i] = ((dif * math.min(stiff or self.stiff, 1)) * dt) --based off of spring force F = -kx
|
||||
self.vel[i] = self.vel[i] + self.acc[i]
|
||||
|
||||
--changes the position, but adds a bouncy bit that both overshoots and decays the movement
|
||||
self.pos[i] = self.pos[i] + (dif * (1-math.min(bounce or self.bounce, 1)) + self.vel[i]) * dt
|
||||
|
||||
--limits range
|
||||
|
||||
if self.upper[i] and self.pos[i] > self.upper[i] then
|
||||
self.pos[i] = self.upper[i]
|
||||
self.vel[i] = 0
|
||||
elseif self.lower[i] and self.pos[i] < self.lower[i] then
|
||||
self.pos[i] = self.lower
|
||||
self.vel[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
--returns position so that you can immediately apply the position as it is changed.
|
||||
return self.pos
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--stiffness factor, > 0
|
||||
--bounce factor, reccomended when in range of 0-1. bigger is bouncier.
|
||||
--if you want to limit the positioning, use lowerlimit and upperlimit, or leave nil
|
||||
squassets.BERP = {}
|
||||
squassets.BERP.__index = squassets.BERP
|
||||
function squassets.BERP:new(stiff, bounce, lowerLimit, upperLimit, initialPos, initialVel)
|
||||
local self = setmetatable({}, squassets.BERP)
|
||||
|
||||
self.stiff = stiff or 0.1
|
||||
self.bounce = bounce or 0.1
|
||||
self.pos = initialPos or 0
|
||||
self.vel = initialVel or 0
|
||||
self.acc = 0
|
||||
self.lower = lowerLimit or nil
|
||||
self.upper = upperLimit or nil
|
||||
|
||||
--target is the target position
|
||||
--dt, or delta time, the time between now and the last update(delta from the events.update() function)
|
||||
--if you want it to have a different stiff or bounce when run input a different stiff bounce
|
||||
function self:berp(target, dt, stiff, bounce)
|
||||
local dt = dt or 1
|
||||
|
||||
--certified bouncy math
|
||||
local dif = (target or 10) - self.pos
|
||||
self.acc = ((dif * math.min(stiff or self.stiff, 1)) * dt) --based off of spring force F = -kx
|
||||
self.vel = self.vel + self.acc
|
||||
|
||||
--changes the position, but adds a bouncy bit that both overshoots and decays the movement
|
||||
self.pos = self.pos + (dif * (1-math.min(bounce or self.bounce, 1)) + self.vel) * dt
|
||||
|
||||
--limits range
|
||||
|
||||
if self.upper and self.pos > self.upper then
|
||||
self.pos = self.upper
|
||||
self.vel = 0
|
||||
elseif self.lower and self.pos < self.lower then
|
||||
self.pos = self.lower
|
||||
self.vel = 0
|
||||
end
|
||||
|
||||
--returns position so that you can immediately apply the position as it is changed.
|
||||
return self.pos
|
||||
end
|
||||
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
return squassets
|
370
scripts/libs/gradient.lua
Normal file
370
scripts/libs/gradient.lua
Normal file
|
@ -0,0 +1,370 @@
|
|||
-- 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,
|
||||
}
|
113
scripts/libs/oklab.lua
Normal file
113
scripts/libs/oklab.lua
Normal file
|
@ -0,0 +1,113 @@
|
|||
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
|
17
scripts/libs/utils.lua
Normal file
17
scripts/libs/utils.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
local module = {}
|
||||
|
||||
--- @param model ModelPart
|
||||
--- @param func fun(model: ModelPart)
|
||||
function module.forEachNonGroup(model, func)
|
||||
if model:getType() == "GROUP" then
|
||||
for _, child in pairs(model:getChildren()) do
|
||||
module.forEachNonGroup(child, func)
|
||||
end
|
||||
end
|
||||
|
||||
if model:getType() == "CUBE" or model:getType() == "MESH" then
|
||||
func(model)
|
||||
end
|
||||
end
|
||||
|
||||
return module
|
7
scripts/main.lua
Normal file
7
scripts/main.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
events.ENTITY_INIT:register(function ()
|
||||
require("scripts.vanilla_model")
|
||||
require("scripts.nameplate")
|
||||
require("scripts.soggy")
|
||||
require("scripts.physics")
|
||||
require("scripts.wheels.main")
|
||||
end)
|
98
scripts/nameplate.lua
Normal file
98
scripts/nameplate.lua
Normal file
|
@ -0,0 +1,98 @@
|
|||
local g = require("scripts.libs.gradient")
|
||||
|
||||
---@class ColoredText
|
||||
---@field text string
|
||||
---@field color string
|
||||
|
||||
---@param name string
|
||||
---@param gradient Gradient
|
||||
---@param count number number of gradients to be present in the span
|
||||
---@param phase_shift number shift of the gradient
|
||||
---@returns string
|
||||
local function render_gradient_name(name, gradient, count, phase_shift)
|
||||
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
|
||||
|
||||
return toJson(result)
|
||||
end
|
||||
|
||||
---@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 phase_shift = 0;
|
||||
local function render()
|
||||
if client.isPaused() then
|
||||
return
|
||||
end
|
||||
|
||||
phase_shift = phase_shift + phase_shift_rate
|
||||
|
||||
local result = render_gradient_name(name, gradient, count, phase_shift)
|
||||
for _, nameplate in pairs(nameplates) do
|
||||
nameplate:setText(result)
|
||||
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 result = render_gradient_name(name, gradient, count, phase_shift);
|
||||
for _, nameplate in pairs(nameplates) do
|
||||
nameplate:setText(result)
|
||||
end
|
||||
end
|
||||
|
||||
local name = "reidlab!"
|
||||
local gradient_count = 1
|
||||
local gradient = g.SimpleGradientBuilder.new()
|
||||
:add({ "#d87b5a", "#e0ab91" })
|
||||
:reflect(false)
|
||||
:build()
|
||||
|
||||
animate_gradient_name(name, gradient, {
|
||||
nameplate.ENTITY,
|
||||
nameplate.LIST
|
||||
}, gradient_count, 0.005)
|
||||
|
||||
static_gradient_name(name, gradient, {
|
||||
nameplate.CHAT
|
||||
}, gradient_count, 0)
|
39
scripts/physics.lua
Normal file
39
scripts/physics.lua
Normal file
|
@ -0,0 +1,39 @@
|
|||
local squapi = require("scripts.libs.SquAPI")
|
||||
|
||||
-- ear physics
|
||||
|
||||
squapi.ear:new(
|
||||
models.models.main.Head.Ears.LeftEar,
|
||||
models.models.main.Head.Ears.RightEar,
|
||||
0.2,
|
||||
nil,
|
||||
nil,
|
||||
false
|
||||
)
|
||||
|
||||
squapi.ear:new(
|
||||
models.models.main.Head.Ears.LeftEar.LeftEar2,
|
||||
models.models.main.Head.Ears.RightEar.RightEar2,
|
||||
-0.30,
|
||||
nil,
|
||||
nil,
|
||||
false
|
||||
)
|
||||
|
||||
-- tail physics
|
||||
|
||||
local tail_segments = {
|
||||
models.models.main.Body.Tail,
|
||||
models.models.main.Body.Tail2,
|
||||
models.models.main.Body.Tail3
|
||||
}
|
||||
|
||||
squapi.tail:new(
|
||||
tail_segments,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
1,
|
||||
5
|
||||
)
|
34
scripts/soggy.lua
Normal file
34
scripts/soggy.lua
Normal file
|
@ -0,0 +1,34 @@
|
|||
local utils = require("scripts.libs.utils")
|
||||
|
||||
local tw = 1 --top wetness --wet effect
|
||||
local bw = 1 --bottom wetness
|
||||
local driptime = 4 --recommended to change based on how low you have TW/BW
|
||||
|
||||
local offset = vec(0,0,0)
|
||||
local offset2 = vec(0,0,0)
|
||||
events.TICK:register(function ()
|
||||
if player:isInRain() then --makes clothes wet if in rain
|
||||
tw = tw - 0.005
|
||||
bw = bw - 0.005
|
||||
if tw <= 0.6 then tw = 0.6 end
|
||||
if bw <= 0.6 then bw = 0.6 end
|
||||
end
|
||||
offset = vec((math.random()-0.5), math.random(), (math.random()-0.5)) --random offset of particles
|
||||
offset2 = vec(math.random(-1,1),math.random(-1,1),math.random(-1,1)) -- velocity
|
||||
if player:isInWater() then bw = 0.6 end --if player is standing in water, make bottom clothes wet
|
||||
if player:isUnderwater() then tw = 0.6 end --if player is submerged in water, make top clothes wet
|
||||
if not player:isUnderwater() and tw ~= 1 and not player:isInRain() then tw = tw + 0.005 end --if not submerged in water, dry top clothes
|
||||
if not player:isInWater() and bw ~= 1 and not player:isInRain() then bw = bw + 0.005 end --if not standing in water, dry bottom clothes
|
||||
if bw >= 1 then bw = 1 end
|
||||
if tw >= 1 then tw = 1 end
|
||||
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)
|
||||
utils.forEachNonGroup(models.models.main.Body, function (part) part:setColor(tw,tw,tw) end)
|
||||
|
||||
utils.forEachNonGroup(models.models.main.LeftLeg, function (part) part:setColor(bw,bw,bw) end)
|
||||
utils.forEachNonGroup(models.models.main.RightLeg, function (part) part:setColor(bw,bw,bw) end)
|
||||
end)
|
6
scripts/vanilla_model.lua
Normal file
6
scripts/vanilla_model.lua
Normal file
|
@ -0,0 +1,6 @@
|
|||
for _, vanillaModel in pairs({
|
||||
vanilla_model.PLAYER,
|
||||
vanilla_model.ARMOR
|
||||
}) do
|
||||
vanillaModel:setVisible(false)
|
||||
end
|
18
scripts/wheels/main.lua
Normal file
18
scripts/wheels/main.lua
Normal file
|
@ -0,0 +1,18 @@
|
|||
local toggles = require("scripts.wheels.toggles")
|
||||
|
||||
local wheels = {
|
||||
toggles
|
||||
}
|
||||
|
||||
for i, v in pairs(wheels) do
|
||||
if i == 1 then action_wheel:setPage(v) end
|
||||
if #wheels ~= 1 then
|
||||
v:newAction()
|
||||
:title("to next page")
|
||||
:item("minecraft:arrow")
|
||||
:onLeftClick(function ()
|
||||
local index = (i + 1) > #wheels and 1 or (i + 1)
|
||||
action_wheel:setPage(wheels[index])
|
||||
end)
|
||||
end
|
||||
end
|
14
scripts/wheels/toggles.lua
Normal file
14
scripts/wheels/toggles.lua
Normal file
|
@ -0,0 +1,14 @@
|
|||
local toggles = action_wheel:newPage()
|
||||
|
||||
function pings.toggleArmor(state)
|
||||
vanilla_model.ARMOR:setVisible(state)
|
||||
end
|
||||
|
||||
local toggle_armor = toggles:newAction()
|
||||
:setToggled(false)
|
||||
:setOnToggle(pings.toggleArmor)
|
||||
:title("toggle armor")
|
||||
:item("red_wool")
|
||||
:toggleItem("green_wool")
|
||||
|
||||
return toggles
|
BIN
textures/extras.png
Normal file
BIN
textures/extras.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 739 B |
BIN
textures/main.png
Normal file
BIN
textures/main.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Loading…
Add table
Add a link
Reference in a new issue