Compare commits

..

22 Commits

Author SHA1 Message Date
1a0ec08ce2
Unhide skin layer in BlockBench 2023-06-29 01:08:34 -04:00
784a1aaea1
Update skin
- Add jacket layer
2023-06-29 01:00:48 -04:00
e415653865
Add script to export avatar as .zip 2023-06-27 00:13:08 -04:00
8428d6e027
Update nulllib 2023-06-26 23:40:20 -04:00
400318bb63
Add start of sharedconfig callbacks 2023-06-26 22:07:41 -04:00
79cd101467
Refresh PartsManager during init 2023-06-26 15:48:30 -04:00
7e1f68e6f8
Refactor deprecated code 2023-06-26 15:48:15 -04:00
b3b518dc42
Update nulllib 2023-06-26 15:45:32 -04:00
58881b06db
Update nulllib 2023-06-26 14:36:16 -04:00
bc4318bd6b
Reposition and fix pivot for revolver 2023-06-25 12:34:52 -04:00
57aaaf1baa
Switch to new sharedstate/sharedconfig libraries 2023-06-25 02:12:13 -04:00
e1617fb880
Update nulllib 2023-06-25 02:06:49 -04:00
fa85946421
Update nulllib 2023-06-22 20:39:50 -04:00
889b09d67c
Add Lua LSP docs for Figura 2023-06-22 13:48:53 -04:00
07456d152e
Update nulllib 2023-06-22 02:45:39 -04:00
d76f9e0465
Enable emissive textures 2023-06-22 02:06:35 -04:00
a9971097b2
Port to rewrite in basic working state 2023-06-22 02:01:10 -04:00
15308b2b2b
Add imports for libraries 2023-06-22 01:20:01 -04:00
d942f55585
Remove code contained in libraries 2023-06-22 01:15:13 -04:00
187b1f5a9e
Add .luarc 2023-06-22 01:14:48 -04:00
54743a90b3
Add TODO and avatar.json 2023-06-22 01:14:37 -04:00
be93aaf4ed
Add nulllib submodule 2023-06-21 23:53:07 -04:00
12 changed files with 685 additions and 561 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "nulllib"]
path = nulllib
url = https://gitea.protogen.io/nullbite/nulllib-lua
[submodule ".vim/figuradocs"]
path = .vim/figuradocs
url = https://github.com/GrandpaScout/FiguraRewriteVSDocs

520
.luarc.json Normal file
View File

@ -0,0 +1,520 @@
{
/*==============================================================================================*\
|* WORKSPACE: Controls workspace behavior. *|
\*==============================================================================================*/
"workspace": {
"library": [
"nulllib",
".vim/figuradocs/src/.vscode/docs",
".vscode/docs",
"../../avatars/.vscode/docs",
"../../../avatars/.vscode/docs",
"../../../../avatars/.vscode/docs",
"../../../../../avatars/.vscode/docs",
"../../../../../../avatars/.vscode/docs"
],
// Figura does not use third-party libraries.
"checkThirdParty": false,
"userThirdParty": [],
// Modify ignored files
"ignoreDir": [".**"], // Directories starting with `.` are ignored.
"useGitIgnore": true, // Ignore the same files as Git.
"ignoreSubmodules": true, // Git submodules are ignored.
// Include all file schemes
"supportScheme": ["file", "untitled", "git"],
// Avoid excessive memory usage.
"maxPreload": 5000,
"preloadFileSize": 500
},
/*==============================================================================================*\
|* RUNTIME: Controls the emulated Lua runtime. *|
\*==============================================================================================*/
"runtime": {
// Figura uses Lua 5.2
"version": "Lua 5.2",
// Change built-in features of Lua to fit Figura's implementation.
"builtin": {
"basic": "disable", // Figura reworks and removes many of the functions in this library.
"bit": "disable", // `bit` is LuaJIT only.
"bit32": "enable", //
"builtin": "enable", // TODO: Turn this off if the Lua server ever supports ops on types.
"coroutine": "disable", // `coroutine` does not exist in Figura.
"debug": "disable", // `debug` does not exist in Figura.
"ffi": "disable", // `ffi` is LuaJIT only.
"io": "disable", // `io` does not exist in Figura.
"jit": "disable", // `jit` is, you guessed it, LuaJIT only.
"math": "enable", //
"os": "disable", // `os` does not exist in Figura.
"package": "disable", // `package` does not exist in Figura. `require` is reworked.
"string": "enable", //
"table": "enable", //
"table.clear": "disable", // `table.clear` is LuaJIT only.
"table.new": "disable", // `table.new` is LuaJIT only.
"utf8": "disable" // `utf8` is Lua 5.3+ only.
},
// Figura's require only allows direct paths.
"path": ["?.lua"],
"pathStrict": false,
// Internal crap.
"fileEncoding": "utf8",
"meta": "${version} ${language} ${encoding}",
// Figura does not use non-standard features.
"nonstandardSymbol": [],
"plugin": "",
"pluginArgs": [],
"special": {},
"unicodeName": false
},
/*==============================================================================================*\
|* COMPLETION: Controls auto-completion and suggestion behavior. *|
\*==============================================================================================*/
"completion": {
// Enable completion changes.
"enable": true,
// Modify visible snippets.
"keywordSnippet": "Both", // Show snippets for both keywords and control blocks.
"callSnippet": "Disable", // Disable seperate call snippets as they clutter suggestions.
// Show parameter names on function suggestions.
"showParams": true,
// Automatically try to `require` words that look like file names.
"autoRequire": true,
// Shows context for hovered function calls by showing the first few lines of the function.
// You can disable this by setting this to 0. Enable by setting to the amount of visbile lines.
"displayContext": 0,
// The character used as the postfix operator for suggestions.
"postfix": "@",
// The character used as the path seperator of requires.
"requireSeparator": ".",
// Disable word suggestions as they are mostly useless.
"showWord": "Disable",
"workspaceWord": false
},
/*==============================================================================================*\
|* DIAGNOSTICS: Controls active diagnostics and the behavior of those diagnostics. *|
\*==============================================================================================*/
"diagnostics": {
// Enable diagnostics.
"enable": true,
// Slow down diagnostics on files that are not currently active to save resources.
"workspaceDelay": 5000,
"workspaceRate": 25,
// Sets the severity of diagnostics to better fit Figura's environment.
// Removing diagnostics from this list does not disable them, use the "disable" list below if
// you wish to do that instead.
"severity": {
// Ambiguity
"ambiguity-1": "Warning", // Requires ambiguous operators to be ordered with `(...)`.
"count-down-loop": "Error", // Don't allow for loops that could be infinite.
"different-requires": "Error", // Requires in Figura are very specific.
"newfield-call": "Warning", // If parentheses are found at the start of a line, it will...
"newline-call": "Warning", // ...be treated as a function call for the variable before it.
// Await
"await-in-sync": "Error", // Async functions can only be called in async contexts.
"not-yieldable": "Error", // `coroutine.yield` can only be used in coroutines.
// Code Style
"codestyle-check": "Information", // Checks code style for consistency.
"spell-check": "Hint", // Check spelling.
// Duplicate
"duplicate-index": "Warning", // Duplicate table indexes will conflict.
"duplicate-set-field": "Warning", // Duplicate class indexes will conflict.
// Global
"global-in-nil-env": "Error", // Attempting to make a global with a missing _ENV fails.
"lowercase-global": "Information", // This is likely caused by not making a variable local.
"undefined-env-child": "Warning", // Basically `undefined-global`, but for new _ENVs.
"undefined-global": "Warning", // Detects attempt to use undefined global variables.
// Luadoc
"circle-doc-class": "Error", // Circular references cause a class to be useless.
"doc-field-no-class": "Error", // `@field`s need a `@class` to work.
"duplicate-doc-alias": "Warning", // Duplicate `@alias` names may conflict.
"duplicate-doc-field": "Warning", // Duplicate `@field` names in a class may conflict.
"duplicate-doc-param": "Warning", // Duplicate `@param` names in a function may conflict.
"undefined-doc-class": "Warning", // Duplicate `@class` names may conflict.
"undefined-doc-name": "Error", // Undefined type annotations act like the `table` type.
"undefined-doc-param": "Warning", // `@param`s need a matching parameter to annotate.
"unknown-cast-variable": "Error", // Undefined variables cannot be typecasted.
"unknown-diag-code": "Information", // `@diagnostic` only accepts valid diagnostic codes.
"unknown-operator": "Error", // Lua cannot have new `@operator`s added to it.
// Redefined
"redefined-local": "Warning", // It is pointless to redefine a local variable.
// Strict
"close-non-object": "Error", // Non-object values cannot be closed.
"deprecated": "Warning", // Deprecated features are not meant to be used in code.
"discard-returns": "Warning", // Some functions cannot discard their return values.
// Strong
"no-unknown": "Warning", // If a varaible's type cannot be inferred, it cannot safely be used.
// Type Check
"assign-type-mismatch": "Warning", // Types of both values in an assignment must match.
"cast-local-type": "Warning", // Casts can only be to matching types.
"cast-type-mismatch": "Warning", // Casts can only be to matching types.
"need-check-nil": "Warning", // Require a check if a value is *possibly* nil.
"param-type-mismatch": "Warning", // Require call arguments to match function parameters.
"return-type-mismatch": "Warning", // Require functions to return the correct type of value.
"undefined-field": "Warning", // Undefined fields have no known type and are not safe.
// Unbalanced
"missing-parameter": "Error", // Functions must receive at least the minimum arguments.
"missing-return": "Warning", // Functions with a `@return` require a return statement.
"missing-return-value": "Warning", // Functions with a `@return` require a return value.
"redundant-parameter": "Warning", // Functions must receive at most the maximum arguments.
"redundant-return-value": "Warning", // Functions must return at most the maximum returns.
"redundant-value": "Warning", // Unbalanced assignments can cause useless values.
"unbalanced-assignments": "Warning", // Unbalanced assignments can cause undefined variables.
// Unused
"code-after-break": "Information", // Code after a break statement will never run.
"empty-block": "Hint", // Empty blocks create clutter.
"redundant-return": "Warning", // Empty returns at the end of a function are redundant.
"trailing-space": "Hint", // Disallows trailing spaces in code.
"unreachable-code": "Information", // If code is unreachable, it is useless.
"unused-function": "Hint", // Unused functions cause clutter.
"unused-label": "Hint", // Unused labels cause clutter.
"unused-local": "Hint", // Unused variables cause clutter.
"unused-vararg": "Hint" // Unused varargs cause clutter.
},
"neededFileStatus": {
"codestyle-check": "Any"
},
// Diagnostics that are deemed useless in Figura's environment.
// Remove the `//;` before an option to disable it. Re-add it to enable it.
"disable": [
// Ambiguity
//;"ambiguity-1",
//;"count-down-loop",
//;"different-requires",
//;"newfield-call",
//;"newline-call",
// Await
"await-in-sync", // Nothing in Figura is true async.
"not-yieldable", // `coroutine.yield` does not exist in Figura.
// Code Style
"codestyle-check", // Code style checking is completely subjective.
"spell-check", // Who needs spell check? Lmao.
// Duplicate
//;"duplicate-index",
//;"duplicate-set-field",
// Global
//;"global-in-nil-env",
//;"lowercase-global",
//;"undefined-env-child",
//;"undefined-global",
// Luadoc
//;"circle-doc-class",
//;"doc-field-no-class",
//;"duplicate-doc-alias",
//;"duplicate-doc-field",
//;"duplicate-doc-param",
//;"undefined-doc-class",
//;"undefined-doc-name",
//;"undefined-doc-param",
//;"unknown-cast-variable",
//;"unknown-diag-code",
//;"unknown-operator",
// Redefined
//;"redefined-local",
// Strict
"close-non-object", // <close> is Lua 5.4+ only.
//;"deprecated",
//;"discard-returns",
// Strong
"no-unknown", // Too strict for Figura scripters.
// Type Check
//;"assign-type-mismatch",
"cast-local-type", // Allow casting as a way of forcibly changing a variable's type.
"cast-type-mismatch", // Allow casting as a way of forcibly changing a variable's type.
"need-check-nil", // Too strict for Figura scripters.
//;"param-type-mismatch",
//;"return-type-mismatch",
//;"undefined-field",
// Unbalanced
//;"missing-parameter",
//;"missing-return",
//;"missing-return-value",
//;"redundant-parameter",
//;"redundant-return-value",
//;"redundant-value",
//;"unbalanced-assignments",
// Unused
//;"code-after-break",
//;"empty-block",
//;"redundant-return",
"trailing-space" // Personal preference.
//;"unreachable-code",
//;"unused-function",
//;"unused-label",
//;"unused-local",
//;"unused-vararg"
],
// Group severity is not used by the docs.
"groupSeverity": {
"ambiguity": "Fallback",
"await": "Fallback",
"codestyle": "Fallback",
"duplicate": "Fallback",
"global": "Fallback",
"luadoc": "Fallback",
"redefined": "Fallback",
"strict": "Fallback",
"strong": "Fallback",
"type-check": "Fallback",
"unbalanced": "Fallback",
"unused": "Fallback"
},
"groupFileStatus": {
"ambiguity": "Fallback",
"await": "Fallback",
"codestyle": "Fallback",
"duplicate": "Fallback",
"global": "Fallback",
"luadoc": "Fallback",
"redefined": "Fallback",
"strict": "Fallback",
"strong": "Fallback",
"type-check": "Fallback",
"unbalanced": "Fallback",
"unused": "Fallback"
},
// Allow all schemes.
"disableScheme": [],
// Don't run diagnostics on library files and ignored files.
"ignoredFiles": "Opened",
"libraryFiles": "Opened",
// The docs handle global variables themselves.
"globals": [],
// All local variables must be checked (except for the empty variable "_")
"unusedLocalExclude": []
},
/*==============================================================================================*\
|* FORMAT: Controls the formatter and code checking options. *|
\*==============================================================================================*/
"format": {
// Only use the formatter for code checking.
"enable": false,
// Formatter settings.
"defaultConfig": {
"align_call_args": "false",
"align_chained_expression_statement": "false",
"align_function_define_params": "false",
"align_table_field_to_first_field": "false",
"call_arg_parentheses": "keep",
"class_name_define_style": "pascal_case",
"continuation_indent_size": "2",
"continuous_assign_statement_align_to_equal_sign": "true",
"continuous_assign_table_field_align_to_equal_sign": "true",
"detect_end_of_line": "true",
"do_statement_no_indent": "false",
"enable_check_codestyle": "true",
"enable_name_style_check": "true",
"end_of_line": "lf",
"function_name_define_style": "camel_case",
"function_param_name_style": "snake_case",
"global_variable_name_define_style": "upper_case",
"if_branch_comments_after_block_no_indent": "false",
"if_condition_no_continuation_indent": "false",
"indent_size": "2",
"indent_style": "space",
"insert_final_newline": "true",
"keep_line_after_do_statement": "keepLine",
"keep_line_after_expression_statement": "keepLine",
"keep_line_after_for_statement": "keepLine",
"keep_line_after_function_define_statement": "keepLine",
"keep_line_after_if_statement": "keepLine",
"keep_line_after_local_or_assign_statement": "keepLine",
"keep_line_after_repeat_statement": "keepLine",
"keep_line_after_while_statement": "keepLine",
"keep_one_space_between_namedef_and_attribute": "true",
"keep_one_space_between_table_and_bracket": "false",
"label_no_indent": "false",
"local_assign_continuation_align_to_first_expression": "false",
"local_function_name_define_style": "camel_case",
"local_name_define_style": "snake_case",
"long_chain_expression_allow_one_space_after_colon": "false",
"max_continuous_line_distance": "0",
"max_line_length": "100",
"module_name_define_style": "same('this')|same(filename, snake_case|pascal_case|camel_case)",
"quote_style": "double",
"remove_empty_header_and_footer_lines_in_function": "true",
"remove_expression_list_finish_comma": "true",
"require_module_name_style": "off",
"space_before_function_open_parenthesis": "false",
"space_before_open_square_bracket": "false",
"space_inside_function_call_parentheses": "false",
"space_inside_function_param_list_parentheses": "false",
"space_inside_square_brackets": "false",
"statement_inline_comment_space": "1",
"table_append_expression_no_space": "true",
"table_field_name_define_style": "snake_case",
"table_seperator_style": "comma",
"trailing_table_separator": "smart"
}
},
/*==============================================================================================*\
|* HINT: Controls the behavior of inline hints. *|
\*==============================================================================================*/
"hint": {
// Enable inline hints.
"enable": true,
// Show an inline hint at assignment.
"setType": true,
// Parameter settings.
"paramName": "All", // Show the name of parameters in function calls.
"paramType": true, // Show the type of parameters in function definitions.
// Show a virtual "await" next to called async functions.
"await": true,
// Show virtual number indexes if it may be hard to count them.
"arrayIndex": "Auto",
// Show a virtual semicolon to seperate statements on the same line.
"semicolon": "Disable"
},
/*==============================================================================================*\
|* HOVER: Controls the behavior of hover hints. *|
\*==============================================================================================*/
"hover": {
// Enable hover hints.
"enable": true,
// Allows large enum lists to be visible.
"enumsLimit": 100,
// Stops enum lists from making huge inline hints.
"expandAlias": false,
// Allows large tables to be visible.
"previewFields": 100,
// Allow viewing the raw form of a value.
"viewNumber": true,
"viewString": true,
"viewStringMax": 1024
},
/*==============================================================================================*\
|* TYPE: Controls type casting behavior. *|
\*==============================================================================================*/
"type": {
// Allows casting between `number` and `integer` (since they are the same thing in Lua.)
"castNumberToInteger": true,
// Allow "casting out" nil. (i.e: You can assign `number` to `number|nil`.)
"weakNilCheck": true,
// Disallow forced narrowing of union types. You must explicitly narrow with code.
"weakUnionCheck": false
},
/*==============================================================================================*\
|* SEMANTIC: Controls semantic coloring. *|
\*==============================================================================================*/
"semantic": {
// Enable semantic coloring.
"enable": true,
// Enable coloring for type annotations.
"annotation": true,
// VSCode already handles keyword coloring.
"keyword": false,
// Enable coloring for variables.
"variable": true
},
/*==============================================================================================*\
|* SIGNATURE HELP: No clue lol. *|
\*==============================================================================================*/
"signatureHelp": {
// Enables this, whatever it is.
"enable": true
},
/*==============================================================================================*\
|* SPELL: Controls the spell-check dictionary. *|
\*==============================================================================================*/
"spell": {
// Custom words for spell-check.
"dict": [
"figura"
]
},
/*==============================================================================================*\
|* TELEMETRY: Big brother is watching. *|
\*==============================================================================================*/
"telemetry": {
// If you wish to send telemetry data, you can enable this.
"enable": false
},
/*==============================================================================================*\
|* WINDOW: Controls the VSCode window's behavior. *|
\*==============================================================================================*/
"window": {
// Show workspace loading progress in the taskbar.
"progressBar": true,
// Show a funny little "😺Lua" indicator in the status bar while the Lua server is running.
"statusBar": true
},
/*==============================================================================================*\
|* MISC: Controls other features and behaviors. *|
\*==============================================================================================*/
"misc": {
// Startup parameters for the Lua language server.
"parameters": []
}
}

1
.vim/figuradocs Submodule

@ -0,0 +1 @@
Subproject commit a0c2690a3f71db97e0603347bdc20b8e7b2cd8e0

4
TODO.md Normal file
View File

@ -0,0 +1,4 @@
# TODO
## Rewrite
- [ ] Action Wheel
- [ ] Commands

5
avatar.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "Yzum <3",
"author": "NullBite",
"autoScripts": ["script"]
}

4
export.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
rm yzum.zip
git submodule update --init --recursive
find . -mindepth 1 -maxdepth 1 -not \( -name '*.xcf' -o -name '.*' -o -name '*.md' -o -name '*.sh' \) -exec apack yzum.zip '{}' +

1
nulllib Submodule

@ -0,0 +1 @@
Subproject commit a65b6b323dc7c3c80f0305b28f126cd9d2b317ac

File diff suppressed because one or more lines are too long

View File

@ -1,312 +1,41 @@
-- vim: set foldmethod=marker ts=4 sw=4 :
-- from figura-protogen commit f3687a4
--- Initial definitions ---
-- rewrite compat
model=models.player_model
ping=pings
-- Texture dimensions --
TEXTURE_WIDTH = 128
TEXTURE_HEIGHT = 128
-- utility functions -- {{{
PartsManager=require("nulllib.PartsManager")
UVManager=require("nulllib.UVManager")
logging=require("nulllib.logging")
nmath=require("nulllib.math")
timers=require("nulllib.timers")
util=require("nulllib.util")
sharedstate=require("nulllib.sharedstate")
sharedconfig=require("nulllib.sharedconfig")
--- Create a string representation of a table
--- @param o table
function dumpTable(o)
if type(o) == 'table' then
local s = '{ '
local first_loop=true
for k,v in pairs(o) do
if not first_loop then
s = s .. ', '
end
first_loop=false
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dumpTable(v)
end
return s .. '} '
else
return tostring(o)
end
end
wave=nmath.wave
lerp=math.lerp
do
local function format_any_value(obj, buffer)
local _type = type(obj)
if _type == "table" then
buffer[#buffer + 1] = '{"'
for key, value in next, obj, nil do
buffer[#buffer + 1] = tostring(key) .. '":'
format_any_value(value, buffer)
buffer[#buffer + 1] = ',"'
end
buffer[#buffer] = '}' -- note the overwrite
elseif _type == "string" then
buffer[#buffer + 1] = '"' .. obj .. '"'
elseif _type == "boolean" or _type == "number" then
buffer[#buffer + 1] = tostring(obj)
else
buffer[#buffer + 1] = '"???' .. _type .. '???"'
end
end
--- Dumps object as UNSAFE json, i stole this from stackoverflow so i could use json.tool to format it so it's easier to read
function dumpJSON(obj)
if obj == nil then return "null" else
local buffer = {}
format_any_value(obj, buffer)
return table.concat(buffer)
end
end
end
---@param uv table
function UV(uv)
return vectors.of({
uv[1]/TEXTURE_WIDTH,
uv[2]/TEXTURE_HEIGHT
})
end
---@param inputstr string
---@param sep string
function splitstring (inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
---@param input string
function unstring(input)
if input=="nil" then
return nil
elseif input == "true" or input == "false" then
return input=="true"
elseif tonumber(input) ~= nil then
return tonumber(input)
else
return input
end
end
---@param func function
---@param table table
function map(func, table)
local t={}
for k, v in pairs(table) do
t[k]=func(v)
end
return t
end
---@param func function
---@param table table
function filter(func, table)
local t={}
for k, v in pairs(table) do
if func(v) then
t[k]=v
end
end
return t
end
---@param tbl table
---@param val any
function has_value(tbl, val)
for _, v in pairs(tbl) do
if v==val then return true end
end
return false
end
--- Unordered reduction, only use when working with dictionaries and
--- execution order does not matter
---@param tbl table Table to reduce
---@param func function Function used to reduce table
---@param init any Initial operand for reduce function
function reduce(func, tbl, init)
local result = init
local first_loop = true
for _, v in pairs(tbl) do
if first_loop and init == nil then
result=v
else
result = func(result, v)
end
first_loop=false
end
return result
end
--- Ordered reduction, does not work with dictionaries
---@param tbl table Table to reduce
---@param func function Function used to reduce table
---@param init any Initial operand for reduce function
function ireduce(func, tbl, init)
local result = init
local first_loop = true
for _, v in ipairs(tbl) do
if first_loop and init == nil then
result=v
else
result = func(result, v)
end
first_loop=false
end
return result
end
--- Merge two tables. First table value takes precedence when conflict occurs.
---@param tb1 table
---@param tb2 table
function mergeTable(tb1, tb2)
local t={}
for k, v in pairs(tb1) do
t[k]=v
end
for k, v in pairs(tb2) do
if type(k)=="number" then
table.insert(t, v)
else
if t[k]==nil then
t[k]=v
end
end
end
return t
end
function debugPrint(var)
print(dumpTable(var))
return var
end
--- Recursively walk a model tree and return a table containing the group and each of its sub-groups
--- @param group table The group to recurse
--- @return table Resulting table
function recurseModelGroup(group)
local t={}
table.insert(t, group)
if group.getType()=="GROUP" then
for k, v in pairs(group.getChildren()) do
for _, v2 in pairs(recurseModelGroup(v)) do
table.insert(t, v2)
end
end
end
return t
end
-- }}}
-- Timer (not mine lol) -- {{{
do
local timers = {}
function wait(ticks,next)
table.insert(timers, {t=world.getTime()+ticks,n=next})
end
function tick()
for key,timer in pairs(timers) do
if world.getTime() >= timer.t then
timer.n()
table.remove(timers,key)
end
end
end
end
-- named timers (this one is mine but heavily based on the other) --
-- if timer is armed twice before expiring it will only be called once) --
do
local timers = {}
function namedWait(ticks, next, name)
-- main difference, this will overwrite an existing timer with
-- the same name
timers[name]={t=world.getTime()+ticks,n=next}
end
function tick()
for key, timer in pairs(timers) do
if world.getTime() >= timer.t then
timer.n()
timers[key]=nil
end
end
end
end
-- named cooldowns
do
local timers={}
function cooldown(ticks, name)
if timers[name] == nil then
timers[name]={t=world.getTime()+ticks}
return true
end
return false
end
function tick()
for key, timer in pairs(timers) do
if world.getTime() >= timer.t then
timers[key]=nil
end
end
end
end
function rateLimit(ticks, next, name)
if cooldown(ticks+1, name) then
namedWait(ticks, next, name)
end
end
-- }}}
-- syncState {{{
function syncState()
ping.syncState(setLocalState())
end
do
local pm_refresh=false
function pmRefresh()
pm_refresh=true
end
function doPmRefresh()
if pm_refresh then
PartsManager.refreshAll()
pm_refresh=false
end
end
end
function ping.syncState(tbl)
for k, v in pairs(tbl) do
local_state[k]=v
end
pmRefresh()
end
-- }}}
-- Math {{{
--- Sine function with period and amplitude
--- @param x number Input value
--- @param period number Period of sine wave
--- @param amp number Peak amplitude of sine wave
function wave(x, period, amp) return math.sin((2/period)*math.pi*(x%period))*amp end
function lerp(a, b, t) return a + ((b - a) * t) end
-- }}}
-- Master and local state variables -- {{{
-- Master config -- {{{
-- Local State (these are copied by pings at runtime) --
---@deprecated use sharedstate or sharedconfig
local_state={}
---@deprecated use sharedstate or sharedconfig
old_state={}
-- master state variables and configuration (do not access within pings) --
do
local is_host=client.isHost()
local function refresh_armor()
if player:isLoaded() then PartsManager.refreshPartList(VANILLA_GROUPS.ARMOR) end
end
local function refresh_tail()
if player:isLoaded() then PartsManager.refreshPart(model.Body_Tail) end
end
local defaults={
["armor_enabled"]=true,
["vanilla_enabled"]=false,
@ -314,275 +43,117 @@ do
["print_settings"]=false,
["tail_enabled"]=true,
}
function setLocalState()
if is_host then
for k, v in pairs(skin_state) do
local_state[k]=v
end
else
for k, v in pairs(defaults) do
if local_state[k] == nil then local_state[k]=v end
end
end
return local_state
end
if is_host then
local savedData=data.loadAll()
if savedData == nil then
for k, v in pairs(defaults) do
data.save(k, v)
end
savedData=data.loadAll()
end
skin_state=mergeTable(
map(unstring,data.loadAll()),
defaults)
else
skin_state=defaults
end
setLocalState()
local callbacks={
["armor_enabled"]=refresh_armor,
["tail_enabled"]=refresh_tail
}
sharedconfig.load_defaults(defaults, callbacks)
end
function printSettings()
print("Settings:")
for k, v in pairs(skin_state) do
print(tostring(k)..": "..tostring(v))
printTable(sharedconfig.load())
end
end
if skin_state.print_settings==true then
if sharedconfig.load("print_settings") then
printSettings()
end
function setState(name, state)
if state == nil then
skin_state[name]=not skin_state[name]
else
skin_state[name]=state
end
data.save(name, skin_state[name])
end
-- }}}
-- PartsManager -- {{{
do
PartsManager={}
local pm={}
--- ensure part is initialized
local function initPart(part)
local part_key=tostring(part)
if pm[part_key] == nil then
pm[part_key]={}
end
pm[part_key].part=part
if pm[part_key].functions == nil then
pm[part_key].functions = {}
end
if pm[part_key].init==nil then
pm[part_key].init="true"
end
end
--- Add function to part in PartsManager.
--- @param part table Any object with a setEnabled() method.
--- @param func function Function to add to model part's function chain.
--- @param init? boolean Default value for chain. Should only be set once, subsequent uses overwrite the entire chain's initial value.
function PartsManager.addPartFunction(part, func, init)
initPart(part)
local part_key=tostring(part)
if init ~= nil then
pm[part_key].init=init
end
table.insert(pm[part_key]["functions"], func)
end
--- Set initial value for chain.
--- @param part table Any object with a setEnabled() method.
--- @param init? boolean Default value for chain. Should only be set once, subsequent uses overwrite the entire chain's initial value.
function PartsManager.setInitialValue(part, init)
assert(init~=nil)
initPart(part)
local part_key=tostring(part)
pm[part_key].init=init
end
--- Set initial value for chain on all objects in table.
--- @param group table A table containing objects with a setEnabled() method.
--- @param init? boolean Default value for chain. Should only be set once, subsequent uses overwrite the entire chain's initial value.
function PartsManager.setGroupInitialValue(group, init)
assert(init~=nil)
for _, v in pairs(group) do
PartsManager.setInitialValue(v, init)
end
end
--- Evaluate a part's chain to determine if it should be visible.
--- @param part table An object managed by PartsManager.
function PartsManager.evaluatePart(part)
local part_key=tostring(part)
assert(pm[part_key] ~= nil)
local evalFunc=function(x, y) return y(x) end
local init=pm[part_key].init
return ireduce(evalFunc, pm[part_key].functions, true)
end
local evaluatePart=PartsManager.evaluatePart
--- Refresh (enable or disable) a part based on the result of it's chain.
--- @param part table An object managed by PartsManager.
function PartsManager.refreshPart(part)
local part_enabled=evaluatePart(part)
part.setEnabled(part_enabled)
return part_enabled
end
--- Refresh all parts managed by PartsManager.
function PartsManager.refreshAll()
for _, v in pairs(pm) do
PartsManager.refreshPart(v.part)
end
end
--- Add function to list of parts in PartsManager
--- @param group table A table containing objects with a setEnabled() method.
--- @param func function Function to add to each model part's function chain.
--- @param default? boolean Default value for chain. Should only be set once, subsequent uses overwrite the entire chain's initial value.
function PartsManager.addPartGroupFunction(group, func, default)
for _, v in ipairs(group) do
PartsManager.addPartFunction(v, func, default)
end
end
end
-- }}}
-- UVManager {{{
do
local mt={}
UVManager = {
step=vectors.of{u=0, v=0},
offset=vectors.of{u=0, v=0},
positions={}
}
mt.__index=UVManager
function UVManager.new(self, step, offset, positions)
local t={}
if step ~= nil then t.step=vectors.of(step) end
if offset ~= nil then t.offset=vectors.of(offset) end
if positions ~= nil then t.positions=positions end
t=setmetatable(t, mt)
return t
end
function UVManager.getUV(self, input)
local vec={}
local stp=self.step
local offset=self.offset
if type(input) == "string" then
if self.positions[input] == nil then return nil end
vec=vectors.of(self.positions[input])
else
vec=vectors.of(input)
end
local u=offset.u+(vec.u*stp.u)
local v=offset.v+(vec.v*stp.v)
return UV{u, v}
end
end
-- }}}
-- Part groups {{{
VANILLA_GROUPS={
["HEAD"]={vanilla_model.HEAD, vanilla_model.HAT},
["TORSO"]={vanilla_model.TORSO, vanilla_model.JACKET},
["TORSO"]={vanilla_model.BODY, vanilla_model.JACKET},
["LEFT_ARM"]={vanilla_model.LEFT_ARM, vanilla_model.LEFT_SLEEVE},
["RIGHT_ARM"]={vanilla_model.RIGHT_ARM, vanilla_model.RIGHT_SLEEVE},
["LEFT_LEG"]={vanilla_model.LEFT_LEG, vanilla_model.LEFT_PANTS_LEG},
["RIGHT_LEG"]={vanilla_model.RIGHT_LEG, vanilla_model.RIGHT_PANTS_LEG},
["OUTER"]={ vanilla_model.HAT, vanilla_model.JACKET, vanilla_model.LEFT_SLEEVE, vanilla_model.RIGHT_SLEEVE, vanilla_model.LEFT_PANTS_LEG, vanilla_model.RIGHT_PANTS_LEG },
["INNER"]={ vanilla_model.HEAD, vanilla_model.TORSO, vanilla_model.LEFT_ARM, vanilla_model.RIGHT_ARM, vanilla_model.LEFT_LEG, vanilla_model.RIGHT_LEG },
["LEFT_LEG"]={vanilla_model.LEFT_LEG, vanilla_model.LEFT_PANTS},
["RIGHT_LEG"]={vanilla_model.RIGHT_LEG, vanilla_model.RIGHT_PANTS},
["OUTER"]={ vanilla_model.HAT, vanilla_model.JACKET, vanilla_model.LEFT_SLEEVE, vanilla_model.RIGHT_SLEEVE, vanilla_model.LEFT_PANTS, vanilla_model.RIGHT_PANTS },
["INNER"]={ vanilla_model.HEAD, vanilla_model.BODY, vanilla_model.LEFT_ARM, vanilla_model.RIGHT_ARM, vanilla_model.LEFT_LEG, vanilla_model.RIGHT_LEG },
["ALL"]={},
["ARMOR"]={}
["ARMOR"]={vanilla_model.HELMET, vanilla_model.LEGGINGS, vanilla_model.BOOTS, vanilla_model.CHESTPLATE}
}
for _, v in pairs(VANILLA_GROUPS.INNER) do table.insert(VANILLA_GROUPS.ALL,v) end
for _, v in pairs(VANILLA_GROUPS.OUTER) do table.insert(VANILLA_GROUPS.ALL,v) end
for _, v in pairs(armor_model) do table.insert(VANILLA_GROUPS.ARMOR, v) end
MAIN_GROUPS={model.Head, model.RightArm, model.LeftArm, model.RightLeg, model.LeftLeg, model.Body } -- RightArm LeftArm RightLeg LeftLeg Body Head
TAIL_BONES={model.Body_Tail, model.Body_Tail.Tail2, model.Body_Tail.Tail2.Tail3, model.Body_Tail.Tail2.Tail3.Tail4}
TAIL_ROT={vectors.of{37.5, 0, 0}, vectors.of{-17.5, 0, 0}, vectors.of{-17.5, 0, 0}, vectors.of{-15, 0, 0}}
TAIL_ROT={vec( 37.5, 0, 0 ), vec( -17.5, 0, 0 ), vec( -17.5, 0, 0 ), vec( -15, 0, 0 )}
-- }}}
-- Enable commands -- {{{
chat_prefix="$"
chat.setFiguraCommandPrefix(chat_prefix)
function onCommand(input)
local pfx=chat_prefix
input=splitstring(input)
if input[1] == chat_prefix .. "vanilla" then
setVanilla()
print("Vanilla skin is now " .. (skin_state.vanilla_enabled and "enabled" or "disabled"))
end
if input[1] == chat_prefix .. "toggle_custom" then
for key, value in pairs(model) do
value.setEnabled(not value.getEnabled())
end
end
if input[1] == chat_prefix .. "toggle_outer" then
for k, v in pairs(VANILLA_GROUPS.OUTER) do
v.setEnabled(not v.getEnabled())
end
end
if input[1] == chat_prefix .. "toggle_inner" then
for k, v in pairs(VANILLA_GROUPS.INNER) do
v.setEnabled(not v.getEnabled())
end
end
if input[1] == chat_prefix .. "test_expression" then
setExpression(input[2], input[3])
print(input[2] .. " " .. input[3])
end
if input[1] == chat_prefix .. "snore" then
if input[2] == "toggle" or #input==1 then
setSnoring()
log("Snoring is now " .. (skin_state.snore_enabled and "enabled" or "disabled"))
end
end
if input[1] == chat_prefix .. "armor" then
setArmor()
log("Armor is now " .. (skin_state.armor_enabled and "enabled" or "disabled"))
end
if input[1] == chat_prefix .. "settings" then
if #input==1 then
printSettings()
elseif #input==2 then
log(tostring(skin_state[input[2]]))
elseif #input==3 then
if skin_state[input[2]] ~= nil then
setState(input[2], unstring(input[3]))
log(tostring(input[2]) .. " is now " .. tostring(skin_state[input[2]]))
syncState()
else
log(tostring(input[2]) .. ": no such setting")
end
end
end
if input[1] == chat_prefix .. "pv" then
setState("vanilla_partial")
syncState()
end
end
--}}}
-- -- Enable commands -- {{{
-- chat_prefix="$"
-- chat.setFiguraCommandPrefix(chat_prefix)
-- function onCommand(input)
-- local pfx=chat_prefix
-- input=splitstring(input)
-- if input[1] == chat_prefix .. "vanilla" then
-- setVanilla()
-- print("Vanilla skin is now " .. (skin_state.vanilla_enabled and "enabled" or "disabled"))
-- end
-- if input[1] == chat_prefix .. "toggle_custom" then
-- for key, value in pairs(model) do
-- value.setEnabled(not value.getEnabled())
-- end
-- end
-- if input[1] == chat_prefix .. "toggle_outer" then
-- for k, v in pairs(VANILLA_GROUPS.OUTER) do
-- v.setEnabled(not v.getEnabled())
-- end
-- end
-- if input[1] == chat_prefix .. "toggle_inner" then
-- for k, v in pairs(VANILLA_GROUPS.INNER) do
-- v.setEnabled(not v.getEnabled())
-- end
-- end
-- if input[1] == chat_prefix .. "test_expression" then
-- setExpression(input[2], input[3])
-- print(input[2] .. " " .. input[3])
-- end
-- if input[1] == chat_prefix .. "snore" then
-- if input[2] == "toggle" or #input==1 then
-- setSnoring()
-- log("Snoring is now " .. (skin_state.snore_enabled and "enabled" or "disabled"))
-- end
-- end
-- if input[1] == chat_prefix .. "armor" then
-- setArmor()
-- log("Armor is now " .. (skin_state.armor_enabled and "enabled" or "disabled"))
-- end
-- if input[1] == chat_prefix .. "settings" then
-- if #input==1 then
-- printSettings()
-- elseif #input==2 then
-- log(tostring(skin_state[input[2]]))
-- elseif #input==3 then
-- if skin_state[input[2]] ~= nil then
-- setState(input[2], unstring(input[3]))
-- log(tostring(input[2]) .. " is now " .. tostring(skin_state[input[2]]))
-- syncState()
-- else
-- log(tostring(input[2]) .. ": no such setting")
-- end
-- end
-- end
-- if input[1] == chat_prefix .. "pv" then
-- setState("vanilla_partial")
-- syncState()
-- end
-- end
-- --}}}
-- PartsManager Rules {{{
do
local can_modify_vanilla=meta.getCanModifyVanilla()
local can_modify_vanilla=avatar:canEditVanillaModel()
local function forceVanilla()
return not can_modify_vanilla or local_state.vanilla_enabled
return not can_modify_vanilla or sharedconfig.load("vanilla_enabled")
end
local function vanillaPartial()
return not local_state.vanilla_enabled and local_state.vanilla_partial
return not sharedconfig.load("vanilla_enabled") and sharedconfig.load("vanilla_partial")
end
@ -591,49 +162,57 @@ do
local vanilla_partial_disabled=MAIN_GROUPS
-- Vanilla state
PM.addPartGroupFunction(VANILLA_GROUPS.ALL, function() return false end)
PM.addPartGroupFunction(VANILLA_GROUPS.ALL, function(last) return last or forceVanilla() end)
PM.addPartListFunction(VANILLA_GROUPS.ALL, function() return false end)
PM.addPartListFunction(VANILLA_GROUPS.ALL, function(last) return last or forceVanilla() end)
PM.addPartGroupFunction(VANILLA_GROUPS.ALL, function(last) return last or vanillaPartial() end)
PM.addPartListFunction(VANILLA_GROUPS.ALL, function(last) return last or vanillaPartial() end)
-- disable cape if tail enabled
PM.addPartFunction(vanilla_model.CAPE, function(last) return last and not local_state.tail_enabled end)
PM.addPartFunction(vanilla_model.CAPE, function(last) return last and not sharedconfig.load("tail_enabled") end)
-- Custom state
PM.addPartGroupFunction(vanilla_partial_disabled, function(last) return last and not vanillaPartial() end)
PM.addPartGroupFunction(MAIN_GROUPS, function(last) return last and not forceVanilla() end)
PM.addPartListFunction(vanilla_partial_disabled, function(last) return last and not vanillaPartial() end)
PM.addPartListFunction(MAIN_GROUPS, function(last) return last and not forceVanilla() end)
-- enable tail
PM.addPartFunction(model.Body_Tail, function(last) return last and local_state.tail_enabled end)
PM.addPartFunction(model.Body_Tail, function(last) return last and sharedconfig.load("tail_enabled") end)
-- Armor state
PM.addPartGroupFunction(VANILLA_GROUPS.ARMOR, function(last) return last and local_state.armor_enabled end)
PM.addPartListFunction(VANILLA_GROUPS.ARMOR, function(last) return last and sharedconfig.load("armor_enabled") end)
end
-- }}}
-- Action Wheel {{{
do
local slot_1_item = item_stack.createItem("minecraft:netherite_helmet")
action_wheel.SLOT_1.setTitle('Toggle Armor')
action_wheel.SLOT_1.setFunction(function() setArmor() end)
action_wheel.SLOT_1.setItem(slot_1_item)
end
-- -- Action Wheel {{{
-- do
-- local slot_1_item = item_stack.createItem("minecraft:netherite_helmet")
-- action_wheel.SLOT_1.setTitle('Toggle Armor')
-- action_wheel.SLOT_1.setFunction(function() setArmor() end)
-- action_wheel.SLOT_1.setItem(slot_1_item)
-- end
function setArmor()
setState("armor_enabled")
syncState()
sharedconfig.save("armor_enabled", not sharedconfig.load("armor_enabled"))
end
-- }}}
function player_init()
for k, v in pairs(reduce(mergeTable, map(recurseModelGroup, model))) do
v.setEnabled(true)
-- for k, v in pairs(reduce(mergeTable, map(recurseModelGroup, model))) do
-- v.setEnabled(true)
-- end
-- syncState()
events.ENTITY_INIT:remove("player_init")
PartsManager.refreshAll()
end
setLocalState()
syncState()
events.ENTITY_INIT:register(function() return player_init() end, "player_init")
if avatar:canEditVanillaModel() then
vanilla_model.PLAYER:setVisible(false)
else
model:setVisible(false)
end
anim_tick=0
@ -650,19 +229,23 @@ function animateTail(val)
local per_x=20*6
for k, v in pairs(TAIL_BONES) do
local cascade=(k-1)*12
TAIL_BONES[k].setRot(vectors.of{TAIL_ROT[k].x + wave(val-cascade, per_x, 3), TAIL_ROT[k].y + wave(val-cascade, per_y, 17.5), TAIL_ROT[k].z})
TAIL_BONES[k]:setRot(vec( TAIL_ROT[k].x + wave(val-cascade, per_x, 3), TAIL_ROT[k].y + wave(val-cascade, per_y, 17.5), TAIL_ROT[k].z ))
end
end
function tick()
if world.getTime() % (20*10) == 0 then
syncState()
-- TODO fix
-- syncState()
end
animateTick()
doPmRefresh()
-- TODO fix
-- doPmRefresh()
end
events.TICK:register(function() if player then tick() end end, "main_tick")
function render(delta)
animateTail(lerp(old_state.anim_cycle, anim_cycle, delta))
end
events.RENDER:register(function(delta) if player then render(delta) end end, "main_render")

BIN
texture.png (Stored with Git LFS)

Binary file not shown.

BIN
texture.xcf (Stored with Git LFS)

Binary file not shown.