maintenance
This commit is contained in:
parent
8c8ceaaf47
commit
d27c1fa39a
23 changed files with 1977 additions and 1564 deletions
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
my figura avatar!! this currently targets version **0.1.5**
|
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
|
something of note, `textures/main.png` contains the skin's base, which can be used as your default minecraft skin if you so choose
|
||||||
|
|
||||||
## development
|
## 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
|
to get proper typings, find the correct branch for your figura version [on this repository](https://github.com/GrandpaScout/FiguraRewriteVSDocs/). then, copy everything from the `src/` folder into your current working folder here. another helpful thing to have is the [figura vscode plugin](https://marketplace.visualstudio.com/items?itemName=Manuel-Underscore.figura)
|
||||||
|
|
||||||
unfortunately, 0.1.5 typings aren't done yet, so just make sure it works :)
|
unfortunately, 0.1.5 typings aren't done yet, so just make sure it works :)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"authors": [
|
"authors": [
|
||||||
"reidlab",
|
"reidlab",
|
||||||
"mrsirsquishy: Squishy's API",
|
"mrsirsquishy: Squishy's API",
|
||||||
"Agapurnis: Gradient and nameplate script",
|
"homomorphist: Gradient and nameplate script",
|
||||||
"skyevg: Oklab script",
|
"skyevg: Oklab script",
|
||||||
"adristel: Wet Clothes/Fur Script"
|
"adristel: Wet Clothes/Fur Script"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
50
scripts/libs/SquAPI_modules/FPHand.lua
Normal file
50
scripts/libs/SquAPI_modules/FPHand.lua
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---@class FPHand
|
||||||
|
local FPHand = {}
|
||||||
|
FPHand.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
--Make sure the setting for modifying first person hands is enabled in the Figura settings for this to work properly!!**
|
||||||
|
---@param element ModelPart The actual hand element to change.
|
||||||
|
---@param position? Vector3 Defaults to `vec(0,0,0)`, the position of the hover point relative to you.
|
||||||
|
---@param scale? number Defaults to `1`, this will multiply the size of the element by this size.
|
||||||
|
---@param onlyVisibleInFP? boolean Defaults to `false`, this will make the element invisible when not in first person if true.
|
||||||
|
---@return SquAPI.FPHand
|
||||||
|
function FPHand:new(element, position, scale, onlyVisibleInFP)
|
||||||
|
---@class SquAPI.FPHand
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(element, "Your First Person Hand path is incorrect")
|
||||||
|
element:setParentType("RightArm")
|
||||||
|
self.element = element
|
||||||
|
self.position = position or vec(0,0,0)
|
||||||
|
self.scale = scale or 1
|
||||||
|
self.onlyVisibleInFP = onlyVisibleInFP
|
||||||
|
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = FPHand})
|
||||||
|
table.insert(FPHand.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
---@param context Event.Render.context
|
||||||
|
function FPHand:render(_, context)
|
||||||
|
if context == "FIRST_PERSON" then
|
||||||
|
if self.onlyVisibleInFP then
|
||||||
|
self.element:setVisible(true)
|
||||||
|
end
|
||||||
|
self.element:setPos(self.position)
|
||||||
|
self.element:setScale(self.scale, self.scale, self.scale)
|
||||||
|
else
|
||||||
|
if self.onlyVisibleInFP then
|
||||||
|
self.element:setVisible(false)
|
||||||
|
end
|
||||||
|
self.element:setPos(0, 0, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return FPHand
|
||||||
373
scripts/libs/SquAPI_modules/SquAssets.lua
Normal file
373
scripts/libs/SquAPI_modules/SquAssets.lua
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
--[[--------------------------------------------------------------------------------------
|
||||||
|
____ _ _ _ _
|
||||||
|
/ ___| __ _ _ _(_)___| |__ _ _ / \ ___ ___ ___| |_ ___
|
||||||
|
\___ \ / _` | | | | / __| '_ \| | | | / _ \ / __/ __|/ _ \ __/ __|
|
||||||
|
___) | (_| | |_| | \__ \ | | | |_| | / ___ \\__ \__ \ __/ |_\__ \
|
||||||
|
|____/ \__, |\__,_|_|___/_| |_|\__, | /_/ \_\___/___/\___|\__|___/
|
||||||
|
|_| |___/
|
||||||
|
--]] --------------------------------------------------------------------------------------Standard
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- Author: Squishy
|
||||||
|
-- Discord tag: @mrsirsquishy
|
||||||
|
|
||||||
|
-- Version: 1.1.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")
|
||||||
|
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
---@class 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(player:getPos():add(0, -0.1, 0)):isSolidBlock()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- returns how fast the player moves forward, negative means backward
|
||||||
|
function squassets.forwardVel()
|
||||||
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
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
|
||||||
|
|
||||||
|
--gets a modelparts relative matrix in a model
|
||||||
|
--Courtesy of @kitcat962
|
||||||
|
function squassets.getMatrixRecursive(part)
|
||||||
|
if not part then return matrices.mat4() end
|
||||||
|
return squassets.getMatrixRecursive(part:getParent()) * part:getPositionMatrix()
|
||||||
|
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)
|
||||||
|
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 Vector3 coordinate of first corner
|
||||||
|
---@param corner2 Vector3 coordinate of second corner
|
||||||
|
---@param color Vector3|string of the color, or a string of one of the preset colors
|
||||||
|
function squassets.bbox(corner1, corner2, color)
|
||||||
|
local dx = corner2[1] - corner1[1]
|
||||||
|
local dy = corner2[2] - corner1[2]
|
||||||
|
local 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)
|
||||||
|
pos = pos or vec(0, 0, 0)
|
||||||
|
local r = radius or 1
|
||||||
|
quality = (quality or 1) * 10
|
||||||
|
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
|
||||||
|
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 Vector3 coordinate where it will render
|
||||||
|
---@param color Vector3|string 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):setScale(0.5):setLifetime(0):setColor(color)
|
||||||
|
end
|
||||||
|
|
||||||
|
--Classes
|
||||||
|
--------------------------------------------------------------------------------------
|
||||||
|
--------------------------------------------------------------------------------------
|
||||||
|
--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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)
|
||||||
|
target = target or vec(0, 0, 0)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
local _mp_getName = models.getName
|
||||||
|
local _str_lower = string.lower
|
||||||
|
local _str_find = string.find
|
||||||
|
|
||||||
|
function squassets.caseInsensitiveFind(str, pattern)
|
||||||
|
return _str_find(_str_lower(_mp_getName(str)), pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
return squassets
|
||||||
94
scripts/libs/SquAPI_modules/VanillaElement.lua
Normal file
94
scripts/libs/SquAPI_modules/VanillaElement.lua
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
---@class vanillaElement
|
||||||
|
local vanillaElement = {}
|
||||||
|
vanillaElement.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The elements model path
|
||||||
|
---@param strength? number How much the element rotates relative to
|
||||||
|
function vanillaElement:new(element, strength, keepPosition)
|
||||||
|
---@class SquAPI.vanillaElement
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
self.keepPosition = keepPosition
|
||||||
|
if keepPosition == nil then self.keepPosition = true end
|
||||||
|
assert(element, "§4The first vanilla Element's model path is incorrect.§c")
|
||||||
|
self.element = element
|
||||||
|
self.element:setParentType("NONE")
|
||||||
|
self.strength = strength or 1
|
||||||
|
|
||||||
|
self.rot = vec(0,0,0)
|
||||||
|
self.pos = vec(0,0,0)
|
||||||
|
self.frozen = false
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = vanillaElement})
|
||||||
|
--table.insert(vanillaElement.all, self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---vanillaElement enable handling
|
||||||
|
function vanillaElement:disable() self.enabled = false return self end
|
||||||
|
function vanillaElement:enable() self.enabled = true return self end
|
||||||
|
function vanillaElement:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function vanillaElement:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--freezes the element where it is
|
||||||
|
function vanillaElement:freeze()
|
||||||
|
self.frozen = true
|
||||||
|
end
|
||||||
|
function vanillaElement:unfreeze()
|
||||||
|
self.frozen = false
|
||||||
|
end
|
||||||
|
|
||||||
|
--returns it to normal attributes
|
||||||
|
function vanillaElement:zero()
|
||||||
|
self.element:setOffsetRot(0, 0, 0)
|
||||||
|
self.element:setPos(0, 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--get the current rot/pos
|
||||||
|
function vanillaElement:getPos()
|
||||||
|
return self.pos
|
||||||
|
end
|
||||||
|
function vanillaElement:getRot()
|
||||||
|
return self.rot
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function vanillaElement:render(dt, _)
|
||||||
|
|
||||||
|
if not self.frozen then
|
||||||
|
if self.enabled then
|
||||||
|
local rot, pos = self:getVanilla()
|
||||||
|
self.element:setOffsetRot(rot*self.strength)
|
||||||
|
if self.keepPosition then
|
||||||
|
self.element:setPos(pos)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.element:setOffsetRot(math.lerp(
|
||||||
|
self.element:getOffsetRot(), 0, dt
|
||||||
|
))
|
||||||
|
self.rot = math.lerp(self.rot, 0, dt)
|
||||||
|
self.pos = math.lerp(self.pos, 0, dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vanillaElement:getVanilla()
|
||||||
|
error("§4vanillaElement has been instatiated even though it is abstract, please use sublclass§c")
|
||||||
|
end
|
||||||
|
|
||||||
|
return vanillaElement
|
||||||
|
|
||||||
|
|
||||||
25
scripts/libs/SquAPI_modules/animateTexture.lua
Normal file
25
scripts/libs/SquAPI_modules/animateTexture.lua
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
---@class animateTexture
|
||||||
|
local animateTexture = {}
|
||||||
|
|
||||||
|
---Easy-use Animated Texture.
|
||||||
|
---@param element ModelPart The part of your model who's texture will be aniamted.
|
||||||
|
---@param numberOfFrames number The number of frames.
|
||||||
|
---@param framePercent number What percent width/height the uv takes up of the whole texture. For example: if there is a 100x100 texture, and the uv is 20x20, this will be .20
|
||||||
|
---@param slowFactor? number Defaults to `1`, increase this to slow down the animation.
|
||||||
|
---@param vertical? boolean Defaults to `false`, set to true if you'd like the animation frames to go down instead of right.
|
||||||
|
---@class SquAPI.AnimateTexture
|
||||||
|
function animateTexture:new(element, numberOfFrames, framePercent, slowFactor, vertical)
|
||||||
|
assert(element,
|
||||||
|
"§4Your model path for animateTexture is incorrect.§c")
|
||||||
|
|
||||||
|
vertical = vertical or false
|
||||||
|
slowFactor = slowFactor or 1
|
||||||
|
|
||||||
|
function events.tick()
|
||||||
|
local time = world.getTime()
|
||||||
|
local frameshift = math.floor(time / slowFactor) % numberOfFrames * framePercent
|
||||||
|
if vertical then element:setUV(0, frameshift) else element:setUV(frameshift, 0) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return animateTexture
|
||||||
53
scripts/libs/SquAPI_modules/arm.lua
Normal file
53
scripts/libs/SquAPI_modules/arm.lua
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
---@meta _
|
||||||
|
local vanillaElement
|
||||||
|
local assetPath = "./VanillaElement"
|
||||||
|
if pcall(require, assetPath) then vanillaElement = require(assetPath) end
|
||||||
|
assert(vanillaElement, "§4The arm module requires VanillaElement, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class Arm
|
||||||
|
local arm = {}
|
||||||
|
setmetatable(arm, {__index = vanillaElement})
|
||||||
|
arm.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The element you want to apply the movement to.
|
||||||
|
---@param strength? number Defaults to `1`, how much it rotates.
|
||||||
|
---@param isRight? boolean Defaults to `false`, if this is the right arm or not.
|
||||||
|
---@param keepPosition? boolean Defaults to `true`, if you want the element to keep it's position as well.
|
||||||
|
---@return SquAPI.Arm
|
||||||
|
function arm:new(element, strength, isRight, keepPosition)
|
||||||
|
---@class SquAPI.arm
|
||||||
|
local self = vanillaElement:new(element, strength, keepPosition)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if isRight == nil then isRight = false end
|
||||||
|
self.isRight = isRight
|
||||||
|
|
||||||
|
setmetatable(self, {__index = arm})
|
||||||
|
table.insert(arm.all, self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
--inherits functions from VanillaElement
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
---Returns the vanilla arm rotation and position vectors
|
||||||
|
---@return Vector3 #Vanilla arm rotation
|
||||||
|
function arm:getVanilla()
|
||||||
|
if self.isRight then
|
||||||
|
self.rot = vanilla_model.RIGHT_ARM:getOriginRot()
|
||||||
|
else
|
||||||
|
self.rot = vanilla_model.LEFT_ARM:getOriginRot()
|
||||||
|
end
|
||||||
|
self.pos = -vanilla_model.LEFT_ARM:getOriginPos()
|
||||||
|
return self.rot, self.pos
|
||||||
|
end
|
||||||
|
|
||||||
|
return arm
|
||||||
97
scripts/libs/SquAPI_modules/bewb.lua
Normal file
97
scripts/libs/SquAPI_modules/bewb.lua
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The bewb module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class bewb
|
||||||
|
local bewb = {}
|
||||||
|
bewb.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The bewb element that you want to affect (models.[modelname].path).
|
||||||
|
---@param bendability? number Defaults to `2`, how much the bewb should move when you move.
|
||||||
|
---@param stiff? number Defaults to `0.05`, how stiff the bewb should be.
|
||||||
|
---@param bounce? number Defaults to `0.9`, how bouncy the bewb should be.
|
||||||
|
---@param doIdle? boolean Defaults to `true`, whether or not the bewb should have an idle sway (like breathing).
|
||||||
|
---@param idleStrength? number Defaults to `4`, how much the bewb should sway when idle.
|
||||||
|
---@param idleSpeed? number Defaults to `1`, how fast the bewb should sway when idle.
|
||||||
|
---@param downLimit? number Defaults to `-10`, the lowest the bewb can rotate.
|
||||||
|
---@param upLimit? number Defaults to `25`, the highest the bewb can rotate.
|
||||||
|
function bewb:new(element, bendability, stiff, bounce, doIdle, idleStrength, idleSpeed,
|
||||||
|
downLimit, upLimit)
|
||||||
|
---@class SquAPI.bewb
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(element, "§4Your model path for bewb is incorrect.§c")
|
||||||
|
self.element = element
|
||||||
|
if doIdle == nil then doIdle = true end
|
||||||
|
self.doIdle = doIdle
|
||||||
|
self.bendability = bendability or 2
|
||||||
|
self.bewby = squassets.BERP:new(stiff or 0.05, bounce or 0.9, downLimit or -10, upLimit or 25)
|
||||||
|
self.idleStrength = idleStrength or 4
|
||||||
|
self.idleSpeed = idleSpeed or 1
|
||||||
|
self.target = 0
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
self.oldPose = "STANDING"
|
||||||
|
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = bewb})
|
||||||
|
table.insert(bewb.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---bewb enable handling
|
||||||
|
function bewb:disable() self.enabled = false return self end
|
||||||
|
function bewb:enable() self.enabled = true return self end
|
||||||
|
function bewb:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function bewb:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function bewb:tick()
|
||||||
|
if self.enabled then
|
||||||
|
local vel = squassets.forwardVel()
|
||||||
|
local yvel = squassets.verticalVel()
|
||||||
|
local worldtime = world.getTime()
|
||||||
|
|
||||||
|
if self.doIdle then
|
||||||
|
self.target = math.sin(worldtime / 8 * self.idleSpeed) * self.idleStrength
|
||||||
|
end
|
||||||
|
|
||||||
|
--physics when crouching/uncrouching
|
||||||
|
local pose = player:getPose()
|
||||||
|
if pose == "CROUCHING" and self.oldpose == "STANDING" then
|
||||||
|
self.bewby.vel = self.bewby.vel + self.bendability
|
||||||
|
elseif pose == "STANDING" and self.oldpose == "CROUCHING" then
|
||||||
|
self.bewby.vel = self.bewby.vel - self.bendability
|
||||||
|
end
|
||||||
|
self.oldpose = pose
|
||||||
|
|
||||||
|
--physics when moving
|
||||||
|
self.bewby.vel = self.bewby.vel - yvel * self.bendability
|
||||||
|
self.bewby.vel = self.bewby.vel - vel * self.bendability
|
||||||
|
else
|
||||||
|
self.target = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function bewb:render(dt, _)
|
||||||
|
self.element:setOffsetRot(self.bewby:berp(self.target, dt), 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return bewb
|
||||||
67
scripts/libs/SquAPI_modules/bounceWalk.lua
Normal file
67
scripts/libs/SquAPI_modules/bounceWalk.lua
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
---@meta _
|
||||||
|
-- local squassets
|
||||||
|
-- local assetPath = "./SquAssets"
|
||||||
|
-- if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
-- assert(squassets, "§4The bounceWalk module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class bounceWalk
|
||||||
|
local bounceWalk = {}
|
||||||
|
bounceWalk.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param model ModelPart The path to your model element.
|
||||||
|
---@param bounceMultiplier? number Defaults to `1`, this multiples how much the bounce occurs.
|
||||||
|
---@return SquAPI.BounceWalk
|
||||||
|
function bounceWalk:new(model, bounceMultiplier)
|
||||||
|
---@class SquAPI.bounceWalk
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(model, "Your model path is incorrect for bounceWalk")
|
||||||
|
self.model = model
|
||||||
|
self.bounceMultiplier = bounceMultiplier or 1
|
||||||
|
self.target = 0
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = bounceWalk})
|
||||||
|
table.insert(bounceWalk.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---bounceWalk enable handling
|
||||||
|
function bounceWalk:disable() self.enabled = false return self end
|
||||||
|
function bounceWalk:enable() self.enabled = true return self end
|
||||||
|
function bounceWalk:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function bounceWalk:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function bounceWalk:render(dt, _)
|
||||||
|
|
||||||
|
local pose = player:getPose()
|
||||||
|
|
||||||
|
if self.enabled and (pose == "STANDING" or pose == "CROUCHING") then
|
||||||
|
local leftlegrot = vanilla_model.LEFT_LEG:getOriginRot()[1]
|
||||||
|
local bounce = self.bounceMultiplier
|
||||||
|
if pose == "CROUCHING" then
|
||||||
|
bounce = bounce / 2
|
||||||
|
end
|
||||||
|
self.target = math.abs(leftlegrot) / 40 * bounce
|
||||||
|
else
|
||||||
|
self.target = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self.model:setPos(0, math.lerp(self.model:getPos()[2], self.target, dt), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return bounceWalk
|
||||||
44
scripts/libs/SquAPI_modules/crouch.lua
Normal file
44
scripts/libs/SquAPI_modules/crouch.lua
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
---*FUNCTION
|
||||||
|
---@param crouchAnim Animation The animation to play when you crouch. Make sure this animation is on "hold on last frame" and override.
|
||||||
|
---@param unCrouchAnim? Animation The animation to play when you unCrouchAnim. make sure to set to "play once" and set to override. If it's just a pose with no actual animation, than you should leave this blank or set to nil.
|
||||||
|
---@param crawl? Animation Same as crouch but for crawling.
|
||||||
|
---@param uncrawl? Animation Same as unCrouchAnim but for crawling.
|
||||||
|
---@return nil
|
||||||
|
return function(crouchAnim, unCrouchAnim, crawl, uncrawl)
|
||||||
|
local oldstate = "STANDING"
|
||||||
|
|
||||||
|
assert(crouchAnim, "§4Your crouch animation is incorrect§c")
|
||||||
|
|
||||||
|
function events.render()
|
||||||
|
local pose = player:getPose()
|
||||||
|
if pose == "SWIMMING" and not player:isInWater() then pose = "CRAWLING" end
|
||||||
|
|
||||||
|
if pose == "CROUCHING" then
|
||||||
|
if unCrouchAnim ~= nil then
|
||||||
|
unCrouchAnim:stop()
|
||||||
|
end
|
||||||
|
crouchAnim:play()
|
||||||
|
elseif oldstate == "CROUCHING" then
|
||||||
|
crouchAnim:stop()
|
||||||
|
if unCrouchAnim ~= nil then
|
||||||
|
unCrouchAnim:play()
|
||||||
|
end
|
||||||
|
elseif crawl ~= nil then
|
||||||
|
if pose == "CRAWLING" then
|
||||||
|
if uncrawl ~= nil then
|
||||||
|
uncrawl:stop()
|
||||||
|
end
|
||||||
|
crawl:play()
|
||||||
|
elseif oldstate == "CRAWLING" then
|
||||||
|
crawl:stop()
|
||||||
|
if uncrawl ~= nil then
|
||||||
|
uncrawl:play()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
oldstate = pose
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
145
scripts/libs/SquAPI_modules/ear.lua
Normal file
145
scripts/libs/SquAPI_modules/ear.lua
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The ear module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class ear
|
||||||
|
local ear = {}
|
||||||
|
ear.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param leftEar ModelPart The left ear's model path.
|
||||||
|
---@param rightEar? ModelPart The right ear's model path, if you don't have a right ear, just leave this blank or set to nil.
|
||||||
|
---@param rangeMultiplier? number Defaults to `1`, how far the ears should rotate with your head.
|
||||||
|
---@param horizontalEars? boolean Defaults to `false`, if you have elf-like ears(ears that stick out horizontally), set this to true.
|
||||||
|
---@param bendStrength? number Defaults to `2`, how much the ears should move when you move.
|
||||||
|
---@param doEarFlick? boolean Defaults to `true`, whether or not the ears should randomly flick.
|
||||||
|
---@param earFlickChance? number Defaults to `400`, how often the ears should flick in ticks, timer is random between 0 to n ticks.
|
||||||
|
---@param earStiffness? number Defaults to `0.1`, how stiff the ears should be.
|
||||||
|
---@param earBounce? number Defaults to `0.8`, how bouncy the ears should be.
|
||||||
|
---@return SquAPI.Ear
|
||||||
|
function ear:new(leftEar, rightEar, rangeMultiplier, horizontalEars, bendStrength, doEarFlick,
|
||||||
|
earFlickChance, earStiffness, earBounce)
|
||||||
|
---@class SquAPI.ear
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(leftEar, "§4The first ear's model path is incorrect.§c")
|
||||||
|
self.leftEar = leftEar
|
||||||
|
self.rightEar = rightEar
|
||||||
|
self.horizontalEars = horizontalEars
|
||||||
|
self.rangeMultiplier = rangeMultiplier or 1
|
||||||
|
if self.horizontalEars then self.rangeMultiplier = self.rangeMultiplier / 2 end
|
||||||
|
self.bendStrength = bendStrength or 2
|
||||||
|
earStiffness = earStiffness or 0.1
|
||||||
|
earBounce = earBounce or 0.8
|
||||||
|
|
||||||
|
if doEarFlick == nil then doEarFlick = true end
|
||||||
|
self.doEarFlick = doEarFlick
|
||||||
|
self.earFlickChance = earFlickChance or 400
|
||||||
|
|
||||||
|
self.eary = squassets.BERP:new(earStiffness, earBounce)
|
||||||
|
self.earx = squassets.BERP:new(earStiffness, earBounce)
|
||||||
|
self.earz = squassets.BERP:new(earStiffness, earBounce)
|
||||||
|
self.targets = { 0, 0, 0 }
|
||||||
|
self.oldpose = "STANDING"
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = ear})
|
||||||
|
table.insert(ear.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---ear enable handling
|
||||||
|
function ear:disable() self.enabled = false return self end
|
||||||
|
function ear:enable() self.enabled = true return self end
|
||||||
|
function ear:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function ear:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function ear:tick()
|
||||||
|
if self.enabled then
|
||||||
|
local vel = math.min(math.max(-0.75, squassets.forwardVel()), 0.75)
|
||||||
|
local yvel = math.min(math.max(-1.5, squassets.verticalVel()), 1.5) * 5
|
||||||
|
local svel = math.min(math.max(-0.5, squassets.sideVel()), 0.5)
|
||||||
|
local headrot = squassets.getHeadRot()
|
||||||
|
local bend = self.bendStrength
|
||||||
|
if headrot[1] < -22.5 then bend = -bend end
|
||||||
|
|
||||||
|
--gives the ears a short push when crouching/uncrouching
|
||||||
|
local pose = player:getPose()
|
||||||
|
if pose == "CROUCHING" and self.oldpose == "STANDING" then
|
||||||
|
self.eary.vel = self.eary.vel + 5 * self.bendStrength
|
||||||
|
elseif pose == "STANDING" and self.oldpose == "CROUCHING" then
|
||||||
|
self.eary.vel = self.eary.vel - 5 * self.bendStrength
|
||||||
|
end
|
||||||
|
self.oldpose = pose
|
||||||
|
|
||||||
|
--main physics
|
||||||
|
if self.horizontalEars then
|
||||||
|
local rot = 10 * bend * (yvel + vel * 10) + headrot[1] * self.rangeMultiplier
|
||||||
|
local addrot = headrot[2] * self.rangeMultiplier
|
||||||
|
self.targets[2] = rot + addrot
|
||||||
|
self.targets[3] = -rot + addrot
|
||||||
|
else
|
||||||
|
self.targets[1] = headrot[1] * self.rangeMultiplier + 2 * bend * (yvel + vel * 15)
|
||||||
|
self.targets[2] = headrot[2] * self.rangeMultiplier - svel * 100 * self.bendStrength
|
||||||
|
self.targets[3] = self.targets[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
--ear flicking
|
||||||
|
if self.doEarFlick then
|
||||||
|
if math.random(0, self.earFlickChance) == 1 then
|
||||||
|
if math.random(0, 1) == 1 then
|
||||||
|
self.earx.vel = self.earx.vel + 50
|
||||||
|
else
|
||||||
|
self.earz.vel = self.earz.vel - 50
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.leftEar:setOffsetRot(0, 0, 0)
|
||||||
|
self.rightEar:setOffsetRot(0, 0, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function ear:render(dt, _)
|
||||||
|
if self.enabled then
|
||||||
|
self.eary:berp(self.targets[1], dt)
|
||||||
|
self.earx:berp(self.targets[2], dt)
|
||||||
|
self.earz:berp(self.targets[3], dt)
|
||||||
|
|
||||||
|
local rot3 = self.earx.pos / 4
|
||||||
|
local rot3b = self.earz.pos / 4
|
||||||
|
|
||||||
|
if self.horizontalEars then
|
||||||
|
local y = self.eary.pos / 4
|
||||||
|
self.leftEar:setOffsetRot(y, self.earx.pos / 3, rot3)
|
||||||
|
if self.rightEar then
|
||||||
|
self.rightEar:setOffsetRot(y, self.earz.pos / 3, rot3b)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.leftEar:setOffsetRot(self.eary.pos, rot3, rot3)
|
||||||
|
if self.rightEar then
|
||||||
|
self.rightEar:setOffsetRot(self.eary.pos, rot3b, rot3b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ear
|
||||||
103
scripts/libs/SquAPI_modules/eye.lua
Normal file
103
scripts/libs/SquAPI_modules/eye.lua
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The eye module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class eye
|
||||||
|
local eye = {}
|
||||||
|
eye.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The eye element that is going to be moved, each eye is seperate.
|
||||||
|
---@param leftDistance? number Defaults to `0.25`, the distance from the eye to it's leftmost posistion.
|
||||||
|
---@param rightDistance? number Defaults to `1.25`, the distance from the eye to it's rightmost posistion.
|
||||||
|
---@param upDistance? number Defaults to `0.5`, the distance from the eye to it's upmost posistion.
|
||||||
|
---@param downDistance? number Defaults to `0.5`, the distance from the eye to it's downmost posistion.
|
||||||
|
---@param switchValues? boolean Defaults to `false`, this will switch from side to side movement to front back movement. this is good if the eyes are on the *side* of the head rather than the *front*.
|
||||||
|
---@return SquAPI.Eye
|
||||||
|
function eye:new(element, leftDistance, rightDistance, upDistance, downDistance, switchValues)
|
||||||
|
---@class SquAPI.eye
|
||||||
|
local self = setmetatable({}, {__index = eye})
|
||||||
|
|
||||||
|
assert(element,
|
||||||
|
"§4Your eye model path is incorrect.§c")
|
||||||
|
self.element = element
|
||||||
|
self.switchValues = switchValues or false
|
||||||
|
self.left = leftDistance or .25
|
||||||
|
self.right = rightDistance or 1.25
|
||||||
|
self.up = upDistance or 0.5
|
||||||
|
self.down = downDistance or 0.5
|
||||||
|
|
||||||
|
self.x = 0
|
||||||
|
self.y = 0
|
||||||
|
self.eyeScale = 1
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
|
||||||
|
table.insert(eye.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---For funzies if you want to change the scale of the eyes you can use self. (lerps to scale)
|
||||||
|
---@param scale number Scale multiplier
|
||||||
|
function eye:setEyeScale(scale)
|
||||||
|
self.eyeScale = scale
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---eye enable handling
|
||||||
|
function eye:disable() self.enabled = false return self end
|
||||||
|
function eye:enable() self.enabled = true return self end
|
||||||
|
function eye:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function eye:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---Resets this eye's position to its initial posistion
|
||||||
|
function eye:zero()
|
||||||
|
self.x, self.y = 0, 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function eye:tick()
|
||||||
|
if self.enabled then
|
||||||
|
local headrot = squassets.getHeadRot()
|
||||||
|
headrot[2] = math.max(math.min(50, headrot[2]), -50)
|
||||||
|
|
||||||
|
--parabolic curve so that you can control the middle position of the eyes.
|
||||||
|
self.x = -squassets.parabolagraph(-50, -self.left, 0, 0, 50, self.right, headrot[2])
|
||||||
|
self.y = squassets.parabolagraph(-90, -self.down, 0, 0, 90, self.up, headrot[1])
|
||||||
|
|
||||||
|
--prevents any eye shenanigans
|
||||||
|
self.x = math.max(math.min(self.left, self.x), -self.right)
|
||||||
|
self.y = math.max(math.min(self.up, self.y), -self.down)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function eye:render(dt, _)
|
||||||
|
local c = self.element:getPos()
|
||||||
|
if self.switchValues then
|
||||||
|
self.element:setPos(0, math.lerp(c[2], self.y, dt), math.lerp(c[3], -self.x, dt))
|
||||||
|
else
|
||||||
|
self.element:setPos(math.lerp(c[1], self.x, dt), math.lerp(c[2], self.y, dt), 0)
|
||||||
|
end
|
||||||
|
local scale = math.lerp(self.element:getOffsetScale()[1], self.eyeScale, dt)
|
||||||
|
self.element:setOffsetScale(scale, scale, scale)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return eye
|
||||||
153
scripts/libs/SquAPI_modules/hoverPoint.lua
Normal file
153
scripts/libs/SquAPI_modules/hoverPoint.lua
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The hoverPoint module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class hoverPoint
|
||||||
|
local hoverPoint = {}
|
||||||
|
hoverPoint.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The element you are moving.
|
||||||
|
---@param elementOffset? Vector3 Defaults to `vec(0,0,0)`, the position of the hover point relative to you.
|
||||||
|
---@param springStrength? number Defaults to `0.2`, how strongly the object is pulled to it's original spot.
|
||||||
|
---@param mass? number Defaults to `5`, how heavy the object is (heavier accelerate/deccelerate slower).
|
||||||
|
---@param resistance? number Defaults to `1`, how much the elements speed decays (like air resistance).
|
||||||
|
---@param rotationSpeed? number Defaults to `0.05`, how fast the element should rotate to it's normal rotation.
|
||||||
|
---@param rotateWithPlayer? boolean Defaults to `true`, wheather or not the hoverPoint should rotate with you
|
||||||
|
---@param doCollisions? boolean Defaults to `false`, whether or not the element should collide with blocks (warning: the system is janky).
|
||||||
|
function hoverPoint:new(element, elementOffset, springStrength, mass, resistance, rotationSpeed, rotateWithPlayer, doCollisions)
|
||||||
|
---@class SquAPI.hoverPoint
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
self.element = element
|
||||||
|
assert(self.element,"§4The Hover point's model path is incorrect.§c")
|
||||||
|
self.element:setParentType("WORLD")
|
||||||
|
elementOffset = elementOffset or vec(0,0,0)
|
||||||
|
self.elementOffset = elementOffset*16
|
||||||
|
self.springStrength = springStrength or 0.2
|
||||||
|
self.mass = mass or 5
|
||||||
|
self.resistance = resistance or 1
|
||||||
|
self.rotationSpeed = rotationSpeed or 0.05
|
||||||
|
self.doCollisions = doCollisions
|
||||||
|
self.rotateWithPlayer = rotateWithPlayer
|
||||||
|
if self.rotateWithPlayer == nil then self.rotateWithPlayer = true end
|
||||||
|
|
||||||
|
self.pos = vec(0,0,0)
|
||||||
|
self.vel = vec(0,0,0)
|
||||||
|
self.init = true
|
||||||
|
self.delay = 0
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = hoverPoint})
|
||||||
|
table.insert(hoverPoint.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---hoverPoint enable handling
|
||||||
|
function hoverPoint:disable() self.enabled = false return self end
|
||||||
|
function hoverPoint:enable() self.enabled = true return self end
|
||||||
|
function hoverPoint:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function hoverPoint:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function hoverPoint:reset()
|
||||||
|
local yaw
|
||||||
|
if self.rotateWithPlayer then
|
||||||
|
yaw = math.rad(player:getBodyYaw() + 180)
|
||||||
|
else
|
||||||
|
yaw = 0
|
||||||
|
end
|
||||||
|
local sin, cos = math.sin(yaw), math.cos(yaw)
|
||||||
|
local offset = vec(
|
||||||
|
cos*self.elementOffset.x - sin*self.elementOffset.z,
|
||||||
|
self.elementOffset.y,
|
||||||
|
sin*self.elementOffset.x + cos*self.elementOffset.z
|
||||||
|
)
|
||||||
|
self.pos = player:getPos() + offset/16
|
||||||
|
self.element:setPos(self.pos*16)
|
||||||
|
self.element:setOffsetRot(0,-player:getBodyYaw()+180,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function hoverPoint:tick()
|
||||||
|
if self.enabled then
|
||||||
|
local yaw
|
||||||
|
if self.rotateWithPlayer then
|
||||||
|
yaw = math.rad(player:getBodyYaw() + 180)
|
||||||
|
else
|
||||||
|
yaw = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local sin, cos = math.sin(yaw), math.cos(yaw)
|
||||||
|
local offset = vec(
|
||||||
|
cos*self.elementOffset.x - sin*self.elementOffset.z,
|
||||||
|
self.elementOffset.y,
|
||||||
|
sin*self.elementOffset.x + cos*self.elementOffset.z
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.init then
|
||||||
|
self.init = false
|
||||||
|
self.pos = player:getPos() + offset/16
|
||||||
|
self.element:setPos(self.pos*16)
|
||||||
|
self.element:setOffsetRot(0,-player:getBodyYaw()+180,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--adjusts the target based on the players rotation
|
||||||
|
local target = player:getPos() + offset/16
|
||||||
|
local pos = self.element:partToWorldMatrix():apply()
|
||||||
|
local dif = self.pos - target
|
||||||
|
|
||||||
|
local force = vec(0,0,0)
|
||||||
|
|
||||||
|
if self.delay == 0 then
|
||||||
|
--behold my very janky collision system
|
||||||
|
if self.doCollisions and world.getBlockState(pos):getCollisionShape()[1] then
|
||||||
|
local block, hitPos, side = raycast:block(pos-self.vel*2, pos)
|
||||||
|
self.pos = self.pos + (hitPos - pos)
|
||||||
|
if side == "east" or side == "west" then
|
||||||
|
self.vel.x = -self.vel.x*0.5
|
||||||
|
elseif side == "north" or side == "south" then
|
||||||
|
self.vel.z = -self.vel.z*0.5
|
||||||
|
else
|
||||||
|
self.vel.y = -self.vel.y*0.5
|
||||||
|
end
|
||||||
|
self.delay = 2
|
||||||
|
else
|
||||||
|
force = force - dif*self.springStrength --spring force
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.delay = self.delay - 1
|
||||||
|
end
|
||||||
|
force = force -self.vel*self.resistance --resistive force(based on air resistance)
|
||||||
|
|
||||||
|
self.vel = self.vel + force/self.mass
|
||||||
|
self.pos = self.pos + self.vel
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function hoverPoint:render(dt, _)
|
||||||
|
self.element:setPos(
|
||||||
|
math.lerp(self.element:getPos(), self.pos * 16, dt / 2)
|
||||||
|
)
|
||||||
|
self.element:setOffsetRot(0,
|
||||||
|
math.lerp(self.element:getOffsetRot()[2], 180-player:getBodyYaw(), dt * self.rotationSpeed), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return hoverPoint
|
||||||
53
scripts/libs/SquAPI_modules/leg.lua
Normal file
53
scripts/libs/SquAPI_modules/leg.lua
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
---@meta _
|
||||||
|
local vanillaElement
|
||||||
|
local assetPath = "./VanillaElement"
|
||||||
|
if pcall(require, assetPath) then vanillaElement = require(assetPath) end
|
||||||
|
assert(vanillaElement, "§4The leg module requires VanillaElement, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class Leg
|
||||||
|
local leg = {}
|
||||||
|
setmetatable(leg, {__index = vanillaElement})
|
||||||
|
leg.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart The element you want to apply the movement to.
|
||||||
|
---@param strength? number Defaults to `1`, how much it rotates.
|
||||||
|
---@param isRight? boolean Defaults to `false`, if this is the right leg or not.
|
||||||
|
---@param keepPosition? boolean Defaults to `true`, if you want the element to keep it's position as well.
|
||||||
|
---@return SquAPI.Leg
|
||||||
|
function leg:new(element, strength, isRight, keepPosition)
|
||||||
|
---@class SquAPI.leg
|
||||||
|
local self = vanillaElement:new(element, strength, keepPosition)
|
||||||
|
setmetatable(self, {__index = leg})
|
||||||
|
|
||||||
|
if isRight == nil then isRight = false end
|
||||||
|
self.isRight = isRight
|
||||||
|
|
||||||
|
|
||||||
|
table.insert(leg.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
--inherits functions from VanillaElement
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
---Returns the vanilla leg rotation and position vectors
|
||||||
|
---@return Vector3 #Vanilla leg rotation
|
||||||
|
---@return Vector3 #Vanilla leg position
|
||||||
|
function leg:getVanilla()
|
||||||
|
if self.isRight then
|
||||||
|
self.rot = vanilla_model.RIGHT_LEG:getOriginRot()
|
||||||
|
self.pos = vanilla_model.RIGHT_LEG:getOriginPos()
|
||||||
|
else
|
||||||
|
self.rot = vanilla_model.LEFT_LEG:getOriginRot()
|
||||||
|
self.pos = vanilla_model.LEFT_LEG:getOriginPos()
|
||||||
|
end
|
||||||
|
return self.rot, self.pos
|
||||||
|
end
|
||||||
|
|
||||||
|
return leg
|
||||||
63
scripts/libs/SquAPI_modules/randimation.lua
Normal file
63
scripts/libs/SquAPI_modules/randimation.lua
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
---@meta _
|
||||||
|
-- local squassets
|
||||||
|
-- local assetPath = "./SquAssets"
|
||||||
|
-- if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
-- assert(squassets, "§4The randimation module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class randimation
|
||||||
|
local randimation = {}
|
||||||
|
randimation.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param animation Animation The animation to play.
|
||||||
|
---@param minTime? number Defaults to `100`, the loweest time in ticks the anim will play(randomly between this and maxTime).
|
||||||
|
---@param maxTime? number Defaults to `300`, the highest time in ticks the anim will play(randomly between minTime and this).
|
||||||
|
---@param stopOnSleep? boolean Defaults to `false`, if this is for blinking set this to true so that it doesn't blink while sleeping.
|
||||||
|
---@return SquAPI.Randimation
|
||||||
|
function randimation:new(animation, minTime, maxTime, stopOnSleep)
|
||||||
|
---@class SquAPI.randimation
|
||||||
|
local self = setmetatable({}, {__index = randimation})
|
||||||
|
self.stopOnSleep = stopOnSleep
|
||||||
|
self.animation = animation
|
||||||
|
self.minTime = minTime or 100
|
||||||
|
self.maxTime = maxTime or 300
|
||||||
|
|
||||||
|
self.timer = math.random(self.minTime, self.maxTime)
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
table.insert(randimation.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---randimation enable handling
|
||||||
|
function randimation:disable() self.enabled = false return self end
|
||||||
|
function randimation:enable() self.enabled = true return self end
|
||||||
|
function randimation:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function randimation:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function randimation:tick()
|
||||||
|
if self.enabled and (not self.stopOnSleep or player:getPose() ~= "SLEEPING") and self.animation:isStopped() then
|
||||||
|
if self.timer <= 0 then
|
||||||
|
self.animation:play()
|
||||||
|
self.timer = math.random(self.minTime, self.maxTime)
|
||||||
|
else
|
||||||
|
self.timer = self.timer - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return randimation
|
||||||
196
scripts/libs/SquAPI_modules/smoothHead.lua
Normal file
196
scripts/libs/SquAPI_modules/smoothHead.lua
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
---SMOOTH HEAD
|
||||||
|
---Mimics a vanilla player head, but smoother and with some extra life. Can also do smooth Torsos and Smooth Necks!
|
||||||
|
|
||||||
|
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The smoothHead module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
---@class smoothHead
|
||||||
|
local smoothHead = {}
|
||||||
|
smoothHead.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param element ModelPart|table<ModelPart> The head element that you wish to effect. If you want a smooth neck or torso, instead of a single element, input a table of head elements(imagine it like {element1, element2, etc.}). this will apply the head rotations to each of these.
|
||||||
|
---@param strength? number|table<number> Defaults to `1`, the target rotation is multiplied by this factor. If you want a smooth neck or torso, instead of an single number, you can put in a table(imagine it like {strength1, strength2, etc.}). this will apply each strength to each respective element.(make sure it is the same length as your element table)
|
||||||
|
---@param tilt? number Defaults to `0.1`, for context the smooth head applies a slight tilt to the head as it's rotated toward the side, this controls the strength of that tilt.
|
||||||
|
---@param speed? number Defaults to `1`, how fast the head will rotate toward the target rotation.
|
||||||
|
---@param keepOriginalHeadPos? boolean|number Defaults to `true`, when true the heads position will follow the vanilla head position. For example when crouching the head will shift down to follow. If set to a number, changes which modelpart gets moved when doing actions such as crouching. this should normally be set to the neck modelpart.
|
||||||
|
---@param fixPortrait? boolean Defaults to `true`, sets whether or not the portrait should be applied if a group named "head" is found in the elements list
|
||||||
|
---@param animStraightenList? table<Animation> Defaults to `nil`, you can put in a list of animations that when played, will cause the head to straighten.
|
||||||
|
---@param straightenMultiplier? number Defaults to `0.5`, how strong the head will straighten during above
|
||||||
|
---@param straightenSpeed? number Defaults to `0.5`, how fast the head will straighten during above
|
||||||
|
---@param blendToConsiderStopped? number Defaults to `0.1`, for above, the animation will be considered stopped when it's blend is below this value.
|
||||||
|
---@return SquAPI.SmoothHead
|
||||||
|
function smoothHead:new(element, strength, tilt, speed, keepOriginalHeadPos, fixPortrait, animStraightenList, straightenMultiplier, straightenSpeed, blendToConsiderStopped)
|
||||||
|
---@class SquAPI.SmoothHead
|
||||||
|
local self = setmetatable({}, {__index = smoothHead})
|
||||||
|
|
||||||
|
--if it's a single element then converts to table
|
||||||
|
if type(element) == "ModelPart" then
|
||||||
|
assert(element, "§4Your model path for smoothHead is incorrect.§c")
|
||||||
|
element = { element }
|
||||||
|
end
|
||||||
|
|
||||||
|
--checks each element amd updates parent type to none
|
||||||
|
for i = 1, #element do
|
||||||
|
assert(element[i]:getType() == "GROUP",
|
||||||
|
"§4The head element at position " ..
|
||||||
|
i ..
|
||||||
|
" of the table is not a group. The head elements need to be groups that are nested inside one another to function properly.§c")
|
||||||
|
assert(element[i], "§4The head segment at position " .. i .. " is incorrect.§c")
|
||||||
|
element[i]:setParentType("NONE")
|
||||||
|
end
|
||||||
|
self.element = element
|
||||||
|
|
||||||
|
--if strength is a single number then converts to table by evenly dividing strength for each element
|
||||||
|
self.strength = strength or 1
|
||||||
|
if type(self.strength) == "number" then
|
||||||
|
local strengthDiv = self.strength / #element
|
||||||
|
self.strength = {}
|
||||||
|
for i = 1, #element do
|
||||||
|
self.strength[i] = strengthDiv
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.tilt = tilt or 0.1
|
||||||
|
self.speed = (speed or 1)
|
||||||
|
if keepOriginalHeadPos == nil then keepOriginalHeadPos = true end
|
||||||
|
self.keepOriginalHeadPos = keepOriginalHeadPos
|
||||||
|
self.animStraightenList = animStraightenList
|
||||||
|
self.straightenMultiplier = straightenMultiplier or 0.5
|
||||||
|
self.straightenSpeed = straightenSpeed or 0.5
|
||||||
|
self.blendToConsiderStopped = blendToConsiderStopped or 0.1
|
||||||
|
|
||||||
|
self.currentStraightenMultiplier = 1
|
||||||
|
|
||||||
|
--portrait fix by FOX
|
||||||
|
if fixPortrait == nil then fixPortrait = true end
|
||||||
|
if fixPortrait then
|
||||||
|
if type(element) == "table" then
|
||||||
|
for _, part in ipairs(element) do
|
||||||
|
if squassets.caseInsensitiveFind(part, "head") then
|
||||||
|
part:copy("_squapi-portrait"):moveTo(models):setParentType("Portrait")
|
||||||
|
:setPos(-part:getPivot())
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif type(element) == "ModelPart" and element:getType() == "GROUP" then
|
||||||
|
if squassets.caseInsensitiveFind(element, "head") then
|
||||||
|
element:copy("_squapi-portrait"):moveTo(models):setParentType("Portrait")
|
||||||
|
:setPos(-element:getPivot())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
self.rot = {}
|
||||||
|
self.rotOld = {}
|
||||||
|
for i in ipairs(self.element) do
|
||||||
|
self.rot[i] = vec(0,0,0)
|
||||||
|
self.rotOld[i] = vec(0,0,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
table.insert(smoothHead.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---Applies an offset to the heads rotation to more easily modify it. Applies as a vector.(for multisegments it will modify the target rotation)
|
||||||
|
---@param xRot number X rotation
|
||||||
|
---@param yRot number Y rotation
|
||||||
|
---@param zRot number Z rotation
|
||||||
|
function smoothHead:setOffset(xRot, yRot, zRot)
|
||||||
|
self.offset = vec(xRot, yRot, zRot)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---smoothHead enable handling
|
||||||
|
function smoothHead:disable() self.enabled = false return self end
|
||||||
|
function smoothHead:enable() self.enabled = true return self end
|
||||||
|
function smoothHead:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function smoothHead:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---Resets this smooth head's position and rotation to their initial values
|
||||||
|
function smoothHead:zero()
|
||||||
|
for _, v in ipairs(self.element) do
|
||||||
|
v:setPos(0, 0, 0)
|
||||||
|
v:setOffsetRot(0, 0, 0)
|
||||||
|
self.headRot = vec(0, 0, 0)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function smoothHead:tick()
|
||||||
|
|
||||||
|
if self.animStraightenList then
|
||||||
|
local strengthMultiplier = 1
|
||||||
|
for _, v in ipairs(self.animStraightenList) do
|
||||||
|
|
||||||
|
if v:isPlaying() and v:getBlend() > self.blendToConsiderStopped then
|
||||||
|
strengthMultiplier = self.straightenMultiplier
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
self.currentStraightenMultiplier = math.lerp(self.currentStraightenMultiplier, strengthMultiplier, self.straightenSpeed)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in ipairs(self.element) do
|
||||||
|
self.rotOld[i] = self.rot[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.enabled then
|
||||||
|
local vanillaHeadRot = squassets.getHeadRot():add(vanilla_model.HEAD:getOffsetRot())
|
||||||
|
|
||||||
|
for i in ipairs(self.element) do
|
||||||
|
self.rot[i] = math.lerp(self.rot[i], (vanillaHeadRot + vanillaHeadRot.__y * self.tilt) * self.strength[i] * self.currentStraightenMultiplier , self.speed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
---@param context Event.Render.context
|
||||||
|
function smoothHead:render(dt, context)
|
||||||
|
if self.enabled then
|
||||||
|
for i, element in ipairs(self.element) do
|
||||||
|
|
||||||
|
element:setOffsetRot(
|
||||||
|
math.lerp(self.rotOld[i], self.rot[i], dt)
|
||||||
|
)
|
||||||
|
-- Better Combat SquAPI Compatibility created by @jimmyhelp and @foxy2526 on Discord
|
||||||
|
if renderer:isFirstPerson() and context == "RENDER" then
|
||||||
|
self.element[i]:setVisible(false)
|
||||||
|
else
|
||||||
|
self.element[i]:setVisible(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.keepOriginalHeadPos then
|
||||||
|
self.element
|
||||||
|
[type(self.keepOriginalHeadPos) == "number" and self.keepOriginalHeadPos or #self.element]
|
||||||
|
:setPos(-vanilla_model.HEAD:getOriginPos())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return smoothHead
|
||||||
110
scripts/libs/SquAPI_modules/squishy.lua
Normal file
110
scripts/libs/SquAPI_modules/squishy.lua
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
---@class squishy
|
||||||
|
local squishy = {}
|
||||||
|
squishy.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param model ModelPart The model to squish
|
||||||
|
---@param dampening? number Defaults to `0.3`, how much the movement is dampened(like air resistance)
|
||||||
|
---@param spingStrength? number Defaults to `0.3`, how strongly the model will return to its original scale.
|
||||||
|
---@param crouchBounce? number Defaults to `-0.5`, when crouching/uncrouching, the model will bounce by this factor. Negative values squish down while positive up.
|
||||||
|
---@param lowerLimit? Vector3 Defaults to `vec(0.6, 0.5, 0.6)`, the minimum scale the model can be squished to.
|
||||||
|
---@param upperLimit? Vector3 Defaults to `vec(1.25, 1.5, 1.25)`, the maximum scale the model can be squished to.
|
||||||
|
function squishy:new(model, spingStrength, dampening, crouchBounce, lowerLimit, upperLimit)
|
||||||
|
---@class SquAPI.squishy
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(model, "§4The squishy model path is incorrect.§c")
|
||||||
|
self.model = model
|
||||||
|
self.springStrength = spingStrength or 0.3
|
||||||
|
self.dampening = dampening or 0.3
|
||||||
|
self.crouchBounce = crouchBounce or -0.5
|
||||||
|
self.lowerLimit = lowerLimit or vec(0.6, 0.5, 0.6)
|
||||||
|
self.upperLimit = upperLimit or vec(1.25, 1.5, 1.25)
|
||||||
|
|
||||||
|
self.scale = vec(1,1,1)
|
||||||
|
self.oldScale = vec(1,1,1)
|
||||||
|
|
||||||
|
self.scaleVel = vec(0,0,0)
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = squishy})
|
||||||
|
table.insert(squishy.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---squishy enable handling
|
||||||
|
function squishy:disable() self.enabled = false return self end
|
||||||
|
function squishy:enable() self.enabled = true return self end
|
||||||
|
function squishy:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function squishy:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
---@param force Vector3 The force vector to apply to the model
|
||||||
|
function squishy:applyForce(force)
|
||||||
|
self.scaleVel = vec(
|
||||||
|
self.scaleVel.x + force.x - force.y/2 - force.z/2,
|
||||||
|
self.scaleVel.y + force.y - force.x/2 - force.z/2,
|
||||||
|
self.scaleVel.z + force.z - force.x/2 - force.y/2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
squishy.oldPose = "STANDING"
|
||||||
|
function squishy:tick()
|
||||||
|
self.oldScale = self.scale
|
||||||
|
|
||||||
|
local verticalVel = -player:getVelocity()[2]
|
||||||
|
|
||||||
|
local pose = player:getPose()
|
||||||
|
--avoid wonky when flying/swimming
|
||||||
|
if pose == "FALL_FLYING" or pose == "SWIMMING" or player:riptideSpinning() then
|
||||||
|
verticalVel = 0
|
||||||
|
end
|
||||||
|
--bounce when crouching/uncrouching
|
||||||
|
if pose == "CROUCHING" and self.oldPose ~= "CROUCHING" then
|
||||||
|
self:applyForce(vec(0, self.crouchBounce, 0))
|
||||||
|
elseif self.oldPose == "CROUCHING" and pose ~= "CROUCHING" then
|
||||||
|
self:applyForce(vec(0, -self.crouchBounce, 0))
|
||||||
|
end
|
||||||
|
self.oldPose = pose
|
||||||
|
|
||||||
|
--pulls back to original scale
|
||||||
|
local dif = vec(1,1,1) - self.scale
|
||||||
|
local springForce = dif*self.springStrength
|
||||||
|
|
||||||
|
--resistive force/dampening
|
||||||
|
local dampeningForce = self.scaleVel * self.dampening
|
||||||
|
|
||||||
|
local netForce = vec(0, verticalVel/4, 0) + springForce - dampeningForce
|
||||||
|
self:applyForce(netForce)
|
||||||
|
|
||||||
|
self.scale = self.scale + self.scaleVel
|
||||||
|
|
||||||
|
--safety net to make sure it doesn't get stuck at a non-original scale, which can happen if the forces cancel out
|
||||||
|
self.scale = self.scale + dif/5
|
||||||
|
|
||||||
|
--limiting scale
|
||||||
|
self.scale = vec(
|
||||||
|
math.clamp(self.scale.x, 0.6, 1.25),
|
||||||
|
math.clamp(self.scale.y, 0.5, 1.5),
|
||||||
|
math.clamp(self.scale.z, 0.6, 1.25)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function squishy:render(dt, _)
|
||||||
|
self.model:setScale(math.lerp(self.oldScale, self.scale, dt))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return squishy
|
||||||
160
scripts/libs/SquAPI_modules/tail.lua
Normal file
160
scripts/libs/SquAPI_modules/tail.lua
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The tail module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class tail
|
||||||
|
local tail = {}
|
||||||
|
tail.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param tailSegmentList table<ModelPart> The list of each individual tail segment of your tail.
|
||||||
|
---@param idleXMovement? number Defaults to `15`, how much the tail should sway side to side.
|
||||||
|
---@param idleYMovement? number Defaults to `5`, how much the tail should sway up and down.
|
||||||
|
---@param idleXSpeed? number Defaults to `1.2`, how fast the tail should sway side to side.
|
||||||
|
---@param idleYSpeed? number Defaults to `2`, how fast the tail should sway up and down.
|
||||||
|
---@param bendStrength? number Defaults to `2`, how strongly the tail moves when you move.
|
||||||
|
---@param velocityPush? number Defaults to `0`, this will cause the tail to bend when you move forward/backward, good if your tail is bent downward or upward.
|
||||||
|
---@param initialMovementOffset? number Defaults to `0`, this will offset the tails initial sway, this is good for when you have multiple tails and you want to desync them.
|
||||||
|
---@param offsetBetweenSegments? number Defaults to `1`, how much each tail segment should be offset from the previous one.
|
||||||
|
---@param stiffness? number Defaults to `0.005`, how stiff the tail should be.
|
||||||
|
---@param bounce? number Defaults to `0.9`, how bouncy the tail should be.
|
||||||
|
---@param flyingOffset? number Defaults to `90`, when flying, riptiding, or swimming, it may look strange to have the tail stick out, so instead it will rotate to this value(so use this to flatten your tail during these movements).
|
||||||
|
---@param downLimit? number Defaults to `-90`, the lowest each tail segment can rotate.
|
||||||
|
---@param upLimit? number Defaults to `45`, the highest each tail segment can rotate.
|
||||||
|
---@return SquAPI.Tail
|
||||||
|
function tail:new(tailSegmentList, idleXMovement, idleYMovement, idleXSpeed, idleYSpeed,
|
||||||
|
bendStrength, velocityPush, initialMovementOffset, offsetBetweenSegments,
|
||||||
|
stiffness, bounce, flyingOffset, downLimit, upLimit)
|
||||||
|
---@class SquAPI.tail
|
||||||
|
local self = setmetatable({}, {__index = tail})
|
||||||
|
|
||||||
|
--error checker
|
||||||
|
self.tailSegmentList = tailSegmentList
|
||||||
|
if type(self.tailSegmentList) == "ModelPart" then
|
||||||
|
self.tailSegmentList = {self.tailSegmentList}
|
||||||
|
end
|
||||||
|
assert(type(self.tailSegmentList) == "table",
|
||||||
|
"your tailSegmentList table seems to to be incorrect")
|
||||||
|
|
||||||
|
self.berps = {}
|
||||||
|
self.targets = {}
|
||||||
|
self.stiffness = stiffness or .005
|
||||||
|
self.bounce = bounce or .9
|
||||||
|
|
||||||
|
if type(self.tailSegmentList[2]) == "number" then --ah I see you stumbled across my custom tail list creator, if you curious ask me. tail must be >= 3 segments. Naming: tail, tailseg, tailseg2, tailseg3..., tailtip
|
||||||
|
local range = self.tailSegmentList[2]
|
||||||
|
local str = ""
|
||||||
|
if self.tailSegmentList[3] then
|
||||||
|
str = self.tailSegmentList[3]
|
||||||
|
end
|
||||||
|
|
||||||
|
self.tailSegmentList[2] = self.tailSegmentList[1][str .. "tailseg"]
|
||||||
|
for i = 2, range - 2 do
|
||||||
|
self.tailSegmentList[i + 1] = self.tailSegmentList[i][str .. "tailseg" .. i]
|
||||||
|
end
|
||||||
|
self.tailSegmentList[range] = self.tailSegmentList[range - 1][str .. "tailtip"]
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #self.tailSegmentList do
|
||||||
|
assert(self.tailSegmentList[i]:getType() == "GROUP",
|
||||||
|
"§4The tail segment at position "..i.." of the table is not a group. The tail segments need to be groups that are nested inside the previous segment.§c")
|
||||||
|
self.berps[i] = {squassets.BERP:new(self.stiffness, self.bounce), squassets.BERP:new(self.stiffness, self.bounce, self.downLimit, self.upLimit)}
|
||||||
|
self.targets[i] = {0, 0}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.downLimit = downLimit or -90
|
||||||
|
self.upLimit = upLimit or 45
|
||||||
|
self.tailSegmentList = tailSegmentList
|
||||||
|
self.idleXMovement = idleXMovement or 15
|
||||||
|
self.idleYMovement = idleYMovement or 5
|
||||||
|
self.idleXSpeed = idleXSpeed or 1.2
|
||||||
|
self.idleYSpeed = idleYSpeed or 2
|
||||||
|
self.bendStrength = bendStrength or 2
|
||||||
|
self.velocityPush = velocityPush or 0
|
||||||
|
self.initialMovementOffset = initialMovementOffset or 0
|
||||||
|
self.flyingOffset = flyingOffset or 90
|
||||||
|
self.offsetBetweenSegments = offsetBetweenSegments or 1
|
||||||
|
|
||||||
|
self.currentBodyRot = 0
|
||||||
|
self.oldBodyRot = 0
|
||||||
|
self.bodyRotSpeed = 0
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
table.insert(tail.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---tail enable handling
|
||||||
|
function tail:disable() self.enabled = false return self end
|
||||||
|
function tail:enable() self.enabled = true return self end
|
||||||
|
function tail:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function tail:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function tail:zero()
|
||||||
|
for _, v in pairs(self.tailSegmentList) do
|
||||||
|
v:setOffsetRot(0, 0, 0)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function tail:tick()
|
||||||
|
if self.enabled then
|
||||||
|
self.oldBodyRot = self.currentBodyRot
|
||||||
|
self.currentBodyRot = player:getBodyYaw()
|
||||||
|
self.bodyRotSpeed = math.max(math.min(self.currentBodyRot-self.oldBodyRot, 20), -20)
|
||||||
|
|
||||||
|
local time = world.getTime()
|
||||||
|
local vel = squassets.forwardVel()
|
||||||
|
local yvel = squassets.verticalVel()
|
||||||
|
local svel = squassets.sideVel()
|
||||||
|
local bendStrength = self.bendStrength/(math.abs((yvel*30))+vel*30 + 1)
|
||||||
|
local pose = player:getPose()
|
||||||
|
|
||||||
|
for i = 1, #self.tailSegmentList do
|
||||||
|
self.targets[i][1] = math.sin((time * self.idleXSpeed)/10 - (i * self.offsetBetweenSegments)) * self.idleXMovement
|
||||||
|
self.targets[i][2] = math.sin((time * self.idleYSpeed)/10 - (i * self.offsetBetweenSegments) + self.initialMovementOffset) * self.idleYMovement
|
||||||
|
|
||||||
|
self.targets[i][1] = self.targets[i][1] + self.bodyRotSpeed*self.bendStrength + svel*self.bendStrength*40
|
||||||
|
self.targets[i][2] = self.targets[i][2] + yvel * 15 * self.bendStrength - vel*self.bendStrength*15*self.velocityPush
|
||||||
|
|
||||||
|
if i == 1 then
|
||||||
|
if pose == "FALL_FLYING" or pose == "SWIMMING" or player:riptideSpinning() then
|
||||||
|
self.targets[i][2] = self.flyingOffset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---@param dt number Tick delta
|
||||||
|
function tail:render(dt, _)
|
||||||
|
if self.enabled then
|
||||||
|
local pose = player:getPose()
|
||||||
|
if pose ~= "SLEEPING" then
|
||||||
|
for i, tail in ipairs(self.tailSegmentList) do
|
||||||
|
tail:setOffsetRot(
|
||||||
|
self.berps[i][2]:berp(self.targets[i][2], dt),
|
||||||
|
self.berps[i][1]:berp(self.targets[i][1], dt),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return tail
|
||||||
84
scripts/libs/SquAPI_modules/taur.lua
Normal file
84
scripts/libs/SquAPI_modules/taur.lua
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
---@meta _
|
||||||
|
local squassets
|
||||||
|
local assetPath = "./SquAssets"
|
||||||
|
if pcall(require, assetPath) then squassets = require(assetPath) end
|
||||||
|
assert(squassets, "§4The taur module requires SquAssets, which was not found!§c")
|
||||||
|
|
||||||
|
|
||||||
|
---@class taur
|
||||||
|
local taur = {}
|
||||||
|
taur.all = {}
|
||||||
|
|
||||||
|
---*CONSTRUCTOR
|
||||||
|
---@param taurBody ModelPart The group of the body that contains all parts of the actual centaur part of the body, pivot should be placed near the connection between body and taurs body.
|
||||||
|
---@param frontLegs? ModelPart The group that contains both front legs.
|
||||||
|
---@param backLegs? ModelPart The group that contains both back legs.
|
||||||
|
---@return SquAPI.Taur
|
||||||
|
function taur:new(taurBody, frontLegs, backLegs)
|
||||||
|
---@class SquAPI.taur
|
||||||
|
local self = {}
|
||||||
|
|
||||||
|
assert(taurBody, "§4Your model path for the body in taurPhysics is incorrect.§c")
|
||||||
|
self.taurBody = taurBody
|
||||||
|
self.frontLegs = frontLegs
|
||||||
|
self.backLegs = backLegs
|
||||||
|
self.taur = squassets.BERP:new(0.01, 0.5)
|
||||||
|
self.target = 0
|
||||||
|
|
||||||
|
self.enabled = true
|
||||||
|
|
||||||
|
self = setmetatable(self, {__index = taur})
|
||||||
|
table.insert(taur.all, self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--*CONTROL FUNCTIONS
|
||||||
|
|
||||||
|
---taur enable handling
|
||||||
|
function taur:disable() self.enabled = false return self end
|
||||||
|
function taur:enable() self.enabled = true return self end
|
||||||
|
function taur:toggle() self.enabled = not self.enabled return self end
|
||||||
|
|
||||||
|
---@param bool boolean
|
||||||
|
function taur:setEnabled(bool)
|
||||||
|
self.enabled = bool or false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--*UPDATE FUNCTIONS
|
||||||
|
|
||||||
|
function taur:tick()
|
||||||
|
if self.enabled then
|
||||||
|
self.target = math.min(math.max(-30, squassets.verticalVel() * 40), 45)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function taur:render(dt, _)
|
||||||
|
if self.enabled then
|
||||||
|
self.taur:berp(self.target, dt / 2)
|
||||||
|
local pose = player:getPose()
|
||||||
|
|
||||||
|
if pose == "FALL_FLYING" or pose == "SWIMMING" or (player:isClimbing() and not player:isOnGround()) or player:riptideSpinning() then
|
||||||
|
self.taurBody:setRot(80, 0, 0)
|
||||||
|
if self.backLegs then
|
||||||
|
self.backLegs:setRot(-50, 0, 0)
|
||||||
|
end
|
||||||
|
if self.frontLegs then
|
||||||
|
self.frontLegs:setRot(-50, 0, 0)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.taurBody:setRot(self.taur.pos, 0, 0)
|
||||||
|
if self.backLegs then
|
||||||
|
self.backLegs:setRot(self.taur.pos * 3, 0, 0)
|
||||||
|
end
|
||||||
|
if self.frontLegs then
|
||||||
|
self.frontLegs:setRot(-self.taur.pos * 3, 0, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return taur
|
||||||
|
|
@ -1,428 +0,0 @@
|
||||||
--[[--------------------------------------------------------------------------------------
|
|
||||||
____ _ _ _ _
|
|
||||||
/ ___| __ _ _ _(_)___| |__ _ _ / \ ___ ___ ___| |_ ___
|
|
||||||
\___ \ / _` | | | | / __| '_ \| | | | / _ \ / __/ __|/ _ \ __/ __|
|
|
||||||
___) | (_| | |_| | \__ \ | | | |_| | / ___ \\__ \__ \ __/ |_\__ \
|
|
||||||
|____/ \__, |\__,_|_|___/_| |_|\__, | /_/ \_\___/___/\___|\__|___/
|
|
||||||
|_| |___/
|
|
||||||
--]]--------------------------------------------------------------------------------------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
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
events.ENTITY_INIT:register(function ()
|
events.ENTITY_INIT:register(function ()
|
||||||
require("scripts.model")
|
require("scripts.model")
|
||||||
require("scripts.nameplate")
|
require("scripts.nameplate")
|
||||||
require("scripts.soggy")
|
|
||||||
require("scripts.physics")
|
require("scripts.physics")
|
||||||
|
require("scripts.soggy")
|
||||||
require("scripts.wheels.main")
|
require("scripts.wheels.main")
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,35 @@
|
||||||
local utils = require("scripts.libs.utils")
|
local utils = require("scripts.libs.utils")
|
||||||
|
|
||||||
local tw = 1 --top wetness --wet effect
|
local tw = 1 -- top wetness
|
||||||
local bw = 1 --bottom wetness
|
local bw = 1 -- bottom wetness
|
||||||
local driptime = 4 --recommended to change based on how low you have TW/BW
|
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)
|
||||||
|
|
||||||
local offset = vec(0,0,0)
|
|
||||||
local offset2 = vec(0,0,0)
|
|
||||||
events.TICK:register(function ()
|
events.TICK:register(function ()
|
||||||
if player:isInRain() then --makes clothes wet if in rain
|
if player:isInRain() then
|
||||||
tw = tw - 0.005
|
tw = tw - 0.005
|
||||||
bw = bw - 0.005
|
bw = bw - 0.005
|
||||||
if tw <= 0.6 then tw = 0.6 end
|
if tw <= 0.6 then tw = 0.6 end
|
||||||
if bw <= 0.6 then bw = 0.6 end
|
if bw <= 0.6 then bw = 0.6 end
|
||||||
end
|
end
|
||||||
offset = vec((math.random()-0.5), math.random(), (math.random()-0.5)) --random offset of particles
|
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
|
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:isInWater() then bw = 0.6 end
|
||||||
if player:isUnderwater() then tw = 0.6 end --if player is submerged in water, make top clothes wet
|
if player:isUnderwater() then tw = 0.6 end
|
||||||
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:isUnderwater() and tw ~= 1 and not player:isInRain() then tw = tw + 0.005 end
|
||||||
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 not player:isInWater() and bw ~= 1 and not player:isInRain() then bw = bw + 0.005 end
|
||||||
if bw >= 1 then bw = 1 end
|
if bw >= 1 then bw = 1 end
|
||||||
if tw >= 1 then tw = 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 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
|
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.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.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.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.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.LeftLeg, function (part) part:setColor(bw, bw,bw) end)
|
||||||
utils.forEachNonGroup(models.models.main.RightLeg, function (part) part:setColor(bw,bw,bw) end)
|
utils.forEachNonGroup(models.models.main.RightLeg, function (part) part:setColor(bw, bw, bw) end)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue