diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 2c28acbb..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "env": { - "browser": true, - "es2021": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" - ], - "ignorePatterns": [ - "node_modules/", - "dist/", - "exporters/", - "tools/", - "svelte.config.ts", - "vitest.config.ts", - "src/util/bufferGeometryUtils.ts", - "tests/**/*" - ], - "overrides": [ - { - "files": ["*.js", "*.d.ts"], - "parserOptions": { - "project": "./tsconfig.json", - "tsconfigRootDir": "./" - }, - "rules": {} - } - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": ["./tsconfig.json"], - "tsconfigRootDir": "./" - }, - "plugins": ["@typescript-eslint"], - "rules": { - "semi": ["warn", "never"], - "prefer-const": "warn", - "no-fallthrough": "off", - "no-mixed-spaces-and-tabs": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-floating-promises": ["error", { "ignoreVoid": true }], - "@typescript-eslint/array-type": ["warn", { "default": "array-simple" }], - "@typescript-eslint/ban-types": "warn", - "@typescript-eslint/consistent-indexed-object-style": ["warn", "record"], - "@typescript-eslint/consistent-generic-constructors": "warn", - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/naming-convention": [ - "warn", - { - "selector": "class", - "format": ["PascalCase"] - }, - { - "selector": ["classProperty", "classMethod"], - "format": ["camelCase"] - }, - { - "selector": ["classProperty", "classMethod"], - "filter": { - "regex": "^_.*$", - "match": true - }, - "prefix": ["_"], - "format": ["camelCase"] - }, - { - "selector": "typeProperty", - "format": null - }, - { - "selector": "variable", - "modifiers": ["const"], - "format": null - }, - { - "selector": "variable", - "modifiers": ["const", "destructured"], - "format": ["camelCase", "PascalCase", "UPPER_CASE"] - }, - { - "selector": "variable", - "modifiers": ["const", "global"], - "format": ["UPPER_CASE"] - }, - { - "selector": "variable", - "modifiers": ["const", "global"], - "filter": { - "regex": "^_.*$", - "match": true - }, - "prefix": ["_"], - "format": ["UPPER_CASE"] - }, - { - "selector": "variable", - "modifiers": ["const", "global"], - "types": ["function"], - "format": ["camelCase", "UPPER_CASE"] - }, - { - "selector": "variable", - "modifiers": ["const", "global", "exported"], - "format": ["camelCase", "UPPER_CASE"] - }, - { "selector": "variableLike", "format": ["camelCase"] }, - { "selector": "interface", "format": ["PascalCase"] }, - { - "selector": "interface", - "modifiers": ["exported"], - "format": ["PascalCase"], - "prefix": ["I"] - }, - { "selector": "typeLike", "format": ["PascalCase"] }, - { "selector": "objectLiteralProperty", "format": null }, - { "selector": "default", "format": ["camelCase"] }, - { - "selector": "parameter", - "modifiers": ["unused"], - "format": null - }, - { - "selector": "enumMember", - "format": ["UPPER_CASE"] - } - ], - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/require-await": "warn", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/triple-slash-reference": "off" - } -} diff --git a/.vscode/settings.json b/.vscode/settings.json index efe4f7ef..5663c09c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "typescript.tsdk": "node_modules\\typescript\\lib", + "typescript.tsserver.experimental.enableProjectDiagnostics": true, "eslint.enable": true, "editor.formatOnSave": true, "prettier.enable": true, @@ -28,5 +29,18 @@ "errorLens.excludePatterns": ["**/node_modules/**/*"], "eslint.validate": ["javascript", "typescript", "svelte"], "eslint.useFlatConfig": true, - "eslint.problems.shortenToSingleLine": true + "eslint.problems.shortenToSingleLine": true, + "workbench.colorCustomizations": { + "editorGroupHeader.tabsBorder": "#00aced", + "commandCenter.foreground": "#ffffffdd", + "commandCenter.background": "#00aced77", + "commandCenter.border": "#00aced" + }, + "svelte.plugin.svelte.compilerWarnings": { + "a11y_click_events_have_key_events": "ignore", + "a11y_no_static_element_interactions": "ignore" + }, + "mc-dp-icons.enableNamespaceIcons": false, + "mc-dp-icons.enableChristmasIcons": "Disable", + "mc-dp-icons.enableLoadTickAutoChange": false } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..7541ec3d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,50 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "command": "yarn check-formatting", + "label": "Check formatting", + "detail": "Reports any formatting issues via the problems view", + "group": "test", + "isBackground": true, + "icon": { + "id": "code", + "color": "terminal.ansiMagenta" + }, + "problemMatcher": "$prettier", + "runOptions": { + "instanceLimit": 1, + "reevaluateOnRerun": true + }, + "presentation": { + "reveal": "never", + "showReuseMessage": true, + "clear": false, + "revealProblems": "onProblem" + } + }, + { + "type": "eslint", + "problemMatcher": ["$eslint-stylish"], + "label": "ESLint: Lint the whole project", + "detail": "Reports any linting issues via the problems view", + "group": "test", + "icon": { + "id": "code", + "color": "terminal.ansiYellow" + }, + "presentation": { + "reveal": "never", + "showReuseMessage": true, + "clear": false, + "revealProblems": "onProblem" + }, + "runOptions": { + "instanceLimit": 1, + "instancePolicy": "terminateOldest", + "reevaluateOnRerun": true + } + } + ] +} diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 00000000..1df67d02 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,273 @@ +import svelteEslint from 'eslint-plugin-svelte' +import svelteParser from 'svelte-eslint-parser' +import tsESLint, { type ConfigWithExtends } from 'typescript-eslint' +import type { NamingConventionRule } from './tools/tslintNamingConventionRule' + +console.log(`⚙️ Loading ESLint config...`) + +const IGNORE_PATTERNS = [ + '.DS_Store', + '.env', + '.env.*', + '.github', + '.vscode', + '**/node_modules/**', + + // Blockbench Plugin Template + 'dist/**/*', + + // Ignore files for PNPM, NPM and YARN + 'pnpm-lock.yaml', + 'package-lock.json', + 'yarn.lock', +] + +const CUSTOM_RULES: ConfigWithExtends['rules'] = { + // ESLint + semi: ['error', 'never'], + 'prefer-const': 'warn', + 'no-fallthrough': 'off', + 'no-mixed-spaces-and-tabs': 'off', + 'no-unreachable': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + vars: 'local', + args: 'after-used', + argsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], + // Svelte + 'svelte/html-quotes': ['warn', { prefer: 'double' }], + 'svelte/block-lang': ['error', { script: ['ts', null], style: null }], + 'svelte/comment-directive': ['error', { reportUnusedDisableDirectives: true }], + 'svelte/valid-compile': ['error', { ignoreWarnings: true }], + // TypeScript + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], + '@typescript-eslint/array-type': ['warn', { default: 'array-simple' }], + '@typescript-eslint/consistent-indexed-object-style': ['warn', 'record'], + '@typescript-eslint/consistent-generic-constructors': 'warn', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'warn', + { + minimumDescriptionLength: 3, + 'ts-check': false, + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': true, + 'ts-nocheck': true, + }, + ], + '@typescript-eslint/require-await': 'warn', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'no-public' }], + // Naming conventions + '@typescript-eslint/naming-convention': [ + 'warn', + { + // DFU Version imports + selector: ['import'], + modifiers: ['default'], + filter: { + regex: 'v\\d+_\\d+_\\d+$', + match: true, + }, + custom: { + match: true, + regex: 'v\\d+_\\d+_\\d+$', + }, + format: null, + }, + { + // Version Enums + selector: ['enumMember'], + filter: { regex: '^([\\d\\.]+)$', match: true }, + format: null, + }, + { + selector: ['import'], + modifiers: ['default'], + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + }, + { + selector: 'class', + format: ['PascalCase'], + }, + { + selector: ['classProperty', 'classMethod'], + format: ['camelCase'], + }, + { + selector: ['classProperty', 'classMethod'], + leadingUnderscore: 'allow', + format: ['camelCase'], + }, + { + selector: ['classProperty', 'classMethod'], + modifiers: ['private'], + leadingUnderscore: 'allowDouble', + trailingUnderscore: 'allowDouble', + format: ['camelCase'], + }, + { + selector: 'typeProperty', + format: null, + }, + { + selector: 'variable', + modifiers: ['const', 'destructured'], + format: null, + }, + { + selector: 'variable', + modifiers: ['const', 'global'], + types: ['function'], + leadingUnderscore: 'allow', + format: ['UPPER_CASE', 'camelCase'], + }, + { + selector: 'variable', + modifiers: ['const', 'global'], + leadingUnderscore: 'allow', + format: ['UPPER_CASE'], + }, + { + selector: 'variable', + modifiers: ['const', 'exported'], + format: ['camelCase', 'UPPER_CASE'], + }, + { + selector: 'variableLike', + format: ['camelCase'], + leadingUnderscore: 'forbid', + }, + { + selector: 'interface', + format: ['PascalCase'], + leadingUnderscore: 'forbid', + }, + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'objectLiteralProperty', format: null }, + { selector: 'default', format: ['camelCase'], leadingUnderscore: 'forbid' }, + { + selector: 'parameter', + modifiers: ['unused'], + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + { + selector: 'parameter', + format: ['camelCase'], + }, + { + selector: 'enumMember', + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + }, + { + selector: 'enum', + format: ['UPPER_CASE'], + }, + ] satisfies NamingConventionRule, +} + +export default tsESLint.config( + { + ignores: IGNORE_PATTERNS, + }, + ...tsESLint.configs.stylisticTypeChecked, + ...svelteEslint.configs['flat/prettier'], + { + plugins: { + '@typescript-eslint': tsESLint.plugin, + svelte: svelteEslint, + }, + }, + { + rules: CUSTOM_RULES, + }, + { + languageOptions: { + parser: tsESLint.parser, + parserOptions: { + project: './tsconfig.json', + extraFileExtensions: ['.svelte'], + }, + globals: { + browser: true, + node: true, + }, + }, + }, + { + files: ['**/*.svelte'], + rules: { + // Causes issues with Svelte and global types + 'no-undef': 'off', + // Annoying when destructuring props + 'prefer-const': 'off', + '@typescript-eslint/naming-convention': [ + 'warn', + { + selector: 'variable', + modifiers: ['exported'], + format: ['camelCase'], + }, + { + selector: 'variable', + modifiers: ['const', 'global'], + format: ['UPPER_CASE'], + }, + { + selector: 'variable', + modifiers: ['const', 'global', 'destructured'], + format: ['camelCase', 'UPPER_CASE'], + }, + { + selector: 'variable', + modifiers: ['const', 'global'], + types: ['function'], + format: ['camelCase'], + }, + { + selector: 'variable', + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + ] satisfies NamingConventionRule, + }, + languageOptions: { + parser: svelteParser, + parserOptions: { + parser: tsESLint.parser, + // svelteConfig: svelteConfig, + extraFileExtensions: ['.svelte'], + }, + globals: { + browser: true, + node: true, + }, + }, + settings: { + ignoreWarnings: ['svelte/a11y-no-onchange', 'a11y-no-onchange'], + }, + }, + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: __dirname, + }, + }, + linterOptions: { + reportUnusedDisableDirectives: true, + }, + } +) +console.log(`✅ ESLint config loaded successfully.`) diff --git a/package.json b/package.json index cf8ef866..d2f066b2 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,10 @@ "name": "animated_java", "title": "Animated Java", "icon": "icon.svg", - "description": "A Blockbench plugin that makes complex animation a breeze in Minecraft: Java Edition.", - "version": "1.7.2", + "description": "Effortlessly craft complex animations for Minecraft: Java Edition", + "version": "1.8.0-beta.1", "min_blockbench_version": "4.12.0", + "max_blockbench_version": "4.12.6", "variant": "desktop", "tags": [ "Minecraft: Java Edition", @@ -79,7 +80,9 @@ "prod": "node ./tools/cleanupDist.cjs && yarn build:scripts && node ./dist/build.cjs", "format": "prettier --write .", "test": "yarn build:scripts && vitest run", - "coverage": "yarn build:scripts && vitest run --coverage" + "coverage": "yarn build:scripts && vitest run --coverage", + "check-formatting": "prettier . --check", + "lint": "eslint . --max-warnings=0 --no-warn-ignored" }, "devDependencies": { "@novacbn/svelte-codejar": "^0.1.2", @@ -96,17 +99,24 @@ "esbuild-plugin-inline-image": "^0.0.9", "esbuild-plugin-inline-worker": "^0.1.1", "esbuild-plugin-svelte": "^0.1.1", - "eslint": "^8.35.0", + "eslint": "^9.37.0", + "eslint-plugin-svelte": "^3.12.4", "firebase": "^9.19.0", + "jiti": "^2.6.1", "js-yaml": "^4.1.0", + "mc-build": "^3.6.7", + "node-modules-vscode-problems-patch": "^1.0.9", + "octokit": "^5.0.3", "prettier": "^2.5.1", "svelte": "^3.55.1", "svelte-awesome-color-picker": "^3.0.0-beta.7", + "svelte-eslint-parser": "^1.3.3", "svelte-multiselect": "^11.0.0-rc.1", "svelte-preprocess": "^5.0.1", "svelte-preprocess-esbuild": "^3.0.1", "typescript": "^4.5.5", - "vitest": "^2.1.8" + "typescript-eslint": "^8.45.0", + "vitest": "^3.2.4" }, "dependencies": { "deepslate": "^0.19.2", @@ -115,7 +125,6 @@ "generic-stream": "^1.2.6", "marked": "^4.3.0", "marked-gfm-heading-id": "^3.0.0", - "mc-build": "^3.5.1", "request-progress": "^3.0.0", "svelte-ace": "^1.0.21", "svelte-dnd-action": "^0.9.38" diff --git a/src/assets/kofi_s_tag_white.webp b/src/assets/kofi_tag_white.webp similarity index 100% rename from src/assets/kofi_s_tag_white.webp rename to src/assets/kofi_tag_white.webp diff --git a/src/blockbenchTypeMods.d.ts b/src/blockbenchTypeMods.d.ts index aa2befcc..47416cf5 100644 --- a/src/blockbenchTypeMods.d.ts +++ b/src/blockbenchTypeMods.d.ts @@ -1,23 +1,3 @@ -import type { - BLUEPRINT_CODEC, - BLUEPRINT_FORMAT, - IBlueprintBoneConfigJSON, - IBlueprintLocatorConfigJSON, -} from './blueprintFormat' -import { blueprintSettingErrors, defaultValues } from './blueprintSettings' -import { openExportProgressDialog } from './interface/dialog/exportProgress' -import { openUnexpectedErrorDialog } from './interface/dialog/unexpectedError' -import { TextDisplay } from './outliner/textDisplay' -import { VanillaBlockDisplay } from './outliner/vanillaBlockDisplay' -import { VanillaItemDisplay } from './outliner/vanillaItemDisplay' -import datapackCompiler from './systems/datapackCompiler' -import { MINECRAFT_REGISTRY } from './systems/minecraft/registryManager' -import resourcepackCompiler from './systems/resourcepackCompiler' -import { EasingKey } from './util/easing' -import { isDataPackPath, isResourcePackPath } from './util/minecraftUtil' -import { Valuable } from './util/stores' -import { type Variant } from './variants' - declare module 'three' { interface Object3D { isVanillaItemModel?: boolean @@ -27,82 +7,4 @@ declare module 'three' { } } -declare global { - interface ModelProject { - animated_java: { [T in keyof typeof defaultValues]: (typeof defaultValues)[T] } - last_used_export_namespace: string - visualBoundingBox?: THREE.LineSegments - pluginMode: Valuable - transparentTexture: Texture - - showingInvalidCubeRotations: boolean - - variants: Variant[] - textDisplays: TextDisplay[] - vanillaItemDisplays: VanillaItemDisplay[] - vanillaBlockDisplays: VanillaBlockDisplay[] - - loadingPromises?: Array> - } - - // eslint-disable-next-line @typescript-eslint/naming-convention - interface _Animation { - excluded_nodes: CollectionItem[] - } - - interface AnimationUndoCopy { - excluded_nodes: string[] - } - - interface AnimationOptions { - excluded_nodes: string[] - } - - // eslint-disable-next-line @typescript-eslint/naming-convention - interface _Keyframe { - easing?: EasingKey - easingArgs?: number[] - } - - interface Group { - configs: { - default: IBlueprintBoneConfigJSON - /** - * @key Variant UUID - * @value Variant Bone Config - */ - variants: Record - } - } - - interface Locator { - config: IBlueprintLocatorConfigJSON - } - - interface Cube { - rotationInvalid: boolean - } - - interface CubeFace { - lastVariant: Variant | undefined - } - - const AnimatedJava: { - API: { - compileDataPack: typeof datapackCompiler - compileResourcePack: typeof resourcepackCompiler - Variant: typeof Variant - MINECRAFT_REGISTRY: typeof MINECRAFT_REGISTRY - openExportProgressDialog: typeof openExportProgressDialog - isResourcePackPath: typeof isResourcePackPath - isDataPackPath: typeof isDataPackPath - blueprintSettingErrors: typeof blueprintSettingErrors - openUnexpectedErrorDialog: typeof openUnexpectedErrorDialog - BLUEPRINT_FORMAT: typeof BLUEPRINT_FORMAT - BLUEPRINT_CODEC: typeof BLUEPRINT_CODEC - TextDisplay: typeof TextDisplay - VanillaItemDisplay: typeof VanillaItemDisplay - VanillaBlockDisplay: typeof VanillaBlockDisplay - } - } -} +export {} diff --git a/src/blueprintFormat.ts b/src/blueprintFormat.ts deleted file mode 100644 index 24515ee3..00000000 --- a/src/blueprintFormat.ts +++ /dev/null @@ -1,722 +0,0 @@ -import * as blueprintSettings from './blueprintSettings' -import FormatPageSvelte from './components/formatPage.svelte' -import ProjectTitleSvelte from './components/projectTitle.svelte' -import { PACKAGE } from './constants' -import { BillboardMode, BoneConfig, LocatorConfig } from './nodeConfigs' -import { process } from './systems/modelDataFixerUpper' -import { events } from './util/events' -import { injectSvelteCompomponent } from './util/injectSvelteComponent' -import { sanitizePathName } from './util/minecraftUtil' -import { addProjectToRecentProjects } from './util/misc' -import { Valuable } from './util/stores' -import { translate } from './util/translation' -import { Variant } from './variants' - -let boundingBoxUpdateIntervalId: ReturnType | undefined - -/** - * The serialized Variant Bone Config - */ -export interface IBlueprintBoneConfigJSON { - custom_name?: BoneConfig['customName'] - custom_name_visible?: BoneConfig['customNameVisible'] - billboard?: BoneConfig['billboard'] - override_brightness?: BoneConfig['overrideBrightness'] - brightness_override?: BoneConfig['brightnessOverride'] - enchanted?: BoneConfig['enchanted'] - glowing?: BoneConfig['glowing'] - override_glow_color?: BoneConfig['overrideGlowColor'] - glow_color?: BoneConfig['glowColor'] - inherit_settings?: BoneConfig['inheritSettings'] - invisible?: BoneConfig['invisible'] - /** - * Custom NBT for the bone that will be merged when this Variant is applied - */ - nbt?: BoneConfig['nbt'] - shadow_radius?: BoneConfig['shadowRadius'] - shadow_strength?: BoneConfig['shadowStrength'] - use_nbt?: BoneConfig['useNBT'] -} - -/** - * The serialized Variant Locator Config - */ -export interface IBlueprintLocatorConfigJSON { - use_entity?: LocatorConfig['useEntity'] - entity_type?: LocatorConfig['entityType'] - sync_passenger_rotation?: LocatorConfig['syncPassengerRotation'] - summon_commands?: LocatorConfig['_summonCommands'] - ticking_commands?: LocatorConfig['tickingCommands'] -} - -/** - * The serialized Variant Camera Config - */ -export interface IBlueprintCameraConfigJSON { - entity_type?: string - nbt?: string - ticking_commands?: string -} - -/** - * The serialized Variant Locator Config - */ -export interface IBlueprintTextDisplayConfigJSON { - billboard?: BillboardMode - override_brightness?: BoneConfig['overrideBrightness'] - brightness_override?: BoneConfig['brightnessOverride'] - glowing?: BoneConfig['glowing'] - override_glow_color?: BoneConfig['overrideGlowColor'] - glow_color?: BoneConfig['glowColor'] - invisible?: BoneConfig['invisible'] - shadow_radius?: BoneConfig['shadowRadius'] - shadow_strength?: BoneConfig['shadowStrength'] - use_nbt?: BoneConfig['useNBT'] - /** - * Custom NBT for the bone that will be merged when this Variant is applied - */ - nbt?: BoneConfig['nbt'] -} - -/** - * The serialized Variant - */ -export interface IBlueprintVariantJSON { - /** - * The display name of the Variant. Only use in Blockbench and for error messages. - */ - display_name: string - /** - * The name of the Variant - */ - name: string - /** - * The uuid of the Variant - */ - uuid: string - /** - * The texture map for the Variant - */ - texture_map: Record - /** - * The list of bones that should be ignored when applying the Variant - */ - excluded_nodes: string[] - /** - * Whether or not this is the default Variant - */ - is_default?: true -} - -/** - * The serialized Blueprint - */ -export interface IBlueprintFormatJSON { - meta: { - format: 'animated_java_blueprint' - format_version: string - uuid: string - last_used_export_namespace: string - box_uv?: boolean - backup?: boolean - save_location?: string - } - /** - * The project settings of the Blueprint - */ - blueprint_settings?: Partial['animated_java']> - /** - * The variants of the Blueprint - */ - variants: { - /** - * The default Variant of the Blueprint - */ - default: IBlueprintVariantJSON - /** - * The list of variants of the Blueprint, excluding the default Variant - */ - list: IBlueprintVariantJSON[] - } - - resolution: { - width: number - height: number - } - - elements: any[] - outliner: any[] - textures: Texture[] - animations: AnimationOptions[] - animation_controllers?: AnimationControllerOptions[] - animation_variable_placeholders: string - backgrounds?: Record -} - -//region > Convert -export function convertToBlueprint() { - // Convert the current project to a Blueprint - Project!.save_path = '' - Project!.last_used_export_namespace = '' - - for (const group of Group.all) { - group.createUniqueName(Group.all.filter(g => g !== group)) - group.sanitizeName() - } - for (const animation of Blockbench.Animation.all) { - animation.createUniqueName(Blockbench.Animation.all.filter(a => a !== animation)) - animation.name = sanitizePathName(animation.name) - } - for (const cube of Cube.all) { - cube.setUVMode(false) - } -} - -export function getDefaultProjectSettings(): ModelProject['animated_java'] { - return { ...blueprintSettings.defaultValues } -} - -function initializeBoundingBoxUpdate() { - if (boundingBoxUpdateIntervalId == undefined) { - boundingBoxUpdateIntervalId = setInterval(() => { - updateBoundingBox() - }, 500) - events.UNLOAD.subscribe(() => clearInterval(boundingBoxUpdateIntervalId), true) - events.UNINSTALL.subscribe(() => clearInterval(boundingBoxUpdateIntervalId), true) - } -} - -export function updateBoundingBox() { - if (!Project || !isCurrentFormat()) return - if (Project.visualBoundingBox) scene.remove(Project.visualBoundingBox) - - if (!Project.animated_java.show_bounding_box) return - - let width = 0 - let height = 0 - if (Project.animated_java.auto_bounding_box) { - for (const cube of Cube.all) { - width = Math.max( - width, - Math.abs(cube.to[0]), - Math.abs(cube.to[2]), - Math.abs(cube.from[0]), - Math.abs(cube.from[2]) - ) - height = Math.max(height, cube.to[1], cube.from[1]) - } - const boundingBoxOverflow = 8 - width += boundingBoxOverflow - height += boundingBoxOverflow - } else { - width = Project.animated_java.bounding_box[0] - height = Project.animated_java.bounding_box[1] - } - - const boundingBox = new THREE.BoxGeometry(width * 2, height, width * 2) - Project.visualBoundingBox = new THREE.LineSegments( - new THREE.EdgesGeometry(boundingBox), - new THREE.LineBasicMaterial({ color: '#855000' }) - ) - Project.visualBoundingBox.position.set(0, height / 2, 0) - scene.add(Project.visualBoundingBox) -} - -// region Codec -export const BLUEPRINT_CODEC = new Blockbench.Codec('animated_java_blueprint', { - name: 'Animated Java Blueprint', - extension: 'ajblueprint', - remember: true, - load_filter: { - extensions: ['ajblueprint'], - type: 'json', - }, - - // region > load - load(model: IBlueprintFormatJSON, file) { - console.log(`Loading Animated Java Blueprint from '${file.name}'...`) - model = process(model) - setupProject(BLUEPRINT_FORMAT, model.meta.uuid) - if (!Project) { - throw new Error('Failed to load Animated Java Blueprint') - } - addProjectToRecentProjects(file) - BLUEPRINT_CODEC.parse!(model, file.path) - console.log( - `Successfully loaded Animated Java Blueprint\n\tProject: ${Project.name}\n\t${Project.uuid}` - ) - }, - - // region > parse - // Takes the model file and injects it's data into the global Project - parse(model: IBlueprintFormatJSON, path) { - console.log(`Parsing Animated Java Blueprint from '${path}'...`) - if (!Project) throw new Error('No project to parse into') - - Project.loadingPromises = [] - - Project.save_path = path - - if (model.meta.box_uv !== undefined) { - Project.box_uv = model.meta.box_uv - } - - if (model.resolution !== undefined) { - Project.texture_width = model.resolution.width - Project.texture_height = model.resolution.height - } - - // Misc Project Properties - for (const key in ModelProject.properties) { - ModelProject.properties[key].merge(Project, model) - } - - if (model.blueprint_settings) { - Project.animated_java = { - ...blueprintSettings.defaultValues, - ...model.blueprint_settings, - } - } - - Project.last_used_export_namespace = - model.meta.last_used_export_namespace ?? Project.animated_java.export_namespace - - if (model.textures) { - for (const texture of model.textures) { - const newTexture = new Texture(texture, texture.uuid).add(false) - if (texture.relative_path && Project.save_path) { - const resolvedPath = PathModule.resolve( - Project.save_path, - texture.relative_path - ) - if (fs.existsSync(resolvedPath)) { - newTexture.fromPath(resolvedPath) - continue - } - } - if (texture.path && fs.existsSync(texture.path) && !model.meta.backup) { - newTexture.fromPath(texture.path) - } else if (texture.source && texture.source.startsWith('data:')) { - newTexture.fromDataURL(texture.source) - } - } - } - - if (model.elements) { - const defaultTexture = Texture.getDefault() - for (const element of model.elements) { - const newElement = OutlinerElement.fromSave(element, true) - switch (true) { - case newElement instanceof Cube: { - for (const face in newElement.faces) { - if (element.faces) { - const texture = - element.faces[face].texture !== undefined && - Texture.all[element.faces[face].texture] - if (texture) { - newElement.faces[face].texture = texture.uuid - } - } else if ( - defaultTexture && - newElement.faces && - newElement.faces[face].texture !== undefined - ) { - newElement.faces[face].texture = defaultTexture.uuid - } - } - break - } - case newElement instanceof AnimatedJava.API.TextDisplay: - case newElement instanceof AnimatedJava.API.VanillaItemDisplay: - case newElement instanceof AnimatedJava.API.VanillaBlockDisplay: { - // ES-Lint doesn't like the types here for some reason, so I'm casing them to please it. - Project.loadingPromises.push(newElement.waitForReady() as Promise) - break - } - } - } - } - - if (model.outliner) { - parseGroups(model.outliner) - - for (const group of Group.all) { - group.name = sanitizePathName(group.name) - } - } - - if (model.variants) { - Variant.fromJSON(model.variants.default, true) - for (const variantJSON of model.variants.list) { - Variant.fromJSON(variantJSON) - } - Project.variants = Variant.all - } else { - new Variant('Default', true) - } - - if (model.animations) { - for (const animation of model.animations) { - const newAnimation = new Blockbench.Animation() - newAnimation.uuid = animation.uuid || guid() - newAnimation.extend(animation).add() - } - } - - if (model.animation_controllers) { - for (const controller of model.animation_controllers) { - const newController = new Blockbench.AnimationController() - newController.uuid = controller.uuid || guid() - newController.extend(controller).add() - } - } - - if (model.animation_variable_placeholders) { - Interface.Panels.variable_placeholders.inside_vue.$data.text = - model.animation_variable_placeholders - } - - if (model.backgrounds) { - for (const key in model.backgrounds) { - if (Object.hasOwn(Project.backgrounds, key)) { - const store = model.backgrounds[key] - const real = Project.backgrounds[key] - - if (store.image !== undefined) { - real.image = store.image - } - if (store.size !== undefined) { - real.size = store.size - } - if (store.x !== undefined) { - real.x = store.x - } - if (store.y !== undefined) { - real.y = store.y - } - if (store.lock !== undefined) { - real.lock = store.lock - } - } - } - Preview.all.forEach(p => { - if (p.canvas.isConnected) { - p.loadBackground() - } - }) - } - - Canvas.updateAll() - Validator.validate() - BLUEPRINT_CODEC.dispatchEvent('parsed', { model }) - }, - - // region > compile - compile(options) { - if (!options) options = {} - console.log(`Compiling Animated Java Blueprint from ${Project!.name}...`) - if (!Project) throw new Error('No project to compile.') - - const model = { - meta: { - format: BLUEPRINT_FORMAT.id, - format_version: PACKAGE.version, - uuid: Project.uuid, - save_location: Project.save_path, - last_used_export_namespace: Project.last_used_export_namespace, - }, - blueprint_settings: { ...Project.animated_java }, - resolution: { - width: Project.texture_width || 16, - height: Project.texture_height || 16, - }, - } as IBlueprintFormatJSON - - console.log(Project.animated_java) - - // Disable variants while compiling - const previouslySelectedVariant = Variant.selected - Variant.selectDefault() - - for (const key in ModelProject.properties) { - if (ModelProject.properties[key].export) - ModelProject.properties[key].copy(Project, model) - } - - model.elements = [] - for (const element of elements) { - model.elements.push(element.getSaveCopy && element.getSaveCopy(!!model.meta)) - } - - model.outliner = compileGroups(true) - - model.textures = [] - for (const texture of Texture.all) { - const save = texture.getSaveCopy() as Texture - delete save.selected - if (isApp && Project.save_path && texture.path && PathModule.isAbsolute(texture.path)) { - const relative = PathModule.relative( - PathModule.dirname(Project.save_path), - texture.path - ) - save.relative_path = relative.replace(/\\/g, '/') - } - if ( - options.bitmaps != false && - (Settings.get('embed_textures') || options.backup || options.bitmaps == true) - ) { - save.source = texture.getDataURL() - save.internal = true - } - if (options.absolute_paths == false) delete save.path - model.textures.push(save) - } - - model.variants = { - default: Variant.all.find(v => v.isDefault)!.toJSON(), - list: Variant.all.filter(v => !v.isDefault).map(v => v.toJSON()), - } - - model.animations = [] as any - const animationOptions = { bone_names: true, absolute_paths: options.absolute_paths } - for (const animation of Blockbench.Animation.all) { - if (!animation.getUndoCopy) continue - model.animations.push(animation.getUndoCopy(animationOptions, true)) - } - - model.animation_controllers = [] - for (const controller of Blockbench.AnimationController.all) { - if (!controller.getUndoCopy) continue - model.animation_controllers.push(controller.getUndoCopy(animationOptions, true)) - } - - if (Interface.Panels.variable_placeholders.inside_vue.$data.text) { - model.animation_variable_placeholders = - Interface.Panels.variable_placeholders.inside_vue.$data.text - } - - if (!options.backup) { - const backgrounds: Record = {} - for (const key in Project.backgrounds) { - const scene = Project.backgrounds[key] - if (scene.image) { - backgrounds[key] = scene.getSaveCopy() - } - } - if (Object.keys(backgrounds).length) { - model.backgrounds = backgrounds - } - } - - previouslySelectedVariant?.select() - - return options.raw ? model : compileJSON(model) - }, - - // region > export - export() { - console.log(`Exporting Animated Java Blueprint for ${Project!.name}...`) - if (!Project) throw new Error('No project to export.') - Blockbench.export({ - resource_id: 'animated_java_blueprint.export', - name: (Project.name || 'unnamed') + '.ajblueprint', - startpath: Project.save_path, - type: 'json', - extensions: [BLUEPRINT_CODEC.extension], - content: BLUEPRINT_CODEC.compile(), - // eslint-disable-next-line @typescript-eslint/naming-convention - custom_writer: (content, path) => { - if (fs.existsSync(PathModule.dirname(path))) { - Project!.save_path = path - BLUEPRINT_CODEC.write(content, path) - } else { - console.error( - `Failed to export Animated Java Blueprint, file location '${path}' does not exist!` - ) - Blockbench.showMessageBox({ - title: translate('error.blueprint_export_path_doesnt_exist.title'), - message: translate('error.blueprint_export_path_doesnt_exist', path), - }) - } - }, - }) - }, - - // ANCHOR - Codec:fileName - fileName() { - if (!Project || !Project.name) return 'unnamed_project.ajblueprint' - return `${Project.name}.ajblueprint` - }, -}) - -// region Format -export const BLUEPRINT_FORMAT = new Blockbench.ModelFormat({ - id: 'animated_java_blueprint', - name: 'Animated Java Blueprint', - icon: 'icon-armor_stand', - category: 'animated_java', - target: 'Minecraft: Java Edition', - confidential: false, - condition: () => true, - show_on_start_screen: true, - format_page: { - component: { - methods: {}, - created() { - void injectSvelteCompomponent({ - elementSelector: () => $('#format_page_animated_java_blueprint_mount')[0], - component: FormatPageSvelte, - props: {}, - }) - }, - template: `
`, - }, - }, - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - onSetup(project, newModel) { - if (!Project) return - console.log('Animated Java Blueprint format setup') - - const defaults = getDefaultProjectSettings() - if (newModel) { - Project.animated_java = defaults - Project.last_used_export_namespace = '' - } else { - Project.animated_java = { ...defaults, ...Project!.animated_java } - } - - const thisProject = Project - Project.variants ??= [] - - initializeBoundingBoxUpdate() - - Project.loadingPromises ??= [] - Project.loadingPromises.push( - new Promise(resolve => { - requestAnimationFrame(() => { - thisProject.pluginMode = new Valuable( - thisProject.animated_java.enable_plugin_mode - ) - // Remove the default title - const element = document.querySelector('#tab_bar_list .icon-armor_stand.icon') - element?.remove() - // Custom title - void injectSvelteCompomponent({ - elementSelector: () => { - const titles = [ - ...document.querySelectorAll( - `.project_tab[title="${project.name}"]` - ), - ] - if (titles.length) { - return titles[0] - } - }, - prepend: true, - component: ProjectTitleSvelte, - props: { pluginMode: thisProject.pluginMode }, - }) - - if (Variant.all.length === 0) new Variant('Default', true) - Variant.selectDefault() - }) - resolve() - }) - ) - }, - - // onActivation() { - // console.group('Animated Java Blueprint format activated') - // }, - - // onDeactivation() { - // console.group('Animated Java Blueprint format deactivated') - // }, - - codec: BLUEPRINT_CODEC, - - animated_textures: true, - animation_controllers: true, - animation_files: true, - texture_mcmeta: true, - animation_mode: true, - bone_binding_expression: true, - bone_rig: true, - box_uv: false, - centered_grid: true, - display_mode: false, - edit_mode: true, - integer_size: false, - java_face_properties: true, - locators: true, - meshes: false, - model_identifier: false, - optional_box_uv: true, - paint_mode: true, - parent_model_id: false, - pose_mode: false, - render_sides: 'front', - rotate_cubes: true, - rotation_limit: false, - select_texture_for_particles: false, - single_texture: false, - texture_folder: false, - texture_meshes: false, - uv_rotation: true, - vertex_color_ambient_occlusion: true, - java_cube_shading_properties: true, - box_uv_float_size: false, - cullfaces: true, -}) -BLUEPRINT_CODEC.format = BLUEPRINT_FORMAT - -export function isCurrentFormat() { - return Format.id === BLUEPRINT_FORMAT.id -} - -export function saveBlueprint() { - if (!Project || !Format) return - if (Format !== BLUEPRINT_FORMAT) return - BLUEPRINT_CODEC.write(BLUEPRINT_CODEC.compile(), Project.save_path) -} - -export function updateRotationLock() { - if (!isCurrentFormat()) return - BLUEPRINT_FORMAT.rotation_limit = !( - !!Group.first_selected || - !!AnimatedJava.API.TextDisplay.selected.length || - !!AnimatedJava.API.VanillaItemDisplay.selected.length || - !!AnimatedJava.API.VanillaBlockDisplay.selected.length || - !!(OutlinerElement.types.camera?.selected && OutlinerElement.types.camera?.selected) - ) - BLUEPRINT_FORMAT.rotation_snap = BLUEPRINT_FORMAT.rotation_limit -} - -export function disableRotationLock() { - if (!isCurrentFormat()) return - BLUEPRINT_FORMAT.rotation_limit = false - BLUEPRINT_FORMAT.rotation_snap = false -} - -events.SELECT_PROJECT.subscribe(project => { - if (project.format.id === BLUEPRINT_FORMAT.id) { - events.SELECT_AJ_PROJECT.dispatch(project) - } -}) -events.UNSELECT_PROJECT.subscribe(project => { - if (project.format.id === BLUEPRINT_FORMAT.id) { - events.UNSELECT_AJ_PROJECT.dispatch(project) - } -}) -events.UPDATE_SELECTION.subscribe(updateRotationLock) -events.SELECT_AJ_PROJECT.subscribe(() => { - requestAnimationFrame(() => { - updateBoundingBox() - updateRotationLock() - }) -}) -events.UNSELECT_AJ_PROJECT.subscribe(project => { - if (project.visualBoundingBox) scene.remove(project.visualBoundingBox) - disableRotationLock() -}) diff --git a/src/blueprintSettings.ts b/src/blueprintSettings.ts deleted file mode 100644 index f9f4e5e6..00000000 --- a/src/blueprintSettings.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MinecraftVersion } from './systems/global' -import { Valuable } from './util/stores' - -export type ExportMode = 'raw' | 'zip' | 'none' - -export const defaultValues = { - export_namespace: 'blueprint', - show_bounding_box: false, - auto_bounding_box: true, - bounding_box: [48, 48], - // Export Settings - enable_plugin_mode: false, - resource_pack_export_mode: 'raw' as ExportMode, - data_pack_export_mode: 'raw' as ExportMode, - target_minecraft_versions: ['1.21.4'] as MinecraftVersion[], - // Resource Pack Settings - display_item: 'minecraft:white_dye', - custom_model_data_offset: 0, - enable_advanced_resource_pack_settings: false, - resource_pack: '', - // Data Pack Settings - enable_advanced_data_pack_settings: false, - data_pack: '', - summon_commands: '', - remove_commands: '', - ticking_commands: '', - interpolation_duration: 1, - teleportation_duration: 1, - use_storage_for_animation: false, - show_function_errors: true, - show_outdated_warning: true, - // Plugin Settings - baked_animations: true, - json_file: '', -} - -export const blueprintSettingErrors = new Valuable>({}) diff --git a/src/components/animatedJavaLoadingPopup.svelte b/src/components/animatedJavaLoadingPopup.svelte index 531a0474..0a9e9f45 100644 --- a/src/components/animatedJavaLoadingPopup.svelte +++ b/src/components/animatedJavaLoadingPopup.svelte @@ -1,5 +1,4 @@ diff --git a/src/components/animationProperties.svelte b/src/components/animationProperties.svelte index 35fe7b57..547a0aa1 100644 --- a/src/components/animationProperties.svelte +++ b/src/components/animationProperties.svelte @@ -1,22 +1,22 @@ - - - -
- - - -
- - diff --git a/src/components/blueprintSettingsDialog.svelte b/src/components/blueprintSettingsDialog.svelte index 9cc9fda1..63146e3a 100644 --- a/src/components/blueprintSettingsDialog.svelte +++ b/src/components/blueprintSettingsDialog.svelte @@ -1,9 +1,10 @@ - - - let showSupportMePopup = - localStorage.getItem('animated_java_settings_support_me_popup') === 'true' - function clickSupportMeXButton() { - localStorage.setItem('animated_java_settings_support_me_popup', 'false') - showSupportMePopup = false - } + + export let onSummonFunction: Valuable + export let onRemoveFunction: Valuable + export let onPreTickFunction: Valuable + export let onPostTickFunction: Valuable -
+ export let interpolationDuration: Valuable + export let teleportationDuration: Valuable + export let useStorageForAnimation: Valuable + export let autoUpdateRigOrientation: Valuable + // Plugin Export Settings + export let bakedAnimations: Valuable + export let jsonFile: Valuable -
- + const DISPLAY_ITEM_REQUIRED = new Valuable(false) + $: { + $DISPLAY_ITEM_REQUIRED = compareVersions('1.21.2', $targetMinecraftVersion) + } + + + + + + + + + + + +{#if !$autoRenderBox} - - - - +{/if} - {#if !$autoBoundingBox} - - {/if} + - + - - - + /> --> - {#if $enablePluginMode} - - - +{#if $enablePluginMode} + - - {:else} - + - - + {#if $resourcePackExportMode !== 'none'} + {#if $DISPLAY_ITEM_REQUIRED} - - - - {#if $enableAdvancedResourcePackSettings} -

- {translate('dialog.blueprint_settings.advanced_settings_warning')} -

- - - {/if} - - {#if $resourcePackExportMode === 'raw'} - - {:else if $resourcePackExportMode === 'zip'} - - {/if} {/if} - {#if $dataPackExportMode !== 'none'} - - - {#if $dataPackExportMode === 'raw'} - - {:else if $dataPackExportMode === 'zip'} - - {/if} - - + {/if} - + - + {#if $enableAdvancedResourcePackSettings} +

+ {translate('dialog.blueprint_settings.advanced_settings_warning')} +

+ {/if} + {/if} - + - + + + + + + + + + + + + + + + + + +{:else} + + + + + + + + + {#if $useNBT} +

+ {translate('dialog.bone_config.use_nbt.use_nbt_warning')} +

+ + {:else} - - - - - - {#if $overrideGlowColor} - - {/if} - - - - - - - - {#if $overrideBrightness} - - {/if} - - - - - {/if} + /> --> {/if} -
+{/if} diff --git a/src/components/customCodeJar.svelte b/src/components/customCodeJar.svelte new file mode 100644 index 00000000..8ad75a94 --- /dev/null +++ b/src/components/customCodeJar.svelte @@ -0,0 +1,116 @@ + + +
+ forceNoWrap()} + preserveIdent + history + class={'language-' + (syntax ?? 'plaintext')} + style=" + font-family: var(--font-code); + font-size: 14px; + padding: 3px 6px; + max-height: 20em; + height: fit-content; + padding-bottom: 1rem; + width: 100%; + outline: none; + overflow-wrap: unset; + overflow-y: scroll; + white-space: pre; + margin: 0; + border: 1px solid var(--color-border); + border-radius: 0; + text-shadow: 0px 1px rgba(0, 0, 0, 0.3); + " + /> + {#if placeholder && (!value || value.length === 0)} +
{placeholder}
+ {/if} +
+ + diff --git a/src/components/customKeyframePanel.svelte b/src/components/customKeyframePanel.svelte deleted file mode 100644 index c06b77cb..00000000 --- a/src/components/customKeyframePanel.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - - -
- {#if selectedKeyframe.channel === 'variant'} - - {:else if selectedKeyframe.channel === 'commands'} - - {:else} -

Unknown keyframe channel: '{selectedKeyframe.channel}'

- {/if} -
- - -
-
- - diff --git a/src/components/dialogItems/baseDialogItem.svelte b/src/components/dialogItems/baseDialogItem.svelte index c570590f..aa24148b 100644 --- a/src/components/dialogItems/baseDialogItem.svelte +++ b/src/components/dialogItems/baseDialogItem.svelte @@ -1,28 +1,46 @@ -
+
+ +
+ {#if errorText} +
+ +
+ + {@html pureMarked(errorText)} +
+
+ {:else if warningText} +
+ + + {@html pureMarked(warningText)} +
+ {/if} + {#if tooltip} + + {@html pureMarked(tooltip)} + {/if} +
-
- {#if error_text} -
- -
- {#each error_text.split('\n') as text} -
{text}
- {/each} -
-
- {:else if warning_text} -
- -
- {#each warning_text.split('\n') as text} -
{text}
- {/each} -
-
- {/if} -
- {#if tooltip} -
- {tooltip} -
- {/if}
diff --git a/src/components/dialogItems/collection.svelte b/src/components/dialogItems/collection.svelte index ed822d30..fcbdf2f4 100644 --- a/src/components/dialogItems/collection.svelte +++ b/src/components/dialogItems/collection.svelte @@ -1,10 +1,10 @@ - +

{availableItemsColumnLable}

@@ -84,7 +84,7 @@ {/if} {item.icon || 'folder'}{item.icon ?? 'folder'}
{item.title}
@@ -123,7 +123,7 @@ {/if} {item.icon || 'folder'}{item.icon ?? 'folder'}
{item.title}
@@ -148,7 +148,7 @@ border: 1px solid var(--color-border); margin: 8px; margin-top: 0px; - padding: 4px 8px 30px; + padding: 6px 8px 30px; max-height: 16rem; overflow-y: auto; } diff --git a/src/components/dialogItems/colorPicker.svelte b/src/components/dialogItems/colorPicker.svelte index ba5dc360..52441ceb 100644 --- a/src/components/dialogItems/colorPicker.svelte +++ b/src/components/dialogItems/colorPicker.svelte @@ -5,12 +5,11 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable let colorPicker = new ColorPicker(`${PACKAGE.name}:${label}-color_picker`, { onChange() { - // @ts-ignore const color = colorPicker.get() as tinycolor.Instance value.set(color.toHexString()) }, diff --git a/src/components/dialogItems/dialogItemUtil.d.ts b/src/components/dialogItems/dialogItemUtil.d.ts index 4ff027dc..d04230b1 100644 --- a/src/components/dialogItems/dialogItemUtil.d.ts +++ b/src/components/dialogItems/dialogItemUtil.d.ts @@ -2,4 +2,8 @@ type DialogItemValueChecker = | ((value: Value) => { type: string; message: string }) | undefined -type CollectionItem = { icon?: string; name: string; value: string } +interface CollectionItem { + icon?: string + name: string + value: string +} diff --git a/src/components/dialogItems/fileSelect.svelte b/src/components/dialogItems/fileSelect.svelte index 63981472..1c5dcf37 100644 --- a/src/components/dialogItems/fileSelect.svelte +++ b/src/components/dialogItems/fileSelect.svelte @@ -4,24 +4,24 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable export let defaultValue: string export let filters: FileFilter[] = [] - export let fileSelectMessage: string = 'Select File' + export let fileSelectMessage = 'Select File' let _value: string = value.get() export let valueChecker: DialogItemValueChecker = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker(value.get()) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.subscribe(() => checkValue()) @@ -31,8 +31,7 @@ } function selectFile() { - Promise.any([ - // @ts-ignore + void Promise.any([ electron.dialog.showOpenDialog({ properties: ['openFile', 'promptToCreate'], filters, @@ -54,7 +53,7 @@ onValueChange() - +
export let defaultValue: string export let filters: FileFilter[] = [] - export let fileSelectMessage: string = 'Select Folder' + export let fileSelectMessage = 'Select Folder' let _value: string = value.get() export let valueChecker: DialogItemValueChecker = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker(value.get()) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.subscribe(() => checkValue()) @@ -31,8 +31,7 @@ } function selectFile() { - Promise.any([ - // @ts-ignore + void Promise.any([ electron.dialog.showOpenDialog({ properties: ['openDirectory'], filters, @@ -54,7 +53,7 @@ onValueChange() - +
export let defaultValue: string - export let disabled: boolean = false + export let disabled = false export let valueChecker: DialogItemValueChecker = undefined let _value: string = value.get() - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function onValueChange() { if (valueChecker) { const result = valueChecker(_value) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.set(_value) @@ -33,7 +33,7 @@ onValueChange() - +
import MultiSelect, { type ObjectOption } from 'svelte-multiselect' import { Valuable } from '../../util/stores' - import { translate } from '../../util/translation' import BaseDialogItem from './baseDialogItem.svelte' type StringOption = ObjectOption & { value: string } export let label: string - export let tooltip: string = '' + export let tooltip = '' export let options: StringOption[] export let defaultValue: StringOption[] export let value: Valuable @@ -16,8 +15,6 @@ .filter(v => options.find(o => o.value == v)) .map(v => options.find(o => o.value == v)!) - let warning_text = '' - if (selected.length === 0) { selected = defaultValue } @@ -26,19 +23,6 @@ selected = options.filter(o => v.includes(o.value)) }) - $: { - value.set(selected.map(v => v.value)) - // FIXME - This warning is unique to the targeted minecraft version setting. - // Since the only use of this component is in the targeted minecraft version setting, this is fine... for now. - if (selected.length > 1) { - warning_text = translate( - 'dialog.blueprint_settings.target_minecraft_versions.warning.multiple_versions' - ) - } else { - warning_text = '' - } - } - function sortByIndex(a: StringOption, b: StringOption) { return options.indexOf(a) - options.indexOf(b) } @@ -48,7 +32,7 @@ } - +
import { onDestroy } from 'svelte' - import { roundTo } from '../../util/misc' + import { roundToNth } from '../../util/misc' import { Valuable } from '../../util/stores' import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable export let defaultValue: number export let min = -Infinity @@ -19,7 +19,7 @@ let slider: HTMLElement const clampValue = (v: number) => { - return Math.clamp(roundTo(v, 1 / (valueStep ?? 1)), min, max) || 0 + return Math.clamp(roundToNth(v, 1 / (valueStep ?? 1)), min, max) || 0 } const onStartDrag = (moveEvent: MouseEvent | TouchEvent) => { @@ -77,9 +77,18 @@ bind:value={$value} inputmode="decimal" /> -
+
code
+ + diff --git a/src/components/dialogItems/sectionHeader.svelte b/src/components/dialogItems/sectionHeader.svelte index 668db02f..5050ea22 100644 --- a/src/components/dialogItems/sectionHeader.svelte +++ b/src/components/dialogItems/sectionHeader.svelte @@ -14,10 +14,10 @@ align-items: center; } h4 { - padding: 4px 8px; - flex-grow: 1; - font-size: 1.12em; - border-bottom: 2px solid var(--color-back); + padding: 6px 24px; + font-size: 24px; + border-bottom: 5px solid var(--color-back); background-color: var(--color-button); + font-family: 'MinecraftFull'; } diff --git a/src/components/dialogItems/select.svelte b/src/components/dialogItems/select.svelte index 3b0a82ac..c94efb61 100644 --- a/src/components/dialogItems/select.svelte +++ b/src/components/dialogItems/select.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let options: Record export let defaultOption: string export let value: Valuable @@ -12,12 +12,11 @@ if (!(value.get() || options[value.get()])) value.set(defaultOption) - // @ts-ignore - const selectInput = new Interface.CustomElements.SelectInput('dialog-select', { + const SELECT_ELEMENT = new Interface.CustomElements.SelectInput('dialog-select', { options, value: value.get(), onChange() { - const v = selectInput.node.getAttribute('value') + const v = SELECT_ELEMENT.node.getAttribute('value') if (v == undefined) { console.warn('Select value is undefined') return @@ -28,13 +27,13 @@ function onReset() { value.set(defaultOption) - if (selectInput.node) { - selectInput.set(defaultOption) + if (SELECT_ELEMENT.node) { + SELECT_ELEMENT.set(defaultOption) } } requestAnimationFrame(() => { - container.appendChild(selectInput.node) + container.appendChild(SELECT_ELEMENT.node) }) diff --git a/src/components/dialogItems/vector2d.svelte b/src/components/dialogItems/vector2d.svelte index 63dd2f5e..2947cd85 100644 --- a/src/components/dialogItems/vector2d.svelte +++ b/src/components/dialogItems/vector2d.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let step: number | undefined = undefined @@ -19,19 +19,19 @@ export let valueChecker: DialogItemValueChecker<{ x: number; y: number }> = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker({ x: valueX.get(), y: valueY.get() }) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } valueX.subscribe(() => checkValue()) valueY.subscribe(() => checkValue()) - const molangParser = new Molang() + const MOLANG_PARSER = new Molang() let inputX: HTMLInputElement let sliderX: HTMLElement @@ -47,22 +47,22 @@ ) { addEventListeners(target, 'mousedown touchstart', (e1: any) => { convertTouchEvent(e1) - let last_difference = 0 + let lastDifference = 0 function move(e2: any) { convertTouchEvent(e2) - let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step || 1) - if (difference != last_difference) { + let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step ?? 1) + if (difference != lastDifference) { value.set( Math.clamp( - value.get() + (difference - last_difference), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity + value.get() + (difference - lastDifference), + min ?? -Infinity, + max ?? Infinity ) ) - last_difference = difference + lastDifference = difference } } - function stop(e2: any) { + function stop() { removeEventListeners(document, 'mousemove touchmove', move) removeEventListeners(document, 'mouseup touchend', stop) } @@ -72,11 +72,7 @@ addEventListeners(inputX, 'focusout dblclick', () => { value.set( - Math.clamp( - molangParser.parse(value.get()), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity - ) + Math.clamp(MOLANG_PARSER.parse(value.get()), min ?? -Infinity, max ?? Infinity) ) }) } @@ -92,7 +88,7 @@ }) - +
diff --git a/src/components/dialogItems/vector3d.svelte b/src/components/dialogItems/vector3d.svelte index fedd06ba..ba5e985d 100644 --- a/src/components/dialogItems/vector3d.svelte +++ b/src/components/dialogItems/vector3d.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let step: number | undefined = undefined @@ -24,20 +24,20 @@ export let valueChecker: DialogItemValueChecker<{ x: number; y: number; z: number }> = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker({ x: valueX.get(), y: valueY.get(), z: valueZ.get() }) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } valueX.subscribe(() => checkValue()) valueY.subscribe(() => checkValue()) valueZ.subscribe(() => checkValue()) - const molangParser = new Molang() + const MOLANG_PARSER = new Molang() let inputX: HTMLInputElement let sliderX: HTMLElement @@ -56,22 +56,22 @@ ) { addEventListeners(target, 'mousedown touchstart', (e1: any) => { convertTouchEvent(e1) - let last_difference = 0 + let lastDifference = 0 function move(e2: any) { convertTouchEvent(e2) - let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step || 1) - if (difference != last_difference) { + let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step ?? 1) + if (difference != lastDifference) { value.set( Math.clamp( - value.get() + (difference - last_difference), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity + value.get() + (difference - lastDifference), + min ?? -Infinity, + max ?? Infinity ) ) - last_difference = difference + lastDifference = difference } } - function stop(e2: any) { + function stop() { removeEventListeners(document, 'mousemove touchmove', move) removeEventListeners(document, 'mouseup touchend', stop) } @@ -81,11 +81,7 @@ addEventListeners(inputX, 'focusout dblclick', () => { value.set( - Math.clamp( - molangParser.parse(value.get()), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity - ) + Math.clamp(MOLANG_PARSER.parse(value.get()), min ?? -Infinity, max ?? Infinity) ) }) } @@ -103,7 +99,7 @@ }) - +
diff --git a/src/components/formatPage.svelte b/src/components/formatPage.svelte index 6e5097ea..3903bcb7 100644 --- a/src/components/formatPage.svelte +++ b/src/components/formatPage.svelte @@ -1,45 +1,5 @@

Create advanced animated models for Vanilla Java Edition

@@ -59,23 +19,8 @@ > to learn how to use Animated Java.

-

-

Good to know:

-

-
    -
  • - The rotations of elements are limited to 22.5 degree steps, and only one axis can be rotated - at a time. However, the rotation of bones is not restricted. -
  • -
  • - Documentation for Animated Java can be found at https://animated-java.dev/docs -
  • -
-
- @@ -86,7 +31,4 @@ width: 100%; height: 40px; } - li { - margin-left: 24px; - } diff --git a/src/components/icon.svelte b/src/components/icon.svelte index 80c5d07b..1ba31025 100644 --- a/src/components/icon.svelte +++ b/src/components/icon.svelte @@ -2,9 +2,7 @@ import AnimatedJavaIcon from '../assets/animated_java_icon.svg' - - - + diff --git a/src/components/incompatabilityPopup.svelte b/src/components/incompatabilityPopup.svelte index 54ca9b8d..9dc66b2a 100644 --- a/src/components/incompatabilityPopup.svelte +++ b/src/components/incompatabilityPopup.svelte @@ -4,13 +4,10 @@ export let plugins: BBPlugin[] - let disabledPlugins: BBPlugin[] = [] - function disablePlugin(plugin: BBPlugin) { plugin.toggleDisabled() plugins.remove(plugin) - disabledPlugins.push(plugin) - disabledPlugins = disabledPlugins + plugins = [...plugins] if (plugins.length === 0) { closeIncompatabilityPopup() } @@ -22,33 +19,36 @@

    {#each plugins as plugin} -
  • - {#if plugin.hasImageIcon()} - - {/if} - {plugin.title} - {#key disabledPlugins} - {#if !disabledPlugins.includes(plugin)} - + {#if !plugin.disabled} +
  • + {#if plugin.hasImageIcon()} + {/if} - {/key} -
  • + {plugin.title} + + + {/if} {/each}
diff --git a/src/components/installedPopup.svelte b/src/components/installedPopup.svelte index 304049ee..9f0dfc72 100644 --- a/src/components/installedPopup.svelte +++ b/src/components/installedPopup.svelte @@ -8,10 +8,7 @@

Welcome to Animated Java!

-

- A Blockbench plugin that makes complex animation a breeze in Minecraft: Java - Edition. -

+

Effortlessly craft complex animations for Minecraft: Java Edition

@@ -25,10 +22,10 @@

You can open our docs at any time via the Animated Java menu at the top of the screen.

-

Need more help?

+

Need Help?

- Feel free to join our Discord server to ask - questions via our support channel. + Join our Discord server to ask questions + via our support channel.

diff --git a/src/components/keyframeEasings.svelte b/src/components/keyframeEasings.svelte index 306ddd97..00ffd07e 100644 --- a/src/components/keyframeEasings.svelte +++ b/src/components/keyframeEasings.svelte @@ -1,29 +1,26 @@ - + + -
+
- + {/if}
- {#if $error} - - {/if}
diff --git a/src/components/vanillaBlockDisplayElementPanel.svelte b/src/components/vanillaBlockDisplayElementPanel.svelte index 9d35b5f1..52cb972d 100644 --- a/src/components/vanillaBlockDisplayElementPanel.svelte +++ b/src/components/vanillaBlockDisplayElementPanel.svelte @@ -1,9 +1,11 @@ - + - - -
- {#if pluginModeEnabled} - + + + + + + + + + + + + + + + + + + +{:else} + + + {#if $useNBT} +

+ {translate('dialog.bone_config.use_nbt.use_nbt_warning')} +

+ + {:else} + + - - - - - - {#if $overrideGlowColor} - - {/if} - - - + {#if $overrideBrightness} + {/if} - - - {#if $overrideBrightness} - - {/if} - - - {/if} {/if} -
+{/if} diff --git a/src/components/vanillaItemDisplayElementPanel.svelte b/src/components/vanillaItemDisplayElementPanel.svelte index be37fc01..bb47c520 100644 --- a/src/components/vanillaItemDisplayElementPanel.svelte +++ b/src/components/vanillaItemDisplayElementPanel.svelte @@ -1,10 +1,12 @@ - + @@ -129,10 +129,6 @@ defaultValue={true} /> -
- {$uuid} -
- {#if !variant.isDefault}
@@ -143,24 +139,17 @@
{}} + on:click={() => createTextureMapping()} > - createTextureMapping()}>add + add
- - - { - const tooltip = translate('dialog.variant_config.texture_map.description') - Blockbench.showQuickMessage(tooltip, 50 * tooltip.length) - }} - />
- - {#key textureMapUpdated} - {#each [...textureMap.map.entries()] as entry, index} + {#key textureMapUpdated} +
    + {#each [...textureMap.map.entries()] as entry}
  • @@ -171,8 +160,7 @@ class="texture-mapping-item-dropdown" on:change={e => selectNewPrimaryTexture(e, entry[0])} > - - {#each primaryTextures as texture} + {#each PRIMARY_TEXTURES as texture} @@ -190,8 +178,7 @@ class="texture-mapping-item-dropdown" on:change={e => selectNewSecondaryTexture(e, entry[0])} > - - {#each secondaryTextures as texture} + {#each SECONDARY_TEXTURES as texture} @@ -207,35 +194,45 @@
  • {:else}
    - {translate('dialog.variant_config.texture_map.no-mappings')} + {translate('dialog.variant_config.texture_map.no_mappings')}
    {/each} - {/key} - +
+ {/key} +
+ {@html translate('dialog.variant_config.texture_map.description')} +
+ {/if} + +
+ {$uuid} +
diff --git a/src/components/variantsPanel.svelte b/src/components/variantsPanel.svelte index 437a2d21..636c265f 100644 --- a/src/components/variantsPanel.svelte +++ b/src/components/variantsPanel.svelte @@ -1,58 +1,67 @@ -
-
- -
createVariant(e)} - > - texture_add -
- -
duplicateVariant(e)} - > - content_copy -
- -
deleteVariant(e)} - > - delete - -
-
-
    - {#each localVariants as item (item.id)} +{#key selectedProject} +
    +
    -
  • selectVariant(item.value)} - on:contextmenu|stopPropagation={e => { - item.value.select() - VARIANT_PANEL_CONTEXT_MENU.open(e) - }} +
    createVariant(e)} > - {#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]} -
    - {:else} - texture -
    - {item.value.displayName} -
    -
    - {#if item.value.isDefault} - edit - {:else} - openVariantConfigDialog(item.value)}>edit - {/if} - {#if Variant.selected === item.value} - visibility - {:else} - - visibility_off - - {/if} - - {#if !item.value.isDefault} - - deleteVariant(e)} - > - delete - + texture_add +
    + +
    duplicateVariant(e)} + > + content_copy +
    + +
    deleteVariant(e)} + > + delete + +
    +
    +
      + {#each localVariants as item (item.id)} + +
    • selectVariant(item.value)} + on:contextmenu|stopPropagation={e => { + item.value.select() + VARIANT_PANEL_CONTEXT_MENU.get()?.open(e) + }} + > + {#if item[SHADOW_ITEM_MARKER_PROPERTY_NAME]} +
      {:else} - - delete - + texture +
      + {item.value.displayName} +
      +
      + {#if item.value.isDefault} + edit + {:else} + openVariantConfigDialog(item.value)}>edit + {/if} + {#if Variant.selected === item.value} + visibility + {:else} + + visibility_off + + {/if} + + {#if !item.value.isDefault} + + deleteVariant(e)} + > + delete + + {:else} + + delete + + {/if} {/if} - {/if} -
    • - {/each} -
    -
  • + + {/each} +
+
+{/key} diff --git a/src/formats/ajmodel/index.ts b/src/formats/ajmodel/index.ts new file mode 100644 index 00000000..eed95ffd --- /dev/null +++ b/src/formats/ajmodel/index.ts @@ -0,0 +1,72 @@ +import { registerModelLoader } from 'src/util/moddingTools' +import { mountSvelteComponent } from 'src/util/mountSvelteComponent' +import { openUnexpectedErrorDialog } from '../../interface/dialog/unexpectedError' +import { sanitizeStorageKey } from '../../util/minecraftUtil' +import { translate } from '../../util/translation' +import { BLUEPRINT_CODEC } from '../blueprint/codec' +import * as modelDatFixerUpper from '../blueprint/dfu' +import FormatPage from './formatPage.svelte' + +// Blockbench has a bug where it calls the onStart function multiple times when double-clicking it. +let waitingForAJModel = false +export async function openAJModel() { + if (waitingForAJModel) return + waitingForAJModel = true + const result = await electron.dialog.showOpenDialog({ + properties: ['openFile'], + filters: [{ name: '.ajmodel', extensions: ['ajmodel'] }], + message: translate('action.upgrade_old_aj_model_loader.select_file'), + }) + waitingForAJModel = false + if (result.canceled) return + convertAJModelToBlueprint(result.filePaths[0]) +} + +export function convertAJModelToBlueprint(path: string) { + try { + console.log(`Converting .ajmodel: ${path}`) + const blueprint = modelDatFixerUpper.process(JSON.parse(fs.readFileSync(path, 'utf8'))) + + const codec = BLUEPRINT_CODEC.get() + if (!codec) throw new Error('Animated Java Blueprint codec is not registered!') + + codec.load(blueprint, { + name: 'Upgrade .ajmodel to Blueprint', + path, + }) + blueprint.blueprint_settings!.export_namespace ??= sanitizeStorageKey(Project!.name) + + requestAnimationFrame(() => { + Project!.save_path = '' + Project!.export_path = '' + Project!.openSettings() + }) + } catch (e) { + console.error(e) + openUnexpectedErrorDialog(e as Error) + } +} + +registerModelLoader( + { id: `animated-java:upgrade-aj-model-loader` }, + { + icon: 'upload_file', + category: 'animated_java', + name: translate('action.upgrade_old_aj_model_loader.name'), + condition: true, + format_page: { + component: { + template: `
`, + mounted() { + // Don't need to worry about unmounting since the whole panel gets replaced when switching formats + mountSvelteComponent({ + component: FormatPage, + target: `#animated-java\\:upgrade-aj-model-loader-target`, + injectIndex: 2, + }) + }, + }, + }, + onStart: openAJModel, + } +) diff --git a/src/formats/blueprint/codec.ts b/src/formats/blueprint/codec.ts new file mode 100644 index 00000000..bc1dc9e1 --- /dev/null +++ b/src/formats/blueprint/codec.ts @@ -0,0 +1,399 @@ +import { sanitizeStorageKey } from 'src/util/minecraftUtil' +import { registerCodec } from 'src/util/moddingTools' +import { translate } from 'src/util/translation' +import { Variant } from 'src/variants' +import { + BLUEPRINT_FORMAT, + BLUEPRINT_FORMAT_ID, + getDefaultProjectSettings, + IBlueprintFormatJSON, + ICollectionJSON, +} from '.' +import { PACKAGE } from '../../constants' +import * as modelDatFixerUpper from './dfu' +import * as blueprintSettings from './settings' + +// region Codec +export const BLUEPRINT_CODEC = registerCodec( + { id: 'animated-java:codec/blueprint' }, + { + name: 'Blueprint', + extension: 'ajblueprint', + remember: true, + load_filter: { + extensions: ['ajblueprint'], + type: 'json', + }, + + // region > load + load(model, file) { + const format = BLUEPRINT_FORMAT.get() + if (!format) throw new Error('Animated Java Blueprint format is not registered!') + + console.log(`Loading Animated Java Blueprint from '${file.name}'...`) + + model = modelDatFixerUpper.process(model) + + setupProject(format, model.meta?.uuid) + if (!Project) throw new Error('Failed to load Animated Java Blueprint') + + this.parse!(model, file.path) + + const name = pathToName(file.path, true) + if (file.path && isApp && !file.no_file) { + Project.name = pathToName(file.path, false) + Project.save_path = file.path + addRecentProject({ + name, + path: file.path, + icon: BLUEPRINT_FORMAT.get()?.icon, + }) + const project = Project + setTimeout(() => { + if (Project === project) void updateRecentProjectThumbnail() + }, 500) + } + Settings.updateSettingsInProfiles() + + console.log( + `Successfully loaded Animated Java Blueprint` + + `\n - Project: ${Project.name}` + + `\n - UUID: ${Project.uuid}` + ) + }, + + // region > parse + // Takes the model file and injects it's data into the global Project + parse(model: IBlueprintFormatJSON, path) { + console.log(`Parsing Animated Java Blueprint from '${path}'...`) + if (!Project) throw new Error('No project to parse into') + + Project.save_path = path + + if (model.meta?.box_uv !== undefined) { + Project.box_uv = model.meta.box_uv + } + + if (model.resolution !== undefined) { + Project.texture_width = model.resolution.width ?? 16 + Project.texture_height = model.resolution.height ?? 16 + } + + // Misc Project Properties + for (const key in ModelProject.properties) { + ModelProject.properties[key].merge(Project, model) + } + + if (model.blueprint_settings) { + Project.animated_java = { + ...blueprintSettings.defaultValues, + ...model.blueprint_settings, + } + } + + // FIXME - Temporarily disable plugin mode for 1.8.0 + if (Project.animated_java.enable_plugin_mode) { + Project.animated_java.enable_plugin_mode = false + } + + Project.last_used_export_namespace = + model.meta?.last_used_export_namespace ?? Project.animated_java.export_namespace + + if (model.textures) { + for (const texture of model.textures) { + const newTexture = new Texture(texture, texture.uuid).add(false) + if (texture.relative_path && Project.save_path) { + const resolvedPath = PathModule.resolve( + Project.save_path, + texture.relative_path + ) + if (fs.existsSync(resolvedPath)) { + newTexture.fromPath(resolvedPath) + continue + } + } + if (texture.path && fs.existsSync(texture.path) && !model.meta?.backup) { + newTexture.fromPath(texture.path) + } else if (texture.source?.startsWith('data:')) { + newTexture.fromDataURL(texture.source) + } + } + } + + if (model.elements) { + const defaultTexture = Texture.getDefault() + for (const element of model.elements) { + const newElement = OutlinerElement.fromSave(element, true) + switch (true) { + case newElement instanceof Cube: { + for (const face in newElement.faces) { + if (element.faces) { + const texture = + element.faces[face].texture !== undefined && + Texture.all[element.faces[face].texture] + if (texture) { + newElement.faces[face].texture = texture.uuid + } + } else if ( + defaultTexture && + newElement.faces && + newElement.faces[face].texture !== undefined + ) { + newElement.faces[face].texture = defaultTexture.uuid + } + } + break + } + } + } + } + + if (model.outliner) { + parseGroups(model.outliner) + + for (const group of Group.all) { + group.name = sanitizeStorageKey(group.name) + } + } + + if (model.variants?.default) { + Variant.fromJSON(model.variants?.default, true) + } else { + console.warn('No default Variant found, creating one named "Default"') + new Variant('Default', true) + } + + if (model.variants?.list) { + for (const variantJSON of model.variants.list) { + Variant.fromJSON(variantJSON) + } + Project.variants = Variant.all + } + + if (model.animations) { + for (const animation of model.animations) { + const newAnimation = new Blockbench.Animation() + newAnimation.uuid = animation.uuid ?? guid() + newAnimation.extend(animation).add() + } + } + + if (model.animation_controllers) { + for (const controller of model.animation_controllers) { + const newController = new Blockbench.AnimationController() + newController.uuid = controller.uuid ?? guid() + newController.extend(controller).add() + } + } + + if (model.animation_variable_placeholders) { + Interface.Panels.variable_placeholders.inside_vue.$data.text = + model.animation_variable_placeholders + } + + if (model.backgrounds) { + for (const key in model.backgrounds) { + if (Object.hasOwn(Project.backgrounds, key)) { + const store = model.backgrounds[key] + const real = Project.backgrounds[key] + + if (store.image !== undefined) { + real.image = store.image + } + if (store.size !== undefined) { + real.size = store.size + } + if (store.x !== undefined) { + real.x = store.x + } + if (store.y !== undefined) { + real.y = store.y + } + if (store.lock !== undefined) { + real.lock = store.lock + } + } + } + Preview.all.forEach(p => { + if (p.canvas.isConnected) { + p.loadBackground() + } + }) + } + + if (Array.isArray(model.collections)) { + for (const collectionJSON of model.collections) { + new Collection(collectionJSON, collectionJSON.uuid).add() + } + } + + Canvas.updateAll() + Validator.validate() + + this.dispatchEvent!('parsed', { model }) + }, + + // region > compile + compile(options = {}) { + console.log(`Compiling Animated Java Blueprint from ${Project!.name}...`) + if (!Project) throw new Error('No project to compile.') + + // Disable variants while compiling + const previouslySelectedVariant = Variant.selected + Variant.selectDefault() + + const model: IBlueprintFormatJSON = { + meta: { + format: BLUEPRINT_FORMAT_ID, + format_version: PACKAGE.version, + uuid: Project.uuid, + + save_location: Project.save_path, + last_used_export_namespace: Project.last_used_export_namespace, + }, + resolution: { + width: Project.texture_width ?? 16, + height: Project.texture_height ?? 16, + }, + } + + const defaultSettings = getDefaultProjectSettings() + + for (const key of Object.keys(Project.animated_java) as Array< + keyof typeof Project.animated_java + >) { + const value = Project.animated_java[key] + if (value == undefined || value === defaultSettings[key]) continue + model.blueprint_settings ??= {} + // @ts-expect-error - Cannot index blueprint_settings with key + model.blueprint_settings[key] = value + } + + for (const key in ModelProject.properties) { + if (ModelProject.properties[key].export) + ModelProject.properties[key].copy(Project, model) + } + + model.elements = [] + for (const element of elements) { + model.elements.push(element.getSaveCopy?.(!!model.meta)) + } + + model.outliner = compileGroups(true) + + model.textures = [] + for (const texture of Texture.all) { + const save = texture.getSaveCopy() as Texture + delete save.selected + if ( + isApp && + Project.save_path && + texture.path && + PathModule.isAbsolute(texture.path) + ) { + const relative = PathModule.relative( + PathModule.dirname(Project.save_path), + texture.path + ) + save.relative_path = relative.replace(/\\/g, '/') + } + if ( + options.bitmaps != false && + (Settings.get('embed_textures') || options.backup || options.bitmaps == true) + ) { + save.source = texture.getDataURL() + save.internal = true + } + if (options.absolute_paths == false) delete save.path + model.textures.push(save) + } + + model.variants = { + default: Variant.all.find(v => v.isDefault)!.toJSON(), + list: Variant.all.filter(v => !v.isDefault).map(v => v.toJSON()), + } + + const animationOptions = { bone_names: true, absolute_paths: options.absolute_paths } + if (Blockbench.Animation.all.length > 0) { + model.animations = [] + for (const animation of Blockbench.Animation.all) { + if (!animation.getUndoCopy) continue + model.animations.push(animation.getUndoCopy(animationOptions, true)) + } + } + + if (Blockbench.AnimationController.all.length > 0) { + model.animation_controllers = [] + for (const controller of Blockbench.AnimationController.all) { + if (!controller.getUndoCopy) continue + model.animation_controllers.push(controller.getUndoCopy(animationOptions, true)) + } + } + + if (Interface.Panels.variable_placeholders.inside_vue.$data.text) { + model.animation_variable_placeholders = + Interface.Panels.variable_placeholders.inside_vue.$data.text + } + + if (!options.backup) { + const backgrounds: Record = {} + for (const key in Project.backgrounds) { + const scene = Project.backgrounds[key] + if (scene.image) { + backgrounds[key] = scene.getSaveCopy() + } + } + if (Object.keys(backgrounds).length) { + model.backgrounds = backgrounds + } + } + + if (Collection.all.length > 0) { + model.collections = Collection.all.map( + collection => collection.getSaveCopy() as ICollectionJSON + ) + } + + previouslySelectedVariant?.select() + + console.log('Successfully compiled Animated Java Blueprint', model) + return options.raw ? model : compileJSON(model) + }, + + // region > export + export() { + console.log(`Exporting Animated Java Blueprint for ${Project!.name}...`) + if (!Project) throw new Error('No project to export.') + + Blockbench.export({ + resource_id: 'animated_java_blueprint.export', + name: (Project.name || 'unnamed') + '.ajblueprint', + startpath: Project.save_path, + type: 'json', + extensions: [this.extension], + content: this.compile!(), + // eslint-disable-next-line @typescript-eslint/naming-convention + custom_writer: (content, path) => { + if (fs.existsSync(PathModule.dirname(path))) { + Project!.save_path = path + this.write!(content, path) + } else { + console.error( + `Failed to export Animated Java Blueprint, file location '${path}' does not exist!` + ) + Blockbench.showMessageBox({ + title: translate('error.blueprint_export_path_doesnt_exist.title'), + message: translate('error.blueprint_export_path_doesnt_exist', path), + }) + } + }, + }) + }, + + // region > fileName + fileName() { + if (!Project?.name) return 'unnamed_project.ajblueprint' + return `${Project.name}.ajblueprint` + }, + } +) diff --git a/src/systems/modelDataFixerUpper.ts b/src/formats/blueprint/dfu.ts similarity index 64% rename from src/systems/modelDataFixerUpper.ts rename to src/formats/blueprint/dfu.ts index bd20252c..ac579710 100644 --- a/src/systems/modelDataFixerUpper.ts +++ b/src/formats/blueprint/dfu.ts @@ -1,12 +1,12 @@ import { NbtCompound, NbtList, NbtString, NbtTag } from 'deepslate/lib/nbt' -import TransparentTexture from '../assets/transparent.png' -import { IBlueprintFormatJSON, getDefaultProjectSettings } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { openUnexpectedErrorDialog } from '../interface/dialog/unexpectedError' -import { BoneConfig } from '../nodeConfigs' - -export function process(model: any): any { - console.log('Running MDFU...', JSON.parse(JSON.stringify(model))) +import { TextDisplay } from 'src/outliner/textDisplay' +import { type IBlueprintFormatJSON, getDefaultProjectSettings } from '.' +import TransparentTexture from '../../assets/transparent.png' +import { PACKAGE } from '../../constants' +import { openUnexpectedErrorDialog } from '../../interface/dialog/unexpectedError' +import { BoneConfig } from '../../nodeConfigs' + +export function process(model: any): IBlueprintFormatJSON { if (model?.meta?.model_format === 'animatedJava/ajmodel') { model.meta.model_format = 'animated_java/ajmodel' model.meta.format_version = '0.0' @@ -17,25 +17,49 @@ export function process(model: any): any { needsUpgrade = needsUpgrade || compareVersions(PACKAGE.version, model.meta.format_version) if (!needsUpgrade) return model - console.log( - 'Upgrading model from version', + console.groupCollapsed( + 'Upgrading project from', model.meta.format_version, 'to', PACKAGE.version ) + console.log('Original model:', JSON.parse(JSON.stringify(model))) - console.group('Upgrade process') if (model.meta.format_version.length === 3) { - console.log('Processing old model format', JSON.parse(JSON.stringify(model))) - if (compareVersions('1.0', model.meta.format_version)) updateModelToOld1_0(model) - if (compareVersions('1.1', model.meta.format_version)) updateModelToOld1_1(model) - if (compareVersions('1.2', model.meta.format_version)) updateModelToOld1_2(model) - if (compareVersions('1.3', model.meta.format_version)) updateModelToOld1_3(model) - if (compareVersions('1.4', model.meta.format_version)) updateModelToOld1_4(model) - model.meta.format_version = '0.3.9' + console.groupCollapsed( + 'Discovered outdated ajmodel format! Upgrading to blueprint format...' + ) + try { + if (compareVersions('1.0', model.meta.format_version)) + model = updateModelToOld1_0(model) + + if (compareVersions('1.1', model.meta.format_version)) + model = updateModelToOld1_1(model) + + if (compareVersions('1.2', model.meta.format_version)) + model = updateModelToOld1_2(model) + + if (compareVersions('1.3', model.meta.format_version)) + model = updateModelToOld1_3(model) + + if (compareVersions('1.4', model.meta.format_version)) + model = updateModelToOld1_4(model) + + model.meta.format_version = '0.3.9' + + console.log( + 'Upgrade to blueprint format complete', + JSON.parse(JSON.stringify(model)) + ) + } catch (e) { + console.error('Failed to upgrade from ajmodel format to blueprint format', e) + throw e + } finally { + console.groupEnd() + } } // Versions below this are post 0.3.10. I changed the versioning system to use the AJ version instead of a unique format version. - if (compareVersions('0.3.10', model.meta.format_version)) updateModelTo0_3_10(model) + if (compareVersions('0.3.10', model.meta.format_version)) model = updateModelTo0_3_10(model) // v1.0.0-pre1 if (compareVersions('0.5.0', model.meta.format_version)) model = updateModelTo1_0pre1(model) // v1.0.0-pre6 @@ -47,20 +71,29 @@ export function process(model: any): any { // v1.4.0 if (compareVersions('1.4.0', model.meta.format_version)) model = updateModelTo1_4_0(model) // v1.6.3 - if (compareVersions('1.6.3', model.meta.format_version)) - model = updateModelTo1_6_3(model as IBlueprintFormatJSON) + if (compareVersions('1.6.3', model.meta.format_version)) model = updateModelTo1_6_3(model) // v1.6.5 - if (compareVersions('1.6.5', model.meta.format_version)) - model = updateModelTo1_6_5(model as IBlueprintFormatJSON) - - console.groupEnd() + if (compareVersions('1.6.5', model.meta.format_version)) model = updateModelTo1_6_5(model) + // v1.8.0 + if (compareVersions('1.8.0', model.meta.format_version)) model = updateModelTo1_8_0(model) + + // Remove unknown blueprint settings + const defaultSettings = getDefaultProjectSettings() + for (const key in model.blueprint_settings) { + if (key in defaultSettings) continue + console.warn('Removing unknown blueprint setting', key, model.blueprint_settings[key]) + delete model.blueprint_settings[key] + } model.meta.format_version = PACKAGE.version - console.log('Upgrade complete') + console.log('Upgrade complete', JSON.parse(JSON.stringify(model))) + return model } catch (e: any) { openUnexpectedErrorDialog(e as Error) throw e + } finally { + console.groupEnd() } } @@ -105,8 +138,7 @@ function updateModelToOld1_0(model: any) { } if ( - model.animations && - model.animations.find((a: any) => + model.animations?.find((a: any) => Object.keys(a.animators as Record).find(name => name === 'effects') ) ) { @@ -197,9 +229,12 @@ function updateModelToOld1_4(model: any) { } // region v0.3.10 -// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars +// eslint-disable-next-line @typescript-eslint/naming-convention function updateModelTo0_3_10(model: any) { console.log('Processing model for AJ 0.3.10', JSON.parse(JSON.stringify(model))) + model.meta ??= {} + model.meta.model_format = 'animated_java/blueprint' + return model } // region v1.0.0-pre1 @@ -212,20 +247,18 @@ function updateModelTo1_0pre1(model: any) { model.animated_java.exporter_settings['animated_java:datapack_exporter'] const defaultVariant = model.animated_java.variants.find((v: any) => !!v.default) - const variants = model.animated_java.variants.filter((v: any) => !v.default) - - const blueprint: IBlueprintFormatJSON = { + const blueprint: Record = { meta: { format: 'animated_java_blueprint', format_version: '0.5.0', - uuid: model.meta.uuid || guid(), + uuid: model.meta.uuid ?? guid(), last_used_export_namespace: model.animated_java.settings.project_namespace, }, project_settings: { // Blueprint Settings - show_bounding_box: defaultSettings.show_bounding_box, - auto_bounding_box: defaultSettings.auto_bounding_box, - bounding_box: defaultSettings.bounding_box, + show_bounding_box: defaultSettings.show_render_box, + auto_bounding_box: defaultSettings.auto_render_box, + bounding_box: defaultSettings.render_box, // Export settings export_namespace: model.animated_java.settings.project_namespace, enable_plugin_mode: defaultSettings.enable_plugin_mode, @@ -247,7 +280,7 @@ function updateModelTo1_0pre1(model: any) { data_pack: datapackExporterSettings?.datapack_mcmeta ? datapackExporterSettings.datapack_mcmeta.replace(/pack\.mcmeta$/, '') : '', - summon_commands: defaultSettings.summon_commands, + summon_commands: defaultSettings.on_summon_function, interpolation_duration: defaultSettings.interpolation_duration, teleportation_duration: defaultSettings.teleportation_duration, use_storage_for_animation: defaultSettings.use_storage_for_animation, @@ -258,10 +291,9 @@ function updateModelTo1_0pre1(model: any) { variants: { default: { name: 'default', - display_name: defaultVariant.name || 'Default', - uuid: defaultVariant.uuid || guid(), - texture_map: defaultVariant.textureMap || {}, - // @ts-ignore + display_name: defaultVariant.name ?? 'Default', + uuid: defaultVariant.uuid ?? guid(), + texture_map: defaultVariant.textureMap ?? {}, excluded_bones: [], }, list: [], @@ -297,20 +329,20 @@ function updateModelTo1_0pre1(model: any) { model.outliner.forEach(recurseOutliner) blueprint.outliner = model.outliner - for (const element of blueprint.elements) { + for (const element of blueprint.elements ?? []) { if (element.type === 'locator') { element.config = { use_entity: true, } if (element.entity_type) element.config.entity_type = element.entity_type if (element.nbt) { - const summon_commands: string[] = [] + const summonCommands: string[] = [] const nbt = NbtTag.fromString(element.nbt as string) as NbtCompound nbt.delete('Passengers') const tags = (nbt.get('Tags') as NbtList)?.map(t => t.getAsString()) nbt.delete('Tags') - summon_commands.push('data merge entity @s ' + nbt.toString()) - if (tags) summon_commands.push(...tags.map(t => `tag @s add ${t}`)) + summonCommands.push('data merge entity @s ' + nbt.toString()) + if (tags) summonCommands.push(...tags.map(t => `tag @s add ${t}`)) const recursePassengers = (stringNbt: string): string[] => { const nbt = NbtTag.fromString(stringNbt) as NbtCompound @@ -348,20 +380,21 @@ function updateModelTo1_0pre1(model: any) { } try { - summon_commands.push(...recursePassengers(element.nbt as string)) + summonCommands.push(...recursePassengers(element.nbt as string)) } catch (e) { console.error('Failed to parse NBT', element.nbt) console.error(e) } - if (summon_commands.length === 0) { - summon_commands.push(`data merge entity @s ${element.nbt as string}`) + if (summonCommands.length === 0) { + summonCommands.push(`data merge entity @s ${element.nbt as string}`) } - element.config.summon_commands = summon_commands.join('\n') + element.config.summon_commands = summonCommands.join('\n') } } } + const variants = model.animated_java?.variants?.filter((v: any) => !v.default) ?? [] for (const variant of variants) { const includedBones = variant.affectedBones.map((v: any) => v.value as string) let excludedBones: string[] @@ -395,7 +428,6 @@ function updateModelTo1_0pre1(model: any) { nbt.delete('Tags') if ([...nbt.keys()].length !== 0) commands.push('data merge entity @s ' + nbt.toString()) if (tags) commands.push(...tags.map(t => `tag @s add ${t}`)) - // @ts-expect-error blueprint.project_settings!.summon_commands = commands.join('\n') } @@ -404,7 +436,7 @@ function updateModelTo1_0pre1(model: any) { // region v1.0.0-pre6 // eslint-disable-next-line @typescript-eslint/naming-convention -function updateModelTo1_0pre6(model: any): IBlueprintFormatJSON { +function updateModelTo1_0pre6(model: any) { console.log('Processing model format 1.0.0-pre6', JSON.parse(JSON.stringify(model))) const defaultVariant = model.variants.default @@ -413,26 +445,26 @@ function updateModelTo1_0pre6(model: any): IBlueprintFormatJSON { delete defaultVariant.excluded_bones } - for (const variant of model?.variants?.list || []) { + for (const variant of model?.variants?.list ?? []) { if (variant?.excluded_bones) { variant.excluded_nodes = variant.excluded_bones delete variant.excluded_bones } } - for (const animation of model?.animations || []) { + for (const animation of model?.animations ?? []) { if (animation?.excluded_bones) { animation.excluded_nodes = animation.excluded_bones delete animation.excluded_bones } } - return model as IBlueprintFormatJSON + return model } // region v1.0.0-pre7 // eslint-disable-next-line @typescript-eslint/naming-convention -function updateModelTo1_0pre7(model: any): IBlueprintFormatJSON { +function updateModelTo1_0pre7(model: any) { console.log('Processing model format 1.0.0-pre7', JSON.parse(JSON.stringify(model))) if (model.project_settings.enable_resource_pack !== undefined) { @@ -450,12 +482,12 @@ function updateModelTo1_0pre7(model: any): IBlueprintFormatJSON { delete model.project_settings.enable_data_pack } - return model as IBlueprintFormatJSON + return model } // region v1.0.0-pre8 // eslint-disable-next-line @typescript-eslint/naming-convention -function updateModelTo1_0pre8(model: any): IBlueprintFormatJSON { +function updateModelTo1_0pre8(model: any) { console.log('Processing model format 1.0.0-pre8', JSON.parse(JSON.stringify(model))) if (model.project_settings) { @@ -463,12 +495,12 @@ function updateModelTo1_0pre8(model: any): IBlueprintFormatJSON { delete model.project_settings } - return model as IBlueprintFormatJSON + return model } // region v1.4.0 // eslint-disable-next-line @typescript-eslint/naming-convention -function updateModelTo1_4_0(model: any): IBlueprintFormatJSON { +function updateModelTo1_4_0(model: any) { console.log('Processing model format 1.4.0', JSON.parse(JSON.stringify(model))) // Separated advanced folders from advanced settings @@ -484,12 +516,12 @@ function updateModelTo1_4_0(model: any): IBlueprintFormatJSON { model.blueprint_settings.enable_advanced_resource_pack_settings = true } - return model as IBlueprintFormatJSON + return model } // region v1.6.3 // eslint-disable-next-line @typescript-eslint/naming-convention -function updateModelTo1_6_3(model: IBlueprintFormatJSON): IBlueprintFormatJSON { +function updateModelTo1_6_3(model: any) { console.log('Processing model format 1.6.3', JSON.parse(JSON.stringify(model))) // Automatically add a transparent texture to the model if it uses the old transparent texture in any of it's variants. @@ -499,7 +531,6 @@ function updateModelTo1_6_3(model: IBlueprintFormatJSON): IBlueprintFormatJSON { { name: 'transparent' }, '797174ae-5c58-4a83-a630-eefd51007c80' ).fromDataURL(TransparentTexture) - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument model.textures.push(texture.getSaveCopy()) break } @@ -509,15 +540,126 @@ function updateModelTo1_6_3(model: IBlueprintFormatJSON): IBlueprintFormatJSON { } // region v1.6.5 -function updateModelTo1_6_5(model: IBlueprintFormatJSON): IBlueprintFormatJSON { +// eslint-disable-next-line @typescript-eslint/naming-convention +function updateModelTo1_6_5(model: any) { console.log('Processing model format 1.6.5', JSON.parse(JSON.stringify(model))) // Update target_minecraft_version to an array if it's a string - if (typeof model.blueprint_settings?.target_minecraft_versions === 'string') { - model.blueprint_settings.target_minecraft_versions = [ - model.blueprint_settings.target_minecraft_versions, + if (typeof model.blueprint_settings?.target_minecraft_version === 'string') { + model.blueprint_settings.target_minecraft_version = [ + model.blueprint_settings.target_minecraft_version, ] } return model } + +// region v1.8.0 +// eslint-disable-next-line @typescript-eslint/naming-convention +function updateModelTo1_8_0(model: any) { + console.log('Processing model format 1.8.0', JSON.parse(JSON.stringify(model))) + const fixed: IBlueprintFormatJSON = JSON.parse(JSON.stringify(model)) + + fixed.blueprint_settings ??= {} + + // Update export mode settings + if (model.blueprint_settings?.resource_pack_export_mode === 'raw') { + fixed.blueprint_settings.resource_pack_export_mode = 'folder' + } + if (model.blueprint_settings?.data_pack_export_mode === 'raw') { + fixed.blueprint_settings.data_pack_export_mode = 'folder' + } + + // Update bounding box settings + if (model.blueprint_settings?.show_bounding_box != undefined) { + fixed.blueprint_settings.show_render_box = model.blueprint_settings.show_bounding_box + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.show_bounding_box + } + if (model.blueprint_settings?.auto_bounding_box != undefined) { + fixed.blueprint_settings.auto_render_box = model.blueprint_settings.auto_bounding_box + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.auto_bounding_box + } + if (model.blueprint_settings?.bounding_box != undefined) { + fixed.blueprint_settings.render_box = model.blueprint_settings.bounding_box + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.bounding_box + } + + // Update command settings + if (model.blueprint_settings?.summon_commands != undefined) { + fixed.blueprint_settings.on_summon_function = model.blueprint_settings.summon_commands + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.summon_commands + } + if (model.blueprint_settings?.remove_commands != undefined) { + fixed.blueprint_settings.on_remove_function = model.blueprint_settings.remove_commands + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.remove_commands + } + if (model.blueprint_settings?.ticking_commands != undefined) { + fixed.blueprint_settings.on_post_tick_function = model.blueprint_settings.ticking_commands + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.ticking_commands + } + + // Update target version settings + if (Array.isArray(model.blueprint_settings?.target_minecraft_versions)) { + fixed.blueprint_settings.target_minecraft_version = + model.blueprint_settings.target_minecraft_versions.at(0) ?? + getDefaultProjectSettings().target_minecraft_version + // @ts-expect-error Does not exist on type + delete fixed.blueprint_settings.target_minecraft_versions + } + + if (Array.isArray(fixed.elements)) { + // Update text display backgrounds to use 8 digit hex colors instead of separate alpha + const textDisplays = fixed.elements.filter(e => e.type === TextDisplay.type) + for (const display of textDisplays) { + if (display.backgroundAlpha !== undefined) { + display.backgroundColor ??= TextDisplay.properties.backgroundColor.default as string + display.backgroundColor = tinycolor(display.backgroundColor) + .setAlpha(display.backgroundAlpha) + .toHex8String() + delete display.backgroundAlpha + } + } + // Update locators to use new event function names + const locators = fixed.elements.filter(e => e.type === Locator.prototype.type) + for (const locator of locators) { + if (locator.config?.summon_commands) { + locator.config.on_summon_function = locator.config.summon_commands + delete locator.config.summon_commands + } + if (locator.config?.ticking_commands) { + locator.config.on_tick_function = locator.config.ticking_commands + delete locator.config.ticking_commands + } + } + } + + // Update commands keyframes to use new keyframe channel name + if (Array.isArray(fixed.animations)) { + debugger + for (const animation of fixed.animations) { + for (const animator of Object.values(animation.animators ?? {})) { + if (!Array.isArray(animator.keyframes)) continue + for (const keyframe of animator.keyframes) { + if (keyframe.channel !== 'commands') continue + keyframe.channel = 'function' + if (Array.isArray(keyframe.data_points)) { + for (const dataPoint of keyframe.data_points) { + if (dataPoint.commands) { + dataPoint.function = dataPoint.commands + delete dataPoint.commands + } + } + } + } + } + } + } + + return fixed +} diff --git a/src/formats/blueprint/index.ts b/src/formats/blueprint/index.ts new file mode 100644 index 00000000..4c882733 --- /dev/null +++ b/src/formats/blueprint/index.ts @@ -0,0 +1,445 @@ +import { type TextDisplay } from 'src/outliner/textDisplay' +import { type VanillaBlockDisplay } from 'src/outliner/vanillaBlockDisplay' +import { type VanillaItemDisplay } from 'src/outliner/vanillaItemDisplay' +import { mountSvelteComponent } from 'src/util/mountSvelteComponent' +import FormatPageSvelte from '../../components/formatPage.svelte' +import ProjectTitleSvelte from '../../components/projectTitle.svelte' +import { type BillboardMode, BoneConfig, LocatorConfig } from '../../nodeConfigs' +import EVENTS from '../../util/events' +import { sanitizeStorageKey } from '../../util/minecraftUtil' +import { registerModelFormat } from '../../util/moddingTools' +import { Valuable } from '../../util/stores' +import { translate } from '../../util/translation' +import { Variant } from '../../variants' +import { BLUEPRINT_CODEC } from './codec' +import type { BlueprintSettings } from './settings' +import * as blueprintSettings from './settings' + +declare global { + interface ModelProject { + animated_java: BlueprintSettings + last_used_export_namespace: string + visualBoundingBox?: THREE.LineSegments + pluginMode: Valuable + transparentTexture: Texture + + variants: Variant[] + textDisplays: TextDisplay[] + vanillaItemDisplays: VanillaItemDisplay[] + vanillaBlockDisplays: VanillaBlockDisplay[] + } +} + +let boundingBoxUpdateIntervalId: ReturnType | undefined + +export const BLUEPRINT_FORMAT_ID = 'animated-java:format/blueprint' + +/** + * The serialized Variant Bone Config + */ +export interface IBlueprintBoneConfigJSON { + custom_name?: BoneConfig['customName'] + custom_name_visible?: BoneConfig['customNameVisible'] + billboard?: BoneConfig['billboard'] + override_brightness?: BoneConfig['overrideBrightness'] + brightness_override?: BoneConfig['brightnessOverride'] + enchanted?: BoneConfig['enchanted'] + glowing?: BoneConfig['glowing'] + override_glow_color?: BoneConfig['overrideGlowColor'] + glow_color?: BoneConfig['glowColor'] + inherit_settings?: BoneConfig['inheritSettings'] + invisible?: BoneConfig['invisible'] + /** + * Custom NBT for the bone that will be merged when this Variant is applied + */ + nbt?: BoneConfig['nbt'] + shadow_radius?: BoneConfig['shadowRadius'] + shadow_strength?: BoneConfig['shadowStrength'] + use_nbt?: BoneConfig['useNBT'] +} + +/** + * The serialized Variant Locator Config + */ +export interface IBlueprintLocatorConfigJSON { + use_entity?: LocatorConfig['useEntity'] + entity_type?: LocatorConfig['entityType'] + sync_passenger_rotation?: LocatorConfig['syncPassengerRotation'] + on_summon_function?: LocatorConfig['__onSummonFunction'] + on_remove_function?: LocatorConfig['__onRemoveFunction'] + on_tick_function?: LocatorConfig['__onTickFunction'] +} + +/** + * The serialized Variant Text Display Config + */ +export interface IBlueprintTextDisplayConfigJSON { + billboard?: BillboardMode + override_brightness?: BoneConfig['overrideBrightness'] + brightness_override?: BoneConfig['brightnessOverride'] + glowing?: BoneConfig['glowing'] + override_glow_color?: BoneConfig['overrideGlowColor'] + glow_color?: BoneConfig['glowColor'] + invisible?: BoneConfig['invisible'] + shadow_radius?: BoneConfig['shadowRadius'] + shadow_strength?: BoneConfig['shadowStrength'] + use_nbt?: BoneConfig['useNBT'] + /** + * Custom NBT for the bone that will be merged when this Variant is applied + */ + nbt?: BoneConfig['nbt'] +} + +/** + * The serialized Variant + */ +export interface IBlueprintVariantJSON { + /** + * The display name of the Variant. Only use in Blockbench and for error messages. + */ + display_name: string + /** + * The name of the Variant + */ + name: string + /** + * The uuid of the Variant + */ + uuid: string + /** + * The texture map for the Variant + */ + texture_map: Record + /** + * The list of bones that should be ignored when applying the Variant + */ + excluded_nodes: string[] + /** + * Whether or not this is the default Variant + */ + is_default?: true +} + +export interface ICollectionJSON { + name?: string + uuid?: string + export_codec?: string + export_path?: string + children?: string[] + visibility?: boolean +} + +/** + * The serialized Blueprint + */ +export interface IBlueprintFormatJSON { + meta?: { + format?: string + format_version?: string + uuid?: string + last_used_export_namespace?: string + box_uv?: boolean + backup?: boolean + save_location?: string + } + /** + * The project settings of the Blueprint + */ + blueprint_settings?: Partial + /** + * The variants of the Blueprint + */ + variants?: { + /** + * The default Variant of the Blueprint + */ + default?: IBlueprintVariantJSON + /** + * The list of variants of the Blueprint, excluding the default Variant + */ + list?: IBlueprintVariantJSON[] + } + + resolution?: { + width?: number + height?: number + } + + elements?: any[] + outliner?: any[] + textures?: Texture[] + animations?: AnimationOptions[] + animation_controllers?: AnimationControllerOptions[] + animation_variable_placeholders?: string + backgrounds?: Record + collections?: ICollectionJSON[] +} + +//region > Convert +export function convertToBlueprint() { + // Convert the current project to a Blueprint + Project!.save_path = '' + Project!.last_used_export_namespace = '' + + for (const group of Group.all) { + group.createUniqueName(Group.all.filter(g => g !== group)) + group.sanitizeName() + } + for (const animation of Blockbench.Animation.all) { + animation.createUniqueName(Blockbench.Animation.all.filter(a => a !== animation)) + animation.name = sanitizeStorageKey(animation.name) + } + for (const cube of Cube.all) { + cube.setUVMode(false) + } +} + +export function getDefaultProjectSettings() { + return { ...blueprintSettings.defaultValues } +} + +function initializeRenderBoxPreview() { + if (boundingBoxUpdateIntervalId == undefined) { + boundingBoxUpdateIntervalId = setInterval(() => { + updateRenderBoxPreview() + }, 500) + EVENTS.PLUGIN_UNLOAD.subscribe(() => clearInterval(boundingBoxUpdateIntervalId), true) + EVENTS.PLUGIN_UNINSTALL.subscribe(() => clearInterval(boundingBoxUpdateIntervalId), true) + } +} + +export function updateRenderBoxPreview() { + if (!Project || !activeProjectIsBlueprintFormat()) return + if (Project.visualBoundingBox) scene.remove(Project.visualBoundingBox) + + if (!Project.animated_java.show_render_box) return + + let width = 0 + let height = 0 + if (Project.animated_java.auto_render_box) { + for (const cube of Cube.all) { + width = Math.max( + width, + Math.abs(cube.to[0]), + Math.abs(cube.to[2]), + Math.abs(cube.from[0]), + Math.abs(cube.from[2]) + ) + height = Math.max(height, cube.to[1], cube.from[1]) + } + const boundingBoxOverflow = 8 + width += boundingBoxOverflow + height += boundingBoxOverflow + } else { + width = Project.animated_java.render_box[0] + height = Project.animated_java.render_box[1] + } + + const boundingBox = new THREE.BoxGeometry(width * 2, height, width * 2) + Project.visualBoundingBox = new THREE.LineSegments( + new THREE.EdgesGeometry(boundingBox), + new THREE.LineBasicMaterial({ color: '#855000' }) + ) + Project.visualBoundingBox.position.set(0, height / 2, 0) + scene.add(Project.visualBoundingBox) +} + +// region Format +export const BLUEPRINT_FORMAT = registerModelFormat( + { id: BLUEPRINT_FORMAT_ID, dependencies: ['animated-java:codec/blueprint'] }, + { + name: translate('format.blueprint.name'), + icon: 'icon-armor_stand', + category: 'animated_java', + target: 'Minecraft: Java Edition', + confidential: false, + condition: () => true, + show_on_start_screen: true, + format_page: { + component: { + template: `
`, + mounted() { + // Don't need to worry about unmounting since the whole panel gets replaced when switching formats + mountSvelteComponent({ + component: FormatPageSvelte, + target: `div[id="${BLUEPRINT_FORMAT_ID}/format_page_mount"]`, + }) + }, + }, + }, + + onSetup(project, newModel) { + console.log('Animated Java Blueprint format setup') + + const defaults = getDefaultProjectSettings() + if (newModel) { + project.animated_java = defaults + project.last_used_export_namespace = '' + } else { + project.animated_java = { ...defaults, ...project!.animated_java } + } + + project.pluginMode = new Valuable(project.animated_java.enable_plugin_mode) + + // project.variants ??= [] + // if (Variant.all.length === 0) { + // console.warn('No variants found, creating default variant') + // new Variant('Default', true) + // } + // Variant.selectDefault() + + initializeRenderBoxPreview() + + requestAnimationFrame(() => { + const projectIndex = ModelProject.all.indexOf(project) + const projectTab = document.querySelectorAll('#tab_bar_list .project_tab')[ + projectIndex + ] + + if (!projectTab) { + console.error('Could not find project tab for Animated Java Blueprint project!') + return + } + projectTab.querySelector('i')?.remove() + + mountSvelteComponent({ + target: projectTab, + prepend: true, + component: ProjectTitleSvelte, + props: { pluginMode: project.pluginMode }, + }) + }) + }, + + animated_textures: true, + animation_controllers: true, + animation_files: true, + texture_mcmeta: true, + animation_mode: true, + bone_binding_expression: true, + bone_rig: true, + box_uv: false, + centered_grid: true, + display_mode: false, + edit_mode: true, + integer_size: false, + java_face_properties: true, + locators: true, + meshes: false, + model_identifier: false, + optional_box_uv: true, + paint_mode: true, + parent_model_id: false, + pose_mode: false, + render_sides: 'front', + rotate_cubes: true, + rotation_limit: false, + select_texture_for_particles: false, + single_texture: false, + texture_folder: false, + texture_meshes: false, + uv_rotation: true, + vertex_color_ambient_occlusion: true, + java_cube_shading_properties: true, + box_uv_float_size: false, + cullfaces: true, + } +) +Language.data['format_category.animated_java'] = translate('format_category.animated_java') + +BLUEPRINT_FORMAT.onCreated(format => { + const codec = BLUEPRINT_CODEC.get() + if (!codec) throw new Error('Animated Java Blueprint codec is not registered!') + + codec.format = format + format.codec = codec +}) + +export function activeProjectIsBlueprintFormat() { + return Format instanceof ModelFormat && Format.id === BLUEPRINT_FORMAT_ID +} + +export function createNewBlueprintProject() { + const format = BLUEPRINT_FORMAT.get() + if (!format) throw new Error('Animated Java Blueprint format is not registered!') + + newProject(format) + + requestAnimationFrame(() => { + Project!.openSettings() + }) +} + +export function saveBlueprint() { + if (!Project || !Format || !activeProjectIsBlueprintFormat()) return + + const codec = BLUEPRINT_CODEC.get() + if (!codec) throw new Error('Animated Java Blueprint codec is not registered!') + + codec.write(codec.compile(), Project.save_path) +} + +export function projectTargetVersionIsAtLeast(version: string): boolean { + return !compareVersions(version, Project!.animated_java.target_minecraft_version) +} + +export function shouldEnableRotationLock(): boolean { + if (!activeProjectIsBlueprintFormat()) return false + + if (projectTargetVersionIsAtLeast('1.21.6')) { + return false + } + + return !( + !!Group.first_selected || + !!AnimatedJava.TextDisplay.selected.length || + !!AnimatedJava.VanillaItemDisplay.selected.length || + !!AnimatedJava.VanillaBlockDisplay.selected.length || + !!( + Array.isArray(OutlinerElement.types.locator?.selected) && + OutlinerElement.types.locator.selected.length + ) || + !!( + Array.isArray(OutlinerElement.types.camera?.selected) && + OutlinerElement.types.camera.selected.length + ) + ) +} + +export function updateRotationLock() { + if (!activeProjectIsBlueprintFormat()) return + const format = BLUEPRINT_FORMAT.get()! + // If any of these node types are selected, we disable rotation lock. + format.rotation_limit = shouldEnableRotationLock() + format.rotation_snap = format.rotation_limit +} + +export function disableRotationLock() { + if (!activeProjectIsBlueprintFormat()) return + const format = BLUEPRINT_FORMAT.get()! + + format.rotation_limit = false + format.rotation_snap = false +} + +EVENTS.SELECT_PROJECT.subscribe(project => { + if (project.format.id === BLUEPRINT_FORMAT_ID) { + EVENTS.SELECT_AJ_PROJECT.publish(project) + } +}) +EVENTS.UNSELECT_PROJECT.subscribe(project => { + if (project.format.id === BLUEPRINT_FORMAT_ID) { + EVENTS.UNSELECT_AJ_PROJECT.publish(project) + } +}) +EVENTS.UPDATE_SELECTION.subscribe(updateRotationLock) +EVENTS.SELECT_AJ_PROJECT.subscribe(() => { + requestAnimationFrame(() => { + updateRenderBoxPreview() + updateRotationLock() + }) +}) +EVENTS.UNSELECT_AJ_PROJECT.subscribe(project => { + if (project.visualBoundingBox) scene.remove(project.visualBoundingBox) + disableRotationLock() +}) diff --git a/src/formats/blueprint/settings.ts b/src/formats/blueprint/settings.ts new file mode 100644 index 00000000..e3442945 --- /dev/null +++ b/src/formats/blueprint/settings.ts @@ -0,0 +1,77 @@ +import { SUPPORTED_MINECRAFT_VERSIONS } from '../../systems/global' +import { Valuable } from '../../util/stores' + +export type ExportMode = 'folder' | 'zip' | 'none' + +export interface BlueprintSettings { + export_namespace: string + + show_render_box: boolean + auto_render_box: boolean + render_box: ArrayVector2 + // Export Settings + enable_plugin_mode: boolean + resource_pack_export_mode: ExportMode + data_pack_export_mode: ExportMode + target_minecraft_version: SUPPORTED_MINECRAFT_VERSIONS + // Resource Pack Settings + display_item: string + custom_model_data_offset: number + enable_advanced_resource_pack_settings: boolean + resource_pack: string + // Data Pack Settings + enable_advanced_data_pack_settings: boolean + data_pack: string + on_summon_function: string + on_remove_function: string + on_pre_tick_function: string + on_post_tick_function: string + interpolation_duration: number + teleportation_duration: number + auto_update_rig_orientation: boolean + use_storage_for_animation: boolean + // Plugin Settings + baked_animations: boolean + json_file: string +} + +export const defaultValues: BlueprintSettings = { + export_namespace: 'blueprint', + + show_render_box: false, + auto_render_box: true, + render_box: [48, 48] as ArrayVector2, + + // Export Settings + enable_plugin_mode: false, + resource_pack_export_mode: 'folder' as ExportMode, + data_pack_export_mode: 'folder' as ExportMode, + target_minecraft_version: SUPPORTED_MINECRAFT_VERSIONS['1.21.9'], + + // Resource Pack Settings + display_item: 'minecraft:white_dye', + custom_model_data_offset: 0, + enable_advanced_resource_pack_settings: false, + resource_pack: '', + + // Data Pack Settings + enable_advanced_data_pack_settings: false, + data_pack: '', + + on_summon_function: '', + on_remove_function: '', + on_pre_tick_function: '', + on_post_tick_function: '', + + interpolation_duration: 1, + teleportation_duration: 1, + + auto_update_rig_orientation: true, + use_storage_for_animation: false, + + // Plugin Settings + baked_animations: true, + json_file: '', +} + +export const blueprintSettingErrors = new Valuable>({}) diff --git a/src/global.d.ts b/src/global.d.ts index e3c0641c..04c03e60 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,4 +1,5 @@ -/// +/// +/// declare module '*.png' { const value: string @@ -30,6 +31,11 @@ declare module '*.mcb' { export = value } +declare module '*.mcbt' { + const value: string + export = value +} + declare module '*.txt' { const value: string export = value @@ -47,3 +53,8 @@ declare module '*.molang' { declare module 'fflate/browser' { export * from 'fflate' } + +declare module '*.css' { + const value: string + export = value +} diff --git a/src/index.ts b/src/index.ts index db3e1408..7012346e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,110 +1,121 @@ import { PACKAGE } from './constants' -import { events } from './util/events' +import EVENTS from './util/events' import './util/translation' // Blueprint Format -import './blueprintFormat' +import './formats/**' // Interface -import './interface' +import './interface/**' // Blockbench Mods -import './mods' +import './mods/**' // Outliner import './outliner/textDisplay' -import './outliner/vanillaItemDisplay' import './outliner/vanillaBlockDisplay' +import './outliner/vanillaItemDisplay' // Compilers import datapackCompiler from './systems/datapackCompiler' // Minecraft Systems -import './systems/minecraft/versionManager' -import './systems/minecraft/registryManager' -import './systems/minecraft/blockstateManager' import './systems/minecraft/assetManager' +import './systems/minecraft/blockstateManager' import './systems/minecraft/fontManager' -// Misc imports -import { Variant } from './variants' import './systems/minecraft/registryManager' -import { MINECRAFT_REGISTRY } from './systems/minecraft/registryManager' -import resourcepackCompiler from './systems/resourcepackCompiler' +import './systems/minecraft/versionManager' +// Misc imports +import { BLUEPRINT_FORMAT } from './formats/blueprint' +import { BLUEPRINT_CODEC } from './formats/blueprint/codec' +import { blueprintSettingErrors } from './formats/blueprint/settings' +import { openChangelogDialog } from './interface/changelogDialog' import { openExportProgressDialog } from './interface/dialog/exportProgress' -import { isDataPackPath, isResourcePackPath, parseResourcePackPath } from './util/minecraftUtil' -import { blueprintSettingErrors } from './blueprintSettings' import { openUnexpectedErrorDialog } from './interface/dialog/unexpectedError' -import { BLUEPRINT_CODEC, BLUEPRINT_FORMAT } from './blueprintFormat' +import { checkForIncompatabilities } from './interface/popup/incompatabilityPopup' +import { openInstallPopup } from './interface/popup/installed' import { TextDisplay } from './outliner/textDisplay' -import { getLatestVersionClientDownloadUrl } from './systems/minecraft/assetManager' -import { getVanillaFont } from './systems/minecraft/fontManager' -import * as assetManager from './systems/minecraft/assetManager' -import * as itemModelManager from './systems/minecraft/itemModelManager' -import * as blockModelManager from './systems/minecraft/blockModelManager' -import { VanillaItemDisplay } from './outliner/vanillaItemDisplay' import { VanillaBlockDisplay, debugBlockState, debugBlocks } from './outliner/vanillaBlockDisplay' -import { BLOCKSTATE_REGISTRY } from './systems/minecraft/blockstateManager' -import { exportProject } from './systems/exporter' -import { openBlueprintLoadingDialog } from './interface/popup/blueprintLoading' -import { openInstallPopup } from './interface/popup/installed' +import { VanillaItemDisplay } from './outliner/vanillaItemDisplay' import { cleanupExportedFiles } from './systems/cleaner' import mcbFiles from './systems/datapackCompiler/mcbFiles' -import { openChangelogDialog } from './interface/changelogDialog' -import { checkForIncompatabilities } from './interface/popup/incompatabilityPopup' +import TELLRAW from './systems/datapackCompiler/tellraw' +import { exportProject } from './systems/exporter' +import { JsonText } from './systems/jsonText' +import * as assetManager from './systems/minecraft/assetManager' +import { getLatestVersionClientDownloadUrl } from './systems/minecraft/assetManager' +import * as blockModelManager from './systems/minecraft/blockModelManager' +import { BLOCKSTATE_REGISTRY } from './systems/minecraft/blockstateManager' +import { getVanillaFont } from './systems/minecraft/fontManager' +import * as itemModelManager from './systems/minecraft/itemModelManager' +import './systems/minecraft/registryManager' +import { MINECRAFT_REGISTRY } from './systems/minecraft/registryManager' +import resourcepackCompiler from './systems/resourcepackCompiler' +import { + isDataPackPath, + isResourcePackPath, + parseResourcePackPath, + toSmallCaps, +} from './util/minecraftUtil' +import { Variant } from './variants' -// @ts-ignore -globalThis.AnimatedJava = { - API: { - parseResourcePackPath, - datapackCompiler, - resourcepackCompiler, - Variant, - MINECRAFT_REGISTRY, - openExportProgressDialog, - isResourcePackPath, - isDataPackPath, - blueprintSettingErrors, - openUnexpectedErrorDialog, - BLUEPRINT_FORMAT, - BLUEPRINT_CODEC, - TextDisplay, - getLatestVersionClientDownloadUrl, - getVanillaFont, - assetManager, - itemModelManager, - blockModelManager, - VanillaItemDisplay, - VanillaBlockDisplay, - debugBlocks, - debugBlockState, - BLOCKSTATE_REGISTRY, - exportProject, - openBlueprintLoadingDialog, - openInstallPopup, - removeCubesAssociatedWithTexture(texture: Texture) { - const cubes = Cube.all.filter(cube => - Object.values(cube.faces).some(face => face.texture === texture.uuid) - ) - Undo.initEdit({ elements: cubes, outliner: true, textures: [texture] }) - cubes.forEach(cube => cube.remove()) - texture.remove() - Undo.finishEdit('Remove Cubes Associated With Texture') - }, - cleanupExportedFiles, - mcbFiles, - openChangelogDialog, - checkForIncompatabilities, +declare global { + interface Window { + AnimatedJava: typeof AnimatedJavaApi + } + // eslint-disable-next-line @typescript-eslint/naming-convention + const AnimatedJava: typeof AnimatedJavaApi +} +// eslint-disable-next-line @typescript-eslint/naming-convention +const AnimatedJavaApi = { + parseResourcePackPath, + datapackCompiler, + resourcepackCompiler, + Variant, + MINECRAFT_REGISTRY, + openExportProgressDialog, + isResourcePackPath, + isDataPackPath, + blueprintSettingErrors, + openUnexpectedErrorDialog, + BLUEPRINT_FORMAT, + BLUEPRINT_CODEC, + TextDisplay, + getLatestVersionClientDownloadUrl, + getVanillaFont, + assetManager, + itemModelManager, + blockModelManager, + VanillaItemDisplay, + VanillaBlockDisplay, + debugBlocks, + debugBlockState, + BLOCKSTATE_REGISTRY, + exportProject, + openInstallPopup, + removeCubesAssociatedWithTexture(texture: Texture) { + const cubes = Cube.all.filter(cube => + Object.values(cube.faces).some(face => face.texture === texture.uuid) + ) + Undo.initEdit({ elements: cubes, outliner: true, textures: [texture] }) + cubes.forEach(cube => cube.remove()) + texture.remove() + Undo.finishEdit('Remove Cubes Associated With Texture') }, + cleanupExportedFiles, + mcbFiles, + openChangelogDialog, + checkForIncompatabilities, + toSmallCharacters: toSmallCaps, + printMinecraftFontSheet: () => { + const fontJson = assetManager.getJSONAsset('assets/minecraft/font/include/default.json') + return fontJson.providers[0].chars.map( + (characters: string, i: number) => `${i}: ` + characters.split('').join(' ') + ) + }, + TELLRAW, + JsonText, } - -requestAnimationFrame(() => { - if (checkForIncompatabilities()) return - - const lastVersion = localStorage.getItem('animated-java-last-version') - if (lastVersion !== PACKAGE.version) { - localStorage.setItem('animated-java-last-version', PACKAGE.version) - openChangelogDialog() - } -}) +window.AnimatedJava = AnimatedJavaApi // Uninstall events -events.EXTRACT_MODS.subscribe(() => { - // @ts-ignore - globalThis.AnimatedJava = undefined +EVENTS.EXTRACT_MODS.subscribe(() => { + // @ts-expect-error Cannot delete type that isn't optional + delete window.AnimatedJava }) BBPlugin.register(PACKAGE.name, { @@ -118,17 +129,20 @@ BBPlugin.register(PACKAGE.name, { tags: ['Minecraft: Java Edition', 'Animation', 'Display Entities'], await_loading: true, onload() { - events.LOAD.dispatch() + // Wait for plugin system to finish loading plugins. + requestAnimationFrame(() => { + EVENTS.PLUGIN_LOAD.publish() + }) }, onunload() { - events.UNLOAD.dispatch() + EVENTS.PLUGIN_UNLOAD.publish() }, oninstall() { - events.INSTALL.dispatch() + EVENTS.PLUGIN_INSTALL.publish() openInstallPopup() }, onuninstall() { - events.UNINSTALL.dispatch() + EVENTS.PLUGIN_UNINSTALL.publish() Blockbench.showMessageBox({ title: 'Animated Java has Been Uninstalled!', message: 'In order to fully uninstall Animated Java, please restart Blockbench.', @@ -136,3 +150,13 @@ BBPlugin.register(PACKAGE.name, { }) }, }) + +EVENTS.PLUGIN_FINISHED_LOADING.subscribe(() => { + if (checkForIncompatabilities()) return + + const lastVersion = localStorage.getItem('animated-java-last-version') + if (lastVersion !== PACKAGE.version) { + localStorage.setItem('animated-java-last-version', PACKAGE.version) + openChangelogDialog() + } +}) diff --git a/src/interface/animatedJavaBarItem.ts b/src/interface/animatedJavaBarItem.ts index b09d3aa2..6e995862 100644 --- a/src/interface/animatedJavaBarItem.ts +++ b/src/interface/animatedJavaBarItem.ts @@ -1,130 +1,222 @@ +import { registerAction, registerBarMenu } from 'src/util/moddingTools' +import { pollUntilResult } from 'src/util/promises' import AnimatedJavaIcon from '../assets/animated_java_icon.svg' -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' +import { activeProjectIsBlueprintFormat, BLUEPRINT_FORMAT_ID } from '../formats/blueprint' import { cleanupExportedFiles } from '../systems/cleaner' import { exportProject } from '../systems/exporter' -import { createAction, createBarMenu } from '../util/moddingTools' import { translate } from '../util/translation' +import { openChangelogDialog } from './changelogDialog' import { openAboutDialog } from './dialog/about' import { openBlueprintSettingsDialog } from './dialog/blueprintSettings' -import { openChangelogDialog } from './changelogDialog' function createIconImg() { - const IMG = document.createElement('img') - Object.assign(IMG, { + const img = document.createElement('img') + Object.assign(img, { src: AnimatedJavaIcon, width: 16, height: 16, }) - Object.assign(IMG.style, { + Object.assign(img.style, { position: 'relative', top: '2px', borderRadius: '2px', marginRight: '6px', boxShadow: '1px 1px 1px #000000aa', }) - return IMG -} -const MENU_ID = `${PACKAGE.name}:menu` as `animated_java:menu` -const BLOCKBENCH_MENU_BAR = document.querySelector('#menu_bar') as HTMLDivElement -export const MENU = createBarMenu(MENU_ID, [], () => Format === BLUEPRINT_FORMAT) as BarMenu & { - label: HTMLDivElement + return img } -MENU.label.style.display = 'inline-block' -MENU.label.innerHTML = 'Animated Java' -MENU.label.prepend(createIconImg()) -BLOCKBENCH_MENU_BAR.appendChild(MENU.label) -MenuBar.addAction( - createAction(`${PACKAGE.name}:about`, { +const SEPARATOR_A = new MenuSeparator('animated-java:menu-separator/menubar-separator-a') +const SEPARATOR_B = new MenuSeparator('animated-java:menu-separator/menubar-separator-b') + +const OPEN_ABOUT = registerAction( + { id: 'animated-java:action/about' }, + { icon: 'info', category: 'animated_java', name: translate('action.open_about.name'), click() { openAboutDialog() }, - }), - MENU.id + } ) -MenuBar.addAction( - createAction(`${PACKAGE.name}:documentation`, { +const OPEN_DOCUMENTATION = registerAction( + { id: 'animated-java:action/documentation' }, + { icon: 'find_in_page', category: 'animated_java', name: translate('action.open_documentation.name'), click() { Blockbench.openLink('https://animated-java.dev/docs') }, - }), - MENU.id + } ) -MenuBar.addAction( - createAction(`${PACKAGE.name}:changelog`, { +const OPEN_CHANGELOG = registerAction( + { id: 'animated-java:action/changelog' }, + { icon: 'history', category: 'animated_java', name: translate('action.open_changelog.name'), click() { openChangelogDialog() }, - }), - MENU.id + } +) + +function areMultipleBlueprintsOpen() { + return Blockbench.ModelProject.all.filter(p => p.format.id === BLUEPRINT_FORMAT_ID).length > 1 +} + +async function exportAll(debugMode: boolean) { + const selectedProject = Project + const blueprints = Blockbench.ModelProject.all.filter(p => p.format.id === BLUEPRINT_FORMAT_ID) + let success = true + for (const project of blueprints) { + project.select() + await new Promise(resolve => requestAnimationFrame(resolve)) + success = await exportProject({ debugMode }) + if (!success) break + await new Promise(resolve => requestAnimationFrame(resolve)) + } + if (success) selectedProject?.select() +} + +const EXPORT_ALL_DEBUG = registerAction( + { id: 'animated-java:action/export-all-debug' }, + { + icon: 'bug_report', + category: 'animated_java', + name: translate('action.export_all_debug.name'), + description: translate('action.export_all_debug.description'), + condition: areMultipleBlueprintsOpen, + click() { + console.log('Exporting all open Blueprints in development mode...') + void exportAll(true) + }, + } ) -MENU.structure.push(new MenuSeparator()) +const EXPORT_ALL = registerAction( + { id: 'animated-java:action/export-all' }, + { + icon: 'insert_drive_file', + category: 'animated_java', + name: translate('action.export_all.name'), + description: translate('action.export_all.description'), + condition: areMultipleBlueprintsOpen, + click() { + console.log('Exporting all open Blueprints...') + void exportAll(false) + }, + } +) -MenuBar.addAction( - createAction(`${PACKAGE.name}:blueprint_settings`, { +const OPEN_BLUEPRINT_SETTINGS = registerAction( + { id: 'animated-java:action/blueprint-settings' }, + { icon: 'settings', category: 'animated_java', name: translate('action.open_blueprint_settings.name'), - condition() { - return Format === BLUEPRINT_FORMAT - }, + condition: activeProjectIsBlueprintFormat, click() { openBlueprintSettingsDialog() }, - }), - MENU.id + } ) -MenuBar.menus[MENU_ID].structure.push({ - id: 'animated_java:extract-open', - name: translate('action.extract.name'), - icon: 'fa-trash-can', - searchable: false, - children: [], - condition() { - return Format === BLUEPRINT_FORMAT - }, -}) - -MenuBar.addAction( - createAction(`${PACKAGE.name}:extract`, { +const EXTRACT = registerAction( + { id: 'animated-java:action/extract' }, + { icon: 'fa-trash-can', category: 'animated_java', name: translate('action.extract.confirm'), - condition() { - return Format === BLUEPRINT_FORMAT - }, + condition: activeProjectIsBlueprintFormat, click() { void cleanupExportedFiles() }, - }), - MENU_ID + '.animated_java:extract-open' + } +) + +const EXPORT_DEBUG = registerAction( + { id: 'animated-java:action/export-debug' }, + { + icon: 'bug_report', + category: 'animated_java', + name: translate('action.export_debug.name'), + condition: activeProjectIsBlueprintFormat, + click() { + void exportProject({ debugMode: true }) + }, + keybind: new Keybind({ ctrl: true, key: 69 /* E */ }), + } ) -MenuBar.addAction( - createAction(`${PACKAGE.name}:export`, { +const EXPORT = registerAction( + { id: 'animated-java:action/export' }, + { icon: 'insert_drive_file', category: 'animated_java', name: translate('action.export.name'), - condition() { - return Format === BLUEPRINT_FORMAT - }, + condition: activeProjectIsBlueprintFormat, click() { void exportProject() }, - }), - MENU.id + keybind: new Keybind({ ctrl: true, shift: true, key: 69 /* E */ }), + } ) + +function createExtractSubMenu() { + if (EXTRACT.get() == undefined) return + return { + id: 'animated_java:submenu/extract', + name: translate('action.extract.name'), + icon: 'fa-trash-can', + searchable: false, + children: [EXTRACT.get()], + condition: activeProjectIsBlueprintFormat, + } +} + +const MENUBAR = registerBarMenu({ id: 'animated-java:menubar/main' }, []) + +MENUBAR.onCreated(menubar => { + menubar.label.style.display = 'inline-block' + menubar.label.innerHTML = translate('menubar.label') + menubar.label.prepend(createIconImg()) + + MenuBar.update() + + void pollUntilResult( + () => { + const items = [ + OPEN_ABOUT.get(), + OPEN_DOCUMENTATION.get(), + OPEN_CHANGELOG.get(), + SEPARATOR_A, + EXPORT_ALL_DEBUG.get(), + EXPORT_ALL.get(), + SEPARATOR_B, + OPEN_BLUEPRINT_SETTINGS.get(), + createExtractSubMenu(), + EXPORT_DEBUG.get(), + EXPORT.get(), + ] + + if (items.every(i => i != undefined)) { + return items + } + }, + // Stop polling if the menu is removed + () => !MENUBAR.get() + ).then(items => { + for (const item of items) { + if (!(item instanceof Action)) continue + // Initialize each action + menubar.addAction(item) + } + // Overwrite structure + menubar.structure = items + }) +}) diff --git a/src/interface/changelogDialog.ts b/src/interface/changelogDialog.ts index d3070fd7..5c68c1a8 100644 --- a/src/interface/changelogDialog.ts +++ b/src/interface/changelogDialog.ts @@ -9,9 +9,10 @@ export function openChangelogDialog() { new SvelteDialog({ id: DIALOG_ID, title: translate('dialog.changelog_dialog.title'), - width: 600, - component: ChangelogDialog, - props: {}, + width: 800, + content: { + component: ChangelogDialog, + }, buttons: ['OK!'], preventKeybinds: true, }).show() diff --git a/src/interface/dialog/about.ts b/src/interface/dialog/about.ts index 050186e3..5d31bede 100644 --- a/src/interface/dialog/about.ts +++ b/src/interface/dialog/about.ts @@ -1,5 +1,5 @@ -import { PACKAGE } from '../../constants' import AboutSvelte from '../../components/about.svelte' +import { PACKAGE } from '../../constants' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -8,8 +8,9 @@ export function openAboutDialog() { id: `${PACKAGE.name}:aboutDialog`, title: translate('dialog.about.title'), width: 700, - component: AboutSvelte, - props: {}, + content: { + component: AboutSvelte, + }, buttons: [translate('dialog.about.close_button')], preventKeybinds: true, }).show() diff --git a/src/interface/dialog/animationProperties.ts b/src/interface/dialog/animationProperties.ts index c867ecf3..9d52ecea 100644 --- a/src/interface/dialog/animationProperties.ts +++ b/src/interface/dialog/animationProperties.ts @@ -16,12 +16,14 @@ export function openAnimationPropertiesDialog(animation: _Animation) { id: DIALOG_ID, title: translate('dialog.animation_properties.title', animation.name), width: 600, - component: AniamtionPropertiesSvelteComponent, - props: { - animationName, - loopMode, - loopDelay, - excludedNodes, + content: { + component: AniamtionPropertiesSvelteComponent, + props: { + animationName, + loopMode, + loopDelay, + excludedNodes, + }, }, preventKeybinds: true, onConfirm() { @@ -30,6 +32,8 @@ export function openAnimationPropertiesDialog(animation: _Animation) { animation.loop = loopMode.get() as any animation.loop_delay = loopDelay.get().toString() animation.excluded_nodes = excludedNodes.get() + + Project!.saved = false }, }).show() } diff --git a/src/interface/dialog/blueprintSettings.ts b/src/interface/dialog/blueprintSettings.ts index ba5fad0a..43a7cbc5 100644 --- a/src/interface/dialog/blueprintSettings.ts +++ b/src/interface/dialog/blueprintSettings.ts @@ -1,9 +1,11 @@ -import { updateBoundingBox } from '../../blueprintFormat' -import { defaultValues, ExportMode } from '../../blueprintSettings' +import KofiPopup from 'src/components/kofiPopup.svelte' +import { updateAllCubeOutlines } from 'src/mods/cube' +import { SUPPORTED_MINECRAFT_VERSIONS } from 'src/systems/global' import BlueprintSettingsDialogSvelteComponent from '../../components/blueprintSettingsDialog.svelte' import { PACKAGE } from '../../constants' -import { MinecraftVersion } from '../../systems/global' -import { sanitizePathName } from '../../util/minecraftUtil' +import { updateRenderBoxPreview, updateRotationLock } from '../../formats/blueprint' +import { defaultValues, type ExportMode } from '../../formats/blueprint/settings' +import { sanitizeStorageKey } from '../../util/minecraftUtil' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -18,23 +20,25 @@ function getSettings() { }), textureSizeX: new Valuable(Project!.texture_width), textureSizeY: new Valuable(Project!.texture_height), - showBoundingBox: new Valuable(Project!.animated_java.show_bounding_box), - autoBoundingBox: new Valuable(Project!.animated_java.auto_bounding_box), - boundingBoxX: new Valuable(Project!.animated_java.bounding_box[0]), - boundingBoxY: new Valuable(Project!.animated_java.bounding_box[1]), + showRenderBox: new Valuable(Project!.animated_java.show_render_box), + autoRenderBox: new Valuable(Project!.animated_java.auto_render_box), + renderBoxX: new Valuable(Project!.animated_java.render_box[0]), + renderBoxY: new Valuable(Project!.animated_java.render_box[1]), // Export Settings enablePluginMode: new Valuable(Project!.animated_java.enable_plugin_mode), exportNamespace: new Valuable(Project!.animated_java.export_namespace, value => { if (!value) { return defaultValues.export_namespace } - return sanitizePathName(value) + return sanitizeStorageKey(value) }), resourcePackExportMode: new Valuable( Project!.animated_java.resource_pack_export_mode as string ), dataPackExportMode: new Valuable(Project!.animated_java.data_pack_export_mode as string), - targetMinecraftVersions: new Valuable(Project!.animated_java.target_minecraft_versions), + targetMinecraftVersion: new Valuable( + Project!.animated_java.target_minecraft_version as string + ), // Resource Pack Settings displayItem: new Valuable(Project!.animated_java.display_item, value => { if (!value) { @@ -52,14 +56,14 @@ function getSettings() { Project!.animated_java.enable_advanced_data_pack_settings ), dataPack: new Valuable(Project!.animated_java.data_pack), - summonCommands: new Valuable(Project!.animated_java.summon_commands), - removeCommands: new Valuable(Project!.animated_java.remove_commands), - tickingCommands: new Valuable(Project!.animated_java.ticking_commands), + onSummonFunction: new Valuable(Project!.animated_java.on_summon_function), + onRemoveFunction: new Valuable(Project!.animated_java.on_remove_function), + onPreTickFunction: new Valuable(Project!.animated_java.on_pre_tick_function), + onPostTickFunction: new Valuable(Project!.animated_java.on_post_tick_function), interpolationDuration: new Valuable(Project!.animated_java.interpolation_duration), teleportationDuration: new Valuable(Project!.animated_java.teleportation_duration), + autoUpdateRigOrientation: new Valuable(Project!.animated_java.auto_update_rig_orientation), useStorageForAnimation: new Valuable(Project!.animated_java.use_storage_for_animation), - showFunctionErrors: new Valuable(Project!.animated_java.show_function_errors), - showOutdatedWarning: new Valuable(Project!.animated_java.show_outdated_warning), // Plugin Settings bakedAnimations: new Valuable(Project!.animated_java.baked_animations), jsonFile: new Valuable(Project!.animated_java.json_file), @@ -72,9 +76,9 @@ function setSettings(settings: ReturnType) { setProjectResolution(settings.textureSizeX.get(), settings.textureSizeY.get(), true) - Project.animated_java.show_bounding_box = settings.showBoundingBox.get() - Project.animated_java.auto_bounding_box = settings.autoBoundingBox.get() - Project.animated_java.bounding_box = [settings.boundingBoxX.get(), settings.boundingBoxY.get()] + Project.animated_java.show_render_box = settings.showRenderBox.get() + Project.animated_java.auto_render_box = settings.autoRenderBox.get() + Project.animated_java.render_box = [settings.renderBoxX.get(), settings.renderBoxY.get()] // Export Settings Project.animated_java.enable_plugin_mode = settings.enablePluginMode.get() @@ -83,8 +87,8 @@ function setSettings(settings: ReturnType) { Project.animated_java.resource_pack_export_mode = settings.resourcePackExportMode.get() as ExportMode Project.animated_java.data_pack_export_mode = settings.dataPackExportMode.get() as ExportMode - Project.animated_java.target_minecraft_versions = - settings.targetMinecraftVersions.get() as MinecraftVersion[] + Project.animated_java.target_minecraft_version = + settings.targetMinecraftVersion.get() as SUPPORTED_MINECRAFT_VERSIONS // Resource Pack Settings Project.animated_java.display_item = settings.displayItem.get() Project.animated_java.custom_model_data_offset = settings.customModelDataOffset.get() @@ -95,14 +99,14 @@ function setSettings(settings: ReturnType) { Project.animated_java.enable_advanced_data_pack_settings = settings.enableAdvancedDataPackSettings.get() Project.animated_java.data_pack = settings.dataPack.get() - Project.animated_java.summon_commands = settings.summonCommands.get() - Project.animated_java.remove_commands = settings.removeCommands.get() - Project.animated_java.ticking_commands = settings.tickingCommands.get() + Project.animated_java.on_summon_function = settings.onSummonFunction.get() + Project.animated_java.on_remove_function = settings.onRemoveFunction.get() + Project.animated_java.on_pre_tick_function = settings.onPreTickFunction.get() + Project.animated_java.on_post_tick_function = settings.onPostTickFunction.get() Project.animated_java.interpolation_duration = settings.interpolationDuration.get() Project.animated_java.teleportation_duration = settings.teleportationDuration.get() + Project.animated_java.auto_update_rig_orientation = settings.autoUpdateRigOrientation.get() Project.animated_java.use_storage_for_animation = settings.useStorageForAnimation.get() - Project.animated_java.show_function_errors = settings.showFunctionErrors.get() - Project.animated_java.show_outdated_warning = settings.showOutdatedWarning.get() // Plugin Settings Project.animated_java.baked_animations = settings.bakedAnimations.get() Project.animated_java.json_file = settings.jsonFile.get() @@ -116,13 +120,24 @@ export function openBlueprintSettingsDialog() { return new SvelteDialog({ id: `${PACKAGE.name}:blueprintSettingsDialog`, title: translate('dialog.blueprint_settings.title'), - width: 700, - component: BlueprintSettingsDialogSvelteComponent, - props: settings, + width: 800, + content: { + component: BlueprintSettingsDialogSvelteComponent, + props: settings, + }, + extra: { + component: KofiPopup, + }, + contentStyle: { + marginTop: '10px', + }, preventKeybinds: true, onConfirm() { setSettings(settings) - updateBoundingBox() + updateRenderBoxPreview() + updateRotationLock() + updateAllCubeOutlines() + Canvas.updateAll() }, }).show() } diff --git a/src/interface/dialog/boneConfig.ts b/src/interface/dialog/boneConfig.ts index 94dacfa5..23cee162 100644 --- a/src/interface/dialog/boneConfig.ts +++ b/src/interface/dialog/boneConfig.ts @@ -1,8 +1,8 @@ -import { BLUEPRINT_FORMAT } from '../../blueprintFormat' -import { BoneConfig } from '../../nodeConfigs' +import { registerAction } from 'src/util/moddingTools' import BoneConfigDialogSvelteComponent from '../../components/boneConfigDialog.svelte' import { PACKAGE } from '../../constants' -import { createAction } from '../../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { BoneConfig } from '../../nodeConfigs' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -31,7 +31,7 @@ function propagateInheritanceDown(group: Group, config: BoneConfig, variant?: st for (const child of group.children) { if (!(child instanceof Group)) continue const childConfig = variant ? child.configs.variants[variant] : child.configs.default - if (childConfig && childConfig.inherit_settings) { + if (childConfig?.inherit_settings) { const childBoneConfig = BoneConfig.fromJSON(childConfig) childBoneConfig.inheritFrom(config) if (variant) child.configs.variants[variant] = childBoneConfig.toJSON() @@ -84,25 +84,27 @@ export function openBoneConfigDialog(bone: Group) { new SvelteDialog({ id: `${PACKAGE.name}:boneConfig`, title: translate('dialog.bone_config.title'), - width: 400, - component: BoneConfigDialogSvelteComponent, - props: { - variant: Variant.selected!, - customName, - customNameVisible, - billboard, - overrideBrightness, - brightnessOverride, - enchanted, - glowing, - overrideGlowColor, - glowColor, - inheritSettings, - invisible, - nbt, - shadowRadius, - shadowStrength, - useNBT, + width: 600, + content: { + component: BoneConfigDialogSvelteComponent, + props: { + variant: Variant.selected!, + customName, + customNameVisible, + billboard, + overrideBrightness, + brightnessOverride, + enchanted, + glowing, + overrideGlowColor, + glowColor, + inheritSettings, + invisible, + nbt, + shadowRadius, + shadowStrength, + useNBT, + }, }, preventKeybinds: true, onConfirm() { @@ -165,16 +167,25 @@ export function openBoneConfigDialog(bone: Group) { bone.configs.default = newConfig.toJSON() propagateInheritanceDown(bone, newConfig) } + + Project!.saved = false }, }).show() } -export const BONE_CONFIG_ACTION = createAction(`${PACKAGE.name}:bone_config`, { - icon: 'settings', - name: translate('action.open_bone_config.name'), - condition: () => Format === BLUEPRINT_FORMAT, - click: () => { - if (!Group.first_selected) return - openBoneConfigDialog(Group.first_selected) - }, +export const BONE_CONFIG_ACTION = registerAction( + { id: `animated-java:bone-config` }, + { + icon: 'settings', + name: translate('action.open_bone_config.name'), + condition: activeProjectIsBlueprintFormat, + click: () => { + if (!Group.first_selected) return + openBoneConfigDialog(Group.first_selected) + }, + } +) + +BONE_CONFIG_ACTION.onCreated(action => { + Group.prototype.menu!.addAction(action, 6) }) diff --git a/src/interface/dialog/changelog.ts b/src/interface/dialog/changelog.ts index de6aa3b6..bda76233 100644 --- a/src/interface/dialog/changelog.ts +++ b/src/interface/dialog/changelog.ts @@ -10,8 +10,7 @@ export function openChangelogDialog() { id: DIALOG_ID, title: translate('dialog.changelog_dialog.title'), width: 600, - component: ChangelogDialog, - props: {}, + content: { component: ChangelogDialog }, buttons: ['OK!'], preventKeybinds: true, }).show() diff --git a/src/interface/dialog/exportProgress.ts b/src/interface/dialog/exportProgress.ts index 38fa54da..0c1a39f1 100644 --- a/src/interface/dialog/exportProgress.ts +++ b/src/interface/dialog/exportProgress.ts @@ -28,11 +28,13 @@ export function openExportProgressDialog(debug?: boolean) { id: `${PACKAGE.name}:exportProgressDialog`, title: translate('dialog.export_progress.title'), width: 512, - component: ExportProgressDialogSvelteComponent, - props: { - progress: PROGRESS, - maxProgress: MAX_PROGRESS, - progressDescription: PROGRESS_DESCRIPTION, + content: { + component: ExportProgressDialogSvelteComponent, + props: { + progress: PROGRESS, + maxProgress: MAX_PROGRESS, + progressDescription: PROGRESS_DESCRIPTION, + }, }, preventKeybinds: true, buttons: [], diff --git a/src/interface/dialog/locatorConfig.ts b/src/interface/dialog/locatorConfig.ts index 5e75e6a2..109d53e8 100644 --- a/src/interface/dialog/locatorConfig.ts +++ b/src/interface/dialog/locatorConfig.ts @@ -1,8 +1,8 @@ -import { BLUEPRINT_FORMAT } from '../../blueprintFormat' +import { registerAction } from 'src/util/moddingTools' import LocatorConfigDialog from '../../components/locatorConfigDialog.svelte' import { PACKAGE } from '../../constants' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' import { LocatorConfig } from '../../nodeConfigs' -import { createAction } from '../../util/moddingTools' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -14,41 +14,53 @@ export function openLocatorConfigDialog(locator: Locator) { const useEntity = new Valuable(locatorConfig.useEntity) const entityType = new Valuable(locatorConfig.entityType) const syncPassengerRotation = new Valuable(locatorConfig.syncPassengerRotation) - const summonCommands = new Valuable(locatorConfig.summonCommands) - const tickingCommands = new Valuable(locatorConfig.tickingCommands) + const onSummonFunction = new Valuable(locatorConfig.onSummonFunction) + const onRemoveFunction = new Valuable(locatorConfig.onRemoveFunction) + const onTickFunction = new Valuable(locatorConfig.onTickFunction) new SvelteDialog({ id: `${PACKAGE.name}:locatorConfig`, title: translate('dialog.locator_config.title'), width: 600, - component: LocatorConfigDialog, - props: { - useEntity, - entityType, - syncPassengerRotation, - summonCommands, - tickingCommands, + content: { + component: LocatorConfigDialog, + props: { + useEntity, + entityType, + syncPassengerRotation, + onSummonFunction, + onRemoveFunction, + onTickFunction, + }, }, preventKeybinds: true, onConfirm() { locatorConfig.useEntity = useEntity.get() locatorConfig.entityType = entityType.get() locatorConfig.syncPassengerRotation = syncPassengerRotation.get() - locatorConfig.summonCommands = summonCommands.get() - locatorConfig.tickingCommands = tickingCommands.get() + locatorConfig.onSummonFunction = onSummonFunction.get() + locatorConfig.onRemoveFunction = onRemoveFunction.get() + locatorConfig.onTickFunction = onTickFunction.get() locator.config = locatorConfig.toJSON() }, }).show() } -export const LOCATOR_CONFIG_ACTION = createAction(`${PACKAGE.name}:locator_config`, { - icon: 'settings', - name: translate('action.open_locator_config.name'), - condition: () => Format === BLUEPRINT_FORMAT, - click: () => { - const locator = Locator.selected.at(0) - if (!locator) return - openLocatorConfigDialog(locator) - }, +const LOCATOR_CONFIG_ACTION = registerAction( + { id: `animated-java:locator-config` }, + { + icon: 'settings', + name: translate('action.open_locator_config.name'), + condition: activeProjectIsBlueprintFormat, + click: () => { + const locator = Locator.selected.at(0) + if (!locator) return + openLocatorConfigDialog(locator) + }, + } +) + +LOCATOR_CONFIG_ACTION.onCreated(action => { + Locator.prototype.menu!.addAction(action, '6') }) diff --git a/src/interface/dialog/textDisplayConfig.ts b/src/interface/dialog/textDisplayConfig.ts index 2042ed9e..fd22aa63 100644 --- a/src/interface/dialog/textDisplayConfig.ts +++ b/src/interface/dialog/textDisplayConfig.ts @@ -1,13 +1,12 @@ -import { isCurrentFormat } from '../../blueprintFormat' -import { TextDisplayConfig } from '../../nodeConfigs' +import { registerAction } from 'src/util/moddingTools' +import TextDisplayConfigDialog from '../../components/textDisplayConfigDialog.svelte' import { PACKAGE } from '../../constants' -import { createAction } from '../../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { TextDisplayConfig } from '../../nodeConfigs' +import { TextDisplay } from '../../outliner/textDisplay' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' -import { Variant } from '../../variants' -import TextDisplayConfigDialog from '../../components/textDisplayConfigDialog.svelte' -import { TextDisplay } from '../../outliner/textDisplay' export function openBoneConfigDialog(bone: TextDisplay) { // Blockbench's JSON stringifier doesn't handle custom toJSON functions, so I'm storing the config JSON in the bone instead of the actual BoneConfig object @@ -28,21 +27,22 @@ export function openBoneConfigDialog(bone: TextDisplay) { new SvelteDialog({ id: `${PACKAGE.name}:textDisplayConfigDialog`, title: translate('dialog.text_display_config.title'), - width: 400, - component: TextDisplayConfigDialog, - props: { - variant: Variant.selected, - billboard, - overrideBrightness, - brightnessOverride, - glowing, - overrideGlowColor, - glowColor, - invisible, - nbt, - shadowRadius, - shadowStrength, - useNBT, + width: 600, + content: { + component: TextDisplayConfigDialog, + props: { + billboard, + overrideBrightness, + brightnessOverride, + glowing, + overrideGlowColor, + glowColor, + invisible, + nbt, + shadowRadius, + shadowStrength, + useNBT, + }, }, preventKeybinds: true, onConfirm() { @@ -80,16 +80,31 @@ export function openBoneConfigDialog(bone: TextDisplay) { newConfig.useNBT === defaultConfig.useNBT && (newConfig.useNBT = undefined) bone.config = newConfig.toJSON() + + Project!.saved = false }, }).show() } -export const TEXT_DISPLAY_CONFIG_ACTION = createAction(`${PACKAGE.name}:text_display_config`, { - icon: 'settings', - name: translate('action.open_text_display_config.name'), - condition: () => isCurrentFormat(), - click: () => { - if (TextDisplay.selected.length === 0) return - openBoneConfigDialog(TextDisplay.selected[0]) - }, +export const TEXT_DISPLAY_CONFIG_ACTION = registerAction( + { id: `animated-java:text-display-config` }, + { + icon: 'settings', + name: translate('action.open_text_display_config.name'), + condition: () => activeProjectIsBlueprintFormat(), + click: () => { + if (TextDisplay.selected.length === 0) return + openBoneConfigDialog(TextDisplay.selected[0]) + }, + } +) + +TEXT_DISPLAY_CONFIG_ACTION.onCreated(action => { + TextDisplay.prototype.menu = new Menu([ + ...Outliner.control_menu_group, + action, + '_', + 'rename', + 'delete', + ]) }) diff --git a/src/interface/dialog/unexpectedError.ts b/src/interface/dialog/unexpectedError.ts index c15aa402..7a31fce7 100644 --- a/src/interface/dialog/unexpectedError.ts +++ b/src/interface/dialog/unexpectedError.ts @@ -8,9 +8,9 @@ export function openUnexpectedErrorDialog(error: Error) { id: `${PACKAGE.name}:unexpectedError`, title: translate('dialog.unexpected_error.title'), width: 600, - component: UnexpectedErrorDialog, - props: { - error, + content: { + component: UnexpectedErrorDialog, + props: { error }, }, preventKeybinds: true, buttons: [translate('dialog.unexpected_error.close_button')], diff --git a/src/interface/dialog/vanillaBlockDisplayConfig.ts b/src/interface/dialog/vanillaBlockDisplayConfig.ts index c1773a56..8a21ba9c 100644 --- a/src/interface/dialog/vanillaBlockDisplayConfig.ts +++ b/src/interface/dialog/vanillaBlockDisplayConfig.ts @@ -1,13 +1,12 @@ -import { isCurrentFormat } from '../../blueprintFormat' -import { BoneConfig } from '../../nodeConfigs' +import { registerAction } from 'src/util/moddingTools' +import VanillaBlockDisplayConfigDialog from '../../components/vanillaBlockDisplayConfigDialog.svelte' import { PACKAGE } from '../../constants' -import { createAction } from '../../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { BoneConfig } from '../../nodeConfigs' +import { VanillaBlockDisplay } from '../../outliner/vanillaBlockDisplay' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' -import { Variant } from '../../variants' -import { VanillaBlockDisplay } from '../../outliner/vanillaBlockDisplay' -import VanillaBlockDisplayConfigDialog from '../../components/vanillaBlockDisplayConfigDialog.svelte' export function openVanillaBlockDisplayConfigDialog(display: VanillaBlockDisplay) { // Blockbench's JSON stringifier doesn't handle custom toJSON functions, so I'm storing the config JSON in the bone instead of the actual BoneConfig object @@ -30,23 +29,24 @@ export function openVanillaBlockDisplayConfigDialog(display: VanillaBlockDisplay new SvelteDialog({ id: `${PACKAGE.name}:vanillaItemDisplayConfigDialog`, title: translate('dialog.vanilla_block_display_config.title'), - width: 400, - component: VanillaBlockDisplayConfigDialog, - props: { - variant: Variant.selected, - customName, - customNameVisible, - billboard, - overrideBrightness, - brightnessOverride, - glowing, - overrideGlowColor, - glowColor, - invisible, - nbt, - shadowRadius, - shadowStrength, - useNBT, + width: 600, + content: { + component: VanillaBlockDisplayConfigDialog, + props: { + customName, + customNameVisible, + billboard, + overrideBrightness, + brightnessOverride, + glowing, + overrideGlowColor, + glowColor, + invisible, + nbt, + shadowRadius, + shadowStrength, + useNBT, + }, }, preventKeybinds: true, onConfirm() { @@ -89,19 +89,31 @@ export function openVanillaBlockDisplayConfigDialog(display: VanillaBlockDisplay newConfig.useNBT === defaultConfig.useNBT && (newConfig.useNBT = undefined) display.config = newConfig.toJSON() + + Project!.saved = false }, }).show() } -export const VANILLA_BLOCK_DISPLAY_CONFIG_ACTION = createAction( - `${PACKAGE.name}:open_vanilla_block_display_config`, +export const VANILLA_BLOCK_DISPLAY_CONFIG_ACTION = registerAction( + { id: `animated-java:open-vanilla-block-display-config` }, { icon: 'settings', name: translate('action.open_vanilla_block_display_config.name'), - condition: () => isCurrentFormat(), + condition: () => activeProjectIsBlueprintFormat(), click: () => { if (VanillaBlockDisplay.selected.length === 0) return openVanillaBlockDisplayConfigDialog(VanillaBlockDisplay.selected[0]) }, } ) + +VANILLA_BLOCK_DISPLAY_CONFIG_ACTION.onCreated(action => { + VanillaBlockDisplay.prototype.menu = new Menu([ + ...Outliner.control_menu_group, + action, + '_', + 'rename', + 'delete', + ]) +}) diff --git a/src/interface/dialog/vanillaItemDisplayConfig.ts b/src/interface/dialog/vanillaItemDisplayConfig.ts index 1cf0ae90..3af832c0 100644 --- a/src/interface/dialog/vanillaItemDisplayConfig.ts +++ b/src/interface/dialog/vanillaItemDisplayConfig.ts @@ -1,13 +1,12 @@ -import { isCurrentFormat } from '../../blueprintFormat' -import { BoneConfig } from '../../nodeConfigs' +import { registerAction } from 'src/util/moddingTools' +import VanillaItemDisplayConfigDialog from '../../components/vanillaItemDisplayConfigDialog.svelte' import { PACKAGE } from '../../constants' -import { createAction } from '../../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { BoneConfig } from '../../nodeConfigs' +import { VanillaItemDisplay } from '../../outliner/vanillaItemDisplay' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' -import { Variant } from '../../variants' -import { VanillaItemDisplay } from '../../outliner/vanillaItemDisplay' -import VanillaItemDisplayConfigDialog from '../../components/vanillaItemDisplayConfigDialog.svelte' export function openVanillaItemDisplayConfigDialog(display: VanillaItemDisplay) { // Blockbench's JSON stringifier doesn't handle custom toJSON functions, so I'm storing the config JSON in the bone instead of the actual BoneConfig object @@ -30,23 +29,24 @@ export function openVanillaItemDisplayConfigDialog(display: VanillaItemDisplay) new SvelteDialog({ id: `${PACKAGE.name}:vanillaItemDisplayConfigDialog`, title: translate('dialog.vanilla_item_display_config.title'), - width: 400, - component: VanillaItemDisplayConfigDialog, - props: { - variant: Variant.selected, - customName, - customNameVisible, - billboard, - overrideBrightness, - brightnessOverride, - glowing, - overrideGlowColor, - glowColor, - invisible, - nbt, - shadowRadius, - shadowStrength, - useNBT, + width: 600, + content: { + component: VanillaItemDisplayConfigDialog, + props: { + customName, + customNameVisible, + billboard, + overrideBrightness, + brightnessOverride, + glowing, + overrideGlowColor, + glowColor, + invisible, + nbt, + shadowRadius, + shadowStrength, + useNBT, + }, }, preventKeybinds: true, onConfirm() { @@ -89,19 +89,31 @@ export function openVanillaItemDisplayConfigDialog(display: VanillaItemDisplay) newConfig.useNBT === defaultConfig.useNBT && (newConfig.useNBT = undefined) display.config = newConfig.toJSON() + + Project!.saved = false }, }).show() } -export const VANILLA_ITEM_DISPLAY_CONFIG_ACTION = createAction( - `${PACKAGE.name}:open_vanilla_item_display_config`, +export const VANILLA_ITEM_DISPLAY_CONFIG_ACTION = registerAction( + { id: `animated-java:open-vanilla-item-display-config` }, { icon: 'settings', name: translate('action.open_vanilla_item_display_config.name'), - condition: () => isCurrentFormat(), + condition: () => activeProjectIsBlueprintFormat(), click: () => { if (VanillaItemDisplay.selected.length === 0) return openVanillaItemDisplayConfigDialog(VanillaItemDisplay.selected[0]) }, } ) + +VANILLA_ITEM_DISPLAY_CONFIG_ACTION.onCreated(action => { + VanillaItemDisplay.prototype.menu = new Menu([ + ...Outliner.control_menu_group, + action, + '_', + 'rename', + 'delete', + ]) +}) diff --git a/src/interface/dialog/variantConfig.ts b/src/interface/dialog/variantConfig.ts index afcec7dd..d19f2759 100644 --- a/src/interface/dialog/variantConfig.ts +++ b/src/interface/dialog/variantConfig.ts @@ -1,6 +1,6 @@ import VariantConfigDialogSvelteComponent from '../../components/variantConfigDialog.svelte' import { PACKAGE } from '../../constants' -import { events } from '../../util/events' +import EVENTS from '../../util/events' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -17,16 +17,18 @@ export function openVariantConfigDialog(variant: Variant) { new SvelteDialog({ id: `${PACKAGE.name}:variantConfig`, title: translate('dialog.variant_config.title'), - width: 512, - component: VariantConfigDialogSvelteComponent, - props: { - variant, - displayName, - name, - uuid, - textureMap, - generateNameFromDisplayName, - excludedNodes, + width: 700, + content: { + component: VariantConfigDialogSvelteComponent, + props: { + variant, + displayName, + name, + uuid, + textureMap, + generateNameFromDisplayName, + excludedNodes, + }, }, preventKeybinds: true, onConfirm() { @@ -36,8 +38,10 @@ export function openVariantConfigDialog(variant: Variant) { variant.textureMap = textureMap variant.generateNameFromDisplayName = generateNameFromDisplayName.get() variant.excludedNodes = excludedNodes.get() - events.UPDATE_VARIANT.dispatch(variant) + EVENTS.UPDATE_VARIANT.publish(variant) variant.select() + + Project!.saved = false }, }).show() } diff --git a/src/interface/importAJModelLoader.ts b/src/interface/importAJModelLoader.ts deleted file mode 100644 index a0d31fb6..00000000 --- a/src/interface/importAJModelLoader.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { SvelteComponentDev } from 'svelte/internal' -import { BLUEPRINT_CODEC, IBlueprintFormatJSON } from '../blueprintFormat' -import ImportAjModelLoaderDialog from '../components/importAJModelLoaderDialog.svelte' -import { PACKAGE } from '../constants' -import * as ModelDatFixerUpper from '../systems/modelDataFixerUpper' -import { injectSvelteCompomponent } from '../util/injectSvelteComponent' -import { sanitizePathName } from '../util/minecraftUtil' -import { createModelLoader } from '../util/moddingTools' -import { translate } from '../util/translation' -import { openUnexpectedErrorDialog } from './dialog/unexpectedError' - -let activeComponent: SvelteComponentDev | null = null - -createModelLoader(`${PACKAGE.name}-upgradeAJModelLoader`, { - icon: 'upload_file', - category: 'animated_java', - name: translate('action.upgrade_old_aj_model_loader.name'), - condition() { - return true - }, - format_page: { - component: { - template: `
`, - }, - }, - onFormatPage() { - if (activeComponent) { - activeComponent.$destroy() - } - void injectSvelteCompomponent({ - component: ImportAjModelLoaderDialog, - props: {}, - elementSelector() { - return document.querySelector(`#${PACKAGE.name}-upgradeAJModelLoader-target`) - }, - postMount(el) { - activeComponent = el - }, - injectIndex: 2, - }) - }, -}) - -export function convertAJModelToBlueprint(path: string) { - try { - console.log(`Convert .ajmodel: ${path}`) - const blueprint = ModelDatFixerUpper.process( - JSON.parse(fs.readFileSync(path, 'utf8')) - ) as IBlueprintFormatJSON - - BLUEPRINT_CODEC.load(blueprint, { - name: 'Upgrade .ajmodel to Blueprint', - path, - }) - blueprint.blueprint_settings!.export_namespace ??= sanitizePathName(Project!.name) - - requestAnimationFrame(() => { - Project!.save_path = '' - Project!.export_path = '' - Project!.openSettings() - }) - } catch (e) { - console.error(e) - openUnexpectedErrorDialog(e as Error) - } -} diff --git a/src/interface/index.ts b/src/interface/index.ts deleted file mode 100644 index ddad7aca..00000000 --- a/src/interface/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import './dialog/about' -import './dialog/animationProperties' -import './dialog/blueprintSettings' -import './dialog/boneConfig' -import './dialog/changelog' -import './dialog/exportProgress' -import './dialog/locatorConfig' -import './dialog/textDisplayConfig' -import './dialog/unexpectedError' -import './dialog/vanillaBlockDisplayConfig' -import './dialog/vanillaItemDisplayConfig' -import './dialog/variantConfig' - -import './panel/customKeyframe' -import './panel/textDisplayElement' -import './panel/vanillaBlockDisplayElement' -import './panel/vanillaItemDisplayElement' -import './panel/variants' - -import './popup/animatedJavaLoading' -import './popup/blueprintLoading' -import './popup/incompatabilityPopup' -import './popup/installed' - -import './importAJModelLoader' -import './animatedJavaBarItem' -import './keyframeEasings' diff --git a/src/interface/keyframeEasings.ts b/src/interface/keyframeEasings.ts index 787d916b..bb0232e6 100644 --- a/src/interface/keyframeEasings.ts +++ b/src/interface/keyframeEasings.ts @@ -1,10 +1,8 @@ +import { registerMountSvelteComponentMod } from 'src/util/mountSvelteComponent' import KeyframeEasingsSvelte from '../components/keyframeEasings.svelte' -import { injectSvelteCompomponentMod } from '../util/injectSvelteComponent' -injectSvelteCompomponentMod({ +registerMountSvelteComponentMod({ + id: 'animated-java:mounted-svelte/keyframe-easings', component: KeyframeEasingsSvelte, - props: {}, - elementSelector() { - return $('#panel_keyframe')[0] - }, + target: '#panel_keyframe', }) diff --git a/src/interface/panel/customKeyframe.ts b/src/interface/panel/customKeyframe.ts index df918b17..c817e1b3 100644 --- a/src/interface/panel/customKeyframe.ts +++ b/src/interface/panel/customKeyframe.ts @@ -1,54 +1,67 @@ -import type { SvelteComponentDev } from 'svelte/internal' -import { isCurrentFormat } from '../../blueprintFormat' -import CustomKeyframePanelSvelteComponent from '../../components/customKeyframePanel.svelte' -import { CUSTOM_CHANNELS } from '../../mods/customKeyframesMod' -import { events } from '../../util/events' -import { injectSvelteCompomponent } from '../../util/injectSvelteComponent' -import { translate } from '../../util/translation' - -let currentPanel: SvelteComponentDev | undefined = undefined - -export function injectCustomKeyframePanel(selectedKeyframe: _Keyframe) { - if ( - !isCurrentFormat() || - !selectedKeyframe || - !CUSTOM_CHANNELS.includes(selectedKeyframe.channel) - ) - return - - const element = document.querySelector( - '#panel_keyframe .panel_vue_wrapper .keyframe_data_point' - ) - if (!element) { - console.warn( - 'Failed to find keyframe panel element. Aborting custom keyframe panel injection.' - ) - return - } - for (const child of [...element.children]) { - child.remove() - } - - void injectSvelteCompomponent({ - component: CustomKeyframePanelSvelteComponent, - props: { - selectedKeyframe, - }, - elementSelector() { - return element - }, - postMount(comp) { - const label = jQuery('#panel_keyframe .panel_vue_wrapper #keyframe_type_label label') - if (label && selectedKeyframe.channel) { - const property = selectedKeyframe.animator.channels[selectedKeyframe.channel] - label.text(translate('panel.keyframe.keyframe_title', `${property.name}`)) - } - currentPanel?.$destroy() - currentPanel = comp - }, - }) -} - -events.SELECT_KEYFRAME.subscribe(kf => { - injectCustomKeyframePanel(kf) +import CommandsKeyframePanel from 'src/components/keyframePanels/commandsKeyframePanel.svelte' +import VariantKeyframePanel from 'src/components/keyframePanels/variantKeyframePanel.svelte' +import { EFFECT_ANIMATOR_CHANNELS, isCustomKeyframeChannel } from 'src/mods/customKeyframes' +import EVENTS from 'src/util/events' +import { registerMod } from 'src/util/moddingTools' +import { translate } from 'src/util/translation' +import { mountSvelteComponent } from '../../util/mountSvelteComponent' + +registerMod({ + id: 'animated-java:panel/custom-keyframe-data-points', + + apply: () => { + Language.data['timeline.variant'] = translate('effect_animator.timeline.variant') + Language.data['timeline.function'] = translate('effect_animator.timeline.function') + + let mounted: VariantKeyframePanel | CommandsKeyframePanel | undefined + + const unsubs = [ + EVENTS.UPDATE_KEYFRAME_SELECTION.subscribe(() => { + mounted?.$destroy() + mounted = undefined + + const keyframe = Timeline.selected.at(0) + const isCustomKeyframe = isCustomKeyframeChannel(keyframe?.channel ?? '') + + if (keyframe && isCustomKeyframe) { + let component: any + switch (keyframe.channel) { + case EFFECT_ANIMATOR_CHANNELS.VARIANT: + component = VariantKeyframePanel + break + + case EFFECT_ANIMATOR_CHANNELS.FUNCTION: + component = CommandsKeyframePanel + break + + default: + console.error(`Unknown custom keyframe channel: '${keyframe.channel}'`) + return + } + + mounted = mountSvelteComponent({ + component, + props: { keyframe }, + target: '#panel_keyframe .panel_vue_wrapper .keyframe_data_point', + hideTargetChildren: true, + }) + } + }), + + EVENTS.UNSELECT_AJ_PROJECT.subscribe(() => { + mounted?.$destroy() + mounted = undefined + }), + ] + + return { mounted, unsubs } + }, + + revert: ({ mounted, unsubs }) => { + delete Language.data['timeline.variant'] + delete Language.data['timeline.function'] + + unsubs.forEach(unsub => unsub()) + mounted?.$destroy() + }, }) diff --git a/src/interface/panel/textDisplayElement.ts b/src/interface/panel/textDisplayElement.ts index dc42a04c..edfeb65d 100644 --- a/src/interface/panel/textDisplayElement.ts +++ b/src/interface/panel/textDisplayElement.ts @@ -1,21 +1,86 @@ -import { isCurrentFormat } from '../../blueprintFormat' +import { JsonTextParser } from 'src/systems/jsonText/parser' +import { registerMountSvelteComponentMod } from 'src/util/mountSvelteComponent' import TextDisplayElementPanel from '../../components/textDisplayElementPanel.svelte' -import { PACKAGE } from '../../constants' -import { Alignment, TextDisplay } from '../../outliner/textDisplay' -import { injectSvelteCompomponentMod } from '../../util/injectSvelteComponent' -import { floatToHex } from '../../util/misc' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { type Alignment, TextDisplay } from '../../outliner/textDisplay' import { translate } from '../../util/translation' -injectSvelteCompomponentMod({ +registerMountSvelteComponentMod({ + id: 'animated-java:panel/text-display', component: TextDisplayElementPanel, - props: {}, - elementSelector() { - return document.querySelector('#panel_element') - }, + target: '#panel_element', }) +type Grammar = ReturnType +type GrammarValue = NonNullable + +function addPrismSyntaxForSnbtTextComponents() { + const quotes: GrammarValue = { + pattern: /^['"]|['"]$/, + alias: 'quotation', + } + + Prism.languages.snbtTextComponent = Prism.languages.extend('json', { + punctuation: /[,:]/, + brackets: { + pattern: /[{}[\]]/g, + }, + property: [ + { + pattern: /('|")?\w+\1\s*(?=\s*:)/, + inside: { + punctuation: quotes, + }, + }, + ], + string: [ + { + pattern: /("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/, + greedy: true, + inside: { + punctuation: quotes, + 'named-unicode-escape-sequence': { + pattern: /\\N\{ *[\w ]+ *\}/, + alias: 'escape-sequence', + inside: { + constant: { + pattern: /(\\N\{ *)[\w ]+?(?= *\})/, + lookbehind: true, + }, + }, + }, + 'unicode-escape-sequence': { + pattern: /\\(?:x[\da-fA-F]{2}|u[\da-fA-F]{4}|U[\da-fA-F]{8})/, + alias: 'escape-sequence', + inside: { + constant: { pattern: /(?:[\da-fA-F]+)/ }, + }, + }, + 'escape-sequence': { + pattern: /\\(?:n|s|t|b|f|r|'|"|\\)/, + }, + }, + }, + { + pattern: /([:,]\s*)\b(?!true|false)\w+\b/i, + inside: { punctuation: quotes }, + lookbehind: true, + }, + ], + boolean: { + pattern: /\b(?:true|false|0b|1b)\b/i, + }, + number: /(?:(?:#|0x)[\dA-Fa-f_]{1,8}\b|[-]?(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.[\d_]+)(?:[eE][+-]?[\d_]+\b)?)/, + }) +} + +addPrismSyntaxForSnbtTextComponents() + +const TEXT_DISPLAY_CONDITION = () => + activeProjectIsBlueprintFormat() && !!TextDisplay.selected.length + export const TEXT_DISPLAY_WIDTH_SLIDER = new NumSlider( - `${PACKAGE.name}:textDisplayLineWidthSlider`, + `animated-java:text-display-line-width-slider`, { name: translate('tool.text_display.line_width.title'), icon: 'format_size', @@ -25,15 +90,15 @@ export const TEXT_DISPLAY_WIDTH_SLIDER = new NumSlider( max: 10000, interval: 1, }, - condition: () => isCurrentFormat() && !!TextDisplay.selected.length, + condition: TEXT_DISPLAY_CONDITION, get() { - const selected = TextDisplay.selected[0] - if (!selected) return 0 + const selected = TextDisplay.selected.at(0) + if (!selected) return TextDisplay.properties.lineWidth.default as number return selected.lineWidth }, change(value) { if (!Project) return - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return const newLineWidth = Math.clamp(value(selected.lineWidth), 1, 10000) if (selected.lineWidth === newLineWidth) return @@ -44,30 +109,33 @@ export const TEXT_DISPLAY_WIDTH_SLIDER = new NumSlider( ) export const TEXT_DISPLAY_BACKGROUND_COLOR_PICKER = new ColorPicker( - `${PACKAGE.name}:textDisplayBackgroundColorPicker`, + `animated-java:text-display-background-color-picker`, { name: translate('tool.text_display.background_color.title'), icon: 'format_color_fill', description: translate('tool.text_display.background_color.description'), - condition: () => isCurrentFormat() && !!TextDisplay.selected.length, + condition: TEXT_DISPLAY_CONDITION, } ) -// @ts-expect-error -TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.jq.spectrum('option', 'defaultColor', '#0000003f') +// @ts-expect-error Missing types +TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.jq.spectrum( + 'option', + 'defaultColor', + TextDisplay.properties.backgroundColor.default +) TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.get = function () { - const selected = TextDisplay.selected[0] - if (!selected) return new tinycolor('#0000003f') - return new tinycolor(selected.backgroundColor + floatToHex(selected.backgroundAlpha)) + const selected = TextDisplay.selected.at(0) + if (!selected) return tinycolor(TextDisplay.properties.backgroundColor.default) + return tinycolor(selected.backgroundColor) } -TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.set = function (this: ColorPicker, color: string) { - this.value = new tinycolor(color) - // @ts-expect-error - this.jq.spectrum('set', this.value.toHex8String()) +TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.set = function (this: ColorPicker, color: tinycolor.Instance) { + this.value = color + // @ts-expect-error Missing types + this.jq.spectrum('set', color.toHex8String()) - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return this - selected.backgroundColor = this.value.toHexString() - selected.backgroundAlpha = this.value.getAlpha() + selected.backgroundColor = color.toHex8String() return this } TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.change = function ( @@ -75,31 +143,26 @@ TEXT_DISPLAY_BACKGROUND_COLOR_PICKER.change = function ( color: InstanceType ) { if (!Project) return this - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return this - const newBackground = color.toHexString() - const newAlpha = color.getAlpha() - if (selected.backgroundColor === newBackground && selected.backgroundAlpha === newAlpha) - return this + const newBackground = color.toHex8String() + if (selected.backgroundColor === newBackground) return this selected.backgroundColor = newBackground - selected.backgroundAlpha = newAlpha Project!.saved = false return this } -export const TEXT_DISPLAY_SHADOW_TOGGLE = new Toggle(`${PACKAGE.name}:textDisplayShadowToggle`, { +export const TEXT_DISPLAY_SHADOW_TOGGLE = new Toggle(`animated-java:text-display-shadow-toggle`, { name: translate('tool.text_display.text_shadow.title'), icon: 'check_box_outline_blank', description: translate('tool.text_display.text_shadow.description'), - condition: () => isCurrentFormat() && !!TextDisplay.selected.length, - click() { - // - }, + condition: TEXT_DISPLAY_CONDITION, + default: TextDisplay.properties.shadow.default as boolean, onChange() { if (!Project) return const scope = TEXT_DISPLAY_SHADOW_TOGGLE scope.setIcon(scope.value ? 'check_box' : 'check_box_outline_blank') - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return if (selected.shadow === TEXT_DISPLAY_SHADOW_TOGGLE.value) return selected.shadow = TEXT_DISPLAY_SHADOW_TOGGLE.value @@ -113,12 +176,12 @@ TEXT_DISPLAY_SHADOW_TOGGLE.set = function (value) { } export const TEXT_DISPLAY_ALIGNMENT_SELECT = new BarSelect( - `${PACKAGE.name}:textDisplayAlignmentSelect`, + `animated-java:text-display-alignment-select`, { name: translate('tool.text_display.text_alignment.title'), icon: 'format_align_left', description: translate('tool.text_display.text_alignment.description'), - condition: () => isCurrentFormat() && !!TextDisplay.selected.length, + condition: TEXT_DISPLAY_CONDITION, options: { left: translate('tool.text_display.text_alignment.options.left'), center: translate('tool.text_display.text_alignment.options.center'), @@ -127,12 +190,12 @@ export const TEXT_DISPLAY_ALIGNMENT_SELECT = new BarSelect( } ) TEXT_DISPLAY_ALIGNMENT_SELECT.get = function () { - const selected = TextDisplay.selected[0] - if (!selected) return 'left' + const selected = TextDisplay.selected.at(0) + if (!selected) return TextDisplay.properties.align.default as Alignment return selected.align } TEXT_DISPLAY_ALIGNMENT_SELECT.set = function (this: BarSelect, value: Alignment) { - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return this this.value = value const name = this.getNameFor(value) @@ -147,20 +210,17 @@ TEXT_DISPLAY_ALIGNMENT_SELECT.set = function (this: BarSelect, value: } export const TEXT_DISPLAY_SEE_THROUGH_TOGGLE = new Toggle( - `${PACKAGE.name}:textDisplaySeeThroughToggle`, + `animated-java:text-display-see-through-toggle`, { name: translate('tool.text_display.see_through.title'), icon: 'check_box_outline_blank', description: translate('tool.text_display.see_through.description'), - condition: () => isCurrentFormat() && !!TextDisplay.selected.length, - click() { - // - }, + condition: TEXT_DISPLAY_CONDITION, onChange() { if (!Project) return const scope = TEXT_DISPLAY_SEE_THROUGH_TOGGLE scope.setIcon(scope.value ? 'check_box' : 'check_box_outline_blank') - const selected = TextDisplay.selected[0] + const selected = TextDisplay.selected.at(0) if (!selected) return if (selected.seeThrough === TEXT_DISPLAY_SEE_THROUGH_TOGGLE.value) return selected.seeThrough = TEXT_DISPLAY_SEE_THROUGH_TOGGLE.value @@ -173,3 +233,31 @@ TEXT_DISPLAY_SEE_THROUGH_TOGGLE.set = function (value) { this.click() return this } + +export const TEXT_DISPLAY_COPY_TEXT_ACTION = new Action( + `animated-java:text-display-copy-text-action`, + { + name: translate('tool.text_display.copy_text.title'), + icon: 'content_copy', + description: translate('tool.text_display.copy_text.description'), + condition: TEXT_DISPLAY_CONDITION, + click: () => { + if (!Project) return + const selected = TextDisplay.selected.at(0) + if (!selected) return + + try { + const text = new JsonTextParser({ + minecraftVersion: Project.animated_java.target_minecraft_version, + }) + .parse(selected.text) + .toString(true, Project.animated_java.target_minecraft_version) + clipboard.writeText(text) + Blockbench.showQuickMessage(translate('tool.text_display.copy_text.copied'), 2000) + } catch (e) { + console.error(e) + Blockbench.showQuickMessage('Failed to copy text to clipboard', 2000) + } + }, + } +) diff --git a/src/interface/panel/vanillaBlockDisplayElement.ts b/src/interface/panel/vanillaBlockDisplayElement.ts index f55ba202..f3e135ae 100644 --- a/src/interface/panel/vanillaBlockDisplayElement.ts +++ b/src/interface/panel/vanillaBlockDisplayElement.ts @@ -1,10 +1,8 @@ +import { registerMountSvelteComponentMod } from 'src/util/mountSvelteComponent' import VanillaBlockDisplayElementPanel from '../../components/vanillaBlockDisplayElementPanel.svelte' -import { injectSvelteCompomponentMod } from '../../util/injectSvelteComponent' -injectSvelteCompomponentMod({ +registerMountSvelteComponentMod({ + id: 'animated-java:append-element-panel/vanilla-block-display', component: VanillaBlockDisplayElementPanel, - props: {}, - elementSelector() { - return document.querySelector('#panel_element') - }, + target: '#panel_element', }) diff --git a/src/interface/panel/vanillaItemDisplayElement.ts b/src/interface/panel/vanillaItemDisplayElement.ts index 2b1dfee7..cf9dbd85 100644 --- a/src/interface/panel/vanillaItemDisplayElement.ts +++ b/src/interface/panel/vanillaItemDisplayElement.ts @@ -1,17 +1,15 @@ -import { isCurrentFormat } from '../../blueprintFormat' +import { registerMountSvelteComponentMod } from 'src/util/mountSvelteComponent' import VanillaItemDisplayElementPanel from '../../components/vanillaItemDisplayElementPanel.svelte' import { PACKAGE } from '../../constants' -import { ItemDisplayMode, VanillaItemDisplay } from '../../outliner/vanillaItemDisplay' -import { events } from '../../util/events' -import { injectSvelteCompomponentMod } from '../../util/injectSvelteComponent' +import { activeProjectIsBlueprintFormat } from '../../formats/blueprint' +import { type ItemDisplayMode, VanillaItemDisplay } from '../../outliner/vanillaItemDisplay' +import EVENTS from '../../util/events' import { translate } from '../../util/translation' -injectSvelteCompomponentMod({ +registerMountSvelteComponentMod({ + id: 'animated-java:append-element-panel/vanilla-item-display', component: VanillaItemDisplayElementPanel, - props: {}, - elementSelector() { - return document.querySelector('#panel_element') - }, + target: '#panel_element', }) export const ITEM_DISPLAY_ITEM_DISPLAY_SELECT = new BarSelect( @@ -20,7 +18,7 @@ export const ITEM_DISPLAY_ITEM_DISPLAY_SELECT = new BarSelect( name: translate('tool.item_display.item_display.title'), icon: 'format_align_left', description: translate('tool.item_display.item_display.description'), - condition: () => isCurrentFormat() && !!VanillaItemDisplay.selected.length, + condition: () => activeProjectIsBlueprintFormat() && !!VanillaItemDisplay.selected.length, options: { none: translate('tool.item_display.item_display.options.none'), thirdperson_lefthand: translate( @@ -79,14 +77,13 @@ ITEM_DISPLAY_ITEM_DISPLAY_SELECT.set = function ( return this } function updateItemDisplaySelect() { - console.log('updateItemDisplaySelect') let value = VanillaItemDisplay.selected.at(0)?.itemDisplay value ??= 'none' ITEM_DISPLAY_ITEM_DISPLAY_SELECT.set(value) } -events.UNDO.subscribe(() => { +EVENTS.UNDO.subscribe(() => { updateItemDisplaySelect() }) -events.REDO.subscribe(() => { +EVENTS.REDO.subscribe(() => { updateItemDisplaySelect() }) diff --git a/src/interface/panel/variants.ts b/src/interface/panel/variants.ts index 26ca68ff..dd4e880e 100644 --- a/src/interface/panel/variants.ts +++ b/src/interface/panel/variants.ts @@ -1,64 +1,82 @@ -import { isCurrentFormat } from '../../blueprintFormat' +import { registerAction, registerMenu } from 'src/util/moddingTools' import VariantsPanel from '../../components/variantsPanel.svelte' -import { PACKAGE } from '../../constants' -import { createAction, createMenu } from '../../util/moddingTools' +import { BLUEPRINT_FORMAT_ID } from '../../formats/blueprint' import { SveltePanel } from '../../util/sveltePanel' import { translate } from '../../util/translation' import { Variant } from '../../variants' import { openVariantConfigDialog } from '../dialog/variantConfig' -export const CREATE_VARIANT_ACTION = createAction(`${PACKAGE.name}:createVariant`, { - name: translate('action.variants.create'), - icon: 'add', - click() { - new Variant('New Variant') - }, -}) +export const CREATE_VARIANT_ACTION = registerAction( + { id: `animated-java:create-variant` }, + { + name: translate('action.variants.create'), + icon: 'add', + click() { + new Variant('New Variant') + }, + } +) -export const DUPLICATE_VARIANT_ACTION = createAction(`${PACKAGE.name}:duplicateVariant`, { - name: translate('action.variants.duplicate'), - icon: 'content_copy', - condition: () => !!Variant.selected, - click() { - if (!Variant.selected) return - Variant.selected.duplicate() - }, -}) +export const DUPLICATE_VARIANT_ACTION = registerAction( + { id: `animated-java:duplicate-variant` }, + { + name: translate('action.variants.duplicate'), + icon: 'content_copy', + condition: () => !!Variant.selected, + click() { + if (!Variant.selected) return + Variant.selected.duplicate() + }, + } +) -export const DELETE_VARIANT_ACTION = createAction(`${PACKAGE.name}:deleteVariant`, { - name: translate('action.variants.delete'), - icon: 'delete', - condition: () => !!Variant.selected && !Variant.selected.isDefault, - click() { - if (!Variant.selected || Variant.selected.isDefault) return - Variant.selected.delete() - }, -}) +export const DELETE_VARIANT_ACTION = registerAction( + { id: `animated-java:delete-variant` }, + { + name: translate('action.variants.delete'), + icon: 'delete', + condition: () => !!Variant.selected && !Variant.selected.isDefault, + click() { + if (!Variant.selected || Variant.selected.isDefault) return + Variant.selected.delete() + }, + } +) -export const OPEN_VARIANT_CONFIG_ACTION = createAction(`${PACKAGE.name}:openVariantConfig`, { - name: translate('action.variants.open_config'), - icon: 'settings', - condition: () => !!Variant.selected && !Variant.selected.isDefault, - click() { - if (!Variant.selected) return - openVariantConfigDialog(Variant.selected) - }, -}) +export const OPEN_VARIANT_CONFIG_ACTION = registerAction( + { id: `animated-java:open-variant-config` }, + { + name: translate('action.variants.open_config'), + icon: 'settings', + condition: () => !!Variant.selected && !Variant.selected.isDefault, + click() { + if (!Variant.selected) return + openVariantConfigDialog(Variant.selected) + }, + } +) -export const VARIANT_PANEL_CONTEXT_MENU = createMenu( - [ - OPEN_VARIANT_CONFIG_ACTION.id, - new MenuSeparator(), - CREATE_VARIANT_ACTION.id, - DUPLICATE_VARIANT_ACTION.id, - new MenuSeparator(), - DELETE_VARIANT_ACTION.id, - ], +export const VARIANT_PANEL_CONTEXT_MENU = registerMenu( + { id: 'animated-java:variant-panel-context-menu' }, + () => { + const items = [ + OPEN_VARIANT_CONFIG_ACTION.get(), + new MenuSeparator(), + CREATE_VARIANT_ACTION.get(), + DUPLICATE_VARIANT_ACTION.get(), + new MenuSeparator(), + DELETE_VARIANT_ACTION.get(), + ] + + if (items.every(i => i != undefined)) return items + + return [] + }, {} ) export const VARIANTS_PANEL = new SveltePanel({ - id: `${PACKAGE.name}:variantsPanel`, + id: `animated-java:variants-panel`, name: translate('panel.variants.title'), expand_button: true, default_side: 'right', @@ -70,13 +88,10 @@ export const VARIANTS_PANEL = new SveltePanel({ folded: false, }, icon: 'settings', - condition: () => - !!( - isCurrentFormat() && - Modes.selected && - (Modes.selected.id === Modes.options.edit.id || - Modes.selected.id === Modes.options.paint.id) - ), + condition: { + formats: [BLUEPRINT_FORMAT_ID], + modes: [Modes.options.edit.id, Modes.options.paint.id], + }, component: VariantsPanel, props: {}, }) diff --git a/src/interface/popup/animatedJavaLoading.ts b/src/interface/popup/animatedJavaLoading.ts index ec808005..6e065d99 100644 --- a/src/interface/popup/animatedJavaLoading.ts +++ b/src/interface/popup/animatedJavaLoading.ts @@ -1,17 +1,18 @@ +import { mountSvelteComponent } from 'src/util/mountSvelteComponent' import { SvelteComponentDev } from 'svelte/internal' import AnimatedJavaLoadingPopup from '../../components/animatedJavaLoadingPopup.svelte' -import { injectSvelteCompomponent } from '../../util/injectSvelteComponent' import { Valuable } from '../../util/stores' const LOADED = new Valuable(false) const OFFLINE = new Valuable(false) const PROGRESS = new Valuable(0) const PROGRESS_LABEL = new Valuable('') -let activeComponent: SvelteComponentDev | undefined -export async function showLoadingPopup() { - if (activeComponent) return - activeComponent = await injectSvelteCompomponent({ +let mountedPopup: SvelteComponentDev | undefined + +export function showLoadingPopup() { + if (mountedPopup) return + mountedPopup = mountSvelteComponent({ component: AnimatedJavaLoadingPopup, props: { loaded: LOADED, @@ -19,30 +20,28 @@ export async function showLoadingPopup() { progress: PROGRESS, progressLabel: PROGRESS_LABEL, }, - elementSelector() { - return document.body - }, + target: document.body, }) } export function hideLoadingPopup() { - if (!activeComponent) return + if (!mountedPopup) return LOADED.set(true) setTimeout(() => { - if (!activeComponent) return - activeComponent.$destroy() - activeComponent = undefined + if (!mountedPopup) return + mountedPopup.$destroy() + mountedPopup = undefined }, 2000) } export function showOfflineError() { - if (!activeComponent) return + if (!mountedPopup) return OFFLINE.set(true) // FIXME - Change this into a X button instead of a timeout. setTimeout(() => { - if (!activeComponent) return - activeComponent.$destroy() - activeComponent = undefined + if (!mountedPopup) return + mountedPopup.$destroy() + mountedPopup = undefined }, 10000) } diff --git a/src/interface/popup/blueprintLoading.ts b/src/interface/popup/blueprintLoading.ts deleted file mode 100644 index 9ae44be5..00000000 --- a/src/interface/popup/blueprintLoading.ts +++ /dev/null @@ -1,36 +0,0 @@ -import BlueprintLoadingPopup from '../../components/blueprintLoadingPopup.svelte' -import { PACKAGE } from '../../constants' -import { Valuable } from '../../util/stores' -import { SvelteDialog } from '../../util/svelteDialog' -import { translate } from '../../util/translation' - -export const PROGRESS = new Valuable(0) -export const MAX_PROGRESS = new Valuable(1) - -let instance: SvelteDialog | null = null - -export function openBlueprintLoadingDialog() { - PROGRESS.set(0) - MAX_PROGRESS.set(1) - - instance = new SvelteDialog({ - id: `${PACKAGE.name}:blueprintLoadingPopup`, - title: translate('dialog.blueprint_loading.title'), - width: 128, - component: BlueprintLoadingPopup, - props: { - progress: PROGRESS, - maxProgress: MAX_PROGRESS, - }, - preventKeybinds: true, - buttons: [], - }).show() - return dialog -} - -export function closeBlueprintLoadingDialog() { - if (instance) { - instance.close(0) - } - instance = null -} diff --git a/src/interface/popup/incompatabilityPopup.ts b/src/interface/popup/incompatabilityPopup.ts index 65032eee..37b71c60 100644 --- a/src/interface/popup/incompatabilityPopup.ts +++ b/src/interface/popup/incompatabilityPopup.ts @@ -1,21 +1,43 @@ +import EVENTS from 'src/util/events' import IncompatabilityPopup from '../../components/incompatabilityPopup.svelte' import { PACKAGE } from '../../constants' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' -const INCOMPATABLE_PLUGINS = ['animation_utils'] +enum INCOMPATABLE_PLUGINS { + GECKOLIB_ANIMATION_UTILS = 'animation_utils', + GECKOLIB = 'geckolib', +} -let currentInstance: SvelteDialog | null = null +let currentInstance: SvelteDialog | null = null export function openIncompatabilityPopup(plugins: BBPlugin[]) { + if (currentInstance) return currentInstance = new SvelteDialog({ id: `${PACKAGE.name}:incompatabilityPopup`, title: translate('popup.incompatability_popup.title'), width: 700, - component: IncompatabilityPopup, - props: { plugins }, + content: { + component: IncompatabilityPopup, + props: { plugins }, + }, preventKeybinds: true, - buttons: [], + preventKeybindCancel: true, + buttons: [ + translate('popup.incompatability_popup.button.disable_all'), + translate('popup.incompatability_popup.button.ignore'), + ], + onButton: button => { + if (button === 0) { + plugins.forEach(plugin => { + if (plugin.disabled) return + plugin.toggleDisabled() + }) + } + }, + onClose: () => { + currentInstance = null + }, }).show() } @@ -28,20 +50,20 @@ export function closeIncompatabilityPopup() { } export function isIncompatiblePlugin(plugin: BBPlugin): boolean { - return INCOMPATABLE_PLUGINS.includes(plugin.id) + return Object.values(INCOMPATABLE_PLUGINS).includes(plugin.id as INCOMPATABLE_PLUGINS) } export function checkForIncompatabilities() { - const plugins: BBPlugin[] = [] - for (const plugin of Plugins.all) { - if (!plugin.installed || plugin.disabled || !isIncompatiblePlugin(plugin)) { - continue - } - plugins.push(plugin) - } - if (plugins.length > 0) { - openIncompatabilityPopup(plugins) + const incompatiblePlugins = Plugins.all.filter( + plugin => plugin.installed && !plugin.disabled && isIncompatiblePlugin(plugin) + ) + if (incompatiblePlugins.length > 0) { + openIncompatabilityPopup(incompatiblePlugins) return true } return false } + +EVENTS.EXTERNAL_PLUGIN_LOAD.subscribe(() => { + checkForIncompatabilities() +}) diff --git a/src/interface/popup/installed.ts b/src/interface/popup/installed.ts index 9ea89d25..780a89fc 100644 --- a/src/interface/popup/installed.ts +++ b/src/interface/popup/installed.ts @@ -6,11 +6,12 @@ import { translate } from '../../util/translation' export function openInstallPopup() { new SvelteDialog({ id: `${PACKAGE.name}:installedPopup`, - title: translate('dialog.installed_popup.title'), + title: translate('popup.installed_popup.title'), width: 700, - component: InstalledPopup, - props: {}, + content: { + component: InstalledPopup, + }, preventKeybinds: true, - buttons: [translate('dialog.installed_popup.close_button')], + buttons: [translate('popup.installed_popup.close_button')], }).show() } diff --git a/src/lang/en.yml b/src/lang/en.yml index 2d7a9cee..57f18d0c 100644 --- a/src/lang/en.yml +++ b/src/lang/en.yml @@ -1,3 +1,7 @@ +animated_java.menubar.label: Animated Java + +animated_java.format.blueprint.name: Blueprint + ### Actions animated_java.action.open_blueprint_settings.name: Blueprint Settings animated_java.action.open_documentation.name: Documentation @@ -7,6 +11,13 @@ animated_java.action.open_bone_config.name: Bone Config animated_java.action.open_locator_config.name: Locator Config animated_java.action.open_text_display_config.name: Text Display Config animated_java.action.export.name: Export +animated_java.action.export_debug.name: Export (Debug) +animated_java.action.export_all.name: Export All +animated_java.action.export_all.description: |- + Export all open Blueprint projects. +animated_java.action.export_all_debug.name: Export All (Debug) +animated_java.action.export_all_debug.description: |- + Export all open Blueprint projects in debug mode. animated_java.action.extract.name: Extract animated_java.action.extract.confirm: Confirm Extraction animated_java.action.create_text_display.title: Add Text Display @@ -25,12 +36,17 @@ animated_java.popup.loading.offline: |- animated_java.popup.installed_popup.title: Thank you for installing! animated_java.popup.installed_popup.close_button: Let's Get Animating! -animated_java.popup.incompatability_popup.title: Incompatible Plugin Detected! +animated_java.popup.incompatability_popup.title: Animated Java Incompatibility Detected animated_java.popup.incompatability_popup.description: |- - Animated Java has detected incompatible plugin(s)! - Please disable / uninstall the following plugin(s) and restart Blockbench to use Animated Java: + You have plugins installed that are known to cause issues with Animated Java. + Please disable or uninstall the following plugin(s) and restart Blockbench to use Animated Java: animated_java.popup.incompatability_popup.disable_button: Disable Plugin -animated_java.dialog.incompatability_notice: This plugin is not compatible with Animated Java! +animated_java.popup.incompatability_popup.button.disable_all: Disable All Incompatible Plugins +animated_java.popup.incompatability_popup.button.ignore: Ignore and Continue (Not Recommended) +animated_java.plugin_dialog.incompatability_notice: |- + This plugin is known to cause issues with Animated Java. + + You will be unable to install this plugin while Animated Java is installed. ### Dialogs animated_java.dialog.reset: Reset to Default @@ -51,168 +67,203 @@ animated_java.dialog.unexpected_error.paragraph: 'Please report this error by jo ## Blueprint Settings Dialog animated_java.dialog.blueprint_settings.title: Blueprint Settings +animated_java.dialog.blueprint_settings.project_settings.title: Project + animated_java.dialog.blueprint_settings.advanced_settings_warning: Advanced settings should only be used if absolutely needed! -animated_java.dialog.blueprint_settings.blueprint_name.title: Blueprint Name -animated_java.dialog.blueprint_settings.blueprint_name.description: The filename of the Blueprint. +animated_java.dialog.blueprint_settings.project_name.title: Name +animated_java.dialog.blueprint_settings.project_name.description: |- + The Blueprint's filename. + + This will be overwritten if you save the Blueprint with a different name. animated_java.dialog.blueprint_settings.texture_size.title: Texture Size -animated_java.dialog.blueprint_settings.texture_size.description: The resolution of the UV editor. This should be the same size as the largest texture. For best results use a square texture, and make sure it's size is a power of 2. -animated_java.dialog.blueprint_settings.texture_size.warning.not_square: The Texture Size should be square for best results. -animated_java.dialog.blueprint_settings.texture_size.warning.not_a_power_of_2: The Texture Size should be a power of 2 for best results. -animated_java.dialog.blueprint_settings.texture_size.warning.does_not_match_largest_texture: The Texture Size should match the largest texture's size. +animated_java.dialog.blueprint_settings.texture_size.description: |- + The resolution of the UV editor. + + This should match the size of the largest texture in your Blueprint. +animated_java.dialog.blueprint_settings.texture_size.warning.not_square: Texture Size should be square if possible! +animated_java.dialog.blueprint_settings.texture_size.warning.not_a_power_of_2: Texture Size should be a power of 2 if possible! +animated_java.dialog.blueprint_settings.texture_size.warning.does_not_match_largest_texture: The chosen Texture Size does not match the size of the largest texture in your Blueprint! # Export Settings -animated_java.dialog.blueprint_settings.export_settings.title: Export Settings +animated_java.dialog.blueprint_settings.export_settings.title: Export + +animated_java.dialog.blueprint_settings.export_namespace.title: Namespace +animated_java.dialog.blueprint_settings.export_namespace.description: |- + The namespace to use when generating the Resource Pack and Data Pack. -animated_java.dialog.blueprint_settings.export_namespace.title: Export Namespace -animated_java.dialog.blueprint_settings.export_namespace.description: The namespace to export the project to. This is the namespace that will be used in the exported Resource Pack and Data Pack. + It is recommended to use a unique namespace to avoid conflicts with other Resource Packs and Data Packs. animated_java.dialog.blueprint_settings.export_namespace.error.empty: The export namespace cannot be empty! animated_java.dialog.blueprint_settings.export_namespace.error.reserved: The export namespace "{0}" is reserved for internal functionality! Please choose a different namespace. animated_java.dialog.blueprint_settings.export_namespace.error.invalid_characters: The export namespace contains invalid characters! Namespaces can only contain letters, numbers, and underscores. +animated_java.dialog.blueprint_settings.show_render_box.title: Show Render Box +animated_java.dialog.blueprint_settings.show_render_box.description: When enabled, the render box will be visible in the editor. + +animated_java.dialog.blueprint_settings.auto_render_box.title: Auto-Size Render Box +animated_java.dialog.blueprint_settings.auto_render_box.description: |- + When enabled, the render box will be automatically calculated based on the model's geometry. +animated_java.dialog.blueprint_settings.render_box.title: Render Box Size +animated_java.dialog.blueprint_settings.render_box.description: |- + The [width and height](https://minecraft.wiki/w/Display#Entity_data) of the rig's display entities. + +animated_java.dialog.blueprint_settings.view_range.title: View Range +animated_java.dialog.blueprint_settings.view_range.description: |- + The default [view range](https://minecraft.wiki/w/Display#Entity_data) of the rig. + animated_java.dialog.blueprint_settings.enable_plugin_mode.title: Plugin Mode -animated_java.dialog.blueprint_settings.enable_plugin_mode.description: Whether or not to enable Plugin Mode. Enable when you plan to use a Plugin-based solution instead of a Resource Pack and/or Data Pack. +animated_java.dialog.blueprint_settings.enable_plugin_mode.description: |- + When enabled, the project will be exported as a JSON file designed for use with plugins. animated_java.dialog.blueprint_settings.resource_pack_export_mode.title: Resource Pack Export Mode -animated_java.dialog.blueprint_settings.resource_pack_export_mode.description: |- - Determines how the Resource Pack should be exported. - Raw - Exports the Resource Pack as a folder. - Zip - Exports the Resource Pack as a .zip file. - None - Disables Resource Pack exporting. -animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.raw: Raw +animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.folder: Folder animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.zip: Zip animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.none: None animated_java.dialog.blueprint_settings.data_pack_export_mode.title: Data Pack Export Mode -animated_java.dialog.blueprint_settings.data_pack_export_mode.description: |- - Determines how the Data Pack should be exported. - Raw - Exports the Data Pack as a folder. - Zip - Exports the Data Pack as a .zip file. - None - Disables Data Pack exporting. -animated_java.dialog.blueprint_settings.data_pack_export_mode.options.raw: Raw +animated_java.dialog.blueprint_settings.data_pack_export_mode.options.folder: Folder animated_java.dialog.blueprint_settings.data_pack_export_mode.options.zip: Zip animated_java.dialog.blueprint_settings.data_pack_export_mode.options.none: None -animated_java.dialog.blueprint_settings.show_bounding_box.title: Show Bounding Box -animated_java.dialog.blueprint_settings.show_bounding_box.description: Whether or not to show the bounding box in the editor. +animated_java.dialog.blueprint_settings.target_minecraft_version.title: Target Minecraft Version +animated_java.dialog.blueprint_settings.target_minecraft_version.description: |- + The version of Minecraft to target when exporting the project. + + If the version you are using is not available, select the closest version below it. -animated_java.dialog.blueprint_settings.auto_bounding_box.title: Auto Bounding Box -animated_java.dialog.blueprint_settings.auto_bounding_box.description: |- - Whether or not to automatically calculate the bounding box based on the model's geometry. - NOTE: The auto bounding box will NOT take bone offsets from animations into account, so the bounding box may be smaller than needed in some cases. + E.g If you are using `1.21.8`, select `1.21.5`. -animated_java.dialog.blueprint_settings.bounding_box.title: Bounding Box -animated_java.dialog.blueprint_settings.bounding_box.description: Determines the culling box of the model. The model will stop rendering when this box is off-screen. + Some features may be altered, or unavailable depending on the selected version. -animated_java.dialog.blueprint_settings.target_minecraft_versions.title: Target Minecraft Versions -animated_java.dialog.blueprint_settings.target_minecraft_versions.description: The versions of Minecraft to target when exporting the project. This will determine the format of the exported Resource Pack and Data Pack. -animated_java.dialog.blueprint_settings.target_minecraft_versions.warning.multiple_versions: |- - Selecting multiple target versions will multiply the size of the exported Data Pack by the number of versions selected. - E.g. Selecting two versions will double the size of the exported Resource and Data Packs. - This will be improved in a future update. + Animated Java will inform you of any changes that need to be made. # Resource Pack Settings -animated_java.dialog.blueprint_settings.resource_pack_settings.title: Resource Pack Settings +animated_java.dialog.blueprint_settings.resource_pack_settings.title: Resource Pack animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_settings.title: Advanced Settings -animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_settings.description: Whether or not to enable the advanced Resource Pack settings. animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_folders.title: Advanced Folders -animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_folders.description: Whether or not to enable the advanced Resource Pack folder settings. animated_java.dialog.blueprint_settings.display_item.title: Display Item -animated_java.dialog.blueprint_settings.display_item.description: The item to display the Blueprints models in-game. Multiple Blueprints can be placed on the same item and they will be merged automatically. +animated_java.dialog.blueprint_settings.display_item.description: |- + The item to use for the rig's models. + + Multiple Blueprints in the same project can use the same item, and they will be merged automatically when exported. animated_java.dialog.blueprint_settings.display_item.error.no_item_selected: No item selected! -animated_java.dialog.blueprint_settings.display_item.error.invalid_item_id.no_namespace: The provided item ID is invalid! Item IDs should be in the format namespace:item_id. +animated_java.dialog.blueprint_settings.display_item.error.invalid_item_id.no_namespace: The provided item ID is invalid! Item IDs should be in the format `namespace:item_id`. animated_java.dialog.blueprint_settings.display_item.error.invalid_item_id.whitespace: The provided item ID is invalid! Item IDs should not contain any whitespace. animated_java.dialog.blueprint_settings.display_item.warning.item_does_not_exist: The selected item does not exist in vanilla! -animated_java.dialog.blueprint_settings.display_item.warning.item_model_not_generated: The selected item does not use 'minecraft:item/generated' as its parent. This may cause model issues in-game. +animated_java.dialog.blueprint_settings.display_item.warning.item_model_not_generated: The selected item does not use `minecraft:item/generated` as its parent. This may cause model issues in-game. animated_java.dialog.blueprint_settings.display_item.error.item_model_not_found: |- The selected item does not have a model file in the vanilla resource pack! + If you believe this is an error, try restarting Blockbench, and waiting for the AJ loading popup to disappear before opening a Blueprint. animated_java.dialog.blueprint_settings.custom_model_data_offset.title: CMD Offset -animated_java.dialog.blueprint_settings.custom_model_data_offset.description: The offset to use for the Custom Model Data of the Display Item. Allows multiple Blueprints on the same item, but in separate, unaffiliated Resource Packs. +animated_java.dialog.blueprint_settings.custom_model_data_offset.description: |- + Offsets the Custom Model Data (CMD) values used in the display item's predicates by the specified amount. animated_java.dialog.blueprint_settings.resource_pack.title: Resource Pack -animated_java.dialog.blueprint_settings.resource_pack.description: The root folder of the Resource Pack to export the project into. +animated_java.dialog.blueprint_settings.resource_pack.description: |- + The root folder of a Resource Pack to export the project to. + + The targeted Resource Pack must contain a `pack.mcmeta` file. +animated_java.dialog.blueprint_settings.resource_pack.warning.missing_assets_folder: The selected Resource Pack is missing an assets folder! animated_java.dialog.blueprint_settings.resource_pack.error.no_folder_selected: No folder selected! animated_java.dialog.blueprint_settings.resource_pack.error.folder_does_not_exist: The selected folder does not exist! animated_java.dialog.blueprint_settings.resource_pack.error.not_a_folder: The selected path is not a folder! animated_java.dialog.blueprint_settings.resource_pack.error.missing_pack_mcmeta: The selected folder is missing a pack.mcmeta file! -animated_java.dialog.blueprint_settings.resource_pack.error.missing_assets_folder: The selected Resource Pack is missing an assets folder! animated_java.dialog.blueprint_settings.resource_pack_zip.title: Resource Pack Zip animated_java.dialog.blueprint_settings.resource_pack_zip.description: The path to the .zip file to export the project to. animated_java.dialog.blueprint_settings.resource_pack_zip.error.no_file_selected: No file selected! animated_java.dialog.blueprint_settings.resource_pack_zip.error.not_a_file: The selected path is not a file! -animated_java.dialog.blueprint_settings.display_item_path.title: Display Item Path -animated_java.dialog.blueprint_settings.display_item_path.description: Where to place the Display Item. This should be a path to a .json file in a Resource Pack. -animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.no_file_selected: No file selected! -animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.file_does_not_exist: The selected file does not exist! -animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.not_a_file: The selected path is not a file! - -animated_java.dialog.blueprint_settings.model_folder.title: Model Folder -animated_java.dialog.blueprint_settings.model_folder.description: Where to place all of the exported models. This should be a path to a folder in a Resource Pack. -animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.no_folder_selected: No folder selected! -animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.folder_does_not_exist: The selected folder does not exist! -animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.not_a_folder: The selected path is not a folder! - -animated_java.dialog.blueprint_settings.texture_folder.title: Texture Folder -animated_java.dialog.blueprint_settings.texture_folder.description: Where to place all of the exported textures. This should be a path to a folder in a Resource Pack. - # Data Pack Settings -animated_java.dialog.blueprint_settings.data_pack_settings.title: Data Pack Settings - -animated_java.dialog.blueprint_settings.enable_advanced_data_pack_settings.title: Use Advanced Settings -animated_java.dialog.blueprint_settings.enable_advanced_data_pack_settings.description: Whether or not to enable the advanced Data Pack settings. +animated_java.dialog.blueprint_settings.data_pack_settings.title: Data Pack animated_java.dialog.blueprint_settings.data_pack.title: Data Pack -animated_java.dialog.blueprint_settings.data_pack.description: The root folder of the Data Pack to export the project into. +animated_java.dialog.blueprint_settings.data_pack.description: |- + The root folder of a Data Pack to export the project to. + + The targeted Data Pack must contain a `pack.mcmeta` file. +animated_java.dialog.blueprint_settings.data_pack.warning.missing_data_folder: The selected Data Pack is missing a data folder! animated_java.dialog.blueprint_settings.data_pack.error.no_folder_selected: No folder selected! animated_java.dialog.blueprint_settings.data_pack.error.folder_does_not_exist: The selected folder does not exist! animated_java.dialog.blueprint_settings.data_pack.error.not_a_folder: The selected path is not a folder! animated_java.dialog.blueprint_settings.data_pack.error.missing_pack_mcmeta: The selected folder is missing a pack.mcmeta file! -animated_java.dialog.blueprint_settings.data_pack.error.missing_data_folder: The selected Data Pack is missing a data folder! animated_java.dialog.blueprint_settings.data_pack_zip.title: Data Pack Zip animated_java.dialog.blueprint_settings.data_pack_zip.description: The path to the .zip file to export the project to. animated_java.dialog.blueprint_settings.data_pack_zip.error.no_file_selected: No file selected! animated_java.dialog.blueprint_settings.data_pack_zip.error.not_a_file: The selected path is not a file! -animated_java.dialog.blueprint_settings.summon_commands.title: On-Summon Commands -animated_java.dialog.blueprint_settings.summon_commands.description: |- - Commands to run as the root entity when summoned. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) +animated_java.dialog.blueprint_settings.on_summon_function.title: On-Summon Function +animated_java.dialog.blueprint_settings.on_summon_function.description: |- + Commands to execute `as` and `at` the root entity when summoned. + + Treat this input as if it were a `.mcfunction` file. + + Supports [MC-Build](https://mcbuild.dev) syntax. + +animated_java.dialog.blueprint_settings.on_remove_function.title: On-Remove Function +animated_java.dialog.blueprint_settings.on_remove_function.description: |- + Commands to run `as` and `at` the root entity when removed. + + Treat this input as if it were a `.mcfunction` file. + + Supports [MC-Build](https://mcbuild.dev) syntax. + +animated_java.dialog.blueprint_settings.on_pre_tick_function.title: Pre-Tick Function +animated_java.dialog.blueprint_settings.on_pre_tick_function.description: |- + Commands to run `as` and `at` the root entity every tick *before* Animated Java's own ticking logic. + + Treat this input as if it were a `.mcfunction` file. -animated_java.dialog.blueprint_settings.remove_commands.title: On-Remove Commands -animated_java.dialog.blueprint_settings.remove_commands.description: |- - Commands to run as the root entity when removed via a built-in remove function such as /remove/this or /remove/all. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) + Supports [MC-Build](https://mcbuild.dev) syntax. -animated_java.dialog.blueprint_settings.ticking_commands.title: Ticking Commands -animated_java.dialog.blueprint_settings.ticking_commands.description: |- - Commands to run every tick at the root entity's position and after the animation logic. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) +animated_java.dialog.blueprint_settings.on_post_tick_function.title: Post-Tick Function +animated_java.dialog.blueprint_settings.on_post_tick_function.description: |- + Commands to run `as` and `at` the root entity every tick *after* Animated Java's own ticking logic. + + Treat this input as if it were a `.mcfunction` file. + + Supports [MC-Build](https://mcbuild.dev) syntax. animated_java.dialog.blueprint_settings.interpolation_duration.title: Interpolation Duration -animated_java.dialog.blueprint_settings.interpolation_duration.description: How many ticks it takes for the model to transition from one frame to the next. Higher values will cause animations to lose precision. Generally, you want this to have a value of 1 or 2. +animated_java.dialog.blueprint_settings.interpolation_duration.description: |- + The default [interpolation duration](https://minecraft.wiki/w/Display#Entity_data) of the rig. + + Higher values make animations smoother but less responsive. animated_java.dialog.blueprint_settings.teleportation_duration.title: Teleport Duration -animated_java.dialog.blueprint_settings.teleportation_duration.description: How many ticks it takes for the entity to move from it's old position to it's new position when teleported. Higher values will cause animated locators with "Use Entity" enabled to lose precision. +animated_java.dialog.blueprint_settings.teleportation_duration.description: |- + The default [teleport duration](https://minecraft.wiki/w/Display#Entity_data) of the rig. + + Higher values make large movements smoother but less responsive. + +animated_java.dialog.blueprint_settings.auto_update_rig_orientation.title: Auto Update Rig Orientation +animated_java.dialog.blueprint_settings.auto_update_rig_orientation.description: |- + When **enabled**, syncs the positions and rotations of all node entities with the root entity every tick. + Allowing you to simply teleport the root entity to move and rotate the entire rig. + + When **disabled**, you must use the move function to teleport the rig: + + ``` + execute as @e[tag=aj._root] \ + positioned rotated run \ + function animated_java:/move + ``` + + Syncing node positions and rotations every tick can be expensive, especially for large rigs. + Disabling this option can improve performance at the cost of convenience. animated_java.dialog.blueprint_settings.use_storage_for_animation.title: Use Storage for Animation animated_java.dialog.blueprint_settings.use_storage_for_animation.description: |- - Whether or not to use NBT storage to store animation data instead of functions. - This will vastly reduce the number of functions in the generated Data Pack, but is 42% slower than the function method. + When enabled, NBT storage will be used to store animation data instead of functions. -animated_java.dialog.blueprint_settings.show_function_errors.title: Function Errors -animated_java.dialog.blueprint_settings.show_function_errors.description: Whether or not to show errors in chat when incorrectly using functions. - -animated_java.dialog.blueprint_settings.show_outdated_warning.title: Outdated Warning -animated_java.dialog.blueprint_settings.show_outdated_warning.description: Whether or not to show a warning in chat when a Rig Instance is outdated, and needs to be resummoned. + This greatly reduces the file count of the exported Data Pack, but is significantly slower. # Plugin Settings animated_java.dialog.blueprint_settings.baked_animations.title: Baked Animations @@ -290,7 +341,12 @@ animated_java.dialog.bone_config.nbt.invalid_nbt.error: |- {0} animated_java.dialog.bone_config.billboard.title: Billboard -animated_java.dialog.bone_config.billboard.description: Controls if this bone should pivot to face player when rendered. It can be fixed (both vertical and horizontal angles are fixed), vertical (faces player around vertical axis), horizontal (pivots around horizontal axis), and center (pivots around center point). +animated_java.dialog.bone_config.billboard.description: |- + Controls if this entity's model should pivot to face player when rendered. + - **Fixed**: Both vertical and horizontal angles are fixed. + - **Vertical**: Pivots around vertical axis. + - **Horizontal**: Pivots around horizontal axis. + - **Center**: Pivots around center point. animated_java.dialog.bone_config.billboard.options.fixed: Fixed animated_java.dialog.bone_config.billboard.options.vertical: Vertical animated_java.dialog.bone_config.billboard.options.horizontal: Horizontal @@ -305,7 +361,8 @@ animated_java.dialog.locator_config.plugin_mode_warning: |- For more information, see the Official Plugin API documentation for more information. animated_java.dialog.locator_config.use_entity.title: Use Entity -animated_java.dialog.locator_config.use_entity.description: Whether or not to attach an entity to the Locator. +animated_java.dialog.locator_config.use_entity.description: |- + When enabled, the Locator will summon and use an entity to represent itself in-game. animated_java.dialog.locator_config.entity_type.title: Entity Type animated_java.dialog.locator_config.entity_type.description: The type of entity to attach to the Locator. @@ -315,15 +372,35 @@ animated_java.dialog.locator_config.entity_type.warning.invalid: The selected en animated_java.dialog.locator_config.sync_passenger_rotation.title: Sync Passenger Rotation animated_java.dialog.locator_config.sync_passenger_rotation.description: Automatically sync the rotation of the Locator's passenger entities. -animated_java.dialog.locator_config.summon_commands.title: On-Summon Commands -animated_java.dialog.locator_config.summon_commands.description: |- - Commands to run as the Locator's entity when summoned. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) +animated_java.dialog.locator_config.on_summon_function.title: On-Summon Function +animated_java.dialog.locator_config.on_summon_function.description: |- + Commands to run `as` the root entity and `at` the Locator's position when the rig is summoned. + + Supports [MC-Build](https://mcbuild.dev) syntax. +animated_java.dialog.locator_config.on_summon_function.description_with_use_entity: |- + Commands to run `as` and `at` the Locator's entity when summoned. + + Supports [MC-Build](https://mcbuild.dev) syntax. + +animated_java.dialog.locator_config.on_remove_function.title: On-Remove Function +animated_java.dialog.locator_config.on_remove_function.description: |- + Commands to run `as` the root entity and `at` the Locator's position when the rig is removed. + + Supports [MC-Build](https://mcbuild.dev) syntax. +animated_java.dialog.locator_config.on_remove_function.description_with_use_entity: |- + Commands to run `as` and `at` the Locator's entity when removed. -animated_java.dialog.locator_config.ticking_commands.title: Ticking Commands -animated_java.dialog.locator_config.ticking_commands.description: |- - Commands to run every tick at the Locator's position. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) + Supports [MC-Build](https://mcbuild.dev) syntax. + +animated_java.dialog.locator_config.on_tick_function.title: On-Tick Function +animated_java.dialog.locator_config.on_tick_function.description: |- + Commands to run `at` the Locator's position every tick. + + Supports [MC-Build](https://mcbuild.dev) syntax. +animated_java.dialog.locator_config.on_tick_function.description_with_use_entity: |- + Commands to run `as` and `at` the Locator's entity every tick. + + Supports [MC-Build](https://mcbuild.dev) syntax. ## Text Display Config Dialog animated_java.dialog.text_display_config.title: Text Display Config @@ -373,13 +450,6 @@ animated_java.dialog.text_display_config.invisible.description: Whether or not t animated_java.dialog.text_display_config.nbt.title: NBT animated_java.dialog.text_display_config.nbt.description: The NBT to apply to the text display. -animated_java.dialog.text_display_config.billboard.title: Billboard -animated_java.dialog.text_display_config.billboard.description: Controls if this text display should pivot to face player when rendered. It can be fixed (both vertical and horizontal angles are fixed), vertical (faces player around vertical axis), horizontal (pivots around horizontal axis), and center (pivots around center point). -animated_java.dialog.text_display_config.billboard.options.fixed: Fixed -animated_java.dialog.text_display_config.billboard.options.vertical: Vertical -animated_java.dialog.text_display_config.billboard.options.horizontal: Horizontal -animated_java.dialog.text_display_config.billboard.options.center: Center - ## Block Display Config Dialog animated_java.dialog.vanilla_block_display_config.title: Block Display Config animated_java.dialog.vanilla_block_display.custom_name.title: Custom Name @@ -419,11 +489,16 @@ animated_java.dialog.variant_config.texture_map.description: A map of which text animated_java.dialog.variant_config.texture_map.create_new_mapping: Create New Mapping animated_java.dialog.variant_config.texture_map.no_mappings: Variant has no mapped textures. -animated_java.dialog.variant_config.bone_lists.description: A list of nodes to include or exclude from the Variant. Only nodes in the included list will be modified by the Variant, and nodes in the excluded list will be ignored. +animated_java.dialog.variant_config.bone_lists.description: |- + Specify which nodes the Variant should affect. + + Nodes in the included list will be changed when applying the Variant. + + Nodes in the excluded list will remain unchanged. animated_java.dialog.variant_config.excluded_nodes.title: Excluded Nodes -animated_java.dialog.variant_config.excluded_nodes.description: A list of nodes to exclude from the Variant. These nodes will not be modified by the Variant. +animated_java.dialog.variant_config.excluded_nodes.description: A list of nodes to exclude from the Variant. These nodes will remain unchanged when applying the Variant. animated_java.dialog.variant_config.included_nodes.title: Included Nodes -animated_java.dialog.variant_config.included_nodes.description: A list of nodes to include in the Variant. Only these nodes will be modified by the Variant. +animated_java.dialog.variant_config.included_nodes.description: A list of nodes to include in the Variant. Only these nodes will be changed when applying the Variant. animated_java.dialog.variant_config.swap_columns_button.tooltip: Swap Lists ## Old AJModel Loader Dialog @@ -440,7 +515,10 @@ animated_java.dialog.animation_properties.animation_name.title: Animation Name animated_java.dialog.animation_properties.animation_name.description: The name of the animation. animated_java.dialog.animation_properties.loop_mode.title: Loop Mode -animated_java.dialog.animation_properties.loop_mode.description: Determines how the animation should loop. Once - The animation will play once and stop. Hold - The animation will play once and hold the last frame. Loop - The animation will loop indefinitely. +animated_java.dialog.animation_properties.loop_mode.description: |- + - Once: The animation will play once, then reset to the first frame. + - Hold: The animation will play once and hold the last frame. + - Loop: The animation will loop indefinitely. animated_java.dialog.animation_properties.loop_mode.options.once: Once animated_java.dialog.animation_properties.loop_mode.options.hold: Hold animated_java.dialog.animation_properties.loop_mode.options.loop: Loop @@ -448,11 +526,19 @@ animated_java.dialog.animation_properties.animation_name.error.empty: The animat animated_java.dialog.animation_properties.animation_name.error.invalid_characters: The animation name contains invalid characters! Animation names should only contain letters, numbers, underscores, and periods. animated_java.dialog.animation_properties.loop_delay.title: Loop Delay -animated_java.dialog.animation_properties.loop_delay.description: The delay between loops. This is the time the animation will pause before starting again. This is only used when the Loop Mode is set to Loop. +animated_java.dialog.animation_properties.loop_delay.description: |- + The number of ticks to wait before restarting the animation when in Loop mode. + + E.g. A value of 20 will cause the animation to wait 1 second before restarting. + +animated_java.dialog.animation_properties.bone_lists.description: |- + Specify which nodes the animation should affect. -animated_java.dialog.animation_properties.bone_lists.description: A list of nodes to include or exclude from the animation. Only nodes in the included list will be modified by the animation, and nodes in the excluded list will be ignored. + Nodes in the included list will be modified by the animation. + + Nodes in the excluded list will remain unchanged. animated_java.dialog.animation_properties.excluded_nodes.title: Excluded Nodes -animated_java.dialog.animation_properties.excluded_nodes.description: A list of nodes to exclude from the animation. These nodes will not be modified by the animation. +animated_java.dialog.animation_properties.excluded_nodes.description: A list of nodes to exclude from the animation. These nodes will remain unchanged when applying the animation. animated_java.dialog.animation_properties.included_nodes.title: Included Nodes animated_java.dialog.animation_properties.included_nodes.description: A list of nodes to include in the animation. Only these nodes will be modified by the animation. animated_java.dialog.animation_properties.swap_columns_button.tooltip: Swap Lists @@ -489,12 +575,14 @@ animated_java.animation.invert_excluded_nodes: Invert Excluded Nodes ## Timeline animated_java.effect_animator.timeline.variant: Variant -animated_java.effect_animator.timeline.commands: Commands +animated_java.effect_animator.timeline.function: Function ## Keyframes -animated_java.effect_animator.keyframes.variant: Variant -animated_java.effect_animator.keyframes.commands: Commands -animated_java.effect_animator.keyframes.execute_condition: Execute Condition +animated_java.effect_animator.keyframe_data_point.variant: Variant +animated_java.effect_animator.keyframe_data_point.function: Function +animated_java.effect_animator.keyframe_data_point.execute_condition: Execute Condition +animated_java.effect_animator.keyframe_data_point.repeat: Repeat +animated_java.effect_animator.keyframe_data_point.repeat_frequency: Repeat Frequency # Keyframe Panel animated_java.panel.keyframe.keyframe_title: Keyframe ({0}) @@ -502,24 +590,29 @@ animated_java.panel.keyframe.keyframe_title: Keyframe ({0}) animated_java.panel.keyframe.variant.title: Variant animated_java.panel.keyframe.variant.description: The Variant to apply to the keyframe. -animated_java.panel.keyframe.commands.title: Commands -animated_java.panel.keyframe.commands.description: |- +animated_java.panel.keyframe.function.title: Function +animated_java.panel.keyframe.function.description: |- Commands to run when the keyframe is reached. - Treat this text input as a .mcfunction file. (Also supports MC-Build syntax!) + + Treat this text input as if it were a `.mcfunction` file. + + Supports [MC-Build](https://mcbuild.dev) syntax. animated_java.panel.keyframe.execute_condition.title: Execute Condition -animated_java.panel.keyframe.execute_condition.description: A condition that must be met for the keyframe to execute. Treat this text input as the sub-commands of an execute command. +animated_java.panel.keyframe.execute_condition.description: |- + A condition that must be met for the keyframe to execute. -animated_java.panel.keyframe.repeat.title: Repeat? -animated_java.panel.keyframe.repeat.description: |- - Whether or not to run the commands in this keyframe repeatedly. - If enabled, the commands will run every Repeat Frequency ticks. + Treat this text input as if it were the condition in an `execute if` command. -animated_java.panel.keyframe.repeat_frequency.title: Repeat Frequency + E.g. `if score @s myScore matches 1..` + +animated_java.panel.keyframe.repeat.title: Repeat +animated_java.panel.keyframe.repeat.description: |- + When enabled, the commands in this keyframe will repeat at the interval specified by Frequency. +animated_java.panel.keyframe.repeat_frequency.title: Frequency animated_java.panel.keyframe.repeat_frequency.description: |- - How many ticks to wait before running the commands in this keyframe again. - Setting this to 1 will run the commands every tick. - NOTE: If the animation is not evenly divisible by this value, the interval may be slightly off when the animation loops. - If this is set to a value larger than the length of the animation, the commands will run as if repeat was disabled (Once when the keyframe is reached). + Number of ticks between command repeats. + + Set to 1 to run every tick of the animation. animated_java.panel.keyframe.easing_type.title: Easing Type animated_java.panel.keyframe.easing_type.description: The type of easing to apply to the keyframe. @@ -575,6 +668,10 @@ animated_java.tool.text_display.text_alignment.options.right: Right animated_java.tool.text_display.see_through.title: See Through animated_java.tool.text_display.see_through.description: Whether or not the text display should be visible through blocks. +animated_java.tool.text_display.copy_text.title: Copy exported text component +animated_java.tool.text_display.copy_text.description: Copy the exported text component to your clipboard. +animated_java.tool.text_display.copy_text.copied: Text copied to clipboard! + # Item Display Panel animated_java.panel.vanilla_item_display.title: Displayed Item animated_java.panel.vanilla_item_display.description: The item to display. @@ -609,11 +706,21 @@ animated_java.misc.failed_to_export.blueprint_settings.message: There are errors animated_java.misc.failed_to_export.blueprint_settings.error_item: 'Found an issue with {0}:' animated_java.misc.failed_to_export.button: Ok animated_java.misc.failed_to_export.invalid_rotation.message: |- - Some cubes in your model have an invalid rotations. - Cubes must have a rotation of -45, -22.5, 0, 22.5, or 45 degrees, and can only be rotated on a single axis at a time. - If you want to rotate a cube more precisely, or on multiple axes, you must put it into a bone and rotate the bone instead. - All of the invalid cubes are outlined in red in the editor. - Please fix these issues before exporting. + Some cubes in your model have an invalid rotations! + + Cubes must have a rotation of -45, -22.5, 0, 22.5, or 45 degrees, and can only be rotated on a single axis at a time when targeting Minecraft versions before 1.21.6. + + If you want to rotate a cube more precisely, or on multiple axes, put it in a bone and rotate the bone instead. + + All of the invalid cubes are outlined in red in the editor. Please fix them before exporting. +animated_java.misc.failed_to_export.invalid_rotation.message_post_1_21_6: |- + Some cubes in your model have an invalid rotations! + + Cubes can only be rotated on a single axis at a time when targeting Minecraft 1.21.6 or later. + + If you want to rotate a cube on multiple axes, put it in a bone and rotate the bone instead. + + All of the invalid cubes are outlined in red in the editor. Please fix them before exporting. animated_java.misc.failed_to_export.rig_has_textures_but_no_custom_models.message: |- You have textures applied to your model, but have no custom models (Cubes) to use them! Please create some Cubes to use these textures, or remove the textures before exporting. @@ -621,6 +728,20 @@ animated_java.misc.failed_to_export.rig_has_custom_models_but_no_textures.messag You have custom models (Cubes) in your model, but have no textures applied to them! Please apply textures to your Cubes, or remove the Cubes before exporting. +animated_java.toast.invalid_rotations: |- + Invalid Cube Rotations! + + Cubes must have a rotation of: 45, 22.5, 0, -22.5, or -45, and can only be rotated on a single axis at a time. + + All cubes with invalid rotations are outlined in red. + +animated_java.toast.invalid_rotations_post_1_21_6: |- + Invalid Cube Rotations! + + Cubes can only be rotated on a single axis at a time when targeting Minecraft 1.21.6 or later. + + All cubes with invalid rotations are outlined in red. + # Format Category animated_java.format_category.animated_java: Animated Java @@ -633,4 +754,5 @@ animated_java.block_model_manager.facing_warning: The "facing" blockstate key is animated_java.error.blueprint_export_path_doesnt_exist.title: Blueprint Export Path Doesn't Exist animated_java.error.blueprint_export_path_doesnt_exist.description: |- The export path '{0}' does not exist! + Make sure the folder you're saving to exists and try again. diff --git a/src/mods/actions.ts b/src/mods/actions.ts new file mode 100644 index 00000000..827e57e7 --- /dev/null +++ b/src/mods/actions.ts @@ -0,0 +1,74 @@ +import { BLUEPRINT_CODEC } from 'src/formats/blueprint/codec' +import { openBlueprintSettingsDialog } from 'src/interface/dialog/blueprintSettings' +import { activeProjectIsBlueprintFormat, saveBlueprint } from '../formats/blueprint' +import { registerConditionalPropertyOverrideMod } from '../util/moddingTools' + +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-click-override/save-project`, + object: BarItems.save_project as Action, + key: 'click', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => saveBlueprint, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-click-override/save-project-as`, + object: BarItems.save_project_as as Action, + key: 'click', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => { + return () => { + const codec = BLUEPRINT_CODEC.get() + if (!codec) throw new Error('Blueprint codec not registered') + codec.export() + } + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-click-override/project-window`, + object: BarItems.project_window as Action, + key: 'click', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => { + return openBlueprintSettingsDialog + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-click-override/export-project`, + object: BarItems.export_over as Action, + key: 'click', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => { + return () => { + if (!Project || !Format) return + + const codec = BLUEPRINT_CODEC.get()! + if (!codec) throw new Error('Blueprint codec not registered') + + const path = Project.save_path || Project.export_path + if (path) { + if (fs.existsSync(PathModule.dirname(path))) { + Project.save_path = path + codec.write(codec.compile(), path) + } else { + console.error( + `Failed to export Animated Java Blueprint, file location '${path}' does not exist!` + ) + codec.export() + } + } else { + codec.export() + } + } + }, +}) diff --git a/src/mods/addLocatorActionMod.ts b/src/mods/addLocatorActionMod.ts deleted file mode 100644 index 269277ad..00000000 --- a/src/mods/addLocatorActionMod.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:addLocatorAction`, - { - action: BarItems.add_locator as Action, - originalCondition: (BarItems.add_locator as Action).condition, - }, - context => { - context.action.condition = () => { - if (isCurrentFormat()) return true - return !!context.originalCondition?.() - } - - Toolbars.outliner.add(context.action, 0) - - return context - }, - context => { - context.action.condition = context.originalCondition - Toolbars.outliner.remove(context.action) - } -) diff --git a/src/mods/animation.ts b/src/mods/animation.ts new file mode 100644 index 00000000..8e60486f --- /dev/null +++ b/src/mods/animation.ts @@ -0,0 +1,133 @@ +import { openAnimationPropertiesDialog } from 'src/interface/dialog/animationProperties' +import { registerConditionalPropertyOverrideMod, registerMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { roundToNth } from '../util/misc' +import { translate } from '../util/translation' + +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface _Animation { + excluded_nodes: CollectionItem[] + } + + interface AnimationUndoCopy { + excluded_nodes: string[] + } + + interface AnimationOptions { + excluded_nodes: string[] + } +} + +export const DEFAULT_SNAPPING_VALUE = 20 +export const MINIMUM_ANIMATION_LENGTH = 0.05 + +//region Extend +registerConditionalPropertyOverrideMod({ + id: `animated-java:function-override/animation/extend`, + object: Blockbench.Animation.prototype, + key: 'extend', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: _Animation, data?: AnimationOptions) { + original.call(this, data) + this.snapping = DEFAULT_SNAPPING_VALUE + this.length = Math.max(this.length, MINIMUM_ANIMATION_LENGTH) + for (const animator of Object.values(this.animators)) { + if (!animator) continue + let lastTime = -Infinity + for (const kf of animator.keyframes) { + let rounded = roundToNth(kf.time, DEFAULT_SNAPPING_VALUE) + if (rounded === kf.time) continue + if (rounded === lastTime) rounded += 0.05 + kf.time = rounded + lastTime = rounded + } + } + return this + } + }, +}) + +//region Set Length +registerConditionalPropertyOverrideMod({ + id: `animated-java:function-override/animation/set-length`, + object: Blockbench.Animation.prototype, + key: 'setLength', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: _Animation, length?: number) { + length = Math.max(length ?? this.length, MINIMUM_ANIMATION_LENGTH) + return original.call(this, length) + } + }, +}) + +//region Properties Dialog +registerConditionalPropertyOverrideMod({ + id: `animated-java:function-override/animation/properties-dialog`, + object: Blockbench.Animation.prototype, + key: 'propertiesDialog', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => { + return function (this: _Animation) { + if (!Blockbench.Animation.selected) { + Blockbench.showQuickMessage('No animation selected') + return + } + openAnimationPropertiesDialog(Blockbench.Animation.selected) + } + }, +}) + +//region Properties +registerMod({ + id: `animated-java:property-definitions/animation`, + + apply: () => { + const excludedNodesProperty = new Property( + Blockbench.Animation, + 'array', + 'excluded_nodes', + { + condition: () => activeProjectIsBlueprintFormat(), + label: translate('animation.excluded_nodes'), + default: [], + } + ) + + return { excludedNodesProperty } + }, + + revert: ({ excludedNodesProperty }) => { + excludedNodesProperty.delete() + }, +}) + +//region Force Saved +registerConditionalPropertyOverrideMod({ + id: `animated-java:animation-force-saved`, + object: Blockbench.Animation.prototype, + key: 'saved', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => true, +}) + +//region Save All Action +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-condition-override/save-all-animations`, + object: BarItems.save_all_animations as Action, + key: 'condition', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => false, +}) diff --git a/src/mods/animationController.ts b/src/mods/animationController.ts new file mode 100644 index 00000000..a9d6d4fe --- /dev/null +++ b/src/mods/animationController.ts @@ -0,0 +1,12 @@ +import { registerConditionalPropertyOverrideMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' + +registerConditionalPropertyOverrideMod({ + id: `animated-java:animation-controller-force-saved`, + object: AnimationController.prototype, + key: 'saved', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => true, +}) diff --git a/src/mods/animationControllerMod.ts b/src/mods/animationControllerMod.ts deleted file mode 100644 index 91cc42be..00000000 --- a/src/mods/animationControllerMod.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod, createPropertySubscribable } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:animationControllerMod`, - undefined, - () => { - const [, set] = createPropertySubscribable(AnimationController.prototype, 'saved') - const unsubSet = set.subscribe(({ storage }) => { - if (Format.id === BLUEPRINT_FORMAT.id) { - storage.value = true - } - }) - return { unsubSet } - }, - context => { - context.unsubSet() - } -) diff --git a/src/mods/animationPropertiesAction.ts b/src/mods/animationPropertiesAction.ts deleted file mode 100644 index 4384641f..00000000 --- a/src/mods/animationPropertiesAction.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { openAnimationPropertiesDialog } from '../interface/dialog/animationProperties' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:animationPropertiesAction`, - { - originalOpen: Blockbench.Animation.prototype.propertiesDialog, - }, - context => { - Blockbench.Animation.prototype.propertiesDialog = function (this: _Animation) { - if (isCurrentFormat()) { - if (!Blockbench.Animation.selected) { - Blockbench.showQuickMessage('No animation selected') - return - } - openAnimationPropertiesDialog(Blockbench.Animation.selected) - } else { - context.originalOpen.call(this) - } - } - return context - }, - context => { - Blockbench.Animation.prototype.propertiesDialog = context.originalOpen - } -) diff --git a/src/mods/animationPropertiesMod.ts b/src/mods/animationPropertiesMod.ts deleted file mode 100644 index fd914745..00000000 --- a/src/mods/animationPropertiesMod.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { roundToNth } from '../util/misc' -import { ContextProperty, createBlockbenchMod } from '../util/moddingTools' -import { translate } from '../util/translation' - -export const DEFAULT_SNAPPING_VALUE = 20 -export const MINIMUM_ANIMATION_LENGTH = 0.05 - -createBlockbenchMod( - `${PACKAGE.name}:animationDefaultPropertiesMod`, - { - originalExtend: Blockbench.Animation.prototype.extend, - originalSetLength: Blockbench.Animation.prototype.setLength, - }, - context => { - Blockbench.Animation.prototype.extend = function ( - this: _Animation, - data: AnimationOptions - ) { - context.originalExtend.call(this, data) - if (isCurrentFormat()) { - this.snapping = DEFAULT_SNAPPING_VALUE - this.length = Math.max(this.length, MINIMUM_ANIMATION_LENGTH) - for (const animator of Object.values(this.animators)) { - if (!animator) continue - let lastTime = -Infinity - for (const kf of animator.keyframes) { - let rounded = roundToNth(kf.time, DEFAULT_SNAPPING_VALUE) - if (rounded === kf.time) continue - if (rounded === lastTime) rounded += 0.05 - kf.time = rounded - lastTime = rounded - } - } - } - return this - } - - Blockbench.Animation.prototype.setLength = function (this: _Animation, length?: number) { - if (isCurrentFormat()) { - length = Math.max(length || this.length, MINIMUM_ANIMATION_LENGTH) - } - return context.originalSetLength.call(this, length) - } - - return context - }, - context => { - Blockbench.Animation.prototype.extend = context.originalExtend - Blockbench.Animation.prototype.setLength = context.originalSetLength - } -) - -createBlockbenchMod( - `${PACKAGE.name}:animationPropertiesMod`, - { - excludedNodesProperty: undefined as ContextProperty<'array'>, - }, - context => { - context.excludedNodesProperty = new Property( - Blockbench.Animation, - 'array', - 'excluded_nodes', - { - condition: () => isCurrentFormat(), - label: translate('animation.excluded_nodes'), - default: [], - } - ) - return context - }, - context => { - context.excludedNodesProperty?.delete() - } -) diff --git a/src/mods/assetSetupMod.ts b/src/mods/assetSetupMod.ts deleted file mode 100644 index c3f55817..00000000 --- a/src/mods/assetSetupMod.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { PACKAGE } from '../constants' -import { - hideLoadingPopup, - showLoadingPopup, - showOfflineError, -} from '../interface/popup/animatedJavaLoading' -import { events } from '../util/events' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:assetLoading`, - undefined, - () => { - // Show loading popup - void showLoadingPopup().then(async () => { - if (!window.navigator.onLine) { - showOfflineError() - // return - } - events.NETWORK_CONNECTED.dispatch() - - await Promise.all([ - new Promise(resolve => - events.MINECRAFT_ASSETS_LOADED.subscribe(() => resolve()) - ), - new Promise(resolve => - events.MINECRAFT_REGISTRY_LOADED.subscribe(() => resolve()) - ), - new Promise(resolve => - events.MINECRAFT_FONTS_LOADED.subscribe(() => resolve()) - ), - new Promise(resolve => - events.BLOCKSTATE_REGISTRY_LOADED.subscribe(() => resolve()) - ), - ]) - .then(() => { - hideLoadingPopup() - }) - .catch(error => { - console.error(error) - Blockbench.showToastNotification({ - text: 'Animated Java failed to load! Please restart Blockbench', - color: 'var(--color-error)', - }) - }) - }) - }, - () => { - // - } -) diff --git a/src/mods/blockbenchReadMod.ts b/src/mods/blockbenchReadMod.ts deleted file mode 100644 index 0a5bf5d4..00000000 --- a/src/mods/blockbenchReadMod.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { PACKAGE } from '../constants' -import { - closeBlueprintLoadingDialog, - openBlueprintLoadingDialog, - PROGRESS, -} from '../interface/popup/blueprintLoading' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:blockbenchReadMod`, - { - original: Blockbench.read, - }, - context => { - async function asyncRead( - files: Parameters['0'], - options: Parameters['1'], - cb: Parameters['2'] - ) { - for (const file of files) { - context.original([file], options, cb) - await new Promise(r => { - if (Project?.loadingPromises) { - openBlueprintLoadingDialog() - const promises: Array> = [] - for (const promise of Project.loadingPromises) { - promises.push( - new Promise(r => { - promise - .catch((err: any) => console.error(err)) - .finally(() => { - PROGRESS.set(PROGRESS.get() + 1) - r() - }) - }) - ) - } - void Promise.all(promises) - .catch(err => { - console.error('Failed to load project') - console.error(err) - }) - .finally(() => { - closeBlueprintLoadingDialog() - r() - }) - return - } - r() - }) - } - } - - Blockbench.read = function (files, options, cb) { - void asyncRead(files, options, cb).catch(console.error) - } - return context - }, - context => { - Blockbench.read = context.original - } -) diff --git a/src/mods/boneAnimatorMod.ts b/src/mods/boneAnimatorMod.ts new file mode 100644 index 00000000..af4c0834 --- /dev/null +++ b/src/mods/boneAnimatorMod.ts @@ -0,0 +1,68 @@ +import { Valuable } from 'src/util/stores' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { roundToNth } from '../util/misc' +import { registerConditionalPropertyOverrideMod } from '../util/moddingTools' + +export const BONE_INTERPOLATION_ENABLED = new Valuable(true) + +//region Interpolate +registerConditionalPropertyOverrideMod({ + id: `animated-java:override-function/bone-animator/interpolate`, + object: BoneAnimator.prototype, + key: 'interpolate', + + condition: () => BONE_INTERPOLATION_ENABLED.get() && activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: BoneAnimator, channel, allowExpression, axis) { + if ( + !BONE_INTERPOLATION_ENABLED.get() || + !activeProjectIsBlueprintFormat() || + allowExpression === false + ) { + return original.call(this, channel, allowExpression, axis) + } + + const realTime = this.animation.time + try { + Timeline.time = roundToNth(this.animation.time, 20) + + let before: ArrayVector3 | false + let after: ArrayVector3 | false + let beforeTime: number + let afterTime: number + + if (Timeline.time < realTime) { + beforeTime = Timeline.time + before = original.call(this, channel, allowExpression, axis) + if (!before) return false + + afterTime = roundToNth(Timeline.time + 0.05, 20) + Timeline.time = afterTime + after = original.call(this, channel, allowExpression, axis) + if (!after) return false + } else { + afterTime = Timeline.time + after = original.call(this, channel, allowExpression, axis) + if (!after) return false + + beforeTime = roundToNth(Timeline.time - 0.05, 20) + Timeline.time = beforeTime + before = original.call(this, channel, allowExpression, axis) + if (!before) return false + } + const diff = (realTime - beforeTime) / (afterTime - beforeTime) + + const result: ArrayVector3 = [ + Math.lerp(before[0], after[0], diff), + Math.lerp(before[1], after[1], diff), + Math.lerp(before[2], after[2], diff), + ] + + return result + } finally { + Timeline.time = realTime + } + } + }, +}) diff --git a/src/mods/boneInterpolationMod.ts b/src/mods/boneInterpolationMod.ts deleted file mode 100644 index 22554d83..00000000 --- a/src/mods/boneInterpolationMod.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { roundToNth } from '../util/misc' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:boneInterpolationMod`, - { - orignalInterpolate: BoneAnimator.prototype.interpolate, - }, - context => { - BoneAnimator.prototype.interpolate = function ( - this: BoneAnimator, - channel, - allowExpression, - axis - ) { - if (!isCurrentFormat() || !allowExpression) { - return context.orignalInterpolate.call(this, channel, allowExpression, axis) - } - - const actualTime = this.animation.time - try { - Timeline.time = roundToNth(this.animation.time, 20) - - let before: ArrayVector3 | false - let after: ArrayVector3 | false - let beforeTime: number - let afterTime: number - - if (Timeline.time < actualTime) { - beforeTime = Timeline.time - before = context.orignalInterpolate.call(this, channel, allowExpression, axis) - if (!before) return false - - afterTime = roundToNth(Timeline.time + 0.05, 20) - Timeline.time = afterTime - after = context.orignalInterpolate.call(this, channel, allowExpression, axis) - if (!after) return false - } else { - afterTime = Timeline.time - after = context.orignalInterpolate.call(this, channel, allowExpression, axis) - if (!after) return false - - beforeTime = roundToNth(Timeline.time - 0.05, 20) - Timeline.time = beforeTime - before = context.orignalInterpolate.call(this, channel, allowExpression, axis) - if (!before) return false - } - const diff = (actualTime - beforeTime) / (afterTime - beforeTime) - - const result: ArrayVector3 = [ - Math.lerp(before[0], after[0], diff), - Math.lerp(before[1], after[1], diff), - Math.lerp(before[2], after[2], diff), - ] - // console.log(diff) - - return result - - // context.orignalInterpolate.call(this, channel, allowExpression, axis) - } finally { - Timeline.time = actualTime - } - } - - return context - }, - context => { - context.orignalInterpolate = BoneAnimator.prototype.interpolate - } -) diff --git a/src/mods/bonePropertiesMod.ts b/src/mods/bonePropertiesMod.ts deleted file mode 100644 index 349640da..00000000 --- a/src/mods/bonePropertiesMod.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { isCurrentFormat as condition } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { type ContextProperty, createBlockbenchMod } from '../util/moddingTools' - -class DeepClonedObjectProperty extends Property<'object'> { - constructor(targetClass: any, name: string, options?: PropertyOptions) { - super(targetClass, 'object', name, options) - } - merge(instance: any, data: any) { - if (typeof data[this.name] === 'object') { - instance[this.name] = JSON.parse(JSON.stringify(data[this.name])) - } - } - copy(instance: any, target: any) { - if (typeof instance[this.name] === 'object') { - target[this.name] = JSON.parse(JSON.stringify(instance[this.name])) - } - } -} - -createBlockbenchMod( - `${PACKAGE.name}:boneProperties`, - { - configs: undefined as ContextProperty<'object'>, - }, - context => { - context.configs = new DeepClonedObjectProperty(Group, 'configs', { - condition, - default: { default: undefined, variants: {} }, - }) - - return context - }, - context => { - context.configs?.delete() - } -) diff --git a/src/mods/cameraNameMod.ts b/src/mods/cameraNameMod.ts deleted file mode 100644 index f061da24..00000000 --- a/src/mods/cameraNameMod.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { sanitizeOutlinerElementName } from '../outliner/util' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:cameraNameMod`, - { - originalRename: OutlinerElement.types.camera?.prototype.saveName, - originalSanitize: OutlinerElement.types.camera?.prototype.sanitizeName, - }, - context => { - if (OutlinerElement.types.camera) { - OutlinerElement.types.camera.prototype.saveName = function ( - this: OutlinerElement, - save?: boolean - ) { - if (isCurrentFormat()) { - this.name = sanitizeOutlinerElementName(this.name, this.uuid) - } - return context.originalRename.call(this, save) - } - OutlinerElement.types.camera.prototype.sanitizeName = function (this: OutlinerElement) { - if (isCurrentFormat()) { - this.name = sanitizeOutlinerElementName(this.name, this.uuid) - } - return context.originalSanitize.call(this) - } - } - return context - }, - context => { - if (OutlinerElement.types.camera) { - OutlinerElement.types.camera.prototype.rename = context.originalRename - } - } -) diff --git a/src/mods/cameraPlugin.ts b/src/mods/cameraPlugin.ts new file mode 100644 index 00000000..ee3fb96f --- /dev/null +++ b/src/mods/cameraPlugin.ts @@ -0,0 +1,36 @@ +import { registerPluginMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { sanitizeOutlinerElementName } from '../outliner/util' + +registerPluginMod({ + id: `animated-java:camera-plugin/camera-name-sanitization`, + + condition: plugin => plugin.id === 'cameras', + + apply: () => { + const camera = OutlinerElement.types.camera + + const originalRename = camera.prototype.saveName + camera.prototype.saveName = function (this: OutlinerElement, save?: boolean) { + if (activeProjectIsBlueprintFormat()) { + this.name = sanitizeOutlinerElementName(this.name, this.uuid) + } + return originalRename.call(this, save) + } + + const originalSanitize = camera.prototype.sanitizeName + camera.prototype.sanitizeName = function (this: OutlinerElement) { + if (activeProjectIsBlueprintFormat()) { + this.name = sanitizeOutlinerElementName(this.name, this.uuid) + } + return originalSanitize.call(this) + } + + return { camera, originalRename, originalSanitize } + }, + + revert: ({ camera, originalRename, originalSanitize }) => { + camera.prototype.rename = originalRename + camera.prototype.sanitizeName = originalSanitize + }, +}) diff --git a/src/mods/collection.ts b/src/mods/collection.ts new file mode 100644 index 00000000..d02ff81b --- /dev/null +++ b/src/mods/collection.ts @@ -0,0 +1,12 @@ +import { activeProjectIsBlueprintFormat } from 'src/formats/blueprint' +import { registerConditionalPropertyOverrideMod } from 'src/util/moddingTools' + +registerConditionalPropertyOverrideMod({ + id: 'animated-java:property-condition-override/collection/export-path', + object: Collection.properties.export_path, + key: 'condition', + + condition: () => activeProjectIsBlueprintFormat(), + + get: () => false, +}) diff --git a/src/mods/cube.ts b/src/mods/cube.ts new file mode 100644 index 00000000..86bc75c0 --- /dev/null +++ b/src/mods/cube.ts @@ -0,0 +1,107 @@ +import { translate } from 'src/util/translation' +import { activeProjectIsBlueprintFormat, projectTargetVersionIsAtLeast } from '../formats/blueprint' +import { isCubeValid } from '../systems/util' +import { createPropertySubscribable, registerMod } from '../util/moddingTools' + +declare global { + interface Cube { + isRotationValid: boolean + } +} + +const ERROR_OUTLINE_MATERIAL = Canvas.outlineMaterial.clone() +ERROR_OUTLINE_MATERIAL.color.set('#ff0000') + +export function updateAllCubeOutlines() { + for (const cube of Cube.all) { + Cube.preview_controller.updateTransform(cube) + } +} + +function updateCubeValidity(cube: Cube, isValid: boolean) { + if (cube.isRotationValid === isValid) return + cube.mesh.outline.material = isValid ? Canvas.outlineMaterial : ERROR_OUTLINE_MATERIAL + cube.isRotationValid = isValid +} + +let toastNotification: Deletable | null = null + +function showToastNotification() { + if (!toastNotification) { + toastNotification = Blockbench.showToastNotification({ + text: translate( + projectTargetVersionIsAtLeast('1.21.6') + ? 'toast.invalid_rotations_post_1_21_6' + : 'toast.invalid_rotations' + ), + color: 'var(--color-error)', + click: () => false, + }) + const intervalId = setInterval(() => { + if ( + Cube.all.some(cube => isCubeValid(cube) === 'invalid') && + document.querySelector('li.toast_notification') + ) + return + clearInterval(intervalId) + toastNotification?.delete() + toastNotification = null + requestAnimationFrame(updateAllCubeOutlines) + }, 1000) + } +} + +registerMod({ + id: `animated-java:cube-outline-mod`, + + apply: () => { + const originalUpdateTransform = Cube.preview_controller.updateTransform + Cube.preview_controller.updateTransform = function (cube: Cube) { + if (activeProjectIsBlueprintFormat()) { + const validity = isCubeValid(cube) + + switch (validity) { + case 'valid': { + updateCubeValidity(cube, true) + break + } + case '1.21.6+': { + if (projectTargetVersionIsAtLeast('1.21.6')) { + updateCubeValidity(cube, true) + break + } + // Fallthrough to invalid if the target version is below 1.21.6 + } + case 'invalid': { + updateCubeValidity(cube, false) + showToastNotification() + break + } + } + } + originalUpdateTransform.call(this, cube) + } + + const originalInit = Cube.prototype.init + Cube.prototype.init = function (this: Cube) { + const cube = originalInit.call(this) + + cube.isRotationValid = true + + const [, setVisible] = createPropertySubscribable(cube.mesh.outline, 'visible') + setVisible.subscribe(({ storage }) => { + if (!activeProjectIsBlueprintFormat()) return + storage.value = !cube.isRotationValid || storage.value + }) + + return cube + } + + return { originalUpdateTransform, originalInit } + }, + + revert: ({ originalUpdateTransform, originalInit }) => { + Cube.preview_controller.updateTransform = originalUpdateTransform + Cube.prototype.init = originalInit + }, +}) diff --git a/src/mods/cubeOutlineMod.ts b/src/mods/cubeOutlineMod.ts deleted file mode 100644 index 0654adf4..00000000 --- a/src/mods/cubeOutlineMod.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { isCubeValid } from '../systems/util' -import { createBlockbenchMod, createPropertySubscribable } from '../util/moddingTools' - -const ERROR_OUTLINE_MATERIAL = Canvas.outlineMaterial.clone() -ERROR_OUTLINE_MATERIAL.color.set('#ff0000') - -createBlockbenchMod( - `${PACKAGE.name}:cubeOutlineMod`, - { - originalUpdateTransform: Cube.preview_controller.updateTransform, - originalInit: Cube.prototype.init, - }, - context => { - Cube.preview_controller.updateTransform = function (cube: Cube) { - if (isCurrentFormat()) { - const isValid = isCubeValid(cube) - if (cube.rotationInvalid && isValid) { - cube.mesh.outline.material = Canvas.outlineMaterial - cube.rotationInvalid = false - } else if (!cube.rotationInvalid && !isValid) { - cube.mesh.outline.material = ERROR_OUTLINE_MATERIAL - cube.rotationInvalid = true - if (!Project!.showingInvalidCubeRotations) { - Blockbench.showToastNotification({ - text: 'Invalid Cube Rotation!\nCubes can only be rotated in 22.5 degree increments (45, 22.5, 0, -22.5, -45) and can only be rotated on a single axis.\nThe offending cubes have been highlighted in red.', - color: 'var(--color-error)', - }) - Project!.showingInvalidCubeRotations = true - } - } - } - context.originalUpdateTransform.call(this, cube) - } - - Cube.prototype.init = function (this: Cube) { - const cube = context.originalInit.call(this) - - cube.rotationInvalid = false - - const [get] = createPropertySubscribable(this.mesh.outline, 'visible') - get.subscribe(({ storage }) => { - if (isCurrentFormat()) storage.value = this.rotationInvalid || storage.value - }) - - return cube - } - - return context - }, - context => { - Cube.preview_controller.updateTransform = context.originalUpdateTransform - Cube.prototype.init = context.originalInit - } -) diff --git a/src/mods/customFormatIcon.ts b/src/mods/customFormatIcon.ts new file mode 100644 index 00000000..3ce3bd2d --- /dev/null +++ b/src/mods/customFormatIcon.ts @@ -0,0 +1,34 @@ +import { BLUEPRINT_FORMAT_ID } from 'src/formats/blueprint' +import { registerMod } from 'src/util/moddingTools' +import Icon from '../components/icon.svelte' +import { registerMountSvelteComponentMod } from '../util/mountSvelteComponent' + +registerMountSvelteComponentMod({ + id: 'animated-java:injected-svelte/format-icon', + component: Icon, + target: `li[format="${BLUEPRINT_FORMAT_ID}"] span`, + replaceChildren: true, +}) + +registerMod({ + id: `animated-java:prioritize-animated-java-formats`, + + apply: () => { + const interval = setInterval(() => { + const ajFormats = $("li.format_category > label:contains('Animated Java')") + .first() + .parent() + if (ajFormats.length === 0) return + + const mcFormats = $("li.format_category > label:contains('General')").first().parent() + + ajFormats.insertBefore(mcFormats) + + clearInterval(interval) + }, 16) + }, + + revert: () => { + // Nothing to do here. The AJ format list entry will be deleted when AJ is unloaded. + }, +}) diff --git a/src/mods/customKeyframes.ts b/src/mods/customKeyframes.ts new file mode 100644 index 00000000..cd52a002 --- /dev/null +++ b/src/mods/customKeyframes.ts @@ -0,0 +1,253 @@ +import { registerConditionalPropertyOverrideMod, registerProjectMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat, BLUEPRINT_FORMAT_ID } from '../formats/blueprint' +import { translate } from '../util/translation' +import { Variant } from '../variants' + +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface _Keyframe { + variant?: Variant + function?: string + execute_condition?: string + repeat?: boolean + repeat_frequency?: number + } +} + +export enum LOCATOR_CHANNELS { + FUNCTION = 'function', +} + +export enum EFFECT_ANIMATOR_CHANNELS { + VARIANT = 'variant', + FUNCTION = 'function', +} + +export enum KEYFRAME_DATA_POINTS { + EXECUTE_CONDITION = 'execute_condition', + REPEAT = 'repeat', + REPEAT_FREQUENCY = 'repeat_frequency', +} + +export function isCustomKeyframeChannel(channel: string) { + return Object.values(EFFECT_ANIMATOR_CHANNELS).includes(channel as any) +} + +registerConditionalPropertyOverrideMod({ + id: `animated-java:keyframe/data-point/variant`, + object: Blockbench.Keyframe.prototype, + key: EFFECT_ANIMATOR_CHANNELS.VARIANT, + + condition: () => activeProjectIsBlueprintFormat(), + + get(this: _Keyframe) { + const uuid = this.data_points.at(0)?.[EFFECT_ANIMATOR_CHANNELS.VARIANT] as + | string + | undefined + if (uuid) return Variant.all.find(v => v.uuid === uuid) + console.error('Keyframe variant', uuid, 'not found!') + }, + + set(this: _Keyframe, value: Variant | undefined) { + const dataPoint = this.data_points.at(0) + if (dataPoint) dataPoint[EFFECT_ANIMATOR_CHANNELS.VARIANT] = value?.uuid + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:keyframe/data-point/function`, + object: Blockbench.Keyframe.prototype, + key: EFFECT_ANIMATOR_CHANNELS.FUNCTION, + + condition: () => activeProjectIsBlueprintFormat(), + + get(this: _Keyframe) { + return this.data_points.at(0)?.[EFFECT_ANIMATOR_CHANNELS.FUNCTION] ?? '' + }, + + set(this: _Keyframe, value: string) { + const dataPoint = this.data_points.at(0) + if (dataPoint) dataPoint[EFFECT_ANIMATOR_CHANNELS.FUNCTION] = value + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:keyframe/data-point/execute-condition`, + object: Blockbench.Keyframe.prototype, + key: KEYFRAME_DATA_POINTS.EXECUTE_CONDITION, + + condition: () => activeProjectIsBlueprintFormat(), + + get(this: _Keyframe) { + return this.data_points.at(0)?.[KEYFRAME_DATA_POINTS.EXECUTE_CONDITION] ?? '' + }, + + set(this: _Keyframe, value: string) { + const dataPoint = this.data_points.at(0) + if (dataPoint) dataPoint[KEYFRAME_DATA_POINTS.EXECUTE_CONDITION] = value + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:keyframe/data-point/repeat`, + object: Blockbench.Keyframe.prototype, + key: KEYFRAME_DATA_POINTS.REPEAT, + + condition: () => activeProjectIsBlueprintFormat(), + + get(this: _Keyframe) { + return this.data_points.at(0)?.[KEYFRAME_DATA_POINTS.REPEAT] ?? false + }, + + set(this: _Keyframe, value: boolean) { + const dataPoint = this.data_points.at(0) + if (dataPoint) dataPoint[KEYFRAME_DATA_POINTS.REPEAT] = value + }, +}) + +registerConditionalPropertyOverrideMod({ + id: `animated-java:keyframe/data-point/repeat-frequency`, + object: Blockbench.Keyframe.prototype, + key: KEYFRAME_DATA_POINTS.REPEAT_FREQUENCY, + + condition: () => activeProjectIsBlueprintFormat(), + + get(this: _Keyframe) { + return this.data_points.at(0)?.[KEYFRAME_DATA_POINTS.REPEAT_FREQUENCY] ?? 1 + }, + + set(this: _Keyframe, value: number) { + const dataPoint = this.data_points.at(0) + if (dataPoint) dataPoint[KEYFRAME_DATA_POINTS.REPEAT_FREQUENCY] = value + }, +}) + +registerProjectMod({ + id: 'animated-java:custom-keyframes', + + condition: project => project.format.id === BLUEPRINT_FORMAT_ID, + + apply: () => { + const defaultChannels = { ...EffectAnimator.prototype.channels } + + // Add custom keyframe channels + EffectAnimator.addChannel(EFFECT_ANIMATOR_CHANNELS.VARIANT, { + name: translate('effect_animator.timeline.variant'), + mutable: true, + max_data_points: 1, + }) + EffectAnimator.addChannel(EFFECT_ANIMATOR_CHANNELS.FUNCTION, { + name: translate('effect_animator.timeline.function'), + mutable: true, + max_data_points: 1, + }) + + // Add custom keyframe properties to the KeyframeDataPoint class + const properties = [ + new Property(KeyframeDataPoint, 'string', EFFECT_ANIMATOR_CHANNELS.VARIANT, { + label: translate('effect_animator.keyframe_data_point.variant'), + condition: datapoint => + datapoint.keyframe.channel === EFFECT_ANIMATOR_CHANNELS.VARIANT, + exposed: false, + default: () => Variant.getDefault().uuid, + }), + + new Property(KeyframeDataPoint, 'string', EFFECT_ANIMATOR_CHANNELS.FUNCTION, { + label: translate('effect_animator.keyframe_data_point.function'), + default: '', + condition: datapoint => + datapoint.keyframe.channel === EFFECT_ANIMATOR_CHANNELS.FUNCTION, + exposed: false, + }), + + new Property(KeyframeDataPoint, 'string', KEYFRAME_DATA_POINTS.EXECUTE_CONDITION, { + label: translate('effect_animator.keyframe_data_point.execute_condition'), + default: '', + condition: datapoint => isCustomKeyframeChannel(datapoint.keyframe.channel), + exposed: false, + }), + + new Property(KeyframeDataPoint, 'boolean', KEYFRAME_DATA_POINTS.REPEAT, { + label: translate('effect_animator.keyframe_data_point.repeat'), + default: false, + condition: datapoint => + datapoint.keyframe.channel === EFFECT_ANIMATOR_CHANNELS.FUNCTION, + exposed: false, + }), + + new Property(KeyframeDataPoint, 'number', KEYFRAME_DATA_POINTS.REPEAT_FREQUENCY, { + label: translate('effect_animator.keyframe_data_point.repeat_frequency'), + default: 1, + condition: datapoint => + datapoint.keyframe.channel === EFFECT_ANIMATOR_CHANNELS.FUNCTION, + exposed: false, + }), + ] + + // Remove default keyframe channels (except sound) + for (const channel of Object.keys(defaultChannels)) { + if (channel === 'sound') continue + delete EffectAnimator.prototype.channels[channel] + } + + // Modify the displayFrame method to handle custom keyframes + const defaultEffectDisplayFrame = EffectAnimator.prototype.displayFrame + EffectAnimator.prototype.displayFrame = function (this: EffectAnimator, inLoop: boolean) { + this.muted.particle = true + this.muted.timeline = true + // Default Blockbench Sound keyframe handling + defaultEffectDisplayFrame.call(this, inLoop) + + if (!(Project && activeProjectIsBlueprintFormat())) return + if (!this.muted[EFFECT_ANIMATOR_CHANNELS.VARIANT]) { + let after, before, result: _Keyframe | undefined + + for (const kf of this[EFFECT_ANIMATOR_CHANNELS.VARIANT] as _Keyframe[]) { + if (kf.time < this.animation.time) { + if (!before || kf.time > before.time) { + before = kf + } + } else { + if (!after || kf.time < after.time) { + after = kf + } + } + } + + if (after && after.time === this.animation.time) { + result = after + } else if (before) { + result = before + } else if (after) { + result = this[EFFECT_ANIMATOR_CHANNELS.VARIANT].at(-1) + } + + result?.variant?.select() + } + + this.last_displayed_time = this.animation.time + } + + return { defaultChannels, defaultEffectDisplayFrame, properties } + }, + + revert: ({ defaultChannels, defaultEffectDisplayFrame, properties }) => { + for (const channel of Object.keys(defaultChannels)) { + if (channel === 'sound') continue // AJ doesn't modify the sound channel + EffectAnimator.prototype.channels[channel] = defaultChannels[channel] + } + + for (const prop of properties) { + KeyframeDataPoint.properties[prop.name]?.delete() + } + + for (const channel of Object.values(EFFECT_ANIMATOR_CHANNELS)) { + delete EffectAnimator.prototype.channels[channel] + } + + delete BoneAnimator.prototype.channels.commands + delete BoneAnimator.prototype.commands + + EffectAnimator.prototype.displayFrame = defaultEffectDisplayFrame + }, +}) diff --git a/src/mods/customKeyframesMod.ts b/src/mods/customKeyframesMod.ts deleted file mode 100644 index ba6ec78d..00000000 --- a/src/mods/customKeyframesMod.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { BLUEPRINT_FORMAT, isCurrentFormat } from '../blueprintFormat' -import { events } from '../util/events' -import { translate } from '../util/translation' -import { Variant } from '../variants' - -const DEFAULT_CHANNELS = { ...EffectAnimator.prototype.channels } -const DEFAULT_EFFECT_DISPLAY_FRAME = EffectAnimator.prototype.displayFrame -export const CUSTOM_CHANNELS = ['variant', 'commands'] - -let installed = false - -function injectCustomKeyframes() { - if (installed) return - - // Add custom keyframe channels - EffectAnimator.addChannel('variant', { - name: translate('effect_animator.timeline.variant'), - mutable: true, - max_data_points: 1, - }) - EffectAnimator.addChannel('commands', { - name: translate('effect_animator.timeline.commands'), - mutable: true, - max_data_points: 1, - }) - - // Add custom keyframe properties to the KeyframeDataPoint class - const variantKeyframeDataPoint = new Property(KeyframeDataPoint, 'string', 'variant', { - label: translate('effect_animator.keyframe.variant'), - condition: datapoint => datapoint.keyframe.channel === 'variant', - exposed: false, - }) - Object.defineProperty(variantKeyframeDataPoint, 'default', { - get() { - return Variant.getDefault().uuid - }, - }) - new Property(KeyframeDataPoint, 'string', 'commands', { - label: translate('effect_animator.timeline.commands'), - default: '', - condition: datapoint => datapoint.keyframe.channel === 'commands', - exposed: false, - }) - new Property(KeyframeDataPoint, 'string', 'execute_condition', { - label: translate('effect_animator.timeline.execute_condition'), - default: '', - condition: datapoint => CUSTOM_CHANNELS.includes(datapoint.keyframe.channel as string), - exposed: false, - }) - new Property(KeyframeDataPoint, 'boolean', 'repeat', { - label: translate('effect_animator.timeline.repeat'), - default: false, - condition: datapoint => CUSTOM_CHANNELS.includes(datapoint.keyframe.channel as string), - exposed: false, - }) - new Property(KeyframeDataPoint, 'number', 'repeat_frequency', { - label: translate('effect_animator.timeline.repeat_frequency'), - default: 1, - condition: datapoint => CUSTOM_CHANNELS.includes(datapoint.keyframe.channel as string), - exposed: false, - }) - - // Remove default keyframe channels (except sound) - for (const channel of Object.keys(DEFAULT_CHANNELS)) { - if (channel === 'sound') continue - delete EffectAnimator.prototype.channels[channel] - } - - // Modify the displayFrame method to handle custom keyframes - EffectAnimator.prototype.displayFrame = function (this: EffectAnimator, inLoop: boolean) { - // Default Blockbench Sound keyframe handling - if (inLoop && !this.muted.sound) { - this.sound.forEach((kf: _Keyframe) => { - const diff = this.animation.time - kf.time - if (diff < 0) return - - let media = Timeline.playing_sounds.find(s => s.keyframe_id == kf.uuid) - if (diff >= 0 && diff < (1 / 60) * (Timeline.playback_speed / 100) && !media) { - if (kf.data_points[0].file && !kf.cooldown) { - media = new Audio(kf.data_points[0].file as string) - media.keyframe_id = kf.uuid - media.playbackRate = Math.clamp(Timeline.playback_speed / 100, 0.1, 4.0) - media.volume = Math.clamp(settings.volume.value / 100, 0, 1) - media.play().catch(() => { - /**/ - }) - Timeline.playing_sounds.push(media) - media.onended = function () { - Timeline.playing_sounds.remove(media) - } - - kf.cooldown = true - setTimeout(() => { - delete kf.cooldown - }, 400) - } - } else if (diff > 0) { - media = Timeline.playing_sounds.find(s => s.keyframe_id == kf.uuid) - if (media && Math.abs(media.currentTime - diff) > 0.08) { - // Resync - media.currentTime = diff - media.playbackRate = Math.clamp(Timeline.playback_speed / 100, 0.1, 4.0) - } - } - }) - } - - if (!(Project && isCurrentFormat())) return - if (!this.muted.variant) { - let after, before, result: _Keyframe | undefined - - for (const kf of this.variant as _Keyframe[]) { - if (kf.time < this.animation.time) { - if (!before || kf.time > before.time) { - before = kf - } - } else { - if (!after || kf.time < after.time) { - after = kf - } - } - } - - if (after && after.time === this.animation.time) { - result = after - } else if (before) { - result = before - } else if (after) { - result = this.variant.at(-1) - } - - if (result) { - const variant = Variant.all.find(v => v.uuid === result.data_points[0].variant) - if (!variant) console.error('Variant', result.data_points[0].variant, 'not found.') - variant?.select() - } - } - - this.last_displayed_time = this.animation.time - } - - installed = true -} - -function extractCustomKeyframes() { - if (!installed) return - - for (const channel of Object.keys(DEFAULT_CHANNELS)) { - if (channel === 'sound') continue - EffectAnimator.prototype.channels[channel] = DEFAULT_CHANNELS[channel] - } - - KeyframeDataPoint.properties.variant?.delete() - KeyframeDataPoint.properties.commands?.delete() - KeyframeDataPoint.properties.execute_condition?.delete() - KeyframeDataPoint.properties.repeat?.delete() - KeyframeDataPoint.properties.repeat_frequency?.delete() - - delete EffectAnimator.prototype.channels.variant - delete EffectAnimator.prototype.variant - delete EffectAnimator.prototype.channels.commands - delete EffectAnimator.prototype.commands - delete BoneAnimator.prototype.channels.commands - delete BoneAnimator.prototype.commands - - EffectAnimator.prototype.displayFrame = DEFAULT_EFFECT_DISPLAY_FRAME - - installed = false -} - -events.PRE_SELECT_PROJECT.subscribe(project => { - if (project.format.id === BLUEPRINT_FORMAT.id) { - injectCustomKeyframes() - } else { - extractCustomKeyframes() - } -}) - -function keyframeValueSetterFactory(channel: string) { - return function (kf: _Keyframe, value: ValueType) { - if (kf.data_points.length === 0) { - kf.data_points.push(new KeyframeDataPoint(kf)) - } - kf.data_points[0][channel] = value - } -} - -function keyframeValueGetterFactory(channel: string) { - return function (kf: _Keyframe) { - return kf.data_points.at(0)?.[channel] as ValueType | undefined - } -} - -export const setKeyframeVariant = keyframeValueSetterFactory('variant') -export const getKeyframeVariant = keyframeValueGetterFactory('variant') -export const setKeyframeCommands = keyframeValueSetterFactory('commands') -export const getKeyframeCommands = keyframeValueGetterFactory('commands') -export const setKeyframeExecuteCondition = keyframeValueSetterFactory('execute_condition') -export const getKeyframeExecuteCondition = keyframeValueGetterFactory('execute_condition') -export const setKeyframeRepeat = keyframeValueSetterFactory('repeat') -export const getKeyframeRepeat = keyframeValueGetterFactory('repeat') -export const setKeyframeRepeatFrequency = keyframeValueSetterFactory('repeat_frequency') -export const getKeyframeRepeatFrequency = keyframeValueGetterFactory('repeat_frequency') diff --git a/src/mods/eventHooks.ts b/src/mods/eventHooks.ts new file mode 100644 index 00000000..6f8a66e5 --- /dev/null +++ b/src/mods/eventHooks.ts @@ -0,0 +1,48 @@ +import EVENTS from 'src/util/events' +import { registerPropertyOverrideMod } from 'src/util/moddingTools' + +registerPropertyOverrideMod({ + id: `animated-java:event-hook/external-plugin-load/load`, + object: BBPlugin.prototype, + key: 'load', + + get: original => { + return function (this: BBPlugin) { + const result = original.call(this) + console.log('Loaded plugin:', this.id) + EVENTS.EXTERNAL_PLUGIN_LOAD.publish(this) + return result + } + }, +}) + +registerPropertyOverrideMod({ + id: `animated-java:event-hook/external-plugin-load/toggle-disabled`, + object: BBPlugin.prototype, + key: 'toggleDisabled', + + get: original => { + return function (this: BBPlugin) { + const result = original.call(this) + if (this.disabled) return result + console.log('Enabled plugin:', this.id) + EVENTS.EXTERNAL_PLUGIN_LOAD.publish(this) + return result + } + }, +}) + +registerPropertyOverrideMod({ + id: `animated-java:event-hook/external-plugin-unload/unload`, + object: BBPlugin.prototype, + key: 'unload', + + get: original => { + return function (this: BBPlugin) { + const result = original.call(this) + console.log('Unloaded plugin:', this.id) + EVENTS.EXTERNAL_PLUGIN_UNLOAD.publish(this) + return result + } + }, +}) diff --git a/src/mods/exportOverActionMod.ts b/src/mods/exportOverActionMod.ts deleted file mode 100644 index fe37b5fe..00000000 --- a/src/mods/exportOverActionMod.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { BLUEPRINT_CODEC, BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:exportOverAction`, - { - action: BarItems.export_over as Action, - originalClick: (BarItems.export_over as Action).click, - }, - context => { - context.action.click = (event: Event) => { - if (!Project || !Format) return - if (Format.id === BLUEPRINT_FORMAT.id) { - const path = Project.save_path || Project.export_path - if (path) { - if (fs.existsSync(PathModule.dirname(path))) { - Project.save_path = path - BLUEPRINT_CODEC.write(BLUEPRINT_CODEC.compile(), path) - } else { - console.error( - `Failed to export Animated Java Blueprint, file location '${path}' does not exist!` - ) - BLUEPRINT_CODEC.export() - } - } else { - BLUEPRINT_CODEC.export() - } - } else { - context.originalClick.call(context.action, event) - } - } - return context - }, - context => { - context.action.click = context.originalClick - } -) diff --git a/src/mods/formatIconMod.ts b/src/mods/formatIconMod.ts deleted file mode 100644 index 4a855724..00000000 --- a/src/mods/formatIconMod.ts +++ /dev/null @@ -1,51 +0,0 @@ -import Icon from '../components/icon.svelte' -import { PACKAGE } from '../constants' -import { injectSvelteCompomponent } from '../util/injectSvelteComponent' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:formatIconMod`, - undefined, - () => { - void injectSvelteCompomponent({ - elementSelector: () => document.querySelector('[format=animated_java_blueprint]'), - component: Icon, - props: {}, - prepend: true, - postMount: () => { - document - .querySelector('[format=animated_java_blueprint] span i') - ?.parentElement?.remove() - const duplicates = [...document.querySelectorAll('#animated_java\\:icon')] - if (duplicates.length > 1) { - duplicates.slice(1).forEach(d => d.remove()) - } - }, - }) - }, - () => { - document.querySelector('#animated_java\\:icon')?.remove() - } -) - -createBlockbenchMod( - `${PACKAGE.name}:prioritizeAnimatedJavaFormats`, - undefined, - () => { - const interval = setInterval(() => { - const ajFormats = $("li.format_category > label:contains('Animated Java')") - .first() - .parent() - if (ajFormats.length === 0) return - - const mcFormats = $("li.format_category > label:contains('General')").first().parent() - - ajFormats.insertBefore(mcFormats) - - clearInterval(interval) - }, 16) - }, - () => { - // Pass - } -) diff --git a/src/mods/globalCssMod.css b/src/mods/globalCssMod.css new file mode 100644 index 00000000..42009841 --- /dev/null +++ b/src/mods/globalCssMod.css @@ -0,0 +1,17 @@ +dialog.dialog p code { + padding: 2px 6px; + border: none; + font-size: smaller; +} + +dialog.dialog pre:has(code) { + background-color: var(--color-back); + padding: 4px 7px; + font-size: smaller; + user-select: text; + white-space: pre-wrap; +} + +#collection_properties_vue > ul > li { + max-width: unset !important; +} diff --git a/src/mods/globalCssMod.ts b/src/mods/globalCssMod.ts new file mode 100644 index 00000000..2c4db740 --- /dev/null +++ b/src/mods/globalCssMod.ts @@ -0,0 +1,14 @@ +import { registerMod } from 'src/util/moddingTools' +import CSS from './globalCssMod.css' + +registerMod({ + id: 'animated-java:global-css', + + apply: () => { + return Blockbench.addCSS(CSS) + }, + + revert: deletable => { + deletable.delete() + }, +}) diff --git a/src/mods/groupContextMenuMod.ts b/src/mods/groupContextMenuMod.ts deleted file mode 100644 index 13024ca7..00000000 --- a/src/mods/groupContextMenuMod.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PACKAGE } from '../constants' -import { BONE_CONFIG_ACTION } from '../interface/dialog/boneConfig' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:groupContextMenu`, - { - menuStructure: Group.prototype.menu!.structure, - }, - context => { - const structure = [...context.menuStructure] - structure.splice(6, 0, BONE_CONFIG_ACTION) - Group.prototype.menu!.structure = structure - - return context - }, - context => { - Group.prototype.menu!.structure = context.menuStructure - } -) diff --git a/src/mods/groupMod.ts b/src/mods/groupMod.ts new file mode 100644 index 00000000..12631540 --- /dev/null +++ b/src/mods/groupMod.ts @@ -0,0 +1,84 @@ +import { sanitizeOutlinerElementName } from 'src/outliner/util' +import { registerConditionalPropertyOverrideMod, registerMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat, type IBlueprintBoneConfigJSON } from '../formats/blueprint' + +declare global { + interface Group { + configs: { + default: IBlueprintBoneConfigJSON + /** + * @key Variant UUID + * @value Variant Bone Config + */ + variants: Record + } + } +} + +class DeepClonedObjectProperty extends Property<'object'> { + constructor(targetClass: any, name: string, options?: PropertyOptions) { + super(targetClass, 'object', name, options) + } + + merge(instance: any, data: any) { + if (typeof data[this.name] === 'object') { + instance[this.name] = JSON.parse(JSON.stringify(data[this.name])) + } + } + + copy(instance: any, target: any) { + if (typeof instance[this.name] === 'object') { + target[this.name] = JSON.parse(JSON.stringify(instance[this.name])) + } + } +} + +// region Properties +registerMod({ + id: `animated-java:bone-properties`, + + apply: () => { + const configs = new DeepClonedObjectProperty(Group, 'configs', { + condition: () => activeProjectIsBlueprintFormat(), + default: { default: undefined, variants: {} }, + }) + + return { configs } + }, + + revert: ({ configs }) => { + configs.delete() + }, +}) + +//region Save Name +registerConditionalPropertyOverrideMod({ + id: `animated-java:override-function/group/save-name`, + object: Group.prototype, + key: 'saveName', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: Group, save?: boolean) { + this.name = sanitizeOutlinerElementName(this.name, this.uuid) + return original.call(this, save) + } + }, +}) + +//region Sanitize Name +registerConditionalPropertyOverrideMod({ + id: `animated-java:override-function/group/sanitize-name`, + object: Group.prototype, + key: 'sanitizeName', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: Group) { + this.name = sanitizeOutlinerElementName(this.name, this.uuid) + return original.call(this) + } + }, +}) diff --git a/src/mods/groupNameMod.ts b/src/mods/groupNameMod.ts deleted file mode 100644 index 6db69049..00000000 --- a/src/mods/groupNameMod.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { sanitizeOutlinerElementName } from '../outliner/util' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:groupNameMod`, - { - originalRename: Group.prototype.saveName, - originalSanitize: Group.prototype.sanitizeName, - }, - context => { - Group.prototype.saveName = function (this: Group, save?: boolean) { - if (isCurrentFormat()) { - this.name = sanitizeOutlinerElementName(this.name, this.uuid) - } - return context.originalRename.call(this, save) - } - Group.prototype.sanitizeName = function (this: Group) { - if (isCurrentFormat()) { - this.name = sanitizeOutlinerElementName(this.name, this.uuid) - } - return context.originalSanitize.call(this) - } - return context - }, - context => { - Group.prototype.rename = context.originalRename - } -) diff --git a/src/mods/index.ts b/src/mods/index.ts deleted file mode 100644 index 7776807d..00000000 --- a/src/mods/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import './assetSetupMod' -import './addLocatorActionMod' -import './animationControllerMod' -import './animationPropertiesAction' -import './animationPropertiesMod' -import './blockbenchReadMod' -import './boneInterpolationMod' -import './bonePropertiesMod' -import './cubeOutlineMod' -import './customKeyframeEasingsMod' -import './customKeyframesMod' -import './exportOverActionMod' -import './formatIconMod' -import './groupContextMenuMod' -import './groupNameMod' -import './keyframeMod' -import './locatorAnimatorMod' -import './locatorContextMenuMod' -import './locatorPropertiesMod' -import './modelFormatConvertToMod' -import './modelFormatMod' -import './molangMod' -import './panelMod' -import './projectSettingsActionOverride' -import './saveAllAnimationsActionMod' -import './saveProjectActionMod' -import './saveProjectAsActionMod' -import './showDefaultPoseMod' -import './variantPreviewCubeFaceMod' -import './cameraNameMod' -import './pluginsDialogMod' diff --git a/src/mods/customKeyframeEasingsMod.ts b/src/mods/keyframeEasing.ts similarity index 66% rename from src/mods/customKeyframeEasingsMod.ts rename to src/mods/keyframeEasing.ts index 64180946..21467c8c 100644 --- a/src/mods/customKeyframeEasingsMod.ts +++ b/src/mods/keyframeEasing.ts @@ -1,15 +1,21 @@ -import { ContextProperty, createBlockbenchMod } from '../util/moddingTools' -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' - +import { registerConditionalPropertyOverrideMod, registerMod } from 'src/util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' import { EASING_DEFAULT, - EasingKey, + type EasingKey, easingFunctions, getEasingArgDefault, hasArgs, } from '../util/easing' +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface _Keyframe { + easing?: EasingKey + easingArgs?: number[] + } +} + interface IEasingProperties { easing?: EasingKey easingArgs?: any[] @@ -19,22 +25,22 @@ function lerp(start: number, stop: number, amt: number): number { return amt * (stop - start) + start } -createBlockbenchMod( - `${PACKAGE.name}:keyframeEasingMod`, - { - originalGetLerp: Blockbench.Keyframe.prototype.getLerp, - easingProperty: undefined as ContextProperty<'string'>, - easingArgsProperty: undefined as ContextProperty<'array'>, - }, - context => { - context.easingProperty = new Property(Blockbench.Keyframe, 'string', 'easing', { - default: EASING_DEFAULT, - condition: isCurrentFormat(), - }) - context.easingArgsProperty = new Property(Blockbench.Keyframe, 'array', 'easingArgs', { - condition: isCurrentFormat(), - }) +registerMod({ + id: `animated-java:keyframe-easing`, + + apply: () => { + const properties = [ + new Property(Blockbench.Keyframe, 'string', 'easing', { + default: EASING_DEFAULT, + condition: () => activeProjectIsBlueprintFormat(), + }), + new Property(Blockbench.Keyframe, 'array', 'easingArgs', { + condition: () => activeProjectIsBlueprintFormat(), + }), + ] + + const originalGetLerp = Blockbench.Keyframe.prototype.getLerp Blockbench.Keyframe.prototype.getLerp = function ( this: _Keyframe, other, @@ -42,10 +48,10 @@ createBlockbenchMod( amount, allowExpression ): number { - const easing = other.easing || 'linear' + const easing = other.easing ?? 'linear' - if (!isCurrentFormat() || easing === 'linear') - return context.originalGetLerp.call(this, other, axis, amount, allowExpression) + if (!activeProjectIsBlueprintFormat() || easing === 'linear') + return originalGetLerp.call(this, other, axis, amount, allowExpression) let easingFunc = easingFunctions[easing] if (hasArgs(easing)) { @@ -54,7 +60,7 @@ createBlockbenchMod( ? other.easingArgs[0] : getEasingArgDefault(other) - easingFunc = easingFunc.bind(null, arg1 || 0) + easingFunc = easingFunc.bind(null, arg1 ?? 0) } const easedAmount = easingFunc(amount) const start = this.calc(axis) @@ -67,14 +73,16 @@ createBlockbenchMod( return result } - return context + return { properties, originalGetLerp } }, - context => { - context.easingProperty?.delete() - context.easingArgsProperty?.delete() - Blockbench.Keyframe.prototype.getLerp = context.originalGetLerp - } -) + revert: ({ properties, originalGetLerp }) => { + for (const prop of properties) { + prop.delete() + } + + Blockbench.Keyframe.prototype.getLerp = originalGetLerp + }, +}) export function reverseEasing(easing?: EasingKey): EasingKey | undefined { if (!easing) return easing @@ -84,15 +92,16 @@ export function reverseEasing(easing?: EasingKey): EasingKey | undefined { return easing } -createBlockbenchMod( - `${PACKAGE.name}:reverseKeyframesMod`, - { - action: BarItems.reverse_keyframes as Action, - originalClick: (BarItems.reverse_keyframes as Action).click, - }, - context => { - context.action.click = function (event?: Event) { - context.originalClick.call(this, event) +registerConditionalPropertyOverrideMod({ + id: `animated-java:action-click-override/reverse-keyframes`, + object: BarItems.reverse_keyframes as Action, + key: 'click', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return (event?: Event) => { + original(event) // There's not really an easy way to merge our undo operation with the original one so we'll make a new one instead Undo.initEdit({ keyframes: Timeline.selected || undefined }) @@ -139,9 +148,5 @@ createBlockbenchMod( updateKeyframeSelection() Animator.preview() } - return context }, - context => { - context.action.click = context.originalClick - } -) +}) diff --git a/src/mods/keyframeMod.ts b/src/mods/keyframeMod.ts deleted file mode 100644 index ae586d6f..00000000 --- a/src/mods/keyframeMod.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { events } from '../util/events' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:keyframeSelectEventMod`, - { - originalKeyframeSelect: Blockbench.Keyframe.prototype.select, - originalUpdateKeyframeSelection: updateKeyframeSelection, - barItem: BarItems.keyframe_interpolation as BarSelect, - originalChange: (BarItems.keyframe_interpolation as BarSelect).set, - }, - context => { - Blockbench.Keyframe.prototype.select = function (this: _Keyframe, event: any) { - if (!isCurrentFormat()) return context.originalKeyframeSelect.call(this, event) - const kf = context.originalKeyframeSelect.call(this, event) - events.SELECT_KEYFRAME.dispatch(kf) - return kf - } - - globalThis.updateKeyframeSelection = function () { - if (isCurrentFormat()) return context.originalUpdateKeyframeSelection() - - Timeline.keyframes.forEach(kf => { - if (kf.selected && Timeline.selected && !Timeline.selected.includes(kf)) { - kf.selected = false - events.UNSELECT_KEYFRAME.dispatch() - } - let hasExpressions = false - if (kf.transform) { - hasExpressions = !!kf.data_points.find(point => { - return ( - !isStringNumber(point.x) || - !isStringNumber(point.y) || - !isStringNumber(point.z) - ) - }) - } - if (hasExpressions != kf.has_expressions) { - kf.has_expressions = hasExpressions - } - }) - - if (Timeline.selected) { - console.log('Selected keyframe:', Timeline.selected[0]) - events.SELECT_KEYFRAME.dispatch(Timeline.selected[0]) - } - - return context.originalUpdateKeyframeSelection() - } - - context.barItem.set = function (this: BarSelect, value) { - const result = context.originalChange.call(this, value) - - if (isCurrentFormat()) { - if (Timeline.selected && Timeline.selected.length > 0) { - events.SELECT_KEYFRAME.dispatch(Timeline.selected[0]) - } else { - events.UNSELECT_KEYFRAME.dispatch() - } - } - - return result - } - - return context - }, - context => { - Blockbench.Keyframe.prototype.select = context.originalKeyframeSelect - globalThis.updateKeyframeSelection = context.originalUpdateKeyframeSelection - context.barItem.change = context.originalChange - } -) diff --git a/src/mods/locatorAnimatorMod.ts b/src/mods/locatorAnimatorMod.ts index 94891074..8ad6e8a6 100644 --- a/src/mods/locatorAnimatorMod.ts +++ b/src/mods/locatorAnimatorMod.ts @@ -1,22 +1,15 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { events } from '../util/events' +import { activeProjectIsBlueprintFormat, BLUEPRINT_FORMAT_ID } from 'src/formats/blueprint' +import EVENTS from 'src/util/events' +import { registerConditionalPropertyOverrideMod, registerProjectMod } from 'src/util/moddingTools' import { translate } from '../util/translation' -const DEFAULT_SHOW_MOTION_TRAIL = Animator.showMotionTrail -const DEFAULT_PREVIEW = Animator.preview -const DEFAULT_UPDATE_SELECTION = globalThis.updateSelection -const DEFAULT_SELECT = Locator.prototype.select - export class LocatorAnimator extends BoneAnimator { - private _name: string - - public uuid: string - public element: Locator | undefined + uuid: string + element: Locator | undefined constructor(uuid: string, animation: _Animation, name: string) { super(uuid, animation, name) this.uuid = uuid - this._name = name } getElement() { @@ -56,7 +49,7 @@ export class LocatorAnimator extends BoneAnimator { } } - if (this.element && this.element.parent && this.element.parent !== 'root') { + if (this.element?.parent && this.element.parent !== 'root') { this.element.parent.openUp() } @@ -65,7 +58,7 @@ export class LocatorAnimator extends BoneAnimator { doRender() { this.getElement() - return !!(this.element && this.element.mesh) + return !!this.element?.mesh } displayPosition(arr?: ArrayVector3, multiplier = 1) { @@ -93,74 +86,98 @@ export class LocatorAnimator extends BoneAnimator { } LocatorAnimator.prototype.type = 'locator' LocatorAnimator.prototype.channels = { - commands: { - name: translate('effect_animator.timeline.commands'), + function: { + name: translate('effect_animator.timeline.function'), mutable: true, transform: true, max_data_points: 1, }, } -let installed = false +registerConditionalPropertyOverrideMod({ + id: 'animated-java:property-override/locator/animator', + object: Locator, + key: 'animator', -function inject() { - if (installed) return + condition: () => activeProjectIsBlueprintFormat(), - Locator.animator = LocatorAnimator as any + get: () => { + return LocatorAnimator as unknown as BoneAnimator + }, +}) - Animator.showMotionTrail = function (target?: Group) { - if (!target || target instanceof Locator) return - DEFAULT_SHOW_MOTION_TRAIL(target) - } - Animator.preview = function (inLoop?: boolean) { - DEFAULT_PREVIEW(inLoop) - if ( - Mode.selected.id === Modes.options.animate.id && - Outliner.selected[0] instanceof Locator - ) { - // @ts-ignore - Canvas.gizmos[0].visible = false - Transformer.visible = false - } - } - globalThis.updateSelection = function () { - DEFAULT_UPDATE_SELECTION() - if ( - Mode.selected.id === Modes.options.animate.id && - Outliner.selected[0] instanceof Locator - ) { - // @ts-ignore - Canvas.gizmos[0].visible = false - Transformer.visible = false +registerConditionalPropertyOverrideMod({ + id: 'animated-java:function-override/locator/select', + object: Locator.prototype, + key: 'select', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (this: Locator, event?: any, isOutlinerClick?: boolean) { + const result = original.call(this, event, isOutlinerClick) + if (Animator.open && Blockbench.Animation.selected) { + Blockbench.Animation.selected.getBoneAnimator().select() + } + return result } - } - Locator.prototype.select = function (this: Locator, event?: any, isOutlinerClick?: boolean) { - const result = DEFAULT_SELECT.call(this, event, isOutlinerClick) - if (Animator.open && Blockbench.Animation.selected) { - Blockbench.Animation.selected.getBoneAnimator().select() + }, +}) + +registerConditionalPropertyOverrideMod({ + id: 'animated-java:function-override/animator/show-motion-trail', + object: Animator, + key: 'showMotionTrail', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (target?: Group) { + if (!target || target instanceof Locator) return + return original(target) } - return result - } + }, +}) - installed = true -} +registerConditionalPropertyOverrideMod({ + id: 'animated-java:function-override/animator/preview', + object: Animator, + key: 'preview', + + condition: () => activeProjectIsBlueprintFormat(), + + get: original => { + return function (inLoop?: boolean) { + const result = original(inLoop) + if ( + Mode.selected.id === Modes.options.animate.id && + Outliner.selected[0] instanceof Locator + ) { + Canvas.gizmos[0].visible = false + Transformer.visible = false + } + return result + } + }, +}) -function extract() { - if (!installed) return - Locator.animator = undefined +registerProjectMod({ + id: 'animated-java:project-mod/locator-animator/hide-gizmos', - Animator.showMotionTrail = DEFAULT_SHOW_MOTION_TRAIL - Animator.preview = DEFAULT_PREVIEW - globalThis.updateSelection = DEFAULT_UPDATE_SELECTION - Locator.prototype.select = DEFAULT_SELECT + condition: project => project.format.id === BLUEPRINT_FORMAT_ID, - installed = false -} + apply: () => { + const unsub = EVENTS.UPDATE_SELECTION.subscribe(() => { + if (Mode.selected.id !== Modes.options.animate.id) return + if (selected.at(0) instanceof Locator) { + Canvas.gizmos[0].visible = false + Transformer.visible = false + } + }) + return { unsub } + }, -events.PRE_SELECT_PROJECT.subscribe(project => { - if (project.format.id === BLUEPRINT_FORMAT.id) { - inject() - } else { - extract() - } + revert: ({ unsub }) => { + unsub() + }, }) diff --git a/src/mods/locatorContextMenuMod.ts b/src/mods/locatorContextMenuMod.ts deleted file mode 100644 index c771e76d..00000000 --- a/src/mods/locatorContextMenuMod.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PACKAGE } from '../constants' -import { LOCATOR_CONFIG_ACTION } from '../interface/dialog/locatorConfig' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:groupContextMenu`, - { - menuStructure: Locator.prototype.menu!.structure, - }, - context => { - const structure = [...context.menuStructure] - structure.splice(6, 0, LOCATOR_CONFIG_ACTION) - Locator.prototype.menu!.structure = structure - - return context - }, - context => { - Locator.prototype.menu!.structure = context.menuStructure - } -) diff --git a/src/mods/locatorPropertiesMod.ts b/src/mods/locatorPropertiesMod.ts index 21806c88..a579bddc 100644 --- a/src/mods/locatorPropertiesMod.ts +++ b/src/mods/locatorPropertiesMod.ts @@ -1,20 +1,28 @@ -import { isCurrentFormat as condition } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { type ContextProperty, createBlockbenchMod } from '../util/moddingTools' +import { + activeProjectIsBlueprintFormat as condition, + type IBlueprintLocatorConfigJSON, +} from '../formats/blueprint' +import { registerMod } from '../util/moddingTools' -createBlockbenchMod( - `${PACKAGE.name}:locatorProperties`, - { - config: undefined as ContextProperty<'instance'>, - }, - context => { - context.config = new Property(Locator, 'instance', 'config', { +declare global { + interface Locator { + config: IBlueprintLocatorConfigJSON + } +} + +registerMod({ + id: `animated-java:locator-properties`, + + apply: () => { + const config = new Property(Locator, 'instance', 'config', { condition, default: undefined, }) - return context + + return { config } }, - context => { - context.config?.delete() - } -) + + revert: ({ config }) => { + config?.delete() + }, +}) diff --git a/src/mods/modelFormatConvertToMod.ts b/src/mods/modelFormatConvertToMod.ts index 6b27cc5b..bdedac22 100644 --- a/src/mods/modelFormatConvertToMod.ts +++ b/src/mods/modelFormatConvertToMod.ts @@ -1,21 +1,22 @@ -import { BLUEPRINT_FORMAT, convertToBlueprint } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' +import { BLUEPRINT_FORMAT_ID, convertToBlueprint } from '../formats/blueprint' +import { registerMod } from '../util/moddingTools' + +registerMod({ + id: `animated-java:model-format-convert-to-mod`, + + apply: () => { + const original = ModelFormat.prototype.convertTo -createBlockbenchMod( - `${PACKAGE.name}:modelFormatConvertToMod`, - { - original: ModelFormat.prototype.convertTo, - }, - context => { ModelFormat.prototype.convertTo = function (this: ModelFormat) { - const result = context.original.call(this) - if (this === BLUEPRINT_FORMAT) convertToBlueprint() + const result = original.call(this) + if (this.id === BLUEPRINT_FORMAT_ID) convertToBlueprint() return result } - return context + + return { original } + }, + + revert: ({ original }) => { + ModelFormat.prototype.convertTo = original }, - context => { - ModelFormat.prototype.convertTo = context.original - } -) +}) diff --git a/src/mods/modelFormatMod.ts b/src/mods/modelFormatMod.ts deleted file mode 100644 index 9dd42ec4..00000000 --- a/src/mods/modelFormatMod.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { events } from '../util/events' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:modelFormatPreSelectProjectEvent`, - { - originalSelect: ModelProject.prototype.select, - }, - context => { - ModelProject.prototype.select = function (this: ModelProject) { - if (this.format.id === BLUEPRINT_FORMAT.id) { - events.PRE_SELECT_PROJECT.dispatch(this) - } - return context.originalSelect.call(this) - } - return context - }, - context => { - ModelProject.prototype.select = context.originalSelect - } -) diff --git a/src/mods/modelFormatSelectMod.ts b/src/mods/modelFormatSelectMod.ts new file mode 100644 index 00000000..0c27dd33 --- /dev/null +++ b/src/mods/modelFormatSelectMod.ts @@ -0,0 +1,21 @@ +import EVENTS from '../util/events' +import { registerMod } from '../util/moddingTools' + +registerMod({ + id: `animated-java:model-format-pre-select-project-event`, + + apply: () => { + const original = ModelProject.prototype.select + + ModelProject.prototype.select = function (this: ModelProject) { + EVENTS.PRE_SELECT_PROJECT.publish(this) + return original.call(this) + } + + return { original } + }, + + revert: ({ original }) => { + ModelProject.prototype.select = original + }, +}) diff --git a/src/mods/molangMod.ts b/src/mods/molangMod.ts index 81c227f8..1336c97e 100644 --- a/src/mods/molangMod.ts +++ b/src/mods/molangMod.ts @@ -1,6 +1,5 @@ -import { PACKAGE } from '../constants' -import { events } from '../util/events' -import { createBlockbenchMod } from '../util/moddingTools' +import { BLUEPRINT_FORMAT_ID } from 'src/formats/blueprint' +import { registerProjectMod } from 'src/util/moddingTools' import MolangFunctionFile from './functions.molang' const GLOBAL_VARIABLES = Animator.MolangParser.global_variables @@ -337,7 +336,7 @@ const CUSTOM_FUNCTIONS: Record number> = {} const CUSTOM_FUNCTION_LABELS: Record = {} for (const [call, body] of Object.entries(MolangFunctionFile)) { - const match = call.match(/^(.+?)\((.*?)\)$/) + const match = /^(.+?)\((.*?)\)$/.exec(call) if (!match) continue const name = match[1] const argList = match[2].split(',').map(v => v.trim()) @@ -378,105 +377,90 @@ function filterAndSortList( }) if (blacklist) blacklist.forEach(black => result.remove(black)) return result.map(text => { - return { text, label: labels && labels[text], overlap: match.length } + return { text, label: labels?.[text], overlap: match.length } }) } -createBlockbenchMod( - `${PACKAGE.name}:molangMod`, - { - originalAutocompleteMolang: Animator.autocompleteMolang, - unsubscribeSelectAjProject: undefined as (() => void) | undefined, - unsuscribeUnselectAjProject: undefined as (() => void) | undefined, - }, - context => { - context.unsubscribeSelectAjProject = events.SELECT_AJ_PROJECT.subscribe(() => { - Object.assign(GLOBAL_VARIABLES, CUSTOM_FUNCTIONS) +registerProjectMod({ + id: `animated-java:molang-mod`, - Animator.autocompleteMolang = function (text, position, type) { - let beginning = text - .substring(0, position) - .split(/[^a-zA-Z_.]\.*/g) - .last() - if (!beginning) return [] + condition: project => project.format.id === BLUEPRINT_FORMAT_ID, - beginning = beginning.toLowerCase() - if (beginning.includes('.')) { - const [namespace, dir] = beginning.split('.') - if (namespace == 'math') { - return filterAndSortList( - MATH_FUNCTIONS, - dir, - undefined, - MATH_FUNCTION_LABELS - ) - } - if (namespace == 'query' || namespace == 'q') { - return filterAndSortList( - MOLANG_QUERIES, - dir, - type !== 'controller' && [ - 'all_animations_finished', - 'any_animation_finished', - ], - MOLANG_QUERY_LABELS - ) - } - if (namespace == 'temp' || namespace == 't') { - const temps = text.match(/([^a-z]|^)t(emp)?\.\w+/gi) - if (temps) { - const temps2 = temps.map(t => t.split('.')[1]) - const temps3 = temps2.filter( - (t, i) => t !== dir && temps2.indexOf(t) === i - ) - return filterAndSortList(temps3, dir) - } - } - if (namespace == 'context' || namespace == 'c') { - return filterAndSortList([...DEFAULT_CONTEXT], dir) - } - if (namespace == 'variable' || namespace == 'v') { - const options = [...getProjectVariables(dir)] - options.safePush(...DEFAULT_VARIABLES) - return filterAndSortList(options, dir) - } - } else { - const root_tokens = ROOT_TOKENS.slice() - let labels = {} - if (type === 'placeholders') { - labels = { - 'toggle()': 'toggle( name )', - 'slider()': 'slider( name, step?, min?, max? )', - 'impulse()': 'impulse( name, duration )', - } - root_tokens.push(...Object.keys(labels)) - } + apply: () => { + Object.assign(GLOBAL_VARIABLES, CUSTOM_FUNCTIONS) + + const originalAutocompleteMolang = Animator.autocompleteMolang + Animator.autocompleteMolang = function (text, position, type) { + let beginning = text + .substring(0, position) + .split(/[^a-zA-Z_.]\.*/g) + .last() + if (!beginning) return [] + + beginning = beginning.toLowerCase() + if (beginning.includes('.')) { + const [namespace, dir] = beginning.split('.') + if (namespace == 'math') { + return filterAndSortList(MATH_FUNCTIONS, dir, undefined, MATH_FUNCTION_LABELS) + } + if (namespace == 'query' || namespace == 'q') { return filterAndSortList( - [...root_tokens, ...Object.keys(CUSTOM_FUNCTION_LABELS)], - beginning, - undefined, - { ...labels, ...CUSTOM_FUNCTION_LABELS } + MOLANG_QUERIES, + dir, + type !== 'controller' && [ + 'all_animations_finished', + 'any_animation_finished', + ], + MOLANG_QUERY_LABELS ) } - return [] - } - }) - - context.unsuscribeUnselectAjProject = events.UNSELECT_AJ_PROJECT.subscribe(() => { - for (const key of Object.keys(CUSTOM_FUNCTIONS)) { - delete GLOBAL_VARIABLES[key] + if (namespace == 'temp' || namespace == 't') { + const temps = text.match(/([^a-z]|^)t(emp)?\.\w+/gi) + if (temps) { + const temps2 = temps.map(t => t.split('.')[1]) + const temps3 = temps2.filter((t, i) => t !== dir && temps2.indexOf(t) === i) + return filterAndSortList(temps3, dir) + } + } + if (namespace == 'context' || namespace == 'c') { + return filterAndSortList([...DEFAULT_CONTEXT], dir) + } + if (namespace == 'variable' || namespace == 'v') { + const options = [...getProjectVariables(dir)] + options.safePush(...DEFAULT_VARIABLES) + return filterAndSortList(options, dir) + } + } else { + const rootTokens = ROOT_TOKENS.slice() + let labels = {} + if (type === 'placeholders') { + labels = { + 'toggle()': 'toggle( name )', + 'slider()': 'slider( name, step?, min?, max? )', + 'impulse()': 'impulse( name, duration )', + } + rootTokens.push(...Object.keys(labels)) + } + return filterAndSortList( + [...rootTokens, ...Object.keys(CUSTOM_FUNCTION_LABELS)], + beginning, + undefined, + { ...labels, ...CUSTOM_FUNCTION_LABELS } + ) } - Animator.autocompleteMolang = context.originalAutocompleteMolang - }) + return [] + } - return context + return { + originalAutocompleteMolang, + } }, - context => { + + revert: ({ originalAutocompleteMolang }) => { for (const key of Object.keys(CUSTOM_FUNCTIONS)) { delete GLOBAL_VARIABLES[key] } - Animator.autocompleteMolang = context.originalAutocompleteMolang - context.unsubscribeSelectAjProject?.() - context.unsuscribeUnselectAjProject?.() - } -) + + Animator.autocompleteMolang = originalAutocompleteMolang + }, +}) diff --git a/src/mods/outlinerToolbar.ts b/src/mods/outlinerToolbar.ts new file mode 100644 index 00000000..a023c793 --- /dev/null +++ b/src/mods/outlinerToolbar.ts @@ -0,0 +1,16 @@ +import { registerMod } from 'src/util/moddingTools' + +//region Add Locator +registerMod({ + id: `animated-java:append-toolbar/outliner/add-locator-action`, + + apply: () => { + const action = BarItems.add_locator as Action + Toolbars.outliner.add(action, 0) + return { action } + }, + + revert: ({ action }) => { + Toolbars.outliner.remove(action) + }, +}) diff --git a/src/mods/panelMod.ts b/src/mods/panelMod.ts index 65f54406..65dd915e 100644 --- a/src/mods/panelMod.ts +++ b/src/mods/panelMod.ts @@ -1,17 +1,15 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { registerMod } from '../util/moddingTools' -createBlockbenchMod( - `${PACKAGE.name}:panelMod`, - { - panel: Interface.Panels.animations, - }, - context => { - const originalFilesFunction = context.panel.inside_vue.$options.computed!.files as () => any +registerMod({ + id: `animated-java:panel-mod`, + + apply: () => { + const panel = Interface.Panels.animations - context.panel.inside_vue.$options.computed!.files = function (this) { - if (Format.id === BLUEPRINT_FORMAT.id) { + const originalFilesFunction = panel.inside_vue.$options.computed!.files as () => any + panel.inside_vue.$options.computed!.files = function (this) { + if (activeProjectIsBlueprintFormat()) { return { '': { animations: [ @@ -26,9 +24,10 @@ createBlockbenchMod( return originalFilesFunction.call(this) } - return { ...context, originalFilesFunction } + return { panel, originalFilesFunction } + }, + + revert: ({ panel, originalFilesFunction }) => { + panel.inside_vue.$options.computed!.files = originalFilesFunction }, - context => { - context.panel.inside_vue.$options.computed!.files = context.originalFilesFunction - } -) +}) diff --git a/src/mods/pluginsDialog.ts b/src/mods/pluginsDialog.ts new file mode 100644 index 00000000..bc8faa4e --- /dev/null +++ b/src/mods/pluginsDialog.ts @@ -0,0 +1,51 @@ +import { registerMod } from 'src/util/moddingTools' +import { mountSvelteComponent } from 'src/util/mountSvelteComponent' +import IncompatiblePluginNotice from '../components/incompatiblePluginNotice.svelte' +import { Valuable } from '../util/stores' + +const SELECTED_PLUGIN = new Valuable(null) + +registerMod({ + id: `animated-java:plugins-dialog-mod`, + + apply: () => { + // @ts-expect-error Missing types + const original = Plugins.dialog.component.methods.selectPlugin + + // @ts-expect-error Missing types + Plugins.dialog.component.methods.selectPlugin = function (this, plugin: BBPlugin) { + const result = original.call(this, plugin) + SELECTED_PLUGIN.set(plugin) + return result + } + + return { original } + }, + + revert: ({ original }) => { + // @ts-expect-error Missing types + Plugins.dialog.component.methods.selectPlugin = original + }, +}) + +let mounted: IncompatiblePluginNotice | null = null + +SELECTED_PLUGIN.subscribe(plugin => { + if (mounted) { + mounted.$destroy() + mounted = null + } + if (!plugin) return + + requestAnimationFrame(() => { + mounted = mountSvelteComponent({ + component: IncompatiblePluginNotice, + props: { selectedPlugin: plugin }, + target: '.plugin_browser_page_header', + prepend: true, + onDestroy: () => { + mounted = null + }, + }) + }) +}) diff --git a/src/mods/pluginsDialogMod.ts b/src/mods/pluginsDialogMod.ts deleted file mode 100644 index 36d841c9..00000000 --- a/src/mods/pluginsDialogMod.ts +++ /dev/null @@ -1,40 +0,0 @@ -import IncompatiblePluginNotice from '../components/incompatiblePluginNotice.svelte' -import { PACKAGE } from '../constants' -import { injectSvelteCompomponentMod } from '../util/injectSvelteComponent' -import { createBlockbenchMod } from '../util/moddingTools' -import { Valuable } from '../util/stores' - -const SELECTED_PLUGIN = new Valuable(null) - -injectSvelteCompomponentMod({ - component: IncompatiblePluginNotice, - props: { - selectedPlugin: SELECTED_PLUGIN, - }, - elementSelector() { - return document.querySelector('.plugin_browser_page_header') - }, -}) - -createBlockbenchMod( - `${PACKAGE.name}:pluginsDialogMod`, - { - // @ts-expect-error - originalSelect: Plugins.dialog.component.methods.selectPlugin, - }, - context => { - // @ts-expect-error - Plugins.dialog.component.methods.selectPlugin = function (this, plugin: BBPlugin) { - const result = context.originalSelect.call(this, plugin) - SELECTED_PLUGIN.set(plugin) - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return result - } - - return context - }, - context => { - // @ts-expect-error - Plugins.dialog.component.methods.selectPlugin = context.originalSelect - } -) diff --git a/src/mods/projectSettingsActionOverride.ts b/src/mods/projectSettingsActionOverride.ts deleted file mode 100644 index 2cdd50d8..00000000 --- a/src/mods/projectSettingsActionOverride.ts +++ /dev/null @@ -1,25 +0,0 @@ -import PACKAGE from '../../package.json' -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { openBlueprintSettingsDialog } from '../interface/dialog/blueprintSettings' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:projectSettingsActionOverride`, - { - action: BarItems.project_window as Action, - oldClick: (BarItems.project_window as Action).click, - }, - context => { - context.action.click = function (this, event: Event) { - if (Format.id === BLUEPRINT_FORMAT.id) { - openBlueprintSettingsDialog() - } else { - context.oldClick.call(this, event) - } - } - return context - }, - context => { - context.action.click = context.oldClick - } -) diff --git a/src/mods/saveAllAnimationsActionMod.ts b/src/mods/saveAllAnimationsActionMod.ts deleted file mode 100644 index eb0b2c44..00000000 --- a/src/mods/saveAllAnimationsActionMod.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:saveAllAnimationsActionMod`, - { - action: BarItems.save_all_animations as Action, - }, - context => { - const originalCondition = context.action.condition! - context.action.condition = function (this: Action) { - if (Format.id === BLUEPRINT_FORMAT.id) { - return false - } - return originalCondition.call(this) - } - return { ...context, originalCondition } - }, - context => { - context.action.condition = context.originalCondition - } -) diff --git a/src/mods/saveProjectActionMod.ts b/src/mods/saveProjectActionMod.ts deleted file mode 100644 index 016ae13a..00000000 --- a/src/mods/saveProjectActionMod.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BLUEPRINT_FORMAT, saveBlueprint } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:save_project`, - { - action: BarItems.save_project as Action, - originalClick: (BarItems.save_project as Action).click, - }, - context => { - context.action.click = (event: Event) => { - if (!Project || !Format) return - if (Format === BLUEPRINT_FORMAT) { - saveBlueprint() - } else { - context.originalClick.call(context.action, event) - } - } - return context - }, - context => { - context.action.click = context.originalClick - } -) diff --git a/src/mods/saveProjectAsActionMod.ts b/src/mods/saveProjectAsActionMod.ts deleted file mode 100644 index 67c24faa..00000000 --- a/src/mods/saveProjectAsActionMod.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BLUEPRINT_CODEC, BLUEPRINT_FORMAT } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' - -createBlockbenchMod( - `${PACKAGE.name}:save_project_as`, - { - action: BarItems.save_project_as as Action, - originalClick: (BarItems.save_project_as as Action).click, - }, - context => { - context.action.click = (event: Event) => { - if (!Project || !Format) return - if (Format === BLUEPRINT_FORMAT) { - BLUEPRINT_CODEC.export() - } else { - context.originalClick.call(context.action, event) - } - } - return context - }, - context => { - context.action.click = context.originalClick - } -) diff --git a/src/mods/showDefaultPoseMod.ts b/src/mods/showDefaultPoseMod.ts index 536061f4..6072c51d 100644 --- a/src/mods/showDefaultPoseMod.ts +++ b/src/mods/showDefaultPoseMod.ts @@ -1,29 +1,26 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { registerMod } from '../util/moddingTools' + +registerMod({ + id: `animated-java:show-default-pose`, + + apply: () => { + const original = Animator.showDefaultPose -createBlockbenchMod( - `${PACKAGE.name}:showDefaultPose`, - { - original: Animator.showDefaultPose, - }, - context => { Animator.showDefaultPose = function (noMatrixUpdate?: boolean) { - if (!isCurrentFormat()) return context.original(noMatrixUpdate) + if (!activeProjectIsBlueprintFormat()) return original(noMatrixUpdate) const nodes = [...Group.all, ...Outliner.elements] for (const node of nodes) { - // @ts-expect-error + // @ts-expect-error Constructor type is Function if (!node.constructor.animator) continue const mesh = node.mesh if (mesh.fix_rotation) mesh.rotation.copy(mesh.fix_rotation as THREE.Euler) if (mesh.fix_position) mesh.position.copy(mesh.fix_position as THREE.Vector3) if (mesh.fix_scale) mesh.scale.copy(mesh.fix_scale) else if ( - // @ts-expect-error - node.constructor.animator.prototype.channels && - // @ts-expect-error - node.constructor.animator.prototype.channels.scale + // @ts-expect-error Constructor type is Function + node.constructor.animator.prototype.channels?.scale ) { mesh.scale.x = mesh.scale.y = mesh.scale.z = 1 } @@ -31,9 +28,10 @@ createBlockbenchMod( if (!noMatrixUpdate) scene.updateMatrixWorld() } - return context + return { original } + }, + + revert: ({ original }) => { + Animator.showDefaultPose = original }, - context => { - Animator.showDefaultPose = context.original - } -) +}) diff --git a/src/mods/variantPreviewCubeFaceMod.ts b/src/mods/variantPreviewCubeFaceMod.ts index 775285af..f1d2330d 100644 --- a/src/mods/variantPreviewCubeFaceMod.ts +++ b/src/mods/variantPreviewCubeFaceMod.ts @@ -1,16 +1,21 @@ -import { isCurrentFormat } from '../blueprintFormat' -import { PACKAGE } from '../constants' -import { createBlockbenchMod } from '../util/moddingTools' +import { activeProjectIsBlueprintFormat } from '../formats/blueprint' +import { registerMod } from '../util/moddingTools' import { Variant } from '../variants' -createBlockbenchMod( - `${PACKAGE.name}:variantPreviewCubeFace`, - { - originalGetTexture: CubeFace.prototype.getTexture, - }, - context => { +declare global { + interface CubeFace { + lastVariant: Variant | undefined + } +} + +registerMod({ + id: `animated-java:variant-preview-cube-face`, + + apply: () => { + const original = CubeFace.prototype.getTexture + CubeFace.prototype.getTexture = function (this: CubeFace): Texture | undefined { - if (isCurrentFormat() && this.texture) { + if (activeProjectIsBlueprintFormat() && this.texture) { const variant = Variant.selected if ( variant && @@ -31,11 +36,13 @@ createBlockbenchMod( } } this.lastVariant = undefined - return context.originalGetTexture.call(this) + return original.call(this) } - return context + + return { original } }, - context => { - CubeFace.prototype.getTexture = context.originalGetTexture - } -) + + revert: ({ original }) => { + CubeFace.prototype.getTexture = original + }, +}) diff --git a/src/nodeConfigs.ts b/src/nodeConfigs.ts index 06ad0760..571f7cc9 100644 --- a/src/nodeConfigs.ts +++ b/src/nodeConfigs.ts @@ -1,30 +1,30 @@ import { NbtByte, NbtCompound, NbtFloat, NbtInt, NbtString, NbtTag } from 'deepslate/lib/nbt' -import { +import type { IBlueprintBoneConfigJSON, - IBlueprintCameraConfigJSON, IBlueprintLocatorConfigJSON, IBlueprintTextDisplayConfigJSON, -} from './blueprintFormat' +} from './formats/blueprint' +import { scrubUndefined } from './util/misc' export type BillboardMode = 'fixed' | 'vertical' | 'horizontal' | 'center' // TODO: Refactor these configs to inherit from a base class export class BoneConfig { - private _customName?: string - private _customNameVisible?: boolean - private _billboard?: BillboardMode - private _overrideBrightness?: boolean - private _brightnessOverride?: number - private _enchanted?: boolean - private _glowing?: boolean - private _overrideGlowColor?: boolean - private _glowColor?: string - private _inheritSettings?: boolean - private _invisible?: boolean - private _nbt?: string - private _shadowRadius?: number - private _shadowStrength?: number - private _useNBT?: boolean + private __customName?: string + private __customNameVisible?: boolean + private __billboard?: BillboardMode + private __overrideBrightness?: boolean + private __brightnessOverride?: number + private __enchanted?: boolean + private __glowing?: boolean + private __overrideGlowColor?: boolean + private __glowColor?: string + private __inheritSettings?: boolean + private __invisible?: boolean + private __nbt?: string + private __shadowRadius?: number + private __shadowStrength?: number + private __useNBT?: boolean static getDefault(): BoneConfig { return BoneConfig.fromJSON({ @@ -46,230 +46,232 @@ export class BoneConfig { }) } - get customName(): NonNullable { - if (this._customName !== undefined) return this._customName + get customName(): NonNullable { + if (this.__customName !== undefined) return this.__customName const defaultConfig = BoneConfig.getDefault() return defaultConfig.customName } - set customName(value: BoneConfig['_customName']) { - this._customName = value + set customName(value: BoneConfig['__customName']) { + this.__customName = value } - get customNameVisible(): NonNullable { - if (this._customNameVisible !== undefined) return this._customNameVisible + get customNameVisible(): NonNullable { + if (this.__customNameVisible !== undefined) return this.__customNameVisible const defaultConfig = BoneConfig.getDefault() return defaultConfig.customNameVisible } - set customNameVisible(value: BoneConfig['_customNameVisible']) { - this._customNameVisible = value + set customNameVisible(value: BoneConfig['__customNameVisible']) { + this.__customNameVisible = value } - get billboard(): NonNullable { - if (this._billboard !== undefined) return this._billboard + get billboard(): NonNullable { + if (this.__billboard !== undefined) return this.__billboard const defaultConfig = BoneConfig.getDefault() return defaultConfig.billboard } - set billboard(value: BoneConfig['_billboard']) { - this._billboard = value + set billboard(value: BoneConfig['__billboard']) { + this.__billboard = value } - get overrideBrightness(): NonNullable { - if (this._overrideBrightness !== undefined) return this._overrideBrightness + get overrideBrightness(): NonNullable { + if (this.__overrideBrightness !== undefined) return this.__overrideBrightness const defaultConfig = BoneConfig.getDefault() return defaultConfig.overrideBrightness } - set overrideBrightness(value: BoneConfig['_overrideBrightness']) { - this._overrideBrightness = value + set overrideBrightness(value: BoneConfig['__overrideBrightness']) { + this.__overrideBrightness = value } - get brightnessOverride(): NonNullable { - if (this._brightnessOverride !== undefined) return this._brightnessOverride + get brightnessOverride(): NonNullable { + if (this.__brightnessOverride !== undefined) return this.__brightnessOverride const defaultConfig = BoneConfig.getDefault() return defaultConfig.brightnessOverride } - set brightnessOverride(value: BoneConfig['_brightnessOverride']) { - this._brightnessOverride = value + set brightnessOverride(value: BoneConfig['__brightnessOverride']) { + this.__brightnessOverride = value } - get enchanted(): NonNullable { - if (this._enchanted !== undefined) return this._enchanted + get enchanted(): NonNullable { + if (this.__enchanted !== undefined) return this.__enchanted const defaultConfig = BoneConfig.getDefault() return defaultConfig.enchanted } - set enchanted(value: BoneConfig['_enchanted']) { - this._enchanted = value + set enchanted(value: BoneConfig['__enchanted']) { + this.__enchanted = value } - get glowing(): NonNullable { - if (this._glowing !== undefined) return this._glowing + get glowing(): NonNullable { + if (this.__glowing !== undefined) return this.__glowing const defaultConfig = BoneConfig.getDefault() return defaultConfig.glowing } - set glowing(value: BoneConfig['_glowing']) { - this._glowing = value + set glowing(value: BoneConfig['__glowing']) { + this.__glowing = value } - get overrideGlowColor(): NonNullable { - if (this._overrideGlowColor !== undefined) return this._overrideGlowColor + get overrideGlowColor(): NonNullable { + if (this.__overrideGlowColor !== undefined) return this.__overrideGlowColor const defaultConfig = BoneConfig.getDefault() return defaultConfig.overrideGlowColor } - set overrideGlowColor(value: BoneConfig['_overrideGlowColor']) { - this._overrideGlowColor = value + set overrideGlowColor(value: BoneConfig['__overrideGlowColor']) { + this.__overrideGlowColor = value } - get glowColor(): NonNullable { - if (this._glowColor !== undefined) return this._glowColor + get glowColor(): NonNullable { + if (this.__glowColor !== undefined) return this.__glowColor const defaultConfig = BoneConfig.getDefault() return defaultConfig.glowColor } - set glowColor(value: BoneConfig['_glowColor']) { - this._glowColor = value + set glowColor(value: BoneConfig['__glowColor']) { + this.__glowColor = value } - get inheritSettings(): NonNullable { - if (this._inheritSettings !== undefined) return this._inheritSettings + get inheritSettings(): NonNullable { + if (this.__inheritSettings !== undefined) return this.__inheritSettings const defaultConfig = BoneConfig.getDefault() return defaultConfig.inheritSettings } - set inheritSettings(value: BoneConfig['_inheritSettings']) { - this._inheritSettings = value + set inheritSettings(value: BoneConfig['__inheritSettings']) { + this.__inheritSettings = value } - get invisible(): NonNullable { - if (this._invisible !== undefined) return this._invisible + get invisible(): NonNullable { + if (this.__invisible !== undefined) return this.__invisible const defaultConfig = BoneConfig.getDefault() return defaultConfig.invisible } - set invisible(value: BoneConfig['_invisible']) { - this._invisible = value + set invisible(value: BoneConfig['__invisible']) { + this.__invisible = value } - get nbt(): NonNullable { - if (this._nbt !== undefined) return this._nbt + get nbt(): NonNullable { + if (this.__nbt !== undefined) return this.__nbt const defaultConfig = BoneConfig.getDefault() return defaultConfig.nbt } - set nbt(value: BoneConfig['_nbt']) { - this._nbt = value + set nbt(value: BoneConfig['__nbt']) { + this.__nbt = value } - get shadowRadius(): NonNullable { - if (this._shadowRadius !== undefined) return this._shadowRadius + get shadowRadius(): NonNullable { + if (this.__shadowRadius !== undefined) return this.__shadowRadius const defaultConfig = BoneConfig.getDefault() return defaultConfig.shadowRadius } - set shadowRadius(value: BoneConfig['_shadowRadius']) { - this._shadowRadius = value + set shadowRadius(value: BoneConfig['__shadowRadius']) { + this.__shadowRadius = value } - get shadowStrength(): NonNullable { - if (this._shadowStrength !== undefined) return this._shadowStrength + get shadowStrength(): NonNullable { + if (this.__shadowStrength !== undefined) return this.__shadowStrength const defaultConfig = BoneConfig.getDefault() return defaultConfig.shadowStrength } - set shadowStrength(value: BoneConfig['_shadowStrength']) { - this._shadowStrength = value + set shadowStrength(value: BoneConfig['__shadowStrength']) { + this.__shadowStrength = value } - get useNBT(): NonNullable { - if (this._useNBT !== undefined) return this._useNBT + get useNBT(): NonNullable { + if (this.__useNBT !== undefined) return this.__useNBT const defaultConfig = BoneConfig.getDefault() return defaultConfig.useNBT } - set useNBT(value: BoneConfig['_useNBT']) { - this._useNBT = value + set useNBT(value: BoneConfig['__useNBT']) { + this.__useNBT = value } - public checkIfEqual(other: BoneConfig) { + checkIfEqual(other: BoneConfig) { return ( - this._customName === other._customName && - this._customNameVisible === other._customNameVisible && - this._billboard === other._billboard && - this._overrideBrightness === other._overrideBrightness && - this._brightnessOverride === other._brightnessOverride && - this._enchanted === other._enchanted && - this._glowing === other._glowing && - this._overrideGlowColor === other._overrideGlowColor && - this._glowColor === other._glowColor && - this._inheritSettings === other._inheritSettings && - this._invisible === other._invisible && - this._nbt === other._nbt && - this._shadowRadius === other._shadowRadius && - this._shadowStrength === other._shadowStrength && - this._useNBT === other._useNBT + this.__customName === other.__customName && + this.__customNameVisible === other.__customNameVisible && + this.__billboard === other.__billboard && + this.__overrideBrightness === other.__overrideBrightness && + this.__brightnessOverride === other.__brightnessOverride && + this.__enchanted === other.__enchanted && + this.__glowing === other.__glowing && + this.__overrideGlowColor === other.__overrideGlowColor && + this.__glowColor === other.__glowColor && + this.__inheritSettings === other.__inheritSettings && + this.__invisible === other.__invisible && + this.__nbt === other.__nbt && + this.__shadowRadius === other.__shadowRadius && + this.__shadowStrength === other.__shadowStrength && + this.__useNBT === other.__useNBT ) } - public isDefault(): boolean { + isDefault(): boolean { return this.checkIfEqual(BoneConfig.getDefault()) } - public toJSON(): IBlueprintBoneConfigJSON { - return { - custom_name: this._customName, - custom_name_visible: this._customNameVisible, - billboard: this._billboard, - override_brightness: this._overrideBrightness, - brightness_override: this._brightnessOverride, - enchanted: this._enchanted, - glowing: this._glowing, - override_glow_color: this._overrideGlowColor, - glow_color: this._glowColor, - inherit_settings: this._inheritSettings, - invisible: this._invisible, - nbt: this._nbt, - shadow_radius: this._shadowRadius, - shadow_strength: this._shadowStrength, - use_nbt: this._useNBT, - } + toJSON(): IBlueprintBoneConfigJSON { + return scrubUndefined({ + custom_name: this.__customName, + custom_name_visible: this.__customNameVisible, + billboard: this.__billboard, + override_brightness: this.__overrideBrightness, + brightness_override: this.__brightnessOverride, + enchanted: this.__enchanted, + glowing: this.__glowing, + override_glow_color: this.__overrideGlowColor, + glow_color: this.__glowColor, + inherit_settings: this.__inheritSettings, + invisible: this.__invisible, + nbt: this.__nbt, + shadow_radius: this.__shadowRadius, + shadow_strength: this.__shadowStrength, + use_nbt: this.__useNBT, + }) } inheritFrom(other: BoneConfig) { - if (other._customName !== undefined) this.customName = other.customName - if (other._customNameVisible !== undefined) this.customNameVisible = other.customNameVisible - if (other._billboard !== undefined) this.billboard = other.billboard - if (other._overrideBrightness !== undefined) + if (other.__customName !== undefined) this.customName = other.customName + if (other.__customNameVisible !== undefined) + this.customNameVisible = other.customNameVisible + if (other.__billboard !== undefined) this.billboard = other.billboard + if (other.__overrideBrightness !== undefined) this.overrideBrightness = other.overrideBrightness - if (other._brightnessOverride !== undefined) + if (other.__brightnessOverride !== undefined) this.brightnessOverride = other.brightnessOverride - if (other._enchanted !== undefined) this.enchanted = other.enchanted - if (other._glowing !== undefined) this.glowing = other.glowing - if (other._overrideGlowColor !== undefined) this.overrideGlowColor = other.overrideGlowColor - if (other._glowColor !== undefined) this.glowColor = other.glowColor - if (other._inheritSettings !== undefined) this.inheritSettings = other.inheritSettings - if (other._invisible !== undefined) this.invisible = other.invisible - if (other._nbt !== undefined) this.nbt = other.nbt - if (other._shadowRadius !== undefined) this.shadowRadius = other.shadowRadius - if (other._shadowStrength !== undefined) this.shadowStrength = other.shadowStrength - if (other._useNBT !== undefined) this.useNBT = other.useNBT - } - - public static fromJSON(json: IBlueprintBoneConfigJSON): BoneConfig { + if (other.__enchanted !== undefined) this.enchanted = other.enchanted + if (other.__glowing !== undefined) this.glowing = other.glowing + if (other.__overrideGlowColor !== undefined) + this.overrideGlowColor = other.overrideGlowColor + if (other.__glowColor !== undefined) this.glowColor = other.glowColor + if (other.__inheritSettings !== undefined) this.inheritSettings = other.inheritSettings + if (other.__invisible !== undefined) this.invisible = other.invisible + if (other.__nbt !== undefined) this.nbt = other.nbt + if (other.__shadowRadius !== undefined) this.shadowRadius = other.shadowRadius + if (other.__shadowStrength !== undefined) this.shadowStrength = other.shadowStrength + if (other.__useNBT !== undefined) this.useNBT = other.useNBT + } + + static fromJSON(json: IBlueprintBoneConfigJSON): BoneConfig { const config = new BoneConfig() - if (json.custom_name !== undefined) config._customName = json.custom_name + if (json.custom_name !== undefined) config.__customName = json.custom_name if (json.custom_name_visible !== undefined) - config._customNameVisible = json.custom_name_visible - if (json.billboard !== undefined) config._billboard = json.billboard + config.__customNameVisible = json.custom_name_visible + if (json.billboard !== undefined) config.__billboard = json.billboard if (json.override_brightness !== undefined) - config._overrideBrightness = json.override_brightness + config.__overrideBrightness = json.override_brightness if (json.brightness_override !== undefined) - config._brightnessOverride = json.brightness_override - if (json.enchanted !== undefined) config._enchanted = json.enchanted - if (json.glowing !== undefined) config._glowing = json.glowing + config.__brightnessOverride = json.brightness_override + if (json.enchanted !== undefined) config.__enchanted = json.enchanted + if (json.glowing !== undefined) config.__glowing = json.glowing if (json.override_glow_color !== undefined) - config._overrideGlowColor = json.override_glow_color - if (json.glow_color !== undefined) config._glowColor = json.glow_color - if (json.inherit_settings !== undefined) config._inheritSettings = json.inherit_settings - if (json.invisible !== undefined) config._invisible = json.invisible - if (json.nbt !== undefined) config._nbt = json.nbt - if (json.shadow_radius !== undefined) config._shadowRadius = json.shadow_radius - if (json.shadow_strength !== undefined) config._shadowStrength = json.shadow_strength - if (json.use_nbt !== undefined) config._useNBT = json.use_nbt + config.__overrideGlowColor = json.override_glow_color + if (json.glow_color !== undefined) config.__glowColor = json.glow_color + if (json.inherit_settings !== undefined) config.__inheritSettings = json.inherit_settings + if (json.invisible !== undefined) config.__invisible = json.invisible + if (json.nbt !== undefined) config.__nbt = json.nbt + if (json.shadow_radius !== undefined) config.__shadowRadius = json.shadow_radius + if (json.shadow_strength !== undefined) config.__shadowStrength = json.shadow_strength + if (json.use_nbt !== undefined) config.__useNBT = json.use_nbt return config } - public toNBT(compound: NbtCompound = new NbtCompound()): NbtCompound { + toNBT(compound: NbtCompound = new NbtCompound()): NbtCompound { if (this.useNBT) { const newData = NbtTag.fromString(this.nbt) as NbtCompound for (const key of newData.keys()) { @@ -278,15 +280,15 @@ export class BoneConfig { return compound } - if (this._customName) { + if (this.__customName) { compound.set('CustomName', new NbtString(this.customName)) } - if (this._customNameVisible) { + if (this.__customNameVisible) { compound.set('CustomNameVisible', new NbtByte(Number(this.customNameVisible))) } - if (this._billboard) { + if (this.__billboard) { compound.set('billboard', new NbtString(this.billboard)) } @@ -331,11 +333,11 @@ export class BoneConfig { // compound.set('invisible', new NbtByte(1)) // } - if (this._shadowRadius) { + if (this.__shadowRadius) { compound.set('shadow_radius', new NbtFloat(this.shadowRadius)) } - if (this._shadowStrength) { + if (this.__shadowStrength) { compound.set('shadow_strength', new NbtFloat(this.shadowStrength)) } @@ -344,184 +346,131 @@ export class BoneConfig { } export class LocatorConfig { - private _useEntity?: boolean - private _entityType?: string - private _syncPassengerRotation?: boolean - private _summonCommands?: string - private _tickingCommands?: string + private __useEntity?: boolean + private __entityType?: string + private __syncPassengerRotation?: boolean + private __onSummonFunction?: string + private __onRemoveFunction?: string + private __onTickFunction?: string getDefault(): LocatorConfig { return LocatorConfig.fromJSON({ use_entity: false, entity_type: 'minecraft:pig', sync_passenger_rotation: false, - summon_commands: '', - ticking_commands: '', + on_summon_function: '', + on_remove_function: '', + on_tick_function: '', }) } - get useEntity(): NonNullable { - if (this._useEntity !== undefined) return this._useEntity + get useEntity(): NonNullable { + if (this.__useEntity !== undefined) return this.__useEntity const defaultConfig = this.getDefault() return defaultConfig.useEntity } - set useEntity(value: NonNullable) { - this._useEntity = value + set useEntity(value: NonNullable) { + this.__useEntity = value } - get entityType(): NonNullable { - if (this._entityType !== undefined) return this._entityType + get entityType(): NonNullable { + if (this.__entityType !== undefined) return this.__entityType const defaultConfig = this.getDefault() return defaultConfig.entityType } - set entityType(value: NonNullable) { - this._entityType = value + set entityType(value: NonNullable) { + this.__entityType = value } - get syncPassengerRotation(): NonNullable { - if (this._syncPassengerRotation !== undefined) return this._syncPassengerRotation + get syncPassengerRotation(): NonNullable { + if (this.__syncPassengerRotation !== undefined) return this.__syncPassengerRotation const defaultConfig = this.getDefault() return defaultConfig.syncPassengerRotation } - set syncPassengerRotation(value: NonNullable) { - this._syncPassengerRotation = value + set syncPassengerRotation(value: NonNullable) { + this.__syncPassengerRotation = value } - get summonCommands(): NonNullable { - if (this._summonCommands !== undefined) return this._summonCommands + get onSummonFunction(): NonNullable { + if (this.__onSummonFunction !== undefined) return this.__onSummonFunction const defaultConfig = this.getDefault() - return defaultConfig.summonCommands + return defaultConfig.onSummonFunction } - set summonCommands(value: NonNullable) { - this._summonCommands = value + set onSummonFunction(value: NonNullable) { + this.__onSummonFunction = value } - get tickingCommands(): NonNullable { - if (this._tickingCommands !== undefined) return this._tickingCommands + get onRemoveFunction(): NonNullable { + if (this.__onRemoveFunction !== undefined) return this.__onRemoveFunction const defaultConfig = this.getDefault() - return defaultConfig.tickingCommands + return defaultConfig.onRemoveFunction } - set tickingCommands(value: NonNullable) { - this._tickingCommands = value + set onRemoveFunction(value: NonNullable) { + this.__onRemoveFunction = value } - public toJSON(): IBlueprintLocatorConfigJSON { - return { - use_entity: this._useEntity, - entity_type: this._entityType, - sync_passenger_rotation: this._syncPassengerRotation, - summon_commands: this._summonCommands, - ticking_commands: this._tickingCommands, - } + get onTickFunction(): NonNullable { + if (this.__onTickFunction !== undefined) return this.__onTickFunction + const defaultConfig = this.getDefault() + return defaultConfig.onTickFunction + } + set onTickFunction(value: NonNullable) { + this.__onTickFunction = value + } + + toJSON(): IBlueprintLocatorConfigJSON { + return scrubUndefined({ + use_entity: this.__useEntity, + entity_type: this.__entityType, + sync_passenger_rotation: this.__syncPassengerRotation, + on_summon_function: this.__onSummonFunction, + on_remove_function: this.__onRemoveFunction, + on_tick_function: this.__onTickFunction, + }) } - public static fromJSON(json: IBlueprintLocatorConfigJSON): LocatorConfig { + static fromJSON(json: IBlueprintLocatorConfigJSON): LocatorConfig { const config = new LocatorConfig() - if (json.use_entity !== undefined) config._useEntity = json.use_entity - if (json.entity_type !== undefined) config._entityType = json.entity_type + if (json.use_entity !== undefined) config.__useEntity = json.use_entity + if (json.entity_type !== undefined) config.__entityType = json.entity_type if (json.sync_passenger_rotation !== undefined) - config._syncPassengerRotation = json.sync_passenger_rotation - if (json.summon_commands !== undefined) config._summonCommands = json.summon_commands - if (json.ticking_commands !== undefined) config._tickingCommands = json.ticking_commands + config.__syncPassengerRotation = json.sync_passenger_rotation + if (json.on_summon_function !== undefined) + config.__onSummonFunction = json.on_summon_function + if (json.on_remove_function !== undefined) + config.__onRemoveFunction = json.on_remove_function + if (json.on_tick_function !== undefined) config.__onTickFunction = json.on_tick_function return config } - public isDefault(): boolean { + isDefault(): boolean { return this.checkIfEqual(new LocatorConfig()) } - public checkIfEqual(other: LocatorConfig) { + checkIfEqual(other: LocatorConfig) { return ( this.useEntity === other.useEntity && this.entityType === other.entityType && this.syncPassengerRotation === other.syncPassengerRotation && - this.summonCommands === other.summonCommands && - this.tickingCommands === other.tickingCommands - ) - } -} - -export class CameraConfig { - private _entityType?: string - private _nbt?: string - private _tickingCommands?: string - - getDefault(): CameraConfig { - return CameraConfig.fromJSON({ - entity_type: 'minecraft:item_display', - nbt: '{}', - ticking_commands: '', - }) - } - - get entityType(): NonNullable { - if (this._entityType !== undefined) return this._entityType - const defaultConfig = this.getDefault() - return defaultConfig.entityType - } - set entityType(value: NonNullable) { - this._entityType = value - } - - get nbt(): NonNullable { - if (this._nbt !== undefined) return this._nbt - const defaultConfig = this.getDefault() - return defaultConfig.nbt - } - set nbt(value: NonNullable) { - this._nbt = value - } - - get tickingCommands(): NonNullable { - if (this._tickingCommands !== undefined) return this._tickingCommands - const defaultConfig = this.getDefault() - return defaultConfig.tickingCommands - } - set tickingCommands(value: NonNullable) { - this._tickingCommands = value - } - - public toJSON(): IBlueprintCameraConfigJSON { - return { - entity_type: this.entityType, - nbt: this.nbt, - ticking_commands: this.tickingCommands, - } - } - - public static fromJSON(json: IBlueprintCameraConfigJSON): CameraConfig { - const config = new CameraConfig() - if (json.entity_type != undefined) config.entityType = json.entity_type - if (json.nbt != undefined) config.nbt = json.nbt - if (json.ticking_commands != undefined) config.tickingCommands = json.ticking_commands - return config - } - - public isDefault(): boolean { - return this.checkIfEqual(new CameraConfig()) - } - - public checkIfEqual(other: CameraConfig) { - return ( - this.entityType === other.entityType && - this.nbt === other.nbt && - this.tickingCommands === other.tickingCommands + this.onSummonFunction === other.onSummonFunction && + this.onRemoveFunction === other.onRemoveFunction && + this.onTickFunction === other.onTickFunction ) } } export class TextDisplayConfig { - private _billboard?: BillboardMode - private _overrideBrightness?: boolean - private _brightnessOverride?: number - private _glowing?: boolean - private _overrideGlowColor?: boolean - private _glowColor?: string - private _invisible?: boolean - private _shadowRadius?: number - private _shadowStrength?: number - private _useNBT?: boolean - private _nbt?: string + private __billboard?: BillboardMode + private __overrideBrightness?: boolean + private __brightnessOverride?: number + private __glowing?: boolean + private __overrideGlowColor?: boolean + private __glowColor?: string + private __invisible?: boolean + private __shadowRadius?: number + private __shadowStrength?: number + private __useNBT?: boolean + private __nbt?: string static getDefault(): TextDisplayConfig { return TextDisplayConfig.fromJSON({ @@ -539,103 +488,103 @@ export class TextDisplayConfig { }) } - get billboard(): NonNullable { - if (this._billboard !== undefined) return this._billboard - const defaultConfig = BoneConfig.getDefault() + get billboard(): NonNullable { + if (this.__billboard !== undefined) return this.__billboard + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.billboard } - set billboard(value: BoneConfig['_billboard']) { - this._billboard = value + set billboard(value: TextDisplayConfig['__billboard']) { + this.__billboard = value } - get overrideBrightness(): NonNullable { - if (this._overrideBrightness !== undefined) return this._overrideBrightness - const defaultConfig = BoneConfig.getDefault() + get overrideBrightness(): NonNullable { + if (this.__overrideBrightness !== undefined) return this.__overrideBrightness + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.overrideBrightness } - set overrideBrightness(value: BoneConfig['_overrideBrightness']) { - this._overrideBrightness = value + set overrideBrightness(value: TextDisplayConfig['__overrideBrightness']) { + this.__overrideBrightness = value } - get brightnessOverride(): NonNullable { - if (this._brightnessOverride !== undefined) return this._brightnessOverride - const defaultConfig = BoneConfig.getDefault() + get brightnessOverride(): NonNullable { + if (this.__brightnessOverride !== undefined) return this.__brightnessOverride + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.brightnessOverride } - set brightnessOverride(value: BoneConfig['_brightnessOverride']) { - this._brightnessOverride = value + set brightnessOverride(value: TextDisplayConfig['__brightnessOverride']) { + this.__brightnessOverride = value } - get glowing(): NonNullable { - if (this._glowing !== undefined) return this._glowing - const defaultConfig = BoneConfig.getDefault() + get glowing(): NonNullable { + if (this.__glowing !== undefined) return this.__glowing + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.glowing } - set glowing(value: BoneConfig['_glowing']) { - this._glowing = value + set glowing(value: TextDisplayConfig['__glowing']) { + this.__glowing = value } - get overrideGlowColor(): NonNullable { - if (this._overrideGlowColor !== undefined) return this._overrideGlowColor - const defaultConfig = BoneConfig.getDefault() + get overrideGlowColor(): NonNullable { + if (this.__overrideGlowColor !== undefined) return this.__overrideGlowColor + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.overrideGlowColor } - set overrideGlowColor(value: BoneConfig['_overrideGlowColor']) { - this._overrideGlowColor = value + set overrideGlowColor(value: TextDisplayConfig['__overrideGlowColor']) { + this.__overrideGlowColor = value } - get glowColor(): NonNullable { - if (this._glowColor !== undefined) return this._glowColor - const defaultConfig = BoneConfig.getDefault() + get glowColor(): NonNullable { + if (this.__glowColor !== undefined) return this.__glowColor + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.glowColor } - set glowColor(value: BoneConfig['_glowColor']) { - this._glowColor = value + set glowColor(value: TextDisplayConfig['__glowColor']) { + this.__glowColor = value } - get invisible(): NonNullable { - if (this._invisible !== undefined) return this._invisible - const defaultConfig = BoneConfig.getDefault() + get invisible(): NonNullable { + if (this.__invisible !== undefined) return this.__invisible + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.invisible } - set invisible(value: BoneConfig['_invisible']) { - this._invisible = value + set invisible(value: TextDisplayConfig['__invisible']) { + this.__invisible = value } - get nbt(): NonNullable { - if (this._nbt !== undefined) return this._nbt - const defaultConfig = BoneConfig.getDefault() + get nbt(): NonNullable { + if (this.__nbt !== undefined) return this.__nbt + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.nbt } - set nbt(value: BoneConfig['_nbt']) { - this._nbt = value + set nbt(value: TextDisplayConfig['__nbt']) { + this.__nbt = value } - get shadowRadius(): NonNullable { - if (this._shadowRadius !== undefined) return this._shadowRadius - const defaultConfig = BoneConfig.getDefault() + get shadowRadius(): NonNullable { + if (this.__shadowRadius !== undefined) return this.__shadowRadius + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.shadowRadius } - set shadowRadius(value: BoneConfig['_shadowRadius']) { - this._shadowRadius = value + set shadowRadius(value: TextDisplayConfig['__shadowRadius']) { + this.__shadowRadius = value } - get shadowStrength(): NonNullable { - if (this._shadowStrength !== undefined) return this._shadowStrength - const defaultConfig = BoneConfig.getDefault() + get shadowStrength(): NonNullable { + if (this.__shadowStrength !== undefined) return this.__shadowStrength + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.shadowStrength } - set shadowStrength(value: BoneConfig['_shadowStrength']) { - this._shadowStrength = value + set shadowStrength(value: TextDisplayConfig['__shadowStrength']) { + this.__shadowStrength = value } - get useNBT(): NonNullable { - if (this._useNBT !== undefined) return this._useNBT - const defaultConfig = BoneConfig.getDefault() + get useNBT(): NonNullable { + if (this.__useNBT !== undefined) return this.__useNBT + const defaultConfig = TextDisplayConfig.getDefault() return defaultConfig.useNBT } - set useNBT(value: BoneConfig['_useNBT']) { - this._useNBT = value + set useNBT(value: TextDisplayConfig['__useNBT']) { + this.__useNBT = value } getDefault(): TextDisplayConfig { @@ -644,52 +593,52 @@ export class TextDisplayConfig { }) } - get tickingCommands(): NonNullable { - if (this._billboard !== undefined) return this._billboard + get tickingCommands(): NonNullable { + if (this.__billboard !== undefined) return this.__billboard const defaultConfig = this.getDefault() return defaultConfig.tickingCommands } - set tickingCommands(value: NonNullable) { - this._billboard = value - } - - public toJSON(): IBlueprintTextDisplayConfigJSON { - return { - billboard: this._billboard, - override_brightness: this._overrideBrightness, - brightness_override: this._brightnessOverride, - glowing: this._glowing, - override_glow_color: this._overrideGlowColor, - glow_color: this._glowColor, - invisible: this._invisible, - nbt: this._nbt, - shadow_radius: this._shadowRadius, - shadow_strength: this._shadowStrength, - use_nbt: this._useNBT, - } + set tickingCommands(value: NonNullable) { + this.__billboard = value + } + + toJSON(): IBlueprintTextDisplayConfigJSON { + return scrubUndefined({ + billboard: this.__billboard, + override_brightness: this.__overrideBrightness, + brightness_override: this.__brightnessOverride, + glowing: this.__glowing, + override_glow_color: this.__overrideGlowColor, + glow_color: this.__glowColor, + invisible: this.__invisible, + nbt: this.__nbt, + shadow_radius: this.__shadowRadius, + shadow_strength: this.__shadowStrength, + use_nbt: this.__useNBT, + }) } - public static fromJSON(json: IBlueprintTextDisplayConfigJSON): TextDisplayConfig { + static fromJSON(json: IBlueprintTextDisplayConfigJSON): TextDisplayConfig { const config = new TextDisplayConfig() - if (json.billboard !== undefined) config._billboard = json.billboard + if (json.billboard !== undefined) config.__billboard = json.billboard if (json.override_brightness !== undefined) - config._overrideBrightness = json.override_brightness + config.__overrideBrightness = json.override_brightness if (json.brightness_override !== undefined) - config._brightnessOverride = json.brightness_override - if (json.glowing !== undefined) config._glowing = json.glowing + config.__brightnessOverride = json.brightness_override + if (json.glowing !== undefined) config.__glowing = json.glowing if (json.override_glow_color !== undefined) - config._overrideGlowColor = json.override_glow_color - if (json.glow_color !== undefined) config._glowColor = json.glow_color - if (json.invisible !== undefined) config._invisible = json.invisible - if (json.nbt !== undefined) config._nbt = json.nbt - if (json.shadow_radius !== undefined) config._shadowRadius = json.shadow_radius - if (json.shadow_strength !== undefined) config._shadowStrength = json.shadow_strength - if (json.use_nbt !== undefined) config._useNBT = json.use_nbt + config.__overrideGlowColor = json.override_glow_color + if (json.glow_color !== undefined) config.__glowColor = json.glow_color + if (json.invisible !== undefined) config.__invisible = json.invisible + if (json.nbt !== undefined) config.__nbt = json.nbt + if (json.shadow_radius !== undefined) config.__shadowRadius = json.shadow_radius + if (json.shadow_strength !== undefined) config.__shadowStrength = json.shadow_strength + if (json.use_nbt !== undefined) config.__useNBT = json.use_nbt return config } - public toNBT(compound = new NbtCompound()) { + toNBT(compound = new NbtCompound()) { if (this.useNBT) { const newData = NbtTag.fromString(this.nbt) as NbtCompound for (const key of newData.keys()) { @@ -698,7 +647,7 @@ export class TextDisplayConfig { return compound } - if (this._billboard) { + if (this.__billboard) { compound.set('billboard', new NbtString(this.billboard)) } @@ -726,34 +675,34 @@ export class TextDisplayConfig { // compound.set('invisible', new NbtByte(1)) // } - if (this._shadowRadius) { + if (this.__shadowRadius) { compound.set('shadow_radius', new NbtFloat(this.shadowRadius)) } - if (this._shadowStrength) { + if (this.__shadowStrength) { compound.set('shadow_strength', new NbtFloat(this.shadowStrength)) } return compound } - public isDefault(): boolean { + isDefault(): boolean { return this.checkIfEqual(new TextDisplayConfig()) } - public checkIfEqual(other: TextDisplayConfig) { + checkIfEqual(other: TextDisplayConfig) { return ( - this._billboard === other._billboard && - this._overrideBrightness === other._overrideBrightness && - this._brightnessOverride === other._brightnessOverride && - this._glowing === other._glowing && - this._overrideGlowColor === other._overrideGlowColor && - this._glowColor === other._glowColor && - this._invisible === other._invisible && - this._nbt === other._nbt && - this._shadowRadius === other._shadowRadius && - this._shadowStrength === other._shadowStrength && - this._useNBT === other._useNBT + this.__billboard === other.__billboard && + this.__overrideBrightness === other.__overrideBrightness && + this.__brightnessOverride === other.__brightnessOverride && + this.__glowing === other.__glowing && + this.__overrideGlowColor === other.__overrideGlowColor && + this.__glowColor === other.__glowColor && + this.__invisible === other.__invisible && + this.__nbt === other.__nbt && + this.__shadowRadius === other.__shadowRadius && + this.__shadowStrength === other.__shadowStrength && + this.__useNBT === other.__useNBT ) } } diff --git a/src/outliner/resizableOutlinerElement.ts b/src/outliner/resizableOutlinerElement.ts index efc6f4df..3905d9d8 100644 --- a/src/outliner/resizableOutlinerElement.ts +++ b/src/outliner/resizableOutlinerElement.ts @@ -1,20 +1,21 @@ import { makeNotZero } from '../util/misc' export class ResizableOutlinerElement extends OutlinerElement { + type = 'resizable' // Properties - public name: string - public position: ArrayVector3 - public rotation: ArrayVector3 - public scale: ArrayVector3 - public visibility: boolean + name: string + position: ArrayVector3 + rotation: ArrayVector3 + scale: ArrayVector3 + visibility: boolean // eslint-disable-next-line @typescript-eslint/naming-convention - public preview_controller = PREVIEW_CONTROLLER + preview_controller = PREVIEW_CONTROLLER // Transform flags - public movable = true - public rotatable = true - public scalable = true - public resizable = true + movable = true + rotatable = true + scalable = true + resizable = true // Resizable Workaround properties get from() { @@ -44,8 +45,6 @@ export class ResizableOutlinerElement extends OutlinerElement { this.rotation ??= [0, 0, 0] this.scale ??= [1, 1, 1] this.visibility ??= true - - // this.sanitizeName() } get origin() { @@ -54,11 +53,11 @@ export class ResizableOutlinerElement extends OutlinerElement { getWorldCenter(): THREE.Vector3 { Reusable.vec3.set(0, 0, 0) - // @ts-ignore + // @ts-expect-error fastWorldPosition types are wrong return THREE.fastWorldPosition(this.mesh, Reusable.vec2).add(Reusable.vec3) as THREE.Vector3 } - public extend(data: any) { + extend(data: any) { for (const key in ResizableOutlinerElement.properties) { ResizableOutlinerElement.properties[key].merge(this, data) } @@ -93,7 +92,7 @@ export class ResizableOutlinerElement extends OutlinerElement { // allowNegative: boolean, // bidirectional: boolean ) { - let before = this.oldScale !== undefined ? this.oldScale : this.size(axis) + let before = this.oldScale ?? this.size(axis) if (before instanceof Array) before = before[axis] // For some unknown reason scale is not inverted on the y axis const sign = before < 0 && axis !== 1 ? -1 : 1 @@ -105,12 +104,21 @@ export class ResizableOutlinerElement extends OutlinerElement { this.preview_controller.updateGeometry?.(this) this.preview_controller.updateTransform(this) } + + getSaveCopy() { + const save = super.getSaveCopy?.() ?? {} + save.uuid = this.uuid + save.type = this.type + return save + } } new Property(ResizableOutlinerElement, 'string', 'name', { default: 'resizable_outliner_element' }) new Property(ResizableOutlinerElement, 'vector', 'position', { default: [0, 0, 0] }) new Property(ResizableOutlinerElement, 'vector', 'rotation', { default: [0, 0, 0] }) new Property(ResizableOutlinerElement, 'vector', 'scale', { default: [1, 1, 1] }) -new Property(ResizableOutlinerElement, 'string', 'visibility', { default: true }) +new Property(ResizableOutlinerElement, 'boolean', 'visibility', { default: true }) +new Property(ResizableOutlinerElement, 'boolean', 'locked', { default: false }) +new Property(ResizableOutlinerElement, 'boolean', 'export', { default: true }) export const PREVIEW_CONTROLLER = new NodePreviewController(ResizableOutlinerElement, { setup(el: ResizableOutlinerElement) { @@ -125,9 +133,9 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(ResizableOutlinerEle Project!.nodes_3d[el.uuid] = mesh el.preview_controller.updateGeometry?.(el) - // el.preview_controller.updateTransform(el) el.preview_controller.dispatchEvent('setup', { element: el }) }, + updateTransform(el: ResizableOutlinerElement) { NodePreviewController.prototype.updateTransform.call(el.preview_controller, el) if (el.mesh.fix_position) { @@ -145,7 +153,6 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(ResizableOutlinerEle el.mesh.fix_scale.set(...el.scale) makeNotZero(el.mesh.fix_scale) } - // @ts-ignore el.preview_controller.dispatchEvent('update_transform', { element: el }) }, }) diff --git a/src/outliner/textDisplay.ts b/src/outliner/textDisplay.ts index d376265b..7573a321 100644 --- a/src/outliner/textDisplay.ts +++ b/src/outliner/textDisplay.ts @@ -1,16 +1,15 @@ -import { - BLUEPRINT_FORMAT, - IBlueprintTextDisplayConfigJSON, - isCurrentFormat, -} from '../blueprintFormat' import { PACKAGE } from '../constants' -import { createAction, createBlockbenchMod } from '../util/moddingTools' +import { + type IBlueprintTextDisplayConfigJSON, + activeProjectIsBlueprintFormat, +} from '../formats/blueprint' +import { registerAction } from '../util/moddingTools' // import * as MinecraftFull from '../assets/MinecraftFull.json' -import { TEXT_DISPLAY_CONFIG_ACTION } from '../interface/dialog/textDisplayConfig' +import { JsonTextParser, JsonTextSyntaxError } from 'src/systems/jsonText/parser' import { TextDisplayConfig } from '../nodeConfigs' -import { getVanillaFont } from '../systems/minecraft/fontManager' -import { JsonText } from '../systems/minecraft/jsonText' -import { events } from '../util/events' +import { JsonText, TextElement } from '../systems/jsonText' +import { getVanillaFont, MinecraftFont } from '../systems/minecraft/fontManager' +import EVENTS from '../util/events' import { Valuable } from '../util/stores' import { translate } from '../util/translation' import { ResizableOutlinerElement } from './resizableOutlinerElement' @@ -32,44 +31,31 @@ export type Alignment = 'left' | 'center' | 'right' export class TextDisplay extends ResizableOutlinerElement { static type = `${PACKAGE.name}:text_display` + static icon = 'text_fields' static selected: TextDisplay[] = [] static all: TextDisplay[] = [] + static invalidJsonText: TextElement = { text: 'Invalid JSON Text!', color: 'red' } - public type = TextDisplay.type - public icon = 'text_fields' - public needsUniqueName = true + type = TextDisplay.type + icon = TextDisplay.icon + needsUniqueName = true // Properties - public config: IBlueprintTextDisplayConfigJSON - - public menu = new Menu([ - ...Outliner.control_menu_group, - TEXT_DISPLAY_CONFIG_ACTION, - '_', - 'rename', - 'delete', - ]) - public buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] + config: IBlueprintTextDisplayConfigJSON + + buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] // eslint-disable-next-line @typescript-eslint/naming-convention - public preview_controller = PREVIEW_CONTROLLER - - public ready = false - public textError = new Valuable('') - - private _updating = false - private _text = new Valuable('Hello World!') - private _newText: string | undefined - private _lineWidth = new Valuable(200) - private _newLineWidth: number | undefined - private _backgroundColor = new Valuable('#000000') - private _newBackgroundColor: string | undefined - private _backgroundAlpha = new Valuable(0.25) - private _newBackgroundAlpha: number | undefined - private _shadow = new Valuable(false) - private _newShadow: boolean | undefined - private _align = new Valuable('center') - private _newAlign: Alignment | undefined - public seeThrough = false + preview_controller = PREVIEW_CONTROLLER + + textError = new Valuable('') + + private __pendingMeshUpdate?: ReturnType + private __text = new Valuable('Hello World!') + private __lineWidth = TextDisplay.properties.lineWidth.default as number + private __backgroundColor = TextDisplay.properties.backgroundColor.default as string + private __shadow = TextDisplay.properties.shadow.default as boolean + private __align: Alignment = TextDisplay.properties.align.default as Alignment + seeThrough = TextDisplay.properties.seeThrough.default as boolean constructor(data: TextDisplayOptions, uuid = guid()) { super(data, uuid) @@ -91,100 +77,77 @@ export class TextDisplay extends ResizableOutlinerElement { this.config ??= {} this.sanitizeName() - - this._text.subscribe(v => { - this._newText = v - void this.updateText() - }) - this._lineWidth.subscribe(v => { - this._newLineWidth = v - void this.updateText() - }) - this._backgroundColor.subscribe(v => { - this._newBackgroundColor = v - void this.updateText() - }) - this._backgroundAlpha.subscribe(v => { - this._newBackgroundAlpha = v - void this.updateText() - }) - this._shadow.subscribe(v => { - this._newShadow = v - void this.updateText() - }) - this._align.subscribe(v => { - this._newAlign = v - void this.updateText() - }) } - public sanitizeName(): string { + sanitizeName(): string { this.name = sanitizeOutlinerElementName(this.name, this.uuid) return this.name } get text() { - if (this._text === undefined) return TextDisplay.properties['text'].default as string - return this._text.get() + if (this.__text === undefined) return TextDisplay.properties.text.default as string + return this.__text.get() } set text(value) { - if (this._text === undefined) return + if (this.__text === undefined) return if (value === this.text) return - this._text.set(value) + this.__text.set(value) + void this.updateTextMesh() } get lineWidth() { - if (this._lineWidth === undefined) - return TextDisplay.properties['lineWidth'].default as number - return this._lineWidth.get() + if (this.__lineWidth === undefined) + return TextDisplay.properties.lineWidth.default as number + return this.__lineWidth } set lineWidth(value) { - if (this._lineWidth === undefined) return - this._lineWidth.set(value) + if (this.__lineWidth === undefined) return + if (value === this.lineWidth) return + this.__lineWidth = value + void this.updateTextMesh() } get backgroundColor() { - if (this._backgroundColor === undefined) - return TextDisplay.properties['backgroundColor'].default as string - return this._backgroundColor.get() + if (this.__backgroundColor === undefined) + return TextDisplay.properties.backgroundColor.default as string + return this.__backgroundColor } set backgroundColor(value) { - if (this._backgroundColor === undefined) return - this._backgroundColor.set(value) - } - - get backgroundAlpha() { - if (this._backgroundAlpha === undefined) - return TextDisplay.properties['backgroundAlpha'].default as number - return this._backgroundAlpha.get() - } - - set backgroundAlpha(value) { - if (this._backgroundAlpha === undefined) return - this._backgroundAlpha.set(value) + if (this.__backgroundColor === undefined) return + if (value === this.backgroundColor) return + this.__backgroundColor = value + void this.updateTextMesh() } get shadow() { - if (this._shadow === undefined) return TextDisplay.properties['shadow'].default as boolean - return this._shadow.get() + if (this.__shadow === undefined) return TextDisplay.properties.shadow.default as boolean + return this.__shadow } set shadow(value) { - if (this._shadow === undefined) return - this._shadow.set(value) + if (this.__shadow === undefined) return + if (value === this.shadow) return + this.__shadow = value + void this.updateTextMesh() } get align() { - if (this._align === undefined) return TextDisplay.properties['align'].default as Alignment - return this._align.get() + if (this.__align === undefined) return TextDisplay.properties.align.default as Alignment + return this.__align } set align(value) { - if (this._align === undefined) return - this._align.set(value) + if (this.__align === undefined) return + if (value === this.align) return + this.__align = value + void this.updateTextMesh() + } + + getTextValuable() { + return this.__text } getUndoCopy() { @@ -201,14 +164,11 @@ export class TextDisplay extends ResizableOutlinerElement { } getSaveCopy() { - const el: any = {} + const save = super.getSaveCopy?.() ?? {} for (const key in TextDisplay.properties) { - TextDisplay.properties[key].copy(this, el) + TextDisplay.properties[key].copy(this, save) } - el.uuid = this.uuid - el.type = this.type - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return el + return save } select() { @@ -251,93 +211,84 @@ export class TextDisplay extends ResizableOutlinerElement { TickUpdates.selection = true } - async updateText() { - if (this._updating) return - this._updating = true - let latestMesh: THREE.Mesh | undefined - while ( - this._newText !== undefined || - this._newLineWidth !== undefined || - this._newBackgroundColor !== undefined || - this._newBackgroundAlpha !== undefined || - this._newShadow !== undefined || - this._newAlign !== undefined - ) { - let text: JsonText | undefined + updateTextMesh() { + let result: JsonText | undefined + try { + const parser = new JsonTextParser({ + minecraftVersion: Project!.animated_java.target_minecraft_version, + }) + parser.enabledFeatures &= ~( + JsonTextParser.FEATURES.ALLOW_CLICK_EVENTS | + JsonTextParser.FEATURES.ALLOW_HOVER_EVENTS + ) + result = parser.parse(this.text) this.textError.set('') - try { - text = JsonText.fromString(this.text) - console.log(text) - } catch (e: any) { - console.error(e) + } catch (e: any) { + console.error(e) + if (e instanceof JsonTextSyntaxError) { + this.textError.set(e.getOriginErrorMessage()) + } else { this.textError.set(e.message as string) - this._updating = false - text = new JsonText({ text: 'Invalid JSON Text!', color: 'red' }) } - this._newText = undefined - this._newLineWidth = undefined - this._newBackgroundColor = undefined - this._newBackgroundAlpha = undefined - this._newShadow = undefined - this._newAlign = undefined - if (text === undefined) continue - latestMesh = await this.setText(text) } - this._updating = false - return latestMesh + result ??= new JsonText({ text: 'Invalid JSON Text!', color: 'red' }) + void this.renderTextMesh(result).then(({ mesh, hitbox, outline }) => { + this.applyTextMesh(mesh, hitbox, outline) + }) } - async waitForReady() { - while (!this.ready) { - await new Promise(resolve => requestAnimationFrame(resolve)) - } + private async renderTextMesh(jsonText: JsonText) { + const promise = getVanillaFont() + .then(font => + font.generateTextDisplayMesh({ + jsonText, + maxLineWidth: this.lineWidth, + backgroundColor: tinycolor(this.backgroundColor), + shadow: this.shadow, + alignment: this.align, + }) + ) + .then(result => { + if (this.__pendingMeshUpdate === promise) { + this.__pendingMeshUpdate = undefined + return result + } + return this.__pendingMeshUpdate + }) as ReturnType + + this.__pendingMeshUpdate = promise + return promise } - private async setText(jsonText: JsonText) { - await this.waitForReady() - const font = await getVanillaFont() - // Hide the geo while rendering - - const { mesh: newMesh, outline } = await font.generateTextMesh({ - jsonText, - maxLineWidth: this.lineWidth, - backgroundColor: this.backgroundColor, - backgroundAlpha: this.backgroundAlpha, - shadow: this.shadow, - alignment: this.align, - }) - newMesh.name = this.uuid + '_text' - const previousMesh = this.mesh.children.find(v => v.name === newMesh.name) - if (previousMesh) this.mesh.remove(previousMesh) + private applyTextMesh( + text: THREE.Mesh, + hitbox: THREE.BufferGeometry, + outline: THREE.LineSegments + ) { + text.name = this.uuid + '_text' + text.isTextDisplayText = true const mesh = this.mesh as THREE.Mesh + mesh.clear() + delete mesh.sprite mesh.name = this.uuid - mesh.geometry = (newMesh.children[0] as THREE.Mesh).geometry.clone() - mesh.geometry.translate( - newMesh.children[0].position.x, - newMesh.children[0].position.y, - newMesh.children[0].position.z - ) - mesh.geometry.rotateY(Math.PI) - mesh.geometry.scale(newMesh.scale.x, newMesh.scale.y, newMesh.scale.z) mesh.material = Canvas.transparentMaterial - - mesh.add(newMesh) + mesh.geometry = hitbox + mesh.add(text) outline.name = this.uuid + '_outline' outline.visible = this.selected mesh.outline = outline - const previousOutline = mesh.children.find(v => v.name === outline.name) - if (previousOutline) mesh.remove(previousOutline) mesh.add(outline) mesh.visible = this.visibility - return newMesh + + return text } } +TextDisplay.prototype.icon = TextDisplay.icon new Property(TextDisplay, 'string', 'text', { default: '"Hello World!"' }) new Property(TextDisplay, 'number', 'lineWidth', { default: 200 }) -new Property(TextDisplay, 'string', 'backgroundColor', { default: '#000000' }) -new Property(TextDisplay, 'number', 'backgroundAlpha', { default: 0.25 }) +new Property(TextDisplay, 'string', 'backgroundColor', { default: '#00000040' }) new Property(TextDisplay, 'string', 'align', { default: 'center' }) new Property(TextDisplay, 'boolean', 'shadow', { default: false }) new Property(TextDisplay, 'boolean', 'seeThrough', { default: false }) @@ -349,42 +300,51 @@ new Property(TextDisplay, 'object', 'config', { OutlinerElement.registerType(TextDisplay, TextDisplay.type) +const TEMP_MESH_MAP = new THREE.TextureLoader().load( + 'data:image/svg+xml,' + + encodeURIComponent( + `` + ) +) +TEMP_MESH_MAP.minFilter = THREE.NearestFilter +TEMP_MESH_MAP.magFilter = THREE.NearestFilter + export const PREVIEW_CONTROLLER = new NodePreviewController(TextDisplay, { setup(el: TextDisplay) { ResizableOutlinerElement.prototype.preview_controller.setup(el) - // Minecraft's transparency is funky 😭 - Project!.nodes_3d[el.uuid].renderOrder = -1 + // Setup temp sprite mesh + const material = new THREE.SpriteMaterial({ + map: TEMP_MESH_MAP, + alphaTest: 0.1, + sizeAttenuation: false, + }) + const sprite = new THREE.Sprite(material) + sprite.scale.setScalar(1 / 32) + const mesh = el.mesh as THREE.Mesh + mesh.add(sprite) + mesh.sprite = sprite - void getVanillaFont() - .then(() => { - el.preview_controller.updateTransform(el) - el.preview_controller.updateGeometry(el) - el.preview_controller.dispatchEvent('setup', { element: el }) - }) - .finally(() => { - el.ready = true - }) + // Minecraft's transparency is funky 😭 + mesh.renderOrder = -1 + el.preview_controller.dispatchEvent('setup', { element: el }) }, + updateGeometry(el: TextDisplay) { - void el.updateText().then(() => { - el.preview_controller.updateTransform(el) - }) + el.updateTextMesh() }, + updateTransform(el: TextDisplay) { ResizableOutlinerElement.prototype.preview_controller.updateTransform(el) }, }) class TextDisplayAnimator extends BoneAnimator { - private _name: string - - public uuid: string - public element: TextDisplay | undefined + uuid: string + element: TextDisplay | undefined constructor(uuid: string, animation: _Animation, name: string) { super(uuid, animation, name) this.uuid = uuid - this._name = name } getElement() { @@ -424,7 +384,7 @@ class TextDisplayAnimator extends BoneAnimator { } } - if (this.element && this.element.parent && this.element.parent !== 'root') { + if (this.element.parent && this.element.parent !== 'root') { this.element.parent.openUp() } @@ -433,7 +393,7 @@ class TextDisplayAnimator extends BoneAnimator { doRender() { this.getElement() - return !!(this.element && this.element.mesh) + return !!this.element?.mesh } displayRotation(arr: ArrayVector3 | ArrayVector4, multiplier = 1) { @@ -445,13 +405,13 @@ class TextDisplayAnimator extends BoneAnimator { if (arr) { if (arr.length === 4) { - const added_rotation = new THREE.Euler().setFromQuaternion( + const addedRotation = new THREE.Euler().setFromQuaternion( new THREE.Quaternion().fromArray(arr), 'ZYX' ) - bone.rotation.x -= added_rotation.x * multiplier - bone.rotation.y -= added_rotation.y * multiplier - bone.rotation.z += added_rotation.z * multiplier + bone.rotation.x -= addedRotation.x * multiplier + bone.rotation.y -= addedRotation.y * multiplier + bone.rotation.z += addedRotation.z * multiplier } else { bone.rotation.x -= Math.degToRad(arr[0]) * multiplier bone.rotation.y -= Math.degToRad(arr[1]) * multiplier @@ -495,68 +455,68 @@ class TextDisplayAnimator extends BoneAnimator { TextDisplayAnimator.prototype.type = TextDisplay.type TextDisplay.animator = TextDisplayAnimator as any -createBlockbenchMod( - `${PACKAGE.name}:textDisplay`, +export const CREATE_ACTION = registerAction( + { id: `animated-java:create-text-display` }, { - subscriptions: [] as Array<() => void>, - }, - context => { - Interface.Panels.outliner.menu.addAction(CREATE_ACTION, 3) - Toolbars.outliner.add(CREATE_ACTION, 0) - MenuBar.menus.edit.addAction(CREATE_ACTION, 8) - - context.subscriptions.push( - events.SELECT_PROJECT.subscribe(project => { - if (project.format.id !== BLUEPRINT_FORMAT.id) return - project.textDisplays ??= [] - TextDisplay.all.empty() - TextDisplay.all.push(...project.textDisplays) - }), - events.UNSELECT_PROJECT.subscribe(project => { - if (project.format.id !== BLUEPRINT_FORMAT.id) return - project.textDisplays = [...TextDisplay.all] - TextDisplay.all.empty() + name: translate('action.create_text_display.title'), + icon: 'text_fields', + category: 'animated_java', + condition() { + return activeProjectIsBlueprintFormat() && Mode.selected.id === Modes.options.edit.id + }, + click() { + Undo.initEdit({ outliner: true, elements: [], selection: true }) + + const textDisplay = new TextDisplay({}).init() + const group = getCurrentGroup() + + if (group instanceof Group) { + textDisplay.addTo(group) + textDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) + } + + selected.forEachReverse(el => el.unselect()) + Group.first_selected?.unselect() + textDisplay.select() + + Undo.finishEdit('Create Text Display', { + outliner: true, + elements: selected, + selection: true, }) - ) - return context - }, - context => { - Interface.Panels.outliner.menu.removeAction(CREATE_ACTION.id) - Toolbars.outliner.remove(CREATE_ACTION) - MenuBar.menus.edit.removeAction(CREATE_ACTION.id) - context.subscriptions.forEach(unsub => unsub()) + return textDisplay + }, } ) -export const CREATE_ACTION = createAction(`${PACKAGE.name}:create_text_display`, { - name: translate('action.create_text_display.title'), - icon: 'text_fields', - category: 'animated_java', - condition() { - return isCurrentFormat() && Mode.selected.id === Modes.options.edit.id - }, - click() { - Undo.initEdit({ outliner: true, elements: [], selection: true }) - - const textDisplay = new TextDisplay({}).init() - const group = getCurrentGroup() - - if (group instanceof Group) { - textDisplay.addTo(group) - textDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) - } - - selected.forEachReverse(el => el.unselect()) - Group.first_selected && Group.first_selected.unselect() - textDisplay.select() - - Undo.finishEdit('Create Text Display', { - outliner: true, - elements: selected, - selection: true, +const CLEANUP_CALLBACKS: Array<() => void> = [] + +CREATE_ACTION.onCreated(action => { + Interface.Panels.outliner.menu.addAction(action, 3) + Toolbars.outliner.add(action, 0) + MenuBar.menus.edit.addAction(action, 8) + + CLEANUP_CALLBACKS.push( + EVENTS.SELECT_PROJECT.subscribe(project => { + if (!activeProjectIsBlueprintFormat()) return + project.textDisplays ??= [] + TextDisplay.all.empty() + TextDisplay.all.push(...project.textDisplays) + }), + + EVENTS.UNSELECT_PROJECT.subscribe(project => { + if (!activeProjectIsBlueprintFormat()) return + project.textDisplays = [...TextDisplay.all] + TextDisplay.all.empty() }) + ) +}) - return textDisplay - }, +CREATE_ACTION.onDeleted(action => { + Interface.Panels.outliner.menu.removeAction(action) + Toolbars.outliner.remove(action) + MenuBar.menus.edit.removeAction(action) + + CLEANUP_CALLBACKS.forEach(unsub => unsub()) }) diff --git a/src/outliner/util.ts b/src/outliner/util.ts index 3b1582bf..25ea6fac 100644 --- a/src/outliner/util.ts +++ b/src/outliner/util.ts @@ -1,10 +1,10 @@ -import { sanitizePathName } from '../util/minecraftUtil' +import { sanitizeStorageKey } from '../util/minecraftUtil' import { TextDisplay } from './textDisplay' import { VanillaBlockDisplay } from './vanillaBlockDisplay' import { VanillaItemDisplay } from './vanillaItemDisplay' export function sanitizeOutlinerElementName(name: string, elementUUID: string): string { - name = sanitizePathName(name) + name = sanitizeStorageKey(name) let otherNodes: OutlinerElement[] = [ ...VanillaBlockDisplay.all, ...Group.all, @@ -24,7 +24,7 @@ export function sanitizeOutlinerElementName(name: string, elementUUID: string): } let i = 1 - const match = name.match(/\d+$/) + const match = /\d+$/.exec(name) if (match) { i = parseInt(match[0]) name = name.slice(0, -match[0].length) diff --git a/src/outliner/vanillaBlockDisplay.ts b/src/outliner/vanillaBlockDisplay.ts index 254872f1..e1fa7ee6 100644 --- a/src/outliner/vanillaBlockDisplay.ts +++ b/src/outliner/vanillaBlockDisplay.ts @@ -1,14 +1,13 @@ -import { IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' +import { registerAction } from 'src/util/moddingTools' import { PACKAGE } from '../constants' -import { VANILLA_BLOCK_DISPLAY_CONFIG_ACTION } from '../interface/dialog/vanillaBlockDisplayConfig' +import { type IBlueprintBoneConfigJSON, activeProjectIsBlueprintFormat } from '../formats/blueprint' import { BoneConfig } from '../nodeConfigs' -import { getBlockModel } from '../systems/minecraft/blockModelManager' -import { BlockStateValue, getBlockState } from '../systems/minecraft/blockstateManager' +import { BlockModelMesh, getBlockModel } from '../systems/minecraft/blockModelManager' +import { type BlockStateValue, getBlockState } from '../systems/minecraft/blockstateManager' import { MINECRAFT_REGISTRY } from '../systems/minecraft/registryManager' import { getCurrentVersion } from '../systems/minecraft/versionManager' -import { events } from '../util/events' +import EVENTS from '../util/events' import { parseBlock } from '../util/minecraftUtil' -import { createAction, createBlockbenchMod } from '../util/moddingTools' import { Valuable } from '../util/stores' import { translate } from '../util/translation' import { ResizableOutlinerElement } from './resizableOutlinerElement' @@ -28,31 +27,23 @@ interface VanillaBlockDisplayOptions { export class VanillaBlockDisplay extends ResizableOutlinerElement { static type = `${PACKAGE.name}:vanilla_block_display` + static icon = 'deployed_code' static selected: VanillaBlockDisplay[] = [] static all: VanillaBlockDisplay[] = [] - public type = VanillaBlockDisplay.type - public icon = 'deployed_code' - public needsUniqueName = true + type = VanillaBlockDisplay.type + icon = VanillaBlockDisplay.icon + needsUniqueName = true // Properties - public _block = new Valuable('minecraft:stone') - public config: IBlueprintBoneConfigJSON - - public error = new Valuable('') - - public menu = new Menu([ - ...Outliner.control_menu_group, - VANILLA_BLOCK_DISPLAY_CONFIG_ACTION, - '_', - 'rename', - 'delete', - ]) - public buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] - // eslint-disable-next-line @typescript-eslint/naming-convention - public preview_controller = PREVIEW_CONTROLLER + private __block = new Valuable('minecraft:stone') + config: IBlueprintBoneConfigJSON + + error = new Valuable('') - public ready = false + buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] + // eslint-disable-next-line @typescript-eslint/naming-convention + preview_controller = PREVIEW_CONTROLLER constructor(data: VanillaBlockDisplayOptions, uuid = guid()) { super(data, uuid) @@ -93,28 +84,22 @@ export class VanillaBlockDisplay extends ResizableOutlinerElement { } } - this._block.subscribe(value => { + this.__block.subscribe(value => { void updateBlock(value) }) } get block() { - if (this._block === undefined) return 'minecraft:stone' - return this._block.get() + if (this.__block === undefined) return 'minecraft:stone' + return this.__block.get() } set block(value: string) { - if (this._block === undefined) return + if (this.__block === undefined) return if (this.block === value) return - this._block.set(value) - } - - async waitForReady() { - while (!this.ready) { - await new Promise(resolve => requestAnimationFrame(resolve)) - } + this.__block.set(value) } - public sanitizeName(): string { + sanitizeName(): string { this.name = sanitizeOutlinerElementName(this.name, this.uuid) return this.name } @@ -132,14 +117,11 @@ export class VanillaBlockDisplay extends ResizableOutlinerElement { } getSaveCopy() { - const el: any = {} + const save = super.getSaveCopy?.() ?? {} for (const key in VanillaBlockDisplay.properties) { - VanillaBlockDisplay.properties[key].copy(this, el) + VanillaBlockDisplay.properties[key].copy(this, save) } - el.uuid = this.uuid - el.type = this.type - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return el + return save } select() { @@ -181,7 +163,27 @@ export class VanillaBlockDisplay extends ResizableOutlinerElement { TickUpdates.selection = true this.preview_controller.updateHighlight(this) } + + applyBlockModel(blockModel: BlockModelMesh) { + const mesh = this.mesh as THREE.Mesh + mesh.name = this.uuid + mesh.geometry = blockModel.boundingBox + mesh.material = Canvas.transparentMaterial + + mesh.clear() + blockModel.outline.name = this.uuid + '_outline' + blockModel.outline.visible = this.selected + mesh.outline = blockModel.outline + mesh.add(blockModel.mesh) + mesh.add(blockModel.outline) + + this.preview_controller.updateHighlight(this) + this.preview_controller.updateTransform(this) + mesh.visible = this.visibility + TickUpdates.selection = true + } } +VanillaBlockDisplay.prototype.icon = VanillaBlockDisplay.icon new Property(VanillaBlockDisplay, 'string', 'block', { default: 'minecraft:stone' }) new Property(VanillaBlockDisplay, 'object', 'config', { get default() { @@ -190,9 +192,29 @@ new Property(VanillaBlockDisplay, 'object', 'config', { }) OutlinerElement.registerType(VanillaBlockDisplay, VanillaBlockDisplay.type) +const TEMP_MESH_MAP = new THREE.TextureLoader().load( + 'data:image/svg+xml,' + + encodeURIComponent( + `` + ) +) +TEMP_MESH_MAP.minFilter = THREE.NearestFilter +TEMP_MESH_MAP.magFilter = THREE.NearestFilter + export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaBlockDisplay, { setup(el: VanillaBlockDisplay) { ResizableOutlinerElement.prototype.preview_controller.setup(el) + // Setup temp sprite mesh + const material = new THREE.SpriteMaterial({ + map: TEMP_MESH_MAP, + alphaTest: 0.1, + sizeAttenuation: false, + }) + const sprite = new THREE.Sprite(material) + sprite.scale.setScalar(1 / 32) + const mesh = el.mesh as THREE.Mesh + mesh.add(sprite) + mesh.sprite = sprite }, updateGeometry(el: VanillaBlockDisplay) { if (!el.mesh) return @@ -200,22 +222,7 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaBlockDisplay, void getBlockModel(el.block) .then(result => { if (!result?.mesh) return - - const mesh = el.mesh as THREE.Mesh - mesh.name = el.uuid - mesh.geometry = result.boundingBox - mesh.material = Canvas.transparentMaterial - - mesh.clear() - result.outline.name = el.uuid + '_outline' - result.outline.visible = el.selected - mesh.outline = result.outline - mesh.add(result.mesh) - mesh.add(result.outline) - - el.preview_controller.updateHighlight(el) - el.preview_controller.updateTransform(el) - mesh.visible = el.visibility + el.applyBlockModel(result) TickUpdates.selection = true }) .catch(err => { @@ -229,14 +236,13 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaBlockDisplay, if (el.error.get()) el.mesh.outline.material = ERROR_OUTLINE_MATERIAL else el.mesh.outline.material = Canvas.outlineMaterial } - el.ready = true }) }, updateTransform(el: VanillaBlockDisplay) { ResizableOutlinerElement.prototype.preview_controller.updateTransform(el) }, updateHighlight(el: VanillaBlockDisplay, force?: boolean | VanillaBlockDisplay) { - if (!isCurrentFormat() || !el?.mesh) return + if (!activeProjectIsBlueprintFormat() || !el?.mesh) return const highlighted = Modes.edit && (force === true || force === el || el.selected) ? 1 : 0 const blockModel = el.mesh.children.at(0) as THREE.Mesh @@ -246,7 +252,6 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaBlockDisplay, const highlight = child.geometry.attributes.highlight if (highlight.array[0] != highlighted) { - // @ts-ignore highlight.array.set(Array(highlight.count).fill(highlighted)) highlight.needsUpdate = true } @@ -255,15 +260,12 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaBlockDisplay, }) class VanillaBlockDisplayAnimator extends BoneAnimator { - private _name: string - - public uuid: string - public element: VanillaBlockDisplay | undefined + uuid: string + element: VanillaBlockDisplay | undefined constructor(uuid: string, animation: _Animation, name: string) { super(uuid, animation, name) this.uuid = uuid - this._name = name } getElement() { @@ -303,7 +305,7 @@ class VanillaBlockDisplayAnimator extends BoneAnimator { } } - if (this.element && this.element.parent && this.element.parent !== 'root') { + if (this.element?.parent && this.element.parent !== 'root') { this.element.parent.openUp() } @@ -312,7 +314,7 @@ class VanillaBlockDisplayAnimator extends BoneAnimator { doRender() { this.getElement() - return !!(this.element && this.element.mesh) + return !!this.element?.mesh } displayRotation(arr: ArrayVector3 | ArrayVector4, multiplier = 1) { @@ -324,13 +326,13 @@ class VanillaBlockDisplayAnimator extends BoneAnimator { if (arr) { if (arr.length === 4) { - const added_rotation = new THREE.Euler().setFromQuaternion( + const addedRotation = new THREE.Euler().setFromQuaternion( new THREE.Quaternion().fromArray(arr), 'ZYX' ) - bone.rotation.x -= added_rotation.x * multiplier - bone.rotation.y -= added_rotation.y * multiplier - bone.rotation.z += added_rotation.z * multiplier + bone.rotation.x -= addedRotation.x * multiplier + bone.rotation.y -= addedRotation.y * multiplier + bone.rotation.z += addedRotation.z * multiplier } else { bone.rotation.x -= Math.degToRad(arr[0]) * multiplier bone.rotation.y -= Math.degToRad(arr[1]) * multiplier @@ -375,68 +377,68 @@ class VanillaBlockDisplayAnimator extends BoneAnimator { VanillaBlockDisplayAnimator.prototype.type = VanillaBlockDisplay.type VanillaBlockDisplay.animator = VanillaBlockDisplayAnimator as any -createBlockbenchMod( - `${PACKAGE.name}:vanillaBlockDisplay`, +export const CREATE_ACTION = registerAction( + { id: `animated-java:create-vanilla-block-display` }, { - subscriptions: [] as Array<() => void>, - }, - context => { - Interface.Panels.outliner.menu.addAction(CREATE_ACTION, 3) - Toolbars.outliner.add(CREATE_ACTION, 0) - MenuBar.menus.edit.addAction(CREATE_ACTION, 8) - - context.subscriptions.push( - events.SELECT_PROJECT.subscribe(project => { - project.vanillaBlockDisplays ??= [] - VanillaBlockDisplay.all.empty() - VanillaBlockDisplay.all.push(...project.vanillaBlockDisplays) - }), - events.UNSELECT_PROJECT.subscribe(project => { - project.vanillaBlockDisplays = [...VanillaBlockDisplay.all] - VanillaBlockDisplay.all.empty() + name: translate('action.create_vanilla_block_display.title'), + icon: 'deployed_code', + category: 'animated_java', + condition() { + return activeProjectIsBlueprintFormat() && Mode.selected.id === Modes.options.edit.id + }, + click() { + Undo.initEdit({ outliner: true, elements: [], selection: true }) + + const vanillaBlockDisplay = new VanillaBlockDisplay({}).init() + const group = getCurrentGroup() + + if (group instanceof Group) { + vanillaBlockDisplay.addTo(group) + vanillaBlockDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) + } + + selected.forEachReverse(el => el.unselect()) + Group.first_selected?.unselect() + vanillaBlockDisplay.select() + + Undo.finishEdit('Create Vanilla Block Display', { + outliner: true, + elements: selected, + selection: true, }) - ) - return context - }, - context => { - Interface.Panels.outliner.menu.removeAction(CREATE_ACTION.id) - Toolbars.outliner.remove(CREATE_ACTION) - MenuBar.menus.edit.removeAction(CREATE_ACTION.id) - context.subscriptions.forEach(unsub => unsub()) + return vanillaBlockDisplay + }, } ) -export const CREATE_ACTION = createAction(`${PACKAGE.name}:create_vanilla_block_display`, { - name: translate('action.create_vanilla_block_display.title'), - icon: 'deployed_code', - category: 'animated_java', - condition() { - return isCurrentFormat() && Mode.selected.id === Modes.options.edit.id - }, - click() { - Undo.initEdit({ outliner: true, elements: [], selection: true }) - - const vanillaBlockDisplay = new VanillaBlockDisplay({}).init() - const group = getCurrentGroup() +const CLEANUP_CALLBACKS: Array<() => void> = [] - if (group instanceof Group) { - vanillaBlockDisplay.addTo(group) - vanillaBlockDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) - } +CREATE_ACTION.onCreated(action => { + Interface.Panels.outliner.menu.addAction(action, 3) + Toolbars.outliner.add(action, 0) + MenuBar.menus.edit.addAction(action, 8) - selected.forEachReverse(el => el.unselect()) - Group.first_selected && Group.first_selected.unselect() - vanillaBlockDisplay.select() + CLEANUP_CALLBACKS.push( + EVENTS.SELECT_PROJECT.subscribe(project => { + project.vanillaBlockDisplays ??= [] + VanillaBlockDisplay.all.empty() + VanillaBlockDisplay.all.push(...project.vanillaBlockDisplays) + }), - Undo.finishEdit('Create Vanilla Block Display', { - outliner: true, - elements: selected, - selection: true, + EVENTS.UNSELECT_PROJECT.subscribe(project => { + project.vanillaBlockDisplays = [...VanillaBlockDisplay.all] + VanillaBlockDisplay.all.empty() }) + ) +}) - return vanillaBlockDisplay - }, +CREATE_ACTION.onDeleted(action => { + Interface.Panels.outliner.menu.removeAction(action) + Toolbars.outliner.remove(action) + MenuBar.menus.edit.removeAction(action) + + CLEANUP_CALLBACKS.forEach(unsub => unsub()) }) export function debugBlocks() { diff --git a/src/outliner/vanillaItemDisplay.ts b/src/outliner/vanillaItemDisplay.ts index fcf7fc81..77052fd0 100644 --- a/src/outliner/vanillaItemDisplay.ts +++ b/src/outliner/vanillaItemDisplay.ts @@ -1,12 +1,11 @@ -import { IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' +import { getItemModel } from 'src/systems/minecraft/itemModelManager' +import { registerAction } from 'src/util/moddingTools' import { PACKAGE } from '../constants' -import { VANILLA_ITEM_DISPLAY_CONFIG_ACTION } from '../interface/dialog/vanillaItemDisplayConfig' +import { type IBlueprintBoneConfigJSON, activeProjectIsBlueprintFormat } from '../formats/blueprint' import { BoneConfig } from '../nodeConfigs' -import { getItemModel } from '../systems/minecraft/itemModelManager' import { MINECRAFT_REGISTRY } from '../systems/minecraft/registryManager' import { getCurrentVersion } from '../systems/minecraft/versionManager' -import { events } from '../util/events' -import { createAction, createBlockbenchMod } from '../util/moddingTools' +import EVENTS from '../util/events' import { Valuable } from '../util/stores' import { translate } from '../util/translation' import { ResizableOutlinerElement } from './resizableOutlinerElement' @@ -35,32 +34,24 @@ interface VanillaItemDisplayOptions { export class VanillaItemDisplay extends ResizableOutlinerElement { static type = `${PACKAGE.name}:vanilla_item_display` + static icon = 'icecream' static selected: VanillaItemDisplay[] = [] static all: VanillaItemDisplay[] = [] - public type = VanillaItemDisplay.type - public icon = 'icecream' - public needsUniqueName = true + type = VanillaItemDisplay.type + icon = VanillaItemDisplay.icon + needsUniqueName = true // Properties - public _item = new Valuable('minecraft:diamond') - public _itemDisplay = new Valuable('none') - public config: IBlueprintBoneConfigJSON - - public error = new Valuable('') - - public menu = new Menu([ - ...Outliner.control_menu_group, - VANILLA_ITEM_DISPLAY_CONFIG_ACTION, - '_', - 'rename', - 'delete', - ]) - public buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] - // eslint-disable-next-line @typescript-eslint/naming-convention - public preview_controller = PREVIEW_CONTROLLER + private __item = new Valuable('minecraft:diamond') + private __itemDisplay = new Valuable('none') + config: IBlueprintBoneConfigJSON + + error = new Valuable('') - public ready = false + buttons = [Outliner.buttons.export, Outliner.buttons.locked, Outliner.buttons.visibility] + // eslint-disable-next-line @typescript-eslint/naming-convention + preview_controller = PREVIEW_CONTROLLER constructor(data: VanillaItemDisplayOptions, uuid = guid()) { super(data, uuid) @@ -104,36 +95,30 @@ export class VanillaItemDisplay extends ResizableOutlinerElement { } } - this._item.subscribe(value => { + this.__item.subscribe(value => { updateItem(value) }) } get item() { - if (this._item === undefined) return 'minecraft:diamond' - return this._item.get() + if (this.__item === undefined) return 'minecraft:diamond' + return this.__item.get() } set item(value: string) { - if (this._item === undefined) return - this._item.set(value) + if (this.__item === undefined) return + this.__item.set(value) } get itemDisplay() { - if (this._itemDisplay === undefined) return 'none' - return this._itemDisplay.get() + if (this.__itemDisplay === undefined) return 'none' + return this.__itemDisplay.get() } set itemDisplay(value: ItemDisplayMode) { - if (this._itemDisplay === undefined) return - this._itemDisplay.set(value) - } - - async waitForReady() { - while (!this.ready) { - await new Promise(resolve => requestAnimationFrame(resolve)) - } + if (this.__itemDisplay === undefined) return + this.__itemDisplay.set(value) } - public sanitizeName(): string { + sanitizeName(): string { this.name = sanitizeOutlinerElementName(this.name, this.uuid) return this.name } @@ -151,14 +136,11 @@ export class VanillaItemDisplay extends ResizableOutlinerElement { } getSaveCopy() { - const el: any = {} + const save = super.getSaveCopy?.() ?? {} for (const key in VanillaItemDisplay.properties) { - VanillaItemDisplay.properties[key].copy(this, el) + VanillaItemDisplay.properties[key].copy(this, save) } - el.uuid = this.uuid - el.type = this.type - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return el + return save } select() { @@ -201,6 +183,7 @@ export class VanillaItemDisplay extends ResizableOutlinerElement { this.preview_controller.updateHighlight(this) } } +VanillaItemDisplay.prototype.icon = VanillaItemDisplay.icon new Property(VanillaItemDisplay, 'string', 'item', { default: 'minecraft:diamond' }) new Property(VanillaItemDisplay, 'string', 'itemDisplay', { default: 'none' }) new Property(VanillaItemDisplay, 'object', 'config', { @@ -210,9 +193,29 @@ new Property(VanillaItemDisplay, 'object', 'config', { }) OutlinerElement.registerType(VanillaItemDisplay, VanillaItemDisplay.type) +const TEMP_MESH_MAP = new THREE.TextureLoader().load( + 'data:image/svg+xml,' + + encodeURIComponent( + `` + ) +) +TEMP_MESH_MAP.minFilter = THREE.NearestFilter +TEMP_MESH_MAP.magFilter = THREE.NearestFilter + export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaItemDisplay, { setup(el: VanillaItemDisplay) { ResizableOutlinerElement.prototype.preview_controller.setup(el) + // Setup temp sprite mesh + const material = new THREE.SpriteMaterial({ + map: TEMP_MESH_MAP, + alphaTest: 0.1, + sizeAttenuation: false, + }) + const sprite = new THREE.Sprite(material) + sprite.scale.setScalar(1 / 20) + const mesh = el.mesh as THREE.Mesh + mesh.add(sprite) + mesh.sprite = sprite }, updateGeometry(el: VanillaItemDisplay) { if (!el.mesh) return @@ -239,15 +242,12 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaItemDisplay, el.error.set(err.message as string) } }) - .finally(() => { - el.ready = true - }) }, updateTransform(el: VanillaItemDisplay) { ResizableOutlinerElement.prototype.preview_controller.updateTransform(el) }, updateHighlight(el: VanillaItemDisplay, force?: boolean | VanillaItemDisplay) { - if (!isCurrentFormat() || !el?.mesh) return + if (!activeProjectIsBlueprintFormat() || !el?.mesh) return const highlighted = Modes.edit && (force === true || force === el || el.selected) ? 1 : 0 const itemModel = el.mesh.children.at(0) as THREE.Mesh @@ -257,7 +257,6 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaItemDisplay, const highlight = child.geometry.attributes.highlight if (highlight.array[0] != highlighted) { - // @ts-ignore highlight.array.set(Array(highlight.count).fill(highlighted)) highlight.needsUpdate = true } @@ -266,15 +265,12 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(VanillaItemDisplay, }) class VanillaItemDisplayAnimator extends BoneAnimator { - private _name: string - - public uuid: string - public element: VanillaItemDisplay | undefined + uuid: string + element: VanillaItemDisplay | undefined constructor(uuid: string, animation: _Animation, name: string) { super(uuid, animation, name) this.uuid = uuid - this._name = name } getElement() { @@ -314,7 +310,7 @@ class VanillaItemDisplayAnimator extends BoneAnimator { } } - if (this.element && this.element.parent && this.element.parent !== 'root') { + if (this.element?.parent && this.element.parent !== 'root') { this.element.parent.openUp() } @@ -323,7 +319,7 @@ class VanillaItemDisplayAnimator extends BoneAnimator { doRender() { this.getElement() - return !!(this.element && this.element.mesh) + return !!this.element?.mesh } displayRotation(arr: ArrayVector3 | ArrayVector4, multiplier = 1) { @@ -335,13 +331,13 @@ class VanillaItemDisplayAnimator extends BoneAnimator { if (arr) { if (arr.length === 4) { - const added_rotation = new THREE.Euler().setFromQuaternion( + const addedRotation = new THREE.Euler().setFromQuaternion( new THREE.Quaternion().fromArray(arr), 'ZYX' ) - bone.rotation.x -= added_rotation.x * multiplier - bone.rotation.y -= added_rotation.y * multiplier - bone.rotation.z += added_rotation.z * multiplier + bone.rotation.x -= addedRotation.x * multiplier + bone.rotation.y -= addedRotation.y * multiplier + bone.rotation.z += addedRotation.z * multiplier } else { bone.rotation.x += Math.degToRad(-arr[0]) * multiplier bone.rotation.y += Math.degToRad(-arr[1]) * multiplier @@ -385,66 +381,67 @@ class VanillaItemDisplayAnimator extends BoneAnimator { VanillaItemDisplayAnimator.prototype.type = VanillaItemDisplay.type VanillaItemDisplay.animator = VanillaItemDisplayAnimator as any -createBlockbenchMod( - `${PACKAGE.name}:vanillaItemDisplay`, +export const CREATE_ACTION = registerAction( + { id: `animated-java:action/create-vanilla-item-display` }, { - subscriptions: [] as Array<() => void>, - }, - context => { - Interface.Panels.outliner.menu.addAction(CREATE_ACTION, 3) - Toolbars.outliner.add(CREATE_ACTION, 0) - MenuBar.menus.edit.addAction(CREATE_ACTION, 8) - - context.subscriptions.push( - events.SELECT_PROJECT.subscribe(project => { - project.vanillaItemDisplays ??= [] - VanillaItemDisplay.all.empty() - VanillaItemDisplay.all.push(...project.vanillaItemDisplays) - }), - events.UNSELECT_PROJECT.subscribe(project => { - project.vanillaItemDisplays = [...VanillaItemDisplay.all] - VanillaItemDisplay.all.empty() + name: translate('action.create_vanilla_item_display.title'), + icon: 'icecream', + category: 'animated_java', + condition() { + return activeProjectIsBlueprintFormat() && Mode.selected.id === Modes.options.edit.id + }, + click() { + Undo.initEdit({ outliner: true, elements: [], selection: true }) + + const vanillaItemDisplay = new VanillaItemDisplay({}).init() + const group = getCurrentGroup() + + if (group instanceof Group) { + vanillaItemDisplay.addTo(group) + vanillaItemDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) + } + + selected.forEachReverse(el => el.unselect()) + Group.first_selected?.unselect() + vanillaItemDisplay.select() + + Undo.finishEdit('Create Vanilla Item Display', { + outliner: true, + elements: selected, + selection: true, }) - ) - return context - }, - context => { - Interface.Panels.outliner.menu.removeAction(CREATE_ACTION.id) - Toolbars.outliner.remove(CREATE_ACTION) - MenuBar.menus.edit.removeAction(CREATE_ACTION.id) - context.subscriptions.forEach(unsub => unsub()) + return vanillaItemDisplay + }, } ) -export const CREATE_ACTION = createAction(`${PACKAGE.name}:create_vanilla_item_display`, { - name: translate('action.create_vanilla_item_display.title'), - icon: 'icecream', - category: 'animated_java', - condition() { - return isCurrentFormat() && Mode.selected.id === Modes.options.edit.id - }, - click() { - Undo.initEdit({ outliner: true, elements: [], selection: true }) - - const vanillaItemDisplay = new VanillaItemDisplay({}).init() - const group = getCurrentGroup() +const CLEANUP_CALLBACKS: Array<() => void> = [] - if (group instanceof Group) { - vanillaItemDisplay.addTo(group) - vanillaItemDisplay.extend({ position: group.origin.slice() as ArrayVector3 }) - } +CREATE_ACTION.onCreated(action => { + Interface.Panels.outliner.menu.addAction(action, 3) + Toolbars.outliner.add(action, 0) + MenuBar.menus.edit.addAction(action, 8) - selected.forEachReverse(el => el.unselect()) - Group.first_selected && Group.first_selected.unselect() - vanillaItemDisplay.select() + CLEANUP_CALLBACKS.push( + EVENTS.SELECT_PROJECT.subscribe(project => { + project.vanillaItemDisplays ??= [] + VanillaItemDisplay.all.empty() + VanillaItemDisplay.all.push(...project.vanillaItemDisplays) + }), - Undo.finishEdit('Create Vanilla Item Display', { - outliner: true, - elements: selected, - selection: true, + EVENTS.UNSELECT_PROJECT.subscribe(project => { + project.vanillaItemDisplays = [...VanillaItemDisplay.all] + VanillaItemDisplay.all.empty() }) + ) +}) - return vanillaItemDisplay - }, +CREATE_ACTION.onDeleted(action => { + Interface.Panels.outliner.menu.removeAction(action) + Toolbars.outliner.remove(action) + MenuBar.menus.edit.removeAction(action) + + CLEANUP_CALLBACKS.forEach(unsub => unsub()) + CLEANUP_CALLBACKS.empty() }) diff --git a/src/pluginPackage/changelog.json b/src/pluginPackage/changelog.json index c987e8ec..6c5c95b9 100644 --- a/src/pluginPackage/changelog.json +++ b/src/pluginPackage/changelog.json @@ -191,5 +191,161 @@ ] } ] + }, + "1.7.3": { + "title": "v1.7.3", + "author": "Titus Evans (SnaveSutit)", + "date": "2025-05-08", + "categories": [ + { + "title": "Fixes", + "list": [ + "Fixed adding cameras to a rig causing the data entity to dismount root.", + "Make variant keyframes reset to default if their variant doesn't exist anymore." + ] + } + ] + }, + "1.8.0-beta.1": { + "title": "v1.8.0-beta.1", + "author": "Titus Evans (SnaveSutit)", + "date": "2025-10-13", + "categories": [ + { + "title": "Changes", + "list": [ + "[BREAKING] `play` and `stop` functions now pause all animations when called.", + "[BREAKING] Removed support for Multiple target Minecraft versions. Blueprints can now only be exported to a single target version at a time.", + "[BREAKING] *Temporarily* disabled plugin mode entirely to avoid confusion while it's being reworked.", + "Added `Export All` and `Export All (Debug)` action to export all open Blueprints at once.", + "Added `Export (Debug)` action to export a Blueprint with debug features enabled. (Replaces `show_function_errors` and `show_outdated_warning` settings)", + "Added support for Minecraft 1.21.6 and 1.21.9", + "Added support for 1.21.6 element rotations. (No more 22.5 degree limitation!)", + "Added full support for SNBT JSON Text components. You can now use the modern SNBT format in any version, and Animated Java will automatically convert it to the correct format for your target version on export.", + "Added full support for 1.21.5 unicode escape sequences in SNBT JSON Text components. As long as the version of Minecraft you're exporting to supports Unicode characters, you can use them in your JSON Text components.", + "Added a button to copy the exported JSON Text component of a Text Display to your clipboard.", + "Updated keyframe UI, making it much more responsive, and easier to use.", + "Added support for `light_emission` on cubes.", + "The Blueprint format now has basic support for Blockbench collections. More features will be added in future updates.", + "Improved Text Display JSON text input syntax highlighting.", + "Improved Text Display JSON text input appearance.", + "Improved JSON Text component parsing and error reporting.", + "Improved all function errors and tellraws in exported Data Packs.", + "Improved outdated warning formatting and functionality.", + "Improved text display rendering performance, and update reactivity.", + "Display item is no longer configurable in versions that use `item_model` (1.20.5+).", + "The `summon` function now provides better feedback when using invalid arguments.", + "Removed loading popup when opening a model. Display entities are now loaded asynchronously, and will display a temporary placeholder model until they are ready.", + "Replaced `animated_java:empty` with vanilla empty model in MC 1.21.4+.", + "Increased width of all node configs from 400px to 600px.", + "Entities riding the root after running the on-summon function will now be removed automatically when the rig is removed.", + "Removed `show_function_errors` and `show_outdated_warning` and replaced them with a new `Export (Debug)` action.", + "Renamed all 'Commands' settings to 'Function'.", + "Renamed 'On-Tick Function' to 'Post-Tick Function'.", + "Added 'Pre-Tick Function' Blueprint setting.", + "Renamed `raw` export mode to `folder`.", + "Added new Blueprint setting: 'Auto Update Rig Orientation'.", + "Added `animated_java:/move` function to move the rig when 'Auto Update Rig Orientation' is enabled.", + "Reduced number of teleport commands run every tick when idle and animating if 'Auto Update Rig Orientation' is disabled.", + "Reduced the number of times the orientation of locators and cameras is updated while animating.", + "Improved performance of `fontManager` char mesh rendering.", + "Updated plugin description to 'Effortlessly craft complex animations for Minecraft: Java Edition'.", + "Replaced all `textarea`s in Blueprint settings, Variant config, and Node configs with a custom CodeJar that provides a better editing experience.", + "Improved plugin incompatibility warnings." + ] + }, + { + "title": "Fixes", + "list": [ + "Fixed a few minor Text Display text wrapping issues.", + "Fixed display entities causing collections to break.", + "Fixed [#440](https://github.com/Animated-Java/animated-java/issues/440)", + "Fixed [#442](https://github.com/Animated-Java/animated-java/issues/442)", + "Fixed [#453](https://github.com/Animated-Java/animated-java/issues/453)", + "Fixed [#213](https://github.com/Animated-Java/animated-java/issues/213)", + "Fixed [#284](https://github.com/Animated-Java/animated-java/issues/284)", + "Fixed GU failing on first load due to missing scoreboard dependency.", + "Fixed [#435](https://github.com/Animated-Java/animated-java/issues/435)", + "Fixed [#439](https://github.com/Animated-Java/animated-java/issues/439)", + "Fixed Text Display JSON text input not catching tab key presses.", + "Fixed outdated rig warning not working in some cases.", + "Fixed outdated rig warning entities missing important tags.", + "Fixed missing translations for install popup.", + "Fixed function tag merging not working correctly.", + "Fixed missing `item_display` NBT tag on exported Item Displays.", + "Fixed missing `Count` tag on Item Displays in Data Packs exported for 1.20.4.", + "Fixed project save status not updating when modifying node configs.", + "Fixed [#441](https://github.com/Animated-Java/animated-java/issues/441)", + "Fixed [#421](https://github.com/Animated-Java/animated-java/issues/421)", + "Fixed [#429](https://github.com/Animated-Java/animated-java/issues/429)", + "Fixed Text Display, Item Display, Block Display, and Variant config menus overflowing off the screen.", + "Fixed [#422](https://github.com/Animated-Java/animated-java/issues/422)", + "Fixed invalid cube warning not updating when editing cubes.", + "Fixed relative paths not being recognized on windows.", + "Fixed `set_frame` failing due to incorrect tag in entity selector.", + "Fixed animation preview not using in-game display entity interpolation simulation. Interpolations of fast-moving and rotating objects in the preview should now be much more accurate to how they appear in-game.", + "Fixed `NumberSlider` dialog item failing to correctly clamp values when `valueStep` is defined.", + "Fixed animation config dialog not marking project as unsaved after making changes.", + "Fixed variants panel animating when switching between projects.", + "Fixed `ajmodel` upgrader not opening when double clicked" + ] + }, + { + "title": "Camera Fixes", + "list": [ + "Fixed [#441](https://github.com/Animated-Java/animated-java/issues/441)", + "Fixed [#435](https://github.com/Animated-Java/animated-java/issues/435)", + "Fixed [#434](https://github.com/Animated-Java/animated-java/issues/434)", + "Fixed [#427](https://github.com/Animated-Java/animated-java/issues/427)", + "Fixed [#421](https://github.com/Animated-Java/animated-java/issues/421)", + "Fixed [#441](https://github.com/Animated-Java/animated-java/issues/441)" + ] + }, + { + "title": "Internal Data Pack Changes and Fixes", + "list": [ + "Updated to the latest version of MC-Build.", + "Added support for `.mcbt` files to the Data Pack compiler.", + "Reduced entity data size by reducing the length of locator and camera transform keys.", + "Removed `global/internal` folder.", + "Expanded all `aj` namespaces to `animated_java` except tags.", + "Renamed `convert_uuid_array_to_string` -> `get_entity_uuid_string`.", + "Removed most transformation of rig entities from `summon`. We now primarily use `set_default_pose` to apply the rig's default pose to reduce code duplication and improve consistency.", + "Fixed global tag in `set_frame` and `apply_frame` functions not using dynamic tag getter.", + "`.mcb` files now use more descriptive variable names.", + "Removed `path_name` in favor of `storage_name` for all rendered rig nodes, and animations.", + "Reduced overhead when generating variant functions.", + "Switched over to using variables for storage names.", + "Renamed `on_summon_commands` -> `on_summon_function`.", + "Renamed `on_remove_commands` -> `on_remove_function`.", + "Renamed `ticking_commands` -> `on_post_tick_function`.", + "Moved some ticking and loading functionality out of individual namespaces and into global.", + "Added `debug` macro to reduce code duplication.", + "Removed an entire indentation level by including the export namespace in the source path instead of as a dir in the .mcb file.", + "Replaced all `arguments[1]` references with new `context` variable." + ] + }, + { + "title": "Other Changes and Fixes", + "list": [ + "Most enum-like values are now using TS enums.", + "Removed config functionality from cameras entirely.", + "Renamed `isCurrentFormat` -> `activeProjectIsBlueprintFormat`.", + "Updated to new modern modding tools. Animated Java is now capable of being disabled, enabled, and uninstalled safely without restarting Blockbench.", + "`AnimatedJava.API` global has been renamed to `AnimatedJava`.", + "Improve handling of Text and Block Display entity NBT when generating root entity passengers.", + "Removed most transformation of rig entities from `summon`. We now primarily use `set_default_pose` to apply the rig's default pose to reduce code duplication and improve consistency.", + "Fixed global tag in `set_frame` and `apply_frame` functions not using dynamic tag getter.", + "`.mcb` files now use more descriptive variable names.", + "`assetManager.getRawAsset` now returns a Buffer.", + "Fixed broken Animated Java bar item condition.", + "Removed `path_name` in favor of `storage_name` for all rendered rig nodes, and animations.", + "Removed `addProjectToRecentProjects` function into custom `load` logic, and let the default `Codec` functionality handle adding the project in `afterSave`.", + "Improve `ModelDataFixerUpper` types, logic flow, and error handling.", + "Add `ModelDataFixerUpper` upgrade function for v1.8.0.", + "Made `TELLRAW.RIG_OUTDATED` runtime-generic." + ] + } + ] } } diff --git a/src/systems/animationRenderer.ts b/src/systems/animationRenderer.ts index 10e919d7..fd3a5450 100644 --- a/src/systems/animationRenderer.ts +++ b/src/systems/animationRenderer.ts @@ -1,18 +1,12 @@ import * as crypto from 'crypto' +import { BONE_INTERPOLATION_ENABLED } from 'src/mods/boneAnimatorMod' import { MAX_PROGRESS, PROGRESS, PROGRESS_DESCRIPTION } from '../interface/dialog/exportProgress' -import { - getKeyframeCommands, - getKeyframeExecuteCondition, - getKeyframeRepeat, - getKeyframeRepeatFrequency, - getKeyframeVariant, -} from '../mods/customKeyframesMod' import { TextDisplay } from '../outliner/textDisplay' import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' -import { sanitizePathName, sanitizeStorageKey } from '../util/minecraftUtil' -import { eulerFromQuaternion, roundToNth } from '../util/misc' -import { AnyRenderedNode, IRenderedRig } from './rigRenderer' +import { sanitizeStorageKey } from '../util/minecraftUtil' +import { eulerFromQuaternion, roundToNth, scrubUndefined } from '../util/misc' +import type { AnyRenderedNode, IRenderedRig } from './rigRenderer' import { sleepForAnimationFrame } from './util' export function correctSceneAngle() { @@ -44,10 +38,10 @@ function getNodeMatrix(node: OutlinerElement, scale: number) { function getDecomposedTransformation(matrix: THREE.Matrix4) { const translation = new THREE.Vector3() - const left_rotation = new THREE.Quaternion() + const leftRotation = new THREE.Quaternion() const scale = new THREE.Vector3() - matrix.decompose(translation, left_rotation, scale) - return { translation, left_rotation, scale } + matrix.decompose(translation, leftRotation, scale) + return { translation, left_rotation: leftRotation, scale } } function threeAxisRotationToTwoAxisRotation(rot: THREE.Quaternion): ArrayVector2 { @@ -69,8 +63,8 @@ export interface INodeTransform { head_rot: ArrayVector2 interpolation?: 'step' | 'pre-post' - commands?: string - commands_execute_condition?: string + function?: string + function_execute_condition?: string } export interface IRenderedFrame { @@ -81,15 +75,13 @@ export interface IRenderedFrame { /** The condition to check before applying variants */ variants_execute_condition?: string /** A mcfunction to run as the root on this frame. (Supports MCB syntax) */ - commands?: string - /** The condition to check before running commands */ - commands_execute_condition?: string + function?: string + /** The condition to check before running the function */ + function_execute_condition?: string } export interface IRenderedAnimation { name: string - /** A sanitized version of {@link IRenderedAnimation.name} that is safe to use in a path in a data pack or resource pack.*/ - path_name: string /** A sanitized version of {@link IRenderedAnimation.name} that is safe to use as a key in a storage object. */ storage_name: string uuid: string @@ -107,11 +99,11 @@ export interface IRenderedAnimation { } let lastAnimation: _Animation | undefined -interface ILastFrameCacheItem { +interface LastFrameCacheItem { matrix: THREE.Matrix4 keyframe?: _Keyframe } -let lastFrameCache = new Map() +let lastFrameCache = new Map() /** * Map of node UUIDs to a map of times to keyframes */ @@ -127,7 +119,7 @@ export function getFrame( time, node_transforms: {}, ...getVariantKeyframe(animation, time), - ...getCommandsKeyframe(animation, time), + ...getFunctionKeyframe(animation, time), } if (lastAnimation !== animation) { @@ -161,66 +153,60 @@ export function getFrame( const prevKeyframe = keyframes.get(time - 0.05) const lastFrame = lastFrameCache.get(uuid) - let matrix: THREE.Matrix4 - let interpolation: INodeTransform['interpolation'] - let commands: string | undefined - let executeCondition: string | undefined - let repeat: boolean | undefined - let repeatFrequency: number | undefined + const transform = {} as INodeTransform switch (node.type) { case 'text_display': case 'item_display': case 'block_display': case 'bone': { - matrix = getNodeMatrix(outlinerNode, node.base_scale) + transform.matrix = getNodeMatrix(outlinerNode, node.base_scale) // Inherit instant interpolation from parent if (node.parent && node.parent !== 'root') { const parentKeyframes = keyframeCache.get(node.parent) const parentKeyframe = parentKeyframes?.get(time) const prevParentKeyframe = parentKeyframes?.get(time - 0.05) if (parentKeyframe?.interpolation === 'step') { - interpolation = 'step' + transform.interpolation = 'step' } else if (prevParentKeyframe?.data_points.length === 2) { - interpolation = 'pre-post' + transform.interpolation = 'pre-post' } } // Only add the frame if the matrix has changed, this is the first frame, or there is an interpolation change. - if (lastFrame && lastFrame.matrix.equals(matrix) && interpolation == undefined) + if ( + lastFrame && + lastFrame.matrix.equals(transform.matrix) && + transform.interpolation == undefined + ) continue // Instant interpolation if (keyframe?.interpolation === 'step') { - interpolation = 'step' + transform.interpolation = 'step' } else if (prevKeyframe?.data_points.length === 2) { - interpolation = 'pre-post' + transform.interpolation = 'pre-post' updatePreview(animation, time + 0.001) const postMatrix = getNodeMatrix(outlinerNode, node.base_scale) - console.warn('pre-post', matrix.equals(postMatrix), matrix, postMatrix) - matrix = postMatrix + transform.matrix = postMatrix updatePreview(animation, time) } - lastFrameCache.set(uuid, { matrix, keyframe }) + lastFrameCache.set(uuid, { matrix: transform.matrix, keyframe }) break } case 'locator': { - matrix = getNodeMatrix(outlinerNode, 1) + transform.matrix = getNodeMatrix(outlinerNode, 1) // // Only add the frame if the matrix has changed, or this is the first frame // if (lastFrame && lastFrame.matrix.equals(matrix)) continue if (keyframe) { - commands = getKeyframeCommands(keyframe) - executeCondition = getKeyframeExecuteCondition(keyframe) - lastFrameCache.set(uuid, { matrix, keyframe }) + transform.function = keyframe.function + transform.function_execute_condition = keyframe.execute_condition + lastFrameCache.set(uuid, { matrix: transform.matrix, keyframe }) } else if (lastFrame?.keyframe) { - repeat = getKeyframeRepeat(lastFrame.keyframe) - repeatFrequency = getKeyframeRepeatFrequency(lastFrame.keyframe) - if ( - repeat && - repeatFrequency && - Math.round(time * 20) % repeatFrequency === 0 - ) { - commands = getKeyframeCommands(lastFrame.keyframe) - executeCondition = getKeyframeExecuteCondition(lastFrame.keyframe) + const repeat = lastFrame.keyframe.repeat + const frequency = lastFrame.keyframe.repeat_frequency + if (repeat && frequency && Math.round(time * 20) % frequency === 0) { + transform.function = lastFrame.keyframe.function + transform.function_execute_condition = lastFrame.keyframe.execute_condition } } // lastFrameCache.set(uuid, { matrix, keyframe }) @@ -228,10 +214,10 @@ export function getFrame( } case 'camera': case 'struct': { - matrix = getNodeMatrix(outlinerNode, 1) + transform.matrix = getNodeMatrix(outlinerNode, 1) // Only add the frame if the matrix has changed, or this is the first frame - if (lastFrame && lastFrame.matrix.equals(matrix)) continue - lastFrameCache.set(uuid, { matrix, keyframe }) + if (lastFrame?.matrix.equals(transform.matrix)) continue + lastFrameCache.set(uuid, { matrix: transform.matrix, keyframe }) break } } @@ -239,24 +225,19 @@ export function getFrame( const pos = new THREE.Vector3() const rot = new THREE.Quaternion() const scale = new THREE.Vector3() - matrix.decompose(pos, rot, scale) - const decomposed = getDecomposedTransformation(matrix) + transform.matrix.decompose(pos, rot, scale) + transform.decomposed = getDecomposedTransformation(transform.matrix) if (node.type === 'locator' || node.type === 'camera') { node.max_distance = Math.max(node.max_distance, pos.length()) } - frame.node_transforms[uuid] = { - matrix, - decomposed, - pos: [pos.x, pos.y, pos.z], - rot: eulerFromQuaternion(rot).toArray(), - scale: [scale.x, scale.y, scale.z], - head_rot: threeAxisRotationToTwoAxisRotation(rot), - interpolation, - commands, - commands_execute_condition: executeCondition?.trim(), - } + transform.pos = [pos.x, pos.y, pos.z] + transform.rot = eulerFromQuaternion(rot).toArray() + transform.scale = [scale.x, scale.y, scale.z] + transform.head_rot = threeAxisRotationToTwoAxisRotation(rot) + + frame.node_transforms[uuid] = transform } return frame @@ -271,33 +252,30 @@ function getVariantKeyframe( const kf = variantKeyframes.find(kf => kf.time === time) if (kf) { // REVIEW - Variant keyframes do not support multiple variants yet. - const uuid = getKeyframeVariant(kf) - if (uuid) { - return { - variants: [uuid], - variants_execute_condition: getKeyframeExecuteCondition(kf)?.trim(), - } + const variant = kf.variant?.uuid + if (variant) { + return scrubUndefined({ + variants: [variant], + variants_execute_condition: kf.execute_condition?.trim(), + }) } } } return {} } -function getCommandsKeyframe( +function getFunctionKeyframe( animation: _Animation, time: number -): Pick { - const commandsKeyframes = animation.animators.effects?.commands as _Keyframe[] - if (commandsKeyframes) { - const kf = commandsKeyframes.find(kf => kf.time === time) +): Pick { + const functionKeyframes = animation.animators.effects?.function as _Keyframe[] + if (functionKeyframes) { + const kf = functionKeyframes.find(kf => kf.time === time) if (kf) { - const commands = getKeyframeCommands(kf)?.trim() - if (commands) { - return { - commands, - commands_execute_condition: getKeyframeExecuteCondition(kf)?.trim(), - } - } + return scrubUndefined({ + function: kf.function?.trim(), + function_execute_condition: kf.execute_condition?.trim(), + }) } } return {} @@ -328,10 +306,9 @@ export function updatePreview(animation: _Animation, time: number) { // Blockbench.dispatchEvent('display_animation_frame') } -export function renderAnimation(animation: _Animation, rig: IRenderedRig) { +function renderAnimation(animation: _Animation, rig: IRenderedRig) { const rendered = { name: animation.name, - path_name: sanitizePathName(animation.name), storage_name: sanitizeStorageKey(animation.name), uuid: animation.uuid, loop_delay: Number(animation.loop_delay) || 0, @@ -350,6 +327,7 @@ export function renderAnimation(animation: _Animation, rig: IRenderedRig) { Object.keys(frame.node_transforms).forEach(n => includedNodes.add(n)) rendered.frames.push(frame) } + rendered.duration = rendered.frames.length rendered.modified_nodes = Object.fromEntries( Array.from(includedNodes).map(uuid => [uuid, rig.nodes[uuid]]) @@ -373,18 +351,18 @@ export function hashAnimations(animations: IRenderedAnimation[]) { hash.update(';' + node.rot.join(';')) hash.update(';' + node.scale.join(';')) node.interpolation && hash.update(';' + node.interpolation) - if (node.commands) hash.update(';' + node.commands) - if (node.commands_execute_condition) - hash.update(';' + node.commands_execute_condition) + if (node.function) hash.update(';' + node.function) + if (node.function_execute_condition) + hash.update(';' + node.function_execute_condition) } if (frame.variants) { hash.update(';' + frame.variants) if (frame.variants_execute_condition) hash.update(';' + frame.variants_execute_condition) } - if (frame.commands) hash.update(';' + frame.commands) - if (frame.commands_execute_condition) - hash.update(';' + frame.commands_execute_condition) + if (frame.function) hash.update(';' + frame.function) + if (frame.function_execute_condition) + hash.update(';' + frame.function_execute_condition) } } return hash.digest('hex') @@ -409,6 +387,8 @@ export async function renderProjectAnimations(project: ModelProject, rig: IRende excludedNodesCache = new Set() nodeCache = new Map() + BONE_INTERPOLATION_ENABLED.set(false) + PROGRESS_DESCRIPTION.set('Rendering Animations...') PROGRESS.set(0) MAX_PROGRESS.set(project.animations.length) @@ -432,6 +412,8 @@ export async function renderProjectAnimations(project: ModelProject, rig: IRende } restoreSceneAngle() + BONE_INTERPOLATION_ENABLED.set(true) + // Restore selected animation if (Mode.selected.id === 'animate' && selectedAnimation) { selectedAnimation.select() diff --git a/src/systems/cleaner.ts b/src/systems/cleaner.ts index 5e8692f8..7bcbb271 100644 --- a/src/systems/cleaner.ts +++ b/src/systems/cleaner.ts @@ -1,5 +1,5 @@ import { isFunctionTagPath } from '../util/fileUtil' -import { IFunctionTag, parseDataPackPath } from '../util/minecraftUtil' +import { type FunctionTagJSON, parseDataPackPath } from '../util/minecraftUtil' import { getExportPaths } from './exporter' import { AJMeta } from './global' import { replacePathPart } from './util' @@ -14,7 +14,7 @@ export async function cleanupExportedFiles() { // displayItemPath, } = getExportPaths() - if (aj.resource_pack_export_mode === 'raw') { + if (aj.resource_pack_export_mode === 'folder') { const assetsMetaPath = PathModule.join(resourcePackFolder, 'assets.ajmeta') const assetsMeta = new AJMeta( assetsMetaPath, @@ -66,7 +66,7 @@ export async function cleanupExportedFiles() { assetsMeta.write() } - if (aj.data_pack_export_mode === 'raw') { + if (aj.data_pack_export_mode === 'folder') { const dataMetaPath = PathModule.join(dataPackFolder, 'data.ajmeta') const dataMeta = new AJMeta( dataMetaPath, @@ -100,7 +100,7 @@ export async function cleanupExportedFiles() { } } // Remove mentions of the export namespace from the file - const content: IFunctionTag = JSON.parse( + const content: FunctionTagJSON = JSON.parse( (await fs.promises.readFile(file)).toString() ) content.values = content.values.filter( diff --git a/src/systems/datapackCompiler/1.20.4/animation.mcb b/src/systems/datapackCompiler/1.20.4/animation.mcb index f32e3b39..126c0a2b 100644 --- a/src/systems/datapackCompiler/1.20.4/animation.mcb +++ b/src/systems/datapackCompiler/1.20.4/animation.mcb @@ -1,950 +1,1110 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt -dir <%export_namespace%> { - function on_load { - IF (use_storage_for_animation) { - REPEAT (animations) as animation { - data remove storage aj.<%export_namespace%>:animations <%animation.storage_name%> - } - <%animationStorage.join('\n')%> +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> + + IF (use_storage_for_animation) { + REPEAT (animations) as animation { + data remove storage <%project_storage%>/animations <%animation.storage_name%> } - <%% - animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) - }) - %%> + <%animationStorage.join('\n')%> } + <%% + animations.forEach(animation => { + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) + }) + %%> +} - function remove_animation_objectives { - <%% - animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) - }) - %%> - tellraw @a <%TELLRAW.UNINSTALL()%> - } +function remove_animation_objectives { + <%% + animations.forEach(animation => { + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) + }) + %%> + tellraw @a <%TELLRAW.UNINSTALL()%> +} - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load + + # Once we have more than 8 animations, calling a function only if at least one animation is playing is more efficient. + IF (animations.length > 8) { + # If no animations are playing, we can skip all animation logic. + # This helps reduce ticking commands for rigs that are idle. + execute \ + unless entity @s[<%animations.map(anim => 'tag=!' + TAGS.ANIMATION_PLAYING(export_namespace, anim.storage_name)).join(',')%>] \ + run block tick_animations { + REPEAT (animations) as animation { + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick + } } - # Tick Playing Animations + } ELSE { REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } + } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run tp @s ~ ~ ~ ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run tp @s ~ ~ ~ ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run tp @s ~ ~ ~ ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} - dir animations { - REPEAT (animations) as animation { - dir <%animation.path_name%> { - # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. - function play { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function stop { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } + tp @s ~ ~ ~ ~ ~ - function pause { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } - function resume { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + execute at @s on passengers run tp @s ~ ~ ~ ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} - function next_frame { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } +dir animations { + REPEAT (animations) as animation { + dir <%animation.storage_name%> { + function play { + debug assert executed_as_root_entity - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/set_frame with storage aj:temp args - } + function *<%export_namespace%>/animations/pause_all - function apply_frame { - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/apply_frame with storage aj:temp args - } + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } + + function stop { + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } - function tween { - # Attempts to smoothly transition from the currently playing animation into this one. - #ARGS: {duration: int, to_frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + function pause { + debug assert executed_as_root_entity + + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function resume { + debug assert executed_as_root_entity + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function next_frame { + debug assert executed_as_root_entity + + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/set_frame with storage <%temp_storage%> args + } + + function apply_frame { + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + } + + function tween { + # Attempts to smoothly transition from the currently playing animation into this one. + #ARGS: {duration: int, to_frame: int} + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) + + scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/apply_frame {frame: 0} + $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + } + + dir zzz { + function on_tick { + # Tweening logic + scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ + data modify entity @s interpolation_duration set value <%interpolation_duration%> + # Animation logic + IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { + # Makes sure function keyframes in the last frame of the animation are activated. + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ + block function_keyframe_loop_patch { + function ./apply_frame {frame: <%animation.duration-1%>} + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + } + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run \ + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + function ./apply_frame with storage <%temp_storage%> args + IF (animation.loop_mode === 'loop') { + # Loop the animation back to the start once it reaches the last frame. + # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-2 + animation.loop_delay%>.. \ + run return run \ + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + } ELSE IF (animation.loop_mode === 'hold') { + # Pause the animation at the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%>.. \ + run return run \ + function ../pause + } ELSE IF (animation.loop_mode === 'once') { + # Stop the animation once it reaches the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%> \ + run return run \ + function ../stop } - function *<%export_namespace%>/animations/pause_all - - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) - - scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/apply_frame {frame: 0} - $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } - dir zzz { - function on_tick { - # Tweening logic - scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ - data modify entity @s interpolation_duration set value <%interpolation_duration%> - # Animation logic - IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { - # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ - block commands_keyframe_loop_patch { - function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - } - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - function ./apply_frame with storage aj:temp args - IF (animation.loop_mode === 'loop') { - # Loop the animation back to the start once it reaches the last frame. - # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-2 + animation.loop_delay%>.. \ - run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> - } ELSE IF (animation.loop_mode === 'hold') { - # Pause the animation at the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%>.. \ - run return run \ - function ../pause - } ELSE IF (animation.loop_mode === 'once') { - # Stop the animation once it reaches the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%> \ - run return run \ - function ../stop - } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + IF (use_storage_for_animation) { + function set_frame { + #ARGS: {frame: int} + $function ./apply_frame {frame: $(frame)} + execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 + return 1 } - IF (use_storage_for_animation) { - function set_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } + function apply_frame { + #ARGS: {frame: int} + REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { + IF (BONE_TYPES.includes(node.type)) { + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ + data modify entity @s {} merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> + } ELSE IF (node.type === 'locator' || node.type === 'camera') { + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + $data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ - data modify entity @s {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } + IF (animation.frames.some(anim => anim.variant)) { + $execute \ + if data storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant \ + unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ + run { with storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant + #ARGS: {name: string, condition: string} + $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply } - return 1 - } - } ELSE { - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - execute on passengers run \ - data modify entity @s[tag=aj.global.data] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - return 1 - } - # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. - dir frames { - <%% - // A record of node uuid to INodeTransform. - // Keeps track of the last time a bone was updated. - // Only used for step keyframe interpolation. - let hasCommands = false - const lastActiveFrame = {} - const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); - for (const [frameIndex, frame] of animation.frames.entries()) { - const to_merge = {cameras: {}, locators: {}} - let frameFunc = ``; - for (const node of modifiedNodes) { - const transform = frame.node_transforms[node.uuid] - // Skip if the node doesn't have a transform for this frame. - if (!transform) continue - switch (node.type) { - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - const lastFrame = lastActiveFrame[node.uuid] - const isStepInterpolation = !!(lastFrame?.interpolation === 'step') - lastActiveFrame[node.uuid] = transform - - if (transform.interpolation === 'pre-post' || isStepInterpolation) { + return 1 + } + } ELSE { + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + execute on passengers if entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] run \ + data modify entity @s start_interpolation set value -1 + + return 1 + } + + function apply_frame { + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + return 1 + } + + # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. + dir frames { + <%% + // A record of node uuid to INodeTransform. + // Keeps track of the last time a bone was updated. + // Only used for step keyframe interpolation. + let hasFunction = false + const lastActiveFrame = {} + const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); + for (const [frameIndex, frame] of animation.frames.entries()) { + const to_merge = {cameras: {}, locators: {}} + let frameFunc = ``; + for (const node of modifiedNodes) { + const transform = frame.node_transforms[node.uuid] + // Skip if the node doesn't have a transform for this frame. + if (!transform) continue + switch (node.type) { + case 'bone': + case 'text_display': + case 'item_display': + case 'block_display': { + const lastFrame = lastActiveFrame[node.uuid] + const isStepInterpolation = !!(lastFrame?.interpolation === 'step') + lastActiveFrame[node.uuid] = transform + + if (transform.interpolation === 'pre-post' || isStepInterpolation) { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: ${isStepInterpolation ? -1 : 0},` + + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` + + `}` + } else { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: 0,` + + `interpolation_duration: ${interpolation_duration}` + + `}` + } + ;hasFunction = true + break + } + case 'locator': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.locators[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; + } + + if (transform.function) { + if (node.config?.use_entity) { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: ${isStepInterpolation ? -1 : 0},` - + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` - + `}` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `tp @s ~ ~ ~ ~ ~\n` + + `${transform.function}` + + `\n}` } else { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: 0,` - + `interpolation_duration: ${interpolation_duration}` - + `}` + `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `${transform.function}` + + `\n}` } - ;hasCommands = true - break } - case 'locator': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.locators[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - - if (transform.commands) { - if (node.config?.use_entity) { - frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `tp @s ~ ~ ~ ~ ~\n` - + `${transform.commands}` - + `\n}` - } else { - frameFunc += - `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `${transform.commands}` - + `\n}` - } - } - break - } - case 'camera': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.cameras[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - ;break + break + } + case 'camera': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.cameras[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; } + ;break } } + } - if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { - frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` - ;hasCommands = true + if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { + frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` + if (!auto_update_rig_orientation) { + frameFunc += `\nfunction ./on_tick/transform_floating_entities` } + hasFunction = true + } - if (frame.variants?.length) { - const variant = rig.variants[frame.variants[0]] - if (!variant) { - throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) - } - const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` - ;hasCommands = true + if (frame.variants?.length) { + const variant = rig.variants[frame.variants[0]] + if (!variant) { + throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) } + const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` + ;hasFunction = true + } - // Root commands keyframes. - if (frame.commands) { - const execute_condition = frame.commands_execute_condition ? frame.commands_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_commands%NEWLINE_PATCH%{\n${frame.commands}\n}` - ;hasCommands = true - } - ;if (frameFunc.length > 0) { - console.log('frameFunc', frameFunc) - frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` - emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) - } + // Root function keyframes. + if (frame.function) { + const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}` + ;hasFunction = true } - %%> - } + ;if (frameFunc.length > 0) { + frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` + emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) + } + } + %%> } } } } - function pause_all { - # Pauses all animations - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - } } + function pause_all { + # Pauses all animations + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} - # frame is ignored unless animation is specified. - - data modify storage aj:temp args set value {variant:'', animation:'', frame: 0} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + REPEAT (animations) as animation { + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } + } +} - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } +function summon { + #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} + # frame is ignored unless animation is specified. + + data modify storage <%temp_storage%> args set value {variant:'', animation:'', frame: 0} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @e[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Animation Argument - # If the animation argument is provided, attempt to apply the animation. - execute if data storage aj:temp args.animation run block animation_arg/process { with storage aj:temp args - # If the animation argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{animation:''}} run return run block if_empty { - tellraw @a <%TELLRAW.ANIMATION_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Automatically set the frame argument to 0 if the frame argument is not provided. - # Takes advantage of `store result score` setting the score to 0 if the command fails. + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { execute \ - store result storage aj:temp args.frame int 1 \ - store result score #frame <%OBJECTIVES.I()%> \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ run \ - data get storage aj:temp args.frame - # If the frame argument is negative, return an error. - execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { - # Tell the user that the frame argument cannot be negative. - tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> - function *<%export_namespace%>/remove/this + function *global/gu/get_entity_uuid_string + + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } + } + + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Attempt to apply the animation frame. - execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage aj:temp args - # Make sure we're only applying transforms when setting the summon pose. - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 - # If the set_frame function fails, the animation doesn't exist, so we return an error. + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. return fail } - # If the set_frame function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { with storage aj:temp args - # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. - $tellraw @a <%TELLRAW.INVALID_ANIMATION('$(animation)', animations)%> - function *<%export_namespace%>/remove/this + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 + } + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. - execute if data storage aj:temp {args:{start_animation: true}} run block start_animation { with storage aj:temp args - $function *<%export_namespace%>/animations/$(animation)/resume - } + function *<%export_namespace%>/set_default_pose + + # Animation Argument + # If the animation argument is provided, attempt to apply the animation. + execute if data storage <%temp_storage%> args.animation run block animation_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the animation argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{animation:''}} run return run block if_empty { + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('animation')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Automatically set the frame argument to 0 if the frame argument is not provided. + # Takes advantage of `store result score` setting the score to 0 if the command fails. + execute \ + store result storage <%temp_storage%> args.frame int 1 \ + store result score #frame <%OBJECTIVES.I()%> \ + run \ + data get storage <%temp_storage%> args.frame + # If the frame argument is negative, return an error. + execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { + # Tell the user that the frame argument cannot be negative. + tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the animation frame. + execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage <%temp_storage%> args + # Make sure we're only applying transforms when setting the summon pose. + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage <%temp_storage%> args + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 + # If the set_frame function fails, the animation doesn't exist, so we return an error. + return fail + } + # If the set_frame function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { + # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. + tellraw @a <%TELLRAW.INVALID_ANIMATION(animations)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. + execute if data storage <%temp_storage%> {args:{start_animation: true}} run block start_animation { with storage <%temp_storage%> args + $function *<%export_namespace%>/animations/$(animation)/resume + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 } - } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run tp @s ~ ~ ~ ~ ~ + + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 + } + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} + +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.tag.CustomModelData set value 1 - } ELSE { - data modify entity @s item.tag.CustomModelData set value <%variant.models[node.uuid].custom_model_data%> - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.tag.CustomModelData set value 1 + } ELSE { + data modify entity @s item.tag.CustomModelData set value <%variant.models[bone.uuid].custom_model_data%> } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function apply_default_pose { - # Changes the pose of the rig to the the default pose with interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +function apply_default_pose { + # Changes the pose of the rig to the the default pose with interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: 0 \ + } + } +} + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ } - } } } diff --git a/src/systems/datapackCompiler/1.20.4/core.mcb b/src/systems/datapackCompiler/1.20.4/core.mcb deleted file mode 100644 index e0b1e6c1..00000000 --- a/src/systems/datapackCompiler/1.20.4/core.mcb +++ /dev/null @@ -1,129 +0,0 @@ -dir global { - - function on_load minecraft:load { - # Initialize Scoreboards - scoreboard objectives add <%OBJECTIVES.I()%> dummy - scoreboard objectives add <%OBJECTIVES.ID()%> dummy - scoreboard objectives add <%OBJECTIVES.IS_RIG_LOADED()%> dummy - scoreboard objectives add <%OBJECTIVES.TWEEN_DURATION()%> dummy - - scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 0 - - # Initialize Storage - data modify storage aj:temp args set value {} - IF (show_outdated_warning) { - # Initialize Rigs - scoreboard players reset * <%OBJECTIVES.IS_RIG_LOADED()%> - } - function #*global/on_load - } - - IF (!is_static || show_outdated_warning) { - function on_tick minecraft:tick { - execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function #*global/root/on_tick - } - } - - tag functions on_load { - *<%export_namespace%>/on_load - } - - dir root { - # TODO Maybe instead of merging tags, I should just generate it from the .ajmeta data every time a rig is exported? - # That way I can also check if the rig's files still exist, and remove tag entries if they don't. - # An entry will be added for each exported rig. - tag functions on_tick { - *<%export_namespace%>/root/on_tick - } - # An entry will be added for each exported rig. - IF (show_outdated_warning) { - tag functions on_load { - *<%export_namespace%>/root/on_load - } - } - } - - IF (show_function_errors) { - dir errors { - function function_not_executed_as_root_entity { - #ARGS: {export_namespace: string, function_path: string} - $tellraw @a <%TELLRAW.FUNCTION_NOT_EXECUTED_AS_ROOT_ERROR('$(function_path)')%> - } - } - } - - dir remove { - # Removes all instances of all rigs from the world. - function everything { - kill @e[tag=<%TAGS.GLOBAL_ENTITY()%>] - } - } - - dir internal { - # Thanks Gibbsly for this code! https://github.com/gibbsly/gu - dir gu { - function load minecraft:load { - scoreboard players set 256 <%OBJECTIVES.I()%> 256 - data modify storage aj:uuid main.hex_chars set value \ - <%JSON.stringify([...Array(0x100).keys()].map(v => {const x = v.toString(16); return x.length > 1 ? x : '0' + x}))%> - } - - function convert_uuid_array_to_string { - data modify storage aj:uuid temp set value {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,a:0,b:0,c:0,d:0,e:0,f:0} - data modify storage aj:uuid main.in set from entity @s UUID - - execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage aj:uuid main.in[0] - execute store result storage aj:uuid temp.0 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.1 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.2 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.3 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - - execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage aj:uuid main.in[1] - execute store result storage aj:uuid temp.4 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.5 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.6 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.7 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - - execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage aj:uuid main.in[2] - execute store result storage aj:uuid temp.8 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.9 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.a int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.b int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - - execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage aj:uuid main.in[3] - execute store result storage aj:uuid temp.c int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.d int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.e int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> - execute store result storage aj:uuid temp.f int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> - - block { with storage aj:uuid temp - REPEAT (0, 15) as i { - $data modify storage aj:uuid temp.<%i.toString(16)%> set from storage aj:uuid main.hex_chars[$(<%i.toString(16)%>)] - } - } - - block { with storage aj:uuid temp - $data modify storage aj:uuid main.out set value "$(3)$(2)$(1)$(0)-$(7)$(6)-$(5)$(4)-$(b)$(a)-$(9)$(8)$(f)$(e)$(d)$(c)" - } - } - } - } -} - -dir <%export_namespace%> { - function on_load { - function *<%export_namespace%>/invalid_version_warning - } - - function invalid_version_warning { - tellraw @a <%TELLRAW.INVALID_VERSION()%> - } -} diff --git a/src/systems/datapackCompiler/1.20.4/core.mcbt b/src/systems/datapackCompiler/1.20.4/core.mcbt deleted file mode 100644 index 10444f3a..00000000 --- a/src/systems/datapackCompiler/1.20.4/core.mcbt +++ /dev/null @@ -1,5 +0,0 @@ -template throw { - with missing_root_context:literal { - $tellraw @a <%TELLRAW.FUNCTION_NOT_EXECUTED_AS_ROOT_ERROR(arguments[1].functions.at(-1))%> - } -} \ No newline at end of file diff --git a/src/systems/datapackCompiler/1.20.4/global.mcb b/src/systems/datapackCompiler/1.20.4/global.mcb new file mode 100644 index 00000000..1f6d833c --- /dev/null +++ b/src/systems/datapackCompiler/1.20.4/global.mcb @@ -0,0 +1,230 @@ +import ./global.mcbt + +dir global { + function on_load minecraft:load { + # Initialize Scoreboards + scoreboard objectives add <%OBJECTIVES.I()%> dummy + scoreboard objectives add <%OBJECTIVES.ID()%> dummy + scoreboard objectives add <%OBJECTIVES.IS_RIG_LOADED()%> dummy + scoreboard objectives add <%OBJECTIVES.TWEEN_DURATION()%> dummy + + scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 0 + + # Purposefully load gu first, as other functions depend on it. + function *global/gu/on_load + IF (debug_mode) { + scoreboard players reset * <%OBJECTIVES.IS_RIG_LOADED()%> + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_load + } + function #*global/on_load + } + + IF (debug_mode) { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_tick + } + } ELSE { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function #*global/root/on_tick + } + } + + tag functions on_load { + *<%export_namespace%>/on_load + } + + dir root { + tag functions on_tick { + *<%export_namespace%>/root/on_tick + } + + IF (debug_mode) { + function on_tick { + execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function *global/root/on_load + function #*global/root/on_tick + } + + function on_load { + # Check if the rig is outdated by comparing the function's rig_hash of the rig to the rig_hash stored in the entity data. + execute if function animated_java:global/util/is_rig_outdated run block outdated_warning/modify_rig { + data remove storage <%temp_storage%> args + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> args.uuid set from storage <%gu_storage%> out + # Get position + execute store result storage <%temp_storage%> args.x double 1 run data get entity @s Pos[0] 1 + execute store result storage <%temp_storage%> args.y double 1 run data get entity @s Pos[1] 1 + execute store result storage <%temp_storage%> args.z double 1 run data get entity @s Pos[2] 1 + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run { + # Get export namespace + data modify storage <%temp_storage%> args.export_namespace set from entity @s data.export_namespace + # Overwrite the rig hash so the warning doesn't show again. + data modify entity @s data.rig_hash set value '<%rig_hash%>' + } + block print { with storage <%temp_storage%> args + $tellraw @a <%TELLRAW.RIG_OUTDATED()%> + } + execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} + summon minecraft:text_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', \ + '<%TAGS.NEW()%>' \ + ], \ + text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ + alignment: 'center', \ + billboard: 'vertical', \ + shadow: true, \ + transformation:{ \ + translation:[0f,<%boundingBox[1]/16%>f,0f], \ + left_rotation:[0f,0f,0f,1f], \ + right_rotation:[0f,0f,0f,1f], \ + scale:[1f,1f,1f] \ + } \ + } + + ride @e[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + limit=1, distance=..1 \ + ] mount @s + + tag @e[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + limit=1, distance=..1 \ + ] remove <%TAGS.NEW()%> + } + scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 + } + } + } + + dir remove { + # Removes every entity related to Animated Java. + function everything { + kill @e[tag=<%TAGS.GLOBAL_ENTITY()%>] + } + + function entity_stack { + execute on passengers if entity @s[type=!player] run function ^0 + kill @s + } + + function entity_stack_by_uuid { + #ARGS: {uuid: string} + $execute as $(uuid) run function ./entity_stack + } + + # Removes locators and cameras owned by the rig, even if they're not included in the currently loaded export. + function outdated_rig { + # Assert that the function is being executed as a root entity. + debug assert executed_as_root_entity + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + data remove storage <%temp_storage%> args + data remove storage <%temp_storage%> uuids + data modify storage <%temp_storage%> uuids set from entity @s data.uuids + execute store result score #aj.length <%OBJECTIVES.I()%> run data get storage <%temp_storage%> uuids + + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run block loop_over_uuids { + data modify storage <%temp_storage%> args.current_uuid set from storage <%temp_storage%> uuids[-1].uuid + data remove storage <%temp_storage%> uuids[-1] + function ./entity_stack_by_uuid with storage <%temp_storage%> args + + scoreboard players remove #aj.length <%OBJECTIVES.I()%> 1 + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run function ^0 + } + } + + function ./entity_stack + } + } + + dir util { + # Recurses through the passengers of the context entity and appends their UUIDs to `storage <%temp_storage%> uuids`. + function get_entity_stack_uuids { + data remove storage <%temp_storage%> uuids + execute on passengers run { + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> uuids append from storage <%gu_storage%> out + execute on passengers run function ^0 + } + } + # Returns 1 if the rig is outdated, else returns 0. + function is_rig_outdated { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run return run { with entity @s data + # REVIEW - The replace here is a bit hacky. + # Because this is a core function, I need to create a generic version of + # project_storage that uses a macro to get the export namespace instead of hardcoding it. + $execute if data storage <%project_storage.replace(export_namespace, '$(export_namespace)')%> {rig_hash:'$(rig_hash)'} run return 0 + return 1 + } + } + } + + # Thanks Gibbsly for this code! https://github.com/gibbsly/gu + dir gu { + function on_load { + scoreboard players set 256 <%OBJECTIVES.I()%> 256 + data modify storage <%gu_storage%> hex_chars set value \ + <%JSON.stringify([...Array(0x100).keys()].map(v => {const x = v.toString(16); return x.length > 1 ? x : '0' + x}))%> + } + + function get_entity_uuid_string { + data modify storage <%gu_storage%> temp set value {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,a:0,b:0,c:0,d:0,e:0,f:0} + data modify storage <%gu_storage%> in set from entity @s UUID + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[0] + execute store result storage <%gu_storage%> temp.0 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.1 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.2 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.3 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[1] + execute store result storage <%gu_storage%> temp.4 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.5 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.6 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.7 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[2] + execute store result storage <%gu_storage%> temp.8 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.9 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.a int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.b int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[3] + execute store result storage <%gu_storage%> temp.c int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.d int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.e int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.f int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + block { with storage <%gu_storage%> temp + REPEAT (0, 15) as i { + $data modify storage <%gu_storage%> temp.<%i.toString(16)%> set from storage <%gu_storage%> hex_chars[$(<%i.toString(16)%>)] + } + } + + block { with storage <%gu_storage%> temp + $data modify storage <%gu_storage%> out set value "$(3)$(2)$(1)$(0)-$(7)$(6)-$(5)$(4)-$(b)$(a)-$(9)$(8)$(f)$(e)$(d)$(c)" + } + } + } +} + +dir <%export_namespace%> { + function on_load { + function *<%export_namespace%>/invalid_version_warning + } + + function invalid_version_warning { + tellraw @a <%TELLRAW.INVALID_VERSION()%> + } +} diff --git a/src/systems/datapackCompiler/1.20.4/global.mcbt b/src/systems/datapackCompiler/1.20.4/global.mcbt new file mode 100644 index 00000000..533779a3 --- /dev/null +++ b/src/systems/datapackCompiler/1.20.4/global.mcbt @@ -0,0 +1,8 @@ +template debug { + with assert:literal executed_as_root_entity:literal { + IF (debug_mode) { + execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ + tellraw @a <%TELLRAW.FUNCTION_NOT_EXECUTED_AS_ROOT_ERROR(context.functions.at(-1))%> + } + } +} \ No newline at end of file diff --git a/src/systems/datapackCompiler/1.20.4/static.mcb b/src/systems/datapackCompiler/1.20.4/static.mcb index a68f25e6..ace84064 100644 --- a/src/systems/datapackCompiler/1.20.4/static.mcb +++ b/src/systems/datapackCompiler/1.20.4/static.mcb @@ -1,488 +1,671 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> +} -dir <%export_namespace%> { - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run tp @s ~ ~ ~ ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run tp @s ~ ~ ~ ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run tp @s ~ ~ ~ ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} + +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string}} - - data modify storage aj:temp args set value {variant:''} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + tp @s ~ ~ ~ ~ ~ + + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } + execute at @s on passengers run tp @s ~ ~ ~ ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} + +function summon { + #Args: {args:{variant: string}} - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify storage <%temp_storage%> args set value {variant:''} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) + + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @e[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + function *global/gu/get_entity_uuid_string - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } } - } - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. + return fail + } + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail + + function *<%export_namespace%>/set_default_pose + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run tp @s ~ ~ ~ ~ ~ - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> + + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} + +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } + } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } + + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.tag.CustomModelData set value 1 - } ELSE { - data modify entity @s item.tag.CustomModelData set value <%variant.models[node.uuid].custom_model_data%> - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.tag.CustomModelData set value 1 + } ELSE { + data modify entity @s item.tag.CustomModelData set value <%variant.models[bone.uuid].custom_model_data%> } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } } + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ + } + } +} diff --git a/src/systems/datapackCompiler/1.20.5/animation.mcb b/src/systems/datapackCompiler/1.20.5/animation.mcb index 737c565c..1992824a 100644 --- a/src/systems/datapackCompiler/1.20.5/animation.mcb +++ b/src/systems/datapackCompiler/1.20.5/animation.mcb @@ -1,950 +1,1110 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt -dir <%export_namespace%> { - function on_load { - IF (use_storage_for_animation) { - REPEAT (animations) as animation { - data remove storage aj.<%export_namespace%>:animations <%animation.storage_name%> - } - <%animationStorage.join('\n')%> +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> + + IF (use_storage_for_animation) { + REPEAT (animations) as animation { + data remove storage <%project_storage%>/animations <%animation.storage_name%> } - <%% - animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) - }) - %%> + <%animationStorage.join('\n')%> } + <%% + animations.forEach(animation => { + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) + }) + %%> +} - function remove_animation_objectives { - <%% - animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) - }) - %%> - tellraw @a <%TELLRAW.UNINSTALL()%> - } +function remove_animation_objectives { + <%% + animations.forEach(animation => { + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) + }) + %%> + tellraw @a <%TELLRAW.UNINSTALL()%> +} - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load + + # Once we have more than 8 animations, calling a function only if at least one animation is playing is more efficient. + IF (animations.length > 8) { + # If no animations are playing, we can skip all animation logic. + # This helps reduce ticking commands for rigs that are idle. + execute \ + unless entity @s[<%animations.map(anim => 'tag=!' + TAGS.ANIMATION_PLAYING(export_namespace, anim.storage_name)).join(',')%>] \ + run block tick_animations { + REPEAT (animations) as animation { + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick + } } - # Tick Playing Animations + } ELSE { REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } + } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run tp @s ~ ~ ~ ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run tp @s ~ ~ ~ ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run tp @s ~ ~ ~ ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} - dir animations { - REPEAT (animations) as animation { - dir <%animation.path_name%> { - # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. - function play { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function stop { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } + tp @s ~ ~ ~ ~ ~ - function pause { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } - function resume { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + execute at @s on passengers run tp @s ~ ~ ~ ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} - function next_frame { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } +dir animations { + REPEAT (animations) as animation { + dir <%animation.storage_name%> { + function play { + debug assert executed_as_root_entity - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/set_frame with storage aj:temp args - } + function *<%export_namespace%>/animations/pause_all - function apply_frame { - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/apply_frame with storage aj:temp args - } + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } + + function stop { + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } - function tween { - # Attempts to smoothly transition from the currently playing animation into this one. - #ARGS: {duration: int, to_frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + function pause { + debug assert executed_as_root_entity + + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function resume { + debug assert executed_as_root_entity + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function next_frame { + debug assert executed_as_root_entity + + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/set_frame with storage <%temp_storage%> args + } + + function apply_frame { + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + } + + function tween { + # Attempts to smoothly transition from the currently playing animation into this one. + #ARGS: {duration: int, to_frame: int} + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) + + scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/apply_frame {frame: 0} + $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + } + + dir zzz { + function on_tick { + # Tweening logic + scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ + data modify entity @s interpolation_duration set value <%interpolation_duration%> + # Animation logic + IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { + # Makes sure function keyframes in the last frame of the animation are activated. + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ + block function_keyframe_loop_patch { + function ./apply_frame {frame: <%animation.duration-1%>} + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + } + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run \ + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + function ./apply_frame with storage <%temp_storage%> args + IF (animation.loop_mode === 'loop') { + # Loop the animation back to the start once it reaches the last frame. + # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-2 + animation.loop_delay%>.. \ + run return run \ + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + } ELSE IF (animation.loop_mode === 'hold') { + # Pause the animation at the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%>.. \ + run return run \ + function ../pause + } ELSE IF (animation.loop_mode === 'once') { + # Stop the animation once it reaches the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%> \ + run return run \ + function ../stop } - function *<%export_namespace%>/animations/pause_all - - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) - - scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/apply_frame {frame: 0} - $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } - dir zzz { - function on_tick { - # Tweening logic - scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ - data modify entity @s interpolation_duration set value <%interpolation_duration%> - # Animation logic - IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { - # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ - block commands_keyframe_loop_patch { - function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - } - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - function ./apply_frame with storage aj:temp args - IF (animation.loop_mode === 'loop') { - # Loop the animation back to the start once it reaches the last frame. - # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-2 + animation.loop_delay%>.. \ - run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> - } ELSE IF (animation.loop_mode === 'hold') { - # Pause the animation at the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%>.. \ - run return run \ - function ../pause - } ELSE IF (animation.loop_mode === 'once') { - # Stop the animation once it reaches the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%> \ - run return run \ - function ../stop - } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + IF (use_storage_for_animation) { + function set_frame { + #ARGS: {frame: int} + $function ./apply_frame {frame: $(frame)} + execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 + return 1 } - IF (use_storage_for_animation) { - function set_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } + function apply_frame { + #ARGS: {frame: int} + REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { + IF (BONE_TYPES.includes(node.type)) { + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ + data modify entity @s {} merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> + } ELSE IF (node.type === 'locator' || node.type === 'camera') { + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + $data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ - data modify entity @s {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } + IF (animation.frames.some(anim => anim.variant)) { + $execute \ + if data storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant \ + unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ + run { with storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant + #ARGS: {name: string, condition: string} + $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply } - return 1 - } - } ELSE { - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - execute on passengers run \ - data modify entity @s[tag=aj.global.data] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - return 1 - } - # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. - dir frames { - <%% - // A record of node uuid to INodeTransform. - // Keeps track of the last time a bone was updated. - // Only used for step keyframe interpolation. - let hasCommands = false - const lastActiveFrame = {} - const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); - for (const [frameIndex, frame] of animation.frames.entries()) { - const to_merge = {cameras: {}, locators: {}} - let frameFunc = ``; - for (const node of modifiedNodes) { - const transform = frame.node_transforms[node.uuid] - // Skip if the node doesn't have a transform for this frame. - if (!transform) continue - switch (node.type) { - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - const lastFrame = lastActiveFrame[node.uuid] - const isStepInterpolation = !!(lastFrame?.interpolation === 'step') - lastActiveFrame[node.uuid] = transform - - if (transform.interpolation === 'pre-post' || isStepInterpolation) { + return 1 + } + } ELSE { + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + execute on passengers if entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] run \ + data modify entity @s start_interpolation set value -1 + + return 1 + } + + function apply_frame { + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + return 1 + } + + # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. + dir frames { + <%% + // A record of node uuid to INodeTransform. + // Keeps track of the last time a bone was updated. + // Only used for step keyframe interpolation. + let hasFunction = false + const lastActiveFrame = {} + const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); + for (const [frameIndex, frame] of animation.frames.entries()) { + const to_merge = {cameras: {}, locators: {}} + let frameFunc = ``; + for (const node of modifiedNodes) { + const transform = frame.node_transforms[node.uuid] + // Skip if the node doesn't have a transform for this frame. + if (!transform) continue + switch (node.type) { + case 'bone': + case 'text_display': + case 'item_display': + case 'block_display': { + const lastFrame = lastActiveFrame[node.uuid] + const isStepInterpolation = !!(lastFrame?.interpolation === 'step') + lastActiveFrame[node.uuid] = transform + + if (transform.interpolation === 'pre-post' || isStepInterpolation) { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: ${isStepInterpolation ? -1 : 0},` + + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` + + `}` + } else { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: 0,` + + `interpolation_duration: ${interpolation_duration}` + + `}` + } + ;hasFunction = true + break + } + case 'locator': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.locators[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; + } + + if (transform.function) { + if (node.config?.use_entity) { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: ${isStepInterpolation ? -1 : 0},` - + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` - + `}` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `tp @s ~ ~ ~ ~ ~\n` + + `${transform.function}` + + `\n}` } else { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: 0,` - + `interpolation_duration: ${interpolation_duration}` - + `}` + `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `${transform.function}` + + `\n}` } - ;hasCommands = true - break } - case 'locator': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.locators[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - - if (transform.commands) { - if (node.config?.use_entity) { - frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `tp @s ~ ~ ~ ~ ~\n` - + `${transform.commands}` - + `\n}` - } else { - frameFunc += - `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `${transform.commands}` - + `\n}` - } - } - break - } - case 'camera': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.cameras[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - ;break + break + } + case 'camera': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.cameras[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; } + ;break } } + } - if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { - frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` - ;hasCommands = true + if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { + frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` + if (!auto_update_rig_orientation) { + frameFunc += `\nfunction ./on_tick/transform_floating_entities` } + hasFunction = true + } - if (frame.variants?.length) { - const variant = rig.variants[frame.variants[0]] - if (!variant) { - throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) - } - const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` - ;hasCommands = true + if (frame.variants?.length) { + const variant = rig.variants[frame.variants[0]] + if (!variant) { + throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) } + const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` + ;hasFunction = true + } - // Root commands keyframes. - if (frame.commands) { - const execute_condition = frame.commands_execute_condition ? frame.commands_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_commands%NEWLINE_PATCH%{\n${frame.commands}\n}` - ;hasCommands = true - } - ;if (frameFunc.length > 0) { - console.log('frameFunc', frameFunc) - frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` - emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) - } + // Root function keyframes. + if (frame.function) { + const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}` + ;hasFunction = true } - %%> - } + ;if (frameFunc.length > 0) { + frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` + emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) + } + } + %%> } } } } - function pause_all { - # Pauses all animations - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - } } + function pause_all { + # Pauses all animations + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} - # frame is ignored unless animation is specified. - - data modify storage aj:temp args set value {variant:'', animation:'', frame: 0} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + REPEAT (animations) as animation { + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } + } +} - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } +function summon { + #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} + # frame is ignored unless animation is specified. + + data modify storage <%temp_storage%> args set value {variant:'', animation:'', frame: 0} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @e[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Animation Argument - # If the animation argument is provided, attempt to apply the animation. - execute if data storage aj:temp args.animation run block animation_arg/process { with storage aj:temp args - # If the animation argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{animation:''}} run return run block if_empty { - tellraw @a <%TELLRAW.ANIMATION_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Automatically set the frame argument to 0 if the frame argument is not provided. - # Takes advantage of `store result score` setting the score to 0 if the command fails. + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { execute \ - store result storage aj:temp args.frame int 1 \ - store result score #frame <%OBJECTIVES.I()%> \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ run \ - data get storage aj:temp args.frame - # If the frame argument is negative, return an error. - execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { - # Tell the user that the frame argument cannot be negative. - tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> - function *<%export_namespace%>/remove/this + function *global/gu/get_entity_uuid_string + + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } + } + + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Attempt to apply the animation frame. - execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage aj:temp args - # Make sure we're only applying transforms when setting the summon pose. - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 - # If the set_frame function fails, the animation doesn't exist, so we return an error. + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. return fail } - # If the set_frame function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { with storage aj:temp args - # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. - $tellraw @a <%TELLRAW.INVALID_ANIMATION('$(animation)', animations)%> - function *<%export_namespace%>/remove/this + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 + } + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. - execute if data storage aj:temp {args:{start_animation: true}} run block start_animation { with storage aj:temp args - $function *<%export_namespace%>/animations/$(animation)/resume - } + function *<%export_namespace%>/set_default_pose + + # Animation Argument + # If the animation argument is provided, attempt to apply the animation. + execute if data storage <%temp_storage%> args.animation run block animation_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the animation argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{animation:''}} run return run block if_empty { + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('animation')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Automatically set the frame argument to 0 if the frame argument is not provided. + # Takes advantage of `store result score` setting the score to 0 if the command fails. + execute \ + store result storage <%temp_storage%> args.frame int 1 \ + store result score #frame <%OBJECTIVES.I()%> \ + run \ + data get storage <%temp_storage%> args.frame + # If the frame argument is negative, return an error. + execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { + # Tell the user that the frame argument cannot be negative. + tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the animation frame. + execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage <%temp_storage%> args + # Make sure we're only applying transforms when setting the summon pose. + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage <%temp_storage%> args + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 + # If the set_frame function fails, the animation doesn't exist, so we return an error. + return fail + } + # If the set_frame function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { + # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. + tellraw @a <%TELLRAW.INVALID_ANIMATION(animations)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. + execute if data storage <%temp_storage%> {args:{start_animation: true}} run block start_animation { with storage <%temp_storage%> args + $function *<%export_namespace%>/animations/$(animation)/resume + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 } - } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run tp @s ~ ~ ~ ~ ~ + + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 + } + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} + +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components.minecraft:custom_model_data set value 1 - } ELSE { - data modify entity @s item.components.minecraft:custom_model_data set value <%variant.models[node.uuid].custom_model_data%> - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:custom_model_data" set value 1 + } ELSE { + data modify entity @s item.components."minecraft:custom_model_data" set value <%variant.models[bone.uuid].custom_model_data%> } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function apply_default_pose { - # Changes the pose of the rig to the the default pose with interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +function apply_default_pose { + # Changes the pose of the rig to the the default pose with interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: 0 \ + } + } +} + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ } - } } } diff --git a/src/systems/datapackCompiler/1.20.5/static.mcb b/src/systems/datapackCompiler/1.20.5/static.mcb index 6b474dd2..e48e021e 100644 --- a/src/systems/datapackCompiler/1.20.5/static.mcb +++ b/src/systems/datapackCompiler/1.20.5/static.mcb @@ -1,488 +1,671 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> +} -dir <%export_namespace%> { - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run tp @s ~ ~ ~ ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run tp @s ~ ~ ~ ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run tp @s ~ ~ ~ ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} + +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string}} - - data modify storage aj:temp args set value {variant:''} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + tp @s ~ ~ ~ ~ ~ + + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } + execute at @s on passengers run tp @s ~ ~ ~ ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} + +function summon { + #Args: {args:{variant: string}} - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify storage <%temp_storage%> args set value {variant:''} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) + + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @e[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @e[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + limit=1, distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run tp @s ~ ~ ~ ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + function *global/gu/get_entity_uuid_string - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } } - } - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. + return fail + } + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail + + function *<%export_namespace%>/set_default_pose + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run tp @s ~ ~ ~ ~ ~ - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> + + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} + +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } + } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } + + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components.minecraft:custom_model_data set value 1 - } ELSE { - data modify entity @s item.components.minecraft:custom_model_data set value <%variant.models[node.uuid].custom_model_data%> - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:custom_model_data" set value 1 + } ELSE { + data modify entity @s item.components."minecraft:custom_model_data" set value <%variant.models[bone.uuid].custom_model_data%> } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } } + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ + } + } +} diff --git a/src/systems/datapackCompiler/1.21.2/animation.mcb b/src/systems/datapackCompiler/1.21.2/animation.mcb index 694adb9f..c449aca6 100644 --- a/src/systems/datapackCompiler/1.21.2/animation.mcb +++ b/src/systems/datapackCompiler/1.21.2/animation.mcb @@ -1,950 +1,1110 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt -dir <%export_namespace%> { - function on_load { - IF (use_storage_for_animation) { - REPEAT (animations) as animation { - data remove storage aj.<%export_namespace%>:animations <%animation.storage_name%> - } - <%animationStorage.join('\n')%> +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> + + IF (use_storage_for_animation) { + REPEAT (animations) as animation { + data remove storage <%project_storage%>/animations <%animation.storage_name%> } - <%% - animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) - }) - %%> + <%animationStorage.join('\n')%> } + <%% + animations.forEach(animation => { + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) + }) + %%> +} - function remove_animation_objectives { - <%% - animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) - }) - %%> - tellraw @a <%TELLRAW.UNINSTALL()%> - } +function remove_animation_objectives { + <%% + animations.forEach(animation => { + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) + }) + %%> + tellraw @a <%TELLRAW.UNINSTALL()%> +} - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load + + # Once we have more than 8 animations, calling a function only if at least one animation is playing is more efficient. + IF (animations.length > 8) { + # If no animations are playing, we can skip all animation logic. + # This helps reduce ticking commands for rigs that are idle. + execute \ + unless entity @s[<%animations.map(anim => 'tag=!' + TAGS.ANIMATION_PLAYING(export_namespace, anim.storage_name)).join(',')%>] \ + run block tick_animations { + REPEAT (animations) as animation { + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick + } } - # Tick Playing Animations + } ELSE { REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } + } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run rotate @s ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run rotate @s ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} - dir animations { - REPEAT (animations) as animation { - dir <%animation.path_name%> { - # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. - function play { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function stop { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } + tp @s ~ ~ ~ ~ ~ - function pause { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } - function resume { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + execute at @s on passengers run rotate @s ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} - function next_frame { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } +dir animations { + REPEAT (animations) as animation { + dir <%animation.storage_name%> { + function play { + debug assert executed_as_root_entity - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/set_frame with storage aj:temp args - } + function *<%export_namespace%>/animations/pause_all - function apply_frame { - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/apply_frame with storage aj:temp args - } + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } + + function stop { + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } - function tween { - # Attempts to smoothly transition from the currently playing animation into this one. - #ARGS: {duration: int, to_frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + function pause { + debug assert executed_as_root_entity + + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function resume { + debug assert executed_as_root_entity + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function next_frame { + debug assert executed_as_root_entity + + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/set_frame with storage <%temp_storage%> args + } + + function apply_frame { + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + } + + function tween { + # Attempts to smoothly transition from the currently playing animation into this one. + #ARGS: {duration: int, to_frame: int} + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) + + scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/apply_frame {frame: 0} + $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + } + + dir zzz { + function on_tick { + # Tweening logic + scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ + data modify entity @s interpolation_duration set value <%interpolation_duration%> + # Animation logic + IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { + # Makes sure function keyframes in the last frame of the animation are activated. + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ + block function_keyframe_loop_patch { + function ./apply_frame {frame: <%animation.duration-1%>} + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + } + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run \ + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + function ./apply_frame with storage <%temp_storage%> args + IF (animation.loop_mode === 'loop') { + # Loop the animation back to the start once it reaches the last frame. + # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-2 + animation.loop_delay%>.. \ + run return run \ + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + } ELSE IF (animation.loop_mode === 'hold') { + # Pause the animation at the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%>.. \ + run return run \ + function ../pause + } ELSE IF (animation.loop_mode === 'once') { + # Stop the animation once it reaches the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%> \ + run return run \ + function ../stop } - function *<%export_namespace%>/animations/pause_all - - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) - - scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/apply_frame {frame: 0} - $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } - dir zzz { - function on_tick { - # Tweening logic - scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ - data modify entity @s interpolation_duration set value <%interpolation_duration%> - # Animation logic - IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { - # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ - block commands_keyframe_loop_patch { - function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - } - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - function ./apply_frame with storage aj:temp args - IF (animation.loop_mode === 'loop') { - # Loop the animation back to the start once it reaches the last frame. - # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-2 + animation.loop_delay%>.. \ - run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> - } ELSE IF (animation.loop_mode === 'hold') { - # Pause the animation at the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%>.. \ - run return run \ - function ../pause - } ELSE IF (animation.loop_mode === 'once') { - # Stop the animation once it reaches the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%> \ - run return run \ - function ../stop - } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + IF (use_storage_for_animation) { + function set_frame { + #ARGS: {frame: int} + $function ./apply_frame {frame: $(frame)} + execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 + return 1 } - IF (use_storage_for_animation) { - function set_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } + function apply_frame { + #ARGS: {frame: int} + REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { + IF (BONE_TYPES.includes(node.type)) { + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ + data modify entity @s {} merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> + } ELSE IF (node.type === 'locator' || node.type === 'camera') { + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + $data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ - data modify entity @s {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } + IF (animation.frames.some(anim => anim.variant)) { + $execute \ + if data storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant \ + unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ + run { with storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant + #ARGS: {name: string, condition: string} + $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply } - return 1 - } - } ELSE { - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - execute on passengers run \ - data modify entity @s[tag=aj.global.data] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - return 1 - } - # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. - dir frames { - <%% - // A record of node uuid to INodeTransform. - // Keeps track of the last time a bone was updated. - // Only used for step keyframe interpolation. - let hasCommands = false - const lastActiveFrame = {} - const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); - for (const [frameIndex, frame] of animation.frames.entries()) { - const to_merge = {cameras: {}, locators: {}} - let frameFunc = ``; - for (const node of modifiedNodes) { - const transform = frame.node_transforms[node.uuid] - // Skip if the node doesn't have a transform for this frame. - if (!transform) continue - switch (node.type) { - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - const lastFrame = lastActiveFrame[node.uuid] - const isStepInterpolation = !!(lastFrame?.interpolation === 'step') - lastActiveFrame[node.uuid] = transform - - if (transform.interpolation === 'pre-post' || isStepInterpolation) { + return 1 + } + } ELSE { + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + execute on passengers if entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] run \ + data modify entity @s start_interpolation set value -1 + + return 1 + } + + function apply_frame { + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + return 1 + } + + # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. + dir frames { + <%% + // A record of node uuid to INodeTransform. + // Keeps track of the last time a bone was updated. + // Only used for step keyframe interpolation. + let hasFunction = false + const lastActiveFrame = {} + const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); + for (const [frameIndex, frame] of animation.frames.entries()) { + const to_merge = {cameras: {}, locators: {}} + let frameFunc = ``; + for (const node of modifiedNodes) { + const transform = frame.node_transforms[node.uuid] + // Skip if the node doesn't have a transform for this frame. + if (!transform) continue + switch (node.type) { + case 'bone': + case 'text_display': + case 'item_display': + case 'block_display': { + const lastFrame = lastActiveFrame[node.uuid] + const isStepInterpolation = !!(lastFrame?.interpolation === 'step') + lastActiveFrame[node.uuid] = transform + + if (transform.interpolation === 'pre-post' || isStepInterpolation) { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: ${isStepInterpolation ? -1 : 0},` + + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` + + `}` + } else { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: 0,` + + `interpolation_duration: ${interpolation_duration}` + + `}` + } + ;hasFunction = true + break + } + case 'locator': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.locators[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; + } + + if (transform.function) { + if (node.config?.use_entity) { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: ${isStepInterpolation ? -1 : 0},` - + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` - + `}` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `tp @s ~ ~ ~ ~ ~\n` + + `${transform.function}` + + `\n}` } else { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: 0,` - + `interpolation_duration: ${interpolation_duration}` - + `}` + `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `${transform.function}` + + `\n}` } - ;hasCommands = true - break } - case 'locator': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.locators[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - - if (transform.commands) { - if (node.config?.use_entity) { - frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `tp @s ~ ~ ~ ~ ~\n` - + `${transform.commands}` - + `\n}` - } else { - frameFunc += - `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `${transform.commands}` - + `\n}` - } - } - break - } - case 'camera': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.cameras[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - ;break + break + } + case 'camera': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.cameras[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; } + ;break } } + } - if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { - frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` - ;hasCommands = true + if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { + frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` + if (!auto_update_rig_orientation) { + frameFunc += `\nfunction ./on_tick/transform_floating_entities` } + hasFunction = true + } - if (frame.variants?.length) { - const variant = rig.variants[frame.variants[0]] - if (!variant) { - throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) - } - const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` - ;hasCommands = true + if (frame.variants?.length) { + const variant = rig.variants[frame.variants[0]] + if (!variant) { + throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) } + const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` + ;hasFunction = true + } - // Root commands keyframes. - if (frame.commands) { - const execute_condition = frame.commands_execute_condition ? frame.commands_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_commands%NEWLINE_PATCH%{\n${frame.commands}\n}` - ;hasCommands = true - } - ;if (frameFunc.length > 0) { - console.log('frameFunc', frameFunc) - frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` - emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) - } + // Root function keyframes. + if (frame.function) { + const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}` + ;hasFunction = true } - %%> - } + ;if (frameFunc.length > 0) { + frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` + emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) + } + } + %%> } } } } - function pause_all { - # Pauses all animations - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - } } + function pause_all { + # Pauses all animations + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} - # frame is ignored unless animation is specified. - - data modify storage aj:temp args set value {variant:'', animation:'', frame: 0} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + REPEAT (animations) as animation { + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } + } +} - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } +function summon { + #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} + # frame is ignored unless animation is specified. + + data modify storage <%temp_storage%> args set value {variant:'', animation:'', frame: 0} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @n[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Animation Argument - # If the animation argument is provided, attempt to apply the animation. - execute if data storage aj:temp args.animation run block animation_arg/process { with storage aj:temp args - # If the animation argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{animation:''}} run return run block if_empty { - tellraw @a <%TELLRAW.ANIMATION_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Automatically set the frame argument to 0 if the frame argument is not provided. - # Takes advantage of `store result score` setting the score to 0 if the command fails. + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { execute \ - store result storage aj:temp args.frame int 1 \ - store result score #frame <%OBJECTIVES.I()%> \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ run \ - data get storage aj:temp args.frame - # If the frame argument is negative, return an error. - execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { - # Tell the user that the frame argument cannot be negative. - tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> - function *<%export_namespace%>/remove/this + function *global/gu/get_entity_uuid_string + + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } + } + + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Attempt to apply the animation frame. - execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage aj:temp args - # Make sure we're only applying transforms when setting the summon pose. - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 - # If the set_frame function fails, the animation doesn't exist, so we return an error. + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. return fail } - # If the set_frame function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { with storage aj:temp args - # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. - $tellraw @a <%TELLRAW.INVALID_ANIMATION('$(animation)', animations)%> - function *<%export_namespace%>/remove/this + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 + } + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. - execute if data storage aj:temp {args:{start_animation: true}} run block start_animation { with storage aj:temp args - $function *<%export_namespace%>/animations/$(animation)/resume - } + function *<%export_namespace%>/set_default_pose + + # Animation Argument + # If the animation argument is provided, attempt to apply the animation. + execute if data storage <%temp_storage%> args.animation run block animation_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the animation argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{animation:''}} run return run block if_empty { + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('animation')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Automatically set the frame argument to 0 if the frame argument is not provided. + # Takes advantage of `store result score` setting the score to 0 if the command fails. + execute \ + store result storage <%temp_storage%> args.frame int 1 \ + store result score #frame <%OBJECTIVES.I()%> \ + run \ + data get storage <%temp_storage%> args.frame + # If the frame argument is negative, return an error. + execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { + # Tell the user that the frame argument cannot be negative. + tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the animation frame. + execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage <%temp_storage%> args + # Make sure we're only applying transforms when setting the summon pose. + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage <%temp_storage%> args + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 + # If the set_frame function fails, the animation doesn't exist, so we return an error. + return fail + } + # If the set_frame function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { + # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. + tellraw @a <%TELLRAW.INVALID_ANIMATION(animations)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. + execute if data storage <%temp_storage%> {args:{start_animation: true}} run block start_animation { with storage <%temp_storage%> args + $function *<%export_namespace%>/animations/$(animation)/resume + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 } - } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run rotate @s ~ ~ + + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 + } + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} + +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components."minecraft:item_model" set value "animated_java:empty" - } ELSE { - data modify entity @s item.components."minecraft:item_model" set value "<%variant.models[node.uuid].item_model%>" - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:item_model" set value "animated_java:empty" + } ELSE { + data modify entity @s item.components."minecraft:item_model" set value "<%variant.models[bone.uuid].item_model%>" } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function apply_default_pose { - # Changes the pose of the rig to the the default pose with interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +function apply_default_pose { + # Changes the pose of the rig to the the default pose with interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: 0 \ + } + } +} + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ } - } } } diff --git a/src/systems/datapackCompiler/1.21.2/global.mcb b/src/systems/datapackCompiler/1.21.2/global.mcb new file mode 100644 index 00000000..8c4140d9 --- /dev/null +++ b/src/systems/datapackCompiler/1.21.2/global.mcb @@ -0,0 +1,230 @@ +import ./global.mcbt + +dir global { + function on_load minecraft:load { + # Initialize Scoreboards + scoreboard objectives add <%OBJECTIVES.I()%> dummy + scoreboard objectives add <%OBJECTIVES.ID()%> dummy + scoreboard objectives add <%OBJECTIVES.IS_RIG_LOADED()%> dummy + scoreboard objectives add <%OBJECTIVES.TWEEN_DURATION()%> dummy + + scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 0 + + # Purposefully load gu first, as other functions depend on it. + function *global/gu/on_load + IF (debug_mode) { + scoreboard players reset * <%OBJECTIVES.IS_RIG_LOADED()%> + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_load + } + function #*global/on_load + } + + IF (debug_mode) { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_tick + } + } ELSE { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function #*global/root/on_tick + } + } + + tag functions on_load { + *<%export_namespace%>/on_load + } + + dir root { + tag functions on_tick { + *<%export_namespace%>/root/on_tick + } + + IF (debug_mode) { + function on_tick { + execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function *global/root/on_load + function #*global/root/on_tick + } + + function on_load { + # Check if the rig is outdated by comparing the function's rig_hash of the rig to the rig_hash stored in the entity data. + execute if function animated_java:global/util/is_rig_outdated run block outdated_warning/modify_rig { + data remove storage <%temp_storage%> args + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> args.uuid set from storage <%gu_storage%> out + # Get position + execute store result storage <%temp_storage%> args.x double 1 run data get entity @s Pos[0] 1 + execute store result storage <%temp_storage%> args.y double 1 run data get entity @s Pos[1] 1 + execute store result storage <%temp_storage%> args.z double 1 run data get entity @s Pos[2] 1 + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run { + # Get export namespace + data modify storage <%temp_storage%> args.export_namespace set from entity @s data.export_namespace + # Overwrite the rig hash so the warning doesn't show again. + data modify entity @s data.rig_hash set value '<%rig_hash%>' + } + block print { with storage <%temp_storage%> args + $tellraw @a <%TELLRAW.RIG_OUTDATED()%> + } + execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} + summon minecraft:text_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', \ + '<%TAGS.NEW()%>' \ + ], \ + text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ + alignment: 'center', \ + billboard: 'vertical', \ + shadow: true, \ + transformation:{ \ + translation:[0f,<%boundingBox[1]/16%>f,0f], \ + left_rotation:[0f,0f,0f,1f], \ + right_rotation:[0f,0f,0f,1f], \ + scale:[1f,1f,1f] \ + } \ + } + + ride @n[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + distance=..1 \ + ] mount @s + + tag @n[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + distance=..1 \ + ] remove <%TAGS.NEW()%> + } + scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 + } + } + } + + dir remove { + # Removes every entity related to Animated Java. + function everything { + kill @e[tag=<%TAGS.GLOBAL_ENTITY()%>] + } + + function entity_stack { + execute on passengers if entity @s[type=!player] run function ^0 + kill @s + } + + function entity_stack_by_uuid { + #ARGS: {uuid: string} + $execute as $(uuid) run function ./entity_stack + } + + # Removes locators and cameras owned by the rig, even if they're not included in the currently loaded export. + function outdated_rig { + # Assert that the function is being executed as a root entity. + debug assert executed_as_root_entity + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + data remove storage <%temp_storage%> args + data remove storage <%temp_storage%> uuids + data modify storage <%temp_storage%> uuids set from entity @s data.uuids + execute store result score #aj.length <%OBJECTIVES.I()%> run data get storage <%temp_storage%> uuids + + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run block loop_over_uuids { + data modify storage <%temp_storage%> args.current_uuid set from storage <%temp_storage%> uuids[-1].uuid + data remove storage <%temp_storage%> uuids[-1] + function ./entity_stack_by_uuid with storage <%temp_storage%> args + + scoreboard players remove #aj.length <%OBJECTIVES.I()%> 1 + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run function ^0 + } + } + + function ./entity_stack + } + } + + dir util { + # Recurses through the passengers of the context entity and appends their UUIDs to `storage <%temp_storage%> uuids`. + function get_entity_stack_uuids { + data remove storage <%temp_storage%> uuids + execute on passengers run { + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> uuids append from storage <%gu_storage%> out + execute on passengers run function ^0 + } + } + # Returns 1 if the rig is outdated, else returns 0. + function is_rig_outdated { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run return run { with entity @s data + # REVIEW - The replace here is a bit hacky. + # Because this is a core function, I need to create a generic version of + # project_storage that uses a macro to get the export namespace instead of hardcoding it. + $execute if data storage <%project_storage.replace(export_namespace, '$(export_namespace)')%> {rig_hash:'$(rig_hash)'} run return 0 + return 1 + } + } + } + + # Thanks Gibbsly for this code! https://github.com/gibbsly/gu + dir gu { + function on_load { + scoreboard players set 256 <%OBJECTIVES.I()%> 256 + data modify storage <%gu_storage%> hex_chars set value \ + <%JSON.stringify([...Array(0x100).keys()].map(v => {const x = v.toString(16); return x.length > 1 ? x : '0' + x}))%> + } + + function get_entity_uuid_string { + data modify storage <%gu_storage%> temp set value {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,a:0,b:0,c:0,d:0,e:0,f:0} + data modify storage <%gu_storage%> in set from entity @s UUID + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[0] + execute store result storage <%gu_storage%> temp.0 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.1 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.2 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.3 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[1] + execute store result storage <%gu_storage%> temp.4 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.5 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.6 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.7 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[2] + execute store result storage <%gu_storage%> temp.8 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.9 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.a int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.b int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[3] + execute store result storage <%gu_storage%> temp.c int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.d int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.e int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.f int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + block { with storage <%gu_storage%> temp + REPEAT (0, 15) as i { + $data modify storage <%gu_storage%> temp.<%i.toString(16)%> set from storage <%gu_storage%> hex_chars[$(<%i.toString(16)%>)] + } + } + + block { with storage <%gu_storage%> temp + $data modify storage <%gu_storage%> out set value "$(3)$(2)$(1)$(0)-$(7)$(6)-$(5)$(4)-$(b)$(a)-$(9)$(8)$(f)$(e)$(d)$(c)" + } + } + } +} + +dir <%export_namespace%> { + function on_load { + function *<%export_namespace%>/invalid_version_warning + } + + function invalid_version_warning { + tellraw @a <%TELLRAW.INVALID_VERSION()%> + } +} diff --git a/src/systems/datapackCompiler/1.21.2/static.mcb b/src/systems/datapackCompiler/1.21.2/static.mcb index 5e93d120..77677f17 100644 --- a/src/systems/datapackCompiler/1.21.2/static.mcb +++ b/src/systems/datapackCompiler/1.21.2/static.mcb @@ -1,488 +1,671 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> +} -dir <%export_namespace%> { - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run rotate @s ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run rotate @s ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} + +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string}} - - data modify storage aj:temp args set value {variant:''} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + tp @s ~ ~ ~ ~ ~ + + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } + execute at @s on passengers run rotate @s ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} + +function summon { + #Args: {args:{variant: string}} - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify storage <%temp_storage%> args set value {variant:''} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) + + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @n[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + function *global/gu/get_entity_uuid_string - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } } - } - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. + return fail + } + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail + + function *<%export_namespace%>/set_default_pose + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run rotate @s ~ ~ - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> + + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} + +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } + } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } + + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components.minecraft:item_model set value "animated_java:empty" - } ELSE { - data modify entity @s item.components.minecraft:item_model set value "<%variant.models[node.uuid].item_model%>" - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:item_model" set value "animated_java:empty" + } ELSE { + data modify entity @s item.components."minecraft:item_model" set value "<%variant.models[bone.uuid].item_model%>" } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } } + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ + } + } +} diff --git a/src/systems/datapackCompiler/1.21.4/animation.mcb b/src/systems/datapackCompiler/1.21.4/animation.mcb index 4ad4b9b6..58ea6320 100644 --- a/src/systems/datapackCompiler/1.21.4/animation.mcb +++ b/src/systems/datapackCompiler/1.21.4/animation.mcb @@ -1,950 +1,1110 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt -dir <%export_namespace%> { - function on_load { - IF (use_storage_for_animation) { - REPEAT (animations) as animation { - data remove storage aj.<%export_namespace%>:animations <%animation.storage_name%> - } - <%animationStorage.join('\n')%> +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> + + IF (use_storage_for_animation) { + REPEAT (animations) as animation { + data remove storage <%project_storage%>/animations <%animation.storage_name%> } - <%% - animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) - }) - %%> + <%animationStorage.join('\n')%> } + <%% + animations.forEach(animation => { + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) + }) + %%> +} - function remove_animation_objectives { - <%% - animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) - }) - %%> - tellraw @a <%TELLRAW.UNINSTALL()%> - } +function remove_animation_objectives { + <%% + animations.forEach(animation => { + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) + }) + %%> + tellraw @a <%TELLRAW.UNINSTALL()%> +} - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load + + # Once we have more than 8 animations, calling a function only if at least one animation is playing is more efficient. + IF (animations.length > 8) { + # If no animations are playing, we can skip all animation logic. + # This helps reduce ticking commands for rigs that are idle. + execute \ + unless entity @s[<%animations.map(anim => 'tag=!' + TAGS.ANIMATION_PLAYING(export_namespace, anim.storage_name)).join(',')%>] \ + run block tick_animations { + REPEAT (animations) as animation { + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick + } } - # Tick Playing Animations + } ELSE { REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } + } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run rotate @s ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run rotate @s ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} - dir animations { - REPEAT (animations) as animation { - dir <%animation.path_name%> { - # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. - function play { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function stop { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } + tp @s ~ ~ ~ ~ ~ - function pause { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } - function resume { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } + execute at @s on passengers run rotate @s ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} - function next_frame { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } +dir animations { + REPEAT (animations) as animation { + dir <%animation.storage_name%> { + function play { + debug assert executed_as_root_entity - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/set_frame with storage aj:temp args - } + function *<%export_namespace%>/animations/pause_all - function apply_frame { - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/apply_frame with storage aj:temp args - } + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } + + function stop { + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/set_frame {frame: 0} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + } - function tween { - # Attempts to smoothly transition from the currently playing animation into this one. - #ARGS: {duration: int, to_frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + function pause { + debug assert executed_as_root_entity + + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function resume { + debug assert executed_as_root_entity + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + } + + function next_frame { + debug assert executed_as_root_entity + + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/set_frame with storage <%temp_storage%> args + } + + function apply_frame { + #ARGS: {frame: int} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $execute store result storage <%temp_storage%> args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) + execute at @s run function ./zzz/apply_frame with storage <%temp_storage%> args + } + + function tween { + # Attempts to smoothly transition from the currently playing animation into this one. + #ARGS: {duration: int, to_frame: int} + debug assert executed_as_root_entity + + function *<%export_namespace%>/animations/pause_all + + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) + + scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + execute at @s run function ./zzz/apply_frame {frame: 0} + $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + } + + dir zzz { + function on_tick { + # Tweening logic + scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 + execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ + data modify entity @s interpolation_duration set value <%interpolation_duration%> + # Animation logic + IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { + # Makes sure function keyframes in the last frame of the animation are activated. + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ + block function_keyframe_loop_patch { + function ./apply_frame {frame: <%animation.duration-1%>} + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 + } + } + data remove storage <%temp_storage%> args + execute store result storage <%temp_storage%> args.frame int 1 run \ + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> + function ./apply_frame with storage <%temp_storage%> args + IF (animation.loop_mode === 'loop') { + # Loop the animation back to the start once it reaches the last frame. + # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-2 + animation.loop_delay%>.. \ + run return run \ + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + } ELSE IF (animation.loop_mode === 'hold') { + # Pause the animation at the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%>.. \ + run return run \ + function ../pause + } ELSE IF (animation.loop_mode === 'once') { + # Stop the animation once it reaches the last frame. + execute \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ + matches <%animation.duration-1%> \ + run return run \ + function ../stop } - function *<%export_namespace%>/animations/pause_all - - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) - - scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/apply_frame {frame: 0} - $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } - dir zzz { - function on_tick { - # Tweening logic - scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ - data modify entity @s interpolation_duration set value <%interpolation_duration%> - # Animation logic - IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { - # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ - block commands_keyframe_loop_patch { - function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - } - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - function ./apply_frame with storage aj:temp args - IF (animation.loop_mode === 'loop') { - # Loop the animation back to the start once it reaches the last frame. - # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-2 + animation.loop_delay%>.. \ - run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> - } ELSE IF (animation.loop_mode === 'hold') { - # Pause the animation at the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%>.. \ - run return run \ - function ../pause - } ELSE IF (animation.loop_mode === 'once') { - # Stop the animation once it reaches the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%> \ - run return run \ - function ../stop - } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + IF (use_storage_for_animation) { + function set_frame { + #ARGS: {frame: int} + $function ./apply_frame {frame: $(frame)} + execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 + return 1 } - IF (use_storage_for_animation) { - function set_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } + function apply_frame { + #ARGS: {frame: int} + REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { + IF (BONE_TYPES.includes(node.type)) { + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ + data modify entity @s {} merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> + } ELSE IF (node.type === 'locator' || node.type === 'camera') { + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + $data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> merge from \ + storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ - data modify entity @s {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } + IF (animation.frames.some(anim => anim.variant)) { + $execute \ + if data storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant \ + unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ + run { with storage <%project_storage%>/animations <%animation.storage_name%>.$(frame).variant + #ARGS: {name: string, condition: string} + $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply } - return 1 - } - } ELSE { - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - execute on passengers run \ - data modify entity @s[tag=aj.global.data] start_interpolation set value -1 - return 1 } - function apply_frame { - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - return 1 - } - # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. - dir frames { - <%% - // A record of node uuid to INodeTransform. - // Keeps track of the last time a bone was updated. - // Only used for step keyframe interpolation. - let hasCommands = false - const lastActiveFrame = {} - const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); - for (const [frameIndex, frame] of animation.frames.entries()) { - const to_merge = {cameras: {}, locators: {}} - let frameFunc = ``; - for (const node of modifiedNodes) { - const transform = frame.node_transforms[node.uuid] - // Skip if the node doesn't have a transform for this frame. - if (!transform) continue - switch (node.type) { - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - const lastFrame = lastActiveFrame[node.uuid] - const isStepInterpolation = !!(lastFrame?.interpolation === 'step') - lastActiveFrame[node.uuid] = transform - - if (transform.interpolation === 'pre-post' || isStepInterpolation) { + return 1 + } + } ELSE { + function set_frame { + # Sets the frame without interpolation + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + execute on passengers if entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] run \ + data modify entity @s start_interpolation set value -1 + + return 1 + } + + function apply_frame { + #ARGS: {frame: int} + $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ + function ./frames/$(frame) with entity @s data.uuids_by_name + + return 1 + } + + # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. + dir frames { + <%% + // A record of node uuid to INodeTransform. + // Keeps track of the last time a bone was updated. + // Only used for step keyframe interpolation. + let hasFunction = false + const lastActiveFrame = {} + const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); + for (const [frameIndex, frame] of animation.frames.entries()) { + const to_merge = {cameras: {}, locators: {}} + let frameFunc = ``; + for (const node of modifiedNodes) { + const transform = frame.node_transforms[node.uuid] + // Skip if the node doesn't have a transform for this frame. + if (!transform) continue + switch (node.type) { + case 'bone': + case 'text_display': + case 'item_display': + case 'block_display': { + const lastFrame = lastActiveFrame[node.uuid] + const isStepInterpolation = !!(lastFrame?.interpolation === 'step') + lastActiveFrame[node.uuid] = transform + + if (transform.interpolation === 'pre-post' || isStepInterpolation) { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: ${isStepInterpolation ? -1 : 0},` + + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` + + `}` + } else { + frameFunc += + `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` + + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` + + `start_interpolation: 0,` + + `interpolation_duration: ${interpolation_duration}` + + `}` + } + ;hasFunction = true + break + } + case 'locator': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.locators[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; + } + + if (transform.function) { + if (node.config?.use_entity) { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: ${isStepInterpolation ? -1 : 0},` - + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` - + `}` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `tp @s ~ ~ ~ ~ ~\n` + + `${transform.function}` + + `\n}` } else { frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: 0,` - + `interpolation_duration: ${interpolation_duration}` - + `}` + `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` + + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + + `${transform.function_execute_condition ? transform.function_execute_condition + ' ' : ''}run ` + + `block ${frameIndex}_locator_${node.storage_name}%NEWLINE_PATCH%{\n` + + `${transform.function}` + + `\n}` } - ;hasCommands = true - break } - case 'locator': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.locators[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - - if (transform.commands) { - if (node.config?.use_entity) { - frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `tp @s ~ ~ ~ ~ ~\n` - + `${transform.commands}` - + `\n}` - } else { - frameFunc += - `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `${transform.commands}` - + `\n}` - } - } - break - } - case 'camera': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.cameras[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - ;break + break + } + case 'camera': { + const lastFrame = lastActiveFrame[node.uuid] + lastActiveFrame[node.uuid] = transform + ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { + to_merge.cameras[node.storage_name] = { + px: transform.pos[0], + py: transform.pos[1], + pz: transform.pos[2], + ry: transform.head_rot[1], + rx: transform.head_rot[0] + }; } + ;break } } + } - if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { - frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` - ;hasCommands = true + if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { + frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` + if (!auto_update_rig_orientation) { + frameFunc += `\nfunction ./on_tick/transform_floating_entities` } + hasFunction = true + } - if (frame.variants?.length) { - const variant = rig.variants[frame.variants[0]] - if (!variant) { - throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) - } - const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` - ;hasCommands = true + if (frame.variants?.length) { + const variant = rig.variants[frame.variants[0]] + if (!variant) { + throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) } + const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` + ;hasFunction = true + } - // Root commands keyframes. - if (frame.commands) { - const execute_condition = frame.commands_execute_condition ? frame.commands_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_commands%NEWLINE_PATCH%{\n${frame.commands}\n}` - ;hasCommands = true - } - ;if (frameFunc.length > 0) { - console.log('frameFunc', frameFunc) - frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` - emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) - } + // Root function keyframes. + if (frame.function) { + const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : '' + frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}` + ;hasFunction = true } - %%> - } + ;if (frameFunc.length > 0) { + frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` + emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) + } + } + %%> } } } } - function pause_all { - # Pauses all animations - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - } } + function pause_all { + # Pauses all animations + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} - # frame is ignored unless animation is specified. - - data modify storage aj:temp args set value {variant:'', animation:'', frame: 0} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + REPEAT (animations) as animation { + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } + } +} - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } +function summon { + #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} + # frame is ignored unless animation is specified. + + data modify storage <%temp_storage%> args set value {variant:'', animation:'', frame: 0} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @n[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Animation Argument - # If the animation argument is provided, attempt to apply the animation. - execute if data storage aj:temp args.animation run block animation_arg/process { with storage aj:temp args - # If the animation argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{animation:''}} run return run block if_empty { - tellraw @a <%TELLRAW.ANIMATION_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Automatically set the frame argument to 0 if the frame argument is not provided. - # Takes advantage of `store result score` setting the score to 0 if the command fails. + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { execute \ - store result storage aj:temp args.frame int 1 \ - store result score #frame <%OBJECTIVES.I()%> \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ run \ - data get storage aj:temp args.frame - # If the frame argument is negative, return an error. - execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { - # Tell the user that the frame argument cannot be negative. - tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> - function *<%export_namespace%>/remove/this + function *global/gu/get_entity_uuid_string + + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } + } + + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Attempt to apply the animation frame. - execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage aj:temp args - # Make sure we're only applying transforms when setting the summon pose. - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 - # If the set_frame function fails, the animation doesn't exist, so we return an error. + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. return fail } - # If the set_frame function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { with storage aj:temp args - # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. - $tellraw @a <%TELLRAW.INVALID_ANIMATION('$(animation)', animations)%> - function *<%export_namespace%>/remove/this + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 + } + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. - execute if data storage aj:temp {args:{start_animation: true}} run block start_animation { with storage aj:temp args - $function *<%export_namespace%>/animations/$(animation)/resume - } + function *<%export_namespace%>/set_default_pose + + # Animation Argument + # If the animation argument is provided, attempt to apply the animation. + execute if data storage <%temp_storage%> args.animation run block animation_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the animation argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{animation:''}} run return run block if_empty { + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('animation')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Automatically set the frame argument to 0 if the frame argument is not provided. + # Takes advantage of `store result score` setting the score to 0 if the command fails. + execute \ + store result storage <%temp_storage%> args.frame int 1 \ + store result score #frame <%OBJECTIVES.I()%> \ + run \ + data get storage <%temp_storage%> args.frame + # If the frame argument is negative, return an error. + execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { + # Tell the user that the frame argument cannot be negative. + tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the animation frame. + execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage <%temp_storage%> args + # Make sure we're only applying transforms when setting the summon pose. + tag @s add <%TAGS.TRANSFORMS_ONLY()%> + $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage <%temp_storage%> args + tag @s remove <%TAGS.TRANSFORMS_ONLY()%> + execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 + # If the set_frame function fails, the animation doesn't exist, so we return an error. + return fail + } + # If the set_frame function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { + # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. + tellraw @a <%TELLRAW.INVALID_ANIMATION(animations)%> + function *<%export_namespace%>/remove/this/without_on_remove_function } - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. + execute if data storage <%temp_storage%> {args:{start_animation: true}} run block start_animation { with storage <%temp_storage%> args + $function *<%export_namespace%>/animations/$(animation)/resume + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 } - } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run rotate @s ~ ~ + + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 + } + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} + +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" - } ELSE { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" + } ELSE { + data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function apply_default_pose { - # Changes the pose of the rig to the the default pose with interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +function apply_default_pose { + # Changes the pose of the rig to the the default pose with interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: 0 \ + } + } +} + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ } - } } } diff --git a/src/systems/datapackCompiler/1.21.4/static.mcb b/src/systems/datapackCompiler/1.21.4/static.mcb index b611a990..eeea27a7 100644 --- a/src/systems/datapackCompiler/1.21.4/static.mcb +++ b/src/systems/datapackCompiler/1.21.4/static.mcb @@ -1,488 +1,671 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. +# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace. +import ../global.mcbt +function on_load { + data modify storage <%project_storage%> rig_hash set value <%"'" + rig_hash + "'"%> +} -dir <%export_namespace%> { - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } +function invalid_version_warning { + # This function will contain a tellraw if the datapack is loaded in the wrong version. +} - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } +dir root { + function on_tick { + execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 + + # Custom pre-tick function + IF (on_pre_tick_function) { + <%% + emit.mcb(on_pre_tick_function) + %%> } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } + + IF (auto_update_rig_orientation) { IF (has_locators || has_cameras) { - # Update locator and camera orientations execute \ on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities + } + execute on passengers run rotate @s ~ ~ + } ELSE IF (has_ticking_locators) { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_locators + } + + # Custom post-tick function + IF (on_post_tick_function) { + <%% + emit.mcb(on_post_tick_function) + %%> + } + } + + IF (has_locators || has_cameras) { + dir on_tick { + function transform_locators { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + block select_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block as_locator_<%locator.storage_name%> { + tp @s ~ ~ ~ ~ ~ + + IF (locator.config?.sync_passenger_rotation) { + execute on passengers run rotate @s ~ ~ } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + + IF (locator.config?.on_tick_function) { <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } + emit.mcb(locator.config.on_tick_function) %%> } } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + } ELSE IF (locator.config?.on_tick_function) { $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block at_locator_<%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_tick_function) + %%> + } } } } } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> + + function transform_floating_entities { + function ./transform_locators + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + block select_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $execute \ + as $(uuid) \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run tp @s ~ ~ ~ ~ ~ + } + } } } } +} + +IF (!auto_update_rig_orientation) { + function move { + debug assert executed_as_root_entity - function summon { - #Args: {args:{variant: string}} - - data modify storage aj:temp args set value {variant:''} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ + tp @s ~ ~ ~ ~ ~ + + IF (has_locators || has_cameras) { + execute \ + at @s on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run function ./on_tick/transform_floating_entities } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } + execute at @s on passengers run rotate @s ~ ~ + } +} ELSE { + function move { + tellraw @a <%TELLRAW.AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING()%> + } +} + +function summon { + #Args: {args:{variant: string}} - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify storage <%temp_storage%> args set value {variant:''} + $execute store success score #success <%OBJECTIVES.I()%> run data modify storage <%temp_storage%> args set value $(args) + + summon minecraft:item_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.NEW()%>', \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.GLOBAL_ROOT()%>', \ + '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ + '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ + ], \ + teleport_duration: 0, \ + interpolation_duration: <%interpolation_duration%>, \ + Passengers:<%root_entity_passengers%>, \ + } + execute as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_ROOT(export_namespace)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..0.01 \ + ] run block zzz/summon/as_root_entity { + execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 + # Align the position and rotation of the root with the command context. + tp @s ~ ~ ~ ~ ~ + + function *global/gu/get_entity_uuid_string + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { + # Rig Root UUID + data modify entity @s data.uuids append from storage <%gu_storage%> out + # Data Entity UUID + function *global/gu/get_entity_uuid_string + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.data_data set from storage <%gu_storage%> out + + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + summon <%locator.config.entity_type%> \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(locator, rig)%>} + execute \ + as @n[ \ + type=<%locator.config.entity_type%>, \ + tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(locator.max_distance)%> \ + ] \ + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%locator.type + '_' + locator.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage <%gu_storage%> out } - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + summon minecraft:item_display \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} + execute \ + as @n[ \ + type=minecraft:item_display, \ + tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ + tag=<%TAGS.NEW()%>, \ + distance=..<%Math.ceil(camera.max_distance)%> \ + ] \ + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { + tag @s remove <%TAGS.NEW()%> + function *global/gu/get_entity_uuid_string } + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%camera.type + '_' + camera.storage_name%> set from storage <%gu_storage%> out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage <%gu_storage%> out } - function *<%export_namespace%>/set_default_pose - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> + REPEAT (Object.values(rig.nodes).filter(node => BONE_TYPES.includes(node.type))) as node { + execute \ + on vehicle \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + function *global/gu/get_entity_uuid_string - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> + data modify entity @s data.uuids append from storage <%gu_storage%> out + data modify entity @s data.uuids_by_name.<%node.type + '_' + node.storage_name%> set from storage <%gu_storage%> out + } } - } - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + # Variant Arguement + IF (Object.keys(rig.variants).length > 1) { + execute if data storage <%temp_storage%> args.variant run block variant_arg/process { with storage <%temp_storage%> args + scoreboard players set #success <%OBJECTIVES.I()%> 0 + # If the variant argument is *explicitly* set to an empty string, return an error. + execute if data storage <%temp_storage%> {args:{variant:''}} run return run block if_empty { + # Tell the user that the variant cannot be empty. + tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('variant')%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + # Attempt to apply the requested variant. + # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. + execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage <%temp_storage%> args + $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 + # If the apply function fails, the variant doesn't exist, so we return an error. + return fail + } + # If the apply function failed, return an error. + execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { + # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. + tellraw @a <%TELLRAW.INVALID_VARIANT(rig.variants)%> + function *<%export_namespace%>/remove/this/without_on_remove_function + } + scoreboard players set #success <%OBJECTIVES.I()%> 1 + } + } ELSE { + execute if data storage <%temp_storage%> args.variant run block zzz/variant_arg/no_variants_warning { + tellraw @a <%TELLRAW.NO_VARIANTS()%> + function *<%export_namespace%>/remove/this/without_on_remove_function + scoreboard players set #success <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ + } + execute if score #success <%OBJECTIVES.I()%> matches 0 run return fail + + function *<%export_namespace%>/set_default_pose + IF (has_locators || has_cameras) { + execute \ on passengers \ if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } + run function ./on_tick/transform_floating_entities } + execute on passengers run rotate @s ~ ~ - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + # Apply teleport duration + data modify entity @s teleport_duration set value <%teleportation_duration%> + execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> + + IF (has_entity_locators) { + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block locators_on_summon { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity && node.config.on_summon_function)) as locator { + block { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block <%locator.storage_name%> { + <%% + emit.mcb(locator.config.on_summon_function) + %%> + # Track any custom entities on the locator. + function *global/util/get_entity_stack_uuids + } } + data modify entity @s data.uuids append from storage <%temp_storage%> uuids } } } + + IF (on_summon_function) { + execute at @s run block rig_on_summon { + <%% + emit.mcb(on_summon_function) + %%> + } + } + + # Remove the NEW tag from the root entity, and it's passengers. + tag @s remove <%TAGS.NEW()%> + execute on passengers run tag @s remove <%TAGS.NEW()%> } +} + +IF (has_entity_locators) { + function as_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.$(name).uuid + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } + $execute as $(uuid) at @s run return run $(command) + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the locator wasn't found. + tellraw @a <%TELLRAW.LOCATOR_ENTITY_NOT_FOUND()%> } } + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> + } } + } - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } + function as_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + data modify storage <%temp_storage%> args.uuid set from entity @s data.locators.<%locator.storage_name%>.uuid + block execute_as_uuid { with storage <%temp_storage%> args + $execute as $(uuid) at @s run $(command) } } } } +} - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) +IF (has_locators) { + function at_locator { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> + } + + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/at_locator/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge from entity @s data.locators.$(name) + + IF (debug_mode) { + execute unless data storage <%temp_storage%> args.uuid run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) + } + + block execute_at_transform { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> + + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_COMMAND_FAILED_TO_EXECUTE()%> } } } - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + function at_all_locators { + #ARGS: {command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {command:'$(command)'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + execute at @s on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + data modify storage <%temp_storage%> args merge from entity @s data.locators.<%locator.storage_name%> + block execute_at_transform { with storage <%temp_storage%> args + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run $(command) + } + } + } + } +} + +IF (has_cameras) { + function as_camera { + #ARGS: {name: string, command: string} + debug assert executed_as_root_entity + + data remove storage <%temp_storage%> args + $data modify storage <%temp_storage%> args merge value {name:'$(name)', command:'$(command)', uuid:'+MISSING_UUID+'} + + IF (debug_mode) { + execute if data storage <%temp_storage%> {args:{name:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('name')%> + execute if data storage <%temp_storage%> {args:{command:''}} run return run tellraw @a <%TELLRAW.ARGUMENT_CANNOT_BE_EMPTY('command')%> } - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/as_camera/as_data { with storage <%temp_storage%> args + $data modify storage <%temp_storage%> args.uuid set from entity @s data.cameras.$(name).uuid + + IF (debug_mode) { + scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 } - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> + block execute_as_uuid { with storage <%temp_storage%> args + IF (debug_mode) { + # If the function successfully instantiated, the provided command is valid. + scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 + } - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } + $execute as $(uuid) at @s run return run $(command) + + IF (debug_mode) { + # If the entity with the provided UUID doesn't exist, the camera wasn't found. + tellraw @a <%TELLRAW.CAMERA_ENTITY_NOT_FOUND()%> } } - execute on passengers run kill @s - kill @s + IF (debug_mode) { + execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_COMMAND_FAILED_TO_EXECUTE()%> + } } + } +} - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s +dir remove { + function all { + # Removes all instances of this rig from the world. + execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this + } + + function entities { + # Removes all entities related to this rig from the world. + kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] + } + + function this { + # Removes the rig this function is executed as. + debug assert executed_as_root_entity + + <%% + if (on_remove_function) emit.mcb(on_remove_function) + %%> + + IF (has_entity_locators) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + IF (locator.config?.on_remove_function) { + IF (locator.config.use_entity) { + block as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute as $(uuid) at @s run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } ELSE { + block at_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + $execute \ + positioned ^$(px) ^$(py) ^$(pz) \ + rotated ~$(ry) ~$(rx) \ + run block locator_<%locator.storage_name%>_on_remove { + <%% + emit.mcb(locator.config.on_remove_function) + %%> + } + } + } } } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) + } + } + + function ./this/without_on_remove_function + } + + dir this { + function without_on_remove_function { + debug assert executed_as_root_entity + + IF (has_entity_locators || has_cameras) { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator' && node.config?.use_entity)) as locator { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.locators.<%locator.storage_name%> + } + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + function animated_java:global/remove/entity_stack_by_uuid with entity @s data.cameras.<%camera.storage_name%> + } } } + + # Remove the rig using the more expensive & thorough method if the rig_hash doesn't match. + execute \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + unless data entity @s {data:{rig_hash: '<%rig_hash%>'}} \ + on vehicle \ + run function animated_java:global/remove/outdated_rig + + function animated_java:global/remove/entity_stack } } +} - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" - } ELSE { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" - } +IF (Object.keys(rig.variants).length > 1) { + dir variants { + REPEAT (Object.values(rig.variants)) as variant { + dir <%variant.name%> { + function apply { + debug assert executed_as_root_entity + + <%% + global.filteredBones = Object.values(rig.nodes).filter( + node => node.type === 'bone' && + !variant.excluded_nodes.includes(node.uuid) && + ( // Variant has a model override or a config override for this bone. + variant.models[node.uuid] !== undefined || + node.configs.variants[variant.uuid] !== undefined + ) + ) + %%> + + REPEAT (global.filteredBones) as bone { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, bone.storage_name)%>] \ + run \ + block zzz/apply_to_bone_<%bone.storage_name%> { + IF (variant.models[bone.uuid] !== undefined) { + # Special case for `animated_java:empty` model. + IF (variant.models[bone.uuid].model === null) { + data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" + } ELSE { + data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } + } + IF (bone.configs.variants[variant.uuid]) { + <%% + global.config = BoneConfig.fromJSON(bone.configs.variants[variant.uuid]) + %%> + IF (!global.config.isDefault()) { + data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> } } - } } - # Return success to allow this function to be used in function conditions. - return 1 } + # Return success to allow this function to be used in function conditions. + return 1 } } } } +} - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} +IF (has_locators || has_cameras) { + dir zzz { + function reset_floating_entities { + IF (has_locators) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_locator_<%locator.storage_name%> { with entity @s data.locators.<%locator.storage_name%> + IF (locator.config?.use_entity) { + $tp $(uuid) \ + ^<%roundTo(locator.default_transform.pos[0], 10)%> \ + ^<%roundTo(locator.default_transform.pos[1], 10)%> \ + ^<%roundTo(locator.default_transform.pos[2], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(locator.default_transform.head_rot[0], 10)%> + } + + data modify entity @s data.locators.<%locator.storage_name%> merge value { \ + px: <%roundTo(locator.default_transform.pos[0], 10)%>, \ + py: <%roundTo(locator.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(locator.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(locator.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \ + } + } + } + } + + IF (has_cameras) { + REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera { + execute \ + at @s \ + on passengers \ + if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ + run block zzz/set_default_pose/as_camera_<%camera.storage_name%> { with entity @s data.cameras.<%camera.storage_name%> + $tp $(uuid) \ + ^<%roundTo(camera.default_transform.pos[0], 10)%> \ + ^<%roundTo(camera.default_transform.pos[1], 10)%> \ + ^<%roundTo(camera.default_transform.pos[2], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ + ~<%roundTo(camera.default_transform.head_rot[0], 10)%> + + data modify entity @s data.cameras.<%camera.storage_name%> merge value { \ + px: <%roundTo(camera.default_transform.pos[0], 10)%>, \ + py: <%roundTo(camera.default_transform.pos[1], 10)%>, \ + pz: <%roundTo(camera.default_transform.pos[2], 10)%>, \ + ry: <%roundTo(camera.default_transform.head_rot[1], 10)%>, \ + rx: <%roundTo(camera.default_transform.head_rot[0], 10)%> \ + } + } + } } } } } + +function set_default_pose { + # Changes the pose of the rig to the the default pose without interpolation + debug assert executed_as_root_entity + + IF (has_locators || has_cameras) { + function ./zzz/reset_floating_entities + } + + REPEAT (Object.values(rig.nodes).filter(node => node.type !== 'locator' && node.type !== 'camera')) as node { + execute \ + on passengers \ + if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] \ + run \ + data merge entity @s { \ + transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, \ + start_interpolation: -1 \ + } + } +} diff --git a/src/systems/datapackCompiler/1.21.5/animation.mcb b/src/systems/datapackCompiler/1.21.5/animation.mcb deleted file mode 100644 index a58f981d..00000000 --- a/src/systems/datapackCompiler/1.21.5/animation.mcb +++ /dev/null @@ -1,950 +0,0 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. - -dir <%export_namespace%> { - function on_load { - IF (use_storage_for_animation) { - REPEAT (animations) as animation { - data remove storage aj.<%export_namespace%>:animations <%animation.storage_name%> - } - <%animationStorage.join('\n')%> - } - <%% - animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) - }) - %%> - } - - function remove_animation_objectives { - <%% - animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) - }) - %%> - tellraw @a <%TELLRAW.UNINSTALL()%> - } - - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } - - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>, \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } - } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } - # Tick Playing Animations - REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick - } - IF (has_locators || has_cameras) { - # Update locator and camera orientations - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> - } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> - } - } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ - } - } - } - } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> - } - } - } - - dir animations { - REPEAT (animations) as animation { - dir <%animation.path_name%> { - # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. - function play { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } - - function stop { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/set_frame {frame: 0} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - } - - function pause { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - - function resume { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - - function next_frame { - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/set_frame with storage aj:temp args - } - - function apply_frame { - #ARGS: {frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) - execute at @s run function ./zzz/apply_frame with storage aj:temp args - } - - function tween { - # Attempts to smoothly transition from the currently playing animation into this one. - #ARGS: {duration: int, to_frame: int} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - function *<%export_namespace%>/animations/pause_all - - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) - - scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - execute at @s run function ./zzz/apply_frame {frame: 0} - $execute at @s run function ./zzz/apply_frame {frame: $(to_frame)} - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute on passengers store result entity @s interpolation_duration int 1 run scoreboard players get #this <%OBJECTIVES.I()%> - } - - dir zzz { - function on_tick { - # Tweening logic - scoreboard players remove @s <%OBJECTIVES.TWEEN_DURATION()%> 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 1.. run return 1 - execute if score @s <%OBJECTIVES.TWEEN_DURATION()%> matches 0 on passengers run \ - data modify entity @s interpolation_duration set value <%interpolation_duration%> - # Animation logic - IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { - # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ - block commands_keyframe_loop_patch { - function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - } - data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> - function ./apply_frame with storage aj:temp args - IF (animation.loop_mode === 'loop') { - # Loop the animation back to the start once it reaches the last frame. - # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-2 + animation.loop_delay%>.. \ - run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> - } ELSE IF (animation.loop_mode === 'hold') { - # Pause the animation at the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%>.. \ - run return run \ - function ../pause - } ELSE IF (animation.loop_mode === 'once') { - # Stop the animation once it reaches the last frame. - execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ - matches <%animation.duration-1%> \ - run return run \ - function ../stop - } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 - } - - IF (use_storage_for_animation) { - function set_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - execute on passengers run data modify entity @s[tag=!<%TAGS.GLOBAL_DATA()%>] start_interpolation set value -1 - return 1 - } - - function apply_frame { - #ARGS: {frame: int} - REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ - data modify entity @s {} merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } ELSE IF (['locator', 'camera'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ - data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> merge from \ - storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> - } - } - IF (animation.frames.some(v => v.variant)) { - $execute \ - if data storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant \ - unless entity @s[tag=<%TAGS.TRANSFORMS_ONLY()%>] \ - run { with storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).variant - #ARGS: {name: string, condition: string} - $execute $(condition)run function *<%export_namespace%>/variants/$(name)/apply - } - } - return 1 - } - } ELSE { - function set_frame { - # Sets the frame without interpolation - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - execute on passengers run \ - data modify entity @s[tag=aj.global.data] start_interpolation set value -1 - return 1 - } - - function apply_frame { - #ARGS: {frame: int} - $execute on passengers if entity @s[tag=aj.global.data] run \ - function ./frames/$(frame) with entity @s data.uuids - return 1 - } - # FIXME - %NEWLINE_PATCH% is a temporary solution to temporarily fix an MCB bug where extra newlines are being added to the output. - dir frames { - <%% - // A record of node uuid to INodeTransform. - // Keeps track of the last time a bone was updated. - // Only used for step keyframe interpolation. - let hasCommands = false - const lastActiveFrame = {} - const modifiedNodes = Object.values(animation.modified_nodes).filter(n => n.type !== 'struct').sort(nodeSorter); - for (const [frameIndex, frame] of animation.frames.entries()) { - const to_merge = {cameras: {}, locators: {}} - let frameFunc = ``; - for (const node of modifiedNodes) { - const transform = frame.node_transforms[node.uuid] - // Skip if the node doesn't have a transform for this frame. - if (!transform) continue - switch (node.type) { - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - const lastFrame = lastActiveFrame[node.uuid] - const isStepInterpolation = !!(lastFrame?.interpolation === 'step') - lastActiveFrame[node.uuid] = transform - - if (transform.interpolation === 'pre-post' || isStepInterpolation) { - frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: ${isStepInterpolation ? -1 : 0},` - + `interpolation_duration: ${isStepInterpolation ? 0 : interpolation_duration}` - + `}` - } else { - frameFunc += - `\n$data merge entity $(${node.type + '_' + node.storage_name})%NEWLINE_PATCH%{` - + `transformation: ${matrixToNbtFloatArray(transform.matrix).toString()},` - + `start_interpolation: 0,` - + `interpolation_duration: ${interpolation_duration}` - + `}` - } - ;hasCommands = true - break - } - case 'locator': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.locators[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - - if (transform.commands) { - if (node.config?.use_entity) { - frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `tp @s ~ ~ ~ ~ ~\n` - + `${transform.commands}` - + `\n}` - } else { - frameFunc += - `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ` - + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` - + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` - + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` - + `block ${frameIndex}_locator_commands%NEWLINE_PATCH%{\n` - + `${transform.commands}` - + `\n}` - } - } - break - } - case 'camera': { - const lastFrame = lastActiveFrame[node.uuid] - lastActiveFrame[node.uuid] = transform - ;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) { - to_merge.cameras[node.storage_name] = { - posx: transform.pos[0], - posy: transform.pos[1], - posz: transform.pos[2], - roty: transform.head_rot[1], - rotx: transform.head_rot[0] - }; - } - ;break - } - } - } - - if (Object.keys(to_merge.locators).length > 0 || Object.keys(to_merge.cameras).length > 0) { - frameFunc += `\ndata modify entity @s data merge value ${JSON.stringify(to_merge)}` - ;hasCommands = true - } - - if (frame.variants?.length) { - const variant = rig.variants[frame.variants[0]] - if (!variant) { - throw new Error(`Could not find Variant with uuid "${frame.variants[0]}" while generating frame "${frameIndex}" of animation "${animation.name}".`) - } - const execute_condition = frame.variants_execute_condition ? frame.variants_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function *${export_namespace}/variants/${variant.name}/apply` - ;hasCommands = true - } - - // Root commands keyframes. - if (frame.commands) { - const execute_condition = frame.commands_execute_condition ? frame.commands_execute_condition + ' ' : '' - frameFunc += `\nexecute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_commands%NEWLINE_PATCH%{\n${frame.commands}\n}` - ;hasCommands = true - } - ;if (frameFunc.length > 0) { - console.log('frameFunc', frameFunc) - frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}` - emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' ')) - } - } - %%> - } - } - } - } - } - function pause_all { - # Pauses all animations - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - } - } - } - - function summon { - #Args: {args:{variant: string, animation: string, frame: int, start_animation: boolean}} - # frame is ignored unless animation is specified. - - data modify storage aj:temp args set value {variant:'', animation:'', frame: 0} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ - } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } - - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out - } - } - - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this - } - } - - function *<%export_namespace%>/set_default_pose - # Animation Argument - # If the animation argument is provided, attempt to apply the animation. - execute if data storage aj:temp args.animation run block animation_arg/process { with storage aj:temp args - # If the animation argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{animation:''}} run return run block if_empty { - tellraw @a <%TELLRAW.ANIMATION_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Automatically set the frame argument to 0 if the frame argument is not provided. - # Takes advantage of `store result score` setting the score to 0 if the command fails. - execute \ - store result storage aj:temp args.frame int 1 \ - store result score #frame <%OBJECTIVES.I()%> \ - run \ - data get storage aj:temp args.frame - # If the frame argument is negative, return an error. - execute if score #frame <%OBJECTIVES.I()%> matches ..-1 run return run block no_negative { - # Tell the user that the frame argument cannot be negative. - tellraw @a <%TELLRAW.FRAME_CANNOT_BE_NEGATIVE()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the animation frame. - execute store success score #success <%OBJECTIVES.I()%> run block try_set_frame { with storage aj:temp args - # Make sure we're only applying transforms when setting the summon pose. - tag @s add <%TAGS.TRANSFORMS_ONLY()%> - $execute store success score #success <%OBJECTIVES.I()%> run function *<%export_namespace%>/animations/$(animation)/zzz/set_frame with storage aj:temp args - tag @s remove <%TAGS.TRANSFORMS_ONLY()%> - execute if score #success <%OBJECTIVES.I()%> matches 1 run return 1 - # If the set_frame function fails, the animation doesn't exist, so we return an error. - return fail - } - # If the set_frame function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_animation { with storage aj:temp args - # Tell the user that the provided animation doesn't exist, remove the rig, and list all available animations for this rig. - $tellraw @a <%TELLRAW.INVALID_ANIMATION('$(animation)', animations)%> - function *<%export_namespace%>/remove/this - } - - # If the animation is successfully applied, and the start_animation argument is set to true, start the animation. - execute if data storage aj:temp {args:{start_animation: true}} run block start_animation { with storage aj:temp args - $function *<%export_namespace%>/animations/$(animation)/resume - } - } - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> - } - } - - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } - } - - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - } - } - } - } - } - - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } - } - } - - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } - } - } - } - } - - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> - } - } - } - - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this - } - - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] - } - - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> - - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } - } - } - - execute on passengers run kill @s - kill @s - } - - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s - } - } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) - } - } - } - } - - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" - } ELSE { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" - } - } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } - } - } - } - } - # Return success to allow this function to be used in function conditions. - return 1 - } - } - } - } - } - - function apply_default_pose { - # Changes the pose of the rig to the the default pose with interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} - } - } - } - - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} - } - } - } -} diff --git a/src/systems/datapackCompiler/1.21.5/global.mcb b/src/systems/datapackCompiler/1.21.5/global.mcb new file mode 100644 index 00000000..c1f9476a --- /dev/null +++ b/src/systems/datapackCompiler/1.21.5/global.mcb @@ -0,0 +1,230 @@ +import ./global.mcbt + +dir global { + function on_load minecraft:load { + # Initialize Scoreboards + scoreboard objectives add <%OBJECTIVES.I()%> dummy + scoreboard objectives add <%OBJECTIVES.ID()%> dummy + scoreboard objectives add <%OBJECTIVES.IS_RIG_LOADED()%> dummy + scoreboard objectives add <%OBJECTIVES.TWEEN_DURATION()%> dummy + + scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 0 + + # Purposefully load gu first, as other functions depend on it. + function *global/gu/on_load + IF (debug_mode) { + scoreboard players reset * <%OBJECTIVES.IS_RIG_LOADED()%> + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_load + } + function #*global/on_load + } + + IF (debug_mode) { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_tick + } + } ELSE { + function on_tick minecraft:tick { + execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function #*global/root/on_tick + } + } + + tag functions on_load { + *<%export_namespace%>/on_load + } + + dir root { + tag functions on_tick { + *<%export_namespace%>/root/on_tick + } + + IF (debug_mode) { + function on_tick { + execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function *global/root/on_load + function #*global/root/on_tick + } + + function on_load { + # Check if the rig is outdated by comparing the function's rig_hash of the rig to the rig_hash stored in the entity data. + execute if function animated_java:global/util/is_rig_outdated run block outdated_warning/modify_rig { + data remove storage <%temp_storage%> args + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> args.uuid set from storage <%gu_storage%> out + # Get position + execute store result storage <%temp_storage%> args.x double 1 run data get entity @s Pos[0] 1 + execute store result storage <%temp_storage%> args.y double 1 run data get entity @s Pos[1] 1 + execute store result storage <%temp_storage%> args.z double 1 run data get entity @s Pos[2] 1 + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run { + # Get export namespace + data modify storage <%temp_storage%> args.export_namespace set from entity @s data.export_namespace + # Overwrite the rig hash so the warning doesn't show again. + data modify entity @s data.rig_hash set value '<%rig_hash%>' + } + block print { with storage <%temp_storage%> args + $tellraw @a <%TELLRAW.RIG_OUTDATED()%> + } + execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} + summon minecraft:text_display ~ ~ ~ { \ + Tags:[ \ + '<%TAGS.GLOBAL_ENTITY()%>', \ + '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', \ + '<%TAGS.NEW()%>' \ + ], \ + text:<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>, \ + alignment: 'center', \ + billboard: 'vertical', \ + shadow: true, \ + transformation:{ \ + translation:[0f,<%boundingBox[1]/16%>f,0f], \ + left_rotation:[0f,0f,0f,1f], \ + right_rotation:[0f,0f,0f,1f], \ + scale:[1f,1f,1f] \ + } \ + } + + ride @n[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + distance=..1 \ + ] mount @s + + tag @n[ \ + type=minecraft:text_display, \ + tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \ + distance=..1 \ + ] remove <%TAGS.NEW()%> + } + scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 + } + } + } + + dir remove { + # Removes every entity related to Animated Java. + function everything { + kill @e[tag=<%TAGS.GLOBAL_ENTITY()%>] + } + + function entity_stack { + execute on passengers if entity @s[type=!player] run function ^0 + kill @s + } + + function entity_stack_by_uuid { + #ARGS: {uuid: string} + $execute as $(uuid) run function ./entity_stack + } + + # Removes locators and cameras owned by the rig, even if they're not included in the currently loaded export. + function outdated_rig { + # Assert that the function is being executed as a root entity. + debug assert executed_as_root_entity + + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data { + data remove storage <%temp_storage%> args + data remove storage <%temp_storage%> uuids + data modify storage <%temp_storage%> uuids set from entity @s data.uuids + execute store result score #aj.length <%OBJECTIVES.I()%> run data get storage <%temp_storage%> uuids + + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run block loop_over_uuids { + data modify storage <%temp_storage%> args.current_uuid set from storage <%temp_storage%> uuids[-1].uuid + data remove storage <%temp_storage%> uuids[-1] + function ./entity_stack_by_uuid with storage <%temp_storage%> args + + scoreboard players remove #aj.length <%OBJECTIVES.I()%> 1 + execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run function ^0 + } + } + + function ./entity_stack + } + } + + dir util { + # Recurses through the passengers of the context entity and appends their UUIDs to `storage <%temp_storage%> uuids`. + function get_entity_stack_uuids { + data remove storage <%temp_storage%> uuids + execute on passengers run { + function *global/gu/get_entity_uuid_string + data modify storage <%temp_storage%> uuids append from storage <%gu_storage%> out + execute on passengers run function ^0 + } + } + # Returns 1 if the rig is outdated, else returns 0. + function is_rig_outdated { + execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run return run { with entity @s data + # REVIEW - The replace here is a bit hacky. + # Because this is a core function, I need to create a generic version of + # project_storage that uses a macro to get the export namespace instead of hardcoding it. + $execute if data storage <%project_storage.replace(export_namespace, '$(export_namespace)')%> {rig_hash:'$(rig_hash)'} run return 0 + return 1 + } + } + } + + # Thanks Gibbsly for this code! https://github.com/gibbsly/gu + dir gu { + function on_load { + scoreboard players set 256 <%OBJECTIVES.I()%> 256 + data modify storage <%gu_storage%> hex_chars set value \ + <%JSON.stringify([...Array(0x100).keys()].map(v => {const x = v.toString(16); return x.length > 1 ? x : '0' + x}))%> + } + + function get_entity_uuid_string { + data modify storage <%gu_storage%> temp set value {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,a:0,b:0,c:0,d:0,e:0,f:0} + data modify storage <%gu_storage%> in set from entity @s UUID + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[0] + execute store result storage <%gu_storage%> temp.0 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.1 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.2 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.3 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[1] + execute store result storage <%gu_storage%> temp.4 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.5 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.6 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.7 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[2] + execute store result storage <%gu_storage%> temp.8 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.9 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.a int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.b int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[3] + execute store result storage <%gu_storage%> temp.c int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.d int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.e int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%> + execute store result storage <%gu_storage%> temp.f int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%> + + block { with storage <%gu_storage%> temp + REPEAT (0, 15) as i { + $data modify storage <%gu_storage%> temp.<%i.toString(16)%> set from storage <%gu_storage%> hex_chars[$(<%i.toString(16)%>)] + } + } + + block { with storage <%gu_storage%> temp + $data modify storage <%gu_storage%> out set value "$(3)$(2)$(1)$(0)-$(7)$(6)-$(5)$(4)-$(b)$(a)-$(9)$(8)$(f)$(e)$(d)$(c)" + } + } + } +} + +dir <%export_namespace%> { + function on_load { + function *<%export_namespace%>/invalid_version_warning + } + + function invalid_version_warning { + tellraw @a <%TELLRAW.INVALID_VERSION()%> + } +} diff --git a/src/systems/datapackCompiler/1.21.5/static.mcb b/src/systems/datapackCompiler/1.21.5/static.mcb deleted file mode 100644 index b611a990..00000000 --- a/src/systems/datapackCompiler/1.21.5/static.mcb +++ /dev/null @@ -1,488 +0,0 @@ -# REVIEW - All uses of `'<%arguments[1].functions.at(-1)%>'` are temporary and should be replaced with an officially supported method once MCB is updated. - -dir <%export_namespace%> { - function invalid_version_warning { - # This function will contain a tellraw if the datapack is loaded in the wrong version. - } - - dir root { - IF (show_outdated_warning) { - function on_load { - # Only run if the entity is an instance of this rig. - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - # Check if the rig is outdated by comparing the function's rigHash of the rig to the rigHash stored in the entity data. - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - unless data entity @s {data:{rigHash: '<%rigHash%>'}} \ - on vehicle \ - run block zzz/check_rig_hash { - data remove storage aj:temp args - execute store result storage aj:temp args.x int 1 store result score #this.x aj.i run data get entity @s Pos[0] 1 - execute store result storage aj:temp args.y int 1 store result score #this.y aj.i run data get entity @s Pos[1] 1 - execute store result storage aj:temp args.z int 1 store result score #this.z aj.i run data get entity @s Pos[2] 1 - block show_outdated_warning { with storage aj:temp args - $tellraw @a <%TELLRAW.RIG_OUTDATED()%> - } - data modify entity @s data.rigHash set value '<%rigHash%>' - execute on passengers run data merge entity @s {Glowing: 1b, glow_color_override: <%0xff0000%>} - summon minecraft:text_display ~ ~ ~ { \ - Tags:['<%TAGS.GLOBAL_ENTITY()%>', '<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', '<%TAGS.NEW()%>'], \ - text:'<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>', \ - alignment: 'center', \ - billboard: 'vertical', \ - transformation:{ \ - translation:[0f,<%boundingBox[1]/16%>f,0f], \ - left_rotation:[0f,0f,0f,1f], \ - right_rotation:[0f,0f,0f,1f], \ - scale:[1f,1f,1f] \ - } \ - } - ride @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] mount @s - tag @e[type=minecraft:text_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] remove <%TAGS.NEW()%> - } - scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1 - } - } - function on_tick { - execute unless entity @s[tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return 0 - IF (show_outdated_warning) { - execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function #*global/root/on_load - } - IF (has_locators || has_cameras) { - # Update locator and camera orientations - execute \ - on passengers \ - if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ - run block zzz/on_tick/locators_and_cameras { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> - IF (node.config?.use_entity) { - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - tp @s ~ ~ ~ ~ ~ - IF (node.config?.sync_passenger_rotation) { - execute on passengers run rotate @s ~ ~ - } - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> - } - } ELSE IF (node.config?.ticking_commands) { - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { - <%% - if (node.config?.ticking_commands) { - emit.mcb(node.config.ticking_commands) - } - %%> - } - } - } - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> - $execute \ - as $(uuid) \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run tp @s ~ ~ ~ ~ ~ - } - } - } - } - # Rotation Logic - execute at @s on passengers run rotate @s ~ ~ - # Custom tick commands - IF (root_ticking_commands) { - <%% - emit.mcb(root_ticking_commands) - %%> - } - } - } - - function summon { - #Args: {args:{variant: string}} - - data modify storage aj:temp args set value {variant:''} - $execute store success score #success <%OBJECTIVES.I()%> run data modify storage aj:temp args set value $(args) - - summon minecraft:item_display ~ ~ ~ { \ - Tags:[ \ - '<%TAGS.NEW()%>', \ - '<%TAGS.GLOBAL_ENTITY()%>', \ - '<%TAGS.GLOBAL_ROOT()%>', \ - '<%TAGS.PROJECT_ENTITY(export_namespace)%>', \ - '<%TAGS.PROJECT_ROOT(export_namespace)%>' \ - ], \ - teleport_duration: 0, \ - interpolation_duration: <%interpolation_duration%>, \ - Passengers:<%root_entity_passengers%>, \ - } - execute as @e[type=minecraft:item_display,tag=<%TAGS.NEW()%>,limit=1,distance=..0.01] run block zzz/summon/as_root_entity { - execute store result score @s <%OBJECTIVES.ID()%> run scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 1 - - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block as_data_entity { - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.data_data set from storage aj:uuid main.out - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - summon <%locator.config.entity_type%> \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(locator, rig)%>} - execute \ - as @n[ \ - type=<%locator.config.entity_type%>, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ - ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { - tag @s remove <%TAGS.NEW()%> - - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(locator.default_transform.pos[0], 10)%> \ - ^<%roundTo(locator.default_transform.pos[1], 10)%> \ - ^<%roundTo(locator.default_transform.pos[2], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(locator.default_transform.head_rot[0], 10)%> - <%% - if (locator.config.summon_commands) { - emit.mcb(locator.config.summon_commands) - } - %%> - } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out - } - - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - summon minecraft:item_display \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - {Tags:<%getNodeTags(camera, rig)%>, teleport_duration: 2} - execute \ - as @n[ \ - type=minecraft:item_display, \ - tag=<%TAGS.NEW()%>, \ - tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ - ] \ - run block ../as_camera/<%camera.path_name%> { - tag @s remove <%TAGS.NEW()%> - function *global/internal/gu/convert_uuid_array_to_string - tp @s \ - ^<%roundTo(camera.default_transform.pos[0], 10)%> \ - ^<%roundTo(camera.default_transform.pos[1], 10)%> \ - ^<%roundTo(camera.default_transform.pos[2], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ - ~<%roundTo(camera.default_transform.head_rot[0], 10)%> - } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out - } - - REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ - function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out - } - } - - # Variant Arguement - IF (Object.keys(rig.variants).length > 1) { - execute if data storage aj:temp args.variant run block variant_arg/process { with storage aj:temp args - # If the variant argument is *explicitly* set to an empty string, return an error. - execute if data storage aj:temp {args:{variant:''}} run return run block if_empty { - # Tell the user that the variant cannot be empty. - tellraw @a <%TELLRAW.VARIANT_CANNOT_BE_EMPTY()%> - function *<%export_namespace%>/remove/this - } - # Attempt to apply the requested variant. - # We get the success of the `try_apply` function in just in case the user's arguments are *very* wrong. - execute store success score #success <%OBJECTIVES.I()%> run block try_apply { with storage aj:temp args - $execute if function animated_java:<%export_namespace%>/variants/$(variant)/apply run return 1 - # If the apply function fails, the variant doesn't exist, so we return an error. - return fail - } - # If the apply function failed, return an error. - execute unless score #success <%OBJECTIVES.I()%> matches 1 run return run block invalid_variant { with storage aj:temp args - # Tell the user that the provided variant doesn't exist, remove the rig, and list all available variants for this rig. - $tellraw @a <%TELLRAW.INVALID_VARIANT('$(variant)', rig.variants)%> - function *<%export_namespace%>/remove/this - } - } - } ELSE { - execute if data storage aj:temp args.variant run block zzz/variant_arg/no_variants_warning { - tellraw @a <%TELLRAW.NO_VARIANTS()%> - function *<%export_namespace%>/remove/this - } - } - - function *<%export_namespace%>/set_default_pose - # Align the position and rotation of the root with the summon context, then align the passengers with the root. - tp @s ~ ~ ~ ~ ~ - execute at @s on passengers run rotate @s ~ ~ - # Apply teleport duration - data modify entity @s teleport_duration set value <%teleportation_duration%> - execute on passengers run data modify entity @s teleport_duration set value <%teleportation_duration%> - # Add in User-defined `On-Summon Commands` - <%% - if (custom_summon_commands) emit.mcb(custom_summon_commands) - %%> - - # Remove the NEW tag from the root entity, and it's passengers. - tag @s remove <%TAGS.NEW()%> - execute on passengers run tag @s remove <%TAGS.NEW()%> - } - } - - IF (has_entity_locators) { - function as_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_locator/as_data { {name: $(name)} - $data modify storage aj:temp args.uuid set from entity @s data.uuids.locator_$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND_ENTITY()%> - } - } - - function as_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - } - } - } - } - } - - IF (has_locators) { - function at_locator { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/at_locator/as_data { {name: $(name)} - $execute unless data entity @s {data:{locators:{$(name):{}}}} run return run tellraw @a <%TELLRAW.LOCATOR_NOT_FOUND()%> - $data modify storage aj:temp args merge from entity @s data.locators.$(name) - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } - } - } - - function at_all_locators { - #ARGS: {command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args.command set value '$(command)' - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> - block execute_at_transform { with storage aj:temp args - $execute \ - positioned ^$(posx) ^$(posy) ^$(posz) \ - rotated ~$(roty) ~$(rotx) \ - run $(command) - } - } - } - } - } - - IF (has_cameras) { - function as_camera { - #ARGS: {name: string, command: string} - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - data remove storage aj:temp args - $data modify storage aj:temp args merge value {name:'$(name)', command:'$(command)'} - $execute \ - on passengers \ - if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] \ - run block zzz/as_camera/as_data { {name: $(name)} - data modify storage aj:temp args.uuid set from entity @s data.cameras.$(name) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 0 - block execute_as_uuid { with storage aj:temp args - $execute as $(uuid) at @s run $(command) - scoreboard players set #aj.check <%OBJECTIVES.I()%> 1 - } - execute if score #aj.check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.CAMERA_NOT_FOUND_ENTITY()%> - } - } - } - - dir remove { - function all { - # Removes all instances of this rig from the world. - execute as @e[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run function *<%export_namespace%>/remove/this - } - - function entities { - # Removes all entities related to this rig from the world. - kill @e[tag=<%TAGS.PROJECT_ENTITY(export_namespace)%>] - } - - function this { - # Removes the rig this function is executed as. - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - - <%% - if (custom_remove_commands) emit.mcb(custom_remove_commands) - %%> - - IF (has_entity_locators || has_cameras) { - execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> - } - REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> - } - } - } - - execute on passengers run kill @s - kill @s - } - - dir zzz { - dir this { - function kill_locator { - #ARGS: {uuid: string} - # Recursively remove stacked locator entities - $execute as $(uuid) run block recursively_kill_passengers { - execute on passengers run function ^0 - kill @s - } - } - function kill_camera { - #ARGS: {uuid: string} - $kill $(uuid) - } - } - } - } - - IF (Object.keys(rig.variants).length > 1) { - dir variants { - REPEAT (Object.values(rig.variants)) as variant { - dir <%variant.name%> { - function apply { - IF (show_function_errors) { - # Assert that the function is being executed as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { - IF (variant.models[node.uuid] !== undefined) { - IF (variant.models[node.uuid].model === null) { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" - } ELSE { - data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>" - } - } - IF (node.configs.variants[variant.uuid]) { - <%% - global.config = BoneConfig.fromJSON(node.configs.variants[variant.uuid]) - %%> - IF (!global.config.isDefault()) { - data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%> - } - } - } - } - } - # Return success to allow this function to be used in function conditions. - return 1 - } - } - } - } - } - - function set_default_pose { - # Changes the pose of the rig to the the default pose without interpolation - IF (show_function_errors) { - # Assert that the function is being run as the root entity. - execute unless entity @s[type=minecraft:item_display,tag=<%TAGS.PROJECT_ROOT(export_namespace)%>] run return run \ - function *global/errors/function_not_executed_as_root_entity \ - {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} - } - REPEAT (Object.values(rig.nodes)) as node { - IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ - data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} - } - } - } -} diff --git a/src/systems/datapackCompiler/1.21.5/tests.mcb b/src/systems/datapackCompiler/1.21.5/tests.mcb deleted file mode 100644 index e69de29b..00000000 diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index 7765baa1..8064da58 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -1,41 +1,37 @@ -import { - NbtByte, - NbtCompound, - NbtFloat, - NbtInt, - NbtList, - NbtString, - NbtTag, -} from 'deepslate/lib/nbt' +import { NbtByte, NbtCompound, NbtFloat, NbtInt, NbtList, NbtString } from 'deepslate/lib/nbt' +import { projectTargetVersionIsAtLeast } from 'src/formats/blueprint' import { MAX_PROGRESS, PROGRESS, PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { BoneConfig, TextDisplayConfig } from '../../nodeConfigs' import { isFunctionTagPath } from '../../util/fileUtil' import { + DataPackTag, + type FunctionTagJSON, getDataPackFormat, - IFunctionTag, - mergeTag, + getNextSupportedVersion, parseBlock, parseDataPackPath, parseResourceLocation, } from '../../util/minecraftUtil' -import { eulerFromQuaternion, floatToHex, roundTo, tinycolorToDecimal } from '../../util/misc' +import { eulerFromQuaternion, roundTo } from '../../util/misc' import { MSLimiter } from '../../util/msLimiter' import { Variant } from '../../variants' -import { IRenderedAnimation } from '../animationRenderer' +import type { IRenderedAnimation } from '../animationRenderer' import mcbFiles from '../datapackCompiler/mcbFiles' import { IntentionalExportError } from '../exporter' -import { AJMeta, MinecraftVersion, PackMeta, PackMetaFormats } from '../global' -import { JsonText } from '../minecraft/jsonText' -import { AnyRenderedNode, IRenderedRig, IRenderedVariant } from '../rigRenderer' +import { AJMeta, PackMeta, SUPPORTED_MINECRAFT_VERSIONS } from '../global' +import { JsonText } from '../jsonText' +import { JsonTextParser } from '../jsonText/parser' +import type { AnyRenderedNode, IRenderedRig } from '../rigRenderer' import { arrayToNbtFloatArray, - ExportedFile, + type ExportedFile, matrixToNbtFloatArray, replacePathPart, transformationToNbt, } from '../util' -import { compile } from './compiler' +import { compileMcbProject } from './mcbCompiler' import { TAGS } from './tags' +import TELLRAW from './tellraw' const BONE_TYPES = ['bone', 'text_display', 'item_display', 'block_display'] @@ -47,14 +43,6 @@ namespace OBJECTIVES { export const TWEEN_DURATION = () => 'aj.tween_duration' } -// 🗡 🏹 🔱 🧪 ⚗ 🎣 🛡 🪓 ⛏ ☠ ☮ ☯ ♠ Ω ♤ ♣ ♧ ❤ ♥ ♡ -// ♦ ♢ ★ ☆ ☄ ☽ ☀ ☁ ☂ ☃ ◎ ☺ ☻ ☹ ☜ ☞ ♪ ♩ ♫ ♬ ✂ ✉ ∞ ♂ ♀ ❤ ™ ® -// © ✘ ■ □ ▲ △ ▼ ▽ ◆ ◇ ○ ◎ ● Δ ʊ ღ ₪ ½ ⅓ ⅔ ¼ ¾ ⅛ ⅜ ⅝ ⅞ ∧ ∨ ∩ -// ⊂ ⊃ ⊥ ∀ Ξ Γ ɐ ə ɘ ε β ɟ ɥ ɯ ɔ и ɹ ʁ я ʌ ʍ λ ч ∞ Σ Π Ⓐ Ⓑ Ⓒ -// Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ -// ▓ □〓≡ ╝╚╔╗╬ ╓ ╩┌ ┐└ ┘ ↑ ↓ → ← ↔ ▀▐ ░ ▒▬ ♦ ◘ -// → ✎ ❣ ✚ ✔ ✖ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ⊻ ⊼ ⊽ ⋃ ⌀ ⌂ - function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { const tags: string[] = [] @@ -65,7 +53,7 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { // Root is ignored } else if (n.parent) { parentNames.push({ - name: rig.nodes[n.parent].path_name, + name: rig.nodes[n.parent].storage_name, type: rig.nodes[n.parent].type, }) recurseParents(rig.nodes[n.parent]) @@ -80,11 +68,11 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.NEW(), TAGS.GLOBAL_ENTITY(), TAGS.GLOBAL_NODE(), - TAGS.GLOBAL_NODE_NAMED(node.path_name), + TAGS.GLOBAL_NODE_NAMED(node.storage_name), // Project TAGS.PROJECT_ENTITY(Project!.animated_java.export_namespace), TAGS.PROJECT_NODE(Project!.animated_java.export_namespace), - TAGS.PROJECT_NODE_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_NODE_NAMED(Project!.animated_java.export_namespace, node.storage_name) ) if (!hasParent) { @@ -94,19 +82,22 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'bone': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_BONE(), - TAGS.GLOBAL_BONE_TREE(node.path_name), // Tree includes self - TAGS.GLOBAL_BONE_TREE_BONE(node.path_name), // Tree includes self + TAGS.GLOBAL_BONE_TREE(node.storage_name), // Tree includes self + TAGS.GLOBAL_BONE_TREE_BONE(node.storage_name), // Tree includes self // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_BONE(Project!.animated_java.export_namespace), - TAGS.PROJECT_BONE_NAMED(Project!.animated_java.export_namespace, node.path_name), - TAGS.PROJECT_BONE_TREE(Project!.animated_java.export_namespace, node.path_name), // Tree includes self - TAGS.PROJECT_BONE_TREE_BONE(Project!.animated_java.export_namespace, node.path_name) // Tree includes self + TAGS.PROJECT_BONE_NAMED(Project!.animated_java.export_namespace, node.storage_name), + TAGS.PROJECT_BONE_TREE(Project!.animated_java.export_namespace, node.storage_name), // Tree includes self + TAGS.PROJECT_BONE_TREE_BONE( + Project!.animated_java.export_namespace, + node.storage_name + ) // Tree includes self ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -146,17 +137,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'item_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_ITEM_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_ITEM_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_ITEM_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -198,17 +189,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'block_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_BLOCK_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_BLOCK_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_BLOCK_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -250,17 +241,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'text_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_TEXT_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_TEXT_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_TEXT_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -305,7 +296,10 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.GLOBAL_LOCATOR(), // Project TAGS.PROJECT_LOCATOR(Project!.animated_java.export_namespace), - TAGS.PROJECT_LOCATOR_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_LOCATOR_NAMED( + Project!.animated_java.export_namespace, + node.storage_name + ) ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -349,7 +343,10 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.GLOBAL_CAMERA(), // Project TAGS.PROJECT_CAMERA(Project!.animated_java.export_namespace), - TAGS.PROJECT_CAMERA_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_CAMERA_NAMED( + Project!.animated_java.export_namespace, + node.storage_name + ) ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -397,281 +394,24 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { return new NbtList(tags.sort().map(v => new NbtString(v))) } -const TELLRAW_PREFIX = () => - new JsonText([ - { text: '\n[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - '] ', - [ - { text: '(from ', color: 'gray', italic: true }, - Project!.animated_java.export_namespace, - ')', - ], - ' -> ', - ]) - -const TELLRAW_ERROR_PREFIX = () => - new JsonText([TELLRAW_PREFIX(), { text: 'ERROR: ', color: 'red' }, '\n ']) - -const TELLRAW_SUFFIX = () => new JsonText(['\n']) - -const TELLRAW_LEARN_MORE_LINK = (url: string) => - new JsonText([ - '\n ', - { - text: 'Click here to learn more', - color: 'blue', - underlined: true, - italic: true, - clickEvent: { action: 'open_url', value: url }, - }, - ]) - -namespace TELLRAW { - export const RIG_OUTDATED = () => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'The ', color: 'red' }, - { text: Project!.animated_java.export_namespace, color: 'yellow' }, - { text: ' rig instance at', color: 'red' }, - [ - { text: ' [', color: 'yellow' }, - { score: { name: '#this.x', objective: OBJECTIVES.I() } }, - ', ', - { score: { name: '#this.y', objective: OBJECTIVES.I() } }, - ', ', - { score: { name: '#this.z', objective: OBJECTIVES.I() } }, - ']', - ], - { - text: ' is outdated! It will not function correctly and should be removed or re-summoned.', - color: 'red', - }, - '\n ', - { - text: '[Click Here to Teleport to the Rig Instance]', - clickEvent: { - action: 'suggest_command', - value: '/tp @s $(x) $(y) $(z)', - }, - color: 'aqua', - underlined: true, - }, - TELLRAW_SUFFIX(), - ]) - export const RIG_OUTDATED_TEXT_DISPLAY = () => - new JsonText([ - '', - { - text: 'This rig instance is outdated!\\nIt will not function correctly and should be removed or re-summoned.', - color: 'red', - }, - ]) - export const FUNCTION_NOT_EXECUTED_AS_ROOT_ERROR = (functionName: string) => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { - text: 'This function', - color: 'blue', - underlined: true, - hoverEvent: { - action: 'show_text', - contents: [{ text: functionName, color: 'yellow' }], - }, - }, - { text: " must be executed as the rig's root entity.", color: 'red' }, - '\n', - TELLRAW_LEARN_MORE_LINK( - 'https://animated-java.dev/docs/rigs/controlling-a-rig-instance' - ), - TELLRAW_SUFFIX(), - ]) - // Summon Function - export const VARIANT_CANNOT_BE_EMPTY = () => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'variant', color: 'yellow' }, - { text: ' cannot be an empty string.', color: 'red' }, - TELLRAW_SUFFIX(), - ]) - export const INVALID_VARIANT = ( - variantName: string, - variants: Record - ) => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'The variant ', color: 'red' }, - { text: variantName, color: 'yellow' }, - { text: ' does not exist.', color: 'red' }, - '\n ', - { text: ' ≡ ', color: 'white' }, - { text: 'Available Variants:', color: 'green' }, - ...Object.values(variants).map( - variant => - new JsonText([ - '\n ', - ' ', - ' ', - { text: ' ● ', color: 'gray' }, - { text: variant.name, color: 'yellow' }, - ]) - ), - TELLRAW_SUFFIX(), - ]) - export const ANIMATION_CANNOT_BE_EMPTY = () => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'animation', color: 'yellow' }, - { text: ' cannot be an empty string.', color: 'red' }, - TELLRAW_SUFFIX(), - ]) - export const FRAME_CANNOT_BE_NEGATIVE = () => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'frame', color: 'yellow' }, - { text: ' must be a non-negative integer.', color: 'red' }, - TELLRAW_SUFFIX(), - ]) - export const INVALID_ANIMATION = (animationName: string, animations: IRenderedAnimation[]) => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'The animation ', color: 'red' }, - { text: animationName, color: 'yellow' }, - { text: ' does not exist.', color: 'red' }, - '\n ', - { text: ' ≡ ', color: 'white' }, - { text: 'Available Animations:', color: 'green' }, - ...animations.map( - anim => - new JsonText([ - '\n ', - ' ', - ' ', - { text: ' ● ', color: 'gray' }, - { text: anim.path_name, color: 'yellow' }, - ]) - ), - TELLRAW_SUFFIX(), - ]) - export const NO_VARIANTS = () => - new JsonText([ - '', - TELLRAW_ERROR_PREFIX(), - { text: 'No variants are available.', color: 'red' }, - TELLRAW_SUFFIX(), - ]) - export const INVALID_VERSION = () => - new JsonText([ - TELLRAW_ERROR_PREFIX(), - [ - { - text: 'Attempting to load an Animated Java Data Pack that was exported for ', - color: 'red', - }, - { - text: `Minecraft ${Project!.animated_java.target_minecraft_versions}`, - color: 'aqua', - }, - { text: ' in the wrong version!', color: 'red' }, - { - text: '\n Please ensure that the data pack is loaded in the correct version, or that your blueprint settings are configured to target the correct version(s) of Minecraft.', - color: 'yellow', - }, - ], - TELLRAW_SUFFIX(), - ]) - export const UNINSTALL = () => - new JsonText([ - TELLRAW_PREFIX(), - { text: 'Successfully removed known animation scoreboard objectives.', color: 'red' }, - { - text: "\nIf you have exported multiple times you may have to manually remove some objectives from previous exports manually, as Animated Java can only remove the latest export's objectives.", - color: 'gray', - italic: true, - }, - TELLRAW_SUFFIX(), - ]) - export const LOCATOR_NOT_FOUND = () => - new JsonText([ - TELLRAW_PREFIX(), - { text: 'Locator ', color: 'red' }, - { nbt: 'args.name', storage: 'aj:temp', color: 'aqua' }, - { text: ' not found!', color: 'red' }, - { - text: "\nPlease ensure that it's name is spelled correctly.", - color: 'gray', - italic: true, - }, - TELLRAW_SUFFIX(), - ]) - export const LOCATOR_NOT_FOUND_ENTITY = () => - new JsonText([ - TELLRAW_PREFIX(), - { text: 'Locator ', color: 'red' }, - { nbt: 'args.name', storage: 'aj:temp', color: 'aqua' }, - { text: ' not found!', color: 'red' }, - { - text: '\nPlease ensure that the locator has ', - color: 'gray', - italic: true, - }, - { - text: 'Use Entity', - color: 'yellow', - italic: true, - }, - { - text: " enabled in it's config, and it's name is spelled correctly.", - color: 'gray', - italic: true, - }, - TELLRAW_SUFFIX(), - ]) - export const CAMERA_NOT_FOUND_ENTITY = () => - new JsonText([ - TELLRAW_PREFIX(), - { text: 'Camera ', color: 'red' }, - { nbt: 'args.name', storage: 'aj:temp', color: 'aqua' }, - { text: ' not found!', color: 'red' }, - { - text: "\nPlease ensure that it's name is spelled correctly.", - color: 'gray', - italic: true, - }, - TELLRAW_SUFFIX(), - ]) -} - async function generateRootEntityPassengers( - version: MinecraftVersion, + version: SUPPORTED_MINECRAFT_VERSIONS, rig: IRenderedRig, rigHash: string ) { const aj = Project!.animated_java const passengers: NbtList = new NbtList() - const { locators, cameras, uuids } = createPassengerStorage(rig) - const dataEntity = new NbtCompound() - switch (version) { - case '1.20.4': - case '1.20.5': - case '1.21.0': - case '1.21.2': - case '1.21.4': - dataEntity.set('id', new NbtString('minecraft:marker')) - break - case '1.21.5': - dataEntity.set('id', new NbtString('minecraft:item_display')) + if (!compareVersions('1.21.5', version) /* >= 1.21.5 */) { + dataEntity.set('id', new NbtString('minecraft:item_display')) + } else if (!compareVersions('1.20.4', version) /* >= 1.20.4 */) { + dataEntity.set('id', new NbtString('minecraft:marker')) + } else { + throw new Error(`Minecraft version is below minimum supported version 1.20.4!`) } + passengers.add( dataEntity .set( @@ -687,10 +427,8 @@ async function generateRootEntityPassengers( .set( 'data', new NbtCompound() - .set('rigHash', new NbtString(rigHash)) - .set('locators', locators) - .set('cameras', cameras) - .set('uuids', uuids) + .set('rig_hash', new NbtString(rigHash)) + .set('export_namespace', new NbtString(aj.export_namespace)) ) ) @@ -702,10 +440,13 @@ async function generateRootEntityPassengers( const tags = getNodeTags(node, rig) passenger.set('Tags', tags) - switch (node.type) { - case 'bone': { - passenger.set('id', new NbtString('minecraft:item_display')) - passenger.set( + if (BONE_TYPES.includes(node.type)) { + passenger + .set('height', new NbtFloat(aj.render_box[1])) + .set('width', new NbtFloat(aj.render_box[0])) + .set('teleport_duration', new NbtInt(0)) + .set('interpolation_duration', new NbtInt(aj.interpolation_duration)) + .set( 'transformation', new NbtCompound() .set('translation', arrayToNbtFloatArray([0, 0, 0])) @@ -713,148 +454,117 @@ async function generateRootEntityPassengers( .set('right_rotation', arrayToNbtFloatArray([0, 0, 0, 1])) .set('scale', arrayToNbtFloatArray([0, 0, 0])) ) - passenger.set('interpolation_duration', new NbtInt(aj.interpolation_duration)) - passenger.set('teleport_duration', new NbtInt(0)) - passenger.set('item_display', new NbtString('head')) - const item = new NbtCompound() + } + + switch (node.type) { + case 'bone': { + const item = new NbtCompound().set('id', new NbtString(aj.display_item)) + passenger + .set('id', new NbtString('minecraft:item_display')) + .set('item', item) + .set('item_display', new NbtString('head')) + const variantModel = rig.variants[Variant.getDefault().uuid].models[uuid] if (!variantModel) { - throw new Error(`Model for bone '${node.path_name}' not found!`) + throw new Error(`Model for bone '${node.storage_name}' not found!`) } - passenger.set('item', item.set('id', new NbtString(aj.display_item))) - switch (version) { - case '1.20.4': { - item.set( - 'tag', - new NbtCompound().set( - 'CustomModelData', - new NbtInt(variantModel.custom_model_data) - ) - ) - // Count defaults to 1, but only in versions above 1.20.4 - item.set('Count', new NbtInt(1)) - break - } - case '1.20.5': - case '1.21.0': { - item.set( - 'components', - new NbtCompound().set( + + if (!compareVersions('1.21.4', version) /* >= 1.21.4 */) { + item.set( + 'components', + new NbtCompound() + .set('minecraft:item_model', new NbtString(variantModel.item_model)) + .set( 'minecraft:custom_model_data', - new NbtInt(variantModel.custom_model_data) - ) - ) - break - } - case '1.21.2': { - item.set( - 'components', - new NbtCompound().set( - 'minecraft:item_model', - new NbtString(variantModel.item_model) + new NbtCompound().set( + 'strings', + new NbtList([new NbtString('default')]) + ) ) + ) + } else if (!compareVersions('1.21.2', version) /* >= 1.21.2 */) { + item.set( + 'components', + new NbtCompound().set( + 'minecraft:item_model', + new NbtString(variantModel.item_model) ) - break - } - case '1.21.4': - case '1.21.5': { - item.set( - 'components', - new NbtCompound() - .set('minecraft:item_model', new NbtString(variantModel.item_model)) - .set( - 'minecraft:custom_model_data', - new NbtCompound().set( - 'strings', - new NbtList([new NbtString('default')]) - ) - ) + ) + } else if (!compareVersions('1.20.5', version) /* >= 1.20.5 */) { + item.set( + 'components', + new NbtCompound().set( + 'minecraft:custom_model_data', + new NbtInt(variantModel.custom_model_data) ) - break - } - default: { - throw new Error( - `Unsupported Minecraft version '${version}' for item display!` + ) + } else if (!compareVersions('1.20.4', version) /* >= 1.20.4 */) { + item.set( + 'tag', + new NbtCompound().set( + 'CustomModelData', + new NbtInt(variantModel.custom_model_data) ) - } + ) + // `Count` does not default to 1. + // However, `count` does default to 1 in later versions, so we only need this for 1.20.4. + item.set('Count', new NbtInt(1)) } if (node.configs?.default) { BoneConfig.fromJSON(node.configs.default).toNBT(passenger) } - - passenger.set('height', new NbtFloat(aj.bounding_box[1])) - passenger.set('width', new NbtFloat(aj.bounding_box[0])) break } case 'text_display': { - passenger.set('id', new NbtString('minecraft:text_display')) - passenger.set( - 'transformation', - new NbtCompound() - .set('translation', arrayToNbtFloatArray([0, 0, 0])) - .set('left_rotation', arrayToNbtFloatArray([0, 0, 0, 1])) - .set('right_rotation', arrayToNbtFloatArray([0, 0, 0, 1])) - .set('scale', arrayToNbtFloatArray([0, 0, 0])) - ) - passenger.set('interpolation_duration', new NbtInt(aj.interpolation_duration)) - passenger.set('teleport_duration', new NbtInt(0)) - - passenger.set('height', new NbtFloat(aj.bounding_box[1])) - passenger.set('width', new NbtFloat(aj.bounding_box[0])) - - switch (version) { - case '1.20.4': - case '1.20.5': - case '1.21.0': - case '1.21.2': - case '1.21.4': - passenger.set( - 'text_display', - new NbtString( - node.text?.toString() ?? `{ "text": "Invalid Text Component" }` - ) - ) - break - case '1.21.5': - passenger.set( - 'text', - NbtTag.fromString( - node.text - ? node.text.toString() - : "{ text: 'Invalid Text Component' }" - ) + passenger + .set('id', new NbtString('minecraft:text_display')) + .set('background', new NbtInt(JsonText.hexToInt(node.background_color))) + .set('line_width', new NbtInt(node.line_width)) + .set('shadow', new NbtByte(node.shadow ? 1 : 0)) + .set('see_through', new NbtByte(node.see_through ? 1 : 0)) + .set('alignment', new NbtString(node.align)) + + if (!compareVersions('1.21.5', version) /* >= 1.21.5 */) { + passenger.set( + 'text', + // SNBT JSON text format + // Hacky workaround for deepslate not supporting MC's new escape sequences. + new NbtString( + '$$$' + node.type + '_' + node.storage_name + '_text_placeholder$$$' ) - break - default: { - throw new Error( - `Unsupported Minecraft version '${version}' for text display!` + ) + } else if (!compareVersions('1.20.4', version) /* >= 1.20.4 */) { + passenger.set( + 'text', + // String JSON text format + new NbtString( + new JsonTextParser({ minecraftVersion: version }) + .parse(node.text) + .toString(true, version) ) - } + ) + } else { + throw new Error(`Unsupported Minecraft version '${version}' for text display!`) } - const color = new tinycolor( - node.background_color + floatToHex(node.background_alpha) - ) - passenger.set('background', new NbtInt(tinycolorToDecimal(color))) - passenger.set('line_width', new NbtInt(node.line_width)) - passenger.set('shadow', new NbtByte(node.shadow ? 1 : 0)) - passenger.set('see_through', new NbtByte(node.see_through ? 1 : 0)) - passenger.set('alignment', new NbtString(node.align)) - if (node.config) { TextDisplayConfig.fromJSON(node.config).toNBT(passenger) } break } case 'item_display': { - passenger.set('id', new NbtString('minecraft:item_display')) - passenger.set( - 'item', - new NbtCompound() - .set('id', new NbtString(node.item)) - .set('count', new NbtInt(1)) - ) + const item = new NbtCompound().set('id', new NbtString(node.item)) + passenger + .set('id', new NbtString('minecraft:item_display')) + .set('item', item) + .set('item_display', new NbtString(node.item_display)) + + if (!compareVersions(version, '1.20.4') /* <= 1.20.4 */) { + // `Count` does not default to 1 in 1.20.4. + item.set('Count', new NbtInt(1)) + break + } if (node.config) { BoneConfig.fromJSON(node.config).toNBT(passenger) @@ -862,26 +572,25 @@ async function generateRootEntityPassengers( break } case 'block_display': { - passenger.set('id', new NbtString('minecraft:block_display')) - + const states = new NbtCompound() const parsed = await parseBlock(node.block) if (!parsed) { throw new Error( - `Invalid Blockstate '${node.block}' in node '${node.path_name}'!` + `Invalid Blockstate '${node.block}' in node '${node.storage_name}'!` ) } - - const states = new NbtCompound() for (const [k, v] of Object.entries(parsed.states)) { states.set(k, new NbtString(v.toString())) } - passenger.set( - 'block_state', - new NbtCompound() - .set('Name', new NbtString(parsed.resource.name)) - .set('Properties', states) - ) + passenger + .set('id', new NbtString('minecraft:block_display')) + .set( + 'block_state', + new NbtCompound() + .set('Name', new NbtString(parsed.resource.name)) + .set('Properties', states) + ) if (node.config) { BoneConfig.fromJSON(node.config).toNBT(passenger) @@ -897,7 +606,20 @@ async function generateRootEntityPassengers( passengers.add(passenger) } - return passengers.toString() + let result = passengers.toString() + + if (!compareVersions('1.21.5', version) /* >= 1.21.5 */) { + for (const display of Object.values(rig.nodes).filter(n => n.type === 'text_display')) { + result = result.replace( + '"$$$' + display.type + '_' + display.storage_name + '_text_placeholder$$$"', + new JsonTextParser({ minecraftVersion: version }) + .parse(display.text) + .toString(true, version) + ) + } + } + + return result } async function createAnimationStorage(rig: IRenderedRig, animations: IRenderedAnimation[]) { @@ -913,9 +635,9 @@ async function createAnimationStorage(rig: IRenderedRig, animations: IRenderedAn PROGRESS_DESCRIPTION.set(`Creating Animation Storage for '${animation.storage_name}'`) let frames = new NbtCompound() const addFrameDataCommand = () => { - const str = `data modify storage aj.${ + const str = `data modify storage animated_java:${ Project!.animated_java.export_namespace - }:animations ${animation.storage_name} merge value ${frames.toString()}` + }/animations ${animation.storage_name} merge value ${frames.toString()}` dataCommands.push(str) frames = new NbtCompound() } @@ -941,11 +663,11 @@ async function createAnimationStorage(rig: IRenderedRig, animations: IRenderedAn thisFrame.set( node.type.charAt(0) + '_' + node.storage_name, new NbtCompound() - .set('posx', new NbtFloat(roundTo(transform.pos[0], 4))) - .set('posy', new NbtFloat(roundTo(transform.pos[1], 4))) - .set('posz', new NbtFloat(roundTo(transform.pos[2], 4))) - .set('rotx', new NbtFloat(roundTo(transform.rot[0], 4))) - .set('roty', new NbtFloat(roundTo(transform.rot[1], 4))) + .set('px', new NbtFloat(roundTo(transform.pos[0], 4))) + .set('py', new NbtFloat(roundTo(transform.pos[1], 4))) + .set('pz', new NbtFloat(roundTo(transform.pos[2], 4))) + .set('rx', new NbtFloat(roundTo(transform.rot[0], 4))) + .set('ry', new NbtFloat(roundTo(transform.rot[1], 4))) ) } } @@ -979,44 +701,6 @@ async function createAnimationStorage(rig: IRenderedRig, animations: IRenderedAn return dataCommands } -function createPassengerStorage(rig: IRenderedRig) { - const uuids = new NbtCompound() - const locators = new NbtCompound() - const cameras = new NbtCompound() - // Data entity - uuids.set('data_data', new NbtString('')) - for (const node of Object.values(rig.nodes)) { - switch (node.type) { - case 'locator': - case 'camera': { - const data = new NbtCompound() - .set('posx', new NbtFloat(node.default_transform.pos[0])) - .set('posy', new NbtFloat(node.default_transform.pos[1])) - .set('posz', new NbtFloat(node.default_transform.pos[2])) - .set('rotx', new NbtFloat(Math.radToDeg(node.default_transform.rot[0]))) - .set('roty', new NbtFloat(Math.radToDeg(node.default_transform.rot[1]))) - if (node.type === 'locator' && node.config?.use_entity) - data.set('uuid', new NbtString('')) - if (node.type === 'camera') { - cameras.set(node.storage_name, data) - } else { - locators.set(node.storage_name, data) - } - uuids.set(node.type + '_' + node.storage_name, new NbtString('')) - break - } - case 'bone': - case 'text_display': - case 'item_display': - case 'block_display': { - uuids.set(node.type + '_' + node.storage_name, new NbtString('')) - break - } - } - } - return { locators, cameras, uuids } -} - function nodeSorter(a: AnyRenderedNode, b: AnyRenderedNode): number { if (a.type === 'locator' && b.type !== 'locator') return 1 if (a.type !== 'locator' && b.type === 'locator') return -1 @@ -1025,13 +709,14 @@ function nodeSorter(a: AnyRenderedNode, b: AnyRenderedNode): number { interface DataPackCompilerOptions { ajmeta: AJMeta - version: MinecraftVersion + version: SUPPORTED_MINECRAFT_VERSIONS coreFiles: Map versionedFiles: Map rig: IRenderedRig animations: IRenderedAnimation[] rigHash: string animationHash: string + debugMode: boolean } export type DataPackCompiler = (options: DataPackCompilerOptions) => Promise @@ -1042,10 +727,11 @@ interface CompileDataPackOptions { dataPackFolder: string rigHash: string animationHash: string + debugMode: boolean } export default async function compileDataPack( - targetVersions: MinecraftVersion[], + targetVersions: SUPPORTED_MINECRAFT_VERSIONS[], options: CompileDataPackOptions ) { console.time('Data Pack Compilation took') @@ -1059,7 +745,7 @@ export default async function compileDataPack( options.dataPackFolder ) - if (aj.data_pack_export_mode === 'raw') { + if (aj.data_pack_export_mode === 'folder') { ajmeta.read() } @@ -1088,18 +774,18 @@ export default async function compileDataPack( versionedFiles, }) - for (let [path, file] of coreFiles) { - path = PathModule.join(coreDataPackFolder, path) - globalCoreFiles.set(path, file) + for (const [path, file] of coreFiles) { + const relative = PathModule.join(coreDataPackFolder, path) + globalCoreFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.coreFiles.add(path) + ajmeta.coreFiles.add(relative) } - for (let [path, file] of versionedFiles) { - path = PathModule.join(versionedDataPackFolder, path) - globalVersionSpecificFiles.set(path, file) + for (const [path, file] of versionedFiles) { + const relative = PathModule.join(versionedDataPackFolder, path) + globalVersionSpecificFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.versionedFiles.add(path) + ajmeta.versionedFiles.add(relative) } console.groupEnd() @@ -1108,42 +794,55 @@ export default async function compileDataPack( console.log('Exported Files:', globalCoreFiles.size + globalVersionSpecificFiles.size) const packMetaPath = PathModule.join(options.dataPackFolder, 'pack.mcmeta') - let packMeta = new PackMeta( - packMetaPath, - 0, - [], - `Animated Java Data Pack for ${targetVersions.join(', ')}` - ) - packMeta.read() - packMeta.pack_format = getDataPackFormat(targetVersions[0]) - packMeta.supportedFormats = [] - - if (targetVersions.length > 1) { - for (const version of targetVersions) { - let format: PackMetaFormats = getDataPackFormat(version) - packMeta.supportedFormats.push(format) - - const existingOverlay = [...packMeta.overlayEntries].find( - e => e.directory === `animated_java_${version.replaceAll('.', '_')}` - ) - if (!existingOverlay) { - packMeta.overlayEntries.add({ - directory: `animated_java_${version.replaceAll('.', '_')}`, - formats: format, - }) - } else { - existingOverlay.formats = format - } + const packMeta = PackMeta.fromFile(packMetaPath) + packMeta.content.pack ??= {} + + const nextVersion = getNextSupportedVersion(targetVersions[0]) + const format = getDataPackFormat(targetVersions[0]) + const nextFormat = nextVersion ? getDataPackFormat(nextVersion) : 10000000 + if (!compareVersions('1.21.9', targetVersions[0]) /* >= 1.21.9 */) { + packMeta.content.pack.min_format = format + packMeta.content.pack.max_format = nextFormat - 1 + } else { + packMeta.content.pack.pack_format = format + packMeta.content.pack.supported_formats = { + min_inclusive: format, + max_inclusive: nextFormat - 1, } } + packMeta.content.pack.description ??= `Animated Java Data Pack for ${targetVersions.join(', ')}` + + // if (targetVersions.length > 1) { + // packMeta.content.pack.supported_formats = [] + // packMeta.content.overlays ??= {} + // packMeta.content.overlays.entries ??= [] + + // for (const version of targetVersions) { + // const format: PackMetaFormats = getDataPackFormat(version) + // packMeta.content.pack.supported_formats.push(format) + + // const existingOverlay = packMeta.content.overlays.entries.find( + // e => e.directory === `animated_java_${version.replaceAll('.', '_')}` + // ) + // if (!existingOverlay) { + // packMeta.content.overlays.entries.push({ + // directory: `animated_java_${version.replaceAll('.', '_')}`, + // formats: format, + // }) + // } else { + // existingOverlay.formats = format + // } + // } + // } + globalCoreFiles.set(PathModule.join(options.dataPackFolder, 'pack.mcmeta'), { content: autoStringify(packMeta.toJSON()), includeInAJMeta: false, }) - if (aj.data_pack_export_mode === 'raw') { - await removeFiles(ajmeta, options.dataPackFolder) + if (aj.data_pack_export_mode === 'folder') { + await removeFiles(ajmeta) // Write new files ajmeta.coreFiles = new Set(globalCoreFiles.keys()) @@ -1163,10 +862,10 @@ export default async function compileDataPack( console.timeEnd('Data Pack Compilation took') } -async function removeFiles(ajmeta: AJMeta, dataPackFolder: string) { +async function removeFiles(ajmeta: AJMeta) { console.time('Removing Files took') const aj = Project!.animated_java - if (aj.data_pack_export_mode === 'raw') { + if (aj.data_pack_export_mode === 'folder') { PROGRESS_DESCRIPTION.set('Removing Old Data Pack Files...') PROGRESS.set(0) MAX_PROGRESS.set(ajmeta.previousVersionedFiles.size) @@ -1191,7 +890,7 @@ async function removeFiles(ajmeta: AJMeta, dataPackFolder: string) { } } // Remove mentions of the export namespace from the file - let content: IFunctionTag + let content: FunctionTagJSON // Remove mentions of the export namespace from the file try { content = JSON.parse((await fs.promises.readFile(file)).toString()) @@ -1231,17 +930,20 @@ async function removeFiles(ajmeta: AJMeta, dataPackFolder: string) { } const dataPackCompiler: DataPackCompiler = async ({ - ajmeta, + // ajmeta, version, - coreFiles, + // coreFiles, versionedFiles, rig, animations, rigHash, animationHash, + debugMode, }) => { + JsonText.defaultMinecraftVersion = version + const aj = Project!.animated_java - const is_static = animations.length === 0 + const isStatic = animations.length === 0 const variables = { export_namespace: aj.export_namespace, interpolation_duration: aj.interpolation_duration, @@ -1254,50 +956,54 @@ const dataPackCompiler: DataPackCompiler = async ({ TAGS, OBJECTIVES, TELLRAW, - custom_summon_commands: aj.summon_commands, - custom_remove_commands: aj.remove_commands, + on_summon_function: aj.on_summon_function, + on_remove_function: aj.on_remove_function, + on_pre_tick_function: aj.on_pre_tick_function, + on_post_tick_function: aj.on_post_tick_function, matrixToNbtFloatArray, transformationToNbt, use_storage_for_animation: aj.use_storage_for_animation, animationStorage: aj.use_storage_for_animation ? await createAnimationStorage(rig, animations) : null, - rigHash, - animationHash, - boundingBox: aj.bounding_box, + rig_hash: rigHash, + animation_hash: animationHash, + boundingBox: aj.render_box, BoneConfig, roundTo, nodeSorter, getRotationFromQuaternion: eulerFromQuaternion, - root_ticking_commands: aj.ticking_commands, - show_function_errors: aj.show_function_errors, - show_outdated_warning: aj.show_outdated_warning, has_locators: Object.values(rig.nodes).filter(n => n.type === 'locator').length > 0, has_entity_locators: Object.values(rig.nodes).filter(n => n.type === 'locator' && n.config?.use_entity) .length > 0, + has_ticking_locators: + Object.values(rig.nodes).filter(n => n.type === 'locator' && n.config?.on_tick_function) + .length > 0, has_cameras: Object.values(rig.nodes).filter(n => n.type === 'camera').length > 0, - is_static, + is_static: isStatic, getNodeTags, + BONE_TYPES, + project_storage: `animated_java:${aj.export_namespace}`, + temp_storage: `animated_java:temp`, + gu_storage: `animated_java:gu`, + auto_update_rig_orientation: aj.auto_update_rig_orientation, + debug_mode: debugMode, } - compile({ - path: 'src/animated_java.mcb', - mcbFile: is_static ? mcbFiles[version].static : mcbFiles[version].animation, + compileMcbProject({ + sourceFiles: { + 'src/global.mcbt': mcbFiles[version].globalTemplates, + 'src/animated_java.mcb': mcbFiles[version].global, + [`src/animated_java/${aj.export_namespace}.mcb`]: isStatic + ? mcbFiles[version].static + : mcbFiles[version].animation, + }, destPath: '.', variables, version, exportedFiles: versionedFiles, }) - - compile({ - path: 'src/animated_java.mcb', - mcbFile: mcbFiles[version].core, - destPath: '.', - variables, - version, - exportedFiles: coreFiles, - }) } async function writeFiles(exportedFiles: Map, dataPackFolder: string) { @@ -1305,6 +1011,7 @@ async function writeFiles(exportedFiles: Map, dataPackFold PROGRESS.set(0) MAX_PROGRESS.set(exportedFiles.size) const aj = Project!.animated_java + const lastNamespace = Project!.last_used_export_namespace const createdFolderCache = new Set() const functionTagQueue = new Map() @@ -1323,7 +1030,12 @@ async function writeFiles(exportedFiles: Map, dataPackFold if (file.writeHandler) { await file.writeHandler(path, file.content) } else { - await fs.promises.writeFile(path, file.content) + await fs.promises.writeFile( + path, + new Uint8Array( + Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content) + ) + ) } PROGRESS.set(PROGRESS.get() + 1) } @@ -1338,81 +1050,60 @@ async function writeFiles(exportedFiles: Map, dataPackFold }) ) if (writeQueue.size >= maxWriteThreads) { - await Promise.any(writeQueue) + await Promise.any(writeQueue.values()) } } await Promise.all(writeQueue.values()) + PROGRESS_DESCRIPTION.set('Merging Function Tags...') + MAX_PROGRESS.set(functionTagQueue.size) + PROGRESS.set(0) for (const [path, file] of functionTagQueue.entries()) { - const oldFile: IFunctionTag = JSON.parse(fs.readFileSync(path, 'utf-8')) - const newFile: IFunctionTag = JSON.parse(file.content.toString()) - const merged = mergeTag(oldFile, newFile) - if (aj.export_namespace !== Project!.last_used_export_namespace) { - merged.values = merged.values.filter(v => { - const value = typeof v === 'string' ? v : v.id - return ( - !value.startsWith(`#animated_java:${Project!.last_used_export_namespace}/`) || - value.startsWith(`animated_java:${Project!.last_used_export_namespace}/`) + const oldTag = DataPackTag.fromJSON(JSON.parse(fs.readFileSync(path, 'utf-8'))) + const merged = oldTag.merge(DataPackTag.fromJSON(JSON.parse(file.content.toString()))) + + merged.filter(entry => { + const id = DataPackTag.getEntryId(entry) + const isTag = id.startsWith('#') + const location = parseResourceLocation(isTag ? id.substring(1) : id) + // Ignore entries unrelated to Animated Java + if (location.namespace !== 'animated_java') return true + + // Remove last namespace entries if the namespace has changed + if (aj.export_namespace !== lastNamespace && location.namespace === lastNamespace) + return false + + // Search for the entry in all data folders + const functionFolderName = projectTargetVersionIsAtLeast('1.21') + ? 'function' + : 'functions' + const subPath = (isTag ? 'tags/' : '') + functionFolderName + const extension = isTag ? '.json' : '.mcfunction' + + for (const folder of fs.readdirSync(dataPackFolder)) { + const fullPath = PathModule.join( + dataPackFolder, + folder, + location.namespace, + subPath, + location.path + extension ) - }) - } - merged.values = merged.values - .filter(v => { - const value = typeof v === 'string' ? v : v.id - const isTag = value.startsWith('#') - const location = parseResourceLocation(isTag ? value.substring(1) : value) - - console.log('Checking:', value, location) - - let exists = false - for (const folder of fs.readdirSync(dataPackFolder)) { - const overrideFolder = PathModule.join(dataPackFolder, folder) - if (!fs.statSync(overrideFolder).isDirectory()) continue - const dataFolder = - folder === 'data' ? overrideFolder : PathModule.join(overrideFolder, 'data') - - const path = isTag - ? PathModule.join( - dataFolder, - location.namespace, - 'tags/functions', - location.path + '.json' - ) - : PathModule.join( - dataFolder, - location.namespace, - 'functions', - location.path + '.mcfunction' - ) - console.log('Checking path:', path) - if ( - !( - fs.existsSync(path) || - fs.existsSync( - path.replace( - `${PathModule.sep}functions${PathModule.sep}`, - `${PathModule.sep}function${PathModule.sep}` - ) - ) - ) - ) - continue - exists = true - break - } - if (!exists) { - const parentLocation = parseDataPackPath(path) - console.warn( - `The referenced ${isTag ? 'tag' : 'function'} '${value}' in '${ - parentLocation?.resourceLocation || path - }' does not exist! Removing reference...` - ) - } - return exists - }) - .sort() + if (fs.existsSync(fullPath)) return true + } + + console.warn( + `Removed reference to ${ + isTag ? 'tag' : 'function' + } '${id}' in '${path}' because it does not exist!` + ) + // Remove the entry if it wasn't found + return false + }) - await fs.promises.writeFile(path, autoStringify(merged)) + merged.sort() + + await fs.promises.writeFile(path, autoStringify(merged.toJSON())) + PROGRESS.set(PROGRESS.get() + 1) } } diff --git a/src/systems/datapackCompiler/compiler.ts b/src/systems/datapackCompiler/mcbCompiler.ts similarity index 51% rename from src/systems/datapackCompiler/compiler.ts rename to src/systems/datapackCompiler/mcbCompiler.ts index 4648a4e3..5ca30fbc 100644 --- a/src/systems/datapackCompiler/compiler.ts +++ b/src/systems/datapackCompiler/mcbCompiler.ts @@ -1,30 +1,28 @@ -import { Parser, SyncIo, Tokenizer } from 'mc-build' -import { Compiler, VariableMap } from 'mc-build/dist/mcl/Compiler' +import { Compiler, Parser, SyncIo, Tokenizer } from 'mc-build' +import { VariableMap } from 'mc-build/dist/mcl/Compiler' import { getDataPackFormat } from '../../util/minecraftUtil' -import { MinecraftVersion } from '../global' -import { ExportedFile } from '../util' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../global' +import type { ExportedFile } from '../util' interface CompilerOptions { - path: string - mcbFile: string + sourceFiles: Record destPath: string variables: Record - version: MinecraftVersion + version: SUPPORTED_MINECRAFT_VERSIONS exportedFiles: Map } -export function compile({ - path, - mcbFile, +export function compileMcbProject({ + sourceFiles, destPath, variables, version, exportedFiles, }: CompilerOptions) { - console.group('Compiling', path) + console.group('Compiling', sourceFiles) console.log('Variables:', variables) - const compiler = new Compiler('src/', { + const compiler = new Compiler('src', { libDir: null, generatedDirName: 'zzz', internalScoreboardName: 'aj.i', @@ -37,7 +35,6 @@ export function compile({ formatVersion: getDataPackFormat(version), }) compiler.disableRequire = true - compiler.templateParsingEnabled = false function createSyncIO() { const io = new SyncIo() @@ -53,8 +50,28 @@ export function compile({ compiler.io = createSyncIO() console.time('MC-Build compiled in') - const tokens = Tokenizer.tokenize(mcbFile, path) - compiler.addFile(path, Parser.parseMcbFile(tokens)) + + const mcbTemplateFiles = Object.entries(sourceFiles).filter(([path]) => path.endsWith('.mcbt')) + const mcbFiles = Object.entries(sourceFiles).filter(([path]) => path.endsWith('.mcb')) + + for (const [path, mcbFile] of mcbTemplateFiles) { + try { + compiler.addFile(path, Parser.parseMcbtFile(Tokenizer.tokenize(mcbFile, path))) + } catch (e) { + if (e instanceof Error) e.message = `Failed to compile "${path}":\n\t${e.message}` + throw e + } + } + + for (const [path, mcbFile] of mcbFiles) { + try { + compiler.addFile(path, Parser.parseMcbFile(Tokenizer.tokenize(mcbFile, path))) + } catch (e) { + if (e instanceof Error) e.message = `Failed to compile "${path}":\n\t${e.message}` + throw e + } + } + compiler.compile(VariableMap.fromObject(variables)) console.timeEnd('MC-Build compiled in') console.log('Exported files:', exportedFiles.keys()) diff --git a/src/systems/datapackCompiler/mcbFiles.ts b/src/systems/datapackCompiler/mcbFiles.ts index 7a351419..a6a2b85d 100644 --- a/src/systems/datapackCompiler/mcbFiles.ts +++ b/src/systems/datapackCompiler/mcbFiles.ts @@ -1,52 +1,75 @@ -import { MinecraftVersion } from '../global' -import animation_1_20_4 from './1.20.4/animation.mcb' -import core_1_20_4 from './1.20.4/core.mcb' -import static_1_20_4 from './1.20.4/static.mcb' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../global' +// FIXME - Figure out how to import these files dynamically and generate the MCB_FILES object automatically. +import ANIMATION_1_20_4 from './1.20.4/animation.mcb' +import GLOBAL_1_20_4 from './1.20.4/global.mcb' +import GLOBAL_TEMPLATES_1_20_4 from './1.20.4/global.mcbt' +import STATIC_1_20_4 from './1.20.4/static.mcb' -import animation_1_20_5 from './1.20.5/animation.mcb' -import static_1_20_5 from './1.20.5/static.mcb' +import ANIMATION_1_20_5 from './1.20.5/animation.mcb' +import STATIC_1_20_5 from './1.20.5/static.mcb' -import animation_1_21_2 from './1.21.2/animation.mcb' -import static_1_21_2 from './1.21.2/static.mcb' +import ANIMATION_1_21_2 from './1.21.2/animation.mcb' +import GLOBAL_1_21_2 from './1.21.2/global.mcb' +import STATIC_1_21_2 from './1.21.2/static.mcb' -import animation_1_21_4 from './1.21.4/animation.mcb' -import static_1_21_4 from './1.21.4/static.mcb' +import ANIMATION_1_21_4 from './1.21.4/animation.mcb' +import STATIC_1_21_4 from './1.21.4/static.mcb' -import animation_1_21_5 from './1.21.5/animation.mcb' -import static_1_21_5 from './1.21.5/static.mcb' +import GLOBAL_1_21_5 from './1.21.5/global.mcb' // The core is content that always goes in the `data` folder directly, // while other files are in the `animated_java/data` folder to be overlayed when the correct version is loaded. -export default { +interface MCBFiles { + animation: string + static: string + global: string + globalTemplates: string +} + +const MCB_FILES: Record = { + '1.21.9': { + animation: ANIMATION_1_21_4, + static: STATIC_1_21_4, + global: GLOBAL_1_21_5, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, + }, + '1.21.6': { + animation: ANIMATION_1_21_4, + static: STATIC_1_21_4, + global: GLOBAL_1_21_5, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, + }, '1.21.5': { - animation: animation_1_21_5, - static: static_1_21_5, - core: core_1_20_4, + animation: ANIMATION_1_21_4, + static: STATIC_1_21_4, + global: GLOBAL_1_21_5, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, }, '1.21.4': { - animation: animation_1_21_4, - static: static_1_21_4, - core: core_1_20_4, + animation: ANIMATION_1_21_4, + static: STATIC_1_21_4, + global: GLOBAL_1_21_2, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, }, '1.21.2': { - animation: animation_1_21_2, - static: static_1_21_2, - core: core_1_20_4, - }, - '1.21.0': { - animation: animation_1_20_5, - static: static_1_20_5, - core: core_1_20_4, + animation: ANIMATION_1_21_2, + static: STATIC_1_21_2, + global: GLOBAL_1_21_2, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, }, '1.20.5': { - animation: animation_1_20_5, - static: static_1_20_5, - core: core_1_20_4, + animation: ANIMATION_1_20_5, + static: STATIC_1_20_5, + global: GLOBAL_1_20_4, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, }, '1.20.4': { - animation: animation_1_20_4, - static: static_1_20_4, - core: core_1_20_4, + animation: ANIMATION_1_20_4, + static: STATIC_1_20_4, + global: GLOBAL_1_20_4, + globalTemplates: GLOBAL_TEMPLATES_1_20_4, }, -} as Record +} + +export default MCB_FILES diff --git a/src/systems/datapackCompiler/tellraw.ts b/src/systems/datapackCompiler/tellraw.ts new file mode 100644 index 00000000..db6cc520 --- /dev/null +++ b/src/systems/datapackCompiler/tellraw.ts @@ -0,0 +1,317 @@ +import { toSmallCaps } from 'src/util/minecraftUtil' +import { type IRenderedAnimation } from '../animationRenderer' +import { JsonText, TextElement } from '../jsonText' +import { type IRenderedVariant } from '../rigRenderer' +import { TAGS } from './tags' + +const TELLRAW_PREFIX = () => + new JsonText([ + { text: '\n ', color: 'gray' }, + { text: toSmallCaps('Animated Java'), color: '#00aced' }, + { + text: `\n (animated_java:${Project!.animated_java.export_namespace})`, + color: 'dark_gray', + italic: true, + }, + '\n → ', + ]).flatten() + +const TELLRAW_SUFFIX = () => '\n' + +const TELLRAW_ERROR = (errorName: string, details: TextElement) => + new JsonText([ + { text: '', color: 'red' }, + TELLRAW_PREFIX(), + toSmallCaps('error') + ': ', + { text: errorName, underlined: true }, + '\n\n ', + ...(Array.isArray(details) ? details : [details]), + TELLRAW_SUFFIX(), + ]) + +const TELLRAW_WARNING = (warningName: string, details: TextElement) => + new JsonText([ + { text: '', color: 'yellow' }, + TELLRAW_PREFIX(), + toSmallCaps('warning') + ': ', + { text: warningName, underlined: true }, + '\n\n ', + ...(Array.isArray(details) ? details : [details]), + TELLRAW_SUFFIX(), + ]) + +const CREATE_TELLRAW_HELP_LINK = (url: string) => + new JsonText([ + '\n\n ', + !compareVersions('1.21.5', Project!.animated_java.target_minecraft_version) + ? { + text: '▶ Learn More ◀', + color: 'blue', + underlined: true, + italic: true, + click_event: { action: 'open_url', url }, + } + : { + text: '▶ Learn More ◀', + color: 'blue', + underlined: true, + italic: true, + clickEvent: { action: 'open_url', value: url }, + }, + ]).flatten() + +namespace TELLRAW { + export const RIG_OUTDATED = () => + TELLRAW_ERROR('Outdated Rig Instance', [ + 'The instance of ', + { text: '$(export_namespace)', color: 'yellow' }, + ' at ', + { text: '$(x), $(y), $(z)', color: 'yellow' }, + ' was summoned using an older export of its Blueprint.', + ' It should be removed and re-summoned to ensure it functions correctly.', + { text: '\n\n ≡ ', color: 'white' }, + !compareVersions('1.21.5', Project!.animated_java.target_minecraft_version) + ? { + text: toSmallCaps('Teleport to Instance'), + click_event: { + action: 'suggest_command', + command: '/tp @s $(uuid)', + }, + color: 'aqua', + underlined: true, + } + : { + text: toSmallCaps('Teleport to Instance'), + clickEvent: { + action: 'suggest_command', + value: '/tp @s $(uuid)', + }, + color: 'aqua', + underlined: true, + }, + { text: '\n ≡ ', color: 'white' }, + !compareVersions('1.21.5', Project!.animated_java.target_minecraft_version) + ? { + text: toSmallCaps('Remove Instance'), + click_event: { + action: 'suggest_command', + command: `/execute as $(uuid) run function animated_java:$(export_namespace)/remove/this`, + }, + color: 'aqua', + underlined: true, + } + : { + text: toSmallCaps('Remove Instance'), + clickEvent: { + action: 'suggest_command', + value: `/execute as $(uuid) run function animated_java:$(export_namespace)/remove/this`, + }, + color: 'aqua', + underlined: true, + }, + ]) + + export const RIG_OUTDATED_TEXT_DISPLAY = () => + new JsonText([ + { text: '⚠ This rig instance is outdated! ⚠', color: 'red' }, + '\n It should be removed and re-summoned to ensure it functions correctly.', + ]) + // Because this is used as NBT in a summon command, we need to double-escape the newlines. + .toString() + .replaceAll('\\n', '\\\\n') + + export const FUNCTION_NOT_EXECUTED_AS_ROOT_ERROR = (functionPath: string) => { + const hoverText = new JsonText([{ text: functionPath, color: 'yellow' }, '']).flatten() + + const exampleCommand = `/execute as @e[tag=${TAGS.PROJECT_ROOT( + Project!.animated_java.export_namespace + )}] run function ${functionPath}` + + return TELLRAW_ERROR('Function Not Executed as Root Entity', [ + !compareVersions('1.21.5', Project!.animated_java.target_minecraft_version) + ? { + text: '[This Function]', + color: 'yellow', + hover_event: { action: 'show_text', value: hoverText }, + } + : { + text: '[This Function]', + color: 'yellow', + hoverEvent: { action: 'show_text', contents: hoverText }, + }, + " must be executed as the rig's root entity.", + { + text: '\n\n ≡ ', + color: 'white', + extra: [ + !compareVersions('1.21.5', Project!.animated_java.target_minecraft_version) + ? { + text: toSmallCaps('Show Example Command'), + color: 'aqua', + underlined: true, + click_event: { action: 'suggest_command', command: exampleCommand }, + } + : { + text: toSmallCaps('Show Example Command'), + color: 'aqua', + underlined: true, + clickEvent: { action: 'suggest_command', value: exampleCommand }, + }, + ], + }, + CREATE_TELLRAW_HELP_LINK( + 'https://animated-java.dev/docs/rigs/controlling-a-rig-instance' + ), + ]) + } + + export const INVALID_VARIANT = (variants: Record) => + TELLRAW_ERROR('Invalid Variant', [ + 'The variant ', + { nbt: 'args.variant', storage: 'animated_java:temp', color: 'yellow' }, + ' does not exist.', + '\n ', + { text: ' ≡ ', color: 'white' }, + { text: 'Available Variants:', color: 'green' }, + ...Object.values(variants).map(variant => + new JsonText([ + { text: '\n ', color: 'gray' }, + '\\s\\s\\s', + ' ● ', + { text: variant.name, color: 'yellow' }, + ]).flatten() + ), + ]) + + export const FRAME_CANNOT_BE_NEGATIVE = () => + TELLRAW_ERROR('Frame cannot be negative', [ + { text: 'frame', color: 'yellow' }, + { text: ' must be a non-negative integer.' }, + ]) + + export const INVALID_ANIMATION = (animations: IRenderedAnimation[]) => + TELLRAW_ERROR('Invalid Animation', [ + 'The animation ', + { nbt: 'args.animation', storage: 'animated_java:temp', color: 'yellow' }, + ' does not exist.', + '\n ', + { text: ' ≡ ', color: 'white' }, + { text: 'Available Animations:', color: 'green' }, + ...animations.map(anim => + new JsonText([ + { text: '\n ', color: 'gray' }, + '\\s\\s\\s', + ' ● ', + { text: anim.storage_name, color: 'yellow' }, + ]).flatten() + ), + ]) + + export const NO_VARIANTS = () => + TELLRAW_ERROR('No Variants', ['This Blueprint has no variants to switch between.']) + + export const INVALID_VERSION = () => + TELLRAW_ERROR('Invalid Minecraft Version', [ + 'Attempted to load an Animated Java Data Pack that was exported for ', + { + text: `Minecraft ${Project!.animated_java.target_minecraft_version}`, + color: 'aqua', + }, + ' in the wrong version!', + '\n Please ensure that the data pack is loaded in the correct version, or that your Blueprint settings are configured to target the correct version(s) of Minecraft.', + ]) + + export const UNINSTALL = () => + new JsonText([ + TELLRAW_PREFIX(), + [ + { text: 'Successfully uninstalled ', color: 'green' }, + { text: Project!.animated_java.export_namespace, color: 'yellow' }, + { text: '!' }, + { + text: '\n If you have exported multiple times, you may have to remove objectives from previous exports manually, as Animated Java only knows about the objectives from the most recent export.', + color: 'gray', + italic: true, + }, + ], + TELLRAW_SUFFIX(), + ]) + + export const ARGUMENT_CANNOT_BE_EMPTY = (name: string) => + TELLRAW_ERROR('Argument Cannot Be Empty', [ + 'Argument ', + { text: name, color: 'yellow' }, + { text: ' cannot be an empty string.' }, + ]) + + export const LOCATOR_NOT_FOUND = () => + TELLRAW_ERROR('Locator Not Found', [ + 'Locator ', + { nbt: 'args.name', storage: 'animated_java:temp', color: 'aqua' }, + ' not found!', + '\n Please ensure that the name is spelled correctly.', + ]) + + export const LOCATOR_ENTITY_NOT_FOUND = () => + TELLRAW_ERROR('Locator Not Found', [ + 'Locator ', + { nbt: 'args.name', storage: 'animated_java:temp', color: 'aqua' }, + ' does not exist!', + { text: '\n Please ensure that the name is spelled correctly, and ' }, + { text: '"Use Entity"', color: 'yellow' }, + " is enabled in the locator's config.", + ]) + + export const LOCATOR_COMMAND_FAILED_TO_EXECUTE = () => + TELLRAW_ERROR('Failed to Execute Command as Locator', [ + 'Failed to execute command ', + { nbt: 'args.command', storage: 'animated_java:temp', color: 'yellow' }, + ' as Locator ', + { nbt: 'args.name', storage: 'animated_java:temp', color: 'aqua' }, + '.', + '\n Please ensure the command is valid.', + ]) + + export const CAMERA_ENTITY_NOT_FOUND = () => + TELLRAW_ERROR('Camera Not Found', [ + 'Camera ', + { nbt: 'args.name', storage: 'animated_java:temp', color: 'aqua' }, + ' does not exist!', + '\n Please ensure that its name is spelled correctly.', + ]) + + export const CAMERA_COMMAND_FAILED_TO_EXECUTE = () => + TELLRAW_ERROR('Failed to Execute Command as Camera', [ + 'Failed to execute command ', + { nbt: 'args.command', storage: 'animated_java:temp', color: 'yellow' }, + ' as Camera ', + { nbt: 'args.name', storage: 'animated_java:temp', color: 'aqua' }, + '.', + '\n Please ensure the command is valid.', + ]) + + export const AUTO_UPDATE_RIG_ORIENTATION_MOVE_WARNING = () => + TELLRAW_ERROR('Called Move Function while Auto Update Rig Orientation is Enabled', [ + 'The ', + { text: 'move', color: 'yellow' }, + ' function cannot be called while ', + { text: 'Auto Update Rig Orientation', color: 'yellow' }, + ' is enabled.', + '\n Please either disable ', + { text: 'Auto Update Rig Orientation', color: 'yellow' }, + ' or avoid calling the ', + { text: 'move', color: 'yellow' }, + ' function.', + ]) + + export const DEPRECATED_FUNCTION_WARNING = (functionName: string, alternative: string) => + TELLRAW_WARNING('Deprecated Function', [ + 'The function ', + { text: functionName, color: 'aqua' }, + ' is deprecated.\nPlease use ', + { text: alternative, color: 'aqua' }, + ' instead.', + ]) +} + +export default TELLRAW diff --git a/src/systems/exporter.ts b/src/systems/exporter.ts index f6e905b7..67bed5c0 100644 --- a/src/systems/exporter.ts +++ b/src/systems/exporter.ts @@ -1,10 +1,11 @@ -import { saveBlueprint } from '../blueprintFormat' -import { blueprintSettingErrors } from '../blueprintSettings' +import { Stopwatch } from 'src/util/stopwatch' +import { projectTargetVersionIsAtLeast, saveBlueprint } from '../formats/blueprint' +import { blueprintSettingErrors } from '../formats/blueprint/settings' import { openBlueprintSettingsDialog } from '../interface/dialog/blueprintSettings' import { PROGRESS_DESCRIPTION, openExportProgressDialog } from '../interface/dialog/exportProgress' import { openUnexpectedErrorDialog } from '../interface/dialog/unexpectedError' import { resolvePath } from '../util/fileUtil' -import { isResourcePackPath, sortMCVersions } from '../util/minecraftUtil' +import { isResourcePackPath } from '../util/minecraftUtil' import { translate } from '../util/translation' import { Variant } from '../variants' import { hashAnimations, renderProjectAnimations } from './animationRenderer' @@ -13,7 +14,42 @@ import resourcepackCompiler from './resourcepackCompiler' import { hashRig, renderRig } from './rigRenderer' import { isCubeValid } from './util' -export class IntentionalExportError extends Error {} +export class IntentionalExportError extends Error { + constructor( + message: string, + public messageBoxOptions?: MessageBoxOptions, + public messageBoxCallback?: Parameters[1] + ) { + super(message) + this.name = 'IntentionalExportError' + } +} + +export class IntentionalExportErrorFromInvalidFile extends IntentionalExportError { + constructor(filePath: string, public originalError: Error) { + const parsed = PathModule.parse(filePath) + super( + `Failed to read file ${parsed.base}:\n\n` + + '```\n' + + originalError + + '\n```', + { + commands: { + open_file: { + text: 'Open File Location', + icon: 'folder_open', + }, + }, + }, + button => { + if (button === 'open_file') { + shell.showItemInFolder(filePath) + } + } + ) + this.name = 'IntentionalExportErrorFromInvalidFile' + } +} export function getExportPaths() { const aj = Project!.animated_java @@ -44,16 +80,23 @@ export function getExportPaths() { } } -async function actuallyExportProject(forceSave = true) { +interface ExportProjectOptions { + forceSave?: boolean + debugMode?: boolean +} + +async function actuallyExportProject({ + forceSave = true, + debugMode = false, +}: ExportProjectOptions = {}): Promise { const aj = Project!.animated_java const dialog = openExportProgressDialog() // Wait for the dialog to open await new Promise(resolve => requestAnimationFrame(resolve)) const selectedVariant = Variant.selected Variant.getDefault().select() + const stopwatch = new Stopwatch('Project Export').start() try { - console.time('Exporting project took') - // Verify that all variant texture maps are valid for (const variant of Variant.all) { variant.verifyTextureMap() @@ -70,11 +113,6 @@ async function actuallyExportProject(forceSave = true) { } } - // Sort target versions - console.log('Target Minecraft Versions', aj.target_minecraft_versions) - aj.target_minecraft_versions = sortMCVersions(aj.target_minecraft_versions) - console.log('Sorted Target Minecraft Versions', aj.target_minecraft_versions) - const { resourcePackFolder, dataPackFolder, @@ -106,7 +144,7 @@ async function actuallyExportProject(forceSave = true) { buttons: [translate('misc.failed_to_export.button')], }) dialog.close(0) - return + return false } const animations = await renderProjectAnimations(Project!, rig) @@ -115,72 +153,71 @@ async function actuallyExportProject(forceSave = true) { const rigHash = hashRig(rig) const animationHash = hashAnimations(animations) - // Always run the resource pack compiler because it calculates the custom model data. - await resourcepackCompiler(aj.target_minecraft_versions, { + // TODO - Plugin mode should run without the resource pack compiler + // Always run the resource pack compiler because it calculates custom model data. + await resourcepackCompiler([aj.target_minecraft_version], { rig, displayItemPath, resourcePackFolder, textureExportFolder, modelExportFolder, + debugMode, }) - // if (aj.enable_plugin_mode) { - // exportJSON({ - // rig, - // animations, - // displayItemPath, - // textureExportFolder, - // modelExportFolder, - // }) - // } else { if (aj.data_pack_export_mode !== 'none') { - await compileDataPack(aj.target_minecraft_versions, { + await compileDataPack([aj.target_minecraft_version], { rig, animations, dataPackFolder, rigHash, animationHash, + debugMode, }) } Project!.last_used_export_namespace = aj.export_namespace - console.timeEnd('Exporting project took') if (forceSave) saveBlueprint() Blockbench.showQuickMessage('Project exported successfully!', 2000) - return + return true } catch (e: any) { console.error(e) if (e instanceof IntentionalExportError) { - Blockbench.showMessageBox({ - title: translate('misc.failed_to_export.title'), - message: e.message, - buttons: [translate('misc.failed_to_export.button')], - }) - return + Blockbench.showMessageBox( + { + title: translate('misc.failed_to_export.title'), + message: e.message, + buttons: [translate('misc.failed_to_export.button')], + ...e.messageBoxOptions, + }, + e.messageBoxCallback + ) + return false } openUnexpectedErrorDialog(e as Error) } finally { selectedVariant?.select() dialog.close(0) + stopwatch.debug() } + return false } -export async function exportProject(forceSave = true) { - if (!Project) return // TODO: Handle this error better +export async function exportProject(options?: ExportProjectOptions): Promise { + if (!Project) return false // TODO: Handle this error better - if ( - // Check if 1.21.3 is newer than the target version - // compareVersions('1.21.3', Project.animated_java.target_minecraft_versions) && - !Cube.all.allAre(c => isCubeValid(c)) - ) { + if (Cube.all.some(cube => isCubeValid(cube) === 'invalid')) { Blockbench.showMessageBox({ title: translate('misc.failed_to_export.title'), - message: translate('misc.failed_to_export.invalid_rotation.message'), + message: translate( + projectTargetVersionIsAtLeast('1.21.6') + ? 'misc.failed_to_export.invalid_rotation.message_post_1_21_6' + : 'misc.failed_to_export.invalid_rotation.message' + ), buttons: [translate('misc.failed_to_export.button')], }) - return + return false } blueprintSettingErrors.set({}) @@ -204,10 +241,10 @@ export async function exportProject(forceSave = true) { .join('\n\n'), buttons: [translate('misc.failed_to_export.button')], }) - return + return false } settingsDialog.close(0) - await actuallyExportProject(forceSave) + return await actuallyExportProject(options) } diff --git a/src/systems/global.ts b/src/systems/global.ts index 34f782db..8e322dbf 100644 --- a/src/systems/global.ts +++ b/src/systems/global.ts @@ -1,32 +1,41 @@ import { normalizePath } from '../util/fileUtil' -import { getDataPackFormat } from '../util/minecraftUtil' -import { IntentionalExportError } from './exporter' +import { IntentionalExportError, IntentionalExportErrorFromInvalidFile } from './exporter' import { sortObjectKeys } from './util' -export type MinecraftVersion = '1.20.4' | '1.20.5' | '1.21.0' | '1.21.2' | '1.21.4' | '1.21.5' +export enum SUPPORTED_MINECRAFT_VERSIONS { + '1.20.4' = '1.20.4', + '1.20.5' = '1.20.5', + '1.21.2' = '1.21.2', + '1.21.4' = '1.21.4', + '1.21.5' = '1.21.5', + '1.21.6' = '1.21.6', + '1.21.9' = '1.21.9', +} -interface OldSerializedAJMeta { - [key: string]: { +type OldSerializedAJMeta = Record< + string, + { files?: string[] } -} +> interface SerializedAJMeta { formatVersion?: string - rigs?: { - [key: string]: { + rigs?: Record< + string, + { coreFiles?: string[] versionedFiles?: string[] } - } + > } export class AJMeta { - public coreFiles = new Set() - public previousCoreFiles = new Set() + coreFiles = new Set() + previousCoreFiles = new Set() - public versionedFiles = new Set() - public previousVersionedFiles = new Set() + versionedFiles = new Set() + previousVersionedFiles = new Set() private previousAJMeta: SerializedAJMeta = {} @@ -107,65 +116,68 @@ export type PackMetaFormats = number | number[] | { min_inclusive: number; max_i interface OverlayEntry { directory?: string + /** Minecraft enforces this field does not to exist if the pack doesn't support versions older than 1.21.9 */ formats?: PackMetaFormats + // Below since 1.21.9 + min_format?: number + max_format?: number } export interface SerializedPackMeta { pack?: { pack_format?: number - supported_formats?: PackMetaFormats[] + /** Minecraft enforces this field does not to exist if the pack doesn't support versions older than 1.21.9 */ + supported_formats?: PackMetaFormats description?: string + // Below since 1.21.9 + min_format?: number + max_format?: number + } + features?: { + enabled?: string[] + } + filter?: { + block?: Array<{ + namespace: string + path: string + }> } overlays?: { - entries?: Array + entries?: OverlayEntry[] + } + language?: { + name?: string + region?: string + bidirectional?: boolean } } +/** + * A class that reads and writes pack.mcmeta files. + * + * Designed to only modify parts of the file we care about, and leave the rest as-is. + */ export class PackMeta { - constructor( - public path: string, - public pack_format = getDataPackFormat('1.20.4'), - public supportedFormats: PackMetaFormats[] = [], - public description = 'Animated Java Resource Pack', - public overlayEntries = new Set() - ) {} + content: SerializedPackMeta = {} - read() { - if (!fs.existsSync(this.path)) return + static fromFile(path: string) { + const meta = new PackMeta() - const raw = fs.readFileSync(this.path, 'utf-8') + if (!fs.existsSync(path)) { + console.warn(`Pack meta file does not exist at ${path}`) + return meta + } + + const raw = fs.readFileSync(path, 'utf-8') try { - const content = JSON.parse(raw) - if (content.pack) { - this.pack_format = content.pack.pack_format ?? this.pack_format - this.description = content.pack.description ?? this.description - this.supportedFormats = content.pack.supported_formats ?? this.supportedFormats - } - if (content.overlays) { - for (const entry of content.overlays.entries ?? []) { - this.overlayEntries.add(entry) - } - } - } catch (e) { - throw new IntentionalExportError( - `Failed to read existing pack.mcmeta file at ${this.path}: ${e}\n\nFile content:\n${raw}` - ) + meta.content = JSON.parse(raw) + } catch (e: any) { + throw new IntentionalExportErrorFromInvalidFile(path, e) } + + return meta } toJSON(): SerializedPackMeta { - const json: SerializedPackMeta = { - pack: { - pack_format: this.pack_format, - supported_formats: - this.supportedFormats.length > 0 ? this.supportedFormats : undefined, - description: this.description, - }, - } - if (this.overlayEntries.size > 0) { - json.overlays = { - entries: Array.from(this.overlayEntries), - } - } - return json + return structuredClone(this.content) } } diff --git a/src/systems/jsonCompiler.ts b/src/systems/jsonCompiler.ts index 568d28e3..7bd46de0 100644 --- a/src/systems/jsonCompiler.ts +++ b/src/systems/jsonCompiler.ts @@ -2,20 +2,14 @@ /// /// -import type { IBlueprintBoneConfigJSON } from '../blueprintFormat' -import { type defaultValues } from '../blueprintSettings' -import { - getKeyframeCommands, - getKeyframeExecuteCondition, - getKeyframeRepeat, - getKeyframeRepeatFrequency, - getKeyframeVariant, -} from '../mods/customKeyframesMod' -import { EasingKey } from '../util/easing' +import type { IBlueprintBoneConfigJSON } from '../formats/blueprint' +import { type defaultValues } from '../formats/blueprint/settings' +import type { EasingKey } from '../util/easing' import { resolvePath } from '../util/fileUtil' import { detectCircularReferences, mapObjEntries, scrubUndefined } from '../util/misc' import { Variant } from '../variants' import type { INodeTransform, IRenderedAnimation, IRenderedFrame } from './animationRenderer' +import { JsonText } from './jsonText' import type { AnyRenderedNode, IRenderedModel, @@ -64,7 +58,7 @@ type ExportedBakedAnimation = Omit< frames: ExportedAnimationFrame[] modified_nodes: string[] } -type ExportedKeyframe = { +interface ExportedKeyframe { time: number channel: string value?: [string, string, string] @@ -96,7 +90,7 @@ type ExportedKeyframe = { repeat_frequency?: number } type ExportedAnimator = ExportedKeyframe[] -type ExportedDynamicAnimation = { +interface ExportedDynamicAnimation { name: string loop_mode: 'once' | 'hold' | 'loop' duration: number @@ -127,7 +121,7 @@ export interface IExportedJSON { */ settings: { export_namespace: (typeof defaultValues)['export_namespace'] - bounding_box: (typeof defaultValues)['bounding_box'] + bounding_box: (typeof defaultValues)['render_box'] // Resource Pack Settings custom_model_data_offset: (typeof defaultValues)['custom_model_data_offset'] // Plugin Settings @@ -148,15 +142,15 @@ function transferKey(obj: any, oldKey: string, newKey: string) { } function serailizeKeyframe(kf: _Keyframe): ExportedKeyframe { - const json = { + const json: ExportedKeyframe = scrubUndefined({ time: kf.time, channel: kf.channel, - commands: getKeyframeCommands(kf), - variant: getKeyframeVariant(kf), - execute_condition: getKeyframeExecuteCondition(kf), - repeat: getKeyframeRepeat(kf), - repeat_frequency: getKeyframeRepeatFrequency(kf), - } as ExportedKeyframe + commands: kf.function, + variant: kf.variant?.uuid, + execute_condition: kf.execute_condition, + repeat: kf.repeat, + repeat_frequency: kf.repeat_frequency, + }) switch (json.channel) { case 'variant': @@ -244,7 +238,7 @@ export function exportJSON(options: { const json: IExportedJSON = { settings: { export_namespace: aj.export_namespace, - bounding_box: aj.bounding_box, + bounding_box: aj.render_box, custom_model_data_offset: aj.custom_model_data_offset, baked_animations: aj.baked_animations, }, @@ -313,8 +307,8 @@ function serailizeNodeTransform(node: INodeTransform): ExportedNodetransform { head_rot: node.head_rot, scale: node.scale, interpolation: node.interpolation, - commands: node.commands, - commands_execute_condition: node.commands_execute_condition, + function: node.function, + function_execute_condition: node.function_execute_condition, } return json } @@ -347,7 +341,7 @@ function serailizeRenderedNode(node: AnyRenderedNode): ExportedRenderedNode { break } case 'text_display': { - json.text = node.text?.toJSON() + json.text = new JsonText(node.text).toJSON() break } } diff --git a/src/systems/jsonText/index.ts b/src/systems/jsonText/index.ts new file mode 100644 index 00000000..d88a1c36 --- /dev/null +++ b/src/systems/jsonText/index.ts @@ -0,0 +1,437 @@ +import { JsonTextParser } from './parser' +import { JsonTextStringifier } from './stringifier' + +export const FONT = '16px MinecraftFull' + +export enum STYLE_KEYS { + BOLD = 'bold', + ITALIC = 'italic', + UNDERLINED = 'underlined', + STRIKETHROUGH = 'strikethrough', + OBFUSCATED = 'obfuscated', + COLOR = 'color', + FONT = 'font', + SHADOW_COLOR = 'shadow_color', +} + +export enum CONTENT_TYPES { + TEXT = 'text', + TRANSLATABLE = 'translatable', + SCORE = 'score', + SELECTOR = 'selector', + KEYBIND = 'keybind', + NBT = 'nbt', + OBJECT = 'object', +} + +export enum CONTENT_KEYS { + TEXT = 'text', + TRANSLATE = 'translate', + SCORE = 'score', + SELECTOR = 'selector', + KEYBIND = 'keybind', + NBT = 'nbt', + SPRITE = 'sprite', + PLAYER = 'player', +} + +export enum EVENT_KEYS { + CLICK_EVENT = 'click_event', + HOVER_EVENT = 'hover_event', + LEGACY_CLICK_EVENT = 'clickEvent', + LEGACY_HOVER_EVENT = 'hoverEvent', +} + +export enum NBT_SOURCE_KEYS { + BLOCK = 'block', + ENTITY = 'entity', + STORAGE = 'storage', +} + +export const COLOR_VALUES = { + dark_red: '#AA0000', + red: '#FF5555', + gold: '#FFAA00', + yellow: '#FFFF55', + dark_green: '#00AA00', + green: '#55FF55', + aqua: '#55FFFF', + dark_aqua: '#00AAAA', + dark_blue: '#0000AA', + blue: '#5555FF', + light_purple: '#FF55FF', + dark_purple: '#AA00AA', + white: '#FFFFFF', + gray: '#AAAAAA', + dark_gray: '#555555', + black: '#000000', +} as const + +export type ComponentStyle = Pick +export type TextObjectColor = keyof typeof COLOR_VALUES | `#${string}` +export type TextObjectShadowColor = number | [number, number, number, number] + +export interface LegacyClickEvent { + action: + | 'open_url' + | 'open_file' + | 'run_command' + | 'suggest_command' + | 'change_page' + | 'copy_to_clipboard' + value: string +} + +export type LegacyHoverEvent = + | { action: 'show_text'; contents: TextElement } + | { + action: 'show_item' + contents: + | string + | { + id: string + count?: number + // Text displays dont support hover events anyway, + // so I'll just ignore this for now. + tag?: any + } + } + | { + action: 'show_entity' + contents: { + type: string + id: string | [number, number, number, number] + name?: string + } + } + +export enum MODERN_CLICK_EVENT_SUBKEYS { + ID = 'id', + URL = 'url', + PATH = 'path', + COMMAND = 'command', + PAGE = 'page', + VALUE = 'value', + DIALOG = 'dialog', + PAYLOAD = 'payload', +} + +export type ModernClickEvent = + | { + action: 'open_url' + url: string + } + | { + action: 'open_file' + path: string + } + | { + action: 'run_command' + command: string + } + | { + action: 'suggest_command' + command: string + } + | { + action: 'change_page' + page: number + } + | { + action: 'copy_to_clipboard' + value: string + } + | { + action: 'show_dialog' + // Text displays don't support click events anyway, so I'll just ignore this type for now. + dialog: string | any + } + | { + action: 'custom' + id: string + payload?: any + } + +export type ModernHoverEvent = + | { + action: 'show_text' + value: TextElement + } + | { + action: 'show_item' + id: string + count?: number + components?: any + } + | { + action: 'show_entity' + id: string + uuid: string | [number, number, number, number] + name?: string + } + +export interface TextObject { + type?: CONTENT_TYPES + + text?: string + + translate?: string + fallback?: string + with?: TextElement[] + + score?: { + name: string + objective: string + } + + selector?: string + separator?: TextElement + + keybind?: string + + nbt?: string + source?: 'block' | 'entity' | 'storage' + block?: string + entity?: string + storage?: string + interpret?: boolean + + object?: 'atlas' | 'player' + sprite?: string + atlas?: string + player?: { + name?: string + id?: string | [number, number, number, number] + texture?: string + cape?: string + model?: 'wide' | 'slim' + hat?: boolean + properties?: Array<{ + name: string + value: string + signature?: string + }> + } + + font?: string + color?: TextObjectColor + bold?: boolean + italic?: boolean + underlined?: boolean + strikethrough?: boolean + obfuscated?: boolean + shadow_color?: TextObjectShadowColor + + extra?: TextElement[] + insertion?: string + + clickEvent?: LegacyClickEvent + hoverEvent?: LegacyHoverEvent + click_event?: ModernClickEvent + hover_event?: ModernHoverEvent +} + +export type TextElement = string | TextElement[] | TextObject + +export class JsonText { + static defaultStyle: ComponentStyle = { color: 'white' } + static defaultMinecraftVersion = '1.21.9' + + isJsonTextClass = true + content: TextElement + + constructor(jsonText: TextElement) { + this.content = jsonText + } + + toString(minify = true, minecraftVersion = JsonText.defaultMinecraftVersion) { + const content = minify ? this.flatten() : this.content + return new JsonTextStringifier(minecraftVersion).stringify(content) + } + + toJSON(): TextElement { + return structuredClone(this.content) + } + + /** + * Returns a flattened version of this JsonText. + * + * If `explicitStyles` is true, all styles will be explicitly set on each component, + * even if they are the same as the parent style. + */ + flatten(explicitStyles = false): Array { + const output: Array = [] + + const processComponent = (element: TextElement, parentStyle: ComponentStyle = {}) => { + const style = JsonText.getComponentStyle(element, parentStyle) + const previous = output[output.length - 1] + switch (true) { + case Array.isArray(element): { + for (const child of element) { + processComponent(child, style) + } + break + } + + case typeof element === 'string': + // Merge with previous element if possible + if (typeof previous === 'string' && JsonText.hasSameStyle(style, parentStyle)) { + output[output.length - 1] = previous + element + break + } else if ( + typeof previous === 'object' && + previous.text !== undefined && + JsonText.hasSameStyle(style, previous) + ) { + previous.text += element + break + } + + if (!explicitStyles && JsonText.hasSameStyle(style, parentStyle)) { + output.push(element) + break + } + output.push({ ...parentStyle, text: element }) + break + + case typeof element === 'object': { + const style = JsonText.getComponentStyle(element, parentStyle) + const processed = { ...element } + delete processed.with + delete processed.extra + output.push({ ...style, ...processed }) + + const { with: withArray = [], extra: extraArray = [] } = element + + if (withArray.length > 0) { + processComponent(withArray, style) + } + if (extraArray.length > 0) { + processComponent(extraArray, style) + } + break + } + + default: + console.warn('Unknown component type in flatten:', element) + break + } + } + + processComponent(this.content) + return output + } + + static getComponentStyle( + component: TextElement, + parentStyle: ComponentStyle = JsonText.defaultStyle + ): ComponentStyle { + switch (true) { + case Array.isArray(component): + if (component.length === 0) return { ...parentStyle } + return JsonText.getComponentStyle(component[0], parentStyle) + + case typeof component === 'string': + return { ...parentStyle } + + case typeof component === 'object': { + const style = { ...parentStyle } + for (const key of Object.values(STYLE_KEYS)) { + if (component[key] === undefined) continue + style[key] = component[key] as any + } + return style + } + + default: + console.warn('Unknown component type in getComponentStyle:', component) + return { ...parentStyle } + } + } + + static hasSameStyle(a: ComponentStyle, b: ComponentStyle): boolean { + for (const key of Object.values(STYLE_KEYS)) { + if (a[key] !== b[key]) return false + } + return true + } + + static intToRgba(color: number): [number, number, number, number] { + const a = (color >> 24) & 0xff + const r = (color >> 16) & 0xff + const g = (color >> 8) & 0xff + const b = color & 0xff + return [r / 255, g / 255, b / 255, a / 255] + } + + static rgbaToInt([r, g, b, a]: [number, number, number, number]): number { + r = Math.floor(r * 255) + g = Math.floor(g * 255) + b = Math.floor(b * 255) + a = Math.floor(a * 255) + return (a << 24) | (r << 16) | (g << 8) | b + } + + static intToHex8(color: number): string { + return `#${(color >>> 0).toString(16).padStart(8, '0')}` + } + + static hexToRgba(hex: string): [number, number, number, number] { + return JsonText.intToRgba(JsonText.hexToInt(hex)) + } + + static moveHex8AlphaToStart(hex: string): string { + const alpha = hex.slice(-2) + return '#' + alpha + hex.slice(1, -2) + } + + static hexToInt(hex: string): number { + if (!hex.startsWith('#') || (hex.length !== 7 && hex.length !== 9)) { + throw new Error('Invalid hex color format. Expected #RRGGBB or #AARRGGBB.') + } + if (hex.length === 7) { + hex = '#ff' + hex.slice(1) // Add alpha + } + const unsigned = parseInt(hex.slice(1), 16) + return unsigned > 0x7fffffff ? unsigned - 0x100000000 : unsigned + } + + static getColor(color: TextObjectColor | TextObjectShadowColor): tinycolor.Instance { + if (Array.isArray(color)) { + return tinycolor({ + r: color[0] * 255, + g: color[1] * 255, + b: color[2] * 255, + a: color[3] ?? 1 * 255, + }) + } else if (typeof color === 'number') { + const rgba = JsonText.intToRgba(color) + return tinycolor({ + r: rgba[0] * 255, + g: rgba[1] * 255, + b: rgba[2] * 255, + a: rgba[3] * 255, + }) + } else if (color.startsWith('#')) { + return tinycolor(color) + } else if (color in COLOR_VALUES) { + return tinycolor(COLOR_VALUES[color as keyof typeof COLOR_VALUES]) + } else { + console.warn('Unknown color:', color) + return tinycolor('white') + } + } + + /** + * Attempts to parse a stringified Json Text Component. + * + * Supports SNBT-based Json Text from 1.21.5+ + */ + static fromString( + str: string, + options?: ConstructorParameters[0] + ): JsonText | undefined { + const parser = new JsonTextParser(options) + return parser.parse(str) + } +} diff --git a/src/systems/jsonText/parser.ts b/src/systems/jsonText/parser.ts new file mode 100644 index 00000000..015e4f5e --- /dev/null +++ b/src/systems/jsonText/parser.ts @@ -0,0 +1,2305 @@ +import { StringStream } from 'generic-stream' +import { Stopwatch } from 'src/util/stopwatch' +import { + COLOR_VALUES, + CONTENT_KEYS, + CONTENT_TYPES, + JsonText, + MODERN_CLICK_EVENT_SUBKEYS, + TextObjectShadowColor, + type LegacyClickEvent, + type LegacyHoverEvent, + type ModernClickEvent, + type ModernHoverEvent, + type TextElement, + type TextObject, + type TextObjectColor, +} from '.' +import type { MergeUnion } from '../../util/utilityTypes' + +enum NUMBER_TYPES { + ANY = 'number', + BYTE = 'byte', + SHORT = 'short', + INTEGER = 'int', + LONG = 'long', + FLOAT = 'float', + DOUBLE = 'double', + HEXADECIMAL = 'hexadecimal', + BINARY = 'binary number', +} + +enum FEATURES { + /** + * Allow unquoted object keys. + */ + LITERAL_KEYS = 1 << 0, + + /** + * Allow unquoted strings. + */ + LITERAL_STRINGS = 1 << 1, + + /** + * Allow using single quotes for object keys and strings. + */ + SINGLE_QUOTES = 1 << 2, + + /** + * Allow trailing commas in objects and arrays. + */ + TRAILING_COMMAS = 1 << 3, + + /** + * Allow omitting commas in objects and arrays. + * + * @NOTE This is not supported by Minecraft. Any JSON text components using this feature must be processed by this parser before using them in-game. + */ + OPTIONAL_COMMAS = 1 << 4, + + /** + * Use modern event format for text component mouse events. + * - `clickEvent` -> `click_event` + * - `hoverEvent` -> `hover_event` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5) or above. + + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=Text%20component%20format) article for more information. + */ + MODERN_EVENT_FORMAT = 1 << 5, + + /** + * Enables the `show_dialog` action for `click_event`. + * + * Requires {@link MODERN_EVENT_FORMAT} to be enabled + * + * Requires [Minecraft 1.21.6](https://minecraft.wiki/w/Java_Edition_1.21.6) or above. + */ + CLICK_EVENT_ACTION_SHOW_DIALOG = 1 << 6, + + /** + * Enables use of `type: "object"` in text objects. + * + * Requires [Minecraft 1.21.9](https://minecraft.wiki/w/Java_Edition_1.21.9) or above. + * + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.9#Command_format:~:text=Text%20component%20format) article for more information. + */ + TEXT_OBJECT_TYPE_OBJECT = 1 << 7, + + /** + * Enables the `shadow_color` field in text objects. + * + * Requires [Minecraft 1.21.4](https://minecraft.wiki/w/Java_Edition_1.21.4) or above. + * + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.4#Command_format:~:text=Raw%20JSON%20text%20format) article for more information. + */ + SHADOW_COLOR = 1 << 8, + + /** + * Enables the `shadow_color` field to accept {@link TextObjectColor} values, in addition to the normal number and array formats. + * + * Requires {@link SHADOW_COLOR} to be enabled + * + * @NOTE This is not supported by Minecraft. Any JSON text components using this feature must be processed by this parser before using them in-game. + */ + SHADOW_COLOR_ACCEPTS_STRING = 1 << 9, + + /** + * Enables `\s` as an escape sequence for space characters in strings. + * + * E.g. `"Hello,\sWorld!"` -> `"Hello, World!"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + SPACE_ESCAPE_SEQUENCE = 1 << 10, + + /** + * Enables `\x` as an escape sequence for hexadecimal characters in strings. + * + * E.g. `"\x41"` -> `"A"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + HEX_ESCAPE_SEQUENCE = 1 << 11, + + /** + * Enables `\U` as an escape sequence for 8-digit unicode characters in strings. + * + * E.g. `"\U0001F600"` -> `"😀"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE = 1 << 12, + + /** + * Enables `\N{...}` as an escape sequence for named unicode characters in strings. + * + * E.g. `"\N{Snowman}"` -> `"☃"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + NAMED_UNICODE_ESCAPE_SEQUENCE = 1 << 13, + + /** + * Allows text objects to infer `text` as an empty string if no other {@link CONTENT_KEYS} are present. + * + * E.g. `{ color: green }` -> `{ text: '', color: green }` + */ + IMPLICIT_TEXT_KEY = 1 << 14, + + /** + * Allows text objects to infer keys from keyless values. + * + * Inferrable keys: + * - `text` + * - `{ 'Hello, World!', color: red }` -> `{ text: 'Hello, World!', color: red }` + * - `{ 'Hello, World!' }` -> `{ text: 'Hello, World!' }` + * - `translate` (if `with` or `fallback` is present) + * - `{ my.translation.key, with: [...] }` -> `{ translate: my.translation.key, with: [...] }` + * - `{ my.translation.key, fallback: 'Hello, World!' }` -> `{ translate: my.translation.key, fallback: 'Hello, World!' }` + * - `color` (if `text` or `translate` is present) + * - `{ text: 'Hello, World!', red }` -> `{ text: 'Hello, World!', color: red }` + * - `{ text: '', '#00aced' }` -> `{ text: '', color: '#00aced' }` + */ + TEXT_OBJECT_INFERRED_KEYS = 1 << 15, + + ALLOW_CLICK_EVENTS = 1 << 16, + ALLOW_HOVER_EVENTS = 1 << 17, +} + +export function compareVersions(a: string, b: string): number { + const aParts = a.split('.').map(Number) + const bParts = b.split('.').map(Number) + for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { + const aPart = aParts[i] ?? 0 + const bPart = bParts[i] ?? 0 + if (aPart > bPart) return 1 + if (aPart < bPart) return -1 + } + return 0 +} + +export class JsonTextParserError extends Error { + constructor(message: string) { + super(message) + this.name = 'JsonTextParserError' + } +} + +interface JsonTextSyntaxErrorOptions { + child?: Error + line?: number + column?: number + pointerLength?: number +} + +export class JsonTextSyntaxError extends Error { + private originalMessage: string + + stream: StringStream + child?: Error + line: number + column: number + pointerLength: number + + constructor( + message: string, + stream: StringStream, + { + child, + line = stream.line, + column = stream.column, + pointerLength = 1, + }: JsonTextSyntaxErrorOptions = {} + ) { + super(message) + this.name = 'JsonTextSyntaxError' + this.stream = stream + this.child = child + this.line = line + this.column = column + this.pointerLength = pointerLength + + this.originalMessage = message + + if (this.child) { + this.message = `${this.message} at ${this.line}:${this.column}\n${this.child.message}` + return + } + + this.updatePointerMessage() + } + + getOriginErrorMessage(): string { + if (this.child) { + if (this.child instanceof JsonTextSyntaxError) { + return this.child.getOriginErrorMessage() + } + return this.child.message + } + return this.message + } + + updatePointerMessage() { + // Unexpected '}' at 1:5 + // Hello, World!"} + // ↑ + + // Complete the line + const startOfLine = this.stream.lines[this.line - 1].startIndex + const endOfLine = this.stream.seek('\n') + + const lineString = this.stream.string.slice(startOfLine, endOfLine).trimEnd() + + // Get column where tabs count as 4 characters + const actualColumn = lineString.slice(0, this.column - 1).replace(/\t/g, ' ').length + 1 + + const pointer = ' '.repeat(actualColumn - 1) + '↑'.repeat(this.pointerLength) + this.message = `${this.originalMessage} at ${this.line}:${this.column}\n${lineString}\n${pointer}` + } +} + +export namespace CHARS { + export const ALPHA_LOWER = Array.from('abcdefghijklmnopqrstuvwxyz') + export const ALPHA_UPPER = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + export const ALPHA = ALPHA_LOWER.concat(ALPHA_UPPER) + export const NUMBER = Array.from('0123456789') + export const INT_START = Array.from('-123456789') + export const NUMBER_START = NUMBER.concat(Array.from('-.')) + export const ALPHANUMERIC = ALPHA.concat(NUMBER) + export const VERTICAL_WHITESPACE = Array.from('\n\r') + export const WHITESPACE = Array.from(' \t').concat(VERTICAL_WHITESPACE) + export const LITERAL = ALPHANUMERIC.concat(Array.from('._-+')) + export const LITERAL_START = ALPHA.concat('_') + export const QUOTES = Array.from(`"'`) + export const BINARY = Array.from('01') + export const HEXADECIMAL = NUMBER.concat(Array.from('abcdef'), Array.from('ABCDEF')) + export const SYNTAX_BOUNDARY = WHITESPACE.concat(QUOTES, Array.from(',:[]{}')) +} + +interface SourcePosition { + index: number + line: number + column: number + equals(other: { line: number; column: number }): boolean +} + +/** + * A multi-version JSON text parser. + */ +export class JsonTextParser { + static maxNestingDepth = 512 + static maxArrayLength = 2 ** 31 - 9 + + // eslint-disable-next-line @typescript-eslint/naming-convention + static FEATURES = FEATURES + + static defaultFeatures = + // Minecraft syntax sugar + FEATURES.LITERAL_KEYS | + FEATURES.LITERAL_STRINGS | + FEATURES.SINGLE_QUOTES | + FEATURES.TRAILING_COMMAS | + // Custom syntax sugar + FEATURES.OPTIONAL_COMMAS | + FEATURES.SHADOW_COLOR_ACCEPTS_STRING | + FEATURES.TEXT_OBJECT_INFERRED_KEYS | + FEATURES.IMPLICIT_TEXT_KEY | + // Mouse events + FEATURES.ALLOW_CLICK_EVENTS | + FEATURES.ALLOW_HOVER_EVENTS + + private s!: StringStream + private currentNestingDepth = 0 + + minecraftVersion: string + enabledFeatures = 0 + + /** + * If {@link featureFlags} is not provided, features will be automatically enabled based on {@link minecraftVersion}. + * + * Individual features can be enabled/disabled post-construction by modifying {@link enabledFeatures}. + * + * By default, the following features are always enabled: + * - {@link FEATURES.LITERAL_KEYS} + * - {@link FEATURES.LITERAL_STRINGS} + * - {@link FEATURES.SINGLE_QUOTES} + * - {@link FEATURES.TRAILING_COMMAS} + * - {@link FEATURES.OPTIONAL_COMMAS} + * - {@link FEATURES.SHADOW_COLOR_ACCEPTS_STRING} + * - {@link FEATURES.TEXT_OBJECT_INFERRED_KEYS} + * - {@link FEATURES.IMPLICIT_TEXT_KEY} + * See {@link FEATURES} for information on each feature. + * + * @example Disable *only* literal strings and enable modern event format: + * ```ts + * const parser = new JsonTextParser('{ text: Hello }') + * parser.enabledFeatures &= ~FEATURES.LITERAL_STRINGS + * parser.enabledFeatures |= FEATURES.MODERN_EVENT_FORMAT + * ``` + * + */ + constructor(options?: { minecraftVersion?: string; featureFlags?: number }) { + const { minecraftVersion = JsonText.defaultMinecraftVersion, featureFlags } = options ?? {} + + this.minecraftVersion = minecraftVersion + this.reset() + + if (typeof featureFlags === 'number') { + this.enabledFeatures = featureFlags + } else { + this.enabledFeatures |= JsonTextParser.defaultFeatures + + if (compareVersions(this.minecraftVersion, '1.21.4') >= 0) { + this.enabledFeatures |= FEATURES.SHADOW_COLOR + } + if (compareVersions(this.minecraftVersion, '1.21.5') >= 0) { + this.enabledFeatures |= + FEATURES.MODERN_EVENT_FORMAT | + FEATURES.SPACE_ESCAPE_SEQUENCE | + FEATURES.HEX_ESCAPE_SEQUENCE | + FEATURES.EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE | + FEATURES.NAMED_UNICODE_ESCAPE_SEQUENCE + } + if (compareVersions(this.minecraftVersion, '1.21.6') >= 0) { + this.enabledFeatures |= FEATURES.CLICK_EVENT_ACTION_SHOW_DIALOG + } + if (compareVersions(this.minecraftVersion, '1.21.9') >= 0) { + this.enabledFeatures |= FEATURES.TEXT_OBJECT_TYPE_OBJECT + } + } + } + + parse(text: string): JsonText { + const stopwatch = new Stopwatch('Parse JSON Text').start() + this.s = new StringStream(text) + this.reset() + + try { + this.consumeWhitespace() + const result = this.parseTextElement() + this.consumeWhitespace() + if (this.s.item) { + this.throwSyntax(`Unexpected trailing '${this.s.item}' after JsonTextElement`) + } + stopwatch.debug(result) + return new JsonText(result) + } catch (e: any) { + if (e instanceof JsonTextParserError) { + throw new JsonTextParserError( + `Internal Parser Error:\n\t${e.message}\nThis is a bug, please report it.` + ) + } + throw e + } + } + + throwSyntax(message: string, options?: JsonTextSyntaxErrorOptions): never { + throw new JsonTextSyntaxError(message, this.s, options) + } + + private recordPosition(): SourcePosition { + return { + index: this.s.index, + line: this.s.line, + column: this.s.column, + equals(other: { line: number; column: number }): boolean { + return this.line === other.line && this.column === other.column + }, + } + } + + private reset() { + this.currentNestingDepth = 0 + } + + private consumeWhitespace() { + this.s.consumeWhile(s => !!s.item && CHARS.WHITESPACE.includes(s.item)) + } + + private parseTextElement(): TextElement { + let result: TextElement + if (this.s.item === '{') { + result = this.parseTextObject() + } else if (this.s.item === '[') { + result = this.parseTextElementArray() + } else if ( + !this.s.item || + CHARS.QUOTES.includes(this.s.item) || + CHARS.LITERAL_START.includes(this.s.item) + ) { + result = this.parseString() + } else { + this.throwSyntax(`Unexpected '${this.s.item}' in JsonTextElement`) + } + + return result + } + + private parseObject({ + objectName, + // Defaults to an empty set to avoid having to check for undefined later + keys = new Set(), + required, + parseKey, + parseValue, + validateResult, + }: { + /** Name of the object being parsed, for error messages. */ + objectName: string + /** Keys that are allowed in the object. */ + keys?: Set + /** Keys that are required in the object. */ + required?: Set + /** + * Called to parse a field's key. If not provided, keys will be parsed as strings. + * + * @returns The parsed key, or `undefined` to indicate the default key parser should be used. + * + * Can be used to parse keyless values. + */ + parseKey?: (keys: Set, obj: Partial) => { key: K; value?: T[K] & string } | undefined + /** + * Called to parse a field's value, and set it on the object. + * + * Will never be called for fields not in {@link expectedFields}. + */ + parseValue: (key: K, obj: Partial) => void + /** + * Called with the completed object for extra validation. + * + * Returns a string to indicate an error, or undefined if the object is valid. + */ + validateResult?: (obj: Partial) => string | void + }): T { + this.currentNestingDepth++ + if (this.currentNestingDepth > JsonTextParser.maxNestingDepth) { + this.throwSyntax('Nesting depth limit exceeded') + } + + const startPosition = this.recordPosition() + + try { + this.expect(this.s.item, '{', 'to begin ' + objectName, true) + this.consumeWhitespace() + const obj = {} as Partial + while (this.s.item !== '}') { + const keyPosition = this.recordPosition() + const quoted = CHARS.QUOTES.includes(this.s.item!) + if (quoted) keyPosition.column++ + + let key: K | undefined + const result = parseKey?.(keys, obj) + if (result) { + key = result.key + } else { + // Default key handling + if (this.enabledFeatures & FEATURES.LITERAL_KEYS) { + key = this.parseString() as K + } else { + key = this.parseQuotedString() as K + } + } + if (result?.value !== undefined) { + obj[key] = result.value + this.consumeWhitespace() + // If a colon is found after a keyless value, it was actually an unknown key + if (this.s.item === ':') { + this.throwSyntax(`Unknown key '${result.value}' in ${objectName}`, { + ...keyPosition, + pointerLength: result.value.length, + }) + } + } else { + if (obj[key] !== undefined) { + this.throwSyntax(`Duplicate key '${key}'`, { + ...keyPosition, + pointerLength: key.length, + }) + } + + if (keys.size > 0 && !keys.has(key)) { + this.throwSyntax(`Unknown key '${key}' in ${objectName}`, { + ...keyPosition, + pointerLength: key.length, + }) + } + + this.expectSyntaxBoundaryAfter(`key '${key}'`) + this.consumeWhitespace() + + this.expect(this.s.item, ':', `to follow key '${key}'`, true) + this.consumeWhitespace() + + if (!this.s.item) { + this.throwSyntax(`Unexpected EOF in ` + objectName, this.s) + } + const valuePosition = this.recordPosition() + parseValue(key, obj) + // If the value parser didn't consume anything, assume the value is missing. + if (valuePosition.equals(this.s)) { + this.throwSyntax(`Missing value for '${key}'`, this.s) + } + } + this.consumeWhitespace() + + if (this.s.item === ',') { + this.s.consume() + this.consumeWhitespace() + if (this.s.item === ',') { + this.throwSyntax(`Extra comma`, this.s) + } + if (this.s.item === '}' && !(this.enabledFeatures & FEATURES.TRAILING_COMMAS)) { + this.throwSyntax(`Trailing comma in ` + objectName, this.s) + } + } else if (this.s.item === '}') { + break + } else if (this.s.item === undefined) { + this.throwSyntax('Unexpected EOF in ' + objectName, this.s) + } else if (!(this.enabledFeatures & FEATURES.OPTIONAL_COMMAS)) { + this.throwSyntax(`Expected ',' or '}' after ${objectName} entry`) + } + } + this.s.consume() // } + + if (required) { + for (const key of required) { + if (obj[key] !== undefined) continue + this.throwSyntax( + `Missing required field '${String(key)}' in ` + objectName, + startPosition + ) + } + } + + if (validateResult) { + const error = validateResult(obj) + if (error) { + this.throwSyntax(error, startPosition) + } + } + + this.currentNestingDepth-- + return obj as T + } catch (e: any) { + this.throwSyntax('Invalid ' + objectName, { child: e, ...startPosition }) + } + } + + private normalizeHexColor(color: string): string { + if (color.startsWith('0x')) color = '#' + color.substring(2) + if (!/^#[0-9a-fA-F]{6}$/i.test(color)) { + this.throwSyntax(`Invalid hex color '${color}'`, this.s) + } + return color.toLowerCase() + } + + private normalizeHex8Color(color: string): string { + if (color.startsWith('0x')) color = '#' + color.substring(2) + if (!/^#[0-9a-fA-F]{8}$/i.test(color)) { + this.throwSyntax(`Invalid hex color '${color}'`, this.s) + } + return color.toLowerCase() + } + + private assertTextObjectColorIsValid(color: string): asserts color is TextObjectColor { + if (color.startsWith('#')) { + this.normalizeHexColor(color) + } else if (!(color in COLOR_VALUES)) { + this.throwSyntax(`Unknown color '${color}'`, this.s) + } + } + + private parseTextObjectColor(): TextObjectColor { + const color = this.parseString() + if (color.startsWith('#')) { + this.normalizeHexColor(color) + return color as `#${string}` + } else if (color in COLOR_VALUES) { + return color as keyof typeof COLOR_VALUES + } else { + this.throwSyntax(`Unknown color '${color}'`, this.s) + } + } + + private parseTextObjectShadowColor(): TextObjectShadowColor { + if (!(this.enabledFeatures & FEATURES.SHADOW_COLOR)) { + this.throwSyntax(`'shadow_color' field is only available in Minecraft 1.21.4 and above`) + } + + if (this.s.item === '[') { + const rgba = this.parseFloatArray(4) as [number, number, number, number] + for (const n of rgba) { + if (typeof n !== 'number' || n < 0 || n > 1) { + this.throwSyntax(`'shadow_color' array values must be numbers between 0 and 1`) + } + } + return rgba + } else if (CHARS.NUMBER.includes(this.s.item!)) { + const colorString = this.parseNumber(NUMBER_TYPES.INTEGER) + let color: number + if (colorString.startsWith('0x')) { + color = parseInt(colorString.substring(2), 16) + } else { + color = parseInt(colorString, 10) + } + if (typeof color !== 'number' || color < 0 || color > 0xffffffff) { + this.throwSyntax(`'shadow_color' value must be a number between 0 and 0xffffffff`) + } + return JsonText.intToRgba(color) + } + + if (!(this.enabledFeatures & FEATURES.SHADOW_COLOR_ACCEPTS_STRING)) { + this.throwSyntax(`Expected value of 'shadow_color' to be a float-array or integer.`) + } + + let hexColor = this.parseString() + if (hexColor.startsWith('#')) { + hexColor = this.normalizeHex8Color(hexColor) + } else if (hexColor in COLOR_VALUES) { + hexColor = COLOR_VALUES[hexColor as keyof typeof COLOR_VALUES] + } else { + this.throwSyntax(`Unknown color '${hexColor}'`, this.s) + } + return JsonText.hexToRgba(hexColor) + } + + private parseTextObject() { + return this.parseObject({ + objectName: 'TextObject', + keys: new Set([ + 'type', + 'font', + 'color', + 'bold', + 'italic', + 'underlined', + 'strikethrough', + 'obfuscated', + 'extra', + 'insertion', + 'clickEvent', + 'hoverEvent', + 'shadow_color', + 'click_event', + 'hover_event', + 'text', + 'translate', + 'fallback', + 'with', + 'score', + 'selector', + 'separator', + 'keybind', + 'nbt', + 'block', + 'entity', + 'storage', + 'interpret', + 'object', + 'sprite', + 'atlas', + 'player', + ]), + parseKey: (keys, obj) => { + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + if (this.s.item === '#') { + if (obj.color !== undefined) { + this.throwSyntax( + `Cannot infer keyless value as 'color' when 'color' is already defined`, + this.s + ) + } + const colorString = this.parseHashedHexColor() + return { key: 'color', value: colorString } + } else if (this.s.look(0, 2) === '0x') { + if (obj.color !== undefined) { + this.throwSyntax( + `Cannot infer keyless value as 'color' when 'color' is already defined`, + this.s + ) + } + // Parse as hex color + const colorString = this.parseNumber(NUMBER_TYPES.HEXADECIMAL) + return { key: 'color', value: this.normalizeHexColor(colorString) } + } + } + + let key: keyof TextObject | undefined + if (this.enabledFeatures & FEATURES.LITERAL_KEYS) { + key = this.parseString() as keyof TextObject + } else { + key = this.parseQuotedString() as keyof TextObject + } + // Let the main parser handle known and duplicate keys + if (keys.has(key) || obj[key] != undefined) return { key } + + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + // Unknown key, try to infer + if ( + obj.color === undefined && + (obj.text !== undefined || obj.translate !== undefined) + ) { + this.assertTextObjectColorIsValid(key) + return { key: 'color', value: key } + } else if ( + obj.translate === undefined && + (obj.fallback !== undefined || obj.with !== undefined) + ) { + return { key: 'translate', value: key } + } else if (obj.text === undefined) { + return { key: 'text', value: key } + } + } + + return { key } // Let the main parser handle unknown keys + }, + parseValue: (key, obj) => { + switch (key) { + case 'sprite': + case 'atlas': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'${key}' field is only available in Minecraft 1.21.9 and above` + ) + } + case 'insertion': + case 'font': + case 'keybind': + case 'selector': + case 'nbt': + case 'block': + case 'entity': + case 'storage': + case 'text': + case 'translate': + case 'fallback': + obj[key] = this.parseString() + break + + case 'bold': + case 'italic': + case 'obfuscated': + case 'strikethrough': + case 'underlined': + case 'interpret': + obj[key] = this.parseBoolean() + break + + case 'source': + obj[key] = this.parseString(['storage', 'block', 'entity']) + break + + case 'separator': + obj[key] = this.parseTextElement() + break + + case 'object': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'object' field is only available in Minecraft 1.21.9 and above` + ) + } + obj[key] = this.parseString(['atlas', 'player']) + break + + case 'player': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'player' field is only available in Minecraft 1.21.9 and above` + ) + } + obj[key] = this.parsePlayerObject() + break + + case 'type': { + const type = this.parseString(Object.values(CONTENT_TYPES)) + if ( + type === CONTENT_TYPES.OBJECT && + !(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT) + ) { + this.throwSyntax( + `Object type '${CONTENT_TYPES.OBJECT}' is only available in Minecraft 1.21.9 and above` + ) + } + obj[key] = type + break + } + + case 'color': + obj[key] = this.parseTextObjectColor() + break + + case 'shadow_color': + obj[key] = this.parseTextObjectShadowColor() + break + + case 'with': + case 'extra': + obj[key] = this.parseTextElementArray() + break + + case 'score': + obj[key] = this.parseScoreObject() + break + + case 'clickEvent': { + if (!(this.enabledFeatures & FEATURES.ALLOW_CLICK_EVENTS)) { + this.throwSyntax(`'clickEvent' field is not allowed`) + } + const event = this.parseLegacyClickEventObject() + if (this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT) { + if (obj.click_event !== undefined) { + this.throwSyntax( + `Cannot use both 'clickEvent' and 'click_event' fields. For Minecraft versions below 1.21.5, use only 'clickEvent'` + ) + } + obj.click_event = this.transformLegacyClickEventToModern(event) + delete obj.clickEvent + break + } + obj[key] = event + break + } + + case 'click_event': { + if (!(this.enabledFeatures & FEATURES.ALLOW_CLICK_EVENTS)) { + this.throwSyntax(`'click_event' field is not allowed`) + } + const event = this.parseModernClickEventObject() + if (!(this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT)) { + if (obj.clickEvent !== undefined) { + this.throwSyntax( + `Cannot use both 'clickEvent' and 'click_event' fields. For Minecraft versions 1.21.5 and above, use only 'click_event'` + ) + } + obj.clickEvent = this.transformModernClickEventObjectToLegacy(event) + delete obj.click_event + break + } + obj[key] = event + break + } + + case 'hoverEvent': { + if (!(this.enabledFeatures & FEATURES.ALLOW_HOVER_EVENTS)) { + this.throwSyntax(`'hoverEvent' field is not allowed`) + } + const event = this.parseLegacyHoverEventObject() + if (this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT) { + if (obj.hover_event !== undefined) { + this.throwSyntax( + `Cannot use both 'hoverEvent' and 'hover_event' fields. For Minecraft versions below 1.21.5, use only 'hoverEvent'` + ) + } + obj.hover_event = this.transformLegacyHoverEventObjectToModern(event) + delete obj.hoverEvent + break + } + obj[key] = event + break + } + + case 'hover_event': + if (!(this.enabledFeatures & FEATURES.ALLOW_HOVER_EVENTS)) { + this.throwSyntax(`'hover_event' field is not allowed`) + } + const event = this.parseModernHoverEventObject() + if (!(this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT)) { + if (obj.hoverEvent !== undefined) { + this.throwSyntax( + `Cannot use both 'hoverEvent' and 'hover_event' fields. For Minecraft versions 1.21.5 and above, use only 'hover_event'` + ) + } + obj.hoverEvent = this.transformModernHoverEventObjectToLegacy(event) + delete obj.hover_event + break + } + obj[key] = event + break + } + }, + validateResult: obj => { + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + if ( + obj.text !== undefined && + obj.translate === undefined && + (obj.with !== undefined || obj.fallback !== undefined) + ) { + obj.translate = obj.text + delete obj.text + } + } + + const contentKeys = Object.values(CONTENT_KEYS).filter(k => obj[k] !== undefined) + if (contentKeys.length > 1) { + return `Only one content field may be present, but found ${ + contentKeys.length + }: '${contentKeys.join("', '")}'` + } + + if (Object.values(CONTENT_KEYS).every(k => obj[k] == undefined)) { + if (this.enabledFeatures & FEATURES.IMPLICIT_TEXT_KEY) { + obj.text ??= '' + } else { + return `At least one content field must be present: ${Object.values( + CONTENT_KEYS + ).join(', ')}` + } + } + + switch (obj.type) { + case CONTENT_TYPES.TEXT: + if (obj.text == undefined) { + return `'text' is required when 'type' is '${CONTENT_TYPES.TEXT}'` + } + break + + case CONTENT_TYPES.TRANSLATABLE: + if (obj.translate == undefined) { + return `'translate' is required when 'type' is '${CONTENT_TYPES.TRANSLATABLE}'` + } + break + + case CONTENT_TYPES.SCORE: + if (obj.score == undefined) { + return `'score' is required when 'type' is '${CONTENT_TYPES.SCORE}'` + } + break + + case CONTENT_TYPES.SELECTOR: + if (obj.selector == undefined) { + return `'selector' is required when 'type' is '${CONTENT_TYPES.SELECTOR}'` + } + break + + case CONTENT_TYPES.KEYBIND: + if (obj.keybind == undefined) { + return `'keybind' is required when 'type' is '${CONTENT_TYPES.KEYBIND}'` + } + break + + case CONTENT_TYPES.NBT: + if (obj.nbt == undefined) { + return `'nbt' is required when 'type' is '${CONTENT_TYPES.NBT}'` + } + break + + case CONTENT_TYPES.OBJECT: + if (obj.sprite == undefined) { + return `'sprite' is required when 'type' is '${CONTENT_TYPES.OBJECT}'` + } + break + } + + if (obj.translate == undefined && obj.with != undefined) + return `'with' requires 'translate'` + else if (obj.translate == undefined && obj.fallback != undefined) + return `'fallback' requires 'translate'` + + if (obj.separator != undefined && obj.nbt == undefined && obj.selector == undefined) + return `'separator' requires 'nbt' or 'selector'` + + if (obj.object != undefined) { + if (obj.object === 'player' && obj.player == undefined) + return `player object requires 'player'` + else if (obj.object === 'atlas' && obj.sprite == undefined) + return `atlas object requires 'sprite'` + else if (obj.player == undefined && obj.sprite == undefined) + return `'object' requires 'player' or 'sprite'` + } + + if (obj.atlas != undefined && obj.sprite == undefined) { + return `'atlas' requires 'sprite'` + } + + if (obj.nbt == undefined) { + if (obj.source != undefined) return `'source' requires 'nbt'` + else if (obj.block != undefined) return `'block' requires 'nbt'` + else if (obj.entity != undefined) return `'entity' requires 'nbt'` + else if (obj.storage != undefined) return `'storage' requires 'nbt'` + } + + if (obj.nbt != undefined) { + if ( + obj.block == undefined && + obj.entity == undefined && + obj.storage == undefined + ) + return `'nbt' requires 'block', 'entity', or 'storage'` + + switch (obj.source) { + case 'block': + if (obj.block == undefined) + return `'nbt' with source of 'block' requires 'block'` + break + + case 'entity': + if (obj.entity == undefined) + return `'nbt' with source of 'entity' requires 'entity'` + break + + case 'storage': + if (obj.storage == undefined) + return `'nbt' with source of 'storage' requires 'storage'` + break + } + } + }, + }) + } + + private parsePlayerPropertyObject() { + return this.parseObject< + NonNullable['properties']>[number] + >({ + objectName: 'player property object', + keys: new Set(['name', 'value', 'signature']), + required: new Set(['name', 'value']), + parseValue: (key, obj) => { + switch (key) { + case 'name': + case 'value': + case 'signature': + obj[key] = this.parseString() + break + } + }, + }) + } + + private parsePlayerPropertiesArray() { + return this.parseArray['properties']>>({ + arrayName: 'player properties array', + parseItem: this.parsePlayerPropertyObject.bind(this), + }) + } + + private parsePlayerObject() { + return this.parseObject>({ + objectName: 'player object', + keys: new Set(['name', 'id', 'texture', 'cape', 'model', 'properties', 'hat']), + parseValue: (key, obj) => { + switch (key) { + case 'id': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // fallthrough + case 'name': + case 'texture': + case 'cape': + obj[key] = this.parseString() + break + + case 'hat': + obj[key] = this.parseBoolean() + break + + case 'model': + obj[key] = this.parseString(['wide', 'slim']) + break + + case 'properties': + obj[key] = this.parsePlayerPropertiesArray() + break + } + }, + }) + } + + private parseUnknownArray(arrayName: string) { + return this.parseArray({ + arrayName, + parseItem: this.parseUnknownValue.bind(this), + }) + } + + private parseUnknownValue() { + if (this.s.item === '{') { + return this.parseUnknownObject('object') + } else if (this.s.item === '[') { + return this.parseUnknownArray('array') + } else if (!this.s.item) { + this.throwSyntax('Unexpected EOF', this.s) + } else if (CHARS.QUOTES.includes(this.s.item)) { + return this.parseQuotedString() + } else if (this.s.item === 't' || this.s.item === 'f') { + return this.parseBoolean() + } else if (CHARS.NUMBER_START.includes(this.s.item)) { + return this.parseNumber() + } else if (CHARS.LITERAL_START.includes(this.s.item)) { + return this.parseLiteral() + } else { + this.throwSyntax(`Unexpected '${this.s.item}'`, this.s) + } + } + + private parseUnknownObject(objectName: string) { + return this.parseObject>({ + objectName, + parseValue: (key, obj) => { + obj[key] = this.parseUnknownValue() + }, + }) + } + + private parseScoreObject() { + return this.parseObject>({ + objectName: 'score object', + keys: new Set(['name', 'objective']), + required: new Set(['name', 'objective']), + parseValue: (key, obj) => { + switch (key) { + case 'name': + case 'objective': + obj[key] = this.parseString() + break + } + }, + }) + } + + private parseLegacyClickEventObject() { + return this.parseObject({ + objectName: 'clickEvent', + keys: new Set(['action', 'value']), + required: new Set(['action', 'value']), + parseValue: (key, obj) => { + switch (key) { + case 'action': + const value = this.parseString([ + 'open_url', + 'open_file', + 'run_command', + 'suggest_command', + 'change_page', + 'copy_to_clipboard', + ] as const) + if (value === 'open_file') { + this.throwSyntax(`clickEvent 'open_file' cannot be used by commands`) + } + obj[key] = value + break + + case 'value': + obj[key] = this.parseString() + break + } + }, + }) + } + + private parseModernClickEventObject() { + return this.parseObject>({ + objectName: 'click_event', + keys: new Set([ + 'action', + 'url', + 'path', + 'command', + 'page', + 'value', + 'dialog', + 'id', + 'payload', + ]), + required: new Set(['action']), + parseValue: (key, obj) => { + switch (key) { + case 'action': + const value = this.parseString([ + 'open_url', + 'open_file', + 'run_command', + 'suggest_command', + 'change_page', + 'copy_to_clipboard', + 'show_dialog', + 'custom', + ] as const) + if (value === 'open_file') { + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) + } + if (value === 'show_dialog') { + if (!(this.enabledFeatures & FEATURES.CLICK_EVENT_ACTION_SHOW_DIALOG)) { + this.throwSyntax( + `Click event 'show_dialog' is only available in Minecraft 1.21.6 and above` + ) + } + } + obj[key] = value + break + + case 'url': + const url: string = this.parseString() + let parsedUrl: URL + try { + parsedUrl = new URL(url) + } catch (e: any) { + this.throwSyntax( + `Invalid URL format for 'url' in 'open_url' clickEvent: ${e.message}` + ) + } + + if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { + this.throwSyntax(`Invalid URL: Protocol must be 'http:' or 'https:'`) + } + obj[key] = url + break + + case 'path': + obj[key] = this.parseString() + break + + case 'command': + obj[key] = this.parseString() + break + + case 'page': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)) + break + + case 'value': + obj[key] = this.parseString() + break + + case 'dialog': + obj[key] = this.parseUnknownObject('click_event.dialog') + break + + case 'id': + obj[key] = this.parseString() + break + + case 'payload': + obj[key] = this.parseUnknownValue() + break + } + }, + validateResult: obj => { + let subkeys = Object.values(MODERN_CLICK_EVENT_SUBKEYS) + switch (obj.action) { + case 'open_url': + if (obj.url === undefined) { + return `Click event of type 'open_url' missing required key 'url'` + } + subkeys = subkeys.filter(k => k !== 'url') + break + + case 'open_file': + if (obj.path === undefined) { + return `Click event of type 'open_file' missing required key 'path'` + } + subkeys = subkeys.filter(k => k !== 'path') + break + + case 'run_command': + if (obj.command === undefined) { + return `Click event of type 'run_command' missing required key 'command'` + } + subkeys = subkeys.filter(k => k !== 'command') + break + + case 'suggest_command': + if (obj.command === undefined) { + return `Click event of type 'suggest_command' missing required key 'command'` + } + subkeys = subkeys.filter(k => k !== 'command') + break + + case 'change_page': + if (obj.page === undefined) { + return `Click event of type 'change_page' missing required key 'page'` + } + subkeys = subkeys.filter(k => k !== 'page') + break + + case 'copy_to_clipboard': + if (obj.value === undefined) { + return `Click event of type 'copy_to_clipboard' missing required key 'value'` + } + subkeys = subkeys.filter(k => k !== 'value') + break + + case 'custom': + if (obj.id === undefined) { + return `Click event of type 'custom' missing required key 'id'` + } + subkeys = subkeys.filter(k => k !== 'id') + subkeys = subkeys.filter(k => k !== 'payload') + break + + case 'show_dialog': + if (obj.dialog === undefined) { + return `Click event of type 'show_dialog' missing required key 'dialog'` + } + subkeys = subkeys.filter(k => k !== 'dialog') + break + } + + if (subkeys.some(k => obj[k] !== undefined)) { + return `Click event of type '${obj.action}' cannot have keys: ${subkeys + .map(k => `'${k}'`) + .join(', ')}` + } + }, + }) as ModernClickEvent + } + + private transformModernClickEventObjectToLegacy(event: ModernClickEvent): LegacyClickEvent { + switch (event.action) { + case 'open_url': + return { + action: 'open_url', + value: event.url, + } + + case 'open_file': + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) + + case 'run_command': + case 'suggest_command': + return { + action: event.action, + value: event.command, + } + + case 'change_page': + return { + action: 'change_page', + value: String(event.page), + } + + case 'copy_to_clipboard': + return { + action: 'copy_to_clipboard', + value: event.value, + } + + case 'show_dialog': + this.throwSyntax( + `Click events of type 'show_dialog' are not supported in versions below 1.21.6` + ) + + case 'custom': + this.throwSyntax( + `Click events of type 'custom' are not supported in versions below 1.21.5` + ) + } + } + + private transformLegacyClickEventToModern(event: LegacyClickEvent): ModernClickEvent { + switch (event.action) { + case 'open_url': { + let url: string + try { + url = new URL(event.value).toString() + } catch { + this.throwSyntax(`Invalid URL format for 'open_url' clickEvent`) + } + if (!/^https?:\/\//.exec(event.value)) { + this.throwSyntax( + `Invalid URL format for 'open_url' clickEvent. URL must start with 'http://' or 'https://'` + ) + } + return { + action: 'open_url', + url: url, + } + } + + case 'open_file': + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) + + case 'run_command': + case 'suggest_command': + return { + action: event.action, + command: event.value, + } + + case 'change_page': + return { + action: 'change_page', + page: Number(event.value), + } + + case 'copy_to_clipboard': + return { + action: 'copy_to_clipboard', + value: event.value, + } + } + } + + private parseLegacyHoverEventObjectShowItemContents() { + return this.parseObject< + Exclude<(LegacyHoverEvent & { action: 'show_item' })['contents'], string> + >({ + objectName: 'hoverEvent.contents', + keys: new Set(['id', 'count', 'tag']), + required: new Set(['id']), + parseValue: (key, obj) => { + switch (key) { + case 'id': + obj[key] = this.parseString() + break + + case 'count': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.BYTE)) + break + + case 'tag': + obj[key] = this.parseUnknownValue() + break + } + }, + }) + } + + private parseLegacyHoverEventObjectShowEntityContents() { + return this.parseObject<(LegacyHoverEvent & { action: 'show_entity' })['contents']>({ + objectName: 'hoverEvent.contents', + keys: new Set(['type', 'id', 'name']), + required: new Set(['type', 'id']), + parseValue: (key, obj) => { + switch (key) { + case 'id': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // ID should fallthrough to string parsing if not an int array + case 'type': + case 'name': + obj[key] = this.parseString() + break + } + }, + }) + } + + private parseLegacyHoverEventObject() { + return this.parseObject({ + objectName: 'hoverEvent', + keys: new Set(['action', 'contents']), + required: new Set(['action', 'contents']), + parseValue: (key, obj) => { + switch (key) { + case 'action': + obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) + break + case 'contents': + switch (obj.action) { + case undefined: { + this.throwSyntax( + `'action' must be defined before 'contents' in hoverEvent` + ) + } + + case 'show_text': { + obj[key] = this.parseTextElement() + break + } + + case 'show_item': { + if (this.s.item === '{') { + obj[key] = this.parseLegacyHoverEventObjectShowItemContents() + break + } + obj[key] = this.parseString() + break + } + + case 'show_entity': { + obj[key] = this.parseLegacyHoverEventObjectShowEntityContents() + break + } + } + break + } + }, + }) + } + + private parseModernHoverEventObject() { + return this.parseObject>({ + objectName: 'hover_event', + keys: new Set(['action', 'value', 'id', 'count', 'components', 'name', 'uuid']), + required: new Set(['action']), + parseValue: (key, obj) => { + switch (key) { + case 'action': + obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) + break + + case 'uuid': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // UUID should fallthrough to string parsing if not an int array + case 'id': + case 'name': + obj[key] = this.parseString() + break + + case 'value': + obj[key] = this.parseTextElement() + break + + case 'count': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.BYTE)) + break + + case 'components': + obj[key] = this.parseUnknownValue() + break + } + }, + validateResult: obj => { + if (obj.action === undefined) { + return `Hover event must include 'action' field` + } + + switch (obj.action) { + case 'show_text': + if (obj.value === undefined) { + return `Hover event 'show_text' missing required field 'value'` + } + break + + case 'show_item': + if (obj.id === undefined) { + return `Hover event 'show_item' missing required field 'id'` + } + break + + case 'show_entity': + if (obj.id === undefined) { + return `Hover event 'show_entity' missing required field 'id'` + } + if (obj.uuid === undefined) { + return `Hover event 'show_entity' missing required field 'uuid'` + } + break + } + + switch (true) { + case obj.value !== undefined && obj.action !== 'show_text': + return `'value' is only valid when hover_event action is 'show_text'` + + case obj.count !== undefined && obj.action !== 'show_item': + return `'count' is only valid when hover_event action is 'show_item'` + + case obj.id !== undefined && + obj.action !== 'show_item' && + obj.action !== 'show_entity': + return `'id' is only valid when hover_event action is 'show_item' or 'show_entity'` + + case obj.components !== undefined && obj.action !== 'show_item': + return `'components' is only valid when hover_event action is 'show_item'` + + case obj.name !== undefined && obj.action !== 'show_entity': + return `'name' is only valid when hover_event action is 'show_entity'` + + case obj.uuid !== undefined && obj.action !== 'show_entity': + return `'uuid' is only valid when hover_event action is 'show_entity'` + } + }, + }) as ModernHoverEvent + } + + private transformLegacyHoverEventObjectToModern(event: LegacyHoverEvent): ModernHoverEvent { + switch (event.action) { + case 'show_text': + return { + action: 'show_text', + value: event.contents, + } + + case 'show_item': + if (typeof event.contents === 'string') { + return { + action: 'show_item', + id: event.contents, + } + } + if (event.contents.tag !== undefined) { + this.throwSyntax( + `Cannot transform 'hoverEvent' with 'tag' into modern 'hover_event'.` + + ` Please use 'hover_event' for Minecraft versions 1.21.5 and above` + ) + } + return { + action: 'show_item', + id: event.contents.id, + count: event.contents.count, + } + + case 'show_entity': + return { + action: 'show_entity', + id: event.contents.type, + uuid: event.contents.id, + name: event.contents.name, + } + } + } + + private transformModernHoverEventObjectToLegacy(event: ModernHoverEvent): LegacyHoverEvent { + switch (event.action) { + case 'show_text': + return { + action: 'show_text', + contents: event.value, + } + + case 'show_item': + if (event.components !== undefined) { + this.throwSyntax( + `Cannot transform 'hover_event' with 'components' into legacy 'hoverEvent'.` + + ` Please use 'hoverEvent' for Minecraft versions below 1.21.5` + ) + } + return { + action: 'show_item', + contents: { + id: event.id, + count: event.count, + }, + } + + case 'show_entity': + if (Array.isArray(event.uuid)) { + this.throwSyntax( + `Cannot transform 'hover_event' with 'uuid' as int-array into legacy 'hoverEvent'.` + + ` Please either use a string UUID, or use 'hoverEvent' for Minecraft versions below 1.21.5` + ) + } + return { + action: 'show_entity', + contents: { + type: event.id, + id: event.uuid, + name: event.name, + }, + } + } + } + + private parseArray({ + arrayName, + parsePrefix, + parseItem, + expectedLength, + }: { + arrayName: string + /** + * Optional function to parse the array prefix (e.g., type identifier). + */ + parsePrefix?: () => void + parseItem: () => T[number] + expectedLength?: number + }): T { + this.currentNestingDepth++ + if (this.currentNestingDepth > JsonTextParser.maxNestingDepth) { + this.throwSyntax('Nesting depth limit exceeded', this.s) + } + + this.expect(this.s.item, '[', 'to begin ' + arrayName, true) + this.consumeWhitespace() + + if (parsePrefix) { + parsePrefix() + this.consumeWhitespace() + } + + const array = [] as unknown as T + while (this.s.item !== ']') { + this.consumeWhitespace() + const itemPosition = this.recordPosition() + const value = parseItem() + if (itemPosition.equals(this.s)) { + throw new JsonTextParserError( + `parseItem function for ${arrayName} did not consume any input` + ) + } + array.push(value) + if (array.length > JsonTextParser.maxArrayLength) { + this.throwSyntax('Array length limit exceeded', this.s) + } + this.expectSyntaxBoundaryAfter('array item') + this.consumeWhitespace() + + if (expectedLength !== undefined && array.length > expectedLength) { + this.throwSyntax( + `Too many items in ` + arrayName + ` (expected ${expectedLength})`, + itemPosition + ) + } + + if (this.s.item === ',') { + this.s.consume() + this.consumeWhitespace() + if (this.s.item === ',') { + this.throwSyntax(`Extra comma`, this.s) + } + if (this.s.item === ']' && !(this.enabledFeatures & FEATURES.TRAILING_COMMAS)) { + this.throwSyntax(`Trailing comma in ` + arrayName, this.s) + } + } else if (this.s.item === ']') { + break + } else if (this.s.item === undefined) { + this.throwSyntax('Unexpected EOF in ' + arrayName, this.s) + } else if (!(this.enabledFeatures & FEATURES.OPTIONAL_COMMAS)) { + this.throwSyntax(`Expected ',' or ']' after ${arrayName} item`, this.s) + } + } + + if (expectedLength !== undefined && array.length < expectedLength) { + this.throwSyntax( + `Not enough items in ` + + arrayName + + ` (expected ${expectedLength}, got ${array.length})` + ) + } + + this.s.consume() // ] + return array + } + + private parseTextElementArray(): TextElement[] { + return this.parseArray({ + arrayName: 'TextElementArray', + parseItem: this.parseTextElement.bind(this), + }) + } + + private parseIntArray(requireTypeIdentifier?: boolean, length?: number): T { + return this.parseArray({ + arrayName: 'int-array', + parsePrefix: () => { + if (this.s.item === 'I') { + this.s.consume() + this.consumeWhitespace() + this.expect(this.s.item, ';', 'to follow array type identifier', true) + } else if (requireTypeIdentifier) { + this.throwSyntax(`Expected explicit int-array`, this.s) + } + }, + parseItem: () => parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)), + expectedLength: length, + }) + } + + private parseFloatArray(length?: number): T { + return this.parseArray({ + arrayName: 'float-array', + parseItem: () => parseFloat(this.parseNumber(NUMBER_TYPES.FLOAT)), + expectedLength: length!, + }) + } + + private collectHexDigits(count: number): string { + let hex = '' + for (let i = 0; i < count; i++) { + if (this.s.item && CHARS.HEXADECIMAL.includes(this.s.item)) { + hex += this.s.item + this.s.consume() + } else { + this.throwSyntax( + `Unexpected '${this.s.item!}' in ${count}-digit hex escape sequence` + ) + } + } + return hex + } + + private parseHashedHexColor(): string { + this.expect(this.s.item, '#', `to begin hex color`, true) + + const digits = this.parseDigits('hex', CHARS.HEXADECIMAL) + + return this.normalizeHexColor('#' + digits) + } + + // Validates and returns a string representation of the escape sequence. + // The renderer, such as Minecraft, will handle the actual escape sequence resolution. + private parseNamedUnicodeEscapeSequence(): string { + this.expect(this.s.look(0, 2), 'N{', `to begin named unicode escape sequence`, true) + + let name = '' + while ( + this.s.item && + this.s.item !== '}' && + !CHARS.VERTICAL_WHITESPACE.includes(this.s.item) + ) { + name += this.s.item + this.s.consume() + } + name = name.trim().toUpperCase() + + if (name.length === 0) { + this.throwSyntax(`Expected name in named unicode escape sequence`, this.s) + } + + this.expect(this.s.item, '}', `to end named unicode escape sequence`, true) + + return `\\N{${name}}` + } + + // Validates and returns a string representation of the escape sequence. + // The renderer, such as Minecraft, will handle the actual escape sequence resolution. + private parseUnicodeEscapeSequence(): string { + this.expect(this.s.item, ['x', 'u', 'U'], `to begin escape sequence`) + const char = this.s.item! + const expectedHexLength = this.s.item === 'x' ? 2 : this.s.item === 'u' ? 4 : 8 + this.s.consume() // u, x, or U + const hex = this.collectHexDigits(expectedHexLength) + return `\\${char}${hex}` + } + + private parseEscapeSequence(): string { + this.expect(this.s.item, '\\', `to begin escape sequence`, true) + + const item = this.s.item + if ( + item === '\\' || + item === "'" || + item === '"' || + item === 'n' || + item === 'b' || + item === 'r' || + item === 't' || + item === 'f' + ) { + this.s.consume() + return '\\' + item + } else if (item === 's') { + if (!(this.enabledFeatures & FEATURES.SPACE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support space escape sequences ('\\s')` + ) + } + + this.s.consume() + return '\\s' + } else if (item === 'u') { + return this.parseUnicodeEscapeSequence() + } else if (item === 'x') { + if (!(this.enabledFeatures & FEATURES.HEX_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support hex unicode escape sequences ('\\x00')` + ) + } + + return this.parseUnicodeEscapeSequence() + } else if (item === 'U') { + if (!(this.enabledFeatures & FEATURES.EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support 8-digit unicode escape sequences ('\\U00000000')` + ) + } + + return this.parseUnicodeEscapeSequence() + } else if (item === 'N') { + if (!(this.enabledFeatures & FEATURES.NAMED_UNICODE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support named unicode escape sequences ('\\${item}{Name}')` + ) + } + + return this.parseNamedUnicodeEscapeSequence() + } else { + this.throwSyntax(`Unknown escape sequence '\\${item!}'`, this.s) + } + } + + private parseQuotedString(): string { + const quote = this.s.item! + this.expect(this.s.item, CHARS.QUOTES, 'to begin string', true) + + if (this.s.item === "'" && !(this.enabledFeatures & FEATURES.SINGLE_QUOTES)) { + this.throwSyntax( + `Single quotes are not supported in Minecraft ${this.minecraftVersion}` + ) + } + + let str = '' + while (this.s.item) { + if (this.s.item === '\\') { + str += this.parseEscapeSequence() + continue + } else if (this.s.item === quote) { + break + } else if (CHARS.VERTICAL_WHITESPACE.includes(this.s.item)) { + this.throwSyntax(`Expected ${quote} to close string`, this.s) + } + str += this.s.item + this.s.consume() + } + if (!this.s.item) { + this.throwSyntax('Unexpected EOF in string', this.s) + } + this.expect(this.s.item, quote, `to close string`, true) + return str + } + + private parseLiteral(): string { + if (!(this.enabledFeatures & FEATURES.LITERAL_STRINGS)) { + this.throwSyntax( + `Literal strings are not supported in Minecraft ${this.minecraftVersion}` + ) + } + if (!this.s.item || !CHARS.LITERAL_START.includes(this.s.item)) { + this.throwSyntax( + `Expected [a-zA-Z0-9_] to start literal string. Found '${this.s.item!}' instead` + ) + } + const str = this.s.collectWhile(s => !!s.item && CHARS.LITERAL.includes(s.item)) + if (str.length === 0) { + throw new JsonTextParserError('Literal string parsing failed unexpectedly') + } + return str + } + + private parseString(): string + private parseString(validStringOptions: T[]): T + private parseString(validStringOptions?: T[]): T { + let str: T + const startPosition = this.recordPosition() + + if (this.s.item && CHARS.QUOTES.includes(this.s.item)) { + str = this.parseQuotedString() as T + } else { + str = this.parseLiteral() as T + } + + if (validStringOptions && !validStringOptions.includes(str)) { + this.throwSyntax(`Expected one of ${validStringOptions.join(', ')}`, startPosition) + } + return str + } + + private parseBoolean(): boolean { + if (this.s.look(0, 4).toLowerCase() === 'true') { + this.s.consumeN(4) + return true + } else if (this.s.look(0, 5).toLowerCase() === 'false') { + this.s.consumeN(5) + return false + } + this.throwSyntax(`Expected boolean`, this.s) + } + + private parseDigits( + name: string, + digitChars = CHARS.NUMBER, + minLength = 0, + maxLength = Infinity + ): string { + let digits = '' + while (this.s.item && digitChars.includes(this.s.item)) { + digits += this.s.item + if (digits.length > maxLength) { + this.throwSyntax(`Too many ${name} digits (max ${maxLength})`, this.s) + } + this.s.consume() + + const beforeWhitespace = this.recordPosition() + const whitespace = CHARS.WHITESPACE.includes(this.s.item) + this.consumeWhitespace() + + if (this.s.item === '_') { + if (whitespace) { + this.throwSyntax(`Underscore must be between ${name} digits`, this.s) + } + this.s.consumeWhile(s => s.item === '_') + if (!(this.s.item && digitChars.includes(this.s.item))) { + this.throwSyntax(`Underscore must be between ${name} digits`, this.s) + } + } else if (whitespace) { + // REVIEW - We should avoid backing up the stream like this. + this.s.index = beforeWhitespace.index - 1 + this.s.consume() + break + } + } + if (digits.length < minLength) { + this.throwSyntax(`Not enough ${name} digits (min ${minLength})`, this.s) + } + return digits + } + + private parseNumber(type = NUMBER_TYPES.ANY): string { + let numberString = '' + let hasDecimal = false + let numberChars = CHARS.NUMBER + + const prefix = this.s.look(0, 2).toLowerCase() + if (prefix === '0x') { + this.s.consumeN(2) + numberString += '0x' + numberChars = CHARS.HEXADECIMAL + type = NUMBER_TYPES.HEXADECIMAL + } else if (prefix === '0b') { + this.s.consumeN(2) + numberString += '0b' + numberChars = CHARS.BINARY + type = NUMBER_TYPES.BINARY + } else if (this.s.item === '-') { + numberString += '-' + this.s.consume() + this.consumeWhitespace() + } + + if (type === NUMBER_TYPES.HEXADECIMAL && prefix !== '0x') { + this.throwSyntax(`Hexadecimal numbers must begin with '0x'`, this.s) + } else if (type === NUMBER_TYPES.BINARY && prefix !== '0b') { + this.throwSyntax(`Binary numbers must begin with '0b'`, this.s) + } + + const expectFloat = type === NUMBER_TYPES.FLOAT || type === NUMBER_TYPES.DOUBLE + + if (this.s.item === '.') { + if (!expectFloat) { + this.throwSyntax(`Decimal points are not permitted in ${type}s`) + } + numberString += '.' + hasDecimal = true + this.s.consume() + this.consumeWhitespace() + } + + this.expect(this.s.item, numberChars, 'to begin ' + type) + + while ( + this.s.item && + (numberChars.includes(this.s.item) || this.s.item === '.' || this.s.item === '_') + ) { + numberString += this.parseDigits(type, numberChars) + this.consumeWhitespace() + + if (this.s.item === 'e' || this.s.item === 'E') { + if (!expectFloat) { + this.throwSyntax(`E notation is not allowed in ${type}s`, this.s) + } + + this.s.consume() // e or E + this.consumeWhitespace() + + const floatValue = parseFloat(numberString) + if (isNaN(floatValue)) { + this.throwSyntax(`Invalid ${type} before exponent`, this.s) + } + + const exponent = parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)) + if (isNaN(exponent)) { + this.throwSyntax(`Invalid exponent`, this.s) + } + + return floatValue.toString() + 'e' + exponent.toString() + } else if ((this.s.item as string) === '.') { + if (hasDecimal) { + this.throwSyntax('Second decimal point in ' + type, this.s) + } + hasDecimal = true + numberString += '.' + this.s.consume() + this.consumeWhitespace() + } + } + + this.expectSyntaxBoundaryAfter(type) + + let value: number + switch (type) { + case NUMBER_TYPES.BYTE: + case NUMBER_TYPES.SHORT: + case NUMBER_TYPES.INTEGER: + case NUMBER_TYPES.LONG: + value = parseInt(numberString) + break + + case NUMBER_TYPES.HEXADECIMAL: + value = parseInt(numberString.substring(2), 16) + break + + case NUMBER_TYPES.BINARY: + value = parseInt(numberString.substring(2), 2) + break + + case NUMBER_TYPES.FLOAT: + case NUMBER_TYPES.DOUBLE: + case NUMBER_TYPES.ANY: + value = parseFloat(numberString) + break + } + + if (isNaN(value)) { + this.throwSyntax(`Invalid ` + type, this.s) + } + + return numberString + } + + private expect( + thing: string | undefined, + toBe: string | string[], + message: string, + consume = false + ): void { + // prettier-ignore + if ( + Array.isArray(toBe) + ? !thing || !toBe.includes(thing) + : thing !== toBe + ) { + if (Array.isArray(toBe)) { + this.throwSyntax( + `Expected one of '${toBe.join("', '")}' ${message}. Found '${thing}' instead`, + ) + } + this.throwSyntax( + `Expected '${toBe}' ${message}. Found '${thing}' instead`, + ) + } + if (consume) { + if (Array.isArray(toBe)) { + this.s.consume() + } else { + this.s.consumeN(toBe.length) + } + } + } + + private expectSyntaxBoundaryAfter(message: string) { + if (this.s.item && !CHARS.SYNTAX_BOUNDARY.includes(this.s.item)) { + const previous = this.s.string.at(this.s.index - 1) + if (previous && CHARS.SYNTAX_BOUNDARY.includes(previous)) return + this.throwSyntax(`Unexpected '${this.s.item}' after ${message}`, this.s) + } + } +} + +// type KeyConstraint = +// | keyof T +// | { +// /** The key that is expected to be present in the object. */ +// key: keyof T +// /** +// * If true, this field is always required. +// */ +// required?: +// | boolean +// | { +// /** Require this field if the {@link dependsOn} field is present in the object. */ +// dependsOn: (keyof T)[] +// } + +// /** Keys that are expected to be present if this key is present. */ +// expects?: (keyof T)[] +// /** At least one of these fields must be present if this key is present. */ +// expectsOneOf?: (keyof T)[] +// /** Keys that are mutually exclusive with this key. */ +// mutuallyExclusiveKeys?: (keyof T)[] +// } + +// interface TestParseObjectOptions { +// name: string +// /** Fields that are allowed in the object. */ +// keys?: KeyConstraint[] +// /** At least one of these fields must be present in the object. */ +// requireOneOf?: (keyof T)[] +// /** +// * Called to parse a field's value in the object. +// * +// * Will never be called for fields not in {@link expectedFields}. +// */ +// parseValue: (ctx: { key: keyof T; obj: Partial; existingKeys: Set }) => void +// /** +// * Called with the completed object for extra validation. +// * +// * Returns a string to indicate an error, or undefined if the object is valid. +// */ +// validateObject?: (obj: Partial) => string | void +// } + +// function test(options: TestParseObjectOptions) {} +// test>({ +// name: 'test', +// keys: [ +// 'type', +// 'text', +// 'font', +// 'color', +// 'bold', +// 'italic', +// 'underlined', +// 'obfuscated', +// 'strikethrough', +// 'shadow_color', +// 'insertion', +// { +// key: 'extra', +// expectsOneOf: Object.values(CONTENT_KEYS), +// }, +// { +// key: 'clickEvent', +// mutuallyExclusiveKeys: ['click_event'], +// }, +// { +// key: 'hoverEvent', +// mutuallyExclusiveKeys: ['hover_event'], +// }, +// { +// key: 'click_event', +// mutuallyExclusiveKeys: ['clickEvent'], +// }, +// { +// key: 'hover_event', +// mutuallyExclusiveKeys: ['hoverEvent'], +// }, +// { +// key: 'translate', +// required: { dependsOn: ['with'] }, +// expects: ['with'], +// }, +// { key: 'with', expects: ['translate'] }, +// { key: 'fallback', expects: ['translate'] }, +// 'score', +// { +// key: 'selector', +// required: { dependsOn: ['separator'] }, +// }, +// { key: 'separator', expects: ['selector'] }, +// 'keybind', +// { +// key: 'nbt', +// required: { dependsOn: ['block', 'entity', 'storage'] }, +// expectsOneOf: ['block', 'entity', 'storage'], +// }, +// { key: 'block', expects: ['nbt'] }, +// { key: 'entity', expects: ['nbt'] }, +// { key: 'storage', expects: ['nbt'] }, +// 'sprite', +// { +// key: 'atlas', +// required: { dependsOn: ['sprite'] }, +// expects: ['sprite'], +// }, +// ], +// parseValue: ({ key, obj }) => {}, +// }) diff --git a/src/systems/jsonText/stringifier.ts b/src/systems/jsonText/stringifier.ts new file mode 100644 index 00000000..25a5493d --- /dev/null +++ b/src/systems/jsonText/stringifier.ts @@ -0,0 +1,337 @@ +import { + type LegacyClickEvent, + type LegacyHoverEvent, + type ModernClickEvent, + type ModernHoverEvent, + type TextElement, + type TextObject, +} from '.' +import { compareVersions } from './parser' + +enum FEATURES { + REQUIRE_DOUBLE_QUOTES = 1 << 0, + RESOLVE_SPACE_ESCAPE_SEQUENCES = 1 << 1, +} + +export class JsonTextStringifier { + enabledFeatures = FEATURES.REQUIRE_DOUBLE_QUOTES | FEATURES.RESOLVE_SPACE_ESCAPE_SEQUENCES + + constructor(private minecraftVersion: string) { + if (compareVersions(this.minecraftVersion, '1.21.5') >= 0) { + this.enabledFeatures &= ~FEATURES.REQUIRE_DOUBLE_QUOTES + this.enabledFeatures &= ~FEATURES.RESOLVE_SPACE_ESCAPE_SEQUENCES + } + } + + stringify(element: TextElement): string { + return this.stringifyTextElement(element) + } + + /** + * Escapes and stringifies a string for SNBT representation. + * + * Reduces escaping by choosing the optimal quote type. + * + * Prefers single quotes. + */ + private stringifyString(str: string): string { + str = str.replaceAll('\n', '\\n') + if (this.enabledFeatures & FEATURES.RESOLVE_SPACE_ESCAPE_SEQUENCES) { + str = str.replaceAll('\\s', ' ') + } + + // Remove escaped quotes for accurate detection + const unescaped = str.replace(/\\'/g, "'").replace(/\\"/g, '"') + const hasSingle = unescaped.includes("'") + const hasDouble = unescaped.includes('"') + + if (this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES) { + return `"${unescaped.replace(/"/g, '\\"')}"` + } else if (hasSingle && hasDouble) { + // Both quotes present, fallback to single quotes and escape single quotes + return `'${unescaped.replace(/'/g, "\\'")}'` + } else if (hasSingle) { + // Only single quotes present, use double quotes + return `"${unescaped.replace(/"/g, '\\"')}"` + } else { + // Use single quotes + return `'${unescaped.replace(/'/g, "\\'")}'` + } + } + + private stringifyTextElementArray(arr: TextElement[]): string { + return `[${arr.map(e => this.stringifyTextElement(e)).join(',')}]` + } + + private stringifyScoreObject(score: NonNullable): string { + if (this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES) { + return ( + `{"name":${this.stringifyString(score.name)}` + + `,"objective":${this.stringifyString(score.objective)}}` + ) + } + return ( + `{name:${this.stringifyString(score.name)}` + + `,objective:${this.stringifyString(score.objective)}}` + ) + } + + private stringifyPlayerObject(player: NonNullable): string { + const q = this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES ? '"' : '' + const result: string[] = [] + if (player.name !== undefined) { + result.push(`${q}name${q}:${this.stringifyString(player.name)}`) + } + if (player.id !== undefined) { + result.push(`${q}id${q}:${JSON.stringify(player.id)}`) + } + if (player.texture !== undefined) { + result.push(`${q}texture${q}:${player.texture}`) + } + if (player.cape !== undefined) { + result.push(`${q}cape${q}:${player.cape}`) + } + if (player.model !== undefined) { + result.push(`${q}model${q}:${this.stringifyString(player.model)}`) + } + if (player.hat !== undefined) { + result.push(`${q}hat${q}:${player.hat}`) + } + if (player.properties !== undefined) { + for (const prop of player.properties) { + result.push( + `{name:${this.stringifyString(prop.name)}` + + `,value:${this.stringifyString(prop.value)}` + + (prop.signature === undefined + ? '' + : `,signature:${this.stringifyString(prop.signature)}`) + + '}' + ) + } + } + return '{' + result.join(',') + '}' + } + + private stringifyLegacyHoverEvent(event: LegacyHoverEvent): string { + switch (event.action) { + case 'show_text': { + return `{"action":"show_text","contents":${this.stringifyTextElement( + event.contents + )}}` + } + + case 'show_item': { + let result = `{"action":"show_item","contents":` + + if (typeof event.contents === 'string') { + result += this.stringifyString(event.contents) + } else { + result += '{"id":' + this.stringifyString(event.contents.id) + + if (event.contents.count !== undefined) { + result += `,"count":${event.contents.count}` + } + + if (event.contents.tag !== undefined) { + result += `,"tag":${this.stringifyTextElement(event.contents.tag)}` + } + } + + return result + `}}` + } + + case 'show_entity': { + let result = `{"action":"show_entity","contents":{"type":${this.stringifyString( + event.contents.type + )}` + + if (Array.isArray(event.contents.id)) { + result += `,"id":[${event.contents.id.join(',')}]` + } else if (typeof event.contents.id === 'string') { + result += `,"id":${this.stringifyString(event.contents.id)}` + } + + if (event.contents.name !== undefined) { + result += `,"name":${this.stringifyTextElement(event.contents.name)}` + } + + return result + `}}` + } + } + } + + private stringifyModernHoverEvent(event: ModernHoverEvent): string { + switch (event.action) { + case 'show_text': { + return `{action:show_text,value:${this.stringifyTextElement(event.value)}}` + } + + case 'show_item': { + let result = `{action:show_item,id:${this.stringifyString(event.id)}` + + if (event.count !== undefined) { + result += `,count:${event.count}` + } + + // `components` is not supported by the parser + + return result + `}` + } + + case 'show_entity': { + let result = `{action:show_entity,id:${this.stringifyString(event.id)}` + + if (event.name !== undefined) { + result += `,name:${this.stringifyTextElement(event.name)}` + } + + if (Array.isArray(event.uuid)) { + result += `,uuid:[I;${event.uuid.join(',')}]` + } else if (typeof event.uuid === 'string') { + result += `,uuid:${this.stringifyString(event.uuid)}` + } + + return result + `}` + } + } + } + + private stringifyLegacyClickEvent(event: LegacyClickEvent): string { + return `{"action":"${event.action}","value":${this.stringifyString(event.value)}}` + } + + private stringifyModernClickEvent(event: ModernClickEvent): string { + switch (event.action) { + case 'open_url': + return `{action:open_url,url:${this.stringifyString(event.url)}}` + + case 'open_file': + return `{action:open_file,path:${this.stringifyString(event.path)}}` + + case 'run_command': + return `{action:run_command,command:${this.stringifyString(event.command)}}` + + case 'suggest_command': + return `{action:suggest_command,command:${this.stringifyString(event.command)}}` + + case 'copy_to_clipboard': + return `{action:copy_to_clipboard,value:${this.stringifyString(event.value)}}` + + case 'change_page': + return `{action:change_page,page:${event.page}}` + + case 'show_dialog': + throw new Error('show_dialog click events are not supported in text displays') + + case 'custom': + let result = `{action:custom,id:${this.stringifyString(event.id)}` + + if (event.payload !== undefined) { + result += `,payload:${this.stringifyTextElement(event.payload)}` + } + + return result + `}` + } + } + + private stringifyTextObject(obj: TextObject): string { + const entries: string[] = [] + + for (const key of Object.keys(obj) as Array) { + if (obj[key] === undefined) continue + + // Quote character to use for keys + const q = this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES ? '"' : '' + const quotedKey = q + key + q + + switch (key) { + case 'type': + case 'text': + case 'translate': + case 'fallback': + case 'keybind': + case 'nbt': + case 'source': + case 'block': + case 'entity': + case 'storage': + case 'selector': + case 'font': + case 'insertion': + case 'object': + case 'sprite': + case 'atlas': + case 'color': + // Value is a string + entries.push(`${quotedKey}:${this.stringifyString(obj[key])}`) + break + + case 'shadow_color': + if (Array.isArray(obj[key])) { + entries.push(`${quotedKey}:${JSON.stringify(obj[key])}`) + break + } + // color and shadow_color fall through to number | bool case + case 'bold': + case 'italic': + case 'obfuscated': + case 'strikethrough': + case 'underlined': + case 'interpret': + // Value is a number or boolean + entries.push(`${quotedKey}:${obj[key]}`) + break + + case 'with': + case 'extra': + case 'separator': + // Value is an array of components + entries.push(`${quotedKey}:${this.stringifyTextElement(obj[key]!)}`) + break + + case 'score': + entries.push(`${quotedKey}:${this.stringifyScoreObject(obj[key])}`) + break + + case 'player': + entries.push(`${quotedKey}:${this.stringifyPlayerObject(obj[key])}`) + break + + case 'clickEvent': + entries.push(`${quotedKey}:${this.stringifyLegacyClickEvent(obj[key])}`) + break + + case 'click_event': + entries.push(`${quotedKey}:${this.stringifyModernClickEvent(obj[key])}`) + break + + case 'hoverEvent': + entries.push(`${quotedKey}:${this.stringifyLegacyHoverEvent(obj[key])}`) + break + + case 'hover_event': + entries.push(`${quotedKey}:${this.stringifyModernHoverEvent(obj[key])}`) + break + + default: + console.warn(`Unknown key in TextObject: '${key}'`) + break + } + } + + return `{${entries.join(',')}}` + } + + private stringifyTextElement(element: TextElement): string { + if (typeof element === 'string') { + return this.stringifyString(element) + } else if (Array.isArray(element)) { + return this.stringifyTextElementArray(element) + } else if (typeof element === 'object' && element !== null) { + return this.stringifyTextObject(element) + } else { + console.error(element) + throw new Error('Invalid TextElement') + } + } +} diff --git a/src/systems/jsonText/unicodeString.ts b/src/systems/jsonText/unicodeString.ts new file mode 100644 index 00000000..b9e25a56 --- /dev/null +++ b/src/systems/jsonText/unicodeString.ts @@ -0,0 +1,211 @@ +import { StringStream } from 'generic-stream' +import { TextDisplay } from 'src/outliner/textDisplay' +import EVENTS from 'src/util/events' + +const ESCAPE_SEQUENCES = { + '\\': '\\', + "'": "'", + '"': '"', + n: '\n', + s: ' ', + t: '\t', + b: '\b', + f: '\f', + r: '\r', +} + +const UNICODE_CHAR_MAP = new Map() + +async function loadUnicodeCharMappings() { + const response = await fetch('https://unicode.org/Public/UNIDATA/UnicodeData.txt') + const text = await response.text() + + UNICODE_CHAR_MAP.clear() + const lines = text.split('\n') + for (const line of lines) { + const parts = line.split(';') + const codePointHex = parts[0] + const name = parts[1] + const codePoint = parseInt(codePointHex, 16) + if (isNaN(codePoint) || name === '' || name === '') continue + UNICODE_CHAR_MAP.set(name, String.fromCodePoint(codePoint)) + } + + console.log(`Loaded ${UNICODE_CHAR_MAP.size} Unicode character mappings`, UNICODE_CHAR_MAP) + + for (const textDisplay of TextDisplay.all) { + void textDisplay.updateTextMesh() + } +} + +EVENTS.NETWORK_CONNECTED.subscribe(() => { + void loadUnicodeCharMappings() +}) + +function collectHexDigits(s: StringStream, count: number): string { + let hex = '' + for (let i = 0; i < count; i++) { + if (s.item && /[0-9a-fA-F]/.test(s.item)) { + hex += s.item + s.consume() + } else { + throw new Error( + `Expected ${count} hex digits in unicode escape sequence, got only ${i}` + ) + } + } + return hex +} + +function resolveNamedUnicodeEscapeSequence(s: StringStream): string { + if (s.next !== '{') { + console.warn(`Expected 'N{' to begin named unicode escape sequence`) + return '\\N' + } + s.consume() // N + s.consume() // { + + let name = '' + while (s.item && s.item !== '}') { + name += s.item + s.consume() + } + name = name.trim().toUpperCase() + + if (name.length === 0) { + console.warn(`Expected name in named unicode escape sequence`) + return '\\N{}' + } + + if (s.item !== '}') { + console.warn(`Expected '}' to end named unicode escape sequence`) + return `\\N{${name}` + } + s.consume() // } + + if (UNICODE_CHAR_MAP.has(name)) { + return UNICODE_CHAR_MAP.get(name)! + } + + return `\\N{${name}}` +} + +function resolveUnicodeEscapeSequence(s: StringStream): string { + if (!'xuU'.includes(s.item!)) { + console.warn(`Expected 'x', 'u', or 'U' to begin unicode escape sequence`) + return '\\' + s.item + } + const char = s.item! + const expectedHexLength = s.item === 'x' ? 2 : s.item === 'u' ? 4 : 8 + s.consume() // u + const hex = collectHexDigits(s, expectedHexLength) + + const codePoint = parseInt(hex, 16) + if (isNaN(codePoint) || codePoint < 0 || codePoint > 0x10ffff) { + console.warn(`Invalid unicode code point: ${hex}`) + return `\\${char}${hex}` + } + return String.fromCodePoint(codePoint) +} + +function resolveEscapeSequences(str: string): string { + let resolved = '' + const s = new StringStream(str) + + while (s.item) { + if (s.item === '\\') { + const escaped = s.look(1) + if ( + (escaped === '\\' || + escaped === "'" || + escaped === '"' || + escaped === 'n' || + escaped === 's' || + escaped === 't' || + escaped === 'b' || + escaped === 'f' || + escaped === 'r') && + ESCAPE_SEQUENCES[escaped] + ) { + if (ESCAPE_SEQUENCES[escaped]) { + s.consume() // Consume the backslash + s.consume() // Consume the escaped character + resolved += ESCAPE_SEQUENCES[escaped] + continue + } + } else if (escaped === 'N') { + s.consume() // Consume the backslash + resolved += resolveNamedUnicodeEscapeSequence(s) + continue + } else if (escaped === 'u' || escaped === 'U' || escaped === 'x') { + s.consume() // Consume the backslash + resolved += resolveUnicodeEscapeSequence(s) + continue + } + // If it's not a recognized escape sequence, keep the backslash + } + resolved += s.item + s.consume() + } + + return resolved +} + +export class UnicodeString { + static regex = /[^]/gmu + + private chars: string[] = [] + + str: string + + constructor(str: string) { + this.str = str + + if (this.str === '') return + + this.str = resolveEscapeSequences(str) + + for (const char of this.str.matchAll(UnicodeString.regex)) { + this.chars.push(char[0]) + } + } + + [Symbol.iterator]() { + return this.chars[Symbol.iterator]() + } + + get length() { + return this.chars.length + } + + includes(search: string) { + return this.chars.includes(search) + } + + indexOf(search: string) { + return this.chars.indexOf(search) + } + + slice(start: number, end?: number) { + const chars = this.chars.slice(start, end) + return UnicodeString.fromChars(chars) + } + + at(index: number) { + if (index < 0 || index >= this.chars.length) return undefined + return this.chars[index] + } + + append(char: string) { + this.chars.push(char) + this.str += char + } + + toString() { + return this.str + } + + static fromChars(chars: string[]) { + return new UnicodeString(chars.join('')) + } +} diff --git a/src/systems/jsonText/wrapping.ts b/src/systems/jsonText/wrapping.ts new file mode 100644 index 00000000..b0ca7ceb --- /dev/null +++ b/src/systems/jsonText/wrapping.ts @@ -0,0 +1,291 @@ +import { Stopwatch } from 'src/util/stopwatch' +import { JsonText, type ComponentStyle, type TextElement, type TextObject } from '.' +import { getVanillaFont } from '../minecraft/fontManager' +import { UnicodeString } from './unicodeString' + +// Jumpstarted by @IanSSenne (FetchBot) and refactored by @SnaveSutit to do line wrapping on JSON Text Components. +// THANK U IAN <3 - SnaveSutit + +function getRawText(element: string | TextObject): UnicodeString { + if (typeof element === 'string') { + return new UnicodeString(element) + } + + switch (true) { + case element.text !== undefined: + return new UnicodeString(element.text) + + case element.translate !== undefined: + return new UnicodeString(`{${element.translate}}`) + + case element.selector !== undefined: + return new UnicodeString(`{${element.selector}}`) + + case element.score !== undefined: + return new UnicodeString(`{${element.score.name}:${element.score.objective}}`) + + case element.keybind !== undefined: + return new UnicodeString(`{${element.keybind}}`) + + case element.nbt !== undefined: + switch (true) { + case element.block !== undefined: + return new UnicodeString(`{${element.block}:${element.nbt}}`) + + case element.entity !== undefined: + return new UnicodeString(`{${element.entity}:${element.nbt}}`) + + case element.storage !== undefined: + return new UnicodeString(`{${element.storage}:${element.nbt}}`) + + default: + return new UnicodeString(`{${element.nbt}}`) + } + + case element.sprite !== undefined: + return new UnicodeString(`{sprite:${element.sprite}}`) + + case element.player !== undefined: + return new UnicodeString(`{player:${element.player}}`) + + default: + console.warn('Unknown JSON Text element:', element) + return new UnicodeString('{Unknown Element}') + } +} + +export interface StyleSpan { + style: ComponentStyle + start: number + end: number +} + +export interface Word { + styles: StyleSpan[] + text: UnicodeString + /** + * The width of the word in pixels. + */ + width: number + forceWrap?: boolean +} + +interface Line { + words: Word[] + width: number +} + +/** + * Gets the words from a JSON Text Element, keeping track of the styles applied to each word. + * + * WARNING: Word width is ***not calculated*** by this function. + */ +export function parseWords(inputElement: TextElement) { + const stopwatch = new Stopwatch('Parse Words').start() + const flattened = new JsonText(inputElement).flatten(true) + if (!flattened.length) return [] + const words: Word[] = [] + + let word: Word | undefined + let element = flattened.shift() + if (element === undefined) return words + + let componentText = getRawText(element) + let span: StyleSpan = { + style: JsonText.getComponentStyle(element), + start: 0, + end: 0, + } + + while (element !== undefined) { + for (const char of componentText) { + if (char === ' ') { + // A group of multiple spaces is treated as a word. + if (word && !(word.text.at(-1) === ' ')) { + span.end++ + if (Object.keys(span.style).length) { + word.styles.push({ ...span }) + span.start = 0 + span.end = 0 + } + words.push(word) + word = undefined + } + } else if (char === '\n') { + if (word) { + if (Object.keys(span.style).length) { + if (span.start < span.end) { + word.styles.push({ ...span }) + } + span.start = 0 + span.end = 0 + } + words.push(word) + } + words.push({ + styles: [], + text: new UnicodeString(''), + width: 0, + forceWrap: true, + }) + word = undefined + continue + } else if (char !== ' ' && word?.text.at(-1) === ' ') { + span.end++ + if (Object.keys(span.style).length) { + word.styles.push({ ...span }) + span.start = 0 + span.end = 0 + } + words.push(word) + word = undefined + } + + word ??= { styles: [], text: new UnicodeString(''), width: 0 } + word.text.append(char) + span.end++ + } + + element = flattened.shift() + + if (element !== undefined) { + componentText = getRawText(element) + if (word) { + word.styles.push(span) + span = { + style: JsonText.getComponentStyle(element), + start: span.end, + end: span.end, + } + } else { + span = { + style: JsonText.getComponentStyle(element), + start: 0, + end: 0, + } + } + } + } + + if (word) { + if (Object.keys(span.style).length) { + word.styles.push(span) + } + words.push(word) + } + + stopwatch.debug() + return words +} + +export async function wrapJsonText(jsonText: JsonText, maxLineWidth = 200) { + const stopwatch = new Stopwatch('Wrap Json Text').start() + + const words = parseWords(jsonText.toJSON()) + const lines: Line[] = [] + // FIXME - This will not work for custom fonts + const font = await getVanillaFont() + + let backgroundWidth = 0 + let currentLine: Line = { words: [], width: 0 } + for (const word of words) { + const wordWidth = font.getWordWidth(word) + const wordStyles = [...word.styles] + // If the word is longer than than the max line width, split it into multiple lines + if (wordWidth - 1 > maxLineWidth) { + if (currentLine.words.length) { + lines.push(currentLine) + backgroundWidth = Math.max(backgroundWidth, currentLine.width) + } + currentLine = { words: [], width: 0 } + + let part = new UnicodeString('') + let partWidth = 0 + let partStartIndex = 0 + let style: StyleSpan | undefined = wordStyles.shift() + if (!style) throw new Error(`No active style found for word '${word.text.toString()}'`) + + for (let i = 0; i < word.text.length; i++) { + const char = word.text.at(i)! + if (wordStyles.length > 1 && i >= style.end) { + style = wordStyles.shift()! + } + + const charWidth = font.getTextWidth(new UnicodeString(char), style) + if (part.length > 0 && partWidth + (charWidth - 1) > maxLineWidth) { + // Find all styles that apply to this part + // FIXME: Attempt to avoid filtering and maping the styles for each character + const partStyles = word.styles + .filter( + span => + span.start < partStartIndex + part.length && + span.end >= partStartIndex + ) + .map(span => ({ + ...span, + start: Math.max(span.start - partStartIndex, 0), + end: Math.min(span.end - partStartIndex, part.length), + })) + lines.push({ + words: [{ text: part, styles: partStyles, width: wordWidth }], + width: partWidth, + }) + backgroundWidth = Math.max(backgroundWidth, partWidth) + partStartIndex += part.length + part = new UnicodeString('') + partWidth = 0 + } + part.append(char) + partWidth += charWidth + } + if (part) { + // Find all styles that apply to this part + // FIXME: Attempt to avoid filtering and maping the styles for each character + const partStyles = word.styles + .filter( + span => + span.start < partStartIndex + part.length && span.end >= partStartIndex + ) + .map(span => ({ + ...span, + start: Math.max(span.start - partStartIndex, 0), + end: Math.min(span.end - partStartIndex, part.length), + })) + backgroundWidth = Math.max(backgroundWidth, partWidth) + currentLine = { + words: [{ text: part, styles: partStyles, width: wordWidth }], + width: partWidth, + } + } + continue + // If the word is a newline character, force a line break + } else if (word.forceWrap) { + if (currentLine.words.length) { + lines.push(currentLine) + backgroundWidth = Math.max(backgroundWidth, currentLine.width) + } + currentLine = { words: [], width: 0 } + // If the current line has words and adding the current word would exceed the max line width, start a new line + } else if (currentLine.words.length && currentLine.width + (wordWidth - 1) > maxLineWidth) { + const lastWord = currentLine.words.at(-1) + // This will only effect space "words" + if (lastWord?.text.at(-1) === ' ') { + currentLine.words.pop() + currentLine.width -= lastWord.width + } + lines.push(currentLine) + backgroundWidth = Math.max(backgroundWidth, currentLine.width) + currentLine = { words: [], width: 0 } + } + word.width = wordWidth + currentLine.words.push(word) + currentLine.width += wordWidth + } + if (currentLine.words.length) { + lines.push(currentLine) + backgroundWidth = Math.max(backgroundWidth, currentLine.width) + } + + stopwatch.debug() + return { lines, backgroundWidth } +} diff --git a/src/systems/minecraft/assetManager.ts b/src/systems/minecraft/assetManager.ts index 3526dc27..4bc07d1b 100644 --- a/src/systems/minecraft/assetManager.ts +++ b/src/systems/minecraft/assetManager.ts @@ -1,16 +1,19 @@ import { PACKAGE } from '../../constants' +import EVENTS from '../../util/events' import { getCurrentVersion, getLatestVersion } from './versionManager' -import { events } from '../../util/events' -import index from '../../assets/vanillaAssetOverrides/index.json' -import { Unzipped } from 'fflate' -import { unzip } from '../util' import download from 'download' +import type { Unzipped } from 'fflate' +import index from '../../assets/vanillaAssetOverrides/index.json' + import { + hideLoadingPopup, + showLoadingPopup, showOfflineError, updateLoadingProgress, updateLoadingProgressLabel, } from '../../interface/popup/animatedJavaLoading' +import { unzip } from '../util' const ASSET_OVERRIDES = index as unknown as Record async function downloadJar(url: string, savePath: string) { @@ -29,7 +32,7 @@ async function downloadJar(url: string, savePath: string) { throw new Error('Failed to download Minecraft client after 3 retries.') } - await fs.promises.writeFile(savePath, data) + await fs.promises.writeFile(savePath, new Uint8Array(data)) } export async function getLatestVersionClientDownloadUrl() { @@ -44,7 +47,7 @@ export async function getLatestVersionClientDownloadUrl() { } catch (error) { console.error('Failed to fetch latest Minecraft version API:', error) } - if (response && response.ok) { + if (response?.ok) { const result = await response.json() if (!result?.downloads?.client) { throw new Error(`Failed to find client download for ${version.id}`) @@ -99,14 +102,16 @@ export async function checkForAssetsUpdate() { await extractAssets() console.log('Minecraft assets are up to date!') localStorage.setItem('assetsLoaded', 'true') - requestAnimationFrame(() => events.MINECRAFT_ASSETS_LOADED.dispatch()) + requestAnimationFrame(() => EVENTS.MINECRAFT_ASSETS_LOADED.publish()) } let loadedAssets: Unzipped | undefined export async function extractAssets() { const cachedJarFilePath = getCachedJarFilePath() - loadedAssets = await unzip(new Uint8Array(await fs.promises.readFile(cachedJarFilePath)), { + const data = await fs.promises.readFile(cachedJarFilePath) + + loadedAssets = await unzip(new Uint8Array(data), { filter: v => v.name.startsWith('assets/'), }) } @@ -116,7 +121,7 @@ export async function assetsLoaded() { if (loadedAssets !== undefined) { resolve() } else { - events.MINECRAFT_ASSETS_LOADED.subscribe(() => resolve(), true) + EVENTS.MINECRAFT_ASSETS_LOADED.subscribe(() => resolve(), true) } }) } @@ -126,30 +131,58 @@ export function hasAsset(path: string) { return !!loadedAssets[path] } -export function getRawAsset(path: string) { +export function getRawAsset(path: string): Buffer { if (!loadedAssets) throw new Error('Assets not loaded') if (ASSET_OVERRIDES[path]) { - if (path.endsWith('.png')) { - return Buffer.from(ASSET_OVERRIDES[path], 'base64') - } - return ASSET_OVERRIDES[path] + return Buffer.from(ASSET_OVERRIDES[path]) } const asset = loadedAssets[path] if (!asset) throw new Error(`Asset not found: ${path}`) - return asset + return Buffer.from(asset) } export function getPngAssetAsDataUrl(path: string) { const asset = getRawAsset(path) if (!asset) throw new Error(`Asset not found: ${path}`) - return `data:image/png;base64,${Buffer.from(asset).toString('base64')}` + return `data:image/png;base64,${asset.toString('base64')}` } -export function getJSONAsset(path: string): any { +export function getJSONAsset(path: string) { const asset = getRawAsset(path) if (!asset) throw new Error(`Asset not found: ${path}`) - const json = JSON.parse(Buffer.from(asset).toString('utf-8')) - return json + const assetString = asset.toString('utf-8') + try { + return JSON.parse(assetString) + } catch (error) { + console.error(`Failed to parse JSON asset from ${path}:`, assetString, error) + throw error + } } + +EVENTS.PLUGIN_LOAD.subscribe(() => { + if (!window.navigator.onLine) { + showOfflineError() + } + EVENTS.NETWORK_CONNECTED.publish() + + showLoadingPopup() + + void Promise.all([ + new Promise(resolve => EVENTS.MINECRAFT_ASSETS_LOADED.subscribe(resolve)), + new Promise(resolve => EVENTS.MINECRAFT_REGISTRY_LOADED.subscribe(resolve)), + new Promise(resolve => EVENTS.MINECRAFT_FONTS_LOADED.subscribe(resolve)), + new Promise(resolve => EVENTS.BLOCKSTATE_REGISTRY_LOADED.subscribe(resolve)), + ]) + .then(() => { + hideLoadingPopup() + }) + .catch(error => { + console.error(error) + Blockbench.showToastNotification({ + text: 'Animated Java failed to load! Please restart Blockbench', + color: 'var(--color-error)', + }) + }) +}) diff --git a/src/systems/minecraft/blockModelManager.ts b/src/systems/minecraft/blockModelManager.ts index 705f1a6d..a01e7f58 100644 --- a/src/systems/minecraft/blockModelManager.ts +++ b/src/systems/minecraft/blockModelManager.ts @@ -1,14 +1,14 @@ import { mergeGeometries } from '../../util/bufferGeometryUtils' import { - IParsedBlock, + type IParsedBlock, getPathFromResourceLocation, parseBlock, resolveBlockstateValueType, } from '../../util/minecraftUtil' import { translate } from '../../util/translation' import { assetsLoaded, getJSONAsset, getPngAssetAsDataUrl, hasAsset } from './assetManager' -import { BlockStateValue } from './blockstateManager' -import { +import type { BlockStateValue } from './blockstateManager' +import type { IBlockModel, IBlockState, IBlockStateMultipartCase, @@ -17,7 +17,7 @@ import { } from './model' import { TEXTURE_FRAG_SHADER, TEXTURE_VERT_SHADER } from './textureShaders' -type BlockModelMesh = { +export interface BlockModelMesh { mesh: THREE.Mesh outline: THREE.LineSegments boundingBox: THREE.BufferGeometry @@ -91,7 +91,7 @@ export async function parseBlockModel( // Interesting that elements aren't merged in vanilla... if (childModel.elements !== undefined) model.elements = childModel.elements if (childModel.display !== undefined) - model.display = Object.assign(model.display || {}, childModel.display) + model.display = Object.assign(model.display ?? {}, childModel.display) if (childModel.ambientocclusion !== undefined) model.ambientocclusion = childModel.ambientocclusion } @@ -108,7 +108,13 @@ async function generateModelMesh( variant: IBlockStateVariant, model: IBlockModel ): Promise { - console.log(`Generating block mesh for '${variant.model}' from `, variant, model) + console.log( + `Generating block mesh for '${variant.model}':`, + '\n - Variant', + variant, + '\n - Model', + model + ) if (!model.elements) { throw new Error(`No elements defined in block model '${variant.model}'`) @@ -180,9 +186,10 @@ async function generateModelMesh( } geometry.translate(-8, -8, -8) - // geometry.rotateY(Math.degToRad(180)) + geometry.rotateY(Math.degToRad(180)) // Blockbench's rotation is mirrored compared to MC if (variant.x) geometry.rotateX(Math.degToRad(variant.x)) if (variant.y) geometry.rotateY(-Math.degToRad(variant.y)) + geometry.rotateY(Math.degToRad(-180)) // Rotate back after mirroring if (variant.isItemModel) { geometry.translate(0, 8, 0) } else { @@ -217,18 +224,18 @@ async function generateModelMesh( const material = new THREE.ShaderMaterial({ uniforms: { map: new THREE.Uniform(texture), - // @ts-expect-error + // @ts-expect-error Uniforms types are wrong SHADE: { type: 'bool', value: settings.shading.value }, LIGHTCOLOR: { - // @ts-expect-error + // @ts-expect-error Uniforms types are wrong type: 'vec3', value: new THREE.Color() .copy(Canvas.global_light_color) .multiplyScalar(settings.brightness.value / 50), }, - // @ts-expect-error + // @ts-expect-error Uniforms types are wrong LIGHTSIDE: { type: 'int', value: Canvas.global_light_side }, - // @ts-expect-error + // @ts-expect-error Uniforms types are wrong EMISSIVE: { type: 'bool', value: false }, }, vertexShader: TEXTURE_VERT_SHADER, @@ -237,7 +244,7 @@ async function generateModelMesh( side: Canvas.getRenderSide(), transparent: true, }) - // @ts-expect-error + // @ts-expect-error Uniforms types are wrong material.map = texture material.name = variant.model materials.push(material) @@ -372,8 +379,6 @@ async function loadTexture(textures: IBlockModel['textures'], key: string): Prom } let texture: THREE.Texture if (hasAsset(texturePath + '.mcmeta')) { - console.log(`Found mcmeta for texture '${texturePath}'`) - const img = new Image() img.src = getPngAssetAsDataUrl(texturePath) const canvas = document.createElement('canvas') @@ -407,6 +412,14 @@ export async function parseBlockState(block: IParsedBlock): Promise @@ -11,8 +11,8 @@ const REGISTRIES_URL = 'https://raw.githubusercontent.com/misode/mcmeta/summary/ export type BlockStateValue = string | number | boolean | Array export class BlockStateRegistryEntry { - public defaultStates: Record = {} - public stateValues: Record = {} + defaultStates: Record = {} + stateValues: Record = {} constructor(blockstate: BlockStateRegistryData) { for (const [k, v] of Object.entries(blockstate[1])) { @@ -49,7 +49,7 @@ async function updateLocalRegistry() { } catch (error) { console.error('Failed to fetch latest BlockState registry:', error) } - if (response && response.ok) { + if (response?.ok) { const newRegistry = (await response.json()) as BlockStateRegistryJSON localStorage.setItem('animated_java:blockStateRegistry', JSON.stringify(newRegistry)) const latestVersion = await getLatestVersion() @@ -88,13 +88,13 @@ export async function checkForRegistryUpdate() { console.log('BlockState Registry is up to date!') updateMemoryRegistry() - requestAnimationFrame(() => events.BLOCKSTATE_REGISTRY_LOADED.dispatch()) + requestAnimationFrame(() => EVENTS.BLOCKSTATE_REGISTRY_LOADED.publish()) } export async function getBlockState(block: string) { if (Object.keys(BLOCKSTATE_REGISTRY).length === 0) { return new Promise(resolve => { - events.BLOCKSTATE_REGISTRY_LOADED.subscribe(() => { + EVENTS.BLOCKSTATE_REGISTRY_LOADED.subscribe(() => { resolve(BLOCKSTATE_REGISTRY[block]) }, true) }) @@ -102,7 +102,7 @@ export async function getBlockState(block: string) { return BLOCKSTATE_REGISTRY[block] } -events.LOAD.subscribe(() => { +EVENTS.PLUGIN_LOAD.subscribe(() => { void checkForRegistryUpdate().catch(err => { console.error(err) }) diff --git a/src/systems/minecraft/fontManager.ts b/src/systems/minecraft/fontManager.ts index 08df3395..d2fc74e4 100644 --- a/src/systems/minecraft/fontManager.ts +++ b/src/systems/minecraft/fontManager.ts @@ -1,92 +1,95 @@ import { createHash } from 'crypto' +import { Stopwatch } from 'src/util/stopwatch' import MissingCharacter from '../../assets/missing_character.png' -import { type Alignment } from '../../outliner/textDisplay' +import { TextDisplay, type Alignment } from '../../outliner/textDisplay' import { mergeGeometries } from '../../util/bufferGeometryUtils' -import { events } from '../../util/events' +import EVENTS from '../../util/events' import { getPathFromResourceLocation } from '../../util/minecraftUtil' -import { UnicodeString } from '../../util/unicodeString' +import { COLOR_VALUES, ComponentStyle, JsonText } from '../jsonText' +import { UnicodeString } from '../jsonText/unicodeString' +import { wrapJsonText, type StyleSpan, type Word } from '../jsonText/wrapping' import * as assets from './assetManager' -import { COLOR_MAP, JsonText } from './jsonText' -import { - computeTextWrapping, - getComponentWords, - IComponentWord, - IStyleSpan, - StyleRecord, -} from './textWrapping' - -interface IFontProviderBitmap { - type: 'bitmap' - file: string - height?: number - // FIXME This isn't actually used for anything yet... - ascent: number - chars: string[] -} -interface IFontProviderReference { - type: 'reference' - id: string - filter?: { - uniform?: boolean +namespace MinecraftJson { + export interface FontProviderBitmap { + type: 'bitmap' + file: string + height?: number + ascent: number + chars: string[] } -} -interface IFontProviderSpace { - type: 'space' - advances: Record -} + export interface FontProviderReference { + type: 'reference' + id: string + filter?: { + uniform?: boolean + } + } + + export interface FontProviderSpace { + type: 'space' + advances: Record + } -type IFontProvider = IFontProviderBitmap | IFontProviderReference | IFontProviderSpace + export type FontProvider = FontProviderBitmap | FontProviderReference | FontProviderSpace -interface IFont { - providers: IFontProvider[] + export interface Font { + providers: FontProvider[] + } } -interface ICachedBitmapChar { +interface CachedBitmapChar { type: 'bitmap' ascent: number width: number atlas: THREE.Texture - pixelUV: [number, number, number, number] - uv: [number, number, number, number] + bitmapUV: { + x: number + y: number + width: number + height: number + } } -interface ICachedSpaceChar { +interface CachedSpaceChar { type: 'space' width: number } -type ICachedChar = ICachedBitmapChar | ICachedSpaceChar +type CachedChar = CachedBitmapChar | CachedSpaceChar -type ICachedCharMesh = { +interface CachedCharGeo { geo?: THREE.BufferGeometry width: number } -class FontProvider { - public type: 'bitmap' | 'reference' | 'space' - public loaded = false +const MISSING_CHARACTER_TEXTURE = new THREE.TextureLoader().load(MissingCharacter) +function createMissingCharacter(): CachedBitmapChar { + return { + type: 'bitmap', + ascent: 7, + width: 6, + atlas: MISSING_CHARACTER_TEXTURE, + bitmapUV: { x: 0, y: 0, width: 8, height: 8 }, + } +} - constructor(providerJSON: IFontProvider) { +abstract class FontProvider { + type: 'bitmap' | 'reference' | 'space' + loaded = false + + constructor(providerJSON: MinecraftJson.FontProvider) { this.type = providerJSON.type } - // eslint-disable-next-line @typescript-eslint/require-await - async load() { - if (this.loaded) return this - this.loaded = true - return this - } + abstract load(): Promise | this - // eslint-disable-next-line @typescript-eslint/no-unused-vars - getChar(_char: string, top = true): ICachedChar | undefined { - return undefined - } + abstract getChar(char: string): CachedChar | undefined static fromAssetPath(assetPath: string) { if (!assetPath.endsWith('.json')) assetPath += '.json' - const providerJSON = assets.getJSONAsset(assetPath) as IFontProvider + const providerJSON = assets.getJSONAsset(assetPath) as MinecraftJson.FontProvider switch (providerJSON.type) { case 'bitmap': return new BitmapFontProvider(providerJSON) @@ -103,9 +106,9 @@ class FontProvider { } class ReferenceFontProvider extends FontProvider { - public reference: MinecraftFont + reference: MinecraftFont - constructor(providerJSON: IFontProviderReference) { + constructor(providerJSON: MinecraftJson.FontProviderReference) { super(providerJSON) const path = getPathFromResourceLocation(providerJSON.id, 'font') this.reference = new MinecraftFont(providerJSON.id, path + '.json') @@ -118,20 +121,30 @@ class ReferenceFontProvider extends FontProvider { return this } - getChar(char: string, top = true) { - return this.reference.getChar(char, top) + getChar(char: string): CachedChar | undefined { + const cached = this.reference.getChar(char) + if (cached.type === 'bitmap' && cached.atlas === MISSING_CHARACTER_TEXTURE) { + return undefined + } + return cached } } class SpaceFontProvider extends FontProvider { - public advances: Record + advances: Record - constructor(providerJSON: IFontProviderSpace) { + constructor(providerJSON: MinecraftJson.FontProviderSpace) { super(providerJSON) this.advances = providerJSON.advances } - getChar(char: string): ICachedChar | undefined { + load() { + if (this.loaded) return this + this.loaded = true + return this + } + + getChar(char: string): CachedChar | undefined { if (this.advances[char] !== undefined) { return { type: 'space', @@ -142,18 +155,18 @@ class SpaceFontProvider extends FontProvider { } class BitmapFontProvider extends FontProvider { - public bitmapPath: string - public charHeight: number - public charWidth: number - public ascent: number - public chars: UnicodeString[] = [] + bitmapPath: string + charHeight: number + charWidth: number + ascent: number + chars: UnicodeString[] = [] - public atlas: THREE.Texture = THREE.Texture.DEFAULT_IMAGE - public canvas: HTMLCanvasElement = document.createElement('canvas') + atlas: THREE.Texture = THREE.Texture.DEFAULT_IMAGE + canvas: HTMLCanvasElement = document.createElement('canvas') - private charCache = new Map() + private charCache = new Map() - constructor(providerJSON: IFontProviderBitmap) { + constructor(providerJSON: MinecraftJson.FontProviderBitmap) { super(providerJSON) this.type = providerJSON.type this.bitmapPath = getPathFromResourceLocation(providerJSON.file, 'textures') @@ -176,13 +189,13 @@ class BitmapFontProvider extends FontProvider { // Update canvas this.canvas.width = texture.image.width this.canvas.height = texture.image.height - const ctx = this.canvas.getContext('2d')! - ctx.drawImage(this.atlas.image as HTMLImageElement, 0, 0) + const ctx = this.canvas.getContext('2d', { willReadFrequently: true })! + ctx.drawImage(this.atlas.image, 0, 0) this.loaded = true return this } - private getCharIndex(char: string): [number, number] { + private getCharIndex(char: string): ArrayVector2 { for (const row of this.chars) { if (row.includes(char)) { return [this.chars.indexOf(row), row.indexOf(char)] @@ -191,66 +204,75 @@ class BitmapFontProvider extends FontProvider { return [-1, -1] } - getChar(char: string) { - if (!this.charCache.has(char)) { - const charPos = this.getCharIndex(char) - if (charPos[0] === -1) return - // Figure out how wide the character is by checking for the last non-transparent pixel - const startX = charPos[1] * this.charWidth - const startY = charPos[0] * this.charHeight - const data = this.canvas - .getContext('2d')! - .getImageData(startX, startY, this.charWidth, this.charHeight) - - let width = 0 - for (let x = 0; x < this.charWidth; x++) { - for (let y = 0; y < this.charHeight; y++) { - const i = (y * this.charWidth + x) * 4 - if (data.data[i + 3] > 0) { - width = x + 1 - break - } + getChar(char: string): CachedChar | undefined { + const cached = this.charCache.get(char) + if (cached) return cached + + const charPos = this.getCharIndex(char) + if (charPos[0] === -1) return + + const startX = charPos[1] * this.charWidth + const startY = charPos[0] * this.charHeight + const data = this.canvas + .getContext('2d')! + .getImageData(startX, startY, this.charWidth, this.charHeight) + // Figure out how wide the character is by checking for the last non-transparent pixel + let width = 0 + for (let x = 0; x < this.charWidth; x++) { + for (let y = 0; y < this.charHeight; y++) { + const i = (y * this.charWidth + x) * 4 + if (data.data[i + 3] > 0) { + width = x + 1 + break } } + } - // eslint-disable-next-line @typescript-eslint/no-this-alias - const scope = this - this.charCache.set(char, { - type: 'bitmap', - ascent: this.ascent, - width: width + 1, // Add 1 pixel of spacing between characters - get atlas() { - return scope.atlas - }, - pixelUV: [startX, startY, width, this.charHeight], - uv: [ - startX / scope.atlas.image.width, - startY / scope.atlas.image.height, - width / scope.atlas.image.width, - this.charHeight / scope.atlas.image.height, - ], - }) + if (width === 0) { + // Character is completely transparent, treat it as missing + return createMissingCharacter() } + + this.charCache.set(char, { + type: 'bitmap', + ascent: this.ascent, + width: width + 1, // Add 1 pixel of spacing between characters + atlas: this.atlas, + bitmapUV: { + x: startX, + y: startY, + width, + height: this.charHeight, + }, + }) + return this.charCache.get(char)! } } export class MinecraftFont { static all: MinecraftFont[] = [] - static missingCharacterAtlas = new THREE.TextureLoader().load(MissingCharacter) - public id: string - public providers: FontProvider[] = [] - public fallback: MinecraftFont | undefined + id: string + providers: FontProvider[] = [] + fallback: MinecraftFont | undefined - private charCache = new Map() private loaded = false - private characterMeshCache = new Map() + private charCache = new Map() + private geoCache = new Map() + private materialCache = new Map() constructor(id: string, assetPath: string, fallback?: MinecraftFont) { this.id = id this.fallback = fallback - const fontJSON = assets.getJSONAsset(assetPath) as IFont + + let fontJSON: MinecraftJson.Font + try { + fontJSON = assets.getJSONAsset(assetPath) as MinecraftJson.Font + } catch (error) { + console.error(`Failed to load font JSON from ${assetPath}:`, error) + throw error + } for (const providerJSON of fontJSON.providers) { switch (providerJSON.type) { @@ -279,153 +301,109 @@ export class MinecraftFont { async load() { if (this.loaded) return this - await Promise.all(this.providers.map(provider => provider.load())).then(() => { - // // Cache commonly used characters - // for (const char of 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}\\|;:\'",.<>/?`~ ') { - // this.getChar(char) - // } - }) + await Promise.all(this.providers.map(provider => provider.load())) this.loaded = true return this } - /** - * @returns The character data for the given character, or undefined if the character is not found. - */ - getChar(char: string, top = true): ICachedChar | undefined { - if (!this.charCache.has(char)) { - for (const provider of this.providers) { - const data = provider.getChar(char, false) - if (data) { - this.charCache.set(char, data) - return data - } - } - if (top) { - return { - type: 'bitmap', - ascent: 7, - width: 6, - atlas: MinecraftFont.missingCharacterAtlas, - pixelUV: [0, 0, 8, 8], - uv: [0, 0, (1 / 8) * 6, 1], - } + getChar(char: string): CachedChar { + const cached = this.charCache.get(char) + if (cached) return cached + + for (const provider of this.providers) { + const data = provider.getChar(char) + if (data) { + this.charCache.set(char, data) + return data } } - return this.charCache.get(char) + + return createMissingCharacter() } - getTextWidth(text: UnicodeString, span: IStyleSpan) { + getTextWidth(text: UnicodeString, span?: StyleSpan) { let width = 0 - const boldExtra = span.style.bold ? 1 : 0 - // eslint-disable-next-line @typescript-eslint/no-this-alias + const boldExtra = span?.style.bold ? 1 : 0 let font: MinecraftFont = this - if (span.style.font && span.style.font !== this.id) { - const newFont = MinecraftFont.getById(span.style.font as string) + + if (span?.style.font && span.style.font !== this.id) { + const newFont = MinecraftFont.getById(span.style.font) if (newFont) font = newFont } + for (const char of text) { if (char === '\n') break const charData = font.getChar(char) - // TODO: Handle missing characters better if (!charData) { - console.warn(`Missing character: '${char}'`) + console.warn('Encountered unknown character while getting text width:', char) continue } width += charData.width + boldExtra } + return Math.max(width, 0) } - getWordWidth(word: IComponentWord) { + getWordWidth(word: Word) { let width = 0 - // eslint-disable-next-line @typescript-eslint/no-this-alias let font: MinecraftFont = this + for (const span of word.styles) { if (span.style.font && span.style.font !== this.id) { - const newFont = MinecraftFont.getById(span.style.font as string) + const newFont = MinecraftFont.getById(span.style.font) if (newFont) font = newFont } const text = word.text.slice(span.start, span.end) const textWidth = font.getTextWidth(text, span) width += textWidth } + return Math.max(width, 0) } - async generateTextMesh({ + getColorMaterial(color: tinycolor.Instance): THREE.Material { + const colorString = color.toHex8String() + let material = this.materialCache.get(colorString) + if (!material) { + const alpha = color.getAlpha() + if (alpha < 1) { + material = new THREE.MeshBasicMaterial({ + color: color.toHexString(), + transparent: true, + opacity: alpha, + }) + } else { + material = new THREE.MeshBasicMaterial({ color: color.toHexString() }) + } + this.materialCache.set(colorString, material) + } + return material + } + + // TODO - Add support for rendering object text components + async generateTextDisplayMesh({ jsonText, - maxLineWidth, - backgroundColor, - backgroundAlpha, - shadow, - alignment, + maxLineWidth = TextDisplay.properties.maxLineWidth.default, + backgroundColor = tinycolor(TextDisplay.properties.backgroundColor.default), + shadow = TextDisplay.properties.shadow.default, + alignment = TextDisplay.properties.align.default, }: { jsonText: JsonText - maxLineWidth: number - backgroundColor: string - backgroundAlpha: number + maxLineWidth?: number + backgroundColor?: tinycolor.Instance + /** Whether or not to render any text shadow */ shadow?: boolean alignment?: Alignment - }): Promise<{ mesh: THREE.Mesh; outline: THREE.LineSegments }> { - console.time('drawTextToMesh') - const mesh = new THREE.Mesh() + }) { + const stopwatch = new Stopwatch('Generate Text Display Mesh').start() - const words = getComponentWords(jsonText) - const { lines, backgroundWidth } = await computeTextWrapping(words, maxLineWidth) + const { lines, backgroundWidth } = await wrapJsonText(jsonText, maxLineWidth) const width = backgroundWidth + 1 - const height = lines.length * 10 + 1 - // // Debug output - // const wordWidths = words.map(word => this.getWordWidth(word)) - // for (const word of words) { - // console.log( - // `${words.indexOf(word)} '${word.text.toString()}' width: ${ - // wordWidths[words.indexOf(word)] - // }` - // ) - // for (const span of word.styles) { - // console.log( - // `'${word.text.slice(span.start, span.end).toString()}' ${span.start}-${ - // span.end - // } = `, - // span.style - // ) - // } - // } - // console.log('Lines:', lines, 'CanvasWidth:', maxLineWidth) - // for (const line of lines) { - // console.log('Line', lines.indexOf(line), line.width) - // for (const word of line.words) { - // console.log( - // 'Word', - // line.words.indexOf(word), - // `'${word.text.toString()}'`, - // word.styles.map(span => span.style), - // word.styles.map( - // span => - // `${span.start}-${span.end} '${word.text - // .slice(span.start, span.end) - // .toString()}'` - // ) - // ) - // } - // } - - const backgroundGeo = new THREE.PlaneBufferGeometry(width, height) - const backgroundMesh = new THREE.Mesh( - backgroundGeo, - new THREE.MeshBasicMaterial({ - color: backgroundColor, - transparent: true, - opacity: backgroundAlpha, - }) - ) - .translateY(height / 2) - .translateZ(-0.05) - mesh.add(backgroundMesh) + const height = (lines.length || 1) * 10 + 1 - const geos: THREE.BufferGeometry[] = [] + const spanGeos: THREE.BufferGeometry[] = [] + const spanMaterials: THREE.Material[] = [] const cursor = { x: 0, y: height - 9 } for (const line of lines) { switch (alignment) { @@ -438,190 +416,196 @@ export class MinecraftFont { default: cursor.x = -width / 2 + 1 } + for (const word of line.words) { for (const span of word.styles) { + const charGeos: THREE.BufferGeometry[] = [] + const shadowGeos: THREE.BufferGeometry[] = [] + const text = word.text.slice(span.start, span.end) for (const char of text) { - const charMesh = this.generateCharMesh(char, span.style, shadow) - if (!charMesh) continue - if (charMesh.geo) { - const clone = charMesh.geo.clone() + const charGeo = this.getCharGeo(char, span.style) + + if (!charGeo) { + console.error('Failed to get character geometry:', char) + continue + } + + if (charGeo.geo) { + const clone = charGeo.geo.clone() clone.translate(cursor.x, cursor.y, 0) - geos.push(clone) + charGeos.push(clone) + if (shadow) { + const shadowGeo = charGeo.geo.clone() + shadowGeo.translate(cursor.x + 1, cursor.y - 1, -0.01) + shadowGeos.push(shadowGeo) + } + } + + cursor.x += charGeo.width + } + + if (charGeos.length > 0) { + spanGeos.push(mergeGeometries(charGeos)!) + + const color = JsonText.getColor(span.style.color ?? COLOR_VALUES.white) + spanMaterials.push(this.getColorMaterial(color)) + + if (shadow && shadowGeos.length > 0) { + spanGeos.push(mergeGeometries(shadowGeos)!) + + if (span.style.shadow_color) { + spanMaterials.push( + this.getColorMaterial( + JsonText.getColor(span.style.shadow_color) + ) + ) + } else { + // Default shadow color is 25% the brightness of the main color + spanMaterials.push( + this.getColorMaterial( + // This version of tinycolor doesn't have a multiply method... + tinycolor( + new THREE.Color(color.toHexString()) + .multiplyScalar(0.25) + .getHexString() + ) + ) + ) + } } - cursor.x += charMesh.width } } } + cursor.y -= 10 } - let charGeo: THREE.BufferGeometry | undefined - if (geos.length > 0) { - charGeo = mergeGeometries(geos)! - const charMesh = new THREE.Mesh( - charGeo, - new THREE.MeshBasicMaterial({ vertexColors: true }) - ) - mesh.add(charMesh) - } + const mesh = new THREE.Mesh() - mesh.scale.set(0.4, 0.4, 0.4) - mesh.rotateY(Math.PI) - mesh.translateX(1 / 5) + // Transforms a geometry to emulate how Minecraft renders text in the world + const transform = (geo: THREE.BufferGeometry) => { + geo.scale(0.4, 0.4, 0.4) + geo.rotateY(Math.PI) + geo.translate(1 / 5, 0, 0) + return geo + } - const outlineGeo = new THREE.EdgesGeometry(backgroundGeo.clone().scale(0.4, 0.4, 0.4)) - const outline = new THREE.LineSegments(outlineGeo, Canvas.outlineMaterial) - const positions = Array.from(outlineGeo.getAttribute('position').array) - for (let i = 0; i < positions.length; i += 3) { - positions[i] -= 1 / 5 - positions[i + 1] += (height / 2) * 0.4 + if (spanGeos.length > 0) { + const charGeo = transform(mergeGeometries(spanGeos, true)!) + const textMesh = new THREE.Mesh(charGeo, spanMaterials) + textMesh.name = 'text' + mesh.add(textMesh) } - outlineGeo.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)) + const backgroundGeo = transform( + // Align bottom-middle of the background with origin, and move it slightly backwards to avoid z-fighting with text. + // Minecraft applies the same backwards offset to prevent z-fighting. + new THREE.PlaneBufferGeometry(width, height).translate(0, height / 2, -0.05) + ) + const backgroundMesh = new THREE.Mesh( + backgroundGeo, + new THREE.MeshBasicMaterial({ + color: backgroundColor.toHexString(), + opacity: backgroundColor.getAlpha(), + transparent: true, + }) + ) + backgroundMesh.name = 'background' + mesh.add(backgroundMesh) + + const outline = new THREE.LineSegments( + new THREE.EdgesGeometry(backgroundGeo), + Canvas.outlineMaterial + ) outline.no_export = true outline.renderOrder = 2 outline.frustumCulled = false - mesh.isTextDisplayText = true - - console.timeEnd('drawTextToMesh') - return { mesh, outline } + stopwatch.debug({ mesh, hitbox: backgroundGeo, outline }) + return { mesh, hitbox: backgroundGeo, outline } } - generateCharMesh( - char: string, - style: StyleRecord, - shadow?: boolean - ): ICachedCharMesh | undefined { - // eslint-disable-next-line @typescript-eslint/no-this-alias + getCharGeo(char: string, style: ComponentStyle): CachedCharGeo { let font: MinecraftFont = this if (style.font) { - const newFont = MinecraftFont.getById(style.font as string) + const newFont = MinecraftFont.getById(style.font) if (newFont) font = newFont } - const charData = font.getChar(char) - if (!charData) { - // Technically this should never happen, but just in case... - console.error('Unknown character:', char) - return - } - - let color = new THREE.Color('#ffffff') - if (typeof style.color === 'string') { - color = - style.color.startsWith('#') && style.color.length === 7 - ? new THREE.Color(style.color) - : new THREE.Color(COLOR_MAP[style.color]) || color - } - let shadowColor: THREE.Color - if (Array.isArray(style.shadow_color)) { - console.log('Shadow color:', style.shadow_color) - shadowColor = new THREE.Color().fromArray(style.shadow_color) - } else { - shadowColor = color.clone().multiplyScalar(0.25) - } + const hash = createHash('sha256') + hash.update(char) + hash.update(';' + font.id) + if (style.bold) hash.update('bold') + if (style.italic) hash.update('italic') + if (style.underlined) hash.update('underlined') + if (style.strikethrough) hash.update('strikethrough') + if (style.font) hash.update(';' + font.id) + const digest = hash.digest('hex') + const charData = font.getChar(char) const boldExtra = style.bold ? 1 : 0 - if (charData.type === 'bitmap') { - const hash = createHash('sha256') - hash.update(char) - hash.update(';' + color.getHexString()) - if (shadow) hash.update('shadow') - if (style.bold) hash.update('bold') - if (style.italic) hash.update('italic') - if (style.underlined) hash.update('underlined') - if (style.strikethrough) hash.update('strikethrough') - if (style.shadow_color) hash.update(';' + shadowColor.getHexString()) - if (style.font) hash.update(';' + font.id) - // if (style.obfuscated) hash.update('obfuscated') - const digest = hash.digest('hex') - - let charMesh = this.characterMeshCache.get(digest) - - if (charMesh === undefined) { - // If no mesh is found, create a new one - const canvas = document.createElement('canvas') - const ctx = canvas.getContext('2d', { willReadFrequently: true })! - canvas.width = charData.pixelUV[2] - canvas.height = charData.pixelUV[3] - ctx.imageSmoothingEnabled = false - ctx.clearRect(0, 0, canvas.width, canvas.height) + let charGeo = this.geoCache.get(digest) + + if (charGeo === undefined) { + const canvas = document.createElement('canvas') + if (charData.type === 'space') { + canvas.width = charData.width + canvas.height = 7 + } else { + canvas.width = charData.bitmapUV.width + canvas.height = charData.bitmapUV.height + } + + const ctx = canvas.getContext('2d', { willReadFrequently: true })! + ctx.imageSmoothingEnabled = false + ctx.clearRect(0, 0, canvas.width, canvas.height) + if (charData.type !== 'space') { ctx.drawImage( - charData.atlas.image as HTMLImageElement, - charData.pixelUV[0], - charData.pixelUV[1], - charData.pixelUV[2], - charData.pixelUV[3], + charData.atlas.image, + charData.bitmapUV.x, + charData.bitmapUV.y, + charData.bitmapUV.width, + charData.bitmapUV.height, 0, 0, canvas.width, canvas.height ) - const data = ctx.getImageData(0, 0, canvas.width, canvas.height) - - const geo = new THREE.BufferGeometry() - let colors: number[] = [] - let vertices: number[] = [] - let indices: number[] = [] - - const createQuad = (x: number, y: number, w: number, h: number) => { - const vertIndex = vertices.length / 3 - // prettier-ignore - vertices.push( - x, y, 0, - x + w, y, 0, - x + w, y + h, 0, - x, y + h, 0 - ) - indices.push( - vertIndex, - vertIndex + 1, - vertIndex + 2, - vertIndex, - vertIndex + 2, - vertIndex + 3 - ) - // prettier-ignore - colors.push( - color.r, color.g, color.b, - color.r, color.g, color.b, - color.r, color.g, color.b, - color.r, color.g, color.b - ) - if (shadow) { - const shadowVertIndex = vertices.length / 3 - x += 1 - y -= 1 - const z = -0.01 - // prettier-ignore - vertices.push( - x, y, z, - x + w, y, z, - x + w, y + h, z, - x, y + h, z - ) - indices.push( - shadowVertIndex, - shadowVertIndex + 1, - shadowVertIndex + 2, - shadowVertIndex, - shadowVertIndex + 2, - shadowVertIndex + 3 - ) - // prettier-ignore - colors.push( - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b - ) - } - } + } + + const data = ctx.getImageData(0, 0, canvas.width, canvas.height) + const geo = new THREE.BufferGeometry() + + const geoData = { + vertices: [] as number[], + indices: [] as number[], + uvs: [] as number[], + } + + const createQuad = (x: number, y: number, w: number, h: number) => { + const vertIndex = geoData.vertices.length / 3 + // prettier-ignore + geoData.vertices.push( + x, y, 0, + x + w, y, 0, + x + w, y + h, 0, + x, y + h, 0 + ) + geoData.indices.push( + vertIndex, + vertIndex + 1, + vertIndex + 2, + vertIndex, + vertIndex + 2, + vertIndex + 3 + ) + } + + if (charData.type !== 'space') { // Generate a quad for each pixel in the character // This also attempts to make a single quad for each horizontal line of connected pixels for (let y = 0; y < canvas.height; y++) { @@ -644,54 +628,52 @@ export class MinecraftFont { createQuad(canvas.width - width, ascent, width + boldExtra, 1) } } + } - geo.setIndex(indices) - geo.setAttribute( - 'position', - new THREE.BufferAttribute(new Float32Array(vertices), 3) - ) - geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3)) + geo.setIndex(geoData.indices) + geo.setAttribute( + 'position', + new THREE.BufferAttribute(new Float32Array(geoData.vertices), 3) + ) + if (style.italic) { + geo.applyMatrix4(new THREE.Matrix4().makeShear(0, 0, 0.2, 0, 0, 0)) + geo.translate(-1, 0, 0) + } - if (style.italic) { - geo.applyMatrix4(new THREE.Matrix4().makeShear(0, 0, 0.2, 0, 0, 0)) - geo.translate(-1, 0, 0) - } + geoData.vertices = Array.from(geo.getAttribute('position').array) + geoData.indices = Array.from(geo.getIndex()!.array) - vertices = Array.from(geo.getAttribute('position').array) - colors = Array.from(geo.getAttribute('color').array) - indices = Array.from(geo.getIndex()!.array) + if (style.underlined) { + createQuad(-1, -1, canvas.width + 2, 1) + } - if (style.underlined) { - createQuad(-1, -1, canvas.width + 2, 1) + if (charData.type === 'space') { + if (style.strikethrough) { + const ascent = 7 / 2 + createQuad(-1, ascent, canvas.width + 2, 1) } - + } else { if (style.strikethrough) { - const ascent = charData.ascent / 2 + 1 + const ascent = charData.ascent / 2 createQuad(-1, ascent, canvas.width + 2, 1) } + } - geo.setIndex(indices) - geo.setAttribute( - 'position', - new THREE.BufferAttribute(new Float32Array(vertices), 3) - ) - geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3)) - - geo.attributes.position.needsUpdate = true - geo.attributes.color.needsUpdate = true - charMesh = { - geo, - width: charData.width + boldExtra, - } + geo.setIndex(geoData.indices) + geo.setAttribute( + 'position', + new THREE.BufferAttribute(new Float32Array(geoData.vertices), 3) + ) - this.characterMeshCache.set(digest, charMesh) - } - return charMesh - } else { - return { - width: charData.width, + geo.attributes.position.needsUpdate = true + charGeo = { + geo, + width: charData.width + boldExtra, } + + this.geoCache.set(digest, charGeo) } + return charGeo } } @@ -718,56 +700,19 @@ function loadMinecraftFonts() { standardGalacticAlphabetFont.load(), ]).then(() => { console.log('Minecraft fonts loaded!') - requestAnimationFrame(() => events.MINECRAFT_FONTS_LOADED.dispatch()) + requestAnimationFrame(() => EVENTS.MINECRAFT_FONTS_LOADED.publish()) }) } export async function getVanillaFont() { if (!vanillaFont) { await new Promise(resolve => { - events.MINECRAFT_FONTS_LOADED.subscribe(() => resolve()) + EVENTS.MINECRAFT_FONTS_LOADED.subscribe(() => resolve()) }) } return vanillaFont.load() } -events.MINECRAFT_ASSETS_LOADED.subscribe(() => { +EVENTS.MINECRAFT_ASSETS_LOADED.subscribe(() => { loadMinecraftFonts() }) - -// events.SELECT_PROJECT.subscribe(() => { -// void getVanillaFont().then(async font => { -// await font.generateTextMesh({ -// jsonText: new JsonText([ -// '', -// { -// text: 'Sometimes ', -// italic: true, -// }, -// 'you ', -// { -// text: 'have', -// bold: true, -// }, -// ' to wear ', -// { -// text: 'stretchy', -// color: 'yellow', -// }, -// ' pants.\n', -// { -// text: "(It's for ", -// }, -// { -// text: 'fun!', -// underlined: true, -// color: 'blue', -// }, -// ')', -// ]), -// maxLineWidth: 100, -// backgroundColor: '#000000', -// backgroundAlpha: 0.25, -// }) -// }) -// }) diff --git a/src/systems/minecraft/itemDefinitions.ts b/src/systems/minecraft/itemDefinitions.d.ts similarity index 98% rename from src/systems/minecraft/itemDefinitions.ts rename to src/systems/minecraft/itemDefinitions.d.ts index e5cca62f..af1b01f1 100644 --- a/src/systems/minecraft/itemDefinitions.ts +++ b/src/systems/minecraft/itemDefinitions.d.ts @@ -35,6 +35,9 @@ type TintSource = } type ItemModel = + | { + type: 'minecraft:empty' + } | { type: 'minecraft:model' model: string @@ -49,7 +52,6 @@ type ItemModel = // 'minecraft:select': SelectModel // 'minecraft:range_dispatch': {} // 'minecraft:bundle/selected_item': {} -// 'minecraft:empty': {} // 'minecraft:special': {} type ConditionModel = { diff --git a/src/systems/minecraft/itemModelManager.ts b/src/systems/minecraft/itemModelManager.ts index d53be4e7..462a344e 100644 --- a/src/systems/minecraft/itemModelManager.ts +++ b/src/systems/minecraft/itemModelManager.ts @@ -2,10 +2,10 @@ import { mergeGeometries } from '../../util/bufferGeometryUtils' import { getPathFromResourceLocation, parseResourceLocation } from '../../util/minecraftUtil' import { assetsLoaded, getJSONAsset, getPngAssetAsDataUrl } from './assetManager' import { parseBlockModel } from './blockModelManager' -import { IItemModel } from './model' +import type { IItemModel } from './model' import { TEXTURE_FRAG_SHADER, TEXTURE_VERT_SHADER } from './textureShaders' -type ItemModelMesh = { +interface ItemModelMesh { mesh: THREE.Mesh outline: THREE.LineSegments boundingBox: THREE.BufferGeometry @@ -81,8 +81,6 @@ async function parseItemModel(location: string, childModel?: IItemModel): Promis // The block model parser handles custom item models made from elements just fine, so we can use it here return await parseBlockModel({ model: location, isItemModel: true }, model) } - - throw new Error(`Unsupported item model '${location}'`) } async function generateItemMesh(location: string, model: IItemModel): Promise { @@ -99,20 +97,20 @@ async function generateItemMesh(location: string, model: IItemModel): Promise = { - dark_red: '#AA0000', - red: '#FF5555', - gold: '#FFAA00', - yellow: '#FFFF55', - dark_green: '#00AA00', - green: '#55FF55', - aqua: '#55FFFF', - dark_aqua: '#00AAAA', - dark_blue: '#0000AA', - blue: '#5555FF', - light_purple: '#FF55FF', - dark_purple: '#AA00AA', - white: '#FFFFFF', - gray: '#AAAAAA', - dark_gray: '#555555', - black: '#000000', -} - -export type JsonTextObject = { - text?: string - font?: string - color?: JsonTextColor - shadow_color?: [number, number, number, number] - extra?: JsonTextArray - bold?: true | false - italic?: true | false - underlined?: true | false - strikethrough?: true | false - obfuscated?: true | false - insertion?: string - clickEvent?: { - action: - | 'open_url' - | 'open_file' - | 'run_command' - | 'suggest_command' - | 'change_page' - | 'copy_to_clipboard' - value: string - } - hoverEvent?: { - action: 'show_text' | 'show_item' | 'show_entity' - contents: - | JsonTextComponent - | { - type: string - id: string - name?: string - } - | { - id: string - count?: number - tag?: string - } - } - translate?: string - fallback?: string - with?: JsonTextArray - score?: { - name: string - objective: string - value?: number - } - selector?: string - separator?: string - keybind?: string - nbt?: string - block?: string - entity?: string - storage?: string -} - -export type JsonTextComponent = string | JsonTextArray | JsonTextObject | JsonText - -export type JsonTextArray = JsonTextComponent[] - -export class JsonText { - public isJsonTextClass = true - private text: JsonTextComponent - - constructor(jsonText: JsonTextComponent) { - this.text = jsonText - } - - toString() { - return JSON.stringify(this.text) - } - - toJSON() { - return this.text - } - - static fromString(str: string): JsonText | undefined { - // return new JsonText(JSON.parse(str) as JsonTextComponent) - const parser = new JsonTextParser(str) - return parser.parse() - } -} - -class ParserError extends Error { - line: number - column: number - constructor( - message: string, - private stream: StringStream, - child?: Error, - line?: number, - column?: number - ) { - super(message) - - this.line = line ?? stream.line - this.column = column ?? stream.column - - if (child) { - this.message = `${message} at ${this.line}:${this.column}\n${child.message}` - return - } - this.setPointerMessage() - } - - setPointerMessage() { - // Unexpected '}' at 1:5 - // World!"} - // ^ - const pointer = ' '.repeat(this.column - 1) + '^' - this.message = `${this.message} at ${this.line}:${this.column}\n${this.stream.lines[ - this.line - 1 - ].content.trimEnd()}\n${pointer}` - } -} - -class JsonTextParser { - private s: StringStream - private numChars = '0123456789' - private whitespaceChars = ' \t\n\r' - - constructor(private str: string) { - this.s = new StringStream(str) - } - - parse(): JsonText { - let text: JsonTextComponent | undefined - try { - text = this.parseTextComponent(true) - } catch (e) { - throw new ParserError('Failed to parse JsonText', this.s, e as Error) - } - if (text) { - return new JsonText(text) - } - return new JsonText('') - } - - consumeWhitespace() { - this.s.consumeWhile(s => !!s.item && this.whitespaceChars.includes(s.item)) - } - - parseTextComponent(single = false): JsonTextComponent { - let result: JsonTextComponent - this.consumeWhitespace() - if (this.s.item === '{') { - result = this.parseTextObject() - } else if (this.s.item === '[') { - result = this.parseArray() - } else if (this.s.item === '"') { - result = this.parseString() - } else { - throw new ParserError( - `Unexpected '${this.s.item as string}' in JsonTextComponent`, - this.s - ) - } - this.consumeWhitespace() - if (single && this.s.item) { - throw new ParserError( - `Unexpected '${this.s.item as string}' in JsonTextComponent`, - this.s - ) - } - return result - } - - parseValue(): JsonTextComponent | boolean | string | number { - const { line, column } = this.s - this.consumeWhitespace() - if (this.s.item === '{') { - return this.parseTextObject() - } else if (this.s.item === '[') { - return this.parseArray() - } else if (this.s.item === '"') { - return this.parseString() - } else if (this.s.item === 't' || this.s.item === 'f') { - return this.parseBoolean() - } else if ( - this.s.item === '-' || - this.s.item === '.' || - (this.s.item && this.numChars.includes(this.s.item)) - ) { - return this.parseNumber() - } else { - throw new ParserError( - `Unexpected ${this.s.item as string}`, - this.s, - undefined, - line, - column - ) - } - } - - parseObject(valueParser: (key: string, obj: any) => void, validator?: (obj: any) => void): any { - const { line, column } = this.s - try { - // TS attempts to incorrectly infer the type of this.item.s as '{' after this if statement. - // Casting it to string here fixes the issue. - if ((this.s.item as string) !== '{') { - throw new ParserError( - `Unexpected '${this.s.item as string}' in JsonTextObject`, - this.s - ) - } - this.s.consume() // { - this.consumeWhitespace() - const obj: any = {} - while (this.s.item !== '}') { - const key = this.parseString() - this.consumeWhitespace() - this.s.consume() // : - this.consumeWhitespace() - valueParser(key, obj) - this.consumeWhitespace() - if (this.s.item === ',') { - this.s.consume() - this.consumeWhitespace() - } else if (this.s.item === '}') { - break - } else if (this.s.item === undefined) { - throw new ParserError('Unexpected EOF in JsonTextObject', this.s) - } else { - throw new ParserError(`Unexpected '${this.s.item}' in JsonTextObject`, this.s) - } - } - this.s.consume() // } - if (validator) validator(obj) - return obj - } catch (e: any) { - throw new ParserError( - 'Failed to parse JsonTextObject', - this.s, - e as Error, - line, - column - ) - } - } - - parseTextObject() { - return this.parseObject( - (key, obj: JsonTextObject) => { - switch (key) { - case 'block': - case 'entity': - case 'font': - case 'insertion': - case 'keybind': - case 'nbt': - case 'selector': - case 'separator': - case 'storage': - case 'text': - case 'translate': - case 'fallback': - obj[key] = this.parseString() - break - case 'color': { - const color = this.parseString() as JsonTextColor - if (!(color.startsWith('#') || COLOR_MAP[color])) { - throw new ParserError(`Unknown color '${color}'`, this.s) - } - obj.color = color - break - } - case 'shadow_color': { - const color = this.parseString() as JsonTextColor - if (!(color.startsWith('#') || COLOR_MAP[color])) { - throw new ParserError(`Unknown color '${color}'`, this.s) - } - const hex = color.startsWith('#') ? color : COLOR_MAP[color] - const rgba = new tinycolor(hex).toRgb() - // Apparently shadow color is actually a rgba value now... Dumb. - obj.shadow_color = [rgba.r / 255, rgba.g / 255, rgba.b / 255, rgba.a / 255] - break - } - case 'bold': - case 'italic': - case 'obfuscated': - case 'strikethrough': - case 'underlined': - obj[key] = this.parseBoolean() - break - case 'with': - case 'extra': - obj[key] = this.parseArray() - break - case 'score': - obj[key] = this.parseScoreObject() - break - case 'clickEvent': - obj[key] = this.parseClickEventObject() - break - case 'hoverEvent': - obj[key] = this.parseHoverEventObject() - break - default: - throw new ParserError(`Unknown key '${key}' in JsonTextObject`, this.s) - } - }, - obj => { - // Make sure the object has at least one of the required keys - if ( - obj.text === undefined && - obj.translate === undefined && - obj.score === undefined && - obj.selector === undefined && - obj.keybind === undefined && - obj.nbt === undefined - ) { - throw new ParserError( - `JsonTextObject does not include one of 'text', 'translate', 'score', 'selector', 'keybind', or 'nbt'.`, - this.s - ) - } - - // Validate the NBT key - if ( - obj.nbt !== undefined && - obj.block === undefined && - obj.entity === undefined && - obj.storage === undefined - ) { - throw new ParserError( - `JsonTextObject includes 'nbt' but does not include one of 'block', 'entity', or 'storage'.`, - this.s - ) - } - } - ) as JsonTextObject - } - - parseScoreObject() { - return this.parseObject( - (key, obj: NonNullable) => { - switch (key) { - case 'name': - case 'objective': - obj[key] = this.parseString() - break - case 'value': - obj[key] = this.parseNumber() - break - default: - throw new ParserError( - `Unknown key '${key}' in JsonTextObject.score`, - this.s - ) - } - }, - obj => { - if (obj.name === undefined || obj.objective === undefined) { - throw new ParserError( - `JsonTextObject.score must include 'name' and 'objective'`, - this.s - ) - } - } - ) as JsonTextObject['score'] - } - - parseClickEventObject() { - return this.parseObject( - (key, obj: NonNullable) => { - switch (key) { - case 'action': - obj[key] = this.parseString([ - 'open_url', - 'open_file', - 'run_command', - 'suggest_command', - 'change_page', - 'copy_to_clipboard', - ]) - break - case 'value': - obj[key] = this.parseString() - break - default: - throw new ParserError( - `Unknown key '${key}' in JsonTextObject.clickEvent`, - this.s - ) - } - }, - obj => { - if (obj.action === undefined) { - throw new ParserError(`JsonTextObject.clickEvent must include 'action'`, this.s) - } else if (obj.value === undefined) { - throw new ParserError(`JsonTextObject.clickEvent must include 'value'`, this.s) - } - } - ) as JsonTextObject['clickEvent'] - } - - parseHoverEventObject() { - return this.parseObject( - (key, obj: NonNullable) => { - switch (key) { - case 'action': - obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) - break - case 'contents': - switch (obj.action) { - case undefined: { - throw new ParserError( - `HoverEvent 'action' is required, and must be defined before 'contents'.`, - this.s - ) - } - case 'show_text': { - obj[key] = this.parseTextComponent() - break - } - case 'show_item': { - obj[key] = this.parseObject( - (key, item) => { - switch (key) { - case 'id': - item[key] = this.parseString() - break - case 'count': - item[key] = this.parseNumber() - break - case 'tag': - item[key] = this.parseString() - break - default: - throw new ParserError( - `Unknown key '${key}' in JsonTextObject.itemHoverEvent.contents`, - this.s - ) - } - }, - obj => { - if (obj.id === undefined) { - throw new ParserError( - `JsonTextObject.itemHoverEvent.contents must include 'id'`, - this.s - ) - } - } - ) - break - } - case 'show_entity': { - obj[key] = this.parseObject( - (key, entity) => { - switch (key) { - case 'type': - entity[key] = this.parseString() - break - case 'id': - entity[key] = this.parseString() - break - case 'name': - entity[key] = this.parseString() - break - default: - throw new ParserError( - `Unknown key '${key}' in JsonTextObject.entityHoverEvent.contents`, - this.s - ) - } - }, - obj => { - if (obj.type === undefined) { - throw new ParserError( - `JsonTextObject.entityHoverEvent.contents must include 'type'`, - this.s - ) - } - } - ) - break - } - } - break - default: - throw new ParserError( - `Unknown key '${key}' in JsonTextObject.hoverEvent`, - this.s - ) - } - }, - obj => { - if (obj.action === undefined) { - throw new ParserError(`JsonTextObject.hoverEvent must include 'action'`, this.s) - } else if (obj.contents === undefined) { - throw new ParserError( - `JsonTextObject.hoverEvent must include 'contents'`, - this.s - ) - } - } - ) as JsonTextObject['hoverEvent'] - } - - parseArray(): JsonTextArray { - this.s.consume() // [ - this.consumeWhitespace() - const arr: JsonTextArray = [] - while (this.s.item !== ']') { - this.consumeWhitespace() - const value = this.parseTextComponent() - arr.push(value) - if (this.s.item === ',') { - this.s.consume() - this.consumeWhitespace() - } else if (this.s.item === ']') { - break - } else { - throw new ParserError( - `Unexpected '${this.s.item as string}' in JsonTextArray`, - this.s - ) - } - } - this.s.consume() // ] - return arr - } - - parseString(): string - parseString(allowedValues: T[]): T - parseString(allowedValues?: string[]): string { - if (this.s.item !== '"') { - throw new ParserError(`Unexpected '${this.s.item as string}' in string`, this.s) - } - this.s.consume() // " - let str = '' - while (this.s.item) { - if ((this.s.item as string) === '\\') { - if (this.s.look(1) === 'n') { - str += '\n' - this.s.consume() // \ - this.s.consume() // n - continue - } else { - this.s.consume() // \ - str += this.s.item - this.s.consume() // Escaped character - continue - } - } - if (this.s.item === '"') { - break - } else if (this.s.item === '\n') { - throw new ParserError('Unexpected newline in string', this.s) - } - str += this.s.item - this.s.consume() - } - if (!this.s.item) { - throw new ParserError('Unexpected EOF in string', this.s) - } - this.s.consume() // " - if (allowedValues && !allowedValues.includes(str)) { - throw new ParserError( - `Unexpected string value '${str}'. Expected one of ${allowedValues.join(', ')}`, - this.s - ) - } - return str - } - - parseBoolean(): boolean { - if (this.s.item === '"') { - const value = this.parseString() - if (value === 'true') { - return true - } else if (value === 'false') { - return false - } - throw new ParserError(`Unexpected incomplete string boolean`, this.s) - } - if (this.s.look(0, 4) === 'true') { - this.s.consumeN(4) - return true - } else if (this.s.look(0, 5) === 'false') { - this.s.consumeN(5) - return false - } - throw new ParserError(`Unexpected incomplete boolean`, this.s) - } - - parseNumber(): number { - let num = '' - let hasDecimal = false - while (this.s.item) { - if (this.s.item === '.') { - if (hasDecimal) { - throw new ParserError('Unexpected second decimal point in number', this.s) - } - hasDecimal = true - } - num += this.s.item - this.s.consume() - } - return parseInt(num) - } -} diff --git a/src/systems/minecraft/registryManager.ts b/src/systems/minecraft/registryManager.ts index 73cb61ca..5445fbeb 100644 --- a/src/systems/minecraft/registryManager.ts +++ b/src/systems/minecraft/registryManager.ts @@ -1,6 +1,6 @@ +import EVENTS from '../../util/events' import { checkForAssetsUpdate } from './assetManager' import { getLatestVersion } from './versionManager' -import { events } from '../../util/events' interface IRegistryJSON { activity: string[] @@ -93,17 +93,17 @@ const REGISTRIES_URL = 'https://raw.githubusercontent.com/misode/mcmeta/summary/registries/data.json' class MinecraftRegistryEntry { - public items: string[] = [] + items: string[] = [] constructor(entries: string[]) { this.items = entries } - public has(item: string): boolean { + has(item: string): boolean { return this.items.includes(item) } - public find(searchFunction: (item: string) => boolean): string | undefined { + find(searchFunction: (item: string) => boolean): string | undefined { return this.items.find(searchFunction) } } @@ -136,7 +136,7 @@ async function updateLocalRegistry() { } catch (error) { console.error('Failed to fetch latest Minecraft registry:', error) } - if (response && response.ok) { + if (response?.ok) { const newRegistry = (await response.json()) as IRegistryJSON localStorage.setItem('animated_java:minecraftRegistry', JSON.stringify(newRegistry)) const latestVersion = await getLatestVersion() @@ -175,10 +175,10 @@ export async function checkForRegistryUpdate() { console.log('Minecraft Registry is up to date!') updateMemoryRegistry() - requestAnimationFrame(() => events.MINECRAFT_REGISTRY_LOADED.dispatch()) + requestAnimationFrame(() => EVENTS.MINECRAFT_REGISTRY_LOADED.publish()) } -events.NETWORK_CONNECTED.subscribe(() => { +EVENTS.NETWORK_CONNECTED.subscribe(() => { void checkForRegistryUpdate().then(async () => { await checkForAssetsUpdate() }) diff --git a/src/systems/minecraft/textWrapping.ts b/src/systems/minecraft/textWrapping.ts deleted file mode 100644 index 05a0c100..00000000 --- a/src/systems/minecraft/textWrapping.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { UnicodeString } from '../../util/unicodeString' -import { getVanillaFont } from './fontManager' -import { JsonText, JsonTextArray, JsonTextComponent, JsonTextObject } from './jsonText' - -// @ts-ignore -// import TestWorker from './textWrapping.worker.ts' -// const WORKER: Worker = new TestWorker() -// WORKER.onmessage = ({ data }) => { -// console.log(data) -// } - -// Jumpstarted by @IanSSenne (FetchBot) and refactored by @SnaveSutit to do line wrapping on JSON Text Components. -// THANK U IAN <3 - SnaveSutit -const STYLE_KEYS = [ - 'bold', - 'italic', - 'underlined', - 'strikethrough', - 'obfuscated', - 'color', - 'font', - 'shadow_color', -] as const - -export type StyleRecord = Partial> -function getStylesFromComponent( - component: JsonTextObject, - parent: StyleRecord = { color: 'white' } -): StyleRecord { - for (const key of STYLE_KEYS) { - if (component[key]) { - parent[key] = component[key] as any - } - } - return parent -} - -function getFirstItemStyle(input: JsonTextArray): StyleRecord { - let item = input.at(0) - if (Array.isArray(item)) { - return getFirstItemStyle(item) - } else if (item instanceof JsonText) { - item = item.toJSON() as JsonTextObject | JsonTextArray - if (Array.isArray(item)) return getFirstItemStyle(item) - else return getStylesFromComponent(item) - } else if (typeof item === 'object') { - return getStylesFromComponent(item) - } - return {} -} - -function flattenTextComponent(input: JsonTextComponent): JsonTextObject[] { - const output: JsonTextObject[] = [] - function flattenComponent(component: JsonTextComponent, parentStyle: StyleRecord = {}) { - if (Array.isArray(component)) { - // The items of an array inherit the first item's style - parentStyle = Object.assign({}, parentStyle, getFirstItemStyle(component)) - for (const subcomponent of component) { - flattenComponent(subcomponent, parentStyle) - } - } else if (typeof component === 'string') { - output.push( - Object.assign({}, parentStyle, { - text: component, - }) as JsonTextObject - ) - } else if (component instanceof JsonText) { - flattenComponent(component.toJSON(), parentStyle) - } else if (typeof component === 'object') { - output.push(Object.assign({}, parentStyle, component, { extra: undefined })) - if (component.extra) { - const childStyles = getStylesFromComponent(component) - flattenComponent(component.extra, childStyles) - } - } - } - flattenComponent(input) - return output -} - -function getText(component: JsonTextObject) { - if (typeof component === 'string') return new UnicodeString(component) - else if (component.text) return new UnicodeString(component.text) - else if (component.translate) return new UnicodeString(`{${component.translate}}`) - else if (component.selector) return new UnicodeString(`{${component.selector}}`) - else if (component.score) { - if (component.score.value) return new UnicodeString(`{${component.score.value}}`) - return new UnicodeString(`{${component.score.name}:${component.score.objective}}`) - } else if (component.keybind) return new UnicodeString(`{${component.keybind}}`) - else if (component.nbt) { - if (component.block) return new UnicodeString(`{${component.block}:${component.nbt}}`) - else if (component.entity) - return new UnicodeString(`{${component.entity}:${component.nbt}}`) - else if (component.storage) - return new UnicodeString(`{${component.storage}:${component.nbt}}`) - return new UnicodeString(`{${component.nbt}}`) - } - return new UnicodeString('') -} - -export interface IStyleSpan { - style: StyleRecord - start: number - end: number -} - -export interface IComponentWord { - styles: IStyleSpan[] - text: UnicodeString - /** - * The width of the word in pixels. - */ - width: number - forceWrap?: boolean -} - -interface IComponentLine { - words: IComponentWord[] - width: number -} -/** - * Gets the words from a JSON Text Component, while keeping track of the styles applied to each word. - * - * WARNING: Word width is not calculated by this function. - */ -export function getComponentWords(input: JsonTextComponent) { - console.time('getComponentWords') - const flattenedComponents = flattenTextComponent(input) - if (!flattenedComponents.length) return [] - const words: IComponentWord[] = [] - let word: IComponentWord | undefined - let component: JsonTextObject | undefined = flattenedComponents.shift() - let componentText = getText(component!) - let style: IStyleSpan = { - style: getStylesFromComponent(component!), - start: 0, - end: 0, - } - - while (component) { - for (const char of componentText) { - if (char === ' ') { - // A group of multiple spaces is treated as a word. - if (word && !(word.text.at(-1) === ' ')) { - style.end++ - if (Object.keys(style.style).length) { - word.styles.push({ ...style }) - style.start = 0 - style.end = 0 - } - words.push(word) - word = undefined - } - } else if (char === '\n') { - if (word) { - if (Object.keys(style.style).length) { - word.styles.push({ ...style }) - style.start = 0 - style.end = 0 - } - words.push(word) - } - words.push({ - styles: [], - text: new UnicodeString(''), - width: 0, - forceWrap: true, - }) - word = undefined - continue - } else if (char !== ' ' && word?.text.at(-1) === ' ') { - style.end++ - if (Object.keys(style.style).length) { - word.styles.push({ ...style }) - style.start = 0 - style.end = 0 - } - words.push(word) - word = undefined - } - - if (!word) { - word = { styles: [], text: new UnicodeString(''), width: 0 } - } - word.text.append(char) - style.end++ - } - component = flattenedComponents.shift() - if (component) { - componentText = getText(component) - if (word) { - word.styles.push(style) - style = { - style: getStylesFromComponent(component), - start: style.end, - end: style.end, - } - } else { - style = { style: getStylesFromComponent(component), start: 0, end: 0 } - } - } - } - - if (word) { - if (Object.keys(style.style).length) { - word.styles.push(style) - } - words.push(word) - } - - console.timeEnd('getComponentWords') - return words -} - -export async function computeTextWrapping(words: IComponentWord[], maxLineWidth = 200) { - console.time('computeTextWrapping') - const lines: IComponentLine[] = [] - const font = await getVanillaFont() - - let backgroundWidth = 0 - let currentLine: IComponentLine = { words: [], width: 0 } - for (const word of words) { - const wordWidth = font.getWordWidth(word) - const wordStyles = [...word.styles] - // If the word is longer than than the max line width, split it into multiple lines - if (wordWidth - 1 > maxLineWidth) { - if (currentLine.words.length) { - lines.push(currentLine) - backgroundWidth = Math.max(backgroundWidth, currentLine.width) - } - currentLine = { words: [], width: 0 } - - let part = new UnicodeString('') - let partWidth = 0 - let partStartIndex = 0 - let style: IStyleSpan | undefined = wordStyles.shift() - if (!style) throw new Error(`No active style found for word '${word.text.toString()}'`) - - for (let i = 0; i < word.text.length; i++) { - const char = word.text.at(i)! - if (wordStyles.length > 1 && i >= style.end) { - style = wordStyles.shift()! - } - - const charWidth = font.getTextWidth(new UnicodeString(char), style) - if (part.length > 0 && partWidth + (charWidth - 1) > maxLineWidth) { - // Find all styles that apply to this part - // FIXME: Attempt to avoid filtering and maping the styles for each character - const partStyles = word.styles - .filter( - span => - span.start < partStartIndex + part.length && - span.end >= partStartIndex - ) - .map(span => ({ - ...span, - start: Math.max(span.start - partStartIndex, 0), - end: Math.min(span.end - partStartIndex, part.length), - })) - lines.push({ - words: [{ text: part, styles: partStyles, width: wordWidth }], - width: partWidth, - }) - backgroundWidth = Math.max(backgroundWidth, partWidth) - partStartIndex += part.length - part = new UnicodeString('') - partWidth = 0 - } - part.append(char) - partWidth += charWidth - } - if (part) { - // Find all styles that apply to this part - // FIXME: Attempt to avoid filtering and maping the styles for each character - const partStyles = word.styles - .filter( - span => - span.start < partStartIndex + part.length && span.end >= partStartIndex - ) - .map(span => ({ - ...span, - start: Math.max(span.start - partStartIndex, 0), - end: Math.min(span.end - partStartIndex, part.length), - })) - backgroundWidth = Math.max(backgroundWidth, partWidth) - currentLine = { - words: [{ text: part, styles: partStyles, width: wordWidth }], - width: partWidth, - } - } - continue - // If the word is a newline character, force a line break - } else if (word.forceWrap) { - if (currentLine.words.length) { - lines.push(currentLine) - backgroundWidth = Math.max(backgroundWidth, currentLine.width) - } - currentLine = { words: [], width: 0 } - // If the current line has words and adding the current word would exceed the max line width, start a new line - } else if (currentLine.words.length && currentLine.width + (wordWidth - 1) > maxLineWidth) { - const lastWord = currentLine.words.at(-1) - // This will only effect space "words" - if (lastWord?.text.at(-1) === ' ') { - currentLine.words.pop() - currentLine.width -= lastWord.width - } - lines.push(currentLine) - backgroundWidth = Math.max(backgroundWidth, currentLine.width) - currentLine = { words: [], width: 0 } - } - word.width = wordWidth - currentLine.words.push(word) - currentLine.width += wordWidth - } - if (currentLine.words.length) { - lines.push(currentLine) - backgroundWidth = Math.max(backgroundWidth, currentLine.width) - } - - console.timeEnd('computeTextWrapping') - return { lines, backgroundWidth } -} diff --git a/src/systems/minecraft/textureShaders.ts b/src/systems/minecraft/textureShaders.ts index 979f47cd..81996429 100644 --- a/src/systems/minecraft/textureShaders.ts +++ b/src/systems/minecraft/textureShaders.ts @@ -1,109 +1,109 @@ export const TEXTURE_VERT_SHADER = ` - attribute float highlight; - - uniform bool SHADE; - uniform int LIGHTSIDE; - - varying vec2 vUv; - varying float light; - varying float lift; - - float AMBIENT = 0.5; - float XFAC = -0.15; - float ZFAC = 0.05; - - void main() - { - - if (SHADE) { - - vec3 N = normalize( vec3( modelMatrix * vec4(normal, 0.0) ) ); - - if (LIGHTSIDE == 1) { - float temp = N.y; - N.y = N.z * -1.0; - N.z = temp; - } - if (LIGHTSIDE == 2) { - float temp = N.y; - N.y = N.x; - N.x = temp; - } - if (LIGHTSIDE == 3) { - N.y = N.y * -1.0; - } - if (LIGHTSIDE == 4) { - float temp = N.y; - N.y = N.z; - N.z = temp; - } - if (LIGHTSIDE == 5) { - float temp = N.y; - N.y = N.x * -1.0; - N.x = temp; - } - - float yLight = (1.0+N.y) * 0.5; - light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT; - - } else { - - light = 1.0; - - } - - if (highlight == 2.0) { - lift = 0.22; - } else if (highlight == 1.0) { - lift = 0.1; - } else { - lift = 0.0; - } - - vUv = uv; - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - gl_Position = projectionMatrix * mvPosition; - }` + attribute float highlight; + + uniform bool SHADE; + uniform int LIGHTSIDE; + + varying vec2 vUv; + varying float light; + varying float lift; + + float AMBIENT = 0.5; + float XFAC = -0.15; + float ZFAC = 0.05; + + void main() + { + + if (SHADE) { + + vec3 N = normalize( vec3( modelMatrix * vec4(normal, 0.0) ) ); + + if (LIGHTSIDE == 1) { + float temp = N.y; + N.y = N.z * -1.0; + N.z = temp; + } + if (LIGHTSIDE == 2) { + float temp = N.y; + N.y = N.x; + N.x = temp; + } + if (LIGHTSIDE == 3) { + N.y = N.y * -1.0; + } + if (LIGHTSIDE == 4) { + float temp = N.y; + N.y = N.z; + N.z = temp; + } + if (LIGHTSIDE == 5) { + float temp = N.y; + N.y = N.x * -1.0; + N.x = temp; + } + + float yLight = (1.0+N.y) * 0.5; + light = yLight * (1.0-AMBIENT) + N.x*N.x * XFAC + N.z*N.z * ZFAC + AMBIENT; + + } else { + + light = 1.0; + + } + + if (highlight == 2.0) { + lift = 0.22; + } else if (highlight == 1.0) { + lift = 0.1; + } else { + lift = 0.0; + } + + vUv = uv; + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + gl_Position = projectionMatrix * mvPosition; + }` export const TEXTURE_FRAG_SHADER = ` - #ifdef GL_ES - precision highp float; - #endif + #ifdef GL_ES + precision highp float; + #endif - uniform sampler2D map; + uniform sampler2D map; - uniform bool SHADE; - uniform bool EMISSIVE; - uniform vec3 LIGHTCOLOR; + uniform bool SHADE; + uniform bool EMISSIVE; + uniform vec3 LIGHTCOLOR; - varying vec2 vUv; - varying float light; - varying float lift; + varying vec2 vUv; + varying float light; + varying float lift; - void main(void) - { - vec4 color = texture2D(map, vUv); + void main(void) + { + vec4 color = texture2D(map, vUv); - if (color.a < 0.01) discard; + if (color.a < 0.01) discard; - if (EMISSIVE == false) { + if (EMISSIVE == false) { - gl_FragColor = vec4(lift + color.rgb * light, color.a); - gl_FragColor.r = gl_FragColor.r * LIGHTCOLOR.r; - gl_FragColor.g = gl_FragColor.g * LIGHTCOLOR.g; - gl_FragColor.b = gl_FragColor.b * LIGHTCOLOR.b; + gl_FragColor = vec4(lift + color.rgb * light, color.a); + gl_FragColor.r = gl_FragColor.r * LIGHTCOLOR.r; + gl_FragColor.g = gl_FragColor.g * LIGHTCOLOR.g; + gl_FragColor.b = gl_FragColor.b * LIGHTCOLOR.b; - } else { + } else { - float light_r = (light * LIGHTCOLOR.r) + (1.0 - light * LIGHTCOLOR.r) * (1.0 - color.a); - float light_g = (light * LIGHTCOLOR.g) + (1.0 - light * LIGHTCOLOR.g) * (1.0 - color.a); - float light_b = (light * LIGHTCOLOR.b) + (1.0 - light * LIGHTCOLOR.b) * (1.0 - color.a); - gl_FragColor = vec4(lift + color.r * light_r, lift + color.g * light_g, lift + color.b * light_b, 1.0); + float light_r = (light * LIGHTCOLOR.r) + (1.0 - light * LIGHTCOLOR.r) * (1.0 - color.a); + float light_g = (light * LIGHTCOLOR.g) + (1.0 - light * LIGHTCOLOR.g) * (1.0 - color.a); + float light_b = (light * LIGHTCOLOR.b) + (1.0 - light * LIGHTCOLOR.b) * (1.0 - color.a); + gl_FragColor = vec4(lift + color.r * light_r, lift + color.g * light_g, lift + color.b * light_b, 1.0); - } + } - if (lift > 0.2) { - gl_FragColor.r = gl_FragColor.r * 0.6; - gl_FragColor.g = gl_FragColor.g * 0.7; - } - }` + if (lift > 0.2) { + gl_FragColor.r = gl_FragColor.r * 0.6; + gl_FragColor.g = gl_FragColor.g * 0.7; + } + }` diff --git a/src/systems/minecraft/versionManager.ts b/src/systems/minecraft/versionManager.ts index 035ab593..0cc7ede3 100644 --- a/src/systems/minecraft/versionManager.ts +++ b/src/systems/minecraft/versionManager.ts @@ -37,7 +37,7 @@ export async function getLatestVersion() { `Failed to fetch latest Minecraft version manifest: ${error.message as string}` ) } - if (response && response.ok) { + if (response?.ok) { const result: IMinecraftVersionManifest = await response.json() const version = result.versions.find( (v: IMinecraftVersion) => v.id === result.latest.snapshot diff --git a/src/systems/resourcepackCompiler/1.20.4.ts b/src/systems/resourcepackCompiler/1.20.4.ts index f8cd7a3d..8c3ab13c 100644 --- a/src/systems/resourcepackCompiler/1.20.4.ts +++ b/src/systems/resourcepackCompiler/1.20.4.ts @@ -1,8 +1,8 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' -import { ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes } from '../rigRenderer' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' +import type { ITextureAtlas } from '../minecraft/textureAtlas' +import type { IRenderedNodes } from '../rigRenderer' import { sortObjectKeys } from '../util' interface IPredicateItemModel { @@ -16,12 +16,13 @@ interface IPredicateItemModel { } class PredicateItemModel { - public lastOverrideId = 1 private overrides = new Map() private externalOverrides = new Map() - public rigs: Record = {} - public parent? = 'item/generated' - public textures: IPredicateItemModel['textures'] = {} + + lastOverrideId = 1 + rigs: Record = {} + parent? = 'item/generated' + textures: IPredicateItemModel['textures'] = {} setOverride(id: number, model: string) { this.overrides.set(id, model) @@ -130,6 +131,7 @@ const compileResourcePack: ResourcePackCompiler = async ({ coreFiles, versionedFiles, rig, + resourcePackPath, displayItemPath, textureExportFolder, modelExportFolder, @@ -149,9 +151,10 @@ const compileResourcePack: ResourcePackCompiler = async ({ // Display Item const displayItemModel = new PredicateItemModel() - if (fs.existsSync(displayItemPath)) { + const absoluteDisplayItemPath = PathModule.join(resourcePackPath, displayItemPath) + if (fs.existsSync(absoluteDisplayItemPath)) { console.warn('Display item already exists! Attempting to merge...') - displayItemModel.readExisting(displayItemPath) + displayItemModel.readExisting(absoluteDisplayItemPath) } displayItemModel.lastOverrideId = Math.max( @@ -186,13 +189,16 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) - if (!texture.name.endsWith('.png')) textureName += '.png' + let textureName = texture.name.replace(/\.png$/, '') + textureName = sanitizeStorageKey(textureName) + '.png' + versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) + if (mcmeta !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '.mcmeta'), { content: mcmeta, }) + if (optifineEmissive !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '_e.png'), { content: optifineEmissive, diff --git a/src/systems/resourcepackCompiler/1.21.2.ts b/src/systems/resourcepackCompiler/1.21.2.ts index 9c79cb38..12920337 100644 --- a/src/systems/resourcepackCompiler/1.21.2.ts +++ b/src/systems/resourcepackCompiler/1.21.2.ts @@ -1,9 +1,9 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { safeReadSync } from '../../util/fileUtil' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' import { type ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes } from '../rigRenderer' +import type { IRenderedNodes } from '../rigRenderer' const compileResourcePack: ResourcePackCompiler = async ({ coreFiles, @@ -84,13 +84,16 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) - if (!texture.name.endsWith('.png')) textureName += '.png' + let textureName = texture.name.replace(/\.png$/, '') + textureName = sanitizeStorageKey(textureName) + '.png' + versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) + if (mcmeta !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '.mcmeta'), { content: mcmeta, }) + if (optifineEmissive !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '_e.png'), { content: optifineEmissive, diff --git a/src/systems/resourcepackCompiler/1.21.4.ts b/src/systems/resourcepackCompiler/1.21.4.ts index a1e4fb4e..8c30f00c 100644 --- a/src/systems/resourcepackCompiler/1.21.4.ts +++ b/src/systems/resourcepackCompiler/1.21.4.ts @@ -1,10 +1,10 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' import { Variant } from '../../variants' -import { IItemDefinition } from '../minecraft/itemDefinitions' +import type { IItemDefinition } from '../minecraft/itemDefinitions' import { type ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes, IRenderedRig, IRenderedVariantModel } from '../rigRenderer' +import type { IRenderedNodes, IRenderedRig, IRenderedVariantModel } from '../rigRenderer' const compileResourcePack: ResourcePackCompiler = async ({ coreFiles, @@ -22,7 +22,6 @@ const compileResourcePack: ResourcePackCompiler = async ({ modelExportFolder, }) - const globalModelsFolder = PathModule.join('assets/animated_java/models/') const itemModelDefinitionsFolder = PathModule.join( 'assets/animated_java/items/blueprint/', aj.export_namespace @@ -59,17 +58,6 @@ const compileResourcePack: ResourcePackCompiler = async ({ content: autoStringify(blockAtlas), }) - // Empty - versionedFiles.set(PathModule.join(globalModelsFolder, 'empty.json'), { content: '{}' }) - versionedFiles.set(PathModule.join('assets/animated_java/items', 'empty.json'), { - content: autoStringify({ - model: { - type: 'minecraft:model', - model: 'animated_java:empty', - }, - }), - }) - // Textures for (const texture of Object.values(rig.textures)) { let image: Buffer | undefined @@ -94,13 +82,16 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) - if (!texture.name.endsWith('.png')) textureName += '.png' + let textureName = texture.name.replace(/\.png$/, '') + textureName = sanitizeStorageKey(textureName) + '.png' + versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) + if (mcmeta !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '.mcmeta'), { content: mcmeta, }) + if (optifineEmissive !== undefined) versionedFiles.set(PathModule.join(textureExportFolder, textureName + '_e.png'), { content: optifineEmissive, @@ -168,10 +159,7 @@ function createMultiVariantItemDefinition( cases: [ { when: 'AJ_INTERNAL_EMPTY', - model: { - type: 'minecraft:model', - model: 'animated_java:empty', - }, + model: { type: 'minecraft:empty' }, }, ], fallback: { diff --git a/src/systems/resourcepackCompiler/index.ts b/src/systems/resourcepackCompiler/index.ts index a81db639..d80d0f10 100644 --- a/src/systems/resourcepackCompiler/index.ts +++ b/src/systems/resourcepackCompiler/index.ts @@ -1,21 +1,25 @@ import { MAX_PROGRESS, PROGRESS, PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' -import { getResourcePackFormat } from '../../util/minecraftUtil' +import { getNextSupportedVersion, getResourcePackFormat } from '../../util/minecraftUtil' import { IntentionalExportError } from '../exporter' import { type IRenderedRig } from '../rigRenderer' -import { ExportedFile } from '../util' - -import { AJMeta, MinecraftVersion, PackMeta, PackMetaFormats } from '../global' -import _1_20_4 from './1.20.4' -import _1_21_2 from './1.21.2' -import _1_21_4 from './1.21.4' - -const VERSIONS = { - '1.20.4': _1_20_4, - '1.20.5': _1_20_4, - '1.21.0': _1_20_4, - '1.21.2': _1_21_2, - '1.21.4': _1_21_4, - '1.21.5': _1_21_4, +import type { ExportedFile } from '../util' + +import { AJMeta, PackMeta, SUPPORTED_MINECRAFT_VERSIONS } from '../global' +import EXPORT_1_20_4 from './1.20.4' +import EXPORT_1_21_2 from './1.21.2' +import EXPORT_1_21_4 from './1.21.4' + +const VERSIONED_RESOURCE_PACK_COMPILERS: Record< + SUPPORTED_MINECRAFT_VERSIONS, + ResourcePackCompiler +> = { + '1.21.9': EXPORT_1_21_4, + '1.21.6': EXPORT_1_21_4, + '1.21.5': EXPORT_1_21_4, + '1.21.4': EXPORT_1_21_4, + '1.21.2': EXPORT_1_21_2, + '1.20.5': EXPORT_1_20_4, + '1.20.4': EXPORT_1_20_4, } interface ResourcePackCompilerOptions { @@ -23,9 +27,11 @@ interface ResourcePackCompilerOptions { coreFiles: Map versionedFiles: Map rig: IRenderedRig + resourcePackPath: string displayItemPath: string textureExportFolder: string modelExportFolder: string + debugMode: boolean } export type ResourcePackCompiler = (options: ResourcePackCompilerOptions) => Promise @@ -36,10 +42,11 @@ export interface CompileResourcePackOptions { resourcePackFolder: string textureExportFolder: string modelExportFolder: string + debugMode: boolean } export default async function compileResourcePack( - targetVersions: MinecraftVersion[], + targetVersions: SUPPORTED_MINECRAFT_VERSIONS[], options: CompileResourcePackOptions ) { const aj = Project!.animated_java @@ -51,7 +58,7 @@ export default async function compileResourcePack( options.resourcePackFolder ) - if (aj.resource_pack_export_mode === 'raw') { + if (aj.resource_pack_export_mode === 'folder') { ajmeta.read() } @@ -73,25 +80,26 @@ export default async function compileResourcePack( : options.resourcePackFolder // Move paths into versioned overlay folders. - await VERSIONS[version]({ + await VERSIONED_RESOURCE_PACK_COMPILERS[version]({ ...options, + resourcePackPath: versionedResourcePackFolder, ajmeta, coreFiles, versionedFiles, }) - for (let [path, file] of coreFiles) { - path = PathModule.join(coreResourcePackFolder, path) - globalCoreFiles.set(path, file) + for (const [path, file] of coreFiles) { + const relative = PathModule.join(coreResourcePackFolder, path) + globalCoreFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.coreFiles.add(path) + ajmeta.coreFiles.add(relative) } - for (let [path, file] of versionedFiles) { - path = PathModule.join(versionedResourcePackFolder, path) - globalVersionSpecificFiles.set(path, file) + for (const [path, file] of versionedFiles) { + const relative = PathModule.join(versionedResourcePackFolder, path) + globalVersionSpecificFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.versionedFiles.add(path) + ajmeta.versionedFiles.add(relative) } console.groupEnd() @@ -101,35 +109,50 @@ export default async function compileResourcePack( // pack.mcmeta const packMetaPath = PathModule.join(options.resourcePackFolder, 'pack.mcmeta') - const packMeta = new PackMeta( - packMetaPath, - 0, - [], - `Animated Java Resource Pack for ${targetVersions.join(', ')}` - ) - packMeta.read() - packMeta.pack_format = getResourcePackFormat(targetVersions[0]) - packMeta.supportedFormats = [] - - if (targetVersions.length > 1) { - for (const version of targetVersions) { - let format: PackMetaFormats = getResourcePackFormat(version) - packMeta.supportedFormats.push(format) - - const existingOverlay = [...packMeta.overlayEntries].find( - e => e.directory === `animated_java_${version.replaceAll('.', '_')}` - ) - if (!existingOverlay) { - packMeta.overlayEntries.add({ - directory: `animated_java_${version.replaceAll('.', '_')}`, - formats: format, - }) - } else { - existingOverlay.formats = format - } + const packMeta = PackMeta.fromFile(packMetaPath) + packMeta.content.pack ??= {} + + const nextVersion = getNextSupportedVersion(targetVersions[0]) + const format = getResourcePackFormat(targetVersions[0]) + const nextFormat = nextVersion ? getResourcePackFormat(nextVersion) : 10000000 + if (!compareVersions('1.21.9', targetVersions[0]) /* >= 1.21.9 */) { + packMeta.content.pack.min_format = format + packMeta.content.pack.max_format = nextFormat - 1 + } else { + packMeta.content.pack.pack_format = format + packMeta.content.pack.supported_formats = { + min_inclusive: format, + max_inclusive: nextFormat - 1, } } + packMeta.content.pack.description ??= `Animated Java Resource Pack for ${targetVersions.join( + ', ' + )}` + + // if (targetVersions.length > 1) { + // packMeta.content.pack.supported_formats ??= [] + // packMeta.content.overlays ??= {} + // packMeta.content.overlays.entries ??= [] + + // for (const version of targetVersions) { + // const format: PackMetaFormats = getResourcePackFormat(version) + // packMeta.content.pack.supported_formats.push(format) + + // const existingOverlay = packMeta.content.overlays.entries.find( + // e => e.directory === `animated_java_${version.replaceAll('.', '_')}` + // ) + // if (!existingOverlay) { + // packMeta.content.overlays.entries.push({ + // directory: `animated_java_${version.replaceAll('.', '_')}`, + // formats: format, + // }) + // } else { + // existingOverlay.formats = format + // } + // } + // } + globalCoreFiles.set(PathModule.join(options.resourcePackFolder, 'pack.mcmeta'), { content: autoStringify(packMeta.toJSON()), }) @@ -137,7 +160,7 @@ export default async function compileResourcePack( if (aj.enable_plugin_mode) { // Do nothing console.log('Plugin mode enabled. Skipping resource pack export.') - } else if (aj.resource_pack_export_mode === 'raw') { + } else if (aj.resource_pack_export_mode === 'folder') { // Clean up old files PROGRESS_DESCRIPTION.set('Removing Old Resource Pack Files...') PROGRESS.set(0) @@ -183,37 +206,16 @@ export default async function compileResourcePack( if (file.writeHandler) { await file.writeHandler(path, file.content) } else { - await fs.promises.writeFile(path, file.content) + await fs.promises.writeFile( + path, + new Uint8Array( + Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content) + ) + ) } PROGRESS.set(PROGRESS.get() + 1) } } else if (aj.resource_pack_export_mode === 'zip') { throw new IntentionalExportError('ZIP export is not yet implemented.') - // exportedFiles.set( - // PathModule.join(options.resourcePackFolder, 'pack.mcmeta'), - // autoStringify({ - // pack: { - // // FIXME - This should be a setting - // pack_format: 32, - // description: `${Project!.name}. Generated with Animated Java`, - // }, - // }) - // ) - - // PROGRESS_DESCRIPTION.set('Writing Resource Pack Zip...') - // const data: Record = {} - // for (const [path, file] of exportedFiles) { - // const relativePath = PathModule.relative(options.resourcePackFolder, path) - // if (typeof file === 'string') { - // data[relativePath] = Buffer.from(file) - // } else { - // data[relativePath] = file - // } - // } - // const zipped = await zip(data, {}) - // await fs.promises.writeFile( - // options.resourcePackFolder + (options.resourcePackFolder.endsWith('.zip') ? '' : '.zip'), - // zipped - // ) } } diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index 7bdcfbb8..39dd2609 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -1,31 +1,29 @@ import * as crypto from 'crypto' -import { - IBlueprintCameraConfigJSON, +import type { + IBlueprintBoneConfigJSON, IBlueprintLocatorConfigJSON, IBlueprintTextDisplayConfigJSON, IBlueprintVariantJSON, - type IBlueprintBoneConfigJSON, -} from '../blueprintFormat' +} from '../formats/blueprint' import { BoneConfig } from '../nodeConfigs' -import { Alignment, TextDisplay } from '../outliner/textDisplay' +import { type Alignment, TextDisplay } from '../outliner/textDisplay' import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' -import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' +import { type ItemDisplayMode, VanillaItemDisplay } from '../outliner/vanillaItemDisplay' import { - IMinecraftResourceLocation, + type IMinecraftResourceLocation, parseResourcePackPath, - sanitizePathName, sanitizeStorageKey, } from '../util/minecraftUtil' import { Variant } from '../variants' import { correctSceneAngle, getFrame, + type INodeTransform, restoreSceneAngle, updatePreview, - type INodeTransform, } from './animationRenderer' import { IntentionalExportError } from './exporter' -import { JsonText } from './minecraft/jsonText' +import { JsonText } from './jsonText' export interface IRenderedFace { uv: number[] @@ -48,6 +46,7 @@ export interface IRenderedElement { } | number[] faces?: Record + light_emission?: number } /** @@ -66,8 +65,6 @@ export interface IRenderedNode { type: string /** The origin name of the node */ name: string - /** A sanitized version of {@link IRenderedNode.name} that is safe to use in a path in a data pack or resource pack.*/ - path_name: string /** A sanitized version of {@link IRenderedNode.name} that is safe to use as a key in a storage object. */ storage_name: string /** @@ -92,7 +89,6 @@ export interface ICamera extends OutlinerElement { linked_preview: string camera_linked: boolean visibility: boolean - config: IBlueprintCameraConfigJSON preview_controller: NodePreviewController } @@ -114,7 +110,6 @@ export interface IRenderedNodes { } Camera: IRenderedNode & { type: 'camera' - config?: IBlueprintCameraConfigJSON /** The maximum distance this node travels away from the root entity while animating. */ max_distance: number } @@ -126,7 +121,7 @@ export interface IRenderedNodes { } TextDisplay: IRenderedNode & { type: 'text_display' - text?: JsonText + text: string line_width: number background_color: string background_alpha: number @@ -142,7 +137,7 @@ export interface IRenderedNodes { ItemDisplay: IRenderedNode & { type: 'item_display' item: string - item_display: string + item_display: ItemDisplayMode /** * The base scale of the bone, used to offset any rescaling done to the bone's model due to exceeding the 3x3x3 model size limit. */ @@ -230,18 +225,16 @@ function renderCube(cube: Cube, rig: IRenderedRig, model: IRenderedModel) { axis, origin: cube.origin, } - } - - if (cube.rescale) { - // @ts-ignore - if (element.rotation) element.rotation.rescale = true - else - element.rotation = { - angle: 0, - axis: cube.rotation_axis || 'y', - origin: cube.origin, - rescale: true, - } + if (cube.rescale) { + element.rotation.rescale = true + } + } else if (cube.rescale) { + element.rotation = { + angle: 0, + axis: cube.rotation_axis || 'y', + origin: cube.origin, + rescale: true, + } } if (cube.parent instanceof Group) { @@ -277,6 +270,11 @@ function renderCube(cube: Cube, rig: IRenderedRig, model: IRenderedModel) { } if (Object.keys(element.faces).length === 0) return + + if (cube.light_emission) { + element.light_emission = cube.light_emission + } + model.elements ??= [] model.elements.push(element) } @@ -286,16 +284,24 @@ export function getTextureResourceLocation(texture: Texture, rig: IRenderedRig) if (TEXTURE_RESOURCE_LOCATION_CACHE.has(texture.uuid)) { return TEXTURE_RESOURCE_LOCATION_CACHE.get(texture.uuid)! } - if (!texture.name.endsWith('.png')) texture.name += '.png' + + let textureName = texture.name.replace(/\.png$/, '') + textureName = sanitizeStorageKey(textureName) + '.png' + if (texture.path && fs.existsSync(texture.path) && fs.statSync(texture.path).isFile()) { const parsed = parseResourcePackPath(texture.path) if (parsed) { TEXTURE_RESOURCE_LOCATION_CACHE.set(texture.uuid, parsed) return parsed } + console.warn( + `Texture ${texture.name} has a custom path that is not in a valid resource pack location: ${texture.path}` + ) } - const path = PathModule.join(rig.texture_export_folder, sanitizePathName(texture.name)) + + const path = PathModule.join(rig.texture_export_folder, textureName) const parsed = parseResourcePackPath(path) + if (parsed) { TEXTURE_RESOURCE_LOCATION_CACHE.set(texture.uuid, parsed) return parsed @@ -347,7 +353,6 @@ function renderGroup( const renderedBone: IRenderedNodes['Bone'] = { type: 'bone', name: group.name, - path_name: sanitizePathName(group.name), storage_name: sanitizeStorageKey(group.name), uuid: group.uuid, parent: parentId, @@ -410,12 +415,11 @@ function renderGroup( } // Export a struct instead of a bone if no elements are present - if (!groupModel.model || !groupModel.model.elements || groupModel.model.elements.length === 0) { + if (!groupModel.model?.elements || groupModel.model.elements.length === 0) { delete defaultVariant.models[group.uuid] const struct: IRenderedNodes['Struct'] = { type: 'struct', name: group.name, - path_name: sanitizePathName(group.name), storage_name: sanitizeStorageKey(group.name), uuid: group.uuid, parent: parentId, @@ -458,7 +462,6 @@ function renderItemDisplay(display: VanillaItemDisplay, rig: IRenderedRig) { const renderedBone: IRenderedNodes['ItemDisplay'] = { type: 'item_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, parent: parentId, @@ -487,7 +490,6 @@ function renderBlockDisplay(display: VanillaBlockDisplay, rig: IRenderedRig) { const renderedBone: IRenderedNodes['BlockDisplay'] = { type: 'block_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, block: display.block, @@ -512,17 +514,17 @@ function renderTextDisplay(display: TextDisplay, rig: IRenderedRig): INodeStruct throw new Error(`Invalid bone path: ${display.name} -> ${path}`) } + const backgroundColor = tinycolor(display.backgroundColor) const renderedBone: IRenderedNodes['TextDisplay'] = { type: 'text_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, parent: parentId, - text: JsonText.fromString(display.text), + text: display.text, line_width: display.lineWidth, - background_color: display.backgroundColor, - background_alpha: display.backgroundAlpha, + background_color: JsonText.moveHex8AlphaToStart(backgroundColor.toHex8String()), + background_alpha: backgroundColor.getAlpha(), align: display.align, shadow: display.shadow, see_through: display.seeThrough, @@ -545,7 +547,6 @@ function renderLocator(locator: Locator, rig: IRenderedRig) { const renderedLocator: IRenderedNodes['Locator'] = { type: 'locator', name: locator.name, - path_name: sanitizePathName(locator.name), storage_name: sanitizeStorageKey(locator.name), uuid: locator.uuid, parent: parentId, @@ -564,11 +565,9 @@ function renderCamera(camera: ICamera, rig: IRenderedRig) { const renderedCamera: IRenderedNodes['Camera'] = { type: 'camera', name: camera.name, - path_name: sanitizePathName(camera.name), storage_name: sanitizeStorageKey(camera.name), uuid: camera.uuid, parent: parentId, - config: camera.config, max_distance: 0, default_transform: {} as INodeTransform, } @@ -614,15 +613,15 @@ function renderVariantModels(variant: Variant, rig: IRenderedRig) { continue } - const modelParent = PathModule.join(rig.model_export_folder, bone.path_name + '.json') + const modelParent = PathModule.join(rig.model_export_folder, bone.storage_name + '.json') const parsed = parseResourcePackPath(modelParent) if (!parsed) { - throw new Error(`Invalid Bone Name: '${bone.path_name}' -> '${modelParent}'`) + throw new Error(`Invalid Bone Name: '${bone.storage_name}' -> '${modelParent}'`) } const modelPath = variant.isDefault - ? PathModule.join(rig.model_export_folder, bone.path_name + '.json') - : PathModule.join(rig.model_export_folder, variant.name, bone.path_name + '.json') + ? PathModule.join(rig.model_export_folder, bone.storage_name + '.json') + : PathModule.join(rig.model_export_folder, variant.name, bone.storage_name + '.json') const parsedModelPath = parseResourcePackPath(modelPath) if (!parsedModelPath) { throw new Error(`Invalid Variant Name: '${variant.name}' -> '${modelPath}'`) @@ -677,14 +676,10 @@ export function hashRig(rig: IRenderedRig) { } break } - case 'camera': { - if (node.config) { - hash.update(';' + JSON.stringify(node.config)) - } + case 'camera': break - } case 'text_display': { - hash.update(`;${node.text?.toString() as string}`) + hash.update(`;${node.text}`) if (node.config) { hash.update(';' + JSON.stringify(node.config)) } @@ -740,7 +735,6 @@ export function renderRig(modelExportFolder: string, textureExportFolder: string } case node instanceof Locator: { renderLocator(node, rig) - break } case node instanceof TextDisplay: { diff --git a/src/systems/util.ts b/src/systems/util.ts index 6cf5e43f..427793ee 100644 --- a/src/systems/util.ts +++ b/src/systems/util.ts @@ -1,14 +1,15 @@ import { NbtCompound, NbtFloat, NbtList } from 'deepslate/lib/nbt' import { - AsyncZipOptions, - AsyncZippable, unzip as cbUnzip, zip as cbZip, type AsyncUnzipOptions, + type AsyncZipOptions, + type AsyncZippable, type Unzipped, } from 'fflate/browser' +import { projectTargetVersionIsAtLeast } from 'src/formats/blueprint' import { roundTo } from '../util/misc' -import { INodeTransform } from './animationRenderer' +import type { INodeTransform } from './animationRenderer' export interface ExportedFile { content: string | Buffer @@ -91,33 +92,28 @@ export const unzip = (data: Uint8Array, options: AsyncUnzipOptions) => { }) } -export function isCubeValid(cube: Cube) { - // Cube is automatically valid if it has no rotation - if (cube.rotation[0] === 0 && cube.rotation[1] === 0 && cube.rotation[2] === 0) { - return true - } - const rotation = cube.rotation[0] + cube.rotation[1] + cube.rotation[2] - // prettier-ignore - if ( - // Make sure the cube is rotated in only one axis by adding all the rotations together, and checking if the sum is equal to one of the rotations. - ( - rotation === cube.rotation[0] || - rotation === cube.rotation[1] || - rotation === cube.rotation[2] - ) - && - // Make sure the cube is rotated in one of the allowed 22.5 degree increments - ( - rotation === -45 || - rotation === -22.5 || - rotation === 0 || - rotation === 22.5 || - rotation === 45 - ) - ) { - return true - } - return false +export function isCubeValid(cube: Cube): '1.21.6+' | 'valid' | 'invalid' { + const totalRotation = cube.rotation[0] + cube.rotation[1] + cube.rotation[2] + + if (totalRotation === 0) return 'valid' + + const isSingleAxisRotation = + totalRotation === cube.rotation[0] || + totalRotation === cube.rotation[1] || + totalRotation === cube.rotation[2] + + if (isSingleAxisRotation && projectTargetVersionIsAtLeast('1.21.4')) return '1.21.6+' + + const isRotationInAllowedSteps = + totalRotation === -45 || + totalRotation === -22.5 || + totalRotation === 0 || + totalRotation === 22.5 || + totalRotation === 45 + + if (isSingleAxisRotation && isRotationInAllowedSteps) return 'valid' + + return 'invalid' } export function getFunctionNamespace(version: string): 'function' | 'functions' { diff --git a/src/tests/jsonText.test.ts b/src/tests/jsonText.test.ts new file mode 100644 index 00000000..c737e1f6 --- /dev/null +++ b/src/tests/jsonText.test.ts @@ -0,0 +1,1013 @@ +import { COLOR_VALUES, JsonText, type TextElement } from 'src/systems/jsonText' +import { describe, expect, test } from 'vitest' +import { JsonTextParser, JsonTextSyntaxError } from '../systems/jsonText/parser' + +type ErrorPattern = string | RegExp | (new (...args: any[]) => Error) +type InputOutputMap = Array<{ input: TInput | TInput[]; expect: TOutput }> + +function text(text: TextElement) { + return new JsonText(text) +} + +function expectParsingResults( + parser: JsonTextParser['parse'], + map: InputOutputMap +) { + for (const { input, expect: expectedResult } of map) { + if (Array.isArray(input)) { + for (const inputItem of input) { + expect(parser(inputItem)).toEqual(text(expectedResult)) + } + } else { + expect(parser(input)).toEqual(text(expectedResult)) + } + } +} + +function expectErrorResults( + parser: JsonTextParser['parse'], + map: InputOutputMap +) { + for (const { input, expect: error } of map) { + if (Array.isArray(input)) { + for (const inputItem of input) { + expect(() => parser(inputItem)).toThrowError(error) + } + } else { + expect(() => parser(input)).toThrowError(error) + } + } +} + +describe('JsonTextParser', () => { + const modernParser = new JsonTextParser() + const modernParse = modernParser.parse.bind(modernParser) + const legacyParser = new JsonTextParser({ minecraftVersion: '1.20.4' }) + const legacyParse = legacyParser.parse.bind(legacyParser) + + describe('parses TextObjects', () => { + describe('text field', () => { + test('basic', () => { + expectParsingResults(modernParse, [ + { + input: [ + `{"text":"Hello, World!"}`, + `{'text':'Hello, World!'}`, + `{text:"Hello, World!"}`, + ` { text: "Hello, World!" } `, + `{\n\ttext: "Hello, World!"\n}`, + ], + expect: { text: 'Hello, World!' }, + }, + ]) + }) + + test('optional commas', () => { + expectParsingResults(modernParse, [ + { + input: [`{"text":"Hello, World!" color:red bold:true}`], + expect: { text: 'Hello, World!', color: 'red', bold: true }, + }, + ]) + }) + + test('inferred', () => { + expectParsingResults(modernParse, [ + { + input: [ + `{"Hello, World!"}`, + ` { "Hello, World!" } `, + `{\n\t'Hello, World!'\n}`, + ], + expect: { text: 'Hello, World!' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{Hello, World}`, + expect: /Unknown color 'World'/, + }, + { + input: `{color: red, Hello, World}`, + expect: /Unknown key 'World'/, + }, + ]) + }) + + test('implicit', () => { + expect(modernParse(`{}`)).toEqual(text({ text: '' })) + }) + }) + + describe('color field', () => { + test('named colors', () => { + for (const color of Object.keys(COLOR_VALUES)) { + const result = modernParse(`{color:${color}}`) + const expected = text({ color: color as keyof typeof COLOR_VALUES, text: '' }) + expect(result).toEqual(expected) + } + }) + + test('hex colors', () => { + const validCases: Array<[string, TextElement]> = [ + ['{color:"#00aced"}', { color: '#00aced', text: '' }], + ['{color:"#FF5555"}', { color: '#FF5555', text: '' }], + ] + for (const [input, expected] of validCases) { + expect(modernParse(input)).toEqual(text(expected)) + } + + expectErrorResults(modernParse, [ + { + input: ['{color:"#FF55550"}', '{color:"#FF555"}'], + expect: /Invalid hex color/, + }, + { + input: '{color:#FF5555}', + expect: /Expected \[a\-zA\-Z0\-9_\] to start literal string/, + }, + ]) + }) + + test('inferred', () => { + expectParsingResults(modernParse, [ + { input: '{#00aced}', expect: { color: '#00aced', text: '' } }, + { input: '{0xFF5555}', expect: { color: '#ff5555', text: '' } }, + { + input: '{"Hello, World!" #00aced}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" 0xFF5555}', + expect: { color: '#ff5555', text: 'Hello, World!' }, + }, + { + input: '{#00aced "Hello, World!"}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{0xFF5555 "Hello, World!"}', + expect: { color: '#ff5555', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" "#00aced"}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" red}', + expect: { color: 'red', text: 'Hello, World!' }, + }, + ]) + + expectErrorResults(modernParse, [ + { + input: [ + '{#FF55550}', + '{#FF555}', + '{0xFF555}', + '{color:red, #FF5555}', + '{color:red, 0xFF5555}', + '{"#FF5555", "Hello, World!"}', + '{red, "Hello, World!"}', + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + }) + + test('shadow_color field', () => { + expectParsingResults(modernParse, [ + { + input: `{shadow_color:"#ff00aced"}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:0xff00aced}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:[${JsonText.hexToRgba('#ff00aced')}]}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:${JsonText.hexToInt('#ff00aced')}}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + ]) + }) + + test('boolean style fields', () => { + const fields = ['bold', 'italic', 'underlined', 'strikethrough', 'obfuscated'] as const + + const cases = [ + { value: true, variants: ['true', 'TRUE'] }, + { value: false, variants: ['false', 'FALSE'] }, + ] + + for (const field of fields) { + for (const { value, variants } of cases) { + for (const variant of variants) { + const input = `{${field}:${variant}}` + const expected = text({ [field]: value, text: '' }) + expect(modernParse(input)).toEqual(expected) + } + } + } + + // Test invalid values + expectErrorResults(modernParse, [ + { + input: [ + '{bold:yes}', + '{italic:no}', + '{underlined:1}', + '{strikethrough:0}', + '{obfuscated:null}', + '{bold:"true"}', + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('font field', () => { + expectParsingResults(modernParse, [ + { + input: `{font:"minecraft:illager"}`, + expect: { font: 'minecraft:illager', text: '' }, + }, + ]) + }) + + test('keybind field', () => { + expectParsingResults(modernParse, [ + { + input: `{keybind:"key.jump"}`, + expect: { keybind: 'key.jump' }, + }, + ]) + }) + + test('extra field', () => { + expectParsingResults(modernParse, [ + { + input: `{extra:["some_extra_text", {"and a text object"}]}`, + expect: { extra: ['some_extra_text', { text: 'and a text object' }], text: '' }, + }, + { + input: `{extra:[{extra:["some_extra_text", {"and a text object"}]}]}`, + expect: { + extra: [ + { extra: ['some_extra_text', { text: 'and a text object' }], text: '' }, + ], + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{extra:"not-an-array"}`, + expect: /Expected '\[' to begin TextElementArray/, + }, + ]) + }) + + test('insertion field', () => { + expectParsingResults(modernParse, [ + { + input: `{insertion:'some_text'}`, + expect: { insertion: 'some_text', text: '' }, + }, + ]) + }) + + test('translate field', () => { + expectParsingResults(modernParse, [ + { + input: `{translate:'my.translation.key'}`, + expect: { translate: 'my.translation.key' }, + }, + { + input: [ + `{translate:'my.translation.key', with:['arg1','arg2']}`, + `{'my.translation.key', with:['arg1','arg2']}`, + ], + expect: { translate: 'my.translation.key', with: ['arg1', 'arg2'] }, + }, + { + input: [ + `{translate:'my.translation.key', fallback:'fallback text'}`, + `{'my.translation.key', fallback:'fallback text'}`, + ], + expect: { translate: 'my.translation.key', fallback: 'fallback text' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{with:['arg1','arg2']}`, + expect: /'with' requires 'translate'/, + }, + { + input: `{fallback:'fallback text'}`, + expect: /'fallback' requires 'translate'/, + }, + ]) + }) + + test('score field', () => { + expectParsingResults(modernParse, [ + { + input: `{score:{name:'Player',objective:'obj1'}}`, + expect: { score: { name: 'Player', objective: 'obj1' } }, + }, + ]) + }) + + test('selector field', () => { + expectParsingResults(modernParse, [ + { + input: `{selector:'@e'}`, + expect: { selector: '@e' }, + }, + { + input: `{selector:'@e',separator:{', '}}`, + expect: { selector: '@e', separator: { text: ', ' } }, + }, + { + input: `{selector:'@e',separator:', '}`, + expect: { selector: '@e', separator: ', ' }, + }, + ]) + }) + + test('nbt field', () => { + expectParsingResults(modernParse, [ + { + input: `{nbt:'Items[]',block:'0 0 0', interpret:true,separator:', '}`, + expect: { nbt: 'Items[]', block: '0 0 0', interpret: true, separator: ', ' }, + }, + { + input: `{nbt:'SelectedItem',entity:'@s', interpret:false,separator:', '}`, + expect: { + nbt: 'SelectedItem', + entity: '@s', + interpret: false, + separator: ', ', + }, + }, + { + input: `{nbt:'temp[{id:2}]',storage:'animated_java:test'}`, + expect: { nbt: 'temp[{id:2}]', storage: 'animated_java:test' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{nbt:'Items[]'}`, + expect: /'nbt' requires 'block', 'entity', or 'storage'/, + }, + { + input: `{block:'~ ~ ~'}`, + expect: /'block' requires 'nbt'/, + }, + { + input: `{entity:'@s'}`, + expect: /'entity' requires 'nbt'/, + }, + { + input: `{storage:'animated_java:test'}`, + expect: /'storage' requires 'nbt'/, + }, + ]) + }) + + test('separator field', () => { + expectErrorResults(modernParse, [ + { + input: `{separator:', '}`, + expect: /'separator' requires 'nbt' or 'selector'/, + }, + ]) + }) + + test('object field', () => { + expectErrorResults(modernParse, [ + { + input: `{object:'atlas'}`, + expect: /atlas object requires 'sprite'/, + }, + { + input: `{object:'player'}`, + expect: /player object requires 'player'/, + }, + ]) + }) + + test('sprite field', () => { + expectParsingResults(modernParse, [ + { + input: `{sprite:'minecraft:stone'}`, + expect: { sprite: 'minecraft:stone' }, + }, + { + input: `{sprite:'minecraft:diamond'}`, + expect: { sprite: 'minecraft:diamond' }, + }, + ]) + }) + + test('atlas object field', () => { + expectErrorResults(modernParse, [ + { + input: `{atlas:'minecraft:items'}`, + expect: /'atlas' requires 'sprite'/, + }, + ]) + }) + + test('player object field', () => { + expectParsingResults(modernParse, [ + { + input: `{player:{}}`, + expect: { player: {} as any }, + }, + { + input: `{player:{name:'SnaveSutit',id:'0-0-0-0-0',texture:'some:texture/path',cape:'some:cape/path',model:'wide',hat:true}}`, + expect: { + player: { + name: 'SnaveSutit', + id: '0-0-0-0-0', + texture: 'some:texture/path', + cape: 'some:cape/path', + model: 'wide', + hat: true, + properties: undefined, + }, + }, + }, + { + input: `{player:{name:'SnaveSutit',properties:[{name:'textures',value:'some_base64_value',signature:'some_base64_signature'}]}}`, + expect: { + player: { + name: 'SnaveSutit', + properties: [ + { + name: 'textures', + value: 'some_base64_value', + signature: 'some_base64_signature', + }, + ], + }, + }, + }, + ]) + }) + + test('legacy clickEvent field', () => { + expectParsingResults(legacyParse, [ + { + input: `{clickEvent:{action:'open_url',value:'https://example.com'}}`, + expect: { + clickEvent: { action: 'open_url', value: 'https://example.com' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'run_command',value:'say hi'}}`, + expect: { + clickEvent: { action: 'run_command', value: 'say hi' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'suggest_command',value:'say hi'}}`, + expect: { + clickEvent: { action: 'suggest_command', value: 'say hi' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'change_page',value:'5'}}`, + expect: { + clickEvent: { action: 'change_page', value: '5' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + clickEvent: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + clickEvent: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + ]) + expectErrorResults(legacyParse, [ + { + input: [ + // Cannot use `open_file` in legacy clickEvent + `{clickEvent:{action:'open_file',value:'file:///example/path'}}`, + // Unknown action + `{clickEvent:{action:'unknown_action',value:'test'}}`, + // Missing value + `{clickEvent:{action:'open_url'}}`, + // Missing action + `{clickEvent:{value:'test'}}`, + // Non-object clickEvent + `{clickEvent:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('modern click_event field', () => { + expectParsingResults(modernParse, [ + { + input: `{click_event:{action:'open_url',url:'https://example.com'}}`, + expect: { + click_event: { action: 'open_url', url: 'https://example.com' }, + text: '', + }, + }, + { + input: `{click_event:{action:'run_command',command:'say hi'}}`, + expect: { + click_event: { action: 'run_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'run_command',command:'say hi'}}`, + expect: { + click_event: { action: 'run_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'change_page',page:5}}`, + expect: { + click_event: { action: 'change_page', page: 5 }, + text: '', + }, + }, + { + input: `{click_event:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + click_event: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{click_event:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + click_event: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{click_event:{action:'show_dialog',dialog:{type:'minecraft:notice',title:'test',body:[{type:'plain_message',contents:{text:'test'}}]}}}`, + expect: { + click_event: { + action: 'show_dialog', + dialog: { + type: 'minecraft:notice', + title: 'test', + body: [{ type: 'plain_message', contents: { text: 'test' } }], + }, + }, + text: '', + }, + }, + { + input: `{click_event:{action:'custom',id:'my_custom_action'}}`, + expect: { + click_event: { action: 'custom', id: 'my_custom_action' }, + text: '', + }, + }, + { + input: `{click_event:{action:'custom',id:'my_custom_action',payload:{some:'data'}}}`, + expect: { + click_event: { + action: 'custom', + id: 'my_custom_action', + payload: { some: 'data' }, + }, + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: [ + // Cannot use `open_file` in commands + `{click_event:{action:'open_file',path:'file:///example/path'}}`, + // Unknown action + `{click_event:{action:'unknown_action',foo:'bar'}}`, + // Missing required field for open_url + `{click_event:{action:'open_url'}}`, + // Missing required field for run_command + `{click_event:{action:'run_command'}}`, + // Missing required field for suggest_command + `{click_event:{action:'suggest_command'}}`, + // Missing required field for change_page + `{click_event:{action:'change_page'}}`, + // Missing required field for copy_to_clipboard + `{click_event:{action:'copy_to_clipboard'}}`, + // Non-object click_event + `{click_event:'not-an-object'}`, + // Invalid dialog structure + `{click_event:{action:'show_dialog',dialog:'not-an-object'}}`, + // Custom action missing id + `{click_event:{action:'custom'}}`, + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('legacy hoverEvent field', () => { + expectParsingResults(legacyParse, [ + { + input: `{hoverEvent:{action:'show_text',contents:'Hello!'}}`, + expect: { + hoverEvent: { action: 'show_text', contents: 'Hello!' }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_text',contents:{text:'Hello!'}}}`, + expect: { + hoverEvent: { action: 'show_text', contents: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { id: 'minecraft:stone', count: 1 }, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1,tag:{some:'data'}}}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { id: 'minecraft:stone', count: 1, tag: { some: 'data' } }, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:'0-0-0-0-0',name:'Player'}}}`, + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: '0-0-0-0-0', + name: 'Player', + }, + }, + text: '', + }, + }, + { + input: [ + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[0,0,0,0],name:'Player'}}}`, + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[I;0,0,0,0],name:'Player'}}}`, + ], + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: [0, 0, 0, 0], + name: 'Player', + }, + }, + text: '', + }, + }, + // Transforms modern hover_event into legacy hoverEvent + { + input: `{hover_event:{action:'show_text',value:'Hello!'}}`, + expect: { + hoverEvent: { action: 'show_text', contents: 'Hello!' }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_text',value:{text:'Hello!'}}}`, + expect: { + hoverEvent: { action: 'show_text', contents: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { + id: 'minecraft:stone', + count: 1, + }, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:'0-0-0-0-0',name:'Player'}}`, + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: '0-0-0-0-0', + name: 'Player', + }, + }, + text: '', + }, + }, + ]) + expectErrorResults(legacyParse, [ + { + input: [ + // Unknown action + `{hoverEvent:{action:'unknown_action',value:'test'}}`, + // Missing value for show_text + `{hoverEvent:{action:'show_text'}}`, + // Missing id for show_item + `{hoverEvent:{action:'show_item',count:1}}`, + // Missing type for show_entity + `{hoverEvent:{action:'show_entity',id:'1234',name:'Player'}}`, + // Non-object hoverEvent + `{hoverEvent:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1,components:{'minecraft:custom_data':{some:'data'}}}}`, + expect: /Cannot transform 'hover_event' with 'components' into legacy 'hoverEvent'/, + }, + { + input: [ + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[0,0,0,0],name:'Player'}}`, + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[I;0,0,0,0],name:'Player'}}`, + ], + expect: /Cannot transform 'hover_event' with 'uuid' as int-array into legacy 'hoverEvent'/, + }, + ]) + }) + + test('modern hover_event field', () => { + expectParsingResults(modernParse, [ + { + input: `{hover_event:{action:'show_text',value:'Hello!'}}`, + expect: { + hover_event: { action: 'show_text', value: 'Hello!' }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_text',value:{text:'Hello!'}}}`, + expect: { + hover_event: { action: 'show_text', value: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1,components:{'minecraft:custom_data':{some:'data'}}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + components: { 'minecraft:custom_data': { some: 'data' } }, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:'0-0-0-0-0',name:'Player'}}`, + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: '0-0-0-0-0', + name: 'Player', + }, + text: '', + }, + }, + { + input: [ + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[0,0,0,0],name:'Player'}}`, + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[I;0,0,0,0],name:'Player'}}`, + ], + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: [0, 0, 0, 0], + name: 'Player', + }, + text: '', + }, + }, + // Transforms legacy hoverEvent into modern hover_event + { + input: `{hoverEvent:{action:'show_text',contents:'Hello!'}}`, + expect: { + hover_event: { action: 'show_text', value: 'Hello!' }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_text',contents:{text:'Hello!'}}}`, + expect: { + hover_event: { action: 'show_text', value: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:'0-0-0-0-0',name:'Player'}}}`, + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: '0-0-0-0-0', + name: 'Player', + }, + text: '', + }, + }, + { + input: [ + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[0,0,0,0],name:'Player'}}}`, + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[I;0,0,0,0],name:'Player'}}}`, + ], + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: [0, 0, 0, 0], + name: 'Player', + }, + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: [ + // Unknown action + `{hover_event:{action:'unknown_action',value:'test'}}`, + // Missing value for show_text + `{hover_event:{action:'show_text'}}`, + // Missing id for show_item + `{hover_event:{action:'show_item',count:1}}`, + // Missing id for show_entity + `{hover_event:{action:'show_entity',uuid:'0-0-0-0-0',name:'Player'}}`, + // Non-object hover_event + `{hover_event:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1,tag:{some:'data'}}}}`, + expect: /Cannot transform 'hoverEvent' with 'tag' into modern 'hover_event'/, + }, + ]) + }) + }) + + test('parses TextElement arrays', () => { + expectParsingResults(modernParse, [ + // Simple object arrays + { + input: [ + '[{"text":"Hello, "},{"text":"World!"}]', + '[{text:"Hello, "},{text:"World!"}]', + ' [ { text: "Hello, " }, { text: "World!" } ] ', + `[\n\t{\n\t\ttext: "Hello, "\n\t},\n\t{\n\t\ttext: "World!"\n\t}\n]`, + '[{"Hello, "},{"World!"}]', + ], + expect: [{ text: 'Hello, ' }, { text: 'World!' }], + }, + // Simple string arrays + { + input: [ + '["Hello, ", "World!"]', + '["Hello, " "World!"]', + ' [ "Hello, " "World!" ] ', + `[\n\t"Hello, "\n\t,\n\t"World!"\n]`, + ], + expect: ['Hello, ', 'World!'], + }, + // Nested string arrays + { + input: [ + '["Hello, ", ["Recursive"], "World!"]', + '["Hello, " ["Recursive"] "World!"]', + ' [ "Hello, " ["Recursive"] "World!" ] ', + `[\n\t"Hello, "\n\t,\n\t["Recursive"]\n\t,\n\t"World!"\n]`, + ], + expect: ['Hello, ', ['Recursive'], 'World!'], + }, + // Nested object arrays + { + input: [ + '[{"text":"Hello, "}, [{"text":"Recursive"}], {"text":"World!"}]', + '[{text:"Hello, "}, [{text:"Recursive"}], {text:"World!"}]', + ' [ { text: "Hello, " }, [ { text: "Recursive" } ], { text: "World!" } ] ', + `[\n\t{\n\t\ttext: "Hello, "\n\t},\n\t[\n\t\t{\n\t\t\ttext: "Recursive"\n\t\t}\n\t],\n\t{\n\t\ttext: "World!"\n\t}\n]`, + ], + expect: [{ text: 'Hello, ' }, [{ text: 'Recursive' }], { text: 'World!' }], + }, + // Additional edge cases + { input: [`[]`], expect: [] }, + { input: [`[{}]`], expect: [{ text: '' }] }, + { input: [`[{"text":""}]`], expect: [{ text: '' }] }, + ]) + }) +}) diff --git a/src/util/bbmodding.ts b/src/util/bbmodding.ts deleted file mode 100644 index 2107a308..00000000 --- a/src/util/bbmodding.ts +++ /dev/null @@ -1,105 +0,0 @@ -type Targetable = Record -type TargetKey = keyof Target & string -// type ModID = string -type ModInjectionMode = 'override' | 'append' | 'prepend' - -// interface RegisteredFunctionMod> { -// id: string -// mode: ModInjectionMode -// cb: ( -// this: Target, -// ctx: ModContext, -// ...args: Parameters -// ) => ModFunctionReturnResult> -// } - -interface ModContext> { - /** - * The original function that was modified. - */ - originalFunction: Target[Key] - /** - * The key of the function that was modified. - */ - key: Key - /** - * The id of the mod that created this function. - */ - modId: string - /** - * The injection mode of the mod that created this function. - */ - modMode: ModInjectionMode -} - -interface ModFunctionReturnResult { - returnValue: T - /** - * If injection mode is 'append', this will prevent the original function from running. - */ - cancelled?: boolean -} - -const REGISTERED_MODS = new Map>() - -function assertEntryExists>( - target: Target, - key: Key -) { - if (!REGISTERED_MODS.has(target)) { - REGISTERED_MODS.set(target, new Map()) - } - if (!REGISTERED_MODS.get(target)!.has(key)) { - REGISTERED_MODS.get(target)!.set(key, new Map()) - } -} - -/** - * Registers a function mod to a target object / class. - * @param target The target object / class to modify. - * @param key The key of the function to modify. - * @param id The ID of the mod. Used to identify errors and conflicts. - * @param mode The mode of the mod. Can be `override`, `append`, or `prepend`. - * - `override` will replace the original function with the new one. - * - `append` will run the original function first, then the new one. - * - `prepend` will run the new function first, then the original one. - * @param cb The callback function to run when the target function is called. - */ -export function registerFunctionMod>( - target: Target, - key: Key, - id: string, - mode: ModInjectionMode, - cb: ( - this: Target, - ctx: ModContext, - ...args: Parameters - ) => ModFunctionReturnResult> -) { - console.log(target, key, id, mode, cb) - assertEntryExists(target, key) - const mods = REGISTERED_MODS.get(target)!.get(key)! - if (mods.has(id)) { - throw new Error(`Mod with ID "${id}" already exists for function "${key}"`) - } - mods.set(id, { id, mode, cb }) -} - -registerFunctionMod(Group.prototype, 'updateElement', 'my_epic_mod', 'override', function (ctx) { - console.log('Hello World!', ctx) - return { returnValue: this } -}) - -/** - * Gets all registered mods for a target object / class. - * @param target The target object / class to get mods for. - * @param key (Optional) The key of the function to get mods for. - */ -export function getRegisteredMods>( - target: Target, - key?: Key -) { - // -} - -getRegisteredMods(Group.prototype) diff --git a/src/util/bufferGeometryUtils.ts b/src/util/bufferGeometryUtils.ts index 32a9cd4f..dc723d88 100644 --- a/src/util/bufferGeometryUtils.ts +++ b/src/util/bufferGeometryUtils.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/prefer-for-of */ +/* eslint-disable @typescript-eslint/naming-convention */ const { BufferAttribute, BufferGeometry, @@ -12,7 +14,7 @@ const { } = THREE function computeMikkTSpaceTangents(geometry: any, MikkTSpace: any, negateSign = true) { - if (!MikkTSpace || !MikkTSpace.isReady) { + if (!MikkTSpace?.isReady) { throw new Error('BufferGeometryUtils: Initialized MikkTSpace library required.') } @@ -327,7 +329,7 @@ function mergeAttributes(attributes: any) { for (let j = 0, l = attribute.count; j < l; j++) { for (let c = 0; c < itemSize; c++) { const value = attribute.getComponent(j, c) - // @ts-ignore + // @ts-expect-error No types result.setComponent(j + tupleOffset, c, value) } } @@ -339,7 +341,7 @@ function mergeAttributes(attributes: any) { } if (gpuType !== undefined) { - // @ts-ignore + // @ts-expect-error No types result.gpuType = gpuType } @@ -356,10 +358,10 @@ export function deepCloneAttribute(attribute: any) { } if (attribute.isInstancedBufferAttribute) { - // @ts-ignore + // @ts-expect-error No types return new InstancedBufferAttribute().copy(attribute) } - // @ts-ignore + // @ts-expect-error No types return new BufferAttribute().copy(attribute) } @@ -413,7 +415,7 @@ function interleaveAttributes(attributes: any) { // at the appropriate offset for (let c = 0; c < count; c++) { for (let k = 0; k < itemSize; k++) { - // @ts-ignore + // @ts-expect-error No types iba[setters[k]](c, attribute[getters[k]](c)) } } @@ -549,7 +551,7 @@ function mergeVertices(geometry: any, tolerance = 1e-4) { const morphAttributes = geometry.morphAttributes[name] if (morphAttributes) { - if (!tmpMorphAttributes[name]) tmpMorphAttributes[name] = [] + tmpMorphAttributes[name] ??= [] morphAttributes.forEach((morphAttr: any, i: any) => { const array = new morphAttr.array.constructor(morphAttr.count * morphAttr.itemSize) tmpMorphAttributes[name][i] = new morphAttr.constructor( @@ -1177,13 +1179,13 @@ function toCreasedNormals(geometry: any, creaseAngle = Math.PI / 3 /* 60 degrees export { computeMikkTSpaceTangents, - mergeGeometries, - mergeAttributes, - interleaveAttributes, - estimateBytesUsed, - mergeVertices, - toTrianglesDrawMode, computeMorphedAttributes, + estimateBytesUsed, + interleaveAttributes, + mergeAttributes, + mergeGeometries, mergeGroups, + mergeVertices, toCreasedNormals, + toTrianglesDrawMode, } diff --git a/src/util/events.ts b/src/util/events.ts index ae02915e..2d8e280f 100644 --- a/src/util/events.ts +++ b/src/util/events.ts @@ -1,76 +1,64 @@ -import * as PACKAGE from '../../package.json' import { Variant } from '../variants' -import { Subscribable } from './subscribable' - -export class PluginEvent extends Subscribable { - protected static events: Record> = {} - constructor(public name: string) { - super() - PluginEvent.events[name] = this - } -} +import { subscribable } from './subscribable' // Plugin Events -export const events = { - LOAD: new PluginEvent('load'), - UNLOAD: new PluginEvent('unload'), - INSTALL: new PluginEvent('install'), - UNINSTALL: new PluginEvent('uninstall'), +const EVENTS = { + PLUGIN_LOAD: subscribable(), + PLUGIN_FINISHED_LOADING: subscribable(), - INJECT_MODS: new PluginEvent('injectMods'), - EXTRACT_MODS: new PluginEvent('extractMods'), + PLUGIN_UNLOAD: subscribable(), + PLUGIN_FINISHED_UNLOADING: subscribable(), - NETWORK_CONNECTED: new PluginEvent('networkConnected'), + PLUGIN_INSTALL: subscribable(), + PLUGIN_UNINSTALL: subscribable(), - MINECRAFT_ASSETS_LOADED: new PluginEvent('minecraftAssetsLoaded'), - MINECRAFT_REGISTRY_LOADED: new PluginEvent('minecraftRegistriesLoaded'), - MINECRAFT_FONTS_LOADED: new PluginEvent('minecraftFontsLoaded'), - BLOCKSTATE_REGISTRY_LOADED: new PluginEvent('blockstateRegistryLoaded'), + EXTERNAL_PLUGIN_LOAD: subscribable(), + EXTERNAL_PLUGIN_UNLOAD: subscribable(), - PRE_SELECT_PROJECT: new PluginEvent('preSelectProject'), - SELECT_PROJECT: new PluginEvent('selectProject'), - UNSELECT_PROJECT: new PluginEvent('deselectProject'), + INJECT_MODS: subscribable(), + EXTRACT_MODS: subscribable(), - SELECT_AJ_PROJECT: new PluginEvent('selectAJProject'), - UNSELECT_AJ_PROJECT: new PluginEvent('unselectAJProject'), + NETWORK_CONNECTED: subscribable(), - CREATE_VARIANT: new PluginEvent('createVariant'), - UPDATE_VARIANT: new PluginEvent('updateVariant'), - DELETE_VARIANT: new PluginEvent('deleteVariant'), - SELECT_VARIANT: new PluginEvent('selectVariant'), + MINECRAFT_ASSETS_LOADED: subscribable(), + MINECRAFT_REGISTRY_LOADED: subscribable(), + MINECRAFT_FONTS_LOADED: subscribable(), + BLOCKSTATE_REGISTRY_LOADED: subscribable(), - SELECT_KEYFRAME: new PluginEvent<_Keyframe>('selectKeyframe'), - UNSELECT_KEYFRAME: new PluginEvent('unselectKeyframe'), + PRE_SELECT_PROJECT: subscribable(), + SELECT_PROJECT: subscribable(), + UNSELECT_PROJECT: subscribable(), + CLOSE_PROJECT: subscribable(), - UPDATE_SELECTION: new PluginEvent('updateSelection'), + SELECT_AJ_PROJECT: subscribable(), + UNSELECT_AJ_PROJECT: subscribable(), - UNDO: new PluginEvent('undo'), - REDO: new PluginEvent('redo'), -} + CREATE_VARIANT: subscribable(), + UPDATE_VARIANT: subscribable(), + DELETE_VARIANT: subscribable(), + SELECT_VARIANT: subscribable(), -function injectionHandler() { - console.groupCollapsed(`Injecting BlockbenchMods added by '${PACKAGE.name}'`) - events.INJECT_MODS.dispatch() - console.groupEnd() -} + UPDATE_KEYFRAME_SELECTION: subscribable(), -function extractionHandler() { - console.groupCollapsed(`Extracting BlockbenchMods added by '${PACKAGE.name}'`) - events.EXTRACT_MODS.dispatch() - console.groupEnd() -} + UPDATE_SELECTION: subscribable(), -events.LOAD.subscribe(injectionHandler) -events.UNLOAD.subscribe(extractionHandler) -events.INSTALL.subscribe(injectionHandler) -events.UNINSTALL.subscribe(extractionHandler) + UNDO: subscribable(), + REDO: subscribable(), +} +export default EVENTS Blockbench.on('select_project', ({ project }: { project: ModelProject }) => { - events.SELECT_PROJECT.dispatch(project) + EVENTS.SELECT_PROJECT.publish(project) }) Blockbench.on('unselect_project', ({ project }: { project: ModelProject }) => { - events.UNSELECT_PROJECT.dispatch(project) + EVENTS.UNSELECT_PROJECT.publish(project) }) -Blockbench.on('update_selection', () => events.UPDATE_SELECTION.dispatch()) -Blockbench.on('undo', (entry: UndoEntry) => events.UNDO.dispatch(entry)) -Blockbench.on('redo', (entry: UndoEntry) => events.REDO.dispatch(entry)) +// Blockbench.on('loaded_plugin', ({ plugin }: { plugin: BBPlugin }) => { +// if (plugin.id === PACKAGE.name) return +// EVENTS.EXTERNAL_PLUGIN_LOAD.publish(plugin) +// }) +Blockbench.on('close_project', () => EVENTS.CLOSE_PROJECT.publish(Project!)) +Blockbench.on('update_keyframe_selection', EVENTS.UPDATE_KEYFRAME_SELECTION.publish) +Blockbench.on('update_selection', EVENTS.UPDATE_SELECTION.publish) +Blockbench.on('undo', EVENTS.UNDO.publish) +Blockbench.on('redo', EVENTS.REDO.publish) diff --git a/src/util/fileUtil.ts b/src/util/fileUtil.ts index 4ca9be1c..cf1a4571 100644 --- a/src/util/fileUtil.ts +++ b/src/util/fileUtil.ts @@ -26,7 +26,12 @@ export function resolveEnvVariables(path: string) { } export function isRelativePath(path: string) { - return path.startsWith('./') || path.startsWith('../') + return ( + path.startsWith('./') || + path.startsWith('../') || + path.startsWith('.\\') || + path.startsWith('..\\') + ) } export function resolveRelativePath(path: string) { @@ -60,7 +65,7 @@ export function swapPathRoot(path: string, oldRoot: string, newRoot: string) { export function safeReadSync(path: string): Buffer | undefined { try { return fs.readFileSync(path) - } catch (e) { + } catch { return undefined } } diff --git a/src/util/injectSvelteComponent.ts b/src/util/injectSvelteComponent.ts deleted file mode 100644 index 1e6998b2..00000000 --- a/src/util/injectSvelteComponent.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { SvelteComponentConstructor } from './misc' -import type { ComponentConstructorOptions, SvelteComponentDev } from 'svelte/internal' -import { pollPromise } from './promises' -import { createBlockbenchMod } from './moddingTools' - -type InjectSvelteComponentOptions = { - /** - * The svelte component constructor. - */ - component: SvelteComponentConstructor - /** - * The props to pass to the component - */ - props: U['props'] - /** - * A function that returns the element to inject the component into. - * - * This function will be polled until it returns a non-null value. - * @returns The element to inject the component into - */ - elementSelector: () => Element | undefined | null - /** - * A function to call after the component has been mounted - * @param comp The mounted svelte component - */ - postMount?: (comp: SvelteComponentDev) => void - /** - * Whether to prepend the component to the element's children. - * - * Overrides `injectIndex` if true. - */ - prepend?: boolean - /** - * The index to inject the component at in the element's children. - */ - injectIndex?: number -} - -/** - * Injects a svelte component into the DOM. - */ -export async function injectSvelteCompomponent>( - options: InjectSvelteComponentOptions> -) { - return pollPromise(options.elementSelector).then(el => { - let anchor = undefined - if (options.prepend) { - anchor = el.children[0] - } else if (options.injectIndex !== undefined) { - anchor = el.children[options.injectIndex] - } - const component = new options.component({ - target: el, - anchor, - props: options.props, - }) as SvelteComponentDev - if (options.postMount) options.postMount(component) - return component - }) -} - -export function injectSvelteCompomponentMod(options: InjectSvelteComponentOptions) { - createBlockbenchMod( - `animated_java:injected_svelte_component[${options.component.name}](${guid()})`, - {}, - () => { - let instance: SvelteComponentDev | undefined - void pollPromise(options.elementSelector).then(el => { - let anchor = undefined - if (options.prepend) { - anchor = el.children[0] - } - instance = new options.component({ - target: el, - anchor, - props: options.props, - }) - if (options.postMount) options.postMount(instance!) - }) - return instance - }, - context => { - if (context) context.$destroy() - } - ) -} diff --git a/src/util/minecraftUtil.ts b/src/util/minecraftUtil.ts index d3702cc9..e748556d 100644 --- a/src/util/minecraftUtil.ts +++ b/src/util/minecraftUtil.ts @@ -1,8 +1,8 @@ import * as pathjs from 'path' -import { MinecraftVersion } from '../systems/global' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../systems/global' import { BlockStateRegistryEntry, - BlockStateValue, + type BlockStateValue, getBlockState, } from '../systems/minecraft/blockstateManager' @@ -17,14 +17,20 @@ export interface IMinecraftResourceLocation { type: string } -/** - * Return a sanitized version of {@param str} that is safe to use as a path name in a data pack or resource pack. - * - * Function names can only contain lowercase letters, numbers, underscores, and periods. - * All other characters are replaced with underscores. - */ -export function sanitizePathName(str: string): string { - return str.toLowerCase().replace(/[^a-z0-9_.]+/g, '_') +const CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789' +const SMALL_CAPS_CHARACTERS = 'ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘꞯʀꜱᴛᴜᴠᴡxʏᴢ⁰¹²³⁴⁵⁶⁷⁸⁹' + +export function toSmallCaps(str: string): string { + let result = '' + for (const char of str) { + const index = CHARACTERS.indexOf(char.toLowerCase()) + if (index !== -1) { + result += SMALL_CAPS_CHARACTERS[index] + } else { + result += char + } + } + return result } /** @@ -57,7 +63,7 @@ export function getPathFromResourceLocation(resourceLocation: string, type: stri export function isResourcePackPath(path: string) { const parsed = parseResourcePackPath(path) - return !!(parsed && parsed.namespace && parsed.resourcePath) + return !!(parsed?.namespace && parsed.resourcePath) } export function parseResourcePackPath(path: string): IMinecraftResourceLocation | undefined { @@ -113,7 +119,7 @@ export function parseResourceLocation(resourceLocation: string) { export function isDataPackPath(path: string) { const parsed = parseDataPackPath(path) - return !!(parsed && parsed.namespace && parsed.resourcePath) + return !!(parsed?.namespace && parsed.resourcePath) } export function parseDataPackPath(path: string): IMinecraftResourceLocation | undefined { @@ -155,28 +161,72 @@ export function parseDataPackPath(path: string): IMinecraftResourceLocation | un } } -export interface IFunctionTag { +export interface FunctionTagJSON { replace?: boolean values: Array } -export function mergeTag(oldTag: IFunctionTag, newTag: IFunctionTag): IFunctionTag { - oldTag.values.forEach(value => { - if (typeof value === 'string') { - if (!newTag.values.some(v => (typeof v === 'object' ? v.id === value : v === value))) { - newTag.values.push(value) - } - } else { - if ( - !newTag.values.some(v => - typeof v === 'object' ? v.id === value.id : v === value.id - ) - ) { - newTag.values.push(value) - } +type TagEntry = string | { id: string; required?: boolean } + +export class DataPackTag { + replace = false + values: Array = [] + + has(entry: TagEntry) { + const id = DataPackTag.getEntryId(entry) + return this.values.some(v => DataPackTag.getEntryId(v) === id) + } + + add(value: TagEntry) { + const existingEntry = this.get(value) + if (existingEntry) return + this.values.push(value) + } + + get(value: TagEntry) { + const id = DataPackTag.getEntryId(value) + return this.values.find(v => DataPackTag.getEntryId(v) === id) + } + + filter(predicate: (value: TagEntry, index: number, array: TagEntry[]) => boolean) { + this.values = this.values.filter(predicate) + return this + } + + merge(other: DataPackTag) { + this.replace = other.replace + + for (const value of other.values) { + this.add(value) } - }) - return newTag + + return this + } + + sort() { + this.values.sort((a, b) => { + const idA = DataPackTag.getEntryId(a) + const idB = DataPackTag.getEntryId(b) + return idA.localeCompare(idB, 'en') + }) + + return this + } + + static getEntryId(entry: TagEntry) { + return typeof entry === 'string' ? entry : entry.id + } + + static fromJSON(json: FunctionTagJSON) { + const tag = new DataPackTag() + if (typeof json.replace === 'boolean') tag.replace = json.replace + if (Array.isArray(json.values)) tag.values = structuredClone(json.values) + return tag + } + + toJSON(): FunctionTagJSON { + return { replace: this.replace, values: structuredClone(this.values) } + } } export function resolveBlockstateValueType( @@ -212,7 +262,7 @@ export interface IParsedBlock { export async function parseBlock(block: string): Promise { const states: Record> = {} if (block.includes('[')) { - const match = block.match(/(.+?)\[((?:[^,=[\]]+=[^,=[\]]+,?)+)?]/) + const match = /(.+?)\[((?:[^,=[\]]+=[^,=[\]]+,?)+)?]/.exec(block) if (!match) return if (match[2] !== undefined) { const args = match[2].split(',') @@ -233,50 +283,67 @@ export async function parseBlock(block: string): Promise { return compareVersions(a, b) ? -1 : 1 }) } -export function getDataPackFormat(version: MinecraftVersion): number { +export function getDataPackFormat(version: SUPPORTED_MINECRAFT_VERSIONS): number { switch (version) { - case '1.20.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.4']: return 26 - case '1.20.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.5']: return 41 - case '1.21.0': - return 48 - case '1.21.2': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.2']: return 57 - case '1.21.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.4']: return 61 - case '1.21.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.5']: return 71 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.6']: + return 80 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.9']: + return 88.0 default: + console.warn(`Unknown Minecraft version: ${version}`) return Infinity } } -export function getResourcePackFormat(version: MinecraftVersion): number { +export function getResourcePackFormat(version: SUPPORTED_MINECRAFT_VERSIONS): number { switch (version) { - case '1.20.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.4']: return 22 - case '1.20.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.5']: return 32 - case '1.21.0': - return 34 - case '1.21.2': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.2']: return 42 - case '1.21.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.4']: return 46 - case '1.21.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.5']: return 55 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.6']: + return 63 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.9']: + return 69.0 default: + console.warn(`Unknown Minecraft version: ${version}`) return Infinity } } +export function getNextSupportedVersion( + version: SUPPORTED_MINECRAFT_VERSIONS +): SUPPORTED_MINECRAFT_VERSIONS | undefined { + const versions = Object.values(SUPPORTED_MINECRAFT_VERSIONS) + const index = versions.indexOf(version) + if (index === -1 || index === versions.length - 1) return undefined + return versions[index + 1] +} + export function functionReferenceExists(dataPackRoot: string, resourceLocation: string): boolean { const parsed = parseResourceLocation(resourceLocation) if (!parsed) return false diff --git a/src/util/misc.ts b/src/util/misc.ts index 649ee2f1..9b56409d 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -1,29 +1,9 @@ -import { BLUEPRINT_FORMAT } from '../blueprintFormat' - -import { ComponentConstructorOptions } from 'svelte' +import { type ComponentConstructorOptions } from 'svelte' export type SvelteComponentConstructor = new ( options: U ) => T -export function addProjectToRecentProjects(file: FileResult) { - if (!Project || !file.path) return - const name = pathToName(file.path, true) - if (file.path && isApp && !file.no_file) { - const project = Project - Project.save_path = file.path - Project.name = pathToName(name, false) - addRecentProject({ - name, - path: file.path, - icon: BLUEPRINT_FORMAT.icon, - }) - setTimeout(() => { - if (Project === project) void updateRecentProjectThumbnail() - }, 200) - } -} - /** * Rounds a number to a certain number of decimal places */ @@ -38,17 +18,6 @@ export function roundToNth(n: number, x: number) { return Math.round(n * x) / x } -export function floatToHex(n: number) { - return Number((255 * n).toFixed(0)) - .toString(16) - .padStart(2, '0') -} - -export function tinycolorToDecimal(color: InstanceType) { - const rgba = color.toRgb() - return ((rgba.a * 255) << 24) | (rgba.r << 16) | (rgba.g << 8) | rgba.b -} - export function makeNotZero(vec: THREE.Vector3 | THREE.Euler) { if (vec.x === 0) vec.x = 0.00001 if (vec.y === 0) vec.y = 0.00001 @@ -63,11 +32,11 @@ export function scrubUndefined>(obj: T) { scrubUndefined(obj[key]) } } - return obj + return obj as { [K in keyof T]: Exclude } } // Developed by FetchBot 💖 -type LLNode = { +interface LLNode { parent?: LLNode name: string } diff --git a/src/util/moddingTools.ts b/src/util/moddingTools.ts index 13b80950..f07f40b9 100644 --- a/src/util/moddingTools.ts +++ b/src/util/moddingTools.ts @@ -1,157 +1,525 @@ -import { events } from './events' -import { Subscribable } from './subscribable' +import { PACKAGE } from 'src/constants' +import EVENTS from './events' +import type { ResourceLocation } from './resourceLocation' +import { subscribable, type Subscribable } from './subscribable' -export type NamespacedString = `${string}${string}:${string}${string}` -// Useful for describing context variables that will become BlochBench class properties in the inject function. -export type ContextProperty = Property | undefined - -class BlockbenchModInstallError extends Error { +class ModInstallError extends Error { constructor(id: string, err: Error) { - super(`Mod '${id}' failed to install: ${err.message}` + (err.stack ? '\n' + err.stack : '')) + super(`'${id}' failed to install: ${err.message}` + (err.stack ? '\n' + err.stack : '')) } } -class BlockbenchModUninstallError extends Error { +class ModUninstallError extends Error { constructor(id: string, err: Error) { - super( - `Mod '${id}' failed to uninstall: ${err.message}` + (err.stack ? '\n' + err.stack : '') - ) + super(`'${id}' failed to uninstall: ${err.message}` + (err.stack ? '\n' + err.stack : '')) } } -/** - * A simple helper function to make modifing Blockbench easier. - * @param id A namespaced ID ('my-plugin-id:my-mod') - * @param context The context of the mod. This is passed to the inject function. - * @param inject The function that is called to install the mod. - * @param extract The function that is called to uninstall the mod. - * @template InjectContext The type of the context passed to the inject function. - * @template ExtractContext The type of the context returned from the inject function and passed to the extract function. - * @example - * ```ts - * createBlockbenchMod( - * 'my-plugin-id:my-mod', - * { - * original: Blockbench.Animation.prototype.select - * }, - * context => { - * // Inject code here - * Blockbench.Animation.prototype.select = function(this: _Animation) { - * if (Format.id === myFormat.id) { - * // Do something here - * } - * return context.original.call(this) - * } - * return context - * }) - * context => { - * // Extract code here - * Blockbench.Animation.prototype.select = context.original - * }) - * ``` - */ -export function createBlockbenchMod( - id: NamespacedString, - context: InjectContext, - inject: (context: InjectContext) => ExtractContext, - extract: (context: ExtractContext) => void -) { - let installed = false - let extractContext: ExtractContext - - events.INJECT_MODS.subscribe(() => { - console.log(`Injecting BBMod '${id}'`) - try { - if (installed) new Error('Mod is already installed!') - extractContext = inject(context) - installed = true - } catch (err) { - throw new BlockbenchModInstallError(id, err as Error) +interface ModHandle { + id: string + dependencies?: string[] + priority: number + isInstalled(): boolean + install: () => Promise + uninstall: () => Promise +} + +const REGISTERED_MODS = new Map() +const MOD_INSTALL_ORDER: string[] = [] + +EVENTS.PLUGIN_LOAD.subscribe(async () => { + console.groupCollapsed(`Installing Mods...`) + + try { + for (const modId of MOD_INSTALL_ORDER) { + const mod = REGISTERED_MODS.get(modId)! + if (mod.isInstalled()) continue + await mod.install() } - console.log('Sucess!') - }) + } catch (e) { + console.groupEnd() + throw e + } + + // Emit fake plugin load events for all already loaded plugins, so mods can hook into them + for (const plugin of Object.values(Plugins.registered)) { + if (plugin.id === PACKAGE.name) continue + EVENTS.EXTERNAL_PLUGIN_LOAD.publish(plugin) + } + + console.groupEnd() + EVENTS.PLUGIN_FINISHED_LOADING.publish() +}) - events.EXTRACT_MODS.subscribe(() => { - console.log(`Extracting BBMod '${id}'`) - try { - if (!installed) new Error('Mod is not installed!') - extract(extractContext) - installed = false - } catch (err) { - throw new BlockbenchModUninstallError(id, err as Error) +EVENTS.PLUGIN_UNLOAD.subscribe(async () => { + console.groupCollapsed(`Uninstalling Mods...`) + + try { + for (const modId of [...MOD_INSTALL_ORDER].reverse()) { + const mod = REGISTERED_MODS.get(modId)! + if (!mod.isInstalled()) continue + await mod.uninstall() } - console.log('Sucess!') + } catch (e) { + console.groupEnd() + throw e + } + + for (const plugin of Object.values(Plugins.registered)) { + if (plugin.id === PACKAGE.name) continue + EVENTS.EXTERNAL_PLUGIN_UNLOAD.publish(plugin) + } + + console.groupEnd() + EVENTS.PLUGIN_FINISHED_UNLOADING.publish() +}) + +export interface BaseModOptions { + id: ResourceLocation.Validate + /** A list of mod IDs that this mod depends on */ + dependencies?: string[] + /** The priority of the mod. Higher priority mods will be installed first. */ + priority?: number +} + +interface ModOptions + extends BaseModOptions { + /** A function that applies the mod. This function should return a context object that will be passed to the revert function. */ + apply: () => Promise | RevertContext + /** + * A function that reverts the mod + * @param ctx The context object returned by the apply function + */ + revert: (ctx: RevertContext) => Promise | void +} + +function updateModInstallOrder() { + MOD_INSTALL_ORDER.sort((a, b) => { + const modA = REGISTERED_MODS.get(a)! + const modB = REGISTERED_MODS.get(b)! + return modB.priority - modA.priority }) + + // Ensure dependencies are installed before the mod that depends on them + for (const modId of MOD_INSTALL_ORDER) { + const mod = REGISTERED_MODS.get(modId)! + if (mod.dependencies === undefined) continue + for (const dependencyId of mod.dependencies) { + const dependencyIndex = MOD_INSTALL_ORDER.indexOf(dependencyId) + if (dependencyIndex === -1) { + throw new Error(`Mod '${modId}' depends on unknown mod '${dependencyId}'`) + } + const modIndex = MOD_INSTALL_ORDER.indexOf(modId) + if (dependencyIndex > modIndex) { + // Move the dependency before the mod + MOD_INSTALL_ORDER.splice(dependencyIndex, 1) + MOD_INSTALL_ORDER.splice(modIndex, 0, dependencyId) + } + } + } } -/** Creates a new Blockbench.Action and automatically handles it's deletion on the plugin unload and uninstall events. - * See https://www.blockbench.net/wiki/api/action for more information on the Blockbench.Action class. - * @param id A namespaced ID ('my-plugin-id:my-action') - * @param options The options for the action. - * @returns The created action. +/** + * Registers a new mod. Mods are changes that need to be applied when the plugin is loaded, and reverted when the plugin is unloaded. + * + * Mods can depend on other mods, and will be installed in the correct order. + * + * If a mod fails to install, an error will be thrown, and the plugin will fail to load. */ -export function createAction(id: NamespacedString, options: ActionOptions) { - const action = new Action(id, options) +export function registerMod( + options: ModOptions +) { + let applyContext: RevertContext + let installed = false + + if (REGISTERED_MODS.has(options.id)) { + throw new Error(`A Mod with the ID '${options.id}' is already registered!`) + } + + const handle: ModHandle = { + id: options.id, + dependencies: options.dependencies, + priority: options.priority ?? 0, + isInstalled() { + return installed + }, + async install() { + console.log(`Installing '${options.id}'`) + try { + if (installed) + throw new Error( + `Attempted to install '${options.id}' while it was already installed.` + ) + applyContext = await options.apply() + installed = true + } catch (err) { + throw new ModInstallError(options.id, err as Error) + } + }, + async uninstall() { + console.log(`Uninstalling '${options.id}'`) + try { + if (!installed) + throw new Error( + `Attempted to uninstall '${options.id}' before it was installed.` + ) + await options.revert(applyContext) + installed = false + } catch (err) { + throw new ModUninstallError(options.id, err as Error) + } + }, + } - events.EXTRACT_MODS.subscribe(() => { - action.delete() - }, true) + REGISTERED_MODS.set(options.id, handle) + MOD_INSTALL_ORDER.push(options.id) + updateModInstallOrder() - return action + return handle +} + +interface RegisterProjectModOptions + extends ModOptions { + apply: () => RevertContext + revert: (ctx: RevertContext) => void + /** A function that checks if the mod should be applied when switching projects */ + condition: (project: ModelProject) => boolean } /** - * Creates a new Blockbench.ModelLoader and automatically handles it's deletion on the plugin unload and uninstall events. - * @param id A namespaced ID ('my-plugin-id:my-model-loader') - * @param options The options for the model loader. - * @returns The created model loader. + * Registers a mod that is applied / reverted when a project is selected / unselected that meets the provided condition. */ -export function createModelLoader(id: string, options: ModelLoaderOptions): ModelLoader { - const modelLoader = new ModelLoader(id, options) +export function registerProjectMod( + options: RegisterProjectModOptions +) { + let revertContext: RevertContext | undefined + + return registerMod({ + ...options, - events.EXTRACT_MODS.subscribe(() => { - modelLoader.delete() - }, true) + apply: () => { + return [ + EVENTS.PRE_SELECT_PROJECT.subscribe(project => { + console.log('Checking project mod condition for', options.id, project) + if (!options.condition(project)) return + console.log(`Applying project mod '${options.id}'`) + revertContext = options.apply() + }), - return modelLoader + EVENTS.UNSELECT_PROJECT.subscribe(() => { + // Effectively using revertContext as a boolean to check if the mod is applied + if (revertContext !== undefined) { + console.log(`Reverting project mod '${options.id}'`) + options.revert(revertContext) + revertContext = undefined + } + }), + ] + }, + + revert: ctx => { + ctx.forEach(unsub => unsub()) + }, + }) +} + +interface RegisterPluginModOptions + extends ModOptions { + apply: () => RevertContext + revert: (ctx: RevertContext) => void + /** A function that checks if the mod should be applied when the plugin is loaded */ + condition: (plugin: BBPlugin) => boolean } /** - * Creates a new Blockbench.Menu and automatically handles it's deletion on the plugin unload and uninstall events. - * See https://www.blockbench.net/wiki/api/menu for more information on the Blockbench.Menu class. - * @param template The menu template. - * @param options The options for the menu. - * @returns The created menu. + * Registers a mod that is applied / reverted when a plugin is loaded / unloaded that meets the provided condition. */ -export function createMenu(template: MenuItem[], options?: MenuOptions) { - const menu = new Menu(template, options) +export function registerPluginMod( + options: RegisterPluginModOptions +) { + let revertContext: RevertContext | undefined + + return registerMod({ + ...options, + + apply: () => { + return [ + EVENTS.EXTERNAL_PLUGIN_LOAD.subscribe(plugin => { + if (!Condition(options.condition, plugin)) return + console.log(`Applying plugin mod '${options.id}'`) + revertContext = options.apply() + }), - // events.EXTRACT_MODS.subscribe(() => { - // menu.delete() - // }, true) + EVENTS.EXTERNAL_PLUGIN_UNLOAD.subscribe(() => { + // Effectively using revertContext as a boolean to check if the mod is applied + if (revertContext !== undefined) { + console.log(`Reverting plugin mod '${options.id}'`) + options.revert(revertContext) + revertContext = undefined + } + }), + ] + }, + + revert: ctx => { + ctx.forEach(unsub => unsub()) + }, + }) +} - return menu +interface DeletableEventHandler { + /** The current instance of this deletable */ + get(): T | null + onCreated: Subscribable['subscribe'] + onDeleted: Subscribable['subscribe'] } +interface RegisterDeletableOptions extends BaseModOptions {} + /** - * Creates a new Blockbench.BarMenu and automatically handles it's deletion on the plugin unload and uninstall events. - * @param id A namespaced ID ('my-plugin-id:my-menu') - * @param structure The menu structure. - * @param condition The condition for the menu to be visible. - * @returns The created menu. + * Defines a new deletable register function, that handles the creation and deletion of deletables on the appropriate Blockbench events. */ -export function createBarMenu( - id: NamespacedString, - structure: MenuItem[], - condition: ConditionResolvable +function registerDeletableFactory( + createInstance: (id: string, ...args: A) => T, + deleteInstance: (instance: T) => void ) { - const menu = new BarMenu(id, structure, condition) + return function handler( + { id, priority, dependencies }: RegisterDeletableOptions, + ...args: A + ) { + let activeInstance: T | null + const created = subscribable() + const deleted = subscribable() + + const handle: DeletableEventHandler = { + get: () => activeInstance, + onCreated: created.subscribe, + onDeleted: deleted.subscribe, + } + + registerMod({ + id, + priority, + dependencies, + apply: () => { + activeInstance = createInstance(id, ...args) + created.publish(activeInstance) + return activeInstance + }, + revert: lastInstance => { + deleteInstance(lastInstance) + activeInstance = null + deleted.publish(lastInstance) + }, + }) - // events.EXTRACT_MODS.subscribe(() => { - // menu.delete() - // }, true) + return handle + } +} + +/** + * Defines a new Blockbench.Action, and handles it's creation and deletion on the appropriate Blockbench events. + * + * See https://www.blockbench.net/wiki/api/action for more information on the Blockbench.Action class. + */ +export const registerAction = registerDeletableFactory( + (id, options: ActionOptions) => new Action(id, options), + action => action.delete() +) + +/** + * Defines a new Blockbench.ModelFormat, and handles it's creation and deletion on the appropriate Blockbench events. + * + * See https://www.blockbench.net/wiki/api/modelformat for more information on the Blockbench.ModelFormat class. + */ +export const registerModelFormat = registerDeletableFactory( + (id, options: Omit) => new ModelFormat({ ...options, id }), + format => format.delete() +) + +/** + * Defines a new Blockbench.Codec, and handles it's creation and deletion on the appropriate Blockbench events. + * + * See https://www.blockbench.net/wiki/api/codec for more information on the Blockbench.Codec class. + */ +export const registerCodec = registerDeletableFactory( + (id, options: CodecOptions) => new Codec(id, { ...options }), + codec => codec.delete() +) + +export const registerBarMenu = registerDeletableFactory( + (id, structure: MenuItem[], options?: BarMenuOptions) => new BarMenu(id, structure, options), + menubar => { + // Swap over to this when Blockbench updates to 5.0 + // menubar.delete() + // Temporary implementation of `menubar.delete()` from Blockbench 5.0 + // @ts-expect-error No types + menubar.node.remove() + menubar.label.remove() + delete MenuBar.menus[menubar.id] + } +) + +export const registerModelLoader = registerDeletableFactory( + (id, options: Omit) => new ModelLoader(id, options), + loader => loader.delete() +) - return menu +export const registerMenu = registerDeletableFactory( + (id, template: MenuItem[] | ((context?: any) => MenuItem[]), options: MenuOptions) => + new Menu(id, template, options), + menu => { + // Swap over to this when Blockbench updates to 5.0 + // menu.delete() + // Temporary implementation of `menu.delete()` from Blockbench 5.0 + // @ts-expect-error No types + menu.node.remove() + } +) + +interface PropertyOverrideModOptions< + ID extends string, + T extends Object, + K extends keyof T, + O extends T[K] +> extends BaseModOptions { + object: T + key: K + get: (this: T, original: T[K]) => O + set?: (this: T, value: O) => void +} + +export function registerPropertyOverrideMod< + ID extends string, + T extends Object, + K extends keyof T, + O extends T[K] +>(options: PropertyOverrideModOptions) { + registerMod({ + ...options, + + apply: () => { + if (options.object == undefined) { + throw new Error(`Cannot override property on undefined object.`) + } + + const original = { + value: undefined as O, + descriptor: undefined as unknown as PropertyDescriptor, + } + + original.value = options.object[options.key] as O + + original.descriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { + value: original.value, + writable: true, + configurable: true, + } + + if (original.descriptor.configurable === false) { + throw new Error( + `Cannot override property '${String( + options.key + )}' on object because it is not configurable.` + ) + } + + Object.defineProperty(options.object, options.key, { + configurable: true, + get() { + return options.get.call(this, original.value) + }, + set(value) { + original.value = value + }, + }) + + return { original } + }, + + revert: ({ original }) => { + Object.defineProperty(options.object, options.key, original.descriptor) + }, + }) +} + +interface ConditionalPropertyOverrideModOptions< + ID extends string, + T extends Object, + K extends keyof T, + O extends T[K] +> extends BaseModOptions { + object: T + key: K + condition: ConditionResolvable + get: (this: T, original: T[K]) => O + set?: (this: T, value: O) => void +} + +export function registerConditionalPropertyOverrideMod< + ID extends string, + T extends Object, + K extends keyof T, + O extends T[K] +>(options: ConditionalPropertyOverrideModOptions) { + registerMod({ + ...options, + + apply: () => { + if (options.object == undefined) { + throw new Error(`Cannot override property on undefined object.`) + } + + const original = { + value: undefined as O, + descriptor: undefined as unknown as PropertyDescriptor, + } + + original.value = options.object[options.key] as O + + original.descriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { + value: original.value, + writable: true, + configurable: true, + } + + if (original.descriptor.configurable === false) { + throw new Error( + `Cannot override property '${String( + options.key + )}' on object because it is not configurable.` + ) + } + + Object.defineProperty(options.object, options.key, { + configurable: true, + enumerable: original.descriptor.enumerable, + get: function (this: T) { + if (Condition(options.condition, this)) { + return options.get.call(this, original.value) + } + return original.value + }, + set: options.set + ? function (this: T, value) { + if (Condition(options.condition, this)) { + options.set!.call(this, value) + } else { + original.value = value + } + } + : value => { + original.value = value + }, + }) + + return { original } + }, + + revert: ({ original }) => { + Object.defineProperty(options.object, options.key, original.descriptor) + }, + }) } interface Storage { @@ -171,7 +539,7 @@ const SUBSCRIBABLES = new Map< * @param key The key of the property on the object. * @returns A tuple of {@link Subscribable | Subscribables} [onGet, onSet] * @example - * Using the subscribables as simple events. + * Using the subscribables as simple EVENTS. * ```ts * const [onGet, onSet] = createPropertySubscribable(Blockbench, 'version') * onGet.subscribe(({ value }) => console.log('Blockbench version:', value)) @@ -195,27 +563,27 @@ export function createPropertySubscribable(object: any, key: string const storage: Storage = { value: object[key] } if (subscribables === undefined) { - const onGet = new Subscribable<{ + const onGet = subscribable<{ storage: Storage value: Value }>() - const onSet = new Subscribable<{ storage: Storage; newValue: Value }>() + const onSet = subscribable<{ storage: Storage; newValue: Value }>() subscribables = [onGet, onSet] SUBSCRIBABLES.set(object, subscribables) Object.defineProperty(object, key, { get() { - onGet.dispatch({ storage, value: storage.value }) + onGet.publish({ storage, value: storage.value }) return storage.value }, set(newValue: Value) { storage.value = newValue - onSet.dispatch({ storage, newValue }) + onSet.publish({ storage, newValue }) }, configurable: true, }) - events.EXTRACT_MODS.subscribe(() => { + EVENTS.PLUGIN_UNLOAD.subscribe(() => { const value = object[key] delete object[key] Object.defineProperty(object, key, { @@ -228,23 +596,32 @@ export function createPropertySubscribable(object: any, key: string return subscribables } -// export function overwriteFunction, Key extends string>( -// /** -// * The object or class to overwrite the function on. -// */ -// target: Target, -// /** -// * The key of the function to overwrite. -// */ -// key: string, -// /** -// * The function to overwrite the original function with. -// */ -// callback: (target: Target, originalFunction: Target[Key]) => void, -// /** -// * The priority of the overwrite. Higher priority overwrites are called first. -// */ -// priority?: number -// ) { -// // -// } +/** + * A wrapper for the Blockbench.Property class that deep-clones the property value when copying or merging. + */ +export class ObjectProperty extends Property<'object'> { + constructor(target: any, name: string, options: PropertyOptions) { + super(target, 'object', name, options) + } + + copy(instance: any, target: any) { + if (instance[this.name] == undefined) { + target[this.name] = instance[this.name] + } else { + target[this.name] = JSON.parse(JSON.stringify(instance[this.name])) + } + } + + merge(instance: any, data: any) { + if (data[this.name] == undefined) { + instance[this.name] = this.default + } else { + instance[this.name] = JSON.parse(JSON.stringify(data[this.name])) + } + } +} + +export const fixClassPropertyInheritance: ClassDecorator = target => { + target.properties = { ...target.properties } + return target +} diff --git a/src/util/mountSvelteComponent.ts b/src/util/mountSvelteComponent.ts new file mode 100644 index 00000000..446d7318 --- /dev/null +++ b/src/util/mountSvelteComponent.ts @@ -0,0 +1,151 @@ +import { ComponentProps, SvelteComponent } from 'svelte' +import { BaseModOptions, registerMod } from './moddingTools' +import { PollingCancelledError, pollUntilResult } from './promises' +import { SvelteComponentOptions } from './svelteUtil' + +type ElementSelector = string | (() => Element | undefined | null) + +type MountSvelteComponentOptions = SvelteComponentOptions & { + /** + * A selector string, function returning an element, or the element itself to mount the component to. + */ + target: Element | ElementSelector + /** + * If this condition returns true, mounting process will be cancelled. + */ + cancelCondition?: () => boolean + /** + * Whether to hide the target element's children when mounting the component. + * + * Useful when other code is relying on the children being present, but you want to hide them. + */ + hideTargetChildren?: boolean + /** + * Whether to replace all children of the target element with the component. + */ + replaceChildren?: boolean + /** + * A function to call after the component has been mounted + */ + onMount?: (component: T, target: Element) => void + /** + * A function to call before the component is destroyed + */ + onDestroy?: (component: T) => void + /** + * Whether to prepend the component to the element's children. + * + * Overrides `injectIndex` if true. + */ + prepend?: boolean + /** + * The index to inject the component at in the element's children. + */ + injectIndex?: number +} + +/** + * Mounts a Svelte component to a target element. + */ +export function mountSvelteComponent( + options: MountSvelteComponentOptions +) { + let target: Element | undefined | null + if (typeof options.target === 'string') { + target = document.querySelector(options.target) + } else if (typeof options.target === 'function') { + target = options.target() + } else { + target = options.target + } + + if (!target) throw new Error('Target element not found') + + let anchor: Element | undefined + if (options.replaceChildren) { + target.replaceChildren() + } else { + if (options.hideTargetChildren) { + for (const child of Array.from(target.children)) { + if (child instanceof HTMLElement) { + child.style.display = 'none' + } + } + } + + if (options.prepend) { + anchor = target.children[0] + } else if (options.injectIndex !== undefined) { + anchor = target.children[options.injectIndex] + } + } + + const mounted = new options.component({ target: target, anchor, props: options.props }) + options.onMount?.(mounted, target) + + mounted.$on('destroy', () => { + options.onDestroy?.(mounted) + }) + + return mounted +} + +type MountSvelteComponentModOptions< + ID extends string, + T extends SvelteComponent +> = BaseModOptions & + Omit, 'target'> & { + target: ElementSelector + } + +export function registerMountSvelteComponentMod( + options: MountSvelteComponentModOptions +) { + const mountSelector = + typeof options.target === 'string' + ? document.querySelector.bind(document, options.target) + : options.target + + let mounted: T | undefined + + const destroy = () => { + if (mounted != undefined) { + options.onDestroy?.(mounted) + mounted?.$destroy() + mounted = undefined + } + } + + const modHandle = registerMod({ + ...options, + + apply: () => { + destroy() + + void pollUntilResult( + () => mountSelector(), + // Prioritize cancelling if the mod is uninstalled while waiting. + () => !modHandle.isInstalled() || !!options.cancelCondition?.() + ) + .catch(e => { + if (e instanceof PollingCancelledError) { + console.error('Mounting Svelte component cancelled for mod', options.id) + return undefined + } else { + console.error('Error mounting Svelte component for mod', options.id, e) + } + }) + .then(target => { + if (!target) return + mounted = mountSvelteComponent({ + ...(options as typeof options & { props: ComponentProps }), + target, + }) + }) + }, + + revert: destroy, + }) + + return modHandle +} diff --git a/src/util/promises.ts b/src/util/promises.ts index 279eb5b2..2e05bde1 100644 --- a/src/util/promises.ts +++ b/src/util/promises.ts @@ -1,15 +1,48 @@ +export class PollingCancelledError extends Error { + constructor() { + super('Polling was cancelled') + this.name = 'PollingCancelledError' + } +} + /** - * Returns a promise that resolves when the given resolver function returns a non-null value - * @param resolver A function that returns a value or null - * @param interval The interval in milliseconds to check the resolver function + * Polls a function every frame until it returns a non-nullish value, or until a cancel condition is met or a timeout occurs. */ -export function pollPromise(resolver: () => T | undefined | null, interval?: 250) { - return new Promise(resolve => { - const id = setInterval(() => { - const result = resolver() - if (result === null || result === undefined) return - clearInterval(id) - resolve(result) - }, interval) +export function pollUntilResult( + /** + * The function to poll. Should return a non-nullish value to resolve the promise. + */ + callback: () => T | undefined | null, + /** + * If this condition returns true, polling will be cancelled. + */ + cancelCondition: () => boolean, + /** + * Maximum time to wait before timing out, in milliseconds. Defaults to 1 minute. + */ + timeout = 1000 * 60, + /** + * Time between polls in milliseconds. Defaults to polling every animation frame. + */ + interval?: number +): Promise { + const startTime = performance.now() + + const timeoutCallback = + interval !== undefined + ? (cb: FrameRequestCallback) => setTimeout(cb, interval) + : requestAnimationFrame + + return new Promise((resolve, reject) => { + const poll = () => { + if (cancelCondition()) return reject(new PollingCancelledError()) + const result = callback() + if (result != undefined) return resolve(result) + if (performance.now() - startTime > timeout) + return reject(new Error('Polling timed out')) + timeoutCallback(poll) + } + // poll() // Check immediately in case the condition is already met + timeoutCallback(poll) }) } diff --git a/src/util/resourceLocation.d.ts b/src/util/resourceLocation.d.ts new file mode 100644 index 00000000..38cb9d86 --- /dev/null +++ b/src/util/resourceLocation.d.ts @@ -0,0 +1,39 @@ +import type { Chars, RestrictString } from './stringUtils' + +// Underscore is purposefully excluded. Get over it :) +type NamespaceChars = Chars.LowercaseAlphaNumeric | '.' | '-' +type PathChars = NamespaceChars | '/' + +export namespace ResourceLocation { + export type ValidatePath

= [P] extends [RestrictString] + ? P + : `Invalid characters in path: ${Exclude}` + + export type ValidateNamespace = [N] extends [ + RestrictString + ] + ? N + : `Invalid characters in namespace: ${Exclude}` + + /** + * Validates Namespaced Resource Identifier. + * + * - Format: `namespace:path` + * - Allowed characters: [a-z0-9.-] + * + * ``` + * // Valid + * 'namespace:path', 'name-space:foo/bar.json', 'snavesutit.animated-java:blueprint-settings-dialog' + * + * // Invalid + * 'Namespace:Path', 'namespace:path:extra', 'namespacepath' + * ``` + */ + export type Validate = L extends `${infer N}:${infer P}` + ? [N] extends [RestrictString] + ? [P] extends [RestrictString] + ? L + : ValidatePath

+ : ValidateNamespace + : 'No namespace separator (:) found' +} diff --git a/src/util/stopwatch.ts b/src/util/stopwatch.ts new file mode 100644 index 00000000..cf72ce54 --- /dev/null +++ b/src/util/stopwatch.ts @@ -0,0 +1,73 @@ +import { roundTo } from './misc' + +export class Stopwatch { + protected startTime?: number + protected endTime?: number + protected duration?: number + + constructor(readonly name: string) {} + + start() { + if (this.startTime !== undefined) throw new Error('Stopwatch already started') + this.startTime = performance.now() + return this + } + + /** + * Stops the stopwatch, and logs the duration to the console. + */ + debug(...extra: any[]) { + this.stop() + const message = this.createMessage() + if (extra.length > 0) message[0] += '\n' + console.log(...message, ...extra) + return this + } + + /** + * Stops the stopwatch, and returns the duration in milliseconds. + */ + stop() { + if (this.startTime === undefined) throw new Error('Stopwatch not started') + this.endTime = performance.now() + this.duration = this.endTime - this.startTime + + return this.duration + } + + private createMessage() { + const durationMessage = + this.duration === undefined ? 'Not finished' : `${roundTo(this.duration, 5)}ms` + return [ + `%c⏱️ ${this.name}%c: %c${durationMessage}`, + 'color: #FFFF55', + 'color: #AAAAAA', + 'color: #55FFFF', + ] + } + + /** + * Wraps a function to measure its execution time. + * + * If the function returns a promise, the duration will be tracked until the promise settles. + */ + static function any>(name: string, func: T): T { + return ((...args: Parameters) => { + const stopwatch = new Stopwatch(name).start() + const result = func(...args) + if (result instanceof Promise) { + return result.finally(stopwatch.debug.bind(stopwatch)) + } + stopwatch.debug() + return result + }) as T + } + + /** + * Tracks the duration of a promise and logs it when the promise settles. + */ + static promise(name: string, promise: Promise): Promise { + const stopwatch = new Stopwatch(name).start() + return promise.finally(stopwatch.debug.bind(stopwatch)) + } +} diff --git a/src/util/stores.ts b/src/util/stores.ts index 809ee975..6e9407c5 100644 --- a/src/util/stores.ts +++ b/src/util/stores.ts @@ -8,7 +8,7 @@ export class Valuable implements Writable { constructor(value: T, valueValidator?: Valuable['valueValidator']) { this.store = writable(value) - this.valueValidator = valueValidator || ((value: T) => value) + this.valueValidator = valueValidator ?? ((value: T) => value) Valuable.all.push(this) } diff --git a/src/util/stringUtils.d.ts b/src/util/stringUtils.d.ts new file mode 100644 index 00000000..e9da8b76 --- /dev/null +++ b/src/util/stringUtils.d.ts @@ -0,0 +1,47 @@ +// This is a big mess to reduce recursion +export type SplitString = Splitter extends '' + ? String extends `${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer Rest}` + ? C1 | C2 | C3 | C4 | C5 | SplitString + : String extends `${infer C1}${infer C2}${infer C3}${infer C4}${infer Rest}` + ? C1 | C2 | C3 | C4 | (Rest extends '' ? never : SplitString) + : String extends `${infer C1}${infer C2}${infer C3}${infer Rest}` + ? C1 | C2 | C3 | (Rest extends '' ? never : SplitString) + : String extends `${infer C1}${infer C2}${infer Rest}` + ? C1 | C2 | (Rest extends '' ? never : SplitString) + : String extends `${infer C1}${infer Rest}` + ? C1 | (Rest extends '' ? never : SplitString) + : String + : String extends `${infer Before}${Splitter}${infer After}` + ? Before | (After extends '' ? never : SplitString) + : String + +export type SliceString< + String extends string, + Slicer extends string, + Return extends 'before' | 'after' +> = String extends `${infer Before}${Slicer}${infer After}` + ? Return extends 'before' + ? Before + : After + : String + +export type JoinStrings = Strings extends [] + ? '' + : Strings extends [infer Only extends string] + ? Only + : never + +export namespace Chars { + export type LowercaseAlpha = SplitString<'abcdefghijklmnopqrstuvwxyz'> + export type UppercaseAlpha = SplitString<'ABCDEFGHIJKLMNOPQRSTUVWXYZ'> + export type Number = SplitString<'0123456789'> + export type AlphaNumeric = LowercaseAlpha | UppercaseAlpha | Number + export type LowercaseAlphaNumeric = LowercaseAlpha | Number + export type UppercaseAlphaNumeric = UppercaseAlpha | Number +} + +export type RestrictString = [ + SplitString +] extends [ValidChars] + ? String + : never diff --git a/src/util/subscribable.ts b/src/util/subscribable.ts index c3b0a223..b4aeff6e 100644 --- a/src/util/subscribable.ts +++ b/src/util/subscribable.ts @@ -1,36 +1,42 @@ +export interface Subscribable { + subscribe: (callback: (value: T) => void, oneShot?: boolean) => () => void + publish: (value: T) => void +} + /** - * A class that can be used to create a subscribable object. + * Creates a simple subscribable object. * @template T The type of the value that is passed to the subscribers. */ -export class Subscribable { - protected subscribers = new Set<(value: T) => void>() - private dispatching = false - - /** - * Subscribe to this subscribable. - * @param callback The callback to be called when the subscribable is dispatched. - * @param oneShot If true, the callback will be removed after it is called once. - * @returns A function that can be called to unsubscribe the callback. - */ - subscribe(callback: (value: T) => void, oneShot = false) { - if (oneShot) { - const wrappedCallback = (value: T) => { - callback(value) - this.subscribers.delete(wrappedCallback) - } - this.subscribers.add(wrappedCallback) - } else this.subscribers.add(callback) - return () => this.subscribers.delete(callback) - } - - /** - * Dispatch a value to all subscribers. - * @param value The value to be passed to the subscribers. - */ - dispatch(value: T) { - if (this.dispatching) return - this.dispatching = true - this.subscribers.forEach(callback => callback(value)) - this.dispatching = false +export function subscribable(): Subscribable { + const subscribers = new Set<(value: T) => void>() + // Used to prevent recursive publishing + let publishing = false + return { + /** + * Subscribe to this subscribable. + * @param callback The callback to be called when the subscribable is published. + * @param oneShot If true, the callback will be removed after it is called once. + * @returns A function that can be called to unsubscribe the callback. + */ + subscribe: (callback: (value: T) => void, oneShot = false) => { + if (oneShot) { + const wrappedCallback = (value: T) => { + callback(value) + subscribers.delete(wrappedCallback) + } + subscribers.add(wrappedCallback) + } else subscribers.add(callback) + return () => subscribers.delete(callback) + }, + /** + * Publish a value to all subscribers. + * @param value The value to be passed to the subscribers. + */ + publish: (value: T) => { + if (publishing) return + publishing = true + subscribers.forEach(callback => callback(value)) + publishing = false + }, } } diff --git a/src/util/svelteDialog.ts b/src/util/svelteDialog.ts index 9d1c94d2..7fa539ed 100644 --- a/src/util/svelteDialog.ts +++ b/src/util/svelteDialog.ts @@ -1,16 +1,32 @@ -import type { ComponentConstructorOptions } from 'svelte' -import type { SvelteComponentDev } from 'svelte/internal' -import type { SvelteComponentConstructor } from './misc' +import type { SvelteComponent } from 'svelte' +import type { SvelteComponentOptions } from './svelteUtil' -const DIALOG_STACK: Array> = [] +const DIALOG_STACK: SvelteDialog[] = [] -type SvelteDialogOptions = Omit< - DialogOptions, - 'lines' -> & { +export type CssStyleObject = { + [K in keyof CSSStyleDeclaration as K extends string + ? CSSStyleDeclaration[K] extends string + ? K + : never + : never]?: CSSStyleDeclaration[K] +} + +type SvelteDialogOptions< + DialogContent extends SvelteComponent, + DialogExtra extends SvelteComponent +> = Omit & { id: string - component: SvelteComponentConstructor - props: U['props'] + content: SvelteComponentOptions + /** A svelte component to be appended directly to the dialog.dialog element. */ + extra?: SvelteComponentOptions + + /** Styles to be applied to the default content element */ + contentStyle?: CssStyleObject + /** Styles to be applied to the default wrapper element */ + wrapperStyle?: CssStyleObject + /** Styles to be applied to the dialog element */ + dialogStyle?: CssStyleObject + preventKeybinds?: boolean preventKeybindConfirm?: boolean preventKeybindCancel?: boolean @@ -18,12 +34,28 @@ type SvelteDialogOptions = Omit< stackable?: boolean } -export class SvelteDialog> extends Dialog { - instance?: SvelteComponentDev | undefined - constructor(options: SvelteDialogOptions>) { +function applyStyleObject(element: HTMLElement, style: CssStyleObject) { + for (const [key, value] of Object.entries(style)) { + // @ts-expect-error - Index type of CSSStyleDeclaration is number for some reason + element.style[key] = value ?? '' + } +} + +export class SvelteDialog< + DialogContent extends SvelteComponent = any, + DialogExtra extends SvelteComponent = any +> extends Dialog { + wrapperElement: HTMLDivElement | null = null + contentElement: HTMLDivElement | null = null + + contentComponent: SvelteComponent | null = null + extraComponent: SvelteComponent | null = null + + constructor(options: SvelteDialogOptions) { const mount = document.createComment(`svelte-dialog-` + guid()) const dialogOptions = { ...options } + // @ts-expect-error Not in DialogOptions delete dialogOptions.component super(options.id, { @@ -32,20 +64,51 @@ export class SvelteDialog> extends Dialog { }) this.onOpen = () => { - const parentElement = mount.parentElement - if (this.instance || !parentElement) return - parentElement.style.overflow = 'visible' - // @ts-ignore - this.instance = new options.component({ - target: parentElement, - props: options.props, + if (this.contentComponent) return + + this.contentElement = mount.parentElement! as HTMLDivElement + if (!this.contentElement) { + console.error('Failed to find dialog content element') + return + } + + this.wrapperElement = this.contentElement.parentElement! as HTMLDivElement + if (!this.wrapperElement) { + console.error('Failed to find dialog wrapper element') + return + } + + if (options.contentStyle) applyStyleObject(this.contentElement, options.contentStyle) + if (options.wrapperStyle) applyStyleObject(this.wrapperElement, options.wrapperStyle) + if (options.dialogStyle) applyStyleObject(this.object!, options.dialogStyle) + + this.contentComponent = new options.content.component({ + target: this.contentElement, + props: options.content.props!, }) + + if (options.extra) { + this.extraComponent = new options.extra.component({ + target: this.object!, + props: options.extra.props!, + }) + } + if (options.onOpen) options.onOpen() if (!options.stackable) { DIALOG_STACK.forEach(v => v.cancel()) DIALOG_STACK.empty() } DIALOG_STACK.push(this) + + requestAnimationFrame(() => { + // Center the dialog vertically, but shifted a bit upwards + const jqObject = $(this.object!) + const height = jqObject.height()! + const diff = Math.max(window.innerHeight - height, 0) + const offset = diff * 0.25 + this.object!.style.top = Math.clamp(diff / 2 - offset, 26, 2000) + 'px' + }) } this.confirm = (e: Event) => { @@ -74,17 +137,19 @@ export class SvelteDialog> extends Dialog { } this.onButton = (...args) => { - if (!this.instance) return - this.instance.$destroy() - this.instance = undefined + if (!this.contentComponent) return + this.extraComponent?.$destroy() + this.contentComponent.$destroy() + this.contentComponent = null if (options.onButton) options.onButton(...args) if (options.onClose) options.onClose() } this.onCancel = (...args) => { - if (!this.instance) return - this.instance.$destroy() - this.instance = undefined + if (!this.contentComponent) return + this.extraComponent?.$destroy() + this.contentComponent.$destroy() + this.contentComponent = null if (options.onCancel) options.onCancel(...args) if (options.onClose) options.onClose() } diff --git a/src/util/sveltePanel.ts b/src/util/sveltePanel.ts index c88a5a0f..c400b0c7 100644 --- a/src/util/sveltePanel.ts +++ b/src/util/sveltePanel.ts @@ -1,7 +1,7 @@ -import { ComponentConstructorOptions, SvelteComponent } from 'svelte' -import { SvelteComponentConstructor } from './misc' -import * as PACKAGE from '../../package.json' -import { pollPromise } from '../util/promises' +import { type ComponentConstructorOptions, SvelteComponent } from 'svelte' +import { PACKAGE } from '../constants' +import { pollUntilResult } from '../util/promises' +import { type SvelteComponentConstructor } from './misc' type SveltePanelOptions = Omit< PanelOptions, @@ -25,7 +25,10 @@ export class SveltePanel extends Panel { }, }) - void pollPromise(() => document.querySelector(`#${mountId}`)).then(el => { + void pollUntilResult( + () => document.querySelector(`#${mountId}`), + () => false + ).then(el => { this.instance = new options.component({ target: el.parentElement, props: options.props, diff --git a/src/util/svelteUtil.d.ts b/src/util/svelteUtil.d.ts new file mode 100644 index 00000000..51c92ce3 --- /dev/null +++ b/src/util/svelteUtil.d.ts @@ -0,0 +1,14 @@ +import type { ComponentProps, ComponentType, SvelteComponent } from 'svelte' + +export type SvelteComponentOptions = ComponentProps extends Record< + string, + never +> + ? { + component: ComponentType + props?: ComponentProps + } + : { + component: ComponentType + props: ComponentProps + } diff --git a/src/util/translation.ts b/src/util/translation.ts index 65711ef5..5016fac3 100644 --- a/src/util/translation.ts +++ b/src/util/translation.ts @@ -1,4 +1,4 @@ -// @ts-ignore +// @ts-expect-error No types import { default as LANGUAGES, filenames as filepaths } from '../lang/*.yml' const FILE_NAMES = filepaths.map((path: string) => PathModule.basename(path, '.yml')) @@ -21,5 +21,3 @@ export function translate(key: string, ...args: string[]) { return key } } - -Language.data['format_category.animated_java'] = translate('format_category.animated_java') diff --git a/src/util/unicodeString.ts b/src/util/unicodeString.ts deleted file mode 100644 index 35e15fe8..00000000 --- a/src/util/unicodeString.ts +++ /dev/null @@ -1,51 +0,0 @@ -export class UnicodeString { - static regex = /[^]/gmu - - private chars: string[] = [] - - constructor(public str: string) { - if (str === '') return - for (const char of str.matchAll(UnicodeString.regex)) { - this.chars.push(char[0]) - } - } - - [Symbol.iterator]() { - return this.chars[Symbol.iterator]() - } - - get length() { - return this.chars.length - } - - includes(search: string) { - return this.chars.includes(search) - } - - indexOf(search: string) { - return this.chars.indexOf(search) - } - - slice(start: number, end?: number) { - const chars = this.chars.slice(start, end) - return UnicodeString.fromChars(chars) - } - - at(index: number) { - if (index < 0 || index >= this.chars.length) return undefined - return this.chars[index] - } - - append(char: string) { - this.chars.push(char) - this.str += char - } - - toString() { - return this.str - } - - static fromChars(chars: string[]) { - return new UnicodeString(chars.join('')) - } -} diff --git a/src/util/utilityTypes.d.ts b/src/util/utilityTypes.d.ts new file mode 100644 index 00000000..58f37cb5 --- /dev/null +++ b/src/util/utilityTypes.d.ts @@ -0,0 +1,26 @@ +export interface Tuple extends Array { + 0: T + length: L +} + +/** Gets all keys of an object union */ +export type AllKeys = T extends any ? keyof T : never +/** Gets keys that are common between all members of a object union */ +export type CommonKeys = Extract, keyof T> +/** Gets keys that aren't common between all members of a object union */ +export type UncommonKeys = Exclude, CommonKeys> +/** Gets the type of a key in an object, or undefined if the key doesn't exist in the object */ +// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style +export type PickType> = T extends { [k in K]?: any } ? T[K] : undefined +/** Gets the type of a key in an object union, or never if the key doesn't exist in any member of the union */ +export type PickTypeOf = K extends AllKeys + ? PickType + : never +/** + * Merges an object union into a single object, where keys that are common between all members are required, and keys that aren't common are optional. + */ +export type MergeUnion = { + [k in CommonKeys]: PickTypeOf +} & { + [k in UncommonKeys]?: PickTypeOf +} diff --git a/src/variants.ts b/src/variants.ts index 4352a971..97504b70 100644 --- a/src/variants.ts +++ b/src/variants.ts @@ -1,6 +1,6 @@ -import { IBlueprintVariantJSON } from './blueprintFormat' -import { events } from './util/events' -import { sanitizePathName } from './util/minecraftUtil' +import type { IBlueprintVariantJSON } from './formats/blueprint' +import EVENTS from './util/events' +import { sanitizeStorageKey } from './util/minecraftUtil' export class TextureMap { map: Map @@ -9,39 +9,39 @@ export class TextureMap { this.map = new Map() } - public add(key: string, value: string) { + add(key: string, value: string) { this.map.set(key, value) } - public get(key: string) { + get(key: string) { return this.map.get(key) } - public has(key: string) { + has(key: string) { return this.map.has(key) } - public delete(key: string) { + delete(key: string) { this.map.delete(key) } /** * Given a texture or texture uuid, return the mapped texture */ - public getMappedTexture(texture: Texture | string): Texture | undefined { + getMappedTexture(texture: Texture | string): Texture | undefined { const uuid = this.map.get(texture instanceof Texture ? texture.uuid : texture) return Texture.all.find(t => t.uuid === uuid) } - public setMappedTexture(texture: Texture, mappedTexture: Texture) { + setMappedTexture(texture: Texture, mappedTexture: Texture) { this.map.set(texture.uuid, mappedTexture.uuid) } - public toJSON() { + toJSON() { return Object.fromEntries(this.map) } - public static fromJSON(json: Record): TextureMap { + static fromJSON(json: Record): TextureMap { const textureMap = new TextureMap() for (const [key, value] of Object.entries(json)) { textureMap.add(key, value) @@ -49,13 +49,13 @@ export class TextureMap { return textureMap } - public copy() { + copy() { const textureMap = new TextureMap() textureMap.map = new Map(this.map) return textureMap } - public verifyTextures() { + verifyTextures() { for (const [key, value] of this.map) { if (!Texture.all.some(t => t.uuid === value)) { this.map.delete(key) @@ -73,17 +73,17 @@ export class VariantBoneConfig { } export class Variant { - public static all: Variant[] = [] - public static selected: Variant | undefined - - public id: number - public displayName: string - public name: string - public uuid: string - public textureMap: TextureMap - public isDefault = false - public generateNameFromDisplayName = true - public excludedNodes: CollectionItem[] = [] + static all: Variant[] = [] + static selected: Variant | undefined + + id: number + displayName: string + name: string + uuid: string + textureMap: TextureMap + isDefault = false + generateNameFromDisplayName = true + excludedNodes: CollectionItem[] = [] constructor(displayName: string, isDefault = false) { this.displayName = Variant.makeDisplayNameUnique(this, displayName) @@ -98,21 +98,21 @@ export class Variant { } Variant.all.push(this) // this.select() - events.CREATE_VARIANT.dispatch(this) + EVENTS.CREATE_VARIANT.publish(this) } - public select() { + select() { if (Variant.selected) Variant.selected.unselect() Variant.selected = this Canvas.updateAllFaces() - events.SELECT_VARIANT.dispatch(this) + EVENTS.SELECT_VARIANT.publish(this) } - public unselect() { + unselect() { Variant.selected = undefined } - public delete() { + delete() { // Cannot delete default variant if (this.isDefault) return @@ -126,10 +126,10 @@ export class Variant { Variant.selectDefault() } - events.DELETE_VARIANT.dispatch(this) + EVENTS.DELETE_VARIANT.publish(this) } - public toJSON() { + toJSON() { const json: IBlueprintVariantJSON = { name: this.name, display_name: this.displayName, @@ -143,7 +143,7 @@ export class Variant { return json } - public duplicate() { + duplicate() { const variant = new Variant(this.displayName, false) variant.uuid = guid() variant.isDefault = false @@ -153,11 +153,11 @@ export class Variant { variant.select() } - public verifyTextureMap() { + verifyTextureMap() { this.textureMap.verifyTextures() } - public static fromJSON(json: IBlueprintVariantJSON, isDefault = false): Variant { + static fromJSON(json: IBlueprintVariantJSON, isDefault = false): Variant { const variant = new Variant(json.display_name, isDefault) variant.uuid = json.uuid if (json.is_default) { @@ -175,13 +175,13 @@ export class Variant { return variant } - public static makeDisplayNameUnique(variant: Variant, displayName: string): string { + static makeDisplayNameUnique(variant: Variant, displayName: string): string { if (!Variant.all.some(v => v !== variant && v.displayName === displayName)) { return displayName } let i = 1 - const match = displayName.match(/\d+$/) + const match = /\d+$/.exec(displayName) if (match) { i = parseInt(match[0]) displayName = displayName.slice(0, -match[0].length) @@ -199,14 +199,14 @@ export class Variant { throw new Error('Could not make Variant display name unique!') } - public static makeNameUnique(variant: Variant, name: string): string { - name = sanitizePathName(name) + static makeNameUnique(variant: Variant, name: string): string { + name = sanitizeStorageKey(name) if (!Variant.all.some(v => v !== variant && v.name === name)) { return name } let i = 1 - const match = name.match(/\d+$/) + const match = /\d+$/.exec(name) if (match) { i = parseInt(match[0]) name = name.slice(0, -match[0].length) @@ -224,20 +224,20 @@ export class Variant { throw new Error('Could not make Variant name unique!') } - public static selectDefault() { + static selectDefault() { const variant = Variant.all.find(v => v.isDefault) if (variant) variant.select() } - public static getDefault(): Variant { + static getDefault(): Variant { return Variant.all.find(v => v.isDefault) ?? Variant.all[0] } } -events.SELECT_PROJECT.subscribe(project => { +EVENTS.SELECT_PROJECT.subscribe(project => { project.variants ??= [] Variant.all = project.variants }) -events.UNSELECT_PROJECT.subscribe(() => { +EVENTS.UNSELECT_PROJECT.subscribe(() => { Variant.all = [] }) diff --git a/svelte.config.ts b/svelte.config.ts index be3ebfe1..8d9e5be8 100644 --- a/svelte.config.ts +++ b/svelte.config.ts @@ -17,16 +17,14 @@ export const preprocess = [ prep({ typescript: false }), ] -const IMPORT_PATH = resolve(__dirname, '../src/util/', 'events.ts') +const IMPORT_PATH = resolve(__dirname, '../src/util/', 'events.ts').replace(/\\/g, '/') -export const transformCssToJs = ( - css: string -) => `import { events as SVELTE_EVENTS } from ${JSON.stringify(IMPORT_PATH)}; -(() => { - const $deletable = Blockbench.addCSS(${JSON.stringify(css)}); - function DELETE_SVELTE_CSS() { $deletable?.delete() } - SVELTE_EVENTS.UNLOAD.subscribe(DELETE_SVELTE_CSS, true); - SVELTE_EVENTS.UNINSTALL.subscribe(DELETE_SVELTE_CSS, true); -})()` +export const transformCssToJs = (css: string) => + `import SVELTE_EVENTS from '${IMPORT_PATH}'; + (() => { + var css; + SVELTE_EVENTS.PLUGIN_LOAD.subscribe(() => css = Blockbench.addCSS(${JSON.stringify(css)})); + SVELTE_EVENTS.PLUGIN_UNLOAD.subscribe(() => css?.delete()); + })()`.replace(/[\t\n]/g, '') export default { preprocess, transformCssToJs } diff --git a/test-packs/1.20.4/blueprints/ajbooth_witch_broom_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/ajbooth_witch_broom_1.20.4.ajblueprint new file mode 100644 index 00000000..1c979ad7 --- /dev/null +++ b/test-packs/1.20.4/blueprints/ajbooth_witch_broom_1.20.4.ajblueprint @@ -0,0 +1,2728 @@ +{ + "meta": { + "format": "animated_java_blueprint", + "format_version": "1.7.3", + "uuid": "5603b13d-d160-8027-d740-d40383fe7c83", + "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.20.4\\blueprints\\ajbooth_witch_broom.ajblueprint", + "last_used_export_namespace": "ajbooth_witch_broom" + }, + "blueprint_settings": { + "export_namespace": "ajbooth_witch_broom", + "show_bounding_box": false, + "auto_bounding_box": true, + "bounding_box": [48, 48], + "enable_plugin_mode": false, + "resource_pack_export_mode": "raw", + "data_pack_export_mode": "raw", + "target_minecraft_versions": ["1.20.4"], + "display_item": "minecraft:white_dye", + "custom_model_data_offset": 0, + "enable_advanced_resource_pack_settings": false, + "resource_pack": "../resources", + "enable_advanced_data_pack_settings": false, + "data_pack": "../datapacks/animated_java", + "summon_commands": "", + "remove_commands": "", + "ticking_commands": "", + "interpolation_duration": 1, + "teleportation_duration": 1, + "use_storage_for_animation": false, + "show_function_errors": false, + "show_outdated_warning": false, + "baked_animations": true, + "json_file": "", + "target_minecraft_version": "1.21.4", + "enable_advanced_resource_pack_folders": false, + "display_item_path": "", + "model_folder": "", + "texture_folder": "" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "elements": [ + { + "name": "Body", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -2], + "to": [4, 24, 2], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "uv_offset": [16, 16], + "faces": { + "north": { + "uv": [20, 20, 28, 32], + "texture": 0 + }, + "east": { + "uv": [16, 20, 20, 32], + "texture": 0 + }, + "south": { + "uv": [32, 20, 40, 32], + "texture": 0 + }, + "west": { + "uv": [28, 20, 32, 32], + "texture": 0 + }, + "up": { + "uv": [28, 20, 20, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 28, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "45d82418-2321-4f24-9a63-b5bc2057e800" + }, + { + "name": "Body Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -2], + "to": [4, 24, 2], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, 0], + "uv_offset": [16, 32], + "faces": { + "north": { + "uv": [20, 36, 28, 48], + "texture": 0 + }, + "east": { + "uv": [16, 36, 20, 48], + "texture": 0 + }, + "south": { + "uv": [32, 36, 40, 48], + "texture": 0 + }, + "west": { + "uv": [28, 36, 32, 48], + "texture": 0 + }, + "up": { + "uv": [28, 36, 20, 32], + "texture": 0 + }, + "down": { + "uv": [36, 32, 28, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "37dca7a9-789b-3984-4d16-fdaa3b292048" + }, + { + "name": "Body Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 24, 2], + "to": [-4, 12, -2], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [8, 12, 4], + "uv_offset": [16, 32], + "faces": { + "north": { + "uv": [32, 48, 40, 36], + "texture": 0 + }, + "east": { + "uv": [28, 48, 32, 36], + "texture": 0 + }, + "south": { + "uv": [20, 48, 28, 36], + "texture": 0 + }, + "west": { + "uv": [16, 48, 20, 36], + "texture": 0 + }, + "up": { + "uv": [28, 32, 36, 36], + "texture": 0 + }, + "down": { + "uv": [20, 36, 28, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "53130d68-3a4f-7a65-51bc-882312407cb7" + }, + { + "name": "Right Arm", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 12, -2.05], + "to": [7, 24, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [40, 16], + "faces": { + "north": { + "uv": [44, 20, 47, 32], + "texture": 0 + }, + "east": { + "uv": [40, 20, 44, 32], + "texture": 0 + }, + "south": { + "uv": [51, 20, 54, 32], + "texture": 0 + }, + "west": { + "uv": [47, 20, 51, 32], + "texture": 0 + }, + "up": { + "uv": [47, 20, 44, 16], + "texture": 0 + }, + "down": { + "uv": [50, 16, 47, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a22f9634-063d-97f7-86c5-24256821bb96" + }, + { + "name": "Right Arm Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 12, -2.05], + "to": [7, 24, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [40, 32], + "faces": { + "north": { + "uv": [44, 36, 47, 48], + "texture": 0 + }, + "east": { + "uv": [40, 36, 44, 48], + "texture": 0 + }, + "south": { + "uv": [51, 36, 54, 48], + "texture": 0 + }, + "west": { + "uv": [47, 36, 51, 48], + "texture": 0 + }, + "up": { + "uv": [47, 36, 44, 32], + "texture": 0 + }, + "down": { + "uv": [50, 32, 47, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "6b4537cd-19f5-52e2-fa6a-4b8cd831ff30" + }, + { + "name": "Right Arm Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [7, 24, 1.95], + "to": [4, 12, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [40, 32], + "faces": { + "north": { + "uv": [51, 48, 54, 36], + "texture": 0 + }, + "east": { + "uv": [47, 48, 51, 36], + "texture": 0 + }, + "south": { + "uv": [44, 48, 47, 36], + "texture": 0 + }, + "west": { + "uv": [40, 48, 44, 36], + "texture": 0 + }, + "up": { + "uv": [47, 32, 50, 36], + "texture": 0 + }, + "down": { + "uv": [44, 36, 47, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "db64cc71-2153-401a-dd3c-b1cea98adebe" + }, + { + "name": "Left Arm", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -2.05], + "to": [-4, 24, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [32, 48], + "faces": { + "north": { + "uv": [36, 52, 39, 64], + "texture": 0 + }, + "east": { + "uv": [32, 52, 36, 64], + "texture": 0 + }, + "south": { + "uv": [43, 52, 46, 64], + "texture": 0 + }, + "west": { + "uv": [39, 52, 43, 64], + "texture": 0 + }, + "up": { + "uv": [39, 52, 36, 48], + "texture": 0 + }, + "down": { + "uv": [42, 48, 39, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "7ff42713-1443-789a-5165-dbef83209ad8" + }, + { + "name": "Left Arm Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -2.05], + "to": [-4, 24, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [48, 48], + "faces": { + "north": { + "uv": [52, 52, 55, 64], + "texture": 0 + }, + "east": { + "uv": [48, 52, 52, 64], + "texture": 0 + }, + "south": { + "uv": [59, 52, 62, 64], + "texture": 0 + }, + "west": { + "uv": [55, 52, 59, 64], + "texture": 0 + }, + "up": { + "uv": [55, 52, 52, 48], + "texture": 0 + }, + "down": { + "uv": [59, 48, 55, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "693f4ac0-3774-4a83-c93b-2a2951987a65" + }, + { + "name": "Left Arm Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, 1.95], + "to": [-7, 12, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [48, 48], + "faces": { + "north": { + "uv": [59, 64, 62, 52], + "texture": 0 + }, + "east": { + "uv": [55, 64, 59, 52], + "texture": 0 + }, + "south": { + "uv": [52, 64, 55, 52], + "texture": 0 + }, + "west": { + "uv": [48, 64, 52, 52], + "texture": 0 + }, + "up": { + "uv": [55, 48, 58, 52], + "texture": 0 + }, + "down": { + "uv": [52, 52, 55, 48], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55a3b67f-8f28-db5a-8b77-62ce1e10fcc4" + }, + { + "name": "Head", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, -4], + "to": [4, 32, 4], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [8, 8, 16, 16], + "texture": 0 + }, + "east": { + "uv": [0, 8, 8, 16], + "texture": 0 + }, + "south": { + "uv": [24, 8, 32, 16], + "texture": 0 + }, + "west": { + "uv": [16, 8, 24, 16], + "texture": 0 + }, + "up": { + "uv": [16, 8, 8, 0], + "texture": 0 + }, + "down": { + "uv": [24, 0, 16, 8], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9a20434d-4a80-a43d-b8f4-c747f469e12b" + }, + { + "name": "Hat Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, -4], + "to": [4, 32, 4], + "autouv": 0, + "color": 0, + "inflate": 0.5, + "origin": [0, 0.5, 0], + "uv_offset": [32, 0], + "faces": { + "north": { + "uv": [40, 8, 48, 16], + "texture": 0 + }, + "east": { + "uv": [32, 8, 40, 16], + "texture": 0 + }, + "south": { + "uv": [56, 8, 64, 16], + "texture": 0 + }, + "west": { + "uv": [48, 8, 56, 16], + "texture": 0 + }, + "up": { + "uv": [48, 8, 40, 0], + "texture": 0 + }, + "down": { + "uv": [56, 0, 48, 8], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f9c88722-d723-f7de-ce1d-40d630c00049" + }, + { + "name": "Hat Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 32, 4], + "to": [-4, 24, -4], + "autouv": 0, + "color": 0, + "inflate": -0.5, + "origin": [8, 8.5, 8], + "uv_offset": [32, 0], + "faces": { + "north": { + "uv": [56, 16, 64, 8], + "texture": 0 + }, + "east": { + "uv": [48, 16, 56, 8], + "texture": 0 + }, + "south": { + "uv": [40, 16, 48, 8], + "texture": 0 + }, + "west": { + "uv": [32, 16, 40, 8], + "texture": 0 + }, + "up": { + "uv": [48, 0, 56, 8], + "texture": 0 + }, + "down": { + "uv": [40, 8, 48, 0], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55fb5425-1275-3a16-70a7-4b6276ac5999" + }, + { + "name": "Right Leg", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.1, 0, -2], + "to": [3.9, 12, 2], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "uv_offset": [0, 16], + "faces": { + "north": { + "uv": [4, 20, 8, 32], + "texture": 0 + }, + "east": { + "uv": [0, 20, 4, 32], + "texture": 0 + }, + "south": { + "uv": [12, 20, 16, 32], + "texture": 0 + }, + "west": { + "uv": [8, 20, 12, 32], + "texture": 0 + }, + "up": { + "uv": [8, 20, 4, 16], + "texture": 0 + }, + "down": { + "uv": [12, 16, 8, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "73b9fdbc-c2f9-e43c-ef6d-bc159d183193" + }, + { + "name": "Right Leg Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.1, 0, -2], + "to": [3.9, 12, 2], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, 0], + "uv_offset": [0, 32], + "faces": { + "north": { + "uv": [4, 36, 8, 48], + "texture": 0 + }, + "east": { + "uv": [0, 36, 4, 48], + "texture": 0 + }, + "south": { + "uv": [12, 36, 16, 48], + "texture": 0 + }, + "west": { + "uv": [8, 36, 12, 48], + "texture": 0 + }, + "up": { + "uv": [8, 36, 4, 32], + "texture": 0 + }, + "down": { + "uv": [12, 32, 8, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "ac495b61-fa9f-44f0-7d4c-30799a96a4c8" + }, + { + "name": "Right Leg Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3.9, 12, 2], + "to": [-0.1, 0, -2], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 4], + "uv_offset": [0, 32], + "faces": { + "north": { + "uv": [12, 48, 16, 36], + "texture": 0 + }, + "east": { + "uv": [8, 48, 12, 36], + "texture": 0 + }, + "south": { + "uv": [4, 48, 8, 36], + "texture": 0 + }, + "west": { + "uv": [0, 48, 4, 36], + "texture": 0 + }, + "up": { + "uv": [8, 32, 12, 36], + "texture": 0 + }, + "down": { + "uv": [4, 36, 8, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "426f5735-a6d5-8fdd-78de-06675b178e88" + }, + { + "name": "Left Leg", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.9, 0, -2.05], + "to": [0.1, 12, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [16, 48], + "faces": { + "north": { + "uv": [20, 52, 24, 64], + "texture": 0 + }, + "east": { + "uv": [16, 52, 20, 64], + "texture": 0 + }, + "south": { + "uv": [28, 52, 32, 64], + "texture": 0 + }, + "west": { + "uv": [24, 52, 28, 64], + "texture": 0 + }, + "up": { + "uv": [24, 52, 20, 48], + "texture": 0 + }, + "down": { + "uv": [28, 48, 24, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "de1c2115-133b-e2de-0275-6d784aee8ecc" + }, + { + "name": "Left Leg Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.9, 0, -2.05], + "to": [0.1, 12, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [0, 48], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 0 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 0 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 0 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 0 + }, + "up": { + "uv": [8, 52, 4, 48], + "texture": 0 + }, + "down": { + "uv": [12, 48, 8, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "0185a3b1-67bf-38b5-373f-6a6facb044bc" + }, + { + "name": "Left Leg Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0.1, 12, 1.95], + "to": [-3.9, 0, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [0, 48], + "faces": { + "north": { + "uv": [12, 64, 16, 52], + "texture": 0 + }, + "east": { + "uv": [8, 64, 12, 52], + "texture": 0 + }, + "south": { + "uv": [4, 64, 8, 52], + "texture": 0 + }, + "west": { + "uv": [0, 64, 4, 52], + "texture": 0 + }, + "up": { + "uv": [8, 48, 12, 52], + "texture": 0 + }, + "down": { + "uv": [4, 52, 8, 48], + "texture": 0 + } + }, + "type": "cube", + "uuid": "da1792fe-8974-f1bb-88bd-262e759f1175" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 10, -16], + "to": [1, 12, 22], + "autouv": 0, + "color": 3, + "origin": [0, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 4, 42, 6], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 2, 4, 0], + "texture": 2 + }, + "up": { + "uv": [42, 4, 4, 2], + "rotation": 90, + "texture": 2 + }, + "down": { + "uv": [4, 6, 42, 8], + "rotation": 90, + "texture": 2 + } + }, + "type": "cube", + "uuid": "63947e2c-8407-1963-bf31-8eee08aee500" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.5, 7.5, 22], + "to": [3.5, 14.5, 32], + "autouv": 0, + "color": 3, + "origin": [-0.5, 10.5, 37], + "uv_offset": [28, 16], + "faces": { + "north": { + "uv": [38, 26, 45, 33], + "texture": 2 + }, + "east": { + "uv": [28, 26, 38, 33], + "texture": 2 + }, + "south": { + "uv": [55, 26, 62, 33], + "texture": 2 + }, + "west": { + "uv": [45, 26, 55, 33], + "texture": 2 + }, + "up": { + "uv": [45, 26, 38, 16], + "texture": 2 + }, + "down": { + "uv": [52, 16, 45, 26], + "texture": 2 + } + }, + "type": "cube", + "uuid": "53f25d61-a8ce-e2c9-4ccb-32f0b688f827" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 13, -8.05], + "to": [6, 14, 3.95], + "autouv": 0, + "color": 1, + "origin": [5, 12, -2.05], + "faces": { + "north": { + "uv": [0, 0, 1, 1], + "texture": 2 + }, + "east": { + "uv": [0, 0, 1, 12], + "rotation": 90, + "texture": 2 + }, + "south": { + "uv": [0, 0, 1, 1], + "texture": 2 + }, + "west": { + "uv": [0, 12, 1, 0], + "rotation": 90, + "texture": 2 + }, + "up": { + "uv": [0, 0, 1, 12], + "texture": 2 + }, + "down": { + "uv": [0, 12, 1, 0], + "texture": 2 + } + }, + "type": "cube", + "uuid": "164667fe-1cad-e25a-efd1-1a3716dfbbf9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.474, 14.95148, -12.73926], + "to": [5.524, 15.95148, -7.73926], + "autouv": 0, + "color": 1, + "rotation": [-22.5, 0, 0], + "origin": [5.5, 14.95148, -7.73926], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [0, 4, 20, 8], + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [20, 4, 0, 8], + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "9b9e03f6-1324-ab71-11bc-b08061b7712a" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.399, 13, -12.55], + "to": [5.599, 14, -7.55], + "autouv": 0, + "color": 1, + "rotation": [22.5, 0, 0], + "origin": [5.5, 13.5, -12.55], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [36, 4, 16, 8], + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [16, 4, 36, 8], + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "e39f2736-2736-e6fb-c569-4a6f55c55aaa" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.449, 11.12464, -8.12194], + "to": [5.549, 17.12464, -7.12194], + "autouv": 0, + "color": 1, + "rotation": [-22.5, 0, 0], + "origin": [5.5, 11.12464, -8.12194], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [32, 4, 56, 8], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [32, 4, 56, 8], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "c7c670d3-c3e7-f1c1-a857-0bfeed6be33d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.424, 9.87536, -8.12194], + "to": [5.574, 15.87536, -7.12194], + "autouv": 0, + "color": 1, + "rotation": [22.5, 0, 0], + "origin": [5.5, 15.87536, -8.12194], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [48, 0, 24, 4], + "rotation": 90, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [48, 0, 24, 4], + "rotation": 90, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + } + }, + "type": "cube", + "uuid": "935a8c82-6b9e-cf70-7360-5ad0197622f7" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.499, 13.0506, -10.49417], + "to": [5.499, 17.0506, -9.49417], + "autouv": 0, + "color": 1, + "origin": [5.5, 20.5506, -9.49417], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [16, 0, 0, 4], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [16, 0, 0, 4], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "43115d9e-1e6c-7684-030b-05e980fb6498" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [6, 10, -6.05], + "to": [6, 13, 3.95], + "autouv": 0, + "color": 1, + "origin": [5, 11, -0.05], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [1, 2, 4, 12], + "rotation": 90, + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [1, 12, 4, 2], + "rotation": 90, + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "94f914ad-eb20-d7bf-0e51-4253cf38d09a" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 9.5, -19.25], + "to": [1.5, 12.5, -16.25], + "autouv": 0, + "color": 0, + "origin": [-0.5, 9.5, -17.25], + "uv_offset": [42, 0], + "faces": { + "north": { + "uv": [45, 3, 48, 6], + "texture": 2 + }, + "east": { + "uv": [42, 3, 45, 6], + "texture": 2 + }, + "south": { + "uv": [51, 3, 54, 6], + "texture": 2 + }, + "west": { + "uv": [48, 3, 51, 6], + "texture": 2 + }, + "up": { + "uv": [48, 3, 45, 0], + "texture": 2 + }, + "down": { + "uv": [51, 0, 48, 3], + "texture": 2 + } + }, + "type": "cube", + "uuid": "0bb3b56e-f47f-5c47-14fe-f6dd3de3a639" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 6, -16], + "to": [-1, 10, 22], + "autouv": 0, + "color": 3, + "origin": [0, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 8, 42, 12], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 8, 4, 12], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + } + }, + "type": "cube", + "uuid": "c0c36880-9cd7-a3e3-749b-2ef00dbd1051" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 6, -16], + "to": [1, 10, 22], + "autouv": 0, + "color": 3, + "origin": [2, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 12, 42, 16], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 12, 4, 16], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + } + }, + "type": "cube", + "uuid": "0cb22105-c1b9-086d-f0d9-e087c1a1139f" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1.5, 12.5, -16.25], + "to": [-1.5, 9.5, -19.25], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [2.5, 12.5, -14.25], + "uv_offset": [54, 6], + "faces": { + "north": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "east": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "south": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "west": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "up": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "down": { + "uv": [54, 0, 57, 3], + "texture": 2 + } + }, + "type": "cube", + "uuid": "7bee31f4-0f08-299a-aaa0-8177d8078dff" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0, 11, 11], + "to": [2, 13, 13], + "autouv": 0, + "color": 7, + "origin": [1, 12, 12], + "uv_offset": [12, 19], + "faces": { + "north": { + "uv": [14, 21, 16, 23], + "texture": 2 + }, + "east": { + "uv": [12, 21, 14, 23], + "texture": 2 + }, + "south": { + "uv": [18, 21, 20, 23], + "texture": 2 + }, + "west": { + "uv": [16, 21, 18, 23], + "texture": 2 + }, + "up": { + "uv": [16, 21, 14, 19], + "texture": 2 + }, + "down": { + "uv": [18, 19, 16, 21], + "texture": 2 + } + }, + "type": "cube", + "uuid": "6fef4f90-1eea-a2f3-84ac-19fa1edeba34" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2.5, 12, 12], + "to": [2.5, 16, 17], + "autouv": 0, + "color": 7, + "origin": [0.5, 12, 13], + "uv_offset": [0, 30], + "faces": { + "north": { + "uv": [5, 35, 10, 39], + "texture": 2 + }, + "east": { + "uv": [0, 35, 5, 39], + "texture": 2 + }, + "south": { + "uv": [15, 35, 20, 39], + "texture": 2 + }, + "west": { + "uv": [10, 35, 15, 39], + "texture": 2 + }, + "up": { + "uv": [10, 35, 5, 30], + "texture": 2 + }, + "down": { + "uv": [15, 30, 10, 35], + "texture": 2 + } + }, + "type": "cube", + "uuid": "ea6d3eea-4586-0816-0a8a-bfc509ba028f" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2, 15, 11], + "to": [2, 18, 15], + "autouv": 0, + "color": 7, + "origin": [1, 14, 11], + "uv_offset": [0, 23], + "faces": { + "north": { + "uv": [4, 27, 8, 30], + "texture": 2 + }, + "east": { + "uv": [0, 27, 4, 30], + "texture": 2 + }, + "south": { + "uv": [12, 27, 16, 30], + "texture": 2 + }, + "west": { + "uv": [8, 27, 12, 30], + "texture": 2 + }, + "up": { + "uv": [8, 27, 4, 23], + "texture": 2 + }, + "down": { + "uv": [12, 23, 8, 27], + "texture": 2 + } + }, + "type": "cube", + "uuid": "f9cc9927-9caa-17b1-7172-3391bc9967c5" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 18, 10], + "to": [1.5, 20, 13], + "autouv": 0, + "color": 7, + "origin": [0.5, 18, 9], + "uv_offset": [0, 16], + "faces": { + "north": { + "uv": [3, 19, 6, 21], + "texture": 2 + }, + "east": { + "uv": [0, 19, 3, 21], + "texture": 2 + }, + "south": { + "uv": [9, 19, 12, 21], + "texture": 2 + }, + "west": { + "uv": [6, 19, 9, 21], + "texture": 2 + }, + "up": { + "uv": [6, 19, 3, 16], + "texture": 2 + }, + "down": { + "uv": [9, 16, 6, 19], + "texture": 2 + } + }, + "type": "cube", + "uuid": "938e8b16-7a21-e402-d481-6035bb73cd17" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.5, 19, 12], + "to": [-0.5, 22, 12], + "autouv": 0, + "color": 7, + "origin": [-1.5, 20, 9], + "faces": { + "north": { + "uv": [17, 16, 20, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [20, 16, 23, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "dd3cb301-f072-0447-26bb-aa239d5864ba" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 18, 9], + "to": [1.5, 19, 10], + "autouv": 0, + "color": 7, + "origin": [0.5, 17, 6], + "uv_offset": [0, 21], + "faces": { + "north": { + "uv": [1, 22, 4, 23], + "texture": 2 + }, + "east": { + "uv": [0, 22, 1, 23], + "texture": 2 + }, + "south": { + "uv": [5, 22, 8, 23], + "texture": 2 + }, + "west": { + "uv": [4, 22, 5, 23], + "texture": 2 + }, + "up": { + "uv": [4, 22, 1, 21], + "texture": 2 + }, + "down": { + "uv": [7, 21, 4, 22], + "texture": 2 + } + }, + "type": "cube", + "uuid": "ade1c9ce-a3f2-b6ae-44fa-55a993b0749e" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2.5, 17, 9], + "to": [2.5, 20, 9], + "autouv": 0, + "color": 7, + "origin": [1.5, 17, 6], + "uv_offset": [1, 22], + "faces": { + "north": { + "uv": [12, 16, 17, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [12, 16, 17, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "bbbdefdc-5fb6-fa1f-4b13-39e8c045e0e3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0.5, 19, 12], + "to": [3.5, 22, 12], + "autouv": 0, + "color": 7, + "origin": [1.5, 20, 9], + "faces": { + "north": { + "uv": [20, 16, 17, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [23, 16, 20, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "0947e07a-fbce-bd81-536a-514bb9ac87aa" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2, 11, 11], + "to": [0, 13, 13], + "autouv": 0, + "color": 7, + "mirror_uv": true, + "origin": [-1, 12, 12], + "uv_offset": [12, 19], + "faces": { + "north": { + "uv": [16, 21, 14, 23], + "texture": 2 + }, + "east": { + "uv": [18, 21, 16, 23], + "texture": 2 + }, + "south": { + "uv": [20, 21, 18, 23], + "texture": 2 + }, + "west": { + "uv": [14, 21, 12, 23], + "texture": 2 + }, + "up": { + "uv": [14, 21, 16, 19], + "texture": 2 + }, + "down": { + "uv": [16, 19, 18, 21], + "texture": 2 + } + }, + "type": "cube", + "uuid": "28d5f17e-1718-c16e-d620-2eae46e6ad48" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0, 12, 16], + "to": [0, 24, 24], + "autouv": 0, + "color": 7, + "origin": [0.5, 13, 17], + "uv_offset": [20, 11], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [28, 19, 20, 31], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [20, 19, 28, 31], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "23229772-9322-7e23-686b-e5fda951e8f3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3.5, 14.5, 32], + "to": [-3.5, 7.5, 22], + "autouv": 0, + "color": 3, + "origin": [6.5, 17.5, 47], + "uv_offset": [28, 16], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [45, 33, 55, 26], + "texture": 2 + }, + "south": { + "uv": [38, 33, 45, 26], + "texture": 2 + }, + "west": { + "uv": [28, 33, 38, 26], + "texture": 2 + }, + "up": { + "uv": [45, 16, 52, 26], + "texture": 2 + }, + "down": { + "uv": [38, 26, 45, 16], + "texture": 2 + } + }, + "type": "cube", + "uuid": "2175040c-9503-bcb8-9bcc-52cc5ec35660" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.374, 9.9506, -10.49417], + "to": [5.624, 13.7006, -9.49417], + "autouv": 0, + "color": 1, + "origin": [5.5, 17.0506, -9.49417], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [28, 0, 13, 4], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [28, 0, 13, 4], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "9d9f4cc7-cb0a-1515-b9cb-28a8dcc176cb" + }, + { + "name": "wand_particles", + "position": [5.5, 13.5, -9.5], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": null, + "uuid": "c7ae7054-1365-0f5b-e777-ff9284ac230e", + "type": "locator" + }, + { + "name": "broom_particles", + "position": [0, 11, 26], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": null, + "uuid": "3857dee2-7a29-d026-bab3-73868390263b", + "type": "locator" + } + ], + "outliner": [ + { + "name": "broom", + "origin": [0, 11, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "e24cb11a-42ef-7cb8-56d8-5eec84e57093", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "63947e2c-8407-1963-bf31-8eee08aee500", + "53f25d61-a8ce-e2c9-4ccb-32f0b688f827", + "0bb3b56e-f47f-5c47-14fe-f6dd3de3a639", + "c0c36880-9cd7-a3e3-749b-2ef00dbd1051", + "0cb22105-c1b9-086d-f0d9-e087c1a1139f", + "7bee31f4-0f08-299a-aaa0-8177d8078dff", + "2175040c-9503-bcb8-9bcc-52cc5ec35660", + { + "name": "witch", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "e54ffebf-c16f-05d6-6c9d-81e55a97dac8", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "86662d66-4b1e-be0a-bbaa-6a6dec088fef", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "45d82418-2321-4f24-9a63-b5bc2057e800", + "37dca7a9-789b-3984-4d16-fdaa3b292048", + "53130d68-3a4f-7a65-51bc-882312407cb7", + { + "name": "rightarm", + "origin": [5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "d8d52ffc-92a4-36dc-eadc-b32e43e95516", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "rightarm_slim", + "origin": [5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "3647609e-a900-8b00-b39e-8c8a909cc6bc", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "a22f9634-063d-97f7-86c5-24256821bb96", + "6b4537cd-19f5-52e2-fa6a-4b8cd831ff30", + "db64cc71-2153-401a-dd3c-b1cea98adebe", + { + "name": "wand", + "origin": [5.5, 13.5, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "899dfa49-6f29-4fc0-db9a-b0a8e710d2ca", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "164667fe-1cad-e25a-efd1-1a3716dfbbf9", + "94f914ad-eb20-d7bf-0e51-4253cf38d09a", + "9b9e03f6-1324-ab71-11bc-b08061b7712a", + "935a8c82-6b9e-cf70-7360-5ad0197622f7", + "e39f2736-2736-e6fb-c569-4a6f55c55aaa", + "9d9f4cc7-cb0a-1515-b9cb-28a8dcc176cb", + "43115d9e-1e6c-7684-030b-05e980fb6498", + "c7c670d3-c3e7-f1c1-a857-0bfeed6be33d", + "c7ae7054-1365-0f5b-e777-ff9284ac230e" + ] + } + ] + } + ] + }, + { + "name": "leftarm", + "origin": [-5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "519e38af-3987-5abc-fc4b-35258cf082d1", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "leftarm_slim", + "origin": [-5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "2e329e12-5f7f-dfe2-5977-83fbbc53cf25", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["7ff42713-1443-789a-5165-dbef83209ad8", "693f4ac0-3774-4a83-c93b-2a2951987a65", "55a3b67f-8f28-db5a-8b77-62ce1e10fcc4"] + } + ] + }, + { + "name": "head", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "b8bb5ce4-8554-85f7-8a9a-74a944beacda", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["9a20434d-4a80-a43d-b8f4-c747f469e12b", "f9c88722-d723-f7de-ce1d-40d630c00049", "55fb5425-1275-3a16-70a7-4b6276ac5999"] + } + ] + }, + { + "name": "rightleg", + "origin": [1.9, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "16a71e7b-efea-4d7e-2197-b83f1c54966e", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["73b9fdbc-c2f9-e43c-ef6d-bc159d183193", "ac495b61-fa9f-44f0-7d4c-30799a96a4c8", "426f5735-a6d5-8fdd-78de-06675b178e88"] + }, + { + "name": "leftleg", + "origin": [-1.9, 12, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "c71134bb-9430-596b-6372-4eefaf8083f5", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["de1c2115-133b-e2de-0275-6d784aee8ecc", "0185a3b1-67bf-38b5-373f-6a6facb044bc", "da1792fe-8974-f1bb-88bd-262e759f1175"] + } + ] + }, + { + "name": "cat", + "origin": [0, 12, 14.5], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "600a0134-73f2-0ef4-f7f1-6ba4007e0acf", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "28d5f17e-1718-c16e-d620-2eae46e6ad48", + "6fef4f90-1eea-a2f3-84ac-19fa1edeba34", + "ea6d3eea-4586-0816-0a8a-bfc509ba028f", + { + "name": "upper_body", + "origin": [0, 16, 14], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "53ae45ad-72b3-918c-5af2-1ac9b48a7ef8", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "f9cc9927-9caa-17b1-7172-3391bc9967c5", + { + "name": "cat_head", + "origin": [0, 18, 12], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "5b9afb46-cbe4-e283-eeae-50b4b9dc488e", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "938e8b16-7a21-e402-d481-6035bb73cd17", + "dd3cb301-f072-0447-26bb-aa239d5864ba", + "bbbdefdc-5fb6-fa1f-4b13-39e8c045e0e3", + "0947e07a-fbce-bd81-536a-514bb9ac87aa", + "ade1c9ce-a3f2-b6ae-44fa-55a993b0749e" + ] + } + ] + }, + { + "name": "tail", + "origin": [0, 14, 17], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "09c04eec-1969-9dda-61c6-e7ef781985e1", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["23229772-9322-7e23-686b-e5fda951e8f3"] + } + ] + }, + "3857dee2-7a29-d026-bab3-73868390263b" + ] + } + ], + "textures": [ + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_skin.png", + "name": "witch_skin.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 2, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "ce16ff1e-f58a-dbf7-24bb-22dbd43317ec", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAACsJJREFUeF7VW32MVcUVnxcVDPK17AK6u4girNKqRQFbTWmobTFCQqVJa2zZxmINoW3arsbQ1KpRq6k1kaZpS0gLGteEoEm1JGDE2pralCioW/uxCnQRWbYCu3yupKD2tb/z9nc597yZO+/tfQvt/efdO3PmzJzfnHPmnJl5BRd5zmwYVQTJ8PHD3PH9J4Ta944y/YAWZe919hVifeSpP2d6vYwP/Xzhmx+V91///G8FXZ7FPzo4AKCFo2Bgyvdj3cfciOYRqX5YNtQAcII+6D1auOC6cwWAt597t6DLcwEAJMeff1bCo/vV91zzzHPke/8777uvDq/LnMCVHTuiIOfRgJCgNQNAm4CedZrDrc0TTysAecBD2+jsVKoBjRPGpsbSs++QfA+1BpxyAKD2NAm8f2/ShY7C2sEAlLs2bY2CnFeIPO2jg6M31X6A9o9fAJD1/N8DoH2Annn6AAh/z5UXu5H1Jx0lyvr73nf3vvaWg3fOM0ND3TZZLri22yWPA0B5CADfMqkBAo9QHHG6ARIAKJxd4ig813sLgBacdeQB/6BjhpAJVRMw6UAnphkwXR242XiE9RIxacF87xDKpwE6HgjxwEC/dPYEGW/77m75bZ3ULL9P/mtfIkcsYMI4r7v2XKF/7nfvRiNM+i7QT9tXcB19vSlTnFHfUNw+oegkerpyar17bUefs7NHAW8plgZsn9WFkkAaIPIgT7bBt37QHx5EbaHZ1JEdxwlatEU7XZ+lEaCz/bAsBcDdUy5P8bmv6w2360+HpeyM4WeKs8MD5/bh8Q/kffI1Y5yvnQYgS4M4sDXL5kkYi2fJyk0CSkxAW+/joQXy8UtQBCFmGt58W+d+hzX8R7t3OsT0Kx7rdI+0zndtnx8j/Fb85rC7rX2ja7t5uuQAjAVapo8X70/NYOcxEyJYAGPFTVcX29ZuFgBiCY2tZ1ut1RwDNQZ9IVkiGEnWBELE9RACD5cxzPoNa37r7px1i5t9+S6p2/LGZPfA1tXumSWfFW3QyyDAe/z4wZQmZTlR7TvoB+64ZGnx4TdXRRMaHe+zDYHTThcO2ZctgiYBQCc1DGshHJ6sdZ71+NURoQZBrwZExmaVKK/VkmjzF/AOpctlDmjOVVcV9+7d7w4cPOSmXzLNLR72ORnzjt5et37P6+6to69kBjbLZkwVW352Yn9KC67fO1K+/zpsnOt8c7sbVzfWTZxY0jbd30uvZPOHmlNDmy4al/RBv5Hq1LnEr5hyfhbKhGkYXZdqVF+clmpbKQDQAK3e1LCnukreP/T0HjmYCbAGQEef9BuKb0x4IS3rrGXyhdIQGoBZKhwozRKfSgEICfjCwQ8T3pqG/W3btbNiACIaIHLMKX5Dunmp8Av5td9RE9jXmRalUgBC6fEpNIEyACi8BqQAm8+ySWuj+Nb0l5444J3sEAAhzQj5DK5Kth1WKd9Ds2hbuzk145qW2iAmcP+8WUFbycrzNUPS3firp6W4sanR9ezpSY1Pl637+qISndpEsbEDo0+t5prhnn+UgGff5EX6JSs3eQHQwgcBIIpY0/XDTliPWZjz4KPu5S0dQrbohvmhCU6VP/3MRvn++OwZKcAWtM6V7w3tLyYgdq2+3cvz8KGjUs4xUlPGjB0l5QRg5FNrytr3f3FJUlbgsgXhfDm9Rtk3krZnX3YUSAPg0wC299Gj7L6ffldI7v72T1Jgghc0iI/mTfAJpmhWU6NrOa+UexAACK3BIAhiAlZwdkQ7s6ZAsFA/bNF3Kpr1LCIMHoK8vepeIbtg6T2iHSgDqADHahdBsAAQKB8AFhB8iwZQtbU9hexfEB6w3SyawdBR2zR/+BUCYYUOgQrw5s66rKKJyQ0ABgsgtEOLaZQVlPQ/6Ngmg/7hjBb5hYbRqcHmad94t/6J0sIXgM46wWAcsOqTd8oqgFCXz4InviWvDy9c7kVx4ZQrkvKO/nXy7gOAJqSdZmLHA9vm+KYDswBAyNlXT/GOYcvmLm856SsGYEHjfAHgI6MnBQFg3d+P7C7rdPtZJlL671kAYnzE9zrWZ5lv1KzbM+KIVDcdG53wWHZNaRdIPwA2a4kG4FlxAHhRIwpII8mcAn7qrpuk6A/3r/WirMF6tOdJL02tCn/8ldleVowDbKWNA1CvI0DSJwDABNZ3vZ7SABDZ2d5x9KSJLGy6wk1taBBeT5x4viy7yxNZgqfODr82c6zT9o/6kPpTOJgBTcACpJdF1BUAwCN/fl7oINitv39Q3rn2ts1sle//JQCsUBYg1GcFQqhP4gDLDLkBcnLuC9w+6ctCsvSPD1R9wOHbxgZfscGBPnzvNTKfxLRtNJiKBEMAMC2OpafVDlYDoNuGyqvlr+gHtx9gO6STpE/Y0LOxak0ICTEEQuuuUumwrkhlgz4N0GrJLTGUwVnmBeBUm4BvBRD5BjZIxNatHfrsEibBdd3abw41TTWt5uirgj4r2xGis4OtEwwsQ/jWfoDv6Jh1AERvYm77Z5902nJefQHv+BX6gXddZgWg8CzH3n0FQmaRBE0gpQE5O6lZc9/RV07mAkB0PyBnJ6nmL279i3SqUtJEA1CO2GLurMuqmlmtPeyMGkZto5YZ7RPy6H5AXgAgNIV6aGW7AMDNCZYTGKS1y5e1JgCwHHQ+DdC8BzHOlAZw7S+LBAfBeEiaDJUJxAYroXCISKfImoZ5AMoGEyGG+oudBseEMfVF3xaYLRMAICiECgnDlJkdIBskCLUEoEoBY+QCgA57dQPWJfao02IKpzUAp7WxHvPW0wxqsARiKEUEQXYbnGNkXQqAwQiJ+ADHWjjT08dq9owPZ444ahOPvWtnQX+zPa/BAADW58hFBAA+dksM5ShLAKApoIJAaNUPhcC+QEqYB7I91unIUtPvrntHrr/UIE+oLBmC4LBjmIB2bhgUYn8+FgA6T2yIWIErAYACJjM0NOlxDITy43HODGeQoS4Fjjk9e9aIw1W9gxRrr88puAsMkALn/3ndTvh4nLlB7MKCHUHe+wWhgxrP+X9u4cEg6tntrnHMUdIp0uFVe7/gtGuA3hIDQqO7G5w+B4ipsM8E9FTF7hdYAG6+sdk9tq576EzA58W1B/c5OFtmd4Gr2RWeN/bfZaocOlkiIc4FanUL3Zvz63zft2zp/YLP1J1RJoC9HBEyVh6phc74Q+1wJlAzAPJ6EqisPaXRdw19/PVRGS9mgs6e8YfGhm3wWjnFqBOMAcT7BaCzFyi0yoaOsnj6C1p7xs/2+n4AT4iZatudJwFyYEeK7e3+gW5zSgDQQOAds87zPR8A+jIE6O39gMFsrIQmsqYA6E54bK41w9o6vnEZwvfo6zMAgN+8EKE3VmJamlWfOhwloQ2JQwyQLfaf/YK3ulIAfI15xo86e+zFuwG1+jdaoZKER+8ZcMAQ3ndcjvo71j8kZBsW/yyRzwcqco3zJ5QuRegndCeANDgcrRkAZDrY/QAAaO8P+AAIgeUDwHdhS/sRmE7NAciyE30V3dLZ3SLU2/sF+j6Bbg9QPnHpyb/NsC50N5D1QxIH+EyBWpG1XXbxqNLJ0tRRpfsCfKzQEFaH1Lx9fu1F/pum2gy0H+DdgJprgAZADxQCYbBIgvQGKnMCC8CKV9uTuwVo+8tPf1+u2eO57WOlq/fkifLli8svQDD+J23g/D/3Cgb+/wGfkeY4ThdFjwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_skin.png" + }, + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_animation.png", + "name": "witch_animation.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 96, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 4, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "c3e57243-93f3-dfd7-c4d5-aaa585e74dc6", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAABgCAYAAAAU0fKgAAAAAXNSR0IArs4c6QAAATtJREFUWEftmDFLxEAQhV/ATrARbEYQS7HSnzaWetVp6fw0rcRSBKew8qorbEaSzZ3JcnfJsSlEXiBsYJch7O775s1U/qIBB5r3A8DXIQABcNqM3zjpTd+YVOg81aN6eL02rU/jwWcbLUU+xrKZWk0fLQC5tCZQL1o38thvBuAm8iIltZRr4cE1cqnu4oPYVZ8Hrs+RQ2MMH26FPFghr/wYx8Jz2zr+wRRi+genoP4UyRhsT+VdPsgsyXh9lX2uMWQqNvHB5Jr+oN1FqpFqnCa9FxNJzWMfq3/RpvU1D15do/b/+xQdNR9MU+FBLXAPeA+msvvFPHiry/9O+2DI6pvc9/2B+l1s6g8M8eG8bUSQB+QBefBneOA6i139w5wPdpb1E/XdY0z/MOeD2Jz+gPXCb+FZnNoYoNxs/wB+FYpheqpQPwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_animation.png" + }, + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_assets.png", + "name": "witch_assets.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "05e98464-7aec-b7f7-3e91-b36b122bef63", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB1NJREFUeF7tWGtsFUUUnlVBLhpoe4tAA1WE8BAJVImKoQYENAISE2zsHyDyCPUVJSKgBA0iCopB46uGhxFjgin8qfhEHhFU1GprFAqmiLS1FOltKSoXIbDmm3o250537s7eu725N3T+7M7u2Zn5vvOdc2bWyuk+yG4+XWOJ/9uocK79c8tp2et6aTdxe69bxHU9+osDp+qc67RrC0T5b5VO/9PG3dIW7ez5MzRU3OvAsixRV9xm2/OuZaL3olHi+ItVovWTFXLOnSf2OWONyO4uqiJNzhrnlQ+7gO/GTcoSJTNqnHn+2nLiEurURtdLG7T80Fznubooa1Lfmfb2Y5ucwe8vGGZv/uW4CHXJkbZjsgYLAK5pahKDcnMlcLWPxZI9TRA916wlALZ3fxkSH94WbSNg8VOix4QB4tSOI6J19fNiTv4EsaF2h3yHcYqv7y3eqax21vh+dMyF3dtPOuPv/a5F3lc/1+ifgFkVI+xvjkbEr9Mb5ASZQAB5d4/YIIgIkJAQAV4KgDd0niclvH64LCkFdJ34kBMCZ794Q7xww33iyR8/iFHAZcuFDckXijmO50EAGkjAO7rHdd20aq3suTStkXt32XgA+R15ZralKiBdCNg3pcUee1O2A5SD4Cqg51wN2lgUQlihLjl29Fxz3CSoJj1KivQ86CT48MAiAVVRQkUSPDzesotn5jpYunbpJnZ+dczp1x88H4OTJ8SMICB0Y08R/aFVVgE3AngViAfI7zsjBaRbGfQL0pcCdMZT8ibbRMRHDR87IZPoYgZvzbOp8qhjYK4g5jBZWzsFxCOAYr6jF/fE0Pn2SwffTppkIwIwGcocGjY7uolpUalYXCrmIHIsdSusYy2VBJh4LigbT5npYj/InNBn2lqbzgKN5Qs81xQUeIzjORklJDUxoR9UTkhrAnTSp9wRL2+YemrA8o02HYawGzX9Lgi7hCcLMieAAACnaxDATMeIIaBy6WP2Nfl54vfahpjvC1a+4th9/tpS+45HVrbrn96yTp4pqHW/d17C5MZb/NriyXKeBZuT34u0ywEA8W+kRVwezhaHKvc76+AEuC2uuXS1/X3VAVE4cax8faap7V9ATsnihEkAUA6S+htLiiQBs0vLLJ2NqfddCbAsS9i2LUAEtez5ixwgax6dYS989b2Yfs4fbbb3TCyMmTsZAgCUg6T+rjULJAHjF661dDa+CIDshxQMl9/kLnlaRFavEOHFy0TTqmflM1IChQapAd/9yUi6s/TdhL3ttmAA3bbtazF8aL70NvWnTr1VmuMd7omI/Qdrnb4vAj4rmWVfFc4WV/fvK7+D96ECtKN1xwQHiWcENFUEACSBrao4JEaNHiLXRvf0jhPiiwAYR7eutxG33XLb/gPSfWj63HZeBXCaQCUHRHrlC9PFweMEkl97Zl0hh2g9+Y8kQ7WBIkznkDkA4OH1uk0Vov/M0Y73dSRE3lrlEFDxU3XMXKNHDhPhB5b4WoBusYhvFeSRmnoJHA1EDBjUL4YAPEO4+CJANQbAUK+wfBw9ERG19Y0iv18f2cd9UB72WiSVO4AiInQEkA3G9Fse27Gl1nOQwJuphzmRiewJHh93s92vT1h6GgkOyRAN92i8j3uQVN8YES/v/jZ5BcDTaPA8qYFIMAUDImGLK0g0JY7mIQLQBzCQQfe48j6/T5oAnuQwEZVIvwRw1UANnADIm2KY7EjeAEtNB1J9rxvLJBwskiqSHsqeW0OJRIWgcOA5wsSzKgHcy9ybbnODkIbjEZHXO+x4HXZUDXBPiTGhEOAx7wYQE6hhwBeqhgQpiJIlVQ2VKJ0KVEDoEwGcLNXrqpJMvI9vLCzYLdZVb1BFoNyAq1tV4CFE1SOeSnis8xgn4Oo6oAQ1BOg7v/EvCXCTHd8e4z22w7wUmgBTlcDncfO+6kFOhk4BsMFeIFHvawngJQxG8D7CgO8L0DetCG4k4xn3vltMEwluOUAdk5KnXxW4KkDdC9BkQRAQrwKoIWBSBZKpAFoF6Ajg/whQHpNVgE4ZqXxurAAOnhaYqm1xRxJiXAX4InhF4BslkJRppMitqpro0Oe1n+8PKCHqvNIBYRHzrzFoNcidIAZFTefnexxtqfFjL878VAaJKHgeSkhkzx8HUIcCp3mdrTA9cIt1esflzk+J/MhssjU28GJKwGurgMECO9rkoiYgZeDTVQGdBLjF15VlG13D7u+i2THPTe2cJNjRwZzA+K4KMAVmapexBKge9yKYCNF95+sHotdkAb2Pq4CLloBC+0HJ7x7rTV88e32XMQrwAqJjxeu7jCNABaoqggB72WVcEjQFZmqXcQT4CnwfxhkTAj4w+TLtJMAXXakx7jwLEM9qCUu0z/2mVo20DgEOmGd3AmFKSLySmBEE6DzoVfK8CEr7/wEqQLdtsBsJqkIyVgFuR9t4hyH15GfyfVqHAAfEwRAJXmd/r+/TPgR0HuUqUElwe6cSxm3SWgGp2HZ0EpAKln3OkdKd4H9dQJRwK05NdwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_assets.png" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "ceca2b6f-acbe-23b5-4e39-b2f2148ed716", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "55cac467-ea1e-c70f-63e8-6a496a57fbe4", + "name": "pose", + "loop": "loop", + "override": false, + "length": 4, + "snapping": 20, + "selected": true, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "86662d66-4b1e-be0a-bbaa-6a6dec088fef": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "21.5326 + math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "6.94082", + "z": "-10.14915" + } + ], + "uuid": "7375b3e2-8bb8-96fe-7d0a-995799b9b124", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1" + } + ], + "uuid": "9bcbb128-fa90-a43c-6715-ca4b3d1287b8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3647609e-a900-8b00-b39e-8c8a909cc6bc": { + "name": "rightarm_slim", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "50.6959 - math.cos(q.life_time * 180 + 45) * 22.5 - 12.25", + "y": "15.6806", + "z": "55.0931 - math.cos(q.life_time * 180 - 45) * 22.5" + } + ], + "uuid": "43a3a5bf-eb57-71a6-38f8-a6908ec75014", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "2e329e12-5f7f-dfe2-5977-83fbbc53cf25": { + "name": "leftarm_slim", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-53.8268 - math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "23.61535", + "z": "7.1579" + } + ], + "uuid": "6b045717-0fac-aceb-f4a2-8421cea86682", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1 + math.cos(q.life_time * 180 - 22.5)", + "z": "math.cos(q.life_time * 180 - 22.5) * 2" + } + ], + "uuid": "153aa669-4c98-c128-e66f-c02e04c24f6b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b8bb5ce4-8554-85f7-8a9a-74a944beacda": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-14.4966 - math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "-6.6848", + "z": "9.10914" + } + ], + "uuid": "2f449be4-da35-4144-e82b-3626a040cda0", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "16a71e7b-efea-4d7e-2197-b83f1c54966e": { + "name": "rightleg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-33.735 + math.cos(q.life_time * 180 + 45) * 12.25", + "y": "9.93191", + "z": "14.4817 - math.cos(q.life_time * 180 - 45) * 12.25" + } + ], + "uuid": "2242ad81-4ea6-c374-29ce-7dd39ea7732d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c71134bb-9430-596b-6372-4eefaf8083f5": { + "name": "leftleg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-27.3779 + math.cos(q.life_time * 180 - 45) * 12.25", + "y": "-1.9293", + "z": "-9.74852" + } + ], + "uuid": "0e06f514-d90a-8e60-eff7-54f12916d179", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "09c04eec-1969-9dda-61c6-e7ef781985e1": { + "name": "tail", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "14.21301", + "y": "-65.79957", + "z": "237.13131" + } + ], + "uuid": "8e858d72-fc56-9bca-b792-6903609981f6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "600a0134-73f2-0ef4-f7f1-6ba4007e0acf": { + "name": "cat", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "12.5", + "z": "-12.5" + } + ], + "uuid": "e994ff64-e40d-173a-16f4-1eb77fdb6512", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 180 - 22.5) * 0.1", + "y": "1 - math.cos(q.life_time * 180 - 22.5) * 0.1", + "z": "1 + math.cos(q.life_time * 180 - 22.5) * 0.1" + } + ], + "uuid": "1d7b63da-2172-2b07-33c0-8d587ccdf851", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "5b9afb46-cbe4-e283-eeae-50b4b9dc488e": { + "name": "cat_head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "14.8737 + math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "9.53055", + "z": "12.3725" + } + ], + "uuid": "28f38076-27d5-ec21-7fae-bfab14b87a42", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "-1.25", + "y": "-0.75", + "z": "0" + } + ], + "uuid": "3129fa1a-35d1-1d11-6b11-7c6a33595618", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "53ae45ad-72b3-918c-5af2-1ac9b48a7ef8": { + "name": "upper_body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "-6", + "z": "-6.5" + } + ], + "uuid": "dbcda364-7b28-fe3f-4265-c8b1825ab332", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "-1.25", + "y": "-0.75 - math.cos(q.life_time * 180 - 45) * 1", + "z": "0" + } + ], + "uuid": "0285b4b5-9258-832f-ac2c-6616e28c7090", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "e24cb11a-42ef-7cb8-56d8-5eec84e57093": { + "name": "broom", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "7.5283 - math.cos(q.life_time * 180) * 12.25", + "y": "4.95712", + "z": "0.65426" + } + ], + "uuid": "39c88ccc-2963-635b-7765-4220f69c5931", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "5 + math.sin(q.life_time * 180) * 4", + "z": "0" + } + ], + "uuid": "1aaa4431-a52a-28b5-eedb-b98102928609", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "e54ffebf-c16f-05d6-6c9d-81e55a97dac8": { + "name": "witch", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.cos(q.life_time * 180) * 8", + "y": "0", + "z": "0" + } + ], + "uuid": "3bc3480f-7f1f-6893-006e-d969bfbb5af8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_controllers": [] +} \ No newline at end of file diff --git a/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint new file mode 100644 index 00000000..3c1e8863 --- /dev/null +++ b/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint @@ -0,0 +1,2242 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0-beta.1", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.5/blueprints/armor_stand.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand_1.20.4_static.ajblueprint similarity index 94% rename from test-packs/1.21.5/blueprints/armor_stand.ajblueprint rename to test-packs/1.20.4/blueprints/armor_stand_1.20.4_static.ajblueprint index b2fce9be..b7a4e273 100644 --- a/test-packs/1.21.5/blueprints/armor_stand.ajblueprint +++ b/test-packs/1.20.4/blueprints/armor_stand_1.20.4_static.ajblueprint @@ -1,47 +1,26 @@ { "meta": { - "format": "animated_java_blueprint", - "format_version": "1.7.0", + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", - "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.21.5\\blueprints\\armor_stand.ajblueprint", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_1.20.4_static.ajblueprint", "last_used_export_namespace": "armor_stand" }, + "resolution": { + "width": 64, + "height": 64 + }, "blueprint_settings": { "export_namespace": "armor_stand", - "show_bounding_box": false, - "auto_bounding_box": true, - "bounding_box": [95, 32], - "enable_plugin_mode": false, - "resource_pack_export_mode": "raw", - "data_pack_export_mode": "raw", - "target_minecraft_versions": ["1.21.5"], - "display_item": "minecraft:white_dye", - "custom_model_data_offset": 0, - "enable_advanced_resource_pack_settings": false, + "render_box": [95, 32], + "target_minecraft_version": "1.20.4", "resource_pack": "../resources", - "enable_advanced_data_pack_settings": false, "data_pack": "../datapacks/animated_java", - "summon_commands": "say On-Summon!", - "remove_commands": "say On-Remove!", - "ticking_commands": "", - "interpolation_duration": 1, - "teleportation_duration": 1, - "use_storage_for_animation": false, - "show_function_errors": true, - "show_outdated_warning": true, + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, "baked_animations": false, - "json_file": "../testPluginExport.json", - "target_minecraft_version": "1.21.4", - "enable_advanced_resource_pack_folders": false, - "display_item_path": "", - "model_folder": "", - "texture_folder": "", - "customModelDataOffset": 0, - "custom_summon_commands": "" - }, - "resolution": { - "width": 64, - "height": 64 + "json_file": "../testPluginExport.json" }, "elements": [ { @@ -511,7 +490,7 @@ "box_uv": false, "rescale": false, "locked": false, - "light_emission": 0, + "light_emission": 15, "render_order": "default", "allow_mirror_modeling": true, "from": [-5, 11.25, -3], @@ -853,8 +832,9 @@ "use_entity": true, "entity_type": "minecraft:item_display", "sync_passenger_rotation": true, - "summon_commands": "", - "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" }, "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", "type": "locator" @@ -870,73 +850,66 @@ "use_entity": true, "entity_type": "minecraft:item_display", "sync_passenger_rotation": false, - "summon_commands": "", - "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" }, "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", "type": "locator" }, { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", "name": "text_display", "position": [0, 38, 0], - "rotation": [45, 0, 0], + "rotation": [0, 0, 0], "scale": [1, 1, 1], "visibility": true, + "locked": false, + "export": true, "block": "minecraft:stone", "config": {}, "item": "minecraft:diamond", "itemDisplay": "none", - "text": "{\n\t\"text\": \"Hello, World!\",\n\t\"color\": \"green\"\n}\n", - "lineWidth": 200, - "backgroundColor": "#ffffff", - "backgroundAlpha": 0.25098, + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", "align": "center", "shadow": true, - "seeThrough": true, - "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", - "type": "animated_java:text_display" + "seeThrough": true }, { "name": "camera", - "box_uv": false, - "rescale": false, + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, "locked": false, - "light_emission": 0, - "render_order": "default", - "allow_mirror_modeling": true, - "from": [0, 0, 0], - "to": [1, 1, 1], - "autouv": 0, - "color": 0, - "origin": [0, 0, 0], - "faces": { - "north": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "east": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "south": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "west": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "up": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "down": { - "uv": [0, 0, 1, 1], - "texture": 0 - } - }, - "type": "cube", - "uuid": "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false } ], "outliner": [ @@ -992,7 +965,7 @@ "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", "export": true, "mirror_uv": false, - "isOpen": false, + "isOpen": true, "locked": false, "visibility": true, "autouv": 0, @@ -1237,7 +1210,7 @@ "visibility": true, "autouv": 0, "selected": false, - "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4"] + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] }, { "name": "left_arm", @@ -1333,7 +1306,12 @@ "visibility": true, "autouv": 0, "selected": false, - "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3", "98475898-cfc8-f67b-3145-abaf659deb3e", "7747736f-85e2-338f-207e-53f8d3d2fa39"] + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] } ] } @@ -1434,8 +1412,7 @@ ] } ] - }, - "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + } ] }, "c2e217f1-b50a-5c9a-b342-71a35e984046" @@ -1604,7 +1581,5 @@ } ] }, - "animations": [], - "animation_controllers": [], "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" } \ No newline at end of file diff --git a/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint new file mode 100644 index 00000000..4e9a7d30 --- /dev/null +++ b/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint @@ -0,0 +1,495 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "b826aacd-866a-0b17-7e71-9de81b709a63", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\rotated_blockstates_1.20.4.ajblueprint", + "last_used_export_namespace": "rotated_blockstates" + }, + "resolution": { + "width": 16, + "height": 16 + }, + "blueprint_settings": { + "export_namespace": "rotated_blockstates", + "render_box": [48, 48], + "resource_pack_export_mode": "none", + "target_minecraft_version": "1.20.4", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false + }, + "elements": [ + { + "uuid": "fe01ee52-5d18-05eb-4a04-f4addc2e3d8a", + "type": "animated_java:vanilla_block_display", + "name": "lever_north", + "position": [-8, 16, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "22ad5245-d92c-7f66-6578-bc4f2ca565ec", + "type": "animated_java:vanilla_block_display", + "name": "lever_south", + "position": [-8, 16, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "2ecf5cb0-eae7-5434-d5c2-85f686edcaf0", + "type": "animated_java:vanilla_block_display", + "name": "lever_east", + "position": [8, 16, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "d509630d-9540-1113-a879-03ce6793117d", + "type": "animated_java:vanilla_block_display", + "name": "lever_west", + "position": [-24, 16, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "f7e90b05-553d-be8a-0a74-a10f3c40f905", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_north", + "position": [-8, 32, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "db073962-3077-37c3-a53d-5acc41e31500", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_south", + "position": [-8, 32, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "ae5551bd-088a-8861-524d-9914984bae41", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_east", + "position": [8, 32, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "19d912ba-80b5-1c18-41d0-857ee8b4d590", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_west", + "position": [-24, 32, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "260e05ee-918f-b242-1570-7cc241284acb", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_north", + "position": [-8, 48, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "5290f4fe-83b8-0c33-81b4-f0407db57738", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_south", + "position": [-8, 48, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "b8918e92-660b-9728-6415-6de83f8b14f1", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_east", + "position": [8, 48, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "fd962fbf-1279-4871-adee-50664bb02c85", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_west", + "position": [-24, 48, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north", + "position": [-8, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "9e758d97-f302-a2f5-1147-ef03c323ba27", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south", + "position": [-8, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "39d33f52-142e-60c8-5912-38b3466c4a3f", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_east", + "position": [8, 0, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_west", + "position": [-24, 0, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north_east", + "position": [-24, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=north,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "1dc76a78-955a-f71b-ae83-b332d3aa3196", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south_east", + "position": [-24, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=west,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "47608e95-885c-7f7c-5331-49d9fb70c979", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south_west", + "position": [8, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=south,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north_west", + "position": [8, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=east,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + "fe01ee52-5d18-05eb-4a04-f4addc2e3d8a", + "22ad5245-d92c-7f66-6578-bc4f2ca565ec", + "2ecf5cb0-eae7-5434-d5c2-85f686edcaf0", + "d509630d-9540-1113-a879-03ce6793117d", + "f7e90b05-553d-be8a-0a74-a10f3c40f905", + "db073962-3077-37c3-a53d-5acc41e31500", + "ae5551bd-088a-8861-524d-9914984bae41", + "19d912ba-80b5-1c18-41d0-857ee8b4d590", + "260e05ee-918f-b242-1570-7cc241284acb", + "5290f4fe-83b8-0c33-81b4-f0407db57738", + "b8918e92-660b-9728-6415-6de83f8b14f1", + "fd962fbf-1279-4871-adee-50664bb02c85", + "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "9e758d97-f302-a2f5-1147-ef03c323ba27", + "47608e95-885c-7f7c-5331-49d9fb70c979", + "39d33f52-142e-60c8-5912-38b3466c4a3f", + "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "1dc76a78-955a-f71b-ae83-b332d3aa3196" + ], + "textures": [], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "789ba6c9-7bed-41be-3fc4-fd271a4dc00d", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "collections": [ + { + "uuid": "75aa7f41-1c57-be69-db96-478d11df81e9", + "name": "cobblestone_stairs", + "export_codec": "", + "children": [ + "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "9e758d97-f302-a2f5-1147-ef03c323ba27", + "47608e95-885c-7f7c-5331-49d9fb70c979", + "39d33f52-142e-60c8-5912-38b3466c4a3f", + "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "1dc76a78-955a-f71b-ae83-b332d3aa3196" + ], + "visibility": false + } + ] +} \ No newline at end of file diff --git a/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta b/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..e28cd62a --- /dev/null +++ b/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,10 @@ +{ + "pack": { + "pack_format": 26, + "description": "AJ Testing DP", + "supported_formats": { + "min_inclusive": 26, + "max_inclusive": 40 + } + } +} \ No newline at end of file diff --git a/test-packs/1.20.4/datapacks/test-framework/mcb.config.js b/test-packs/1.20.4/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta b/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..4b6f5428 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 26, + "description": "AJ Testing DP" + } +} diff --git a/test-packs/1.20.4/datapacks/test-framework/package.json b/test-packs/1.20.4/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.20.4/datapacks/test-framework/src/test.mcb b/test-packs/1.20.4/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..18a11393 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/src/test.mcb @@ -0,0 +1,42 @@ +function on_load minecraft:load { + tellraw @a {"text":"Test Framework loaded!", "color":"green"} +} + +clock 1s 1t { + execute store result score #count aj.i if entity @e[type=item_display,tag=aj.global.root] + title @a actionbar [{"text":"Rig Count: "}, {"score":{"name":"#count","objective":"aj.i"}}] +} + +dir armor_stand { + function summon { + #ARGS: {args:{...}} + function animated_java:global/remove/everything + $execute positioned 0 1 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} + } + + function summon_minimal { + #ARGS: {args:{...}} + function animated_java:global/remove/everything + $execute positioned 0 1 0 rotated 0 0 run function animated_java:armor_stand_minimal/summon {args:$(args)} + } + + function summon_many { + data remove storage test:temp args + $data modify storage test:temp args set value {args:$(args)} + REPEAT (0, 199) as i { + schedule 1t { + execute positioned <%i % 16%> 1 <%Math.floor(i / 16)%> rotated 0 0 run function animated_java:armor_stand/summon with storage test:temp args + } + } + } + + function summon_many_minimal { + data remove storage test:temp args + $data modify storage test:temp args set value {args:$(args)} + REPEAT (0, 199) as i { + schedule 1t { + execute positioned <%i % 16%> 1 <%Math.floor(i / 16)%> rotated 0 0 run function animated_java:armor_stand_minimal/summon with storage test:temp args + } + } + } +} diff --git a/test-packs/1.20.4/resources/pack.mcmeta b/test-packs/1.20.4/resources/pack.mcmeta new file mode 100644 index 00000000..54a46748 --- /dev/null +++ b/test-packs/1.20.4/resources/pack.mcmeta @@ -0,0 +1,10 @@ +{ + "pack": { + "pack_format": 22, + "description": "AJ Testing RP", + "supported_formats": { + "min_inclusive": 22, + "max_inclusive": 31 + } + } +} \ No newline at end of file diff --git a/test-packs/1.20.5/blueprints/armor_stand_1.20.5.ajblueprint b/test-packs/1.20.5/blueprints/armor_stand_1.20.5.ajblueprint new file mode 100644 index 00000000..30cc102d --- /dev/null +++ b/test-packs/1.20.5/blueprints/armor_stand_1.20.5.ajblueprint @@ -0,0 +1,2242 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.20.5/blueprints/armor_stand_1.20.5_static.ajblueprint b/test-packs/1.20.5/blueprints/armor_stand_1.20.5_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.20.5/blueprints/armor_stand_1.20.5_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.20.5/blueprints/armor_stand_minimal_1.20.5.ajblueprint b/test-packs/1.20.5/blueprints/armor_stand_minimal_1.20.5.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.20.5/blueprints/armor_stand_minimal_1.20.5.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.20.5/datapacks/animated_java/pack.mcmeta b/test-packs/1.20.5/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..04a53ff1 --- /dev/null +++ b/test-packs/1.20.5/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 41, + "description": "AJ Testing DP" + } +} \ No newline at end of file diff --git a/test-packs/1.20.5/datapacks/test-framework/mcb.config.js b/test-packs/1.20.5/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.20.5/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.20.5/datapacks/test-framework/pack.mcmeta b/test-packs/1.20.5/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..4b6f5428 --- /dev/null +++ b/test-packs/1.20.5/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 26, + "description": "AJ Testing DP" + } +} diff --git a/test-packs/1.20.5/datapacks/test-framework/package.json b/test-packs/1.20.5/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.20.5/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.20.5/datapacks/test-framework/src/test.mcb b/test-packs/1.20.5/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..18a11393 --- /dev/null +++ b/test-packs/1.20.5/datapacks/test-framework/src/test.mcb @@ -0,0 +1,42 @@ +function on_load minecraft:load { + tellraw @a {"text":"Test Framework loaded!", "color":"green"} +} + +clock 1s 1t { + execute store result score #count aj.i if entity @e[type=item_display,tag=aj.global.root] + title @a actionbar [{"text":"Rig Count: "}, {"score":{"name":"#count","objective":"aj.i"}}] +} + +dir armor_stand { + function summon { + #ARGS: {args:{...}} + function animated_java:global/remove/everything + $execute positioned 0 1 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} + } + + function summon_minimal { + #ARGS: {args:{...}} + function animated_java:global/remove/everything + $execute positioned 0 1 0 rotated 0 0 run function animated_java:armor_stand_minimal/summon {args:$(args)} + } + + function summon_many { + data remove storage test:temp args + $data modify storage test:temp args set value {args:$(args)} + REPEAT (0, 199) as i { + schedule 1t { + execute positioned <%i % 16%> 1 <%Math.floor(i / 16)%> rotated 0 0 run function animated_java:armor_stand/summon with storage test:temp args + } + } + } + + function summon_many_minimal { + data remove storage test:temp args + $data modify storage test:temp args set value {args:$(args)} + REPEAT (0, 199) as i { + schedule 1t { + execute positioned <%i % 16%> 1 <%Math.floor(i / 16)%> rotated 0 0 run function animated_java:armor_stand_minimal/summon with storage test:temp args + } + } + } +} diff --git a/test-packs/1.20.5/resources/pack.mcmeta b/test-packs/1.20.5/resources/pack.mcmeta new file mode 100644 index 00000000..3ac81056 --- /dev/null +++ b/test-packs/1.20.5/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 32, + "description": "AJ Testing RP" + } +} \ No newline at end of file diff --git a/test-packs/1.21.2/blueprints/armor_stand_1.21.2.ajblueprint b/test-packs/1.21.2/blueprints/armor_stand_1.21.2.ajblueprint new file mode 100644 index 00000000..e04fb958 --- /dev/null +++ b/test-packs/1.21.2/blueprints/armor_stand_1.21.2.ajblueprint @@ -0,0 +1,2242 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.21.2\\blueprints\\armor_stand_1.21.2.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.21.2", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.2/blueprints/armor_stand_1.21.2_static.ajblueprint b/test-packs/1.21.2/blueprints/armor_stand_1.21.2_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.21.2/blueprints/armor_stand_1.21.2_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.2/blueprints/armor_stand_minimal_1.21.2.ajblueprint b/test-packs/1.21.2/blueprints/armor_stand_minimal_1.21.2.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.21.2/blueprints/armor_stand_minimal_1.21.2.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.2/datapacks/animated_java/pack.mcmeta b/test-packs/1.21.2/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..a0177a18 --- /dev/null +++ b/test-packs/1.21.2/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 57, + "description": "AJ Testing DP" + } +} \ No newline at end of file diff --git a/test-packs/1.21.2/datapacks/test-framework/mcb.config.js b/test-packs/1.21.2/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.21.2/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.21.2/datapacks/test-framework/pack.mcmeta b/test-packs/1.21.2/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..618a6a2f --- /dev/null +++ b/test-packs/1.21.2/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack":{ + "pack_format": 74, + "description": ". Made with MC-Build!" + } +} \ No newline at end of file diff --git a/test-packs/1.21.2/datapacks/test-framework/package.json b/test-packs/1.21.2/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.21.2/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.21.2/datapacks/test-framework/src/test.mcb b/test-packs/1.21.2/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..57cbd78c --- /dev/null +++ b/test-packs/1.21.2/datapacks/test-framework/src/test.mcb @@ -0,0 +1,9 @@ +function on_load minecraft:load { + tellraw @a {text:'Test Framework loaded!', color:green} +} + +function summon { + #ARGS: {args:{...}} + function animated_java:armor_stand/remove/all + $execute positioned 0 -63 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} +} diff --git a/test-packs/1.21.2/resources/pack.mcmeta b/test-packs/1.21.2/resources/pack.mcmeta new file mode 100644 index 00000000..e9f04eb9 --- /dev/null +++ b/test-packs/1.21.2/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 42, + "description": "AJ Testing RP" + } +} \ No newline at end of file diff --git a/test-packs/1.21.4/blueprints/armor_stand.ajblueprint b/test-packs/1.21.4/blueprints/armor_stand_1.21.4.ajblueprint similarity index 81% rename from test-packs/1.21.4/blueprints/armor_stand.ajblueprint rename to test-packs/1.21.4/blueprints/armor_stand_1.21.4.ajblueprint index 0015eda6..199f153a 100644 --- a/test-packs/1.21.4/blueprints/armor_stand.ajblueprint +++ b/test-packs/1.21.4/blueprints/armor_stand_1.21.4.ajblueprint @@ -1,47 +1,25 @@ { "meta": { - "format": "animated_java_blueprint", - "format_version": "1.7.1", + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", - "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.21.4\\blueprints\\armor_stand.ajblueprint", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.21.4\\blueprints\\armor_stand_1.21.4.ajblueprint", "last_used_export_namespace": "armor_stand" }, + "resolution": { + "width": 64, + "height": 64 + }, "blueprint_settings": { "export_namespace": "armor_stand", - "show_bounding_box": false, - "auto_bounding_box": true, - "bounding_box": [95, 32], - "enable_plugin_mode": false, - "resource_pack_export_mode": "raw", - "data_pack_export_mode": "raw", - "target_minecraft_versions": ["1.21.4"], - "display_item": "minecraft:white_dye", - "custom_model_data_offset": 0, - "enable_advanced_resource_pack_settings": false, + "render_box": [95, 32], + "target_minecraft_version": "1.21.4", "resource_pack": "../resources", - "enable_advanced_data_pack_settings": false, "data_pack": "../datapacks/animated_java", - "summon_commands": "say On-Summon!", - "remove_commands": "say On-Remove!", - "ticking_commands": "", - "interpolation_duration": 1, - "teleportation_duration": 1, - "use_storage_for_animation": false, - "show_function_errors": true, - "show_outdated_warning": true, + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", "baked_animations": false, - "json_file": "../testPluginExport.json", - "target_minecraft_version": "1.21.4", - "enable_advanced_resource_pack_folders": false, - "display_item_path": "", - "model_folder": "", - "texture_folder": "", - "customModelDataOffset": 0, - "custom_summon_commands": "" - }, - "resolution": { - "width": 64, - "height": 64 + "json_file": "../testPluginExport.json" }, "elements": [ { @@ -511,7 +489,7 @@ "box_uv": false, "rescale": false, "locked": false, - "light_emission": 0, + "light_emission": 15, "render_order": "default", "allow_mirror_modeling": true, "from": [-5, 11.25, -3], @@ -853,8 +831,9 @@ "use_entity": true, "entity_type": "minecraft:item_display", "sync_passenger_rotation": true, - "summon_commands": "", - "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" }, "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", "type": "locator" @@ -870,73 +849,66 @@ "use_entity": true, "entity_type": "minecraft:item_display", "sync_passenger_rotation": false, - "summon_commands": "", - "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" }, "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", "type": "locator" }, { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", "name": "text_display", "position": [0, 38, 0], - "rotation": [45, 0, 0], + "rotation": [0, 0, 0], "scale": [1, 1, 1], "visibility": true, + "locked": false, + "export": true, "block": "minecraft:stone", "config": {}, "item": "minecraft:diamond", "itemDisplay": "none", - "text": "{\n\t\"text\": \"Hello, World!\",\n\t\"color\": \"green\"\n}\n", - "lineWidth": 200, - "backgroundColor": "#ffffff", - "backgroundAlpha": 0.25098, + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", "align": "center", "shadow": true, - "seeThrough": true, - "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", - "type": "animated_java:text_display" + "seeThrough": true }, { "name": "camera", - "box_uv": false, - "rescale": false, + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, "locked": false, - "light_emission": 0, - "render_order": "default", - "allow_mirror_modeling": true, - "from": [0, 0, 0], - "to": [1, 1, 1], - "autouv": 0, - "color": 0, - "origin": [0, 0, 0], - "faces": { - "north": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "east": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "south": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "west": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "up": { - "uv": [0, 0, 1, 1], - "texture": 0 - }, - "down": { - "uv": [0, 0, 1, 1], - "texture": 0 - } - }, - "type": "cube", - "uuid": "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false } ], "outliner": [ @@ -992,7 +964,7 @@ "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", "export": true, "mirror_uv": false, - "isOpen": false, + "isOpen": true, "locked": false, "visibility": true, "autouv": 0, @@ -1237,7 +1209,7 @@ "visibility": true, "autouv": 0, "selected": false, - "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4"] + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] }, { "name": "left_arm", @@ -1333,7 +1305,12 @@ "visibility": true, "autouv": 0, "selected": false, - "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3", "98475898-cfc8-f67b-3145-abaf659deb3e", "7747736f-85e2-338f-207e-53f8d3d2fa39"] + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] } ] } @@ -1434,8 +1411,7 @@ ] } ] - }, - "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + } ] }, "c2e217f1-b50a-5c9a-b342-71a35e984046" @@ -1606,14 +1582,14 @@ }, "animations": [ { - "uuid": "5b786abf-47f3-0d36-5deb-834412c63569", + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", "name": "walk", "loop": "loop", "override": false, "length": 1, "snapping": 20, - "selected": true, - "saved": false, + "selected": false, + "saved": true, "path": "", "anim_time_update": "", "blend_weight": "", @@ -1865,33 +1841,402 @@ "easingArgs": [] } ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] }, "effects": { "name": "Effects", "type": "effect", "keyframes": [ { - "channel": "variant", + "channel": "function", "data_points": [ { - "variant": "5417306e-2c69-3f36-1e3c-edd904034a36", + "function": "say hi", "execute_condition": "", "repeat": false, "repeat_frequency": 1 } ], - "uuid": "1baa095b-e941-62df-a215-698b50c93524", + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", "time": 0, "color": -1, "interpolation": "linear", "easing": "linear", "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] } ] } } } ], - "animation_controllers": [], "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" } \ No newline at end of file diff --git a/test-packs/1.21.4/blueprints/armor_stand_1.21.4_static.ajblueprint b/test-packs/1.21.4/blueprints/armor_stand_1.21.4_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.21.4/blueprints/armor_stand_1.21.4_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.4/blueprints/armor_stand_minimal_1.21.4.ajblueprint b/test-packs/1.21.4/blueprints/armor_stand_minimal_1.21.4.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.21.4/blueprints/armor_stand_minimal_1.21.4.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.5/blueprints/armor_stand_1.21.5.ajblueprint b/test-packs/1.21.5/blueprints/armor_stand_1.21.5.ajblueprint new file mode 100644 index 00000000..2ad5af18 --- /dev/null +++ b/test-packs/1.21.5/blueprints/armor_stand_1.21.5.ajblueprint @@ -0,0 +1,2242 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.21.5\\blueprints\\armor_stand_1.21.5.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.21.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.5/blueprints/armor_stand_1.21.5_static.ajblueprint b/test-packs/1.21.5/blueprints/armor_stand_1.21.5_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.21.5/blueprints/armor_stand_1.21.5_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.5/blueprints/armor_stand_minimal_1.21.5.ajblueprint b/test-packs/1.21.5/blueprints/armor_stand_minimal_1.21.5.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.21.5/blueprints/armor_stand_minimal_1.21.5.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.5/datapacks/animated_java/pack.mcmeta b/test-packs/1.21.5/datapacks/animated_java/pack.mcmeta index a1929ee7..df388ca7 100644 --- a/test-packs/1.21.5/datapacks/animated_java/pack.mcmeta +++ b/test-packs/1.21.5/datapacks/animated_java/pack.mcmeta @@ -1,6 +1,10 @@ { "pack": { "pack_format": 71, - "description": "AJ Testing DP" + "description": "AJ Testing DP", + "supported_formats": { + "min_inclusive": 71, + "max_inclusive": 79 + } } } \ No newline at end of file diff --git a/test-packs/1.21.5/resources/pack.mcmeta b/test-packs/1.21.5/resources/pack.mcmeta index e90257db..1858ca8b 100644 --- a/test-packs/1.21.5/resources/pack.mcmeta +++ b/test-packs/1.21.5/resources/pack.mcmeta @@ -1,6 +1,10 @@ { "pack": { "pack_format": 55, - "description": "AJ Testing RP" + "description": "AJ Testing RP", + "supported_formats": { + "min_inclusive": 55, + "max_inclusive": 62 + } } } \ No newline at end of file diff --git a/test-packs/1.21.6/blueprints/armor_stand_1.21.6.ajblueprint b/test-packs/1.21.6/blueprints/armor_stand_1.21.6.ajblueprint new file mode 100644 index 00000000..2455b73d --- /dev/null +++ b/test-packs/1.21.6/blueprints/armor_stand_1.21.6.ajblueprint @@ -0,0 +1,2286 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.21.6\\blueprints\\armor_stand_1.21.6.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.21.6", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 9, 3], + "to": [5, 24, 4], + "autouv": 0, + "color": 2, + "rotation": [-7.5, 0, 0], + "origin": [0, 22.5, 4.5], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "7bb24e81-f2f5-ca5e-64d2-ce7dd4de9039" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "7bb24e81-f2f5-ca5e-64d2-ce7dd4de9039", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.6/blueprints/armor_stand_1.21.6_static.ajblueprint b/test-packs/1.21.6/blueprints/armor_stand_1.21.6_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.21.6/blueprints/armor_stand_1.21.6_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.6/blueprints/armor_stand_minimal_1.21.6.ajblueprint b/test-packs/1.21.6/blueprints/armor_stand_minimal_1.21.6.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.21.6/blueprints/armor_stand_minimal_1.21.6.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.6/datapacks/animated_java/pack.mcmeta b/test-packs/1.21.6/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..b69ef231 --- /dev/null +++ b/test-packs/1.21.6/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,10 @@ +{ + "pack": { + "pack_format": 80, + "description": "AJ Testing DP", + "supported_formats": { + "min_inclusive": 80, + "max_inclusive": 87 + } + } +} \ No newline at end of file diff --git a/test-packs/1.21.6/datapacks/test-framework/mcb.config.js b/test-packs/1.21.6/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.21.6/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.21.6/datapacks/test-framework/pack.mcmeta b/test-packs/1.21.6/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..618a6a2f --- /dev/null +++ b/test-packs/1.21.6/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack":{ + "pack_format": 74, + "description": ". Made with MC-Build!" + } +} \ No newline at end of file diff --git a/test-packs/1.21.6/datapacks/test-framework/package.json b/test-packs/1.21.6/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.21.6/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.21.6/datapacks/test-framework/src/test.mcb b/test-packs/1.21.6/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..57cbd78c --- /dev/null +++ b/test-packs/1.21.6/datapacks/test-framework/src/test.mcb @@ -0,0 +1,9 @@ +function on_load minecraft:load { + tellraw @a {text:'Test Framework loaded!', color:green} +} + +function summon { + #ARGS: {args:{...}} + function animated_java:armor_stand/remove/all + $execute positioned 0 -63 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} +} diff --git a/test-packs/1.21.6/resources/pack.mcmeta b/test-packs/1.21.6/resources/pack.mcmeta new file mode 100644 index 00000000..80eca407 --- /dev/null +++ b/test-packs/1.21.6/resources/pack.mcmeta @@ -0,0 +1,10 @@ +{ + "pack": { + "pack_format": 63, + "description": "AJ Testing RP", + "supported_formats": { + "min_inclusive": 63, + "max_inclusive": 68 + } + } +} \ No newline at end of file diff --git a/test-packs/1.21.9/blueprints/armor_stand_1.21.9.ajblueprint b/test-packs/1.21.9/blueprints/armor_stand_1.21.9.ajblueprint new file mode 100644 index 00000000..dc2456c8 --- /dev/null +++ b/test-packs/1.21.9/blueprints/armor_stand_1.21.9.ajblueprint @@ -0,0 +1,2241 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.21.9\\blueprints\\armor_stand_1.21.9.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "function": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "function", + "data_points": [ + { + "function": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "function", + "data_points": [ + { + "function": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "791551a5-7e73-8e04-068e-fc13614050d9", + "name": "test", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "bc141119-7f51-9690-7e21-e119b34fb3a2": { + "name": "item_display", + "type": "animated_java:vanilla_item_display", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "math.sin(q.life_time * 360 * 8) * 32", + "y": "0", + "z": "math.cos(q.life_time * 360 * 8) * 32" + } + ], + "uuid": "a69ba6a4-665c-ec76-2a8c-8b8e131069ce", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046": { + "name": "text_display", + "type": "animated_java:text_display", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * 360) * 2", + "y": "math.cos(q.life_time * 360) * 2", + "z": "0" + } + ], + "uuid": "c1567f01-545a-acab-0f30-60be7926f8ad", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-math.cos(q.life_time * 360) * 1", + "z": "0" + } + ], + "uuid": "0ef7b645-7023-abf3-6de5-1273f9531880", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 360) * 0.05", + "y": "1 + math.sin(q.life_time * 360) * 0.05", + "z": "1" + } + ], + "uuid": "cc319bc4-2caf-bd79-01a4-fbfa78d8b859", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "57100f98-a3c8-2bdb-1124-f26f002a4680", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "10", + "z": "0" + } + ], + "uuid": "8a4dbb22-bc64-41ed-2ee5-ad035414a7bf", + "time": 0.5, + "color": -1, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0" + } + ], + "uuid": "ce5edc5a-11dd-2b47-36e5-16200667dd1f", + "time": 1, + "color": -1, + "interpolation": "linear", + "easing": "easeInQuad", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "dda5a134-8352-b67e-5c03-c7655842ffa5", + "time": 0, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "465d64a5-61bf-db04-f1f5-bfdc24a3a925", + "time": 0.5, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "easeOutBack", + "easingArgs": [2] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1", + "y": "1", + "z": "1" + } + ], + "uuid": "4a7d7cfb-5436-9bff-eaa7-a0834787100d", + "time": 1, + "color": -1, + "uniform": true, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "0.9", + "y": "1.1", + "z": "0.9\n" + } + ], + "uuid": "5b63d06f-8aa2-33dc-e3ab-6da22b3c61c3", + "time": 0.15, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "easeOutCubic", + "easingArgs": [1] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.9/blueprints/armor_stand_1.21.9_static.ajblueprint b/test-packs/1.21.9/blueprints/armor_stand_1.21.9_static.ajblueprint new file mode 100644 index 00000000..c99bd70d --- /dev/null +++ b/test-packs/1.21.9/blueprints/armor_stand_1.21.9_static.ajblueprint @@ -0,0 +1,1585 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.5\\blueprints\\armor_stand_1.20.5_static.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "render_box": [95, 32], + "target_minecraft_version": "1.20.5", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "on_summon_function": "say On-Summon!", + "on_remove_function": "say On-Remove!", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 15, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "on_summon_function": "say Summon Left Hand!", + "on_remove_function": "say Remove Left Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "on_summon_function": "say Summon Right Hand!", + "on_remove_function": "say Remove Right Hand!", + "on_tick_function": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", + "name": "text_display", + "position": [0, 38, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", + "lineWidth": 400, + "backgroundColor": "#00000040", + "align": "center", + "shadow": true, + "seeThrough": true + }, + { + "name": "camera", + "position": [0, 27, 0], + "rotation": [0, 0, 0], + "fov": 70, + "aspect_ratio": [16, 9], + "linked_preview": "", + "camera_linked": false, + "visibility": true, + "type": "camera", + "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" + }, + { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", + "name": "item_display", + "position": [6, 12, -4], + "rotation": [-90, 0, 0], + "scale": [0.5, 0.5, 0.5], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4", "3ffeee76-901e-f8a2-c29a-82f90e16fd1e"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "13293700-7f42-2279-6a1f-4e0a6a4d89b3", + "98475898-cfc8-f67b-3145-abaf659deb3e", + "7747736f-85e2-338f-207e-53f8d3d2fa39", + "bc141119-7f51-9690-7e21-e119b34fb3a2" + ] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + } + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.9/blueprints/armor_stand_minimal_1.21.9.ajblueprint b/test-packs/1.21.9/blueprints/armor_stand_minimal_1.21.9.ajblueprint new file mode 100644 index 00000000..b9080027 --- /dev/null +++ b/test-packs/1.21.9/blueprints/armor_stand_minimal_1.21.9.ajblueprint @@ -0,0 +1,1277 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "173aa82f-fb4f-c354-ad34-a34d7fbce647", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand_minimal_1.20.4.ajblueprint", + "last_used_export_namespace": "armor_stand_minimal" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "blueprint_settings": { + "export_namespace": "armor_stand_minimal", + "render_box": [48, 48], + "target_minecraft_version": "1.20.4", + "resource_pack": "../resources", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false, + "baked_animations": false, + "json_file": "../testPluginExport.json" + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54"] + } + ] + } + ] + } + ] + } + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": true, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.21.9/datapacks/animated_java/pack.mcmeta b/test-packs/1.21.9/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..63070529 --- /dev/null +++ b/test-packs/1.21.9/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": "AJ Testing DP", + "min_format": 88, + "max_format": 10000000 + } +} \ No newline at end of file diff --git a/test-packs/1.21.9/datapacks/test-framework/mcb.config.js b/test-packs/1.21.9/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.21.9/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.21.9/datapacks/test-framework/pack.mcmeta b/test-packs/1.21.9/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..618a6a2f --- /dev/null +++ b/test-packs/1.21.9/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack":{ + "pack_format": 74, + "description": ". Made with MC-Build!" + } +} \ No newline at end of file diff --git a/test-packs/1.21.9/datapacks/test-framework/package.json b/test-packs/1.21.9/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.21.9/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.21.9/datapacks/test-framework/src/test.mcb b/test-packs/1.21.9/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..57cbd78c --- /dev/null +++ b/test-packs/1.21.9/datapacks/test-framework/src/test.mcb @@ -0,0 +1,9 @@ +function on_load minecraft:load { + tellraw @a {text:'Test Framework loaded!', color:green} +} + +function summon { + #ARGS: {args:{...}} + function animated_java:armor_stand/remove/all + $execute positioned 0 -63 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} +} diff --git a/test-packs/1.21.9/resources/pack.mcmeta b/test-packs/1.21.9/resources/pack.mcmeta new file mode 100644 index 00000000..0c283a5f --- /dev/null +++ b/test-packs/1.21.9/resources/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "min_format": 69, + "max_format": 10000000, + "description": "AJ Testing RP" + } +} \ No newline at end of file diff --git a/tools/esbuild.ts b/tools/esbuild.ts index 34d7728d..83881ee8 100644 --- a/tools/esbuild.ts +++ b/tools/esbuild.ts @@ -11,15 +11,17 @@ import ImportGlobPlugin from 'esbuild-plugin-import-glob' import inlineImage from 'esbuild-plugin-inline-image' import * as fs from 'fs' import { load } from 'js-yaml' +import vscodeProblemsPatch from 'node-modules-vscode-problems-patch' import * as path from 'path' import { isAbsolute, join } from 'path' import { TextDecoder } from 'util' import svelteConfig from '../svelte.config.js' import assetOverridePlugin from './plugins/assetOverridePlugin' +import bufferPatchPlugin from './plugins/bufferPatchFunction.js' +import importFolderPlugin from './plugins/importFolder' import mcbCompressionPlugin from './plugins/mcbCompressionPlugin' import packagerPlugin from './plugins/packagerPlugin' import sveltePlugin from './plugins/sveltePlugin' -import inlineWorkerPlugin from './plugins/workerPlugin' const PACKAGE = JSON.parse(fs.readFileSync('./package.json', 'utf-8')) const INFO_PLUGIN: esbuild.Plugin = { @@ -55,18 +57,17 @@ const DEPENDENCY_QUARKS: esbuild.Plugin = { } }) build.onResolve({ filter: /^deepslate\// }, args => { - // esbuild respects the package.json "exports" field - // but the version of typescript we're using doesn't - // so we need to resolve the path manually - const file_path = path.resolve( - process.cwd(), - path.dirname(require.resolve('deepslate')), - '..', - args.path.split('/').slice(1).join('/'), - 'index.js' - ) return { - path: file_path, + // esbuild respects the package.json "exports" field + // but the version of typescript we're using doesn't + // so we need to resolve the path manually + path: path.resolve( + process.cwd(), + path.dirname(require.resolve('deepslate')), + '..', + args.path.split('/').slice(1).join('/'), + 'index.js' + ), } }) }, @@ -76,7 +77,7 @@ function createBanner() { return s.replace(new RegExp(`(?![^\\n]{1,${width}}$)([^\\n]{1,${width}})\\s`, 'g'), '$1\n') } - const LICENSE = fs.readFileSync('./LICENSE').toString() + const license = fs.readFileSync('./LICENSE').toString() const fetchbot = PACKAGE.contributors[0] const dominexis = PACKAGE.contributors[1] let lines: string[] = [ @@ -107,7 +108,7 @@ function createBanner() { `${PACKAGE.repository.url as string}`, ``, `[ LICENSE ]`, - ...LICENSE.split('\n').map(v => v.trim()), + ...license.split('\n').map(v => v.trim()), ] const maxLength = Math.max(...lines.map(line => line.length)) @@ -143,7 +144,7 @@ function createBanner() { const DEFINES: Record = {} Object.entries(process.env).forEach(([key, value]) => { - if (key.match(/[^A-Za-z0-9_]/i)) return + if (/[^A-Za-z0-9_]/i.exec(key)) return DEFINES[`process.env.${key}`] = JSON.stringify(value) }) @@ -162,7 +163,10 @@ const yamlPlugin: (opts: { }) build.onLoad({ filter: /.*/, namespace: 'yaml' }, async args => { const yamlContent = await fs.promises.readFile(args.path) - let parsed = load(new TextDecoder().decode(yamlContent), options?.loadOptions) + let parsed = load( + new TextDecoder().decode(new Uint8Array(yamlContent)), + options?.loadOptions + ) if (options?.transform && options.transform(parsed, args.path) !== void 0) parsed = options.transform(parsed, args.path) return { @@ -174,40 +178,20 @@ const yamlPlugin: (opts: { }, }) -const devWorkerConfig: esbuild.BuildOptions = { - bundle: true, - minify: false, - platform: 'node', - sourcemap: 'inline', - sourceRoot: 'http://animated-java/', - loader: { '.svg': 'dataurl', '.ttf': 'binary', '.mcb': 'text' }, - plugins: [ - // inlineImage({ - // limit: -1, - // }), - // @ts-ignore - // ImportGlobPlugin.default(), - // INFO_PLUGIN, - // yamlPlugin({}), - // sveltePlugin(svelteConfig), - // packagerPlugin(), - ], - // format: 'iife', - // define: DEFINES, -} -const devConfig: esbuild.BuildOptions = { +const COMMON_CONFIG: esbuild.BuildOptions = { banner: createBanner(), entryPoints: ['./src/index.ts'], - outfile: `./dist/${PACKAGE.name as string}.js`, + outfile: `./dist/${PACKAGE.name}.js`, bundle: true, - minify: false, platform: 'node', - sourcemap: 'inline', - sourceRoot: 'http://animated-java/', - loader: { '.svg': 'dataurl', '.ttf': 'binary', '.mcb': 'text' }, + loader: { '.svg': 'dataurl', '.ttf': 'binary', '.css': 'text' }, plugins: [ - // @ts-ignore + // @ts-expect-error broken default import + vscodeProblemsPatch.default(), + importFolderPlugin, + // @ts-expect-error broken default import ImportGlobPlugin.default(), + bufferPatchPlugin(), inlineImage({ limit: -1, }), @@ -215,7 +199,6 @@ const devConfig: esbuild.BuildOptions = { yamlPlugin({}), sveltePlugin(svelteConfig), packagerPlugin(), - inlineWorkerPlugin(devWorkerConfig), assetOverridePlugin(), mcbCompressionPlugin(), DEPENDENCY_QUARKS, @@ -226,46 +209,27 @@ const devConfig: esbuild.BuildOptions = { treeShaking: true, } -const prodConfig: esbuild.BuildOptions = { - entryPoints: ['./src/index.ts'], - outfile: `./dist/${PACKAGE.name as string}.js`, - bundle: true, +const DEV_CONFIG: esbuild.BuildOptions = { + ...COMMON_CONFIG, + minify: false, + sourcemap: 'inline', + sourceRoot: 'http://animated-java/', +} + +const PROD_CONFIG: esbuild.BuildOptions = { minify: true, - platform: 'node', - loader: { '.svg': 'dataurl', '.ttf': 'binary', '.mcb': 'text' }, - plugins: [ - // @ts-ignore - ImportGlobPlugin.default(), - inlineImage({ - limit: -1, - }), - INFO_PLUGIN, - inlineWorkerPlugin({}), - yamlPlugin({}), - sveltePlugin(svelteConfig), - packagerPlugin(), - inlineWorkerPlugin({}), - assetOverridePlugin(), - mcbCompressionPlugin(), - DEPENDENCY_QUARKS, - ], - alias: { svelte: 'svelte' }, keepNames: true, - banner: createBanner(), drop: ['debugger'], - format: 'iife', - define: DEFINES, - treeShaking: true, metafile: true, } async function buildDev() { - const ctx = await esbuild.context(devConfig) + const ctx = await esbuild.context(DEV_CONFIG) await ctx.watch() } async function buildProd() { - const result = await esbuild.build(prodConfig).catch(() => process.exit(1)) + const result = await esbuild.build(PROD_CONFIG).catch(() => process.exit(1)) if (result.errors.length > 0) { console.error(result.errors) process.exit(1) diff --git a/tools/plugins/assetOverridePlugin.ts b/tools/plugins/assetOverridePlugin.ts index 20fe6997..c254c614 100644 --- a/tools/plugins/assetOverridePlugin.ts +++ b/tools/plugins/assetOverridePlugin.ts @@ -1,4 +1,4 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' import * as fs from 'fs/promises' import * as pathjs from 'path' @@ -20,7 +20,6 @@ function plugin(): Plugin { if (path.endsWith('.json') || path.endsWith('.png')) { const key = pathjs .join('assets', dir, path) - // @ts-expect-error .replaceAll(pathjs.sep, '/') .replace(ASSET_OVERRIDES_PATH, '') if (path.endsWith('.json')) { diff --git a/tools/plugins/bufferPatchFunction.ts b/tools/plugins/bufferPatchFunction.ts new file mode 100644 index 00000000..6d170850 --- /dev/null +++ b/tools/plugins/bufferPatchFunction.ts @@ -0,0 +1,19 @@ +import { type Plugin } from 'esbuild' + +export default function plugin(): Plugin { + return { + name: 'bufferPatchPlugin', + setup(build) { + build.onResolve({ filter: /^buffer$/ }, args => { + return { path: args.path, namespace: 'buffer-namespace' } + }) + + build.onLoad({ filter: /^buffer$/, namespace: 'buffer-namespace' }, () => { + return { + contents: `export const Buffer = globalThis.Buffer;`, + loader: 'js', + } + }) + }, + } +} diff --git a/tools/plugins/importFolder.ts b/tools/plugins/importFolder.ts new file mode 100644 index 00000000..f7dac778 --- /dev/null +++ b/tools/plugins/importFolder.ts @@ -0,0 +1,150 @@ +import type { Plugin } from 'esbuild' +import { existsSync, readdirSync, statSync, type ObjectEncodingOptions } from 'fs' +import { dirname, extname, join, posix, relative, resolve, sep } from 'path' + +interface IRecursiveDirEntry { + // The file's name with extension + name: string + // The file's extension + ext: string + // The file's absolute path + path: string + // The parent directory's absolute path + parentPath: string + // The file's path relative to the directory being recursed + localPath: string +} + +interface IRecursiveReadDirSyncOptions { + encoding?: ObjectEncodingOptions['encoding'] + maxDepth?: number + filter?: (file: IRecursiveDirEntry) => boolean +} + +/** + * Recursively reads a directory and returns an array of file information. + * @param dir The directory to read. + * @param encoding The encoding to use when reading file names. + * @param maxDepth The maximum depth to recurse into subdirectories. + * @returns An array of file information objects. + */ +function recursiveReadDirSync( + dir: string, + { encoding = 'utf-8', maxDepth = 200, filter }: IRecursiveReadDirSyncOptions +): IRecursiveDirEntry[] { + const files: IRecursiveDirEntry[] = [] + + function recurse(localDir: string, depth = 0) { + // If a local index is found, it is imported and the rest of the directory is ignored. + const indexPath = join(localDir, 'index.ts') + if (existsSync(indexPath)) { + const absolutePath = resolve(localDir, 'index.ts') + files.push({ + name: 'index', + ext: '.ts', + path: absolutePath, + parentPath: localDir, + localPath: relative(dir, absolutePath), + }) + return + } + + readdirSync(localDir, { encoding, withFileTypes: true }).forEach(dirEntry => { + const absolutePath = join(localDir, dirEntry.name) + if (dirEntry.isDirectory() && depth <= maxDepth) { + recurse(absolutePath, depth + 1) + } else { + const fileEntry: IRecursiveDirEntry = { + name: dirEntry.name, + ext: extname(dirEntry.name), + path: absolutePath, + parentPath: dirname(absolutePath), + localPath: relative(dir, absolutePath), + } + if (!!filter && !filter(fileEntry)) return + files.push(fileEntry) + } + }) + } + recurse(dir) + + return files +} + +function normalizePathToPosix(path: string) { + return path.replaceAll(sep, posix.sep) +} + +/** + * A plugin for importing all files in a folder without manually updating an index file. + * + * Use the `/*` suffix to import a folder. + * + * Use the `/**` suffix to recurse into subdirectories. + * + * NOTE - If you're using a glob plugin, this plugin should be executed first. + */ +const plugin: Plugin = { + name: 'import-folder', + setup: build => { + build.onResolve({ filter: /.+\/\*\*?$/, namespace: 'file' }, args => { + const fullPath = normalizePathToPosix(join(args.resolveDir, args.path)).replace( + /\/\*\*?$/, + '' + ) + + const stat = statSync(fullPath) + if (!stat.isDirectory()) { + return { + errors: [ + { + text: `"${fullPath}" is not a directory, but is being imported as a folder.`, + location: { file: args.importer }, + }, + ], + } + } + + return { + namespace: 'import-folder', + path: fullPath, + pluginData: { recursive: args.path.endsWith('/**'), importer: args.importer }, + } + }) + + build.onLoad({ filter: /.+/, namespace: 'import-folder' }, args => { + let files: IRecursiveDirEntry[] + + const filter: IRecursiveReadDirSyncOptions['filter'] = file => { + return file.ext === '.js' || file.ext === '.ts' + } + + if (args.pluginData.recursive) { + files = recursiveReadDirSync(args.path, { encoding: 'utf-8', filter }) + } else { + files = recursiveReadDirSync(args.path, { encoding: 'utf-8', filter, maxDepth: 0 }) + } + + const contents = files + .map(file => `import './${normalizePathToPosix(file.localPath)}';`) + .join('\n') + + console.log( + `📃 ${normalizePathToPosix( + relative(process.cwd(), args.pluginData.importer) + )} imports folder ${normalizePathToPosix(relative(process.cwd(), args.path))}${ + args.pluginData.recursive ? ' recursively' : '' + }.` + ) + + return { + loader: 'js', + contents, + watchFiles: files.map(file => join(file.parentPath, file.name)), + resolveDir: args.path, + } + }) + }, +} + +export default plugin diff --git a/tools/plugins/mcbCompressionPlugin.ts b/tools/plugins/mcbCompressionPlugin.ts index 6effe73d..a5e36583 100644 --- a/tools/plugins/mcbCompressionPlugin.ts +++ b/tools/plugins/mcbCompressionPlugin.ts @@ -1,7 +1,8 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' +import * as fflate from 'fflate' +import { existsSync } from 'fs' import * as fs from 'fs/promises' import * as pathjs from 'path' -import * as fflate from 'fflate' function zip(data: fflate.AsyncZippable): Promise { return new Promise((resolve, reject) => { @@ -18,10 +19,24 @@ export default function plugin(): Plugin { setup(build) { const mcbFiles = new Map() - build.onLoad({ filter: /\.mcb$/ }, async ({ path }) => { + build.onResolve({ filter: /\.mcbt?$/ }, args => { + const path = pathjs.join(args.resolveDir, args.path) + + if (!existsSync(path)) { + return { errors: [{ text: `MCB file not found: ${path}` }] } + } + + return { + path, + namespace: 'mcb', + watchFiles: [path], + } + }) + + build.onLoad({ filter: /\.mcbt?$/, namespace: 'mcb' }, async ({ path }) => { const localPath = pathjs.relative(process.cwd(), path).replace(/\\/g, '/') const data = await fs.readFile(path) - mcbFiles.set(localPath, data) + mcbFiles.set(localPath, new Uint8Array(data)) return { contents: ` @@ -39,7 +54,7 @@ export default getZipFile('${localPath}') } }) - build.onLoad({ filter: /.*/, namespace: 'mcbZipData' }, async ({ path }) => { + build.onLoad({ filter: /.*/, namespace: 'mcbZipData' }, async () => { const zipped = await zip(Object.fromEntries(mcbFiles.entries())) const data = Buffer.from(zipped).toString('base64') return { diff --git a/tools/plugins/packagerPlugin.ts b/tools/plugins/packagerPlugin.ts index e6922460..5500502c 100644 --- a/tools/plugins/packagerPlugin.ts +++ b/tools/plugins/packagerPlugin.ts @@ -1,10 +1,14 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' import * as fs from 'fs' +import { readFileSync, writeFileSync } from 'fs' +import { Octokit } from 'octokit' import * as pathjs from 'path' +// @ts-expect-error No types +import * as prettier from 'prettier' import * as c from 'svelte/compiler' -import { readFileSync, writeFileSync } from 'fs' import * as svelteInternal from 'svelte/internal' -import * as prettier from 'prettier' + +const OCTO_KIT = new Octokit({}) const PACKAGE = JSON.parse(fs.readFileSync('./package.json', 'utf-8')) const PLUGIN_PACKAGE_PATH = './src/pluginPackage/' @@ -16,16 +20,30 @@ const PLUGIN_REPO_PATH = 'D:/github-repos/snavesutit/blockbench-plugins/plugins/ const PLUGIN_MANIFEST_PATH = 'D:/github-repos/snavesutit/blockbench-plugins/plugins.json' const CHANGELOG_PATH = './src/pluginPackage/changelog.json' const RELEASE_NOTES_TEMPLATES = './tools/plugins/releaseNoteTemplates/' +const URL_REGEX = + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gm function replaceTemplateVars(str: string, items: Record) { - return str.replace(/\{(.+?)\}/g, str => items[str.replace(/[\{\}]/g, '')] || str) + return str.replace(/\{(.+?)\}/g, str => items[str.replace(/[\{\}]/g, '')] ?? str) +} + +const VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9]+))?/ + +function getVersionNumbers(version: string) { + const match = VERSION_REGEX.exec(version) + if (!match) return null + const major = parseInt(match[1]) + const minor = parseInt(match[2]) + const patch = parseInt(match[3]) + const preRelease = match[4] ?? null + return { major, minor, patch, preRelease } } function plugin(): Plugin { return { name: 'packagerPlugin', setup(build) { - build.onEnd(() => { + build.onEnd(async () => { console.log('📦 Packaging...') fs.rmSync(DIST_PACKAGE_PATH, { recursive: true, force: true }) fs.cpSync(PLUGIN_PACKAGE_PATH, DIST_PACKAGE_PATH, { recursive: true }) @@ -35,7 +53,7 @@ function plugin(): Plugin { ) const svelteResult = c.compile(readFileSync(SVELTE_FILE, 'utf-8'), { generate: 'ssr', - cssHash({ name, filename, hash, css }) { + cssHash({ hash, css }) { return `animated-java-plugin-page-${hash(css)}` }, }) @@ -53,25 +71,104 @@ function plugin(): Plugin { fs.unlinkSync(pathjs.join(DIST_PACKAGE_PATH, 'about.svelte')) if (process.env.NODE_ENV === 'production') { - console.log('📝 Creating changelogs...') - const changelog = JSON.parse(fs.readFileSync(CHANGELOG_PATH, 'utf-8')) - for (const file of fs.readdirSync(RELEASE_NOTES_TEMPLATES)) { - let content = fs.readFileSync( - pathjs.join(RELEASE_NOTES_TEMPLATES, file), - 'utf-8' - ) - content = replaceTemplateVars(content, { - version: PACKAGE.version, - changes: changelog[PACKAGE.version].categories - .find(c => c.title === 'Changes') - ?.list.map(v => '- ' + v) - .join('\n'), - fixes: changelog[PACKAGE.version].categories - .find(c => c.title === 'Fixes') - ?.list.map(v => '- ' + v) - .join('\n'), - }) - fs.writeFileSync(pathjs.join(DIST_PATH, file), content) + try { + console.log('📝 Creating changelogs...') + const rawChangelog = fs.readFileSync(CHANGELOG_PATH, 'utf-8') + const changelog = JSON.parse(rawChangelog) + for (const file of fs.readdirSync(RELEASE_NOTES_TEMPLATES)) { + let content = fs.readFileSync( + pathjs.join(RELEASE_NOTES_TEMPLATES, file), + 'utf-8' + ) + let pings = '' + const version = getVersionNumbers(PACKAGE.version) + if (!version) { + throw new Error( + `Version ${PACKAGE.version} in package.json is not valid semver!` + ) + } + const latestRelease = getVersionNumbers( + ( + await OCTO_KIT.request('GET /repos/{owner}/{repo}/releases', { + owner: 'animated-java', + repo: 'animated-java', + per_page: 1, + headers: { + accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + ).data[0].tag_name + ) + if (!latestRelease) { + throw new Error('No latest release found on github!') + } + if (version.major > latestRelease.major) { + pings += `@Major Release Ping` + } + if (version.minor > latestRelease.minor) { + pings += ` @Minor Release Ping` + } + if (version.patch > latestRelease.patch) { + pings += ` @Patch Release Ping` + } + if (latestRelease.preRelease) { + pings += ` @Pre-Release Ping` + } + if (rawChangelog.includes('[BREAKING]')) { + pings += ` @Breaking Changes Ping` + } + + const versionChangelog = changelog[PACKAGE.version] + if (!versionChangelog) { + throw new Error( + `No changelog found for version ${PACKAGE.version} in ${CHANGELOG_PATH}` + ) + } + + const changeList = versionChangelog.categories.find( + (c: any) => c.title === 'Changes' + ) + let changes = '' + if (changeList) { + changes = + '### Changes\n\n' + + changeList.list + .map((v: any) => '- ' + v) + .join('\n') + .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') + } + const fixList = versionChangelog.categories.find( + (c: any) => c.title === 'Fixes' + ) + let fixes = '' + if (fixList) { + fixes = + '### Fixes\n\n' + + fixList.list + .map((v: any) => '- ' + v) + .join('\n') + .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') + } + + content = replaceTemplateVars(content, { + version: PACKAGE.version, + changes, + fixes, + pings: pings.trim(), + }) + + if (content.includes('[[ESCAPE_URLS]]')) { + content = content + .replace('[[ESCAPE_URLS]]', '') + .replaceAll(URL_REGEX, (match: string) => '<' + match + '>') + } + + fs.writeFileSync(pathjs.join(DIST_PATH, file), content) + } + } catch (e) { + console.error('Error creating changelogs:', e) + throw e } if (fs.existsSync(PLUGIN_REPO_PATH)) { @@ -84,6 +181,7 @@ function plugin(): Plugin { manifest.animated_java.description = PACKAGE.description manifest.animated_java.version = PACKAGE.version manifest.animated_java.min_version = PACKAGE.min_blockbench_version + manifest.animated_java.max_version = PACKAGE.max_blockbench_version manifest.animated_java.variant = PACKAGE.variant manifest.animated_java.tags = PACKAGE.tags manifest.animated_java.has_changelog = true diff --git a/tools/plugins/releaseNoteTemplates/discord_release_notes_template b/tools/plugins/releaseNoteTemplates/discord_release_notes_template index e02fda8d..5268f35b 100644 --- a/tools/plugins/releaseNoteTemplates/discord_release_notes_template +++ b/tools/plugins/releaseNoteTemplates/discord_release_notes_template @@ -1,18 +1,16 @@ -# :AnimatedJava: Animated Java Release {version} +[[ESCAPE_URLS]] -### Changes +# :AnimatedJava: Animated Java Release v{version} {changes} -### Fixes - {fixes} ## How to Install -For now, you can download and install the latest build of AJ [here]() or install it via this link: - -Once [this PR]() has been merged, you can install the latest release of AJ from the Blockbench plugin list. -[Follow this tutorial]() if you don't know how to install plugins in Blockbench. +For now, you can download and install the latest build of AJ [here](https://builds.animated-java.dev/latest) or install it via this link: +https://builds.animated-java.dev/latest/download/animated_java.js +Once [this PR](https://github.com/JannisX11/blockbench-plugins/pull/{pr_number}) has been merged, you can install the latest release of AJ from the Blockbench plugin list. +[Follow this tutorial](https://animated-java.github.io/docs/getting-started/installing-animated-java) if you don't know how to install plugins in Blockbench. -{pings} +-# {pings} diff --git a/tools/plugins/releaseNoteTemplates/github_release_notes_template b/tools/plugins/releaseNoteTemplates/github_release_notes_template index 387714a6..198dd399 100644 --- a/tools/plugins/releaseNoteTemplates/github_release_notes_template +++ b/tools/plugins/releaseNoteTemplates/github_release_notes_template @@ -1,16 +1,12 @@ -# Animated Java Release {version} - -### Changes +# Animated Java Release v{version} {changes} -### Fixes - {fixes} ## How to Install For now, you can download and install the latest build of AJ [here](https://builds.animated-java.dev/latest) or install it via this link: - +https://builds.animated-java.dev/latest/download/animated_java.js Once [this PR](https://github.com/JannisX11/blockbench-plugins/pull/{pr_number}) has been merged, you can install the latest release of AJ from the Blockbench plugin list. [Follow this tutorial](https://animated-java.github.io/docs/getting-started/installing-animated-java) if you don't know how to install plugins in Blockbench. diff --git a/tools/plugins/sveltePlugin.ts b/tools/plugins/sveltePlugin.ts index 79068aaa..6f785046 100644 --- a/tools/plugins/sveltePlugin.ts +++ b/tools/plugins/sveltePlugin.ts @@ -6,10 +6,10 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) +import type { Plugin } from 'esbuild' import { readFile } from 'fs/promises' -import { preprocess, compile } from 'svelte/compiler' import { relative } from 'path' -import { Plugin } from 'esbuild' +import { compile, preprocess } from 'svelte/compiler' /** * Convert a warning or error emitted from the svelte compiler for esbuild. */ @@ -49,15 +49,18 @@ function esbuildPluginSvelte( build.onLoad({ filter: /\.svelte$/ }, async ({ path }) => { let source = await readFile(path, 'utf-8') const filename = relative(process.cwd(), path) + let sourcemap: any if (opts.preprocess) { const processed = await preprocess(source, opts.preprocess, { filename, }) source = processed.code + sourcemap = processed.map } const compilerOptions = { css: false, ...opts.compilerOptions, + sourcemap, } let res try { diff --git a/tools/plugins/workerPlugin.ts b/tools/plugins/workerPlugin.ts deleted file mode 100644 index a5cd59b9..00000000 --- a/tools/plugins/workerPlugin.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-env node */ -import * as esbuild from 'esbuild' -import findCacheDir from 'find-cache-dir' -import * as fs from 'fs' -import * as path from 'path' - -export { inlineWorkerPlugin as default } - -function inlineWorkerPlugin(extraConfig) { - return { - name: 'esbuild-plugin-inline-worker', - - setup(build) { - build.onLoad({ filter: /\.worker\.(js|jsx|ts|tsx)$/ }, async ({ path: workerPath }) => { - // let workerCode = await fs.promises.readFile(workerPath, { - // encoding: 'utf-8', - // }); - - let workerCode = await buildWorker(workerPath, extraConfig) - return { - contents: `import inlineWorker from '__inline-worker' -export default function Worker() { - return inlineWorker(${JSON.stringify(workerCode)}); -} -`, - loader: 'js', - } - }) - - const name = extraConfig.workerName ? { name: extraConfig.workerName } : {} - - const inlineWorkerFunctionCode = ` -export default function inlineWorker(scriptText) { - let blob = new Blob([scriptText], {type: 'text/javascript'}); - let url = URL.createObjectURL(blob); - let worker = new Worker(url, ${JSON.stringify(name)}); - URL.revokeObjectURL(url); - return worker; -} -` - - build.onResolve({ filter: /^__inline-worker$/ }, ({ path }) => { - return { path, namespace: 'inline-worker' } - }) - build.onLoad({ filter: /.*/, namespace: 'inline-worker' }, () => { - return { contents: inlineWorkerFunctionCode, loader: 'js' } - }) - }, - } -} - -let cacheDir = findCacheDir({ - name: 'esbuild-plugin-inline-worker', - create: true, -}) - -async function buildWorker(workerPath, extraConfig) { - let scriptNameParts = path.basename(workerPath).split('.') - scriptNameParts.pop() - scriptNameParts.push('js') - let scriptName = scriptNameParts.join('.') - let bundlePath = path.resolve(cacheDir, scriptName) - - if (extraConfig) { - delete extraConfig.entryPoints - delete extraConfig.outfile - delete extraConfig.outdir - delete extraConfig.workerName - } - - await esbuild.build({ - entryPoints: [workerPath], - bundle: true, - minify: true, - outfile: bundlePath, - target: 'es2017', - format: 'esm', - ...extraConfig, - }) - - return fs.promises.readFile(bundlePath, { encoding: 'utf-8' }) -} diff --git a/tools/tslintNamingConventionRule.d.ts b/tools/tslintNamingConventionRule.d.ts new file mode 100644 index 00000000..321ac053 --- /dev/null +++ b/tools/tslintNamingConventionRule.d.ts @@ -0,0 +1,185 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +declare namespace SharedConfig { + export type Severity = 0 | 1 | 2 + export type SeverityString = 'error' | 'off' | 'warn' + export type RuleLevel = Severity | SeverityString + + export type RuleLevelAndOptions = [RuleLevel, ...unknown[]] + + export type RuleEntry = RuleLevel | RuleLevelAndOptions + export type RulesRecord = Partial> + + export type GlobalVariableOptionBase = + | 'off' + | /** @deprecated use `'readonly'` */ 'readable' + | 'readonly' + | 'writable' + | /** @deprecated use `'writable'` */ 'writeable' + export type GlobalVariableOptionBoolean = + | /** @deprecated use `'readonly'` */ false + | /** @deprecated use `'writable'` */ true + export type GlobalVariableOption = GlobalVariableOptionBase | GlobalVariableOptionBoolean + + export type GlobalsConfig = Record + export type EnvironmentConfig = Record + + export interface PluginMeta { + /** + * The meta.name property should match the npm package name for your plugin. + */ + name: string + /** + * The meta.version property should match the npm package version for your plugin. + */ + version: string + } +} + +declare enum PredefinedFormats { + camelCase = 1, + strictCamelCase, + PascalCase, + StrictPascalCase, + snake_case, + UPPER_CASE, +} +declare type PredefinedFormatsString = keyof typeof PredefinedFormats + +declare enum UnderscoreOptions { + forbid = 1, + allow, + require, + + // special cases as it's common practice to use double underscore + requireDouble, + allowDouble, + allowSingleOrDouble, +} +declare type UnderscoreOptionsString = keyof typeof UnderscoreOptions + +declare enum Selectors { + // variableLike + variable = 1 << 0, + function = 1 << 1, + parameter = 1 << 2, + + // memberLike + parameterProperty = 1 << 3, + classicAccessor = 1 << 4, + enumMember = 1 << 5, + classMethod = 1 << 6, + objectLiteralMethod = 1 << 7, + typeMethod = 1 << 8, + classProperty = 1 << 9, + objectLiteralProperty = 1 << 10, + typeProperty = 1 << 11, + autoAccessor = 1 << 12, + + // typeLike + class = 1 << 13, + interface = 1 << 14, + typeAlias = 1 << 15, + enum = 1 << 16, + typeParameter = 1 << 17, + + // other + import = 1 << 18, +} +declare type SelectorsString = keyof typeof Selectors + +declare enum MetaSelectors { + default = -1, + variableLike = 0 | Selectors.variable | Selectors.function | Selectors.parameter, + memberLike = 0 | + Selectors.classProperty | + Selectors.objectLiteralProperty | + Selectors.typeProperty | + Selectors.parameterProperty | + Selectors.enumMember | + Selectors.classMethod | + Selectors.objectLiteralMethod | + Selectors.typeMethod | + Selectors.classicAccessor | + Selectors.autoAccessor, + typeLike = 0 | + Selectors.class | + Selectors.interface | + Selectors.typeAlias | + Selectors.enum | + Selectors.typeParameter, + method = 0 | Selectors.classMethod | Selectors.objectLiteralMethod | Selectors.typeMethod, + property = 0 | + Selectors.classProperty | + Selectors.objectLiteralProperty | + Selectors.typeProperty, + accessor = 0 | Selectors.classicAccessor | Selectors.autoAccessor, +} +declare type MetaSelectorsString = keyof typeof MetaSelectors +declare type IndividualAndMetaSelectorsString = MetaSelectorsString | SelectorsString + +declare enum Modifiers { + // const variable + const = 1 << 0, + // readonly members + readonly = 1 << 1, + // static members + static = 1 << 2, + // member accessibility + public = 1 << 3, + protected = 1 << 4, + private = 1 << 5, + '#private' = 1 << 6, + abstract = 1 << 7, + // destructured variable + destructured = 1 << 8, + // variables declared in the top-level scope + global = 1 << 9, + // things that are exported + exported = 1 << 10, + // things that are unused + unused = 1 << 11, + // properties that require quoting + requiresQuotes = 1 << 12, + // class members that are overridden + override = 1 << 13, + // class methods, object function properties, or functions that are async via the `async` keyword + async = 1 << 14, + // default imports + default = 1 << 15, + // namespace imports + namespace = 1 << 16, + + // make sure TypeModifiers starts at Modifiers + 1 or else sorting won't work +} +declare type ModifiersString = keyof typeof Modifiers + +declare enum TypeModifiers { + boolean = 1 << 17, + string = 1 << 18, + number = 1 << 19, + function = 1 << 20, + array = 1 << 21, +} +declare type TypeModifiersString = keyof typeof TypeModifiers + +interface MatchRegex { + match: boolean + regex: string +} + +declare interface Selector { + custom?: MatchRegex + filter?: string | MatchRegex + // format options + format: PredefinedFormatsString[] | null + leadingUnderscore?: UnderscoreOptionsString + modifiers?: ModifiersString[] + prefix?: string[] + // selector options + selector: IndividualAndMetaSelectorsString | IndividualAndMetaSelectorsString[] + suffix?: string[] + trailingUnderscore?: UnderscoreOptionsString + types?: TypeModifiersString[] +} + +export type NamingConventionRule = [SharedConfig.RuleEntry, ...Selector[]] diff --git a/tsconfig.json b/tsconfig.json index 4ea32954..49186bf5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,12 @@ { - "include": ["./src/**/*"], + "include": [ + "src/**/*", + "tools/**/*", + "./eslint.config.ts", + "./vitest.config.ts", + "./svelte.config.ts" + ], + "exclude": ["**/node_modules/**"], "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, @@ -7,9 +14,15 @@ "target": "ES2022", "moduleResolution": "node", "strict": true, - "types": ["node", "./types/blockbench-types", "svelte"], + "types": ["node"], "resolveJsonModule": true, "allowSyntheticDefaultImports": true, - "allowUmdGlobalAccess": true + "allowUmdGlobalAccess": true, + "allowUnreachableCode": true, + "esModuleInterop": true, + "noEmit": true, + "isolatedModules": true, + "alwaysStrict": true, + "baseUrl": "." } } diff --git a/vitest.config.ts b/vitest.config.ts index 143d16f2..ba83ee18 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - dir: 'tests', + dir: 'src/tests', }, }) diff --git a/yarn.lock b/yarn.lock index 0a1dddaf..c2ccc35e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,10 +54,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.3.tgz#78d3e6dcd19c1cb91f3940143e86dad1094aee81" integrity sha512-yTgnwQpFVYfvvo4SvRFB0SwrW8YjOxEoT7wfMT7Ol5v7v5LDNvSGo67aExmxOb87nQNeWPVvaGBNfQ7BXcrZ9w== -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== +"@esbuild/aix-ppc64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz#ee6b7163a13528e099ecf562b972f2bcebe0aa97" + integrity sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw== "@esbuild/android-arm64@0.17.19": version "0.17.19" @@ -69,10 +69,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.3.tgz#5eea56c21d61734942e050840d881eb7bedc3993" integrity sha512-c+ty9necz3zB1Y+d/N+mC6KVVkGUUOcm4ZmT5i/Fk5arOaY3i6CA3P5wo/7+XzV8cb4GrI/Zjp8NuOQ9Lfsosw== -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== +"@esbuild/android-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz#115fc76631e82dd06811bfaf2db0d4979c16e2cb" + integrity sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg== "@esbuild/android-arm@0.17.19": version "0.17.19" @@ -84,10 +84,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.3.tgz#7fda92e3231043c071ea6aa76c92accea86439fd" integrity sha512-bviJOLMgurLJtF1/mAoJLxDZDL6oU5/ztMHnJQRejbJrSc9FFu0QoUoFhvi6qSKJEw9y5oGyvr9fuDtzJ30rNQ== -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== +"@esbuild/android-arm@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.10.tgz#8d5811912da77f615398611e5bbc1333fe321aa9" + integrity sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w== "@esbuild/android-x64@0.17.19": version "0.17.19" @@ -99,10 +99,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.3.tgz#625d139bde81b81f54ff493b1381ca0f540200f3" integrity sha512-JReHfYCRK3FVX4Ra+y5EBH1b9e16TV2OxrPAvzMsGeES0X2Ndm9ImQRI4Ket757vhc5XBOuGperw63upesclRw== -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== +"@esbuild/android-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.10.tgz#e3e96516b2d50d74105bb92594c473e30ddc16b1" + integrity sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg== "@esbuild/darwin-arm64@0.17.19": version "0.17.19" @@ -114,10 +114,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.3.tgz#fa25f38a43ff4c469589d1dc93448d534d7f313b" integrity sha512-U3fuQ0xNiAkXOmQ6w5dKpEvXQRSpHOnbw7gEfHCRXPeTKW9sBzVck6C5Yneb8LfJm0l6le4NQfkNPnWMSlTFUQ== -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== +"@esbuild/darwin-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz#6af6bb1d05887dac515de1b162b59dc71212ed76" + integrity sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA== "@esbuild/darwin-x64@0.17.19": version "0.17.19" @@ -129,10 +129,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.3.tgz#2e450b8214f179a56b4559b2f107060e2b792c7e" integrity sha512-3m1CEB7F07s19wmaMNI2KANLcnaqryJxO1fXHUV5j1rWn+wMxdUYoPyO2TnAbfRZdi7ADRwJClmOwgT13qlP3Q== -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== +"@esbuild/darwin-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz#99ae82347fbd336fc2d28ffd4f05694e6e5b723d" + integrity sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg== "@esbuild/freebsd-arm64@0.17.19": version "0.17.19" @@ -144,10 +144,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.3.tgz#f6b29e07bce25c545f6f7bb031d3be6a6ea1dc50" integrity sha512-fsNAAl5pU6wmKHq91cHWQT0Fz0vtyE1JauMzKotrwqIKAswwP5cpHUCxZNSTuA/JlqtScq20/5KZ+TxQdovU/g== -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== +"@esbuild/freebsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz#0c6d5558a6322b0bdb17f7025c19bd7d2359437d" + integrity sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg== "@esbuild/freebsd-x64@0.17.19": version "0.17.19" @@ -159,10 +159,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.3.tgz#1a5da2bf89f8d67102820d893d271a270ae55751" integrity sha512-tci+UJ4zP5EGF4rp8XlZIdq1q1a/1h9XuronfxTMCNBslpCtmk97Q/5qqy1Mu4zIc0yswN/yP/BLX+NTUC1bXA== -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== +"@esbuild/freebsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz#8c35873fab8c0857a75300a3dcce4324ca0b9844" + integrity sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA== "@esbuild/linux-arm64@0.17.19": version "0.17.19" @@ -174,10 +174,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.3.tgz#355f6624c1ac6f5f68841a327ac90b98c679626c" integrity sha512-vvG6R5g5ieB4eCJBQevyDMb31LMHthLpXTc2IGkFnPWS/GzIFDnaYFp558O+XybTmYrVjxnryru7QRleJvmZ6Q== -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== +"@esbuild/linux-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz#3edc2f87b889a15b4cedaf65f498c2bed7b16b90" + integrity sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ== "@esbuild/linux-arm@0.17.19": version "0.17.19" @@ -189,10 +189,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.3.tgz#872a476ca18a962a98700024c447a79279db1d45" integrity sha512-f6kz2QpSuyHHg01cDawj0vkyMwuIvN62UAguQfnNVzbge2uWLhA7TCXOn83DT0ZvyJmBI943MItgTovUob36SQ== -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== +"@esbuild/linux-arm@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz#86501cfdfb3d110176d80c41b27ed4611471cde7" + integrity sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg== "@esbuild/linux-ia32@0.17.19": version "0.17.19" @@ -204,10 +204,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.3.tgz#da713eb80ff6c011ed01aa4deebb5fc758906046" integrity sha512-HjCWhH7K96Na+66TacDLJmOI9R8iDWDDiqe17C7znGvvE4sW1ECt9ly0AJ3dJH62jHyVqW9xpxZEU1jKdt+29A== -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== +"@esbuild/linux-ia32@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz#e6589877876142537c6864680cd5d26a622b9d97" + integrity sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ== "@esbuild/linux-loong64@0.17.19": version "0.17.19" @@ -219,10 +219,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.3.tgz#a7c5dc9e961009018d23ec53a43baa8c03c5a1d5" integrity sha512-BGpimEccmHBZRcAhdlRIxMp7x9PyJxUtj7apL2IuoG9VxvU/l/v1z015nFs7Si7tXUwEsvjc1rOJdZCn4QTU+Q== -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== +"@esbuild/linux-loong64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz#11119e18781f136d8083ea10eb6be73db7532de8" + integrity sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg== "@esbuild/linux-mips64el@0.17.19": version "0.17.19" @@ -234,10 +234,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.3.tgz#b97543f3d8655365729f3702ed07f6e41be5e48e" integrity sha512-5rMOWkp7FQGtAH3QJddP4w3s47iT20hwftqdm7b+loe95o8JU8ro3qZbhgMRy0VuFU0DizymF1pBKkn3YHWtsw== -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== +"@esbuild/linux-mips64el@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz#3052f5436b0c0c67a25658d5fc87f045e7def9e6" + integrity sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA== "@esbuild/linux-ppc64@0.17.19": version "0.17.19" @@ -249,10 +249,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.3.tgz#23b9064d5bc0bf28a115a2f9cf69f3b01cdfe01c" integrity sha512-h0zj1ldel89V5sjPLo5H1SyMzp4VrgN1tPkN29TmjvO1/r0MuMRwJxL8QY05SmfsZRs6TF0c/IDH3u7XYYmbAg== -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== +"@esbuild/linux-ppc64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz#2f098920ee5be2ce799f35e367b28709925a8744" + integrity sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA== "@esbuild/linux-riscv64@0.17.19": version "0.17.19" @@ -264,10 +264,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.3.tgz#4f2536327f6d444c0573bd35bbd3a67897dbd5da" integrity sha512-dkAKcTsTJ+CRX6bnO17qDJbLoW37npd5gSNtSzjYQr0svghLJYGYB0NF1SNcU1vDcjXLYS5pO4qOW4YbFama4A== -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== +"@esbuild/linux-riscv64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz#fa51d7fd0a22a62b51b4b94b405a3198cf7405dd" + integrity sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA== "@esbuild/linux-s390x@0.17.19": version "0.17.19" @@ -279,10 +279,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.3.tgz#05e6f3a12a0dcd60672f25e8789a83cd3affa487" integrity sha512-vnD1YUkovEdnZWEuMmy2X2JmzsHQqPpZElXx6dxENcIwTu+Cu5ERax6+Ke1QsE814Zf3c6rxCfwQdCTQ7tPuXA== -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== +"@esbuild/linux-s390x@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz#a27642e36fc282748fdb38954bd3ef4f85791e8a" + integrity sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew== "@esbuild/linux-x64@0.17.19": version "0.17.19" @@ -294,10 +294,15 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.3.tgz#994d347e7f530c33628e35e48ccde8f299adbcb6" integrity sha512-IOXOIm9WaK7plL2gMhsWJd+l2bfrhfilv0uPTptoRoSb2p09RghhQQp9YY6ZJhk/kqmeRt6siRdMSLLwzuT0KQ== -"@esbuild/linux-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" - integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== +"@esbuild/linux-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz#9d9b09c0033d17529570ced6d813f98315dfe4e9" + integrity sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA== + +"@esbuild/netbsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz#25c09a659c97e8af19e3f2afd1c9190435802151" + integrity sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A== "@esbuild/netbsd-x64@0.17.19": version "0.17.19" @@ -309,10 +314,15 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.3.tgz#309d8c323632e9c70ee92cf5414fa65b5eb7e00e" integrity sha512-uTgCwsvQ5+vCQnqM//EfDSuomo2LhdWhFPS8VL8xKf+PKTCrcT/2kPPoWMTs22aB63MLdGMJiE3f1PHvCDmUOw== -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== +"@esbuild/netbsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz#7fa5f6ffc19be3a0f6f5fd32c90df3dc2506937a" + integrity sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig== + +"@esbuild/openbsd-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz#8faa6aa1afca0c6d024398321d6cb1c18e72a1c3" + integrity sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw== "@esbuild/openbsd-x64@0.17.19": version "0.17.19" @@ -324,10 +334,15 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.3.tgz#28820f9431fe00f2b04aac57511754213ff060eb" integrity sha512-vNAkR17Ub2MgEud2Wag/OE4HTSI6zlb291UYzHez/psiKarp0J8PKGDnAhMBcHFoOHMXHfExzmjMojJNbAStrQ== -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== +"@esbuild/openbsd-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz#a42979b016f29559a8453d32440d3c8cd420af5e" + integrity sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw== + +"@esbuild/openharmony-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz#fd87bfeadd7eeb3aa384bbba907459ffa3197cb1" + integrity sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag== "@esbuild/sunos-x64@0.17.19": version "0.17.19" @@ -339,10 +354,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.3.tgz#a1f7f98b85bd221fe0f545d01abc0e6123ae60dc" integrity sha512-W8H9jlGiSBomkgmouaRoTXo49j4w4Kfbl6I1bIdO/vT0+0u4f20ko3ELzV3hPI6XV6JNBVX+8BC+ajHkvffIJA== -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== +"@esbuild/sunos-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz#3a18f590e36cb78ae7397976b760b2b8c74407f4" + integrity sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ== "@esbuild/win32-arm64@0.17.19": version "0.17.19" @@ -354,10 +369,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.3.tgz#c6c3c0b1a1dfc6327ef4db6aa4fb6efd9df531f7" integrity sha512-EjEomwyLSCg8Ag3LDILIqYCZAq/y3diJ04PnqGRgq8/4O3VNlXyMd54j/saShaN4h5o5mivOjAzmU6C3X4v0xw== -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== +"@esbuild/win32-arm64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz#e71741a251e3fd971408827a529d2325551f530c" + integrity sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw== "@esbuild/win32-ia32@0.17.19": version "0.17.19" @@ -369,10 +384,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.3.tgz#471b8d2cad1bd6479eee5acf04bba2c0e4d37e24" integrity sha512-WGiE/GgbsEwR33++5rzjiYsKyHywE8QSZPF7Rfx9EBfK3Qn3xyR6IjyCr5Uk38Kg8fG4/2phN7sXp4NPWd3fcw== -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== +"@esbuild/win32-ia32@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz#c6f010b5d3b943d8901a0c87ea55f93b8b54bf94" + integrity sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw== "@esbuild/win32-x64@0.17.19": version "0.17.19" @@ -384,10 +399,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.3.tgz#899c03576c4c28c83228f0e64dfa10edae99c9a2" integrity sha512-xRxC0jaJWDLYvcUvjQmHCJSfMrgmUuvsoXgDeU/wTorQ1ngDdUBuFtgY3W1Pc5sprGAvZBtWdJX7RPg/iZZUqA== -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== +"@esbuild/win32-x64@0.25.10": + version "0.25.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz#e4b3e255a1b4aea84f6e1d2ae0b73f826c3785bd" + integrity sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw== "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -396,30 +411,78 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/eslint-utils@^4.6.1", "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint-community/regexpp@^4.4.0": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== +"@eslint/config-array@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636" + integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.0.tgz#e9f94ba3b5b875e32205cb83fece18e64486e9e6" + integrity sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog== + dependencies: + "@eslint/core" "^0.16.0" + +"@eslint/core@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.16.0.tgz#490254f275ba9667ddbab344f4f0a6b7a7bd7209" + integrity sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" + espree "^10.0.1" + globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@9.37.0": + version "9.37.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.37.0.tgz#0cfd5aa763fe5d1ee60bedf84cd14f54bcf9e21b" + integrity sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz#f6a245b42886abf6fc9c7ab7744a932250335ab2" + integrity sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A== + dependencies: + "@eslint/core" "^0.16.0" + levn "^0.4.1" "@firebase/analytics-compat@0.2.6": version "0.2.6" @@ -822,24 +885,52 @@ protobufjs "^7.2.5" yargs "^17.7.2" -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" @@ -870,6 +961,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" @@ -891,7 +987,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -907,6 +1003,234 @@ codejar "^3.5.0" svelte "^3.42.3" +"@octokit/app@^16.0.1": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-16.1.0.tgz#e0d7472fc2e7ae7b0ab3f1e4cca8c2aba5f052ad" + integrity sha512-OdKHnm0CYLk8Setr47CATT4YnRTvWkpTYvE+B/l2B0mjszlfOIit3wqPHVslD2jfc1bD4UbO7Mzh6gjCuMZKsA== + dependencies: + "@octokit/auth-app" "^8.1.0" + "@octokit/auth-unauthenticated" "^7.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-app" "^8.0.1" + "@octokit/plugin-paginate-rest" "^13.0.0" + "@octokit/types" "^14.0.0" + "@octokit/webhooks" "^14.0.0" + +"@octokit/auth-app@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-8.1.0.tgz#19dacfe49ea62a1f40189ac222925e8a757b6a7c" + integrity sha512-6bWhyvLXqCSfHiqlwzn9pScLZ+Qnvh/681GR/UEEPCMIVwfpRDBw0cCzy3/t2Dq8B7W2X/8pBgmw6MOiyE0DXQ== + dependencies: + "@octokit/auth-oauth-app" "^9.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + toad-cache "^3.7.0" + universal-github-app-jwt "^2.2.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-app@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.1.tgz#d8d2e950c95e9fcbe6f2fb98d4539ee8c9871766" + integrity sha512-TthWzYxuHKLAbmxdFZwFlmwVyvynpyPmjwc+2/cI3cvbT7mHtsAW9b1LvQaNnAuWL+pFnqtxdmrU8QpF633i1g== + dependencies: + "@octokit/auth-oauth-device" "^8.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/request" "^10.0.2" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-device@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.1.tgz#232ec13e299dd6bf199fe237527d04ec12decffb" + integrity sha512-TOqId/+am5yk9zor0RGibmlqn4V0h8vzjxlw/wYr3qzkQxl8aBPur384D1EyHtqvfz0syeXji4OUvKkHvxk/Gw== + dependencies: + "@octokit/oauth-methods" "^6.0.0" + "@octokit/request" "^10.0.2" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-user@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.0.tgz#ba643060824536cd848c72d835061b1c00007286" + integrity sha512-GV9IW134PHsLhtUad21WIeP9mlJ+QNpFd6V9vuPWmaiN25HEJeEQUcS4y5oRuqCm9iWDLtfIs+9K8uczBXKr6A== + dependencies: + "@octokit/auth-oauth-device" "^8.0.1" + "@octokit/oauth-methods" "^6.0.0" + "@octokit/request" "^10.0.2" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-token@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-6.0.0.tgz#b02e9c08a2d8937df09a2a981f226ad219174c53" + integrity sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w== + +"@octokit/auth-unauthenticated@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.1.tgz#427b8a52e672318f84757307e766a448c741116b" + integrity sha512-qVq1vdjLLZdE8kH2vDycNNjuJRCD1q2oet1nA/GXWaYlpDxlR7rdVhX/K/oszXslXiQIiqrQf+rdhDlA99JdTQ== + dependencies: + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + +"@octokit/core@^7.0.2": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-7.0.4.tgz#91efe208b52bbb47f997d634cdc95fc839a75e38" + integrity sha512-jOT8V1Ba5BdC79sKrRWDdMT5l1R+XNHTPR6CPWzUP2EcfAcvIHZWF0eAbmRcpOOP5gVIwnqNg0C4nvh6Abc3OA== + dependencies: + "@octokit/auth-token" "^6.0.0" + "@octokit/graphql" "^9.0.1" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^15.0.0" + before-after-hook "^4.0.0" + universal-user-agent "^7.0.0" + +"@octokit/endpoint@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-11.0.0.tgz#189fcc022721b4c49d0307eea6be3de1cfb53026" + integrity sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ== + dependencies: + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.2" + +"@octokit/graphql@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-9.0.1.tgz#eb258fc9981403d2d751720832652c385b6c1613" + integrity sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg== + dependencies: + "@octokit/request" "^10.0.2" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/oauth-app@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-8.0.1.tgz#7cb889945c3ccacfd0ff9aa9f05e9748b439f7f3" + integrity sha512-QnhMYEQpnYbEPn9cae+wXL2LuPMFglmfeuDJXXsyxIXdoORwkLK8y0cHhd/5du9MbO/zdG/BXixzB7EEwU63eQ== + dependencies: + "@octokit/auth-oauth-app" "^9.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/auth-unauthenticated" "^7.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-authorization-url" "^8.0.0" + "@octokit/oauth-methods" "^6.0.0" + "@types/aws-lambda" "^8.10.83" + universal-user-agent "^7.0.0" + +"@octokit/oauth-authorization-url@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz#fdbab39a07d38faaad8621a5fdf04bc0c36d63e7" + integrity sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ== + +"@octokit/oauth-methods@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-6.0.0.tgz#a138bbbec6762b52249f7c47d0c548fc1cf6ad7b" + integrity sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA== + dependencies: + "@octokit/oauth-authorization-url" "^8.0.0" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + +"@octokit/openapi-types@^25.1.0": + version "25.1.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-25.1.0.tgz#5a72a9dfaaba72b5b7db375fd05e90ca90dc9682" + integrity sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA== + +"@octokit/openapi-types@^26.0.0": + version "26.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-26.0.0.tgz#a528b560dbc4f02040dc08d19575498d57fff19d" + integrity sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA== + +"@octokit/openapi-webhooks-types@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.0.3.tgz#fa44fb31fbb4c444c5fd640dbbf537f00c20617c" + integrity sha512-90MF5LVHjBedwoHyJsgmaFhEN1uzXyBDRLEBe7jlTYx/fEhPAk3P3DAJsfZwC54m8hAIryosJOL+UuZHB3K3yA== + +"@octokit/plugin-paginate-graphql@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz#acdefd7e85ce24716e7ad7352f2df4d29d0e273b" + integrity sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ== + +"@octokit/plugin-paginate-rest@^13.0.0": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz#ca5bb1c7b85a583691263c1f788f607e9bcb74b3" + integrity sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw== + dependencies: + "@octokit/types" "^14.1.0" + +"@octokit/plugin-rest-endpoint-methods@^16.0.0": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.1.0.tgz#eb17ce9e37327dcf7b15f4b31244d7f0b58491d1" + integrity sha512-nCsyiKoGRnhH5LkH8hJEZb9swpqOcsW+VXv1QoyUNQXJeVODG4+xM6UICEqyqe9XFr6LkL8BIiFCPev8zMDXPw== + dependencies: + "@octokit/types" "^15.0.0" + +"@octokit/plugin-retry@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz#ee4a0487d31b97ad3deaf737faad68abeca3c227" + integrity sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw== + dependencies: + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + bottleneck "^2.15.3" + +"@octokit/plugin-throttling@^11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz#31a0b5e759f0313514d9522a4103360f17ffc2e4" + integrity sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw== + dependencies: + "@octokit/types" "^14.0.0" + bottleneck "^2.15.3" + +"@octokit/request-error@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-7.0.0.tgz#48ae2cd79008315605d00e83664891a10a5ddb97" + integrity sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg== + dependencies: + "@octokit/types" "^14.0.0" + +"@octokit/request@^10.0.2": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-10.0.3.tgz#2ffdb88105ce20d25dcab8a592a7040ea48306c7" + integrity sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA== + dependencies: + "@octokit/endpoint" "^11.0.0" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + fast-content-type-parse "^3.0.0" + universal-user-agent "^7.0.2" + +"@octokit/types@^14.0.0", "@octokit/types@^14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-14.1.0.tgz#3bf9b3a3e3b5270964a57cc9d98592ed44f840f2" + integrity sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g== + dependencies: + "@octokit/openapi-types" "^25.1.0" + +"@octokit/types@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-15.0.0.tgz#6afe9b012115284ded9bf779e90d04081e0eadd3" + integrity sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ== + dependencies: + "@octokit/openapi-types" "^26.0.0" + +"@octokit/webhooks-methods@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz#34abf78aec6f826fe561cfe79d2ebb1950d1d25f" + integrity sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ== + +"@octokit/webhooks@^14.0.0": + version "14.1.3" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-14.1.3.tgz#2d0bed71b07745c0b33363d69e0ae0e440469a18" + integrity sha512-gcK4FNaROM9NjA0mvyfXl0KPusk7a1BeA8ITlYEZVQCXF5gcETTd4yhAU0Kjzd8mXwYHppzJBWgdBVpIR9wUcQ== + dependencies: + "@octokit/openapi-webhooks-types" "12.0.3" + "@octokit/request-error" "^7.0.0" + "@octokit/webhooks-methods" "^6.0.0" + "@polka/url@^1.0.0-next.20": version "1.0.0-next.25" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" @@ -965,100 +1289,115 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@rollup/rollup-android-arm-eabi@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz#7f4c4d8cd5ccab6e95d6750dbe00321c1f30791e" - integrity sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ== - -"@rollup/rollup-android-arm64@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz#17ea71695fb1518c2c324badbe431a0bd1879f2d" - integrity sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA== - -"@rollup/rollup-darwin-arm64@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz#dac0f0d0cfa73e7d5225ae6d303c13c8979e7999" - integrity sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ== - -"@rollup/rollup-darwin-x64@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz#8f63baa1d31784904a380d2e293fa1ddf53dd4a2" - integrity sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ== - -"@rollup/rollup-freebsd-arm64@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz#30ed247e0df6e8858cdc6ae4090e12dbeb8ce946" - integrity sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA== - -"@rollup/rollup-freebsd-x64@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz#57846f382fddbb508412ae07855b8a04c8f56282" - integrity sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ== - -"@rollup/rollup-linux-arm-gnueabihf@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz#378ca666c9dae5e6f94d1d351e7497c176e9b6df" - integrity sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA== - -"@rollup/rollup-linux-arm-musleabihf@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz#a692eff3bab330d5c33a5d5813a090c15374cddb" - integrity sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg== - -"@rollup/rollup-linux-arm64-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz#6b1719b76088da5ac1ae1feccf48c5926b9e3db9" - integrity sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA== - -"@rollup/rollup-linux-arm64-musl@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz#865baf5b6f5ff67acb32e5a359508828e8dc5788" - integrity sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A== - -"@rollup/rollup-linux-loongarch64-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz#23c6609ba0f7fa7a7f2038b6b6a08555a5055a87" - integrity sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA== - -"@rollup/rollup-linux-powerpc64le-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz#652ef0d9334a9f25b9daf85731242801cb0fc41c" - integrity sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A== - -"@rollup/rollup-linux-riscv64-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz#1eb6651839ee6ebca64d6cc64febbd299e95e6bd" - integrity sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA== - -"@rollup/rollup-linux-s390x-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz#015c52293afb3ff2a293cf0936b1d43975c1e9cd" - integrity sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg== - -"@rollup/rollup-linux-x64-gnu@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz#b83001b5abed2bcb5e2dbeec6a7e69b194235c1e" - integrity sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw== - -"@rollup/rollup-linux-x64-musl@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz#6cc7c84cd4563737f8593e66f33b57d8e228805b" - integrity sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g== - -"@rollup/rollup-win32-arm64-msvc@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz#631ffeee094d71279fcd1fe8072bdcf25311bc11" - integrity sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A== - -"@rollup/rollup-win32-ia32-msvc@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz#06d1d60d5b9f718e8a6c4a43f82e3f9e3254587f" - integrity sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA== - -"@rollup/rollup-win32-x64-msvc@4.28.1": - version "4.28.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz#4dff5c4259ebe6c5b4a8f2c5bc3829b7a8447ff0" - integrity sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA== +"@rollup/rollup-android-arm-eabi@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz#59e7478d310f7e6a7c72453978f562483828112f" + integrity sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA== + +"@rollup/rollup-android-arm64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz#a825192a0b1b2f27a5c950c439e7e37a33c5d056" + integrity sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w== + +"@rollup/rollup-darwin-arm64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz#4ee37078bccd725ae3c5f30ef92efc8e1bf886f3" + integrity sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg== + +"@rollup/rollup-darwin-x64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz#43cc08bd05bf9f388f125e7210a544e62d368d90" + integrity sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw== + +"@rollup/rollup-freebsd-arm64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz#bc8e640e28abe52450baf3fc80d9b26d9bb6587d" + integrity sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ== + +"@rollup/rollup-freebsd-x64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz#e981a22e057cc8c65bb523019d344d3a66b15bbc" + integrity sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw== + +"@rollup/rollup-linux-arm-gnueabihf@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz#4036b68904f392a20f3499d63b33e055b67eb274" + integrity sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ== + +"@rollup/rollup-linux-arm-musleabihf@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz#d3b1b9589606e0ff916801c855b1ace9e733427a" + integrity sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q== + +"@rollup/rollup-linux-arm64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz#cbf0943c477e3b96340136dd3448eaf144378cf2" + integrity sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg== + +"@rollup/rollup-linux-arm64-musl@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz#837f5a428020d5dce1c3b4cc049876075402cf78" + integrity sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g== + +"@rollup/rollup-linux-loong64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz#532c214ababb32ab4bc21b4054278b9a8979e516" + integrity sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ== + +"@rollup/rollup-linux-ppc64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz#93900163b61b49cee666d10ee38257a8b1dd161a" + integrity sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g== + +"@rollup/rollup-linux-riscv64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz#f0ffdcc7066ca04bc972370c74289f35c7a7dc42" + integrity sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg== + +"@rollup/rollup-linux-riscv64-musl@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz#361695c39dbe96773509745d77a870a32a9f8e48" + integrity sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA== + +"@rollup/rollup-linux-s390x-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz#09fc6cc2e266a2324e366486ae5d1bca48c43a6a" + integrity sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA== + +"@rollup/rollup-linux-x64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz#aa9d5b307c08f05d3454225bb0a2b4cc87eeb2e1" + integrity sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg== + +"@rollup/rollup-linux-x64-musl@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz#26949e5b4645502a61daba2f7a8416bd17cb5382" + integrity sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw== + +"@rollup/rollup-openharmony-arm64@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz#ef493c072f9dac7e0edb6c72d63366846b6ffcd9" + integrity sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA== + +"@rollup/rollup-win32-arm64-msvc@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz#56e1aaa6a630d2202ee7ec0adddd05cf384ffd44" + integrity sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ== + +"@rollup/rollup-win32-ia32-msvc@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz#0a44bbf933a9651c7da2b8569fa448dec0de7480" + integrity sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw== + +"@rollup/rollup-win32-x64-gnu@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz#730e12f0b60b234a7c02d5d3179ca3ec7972033d" + integrity sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ== + +"@rollup/rollup-win32-x64-msvc@4.52.4": + version "4.52.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz#5b2dd648a960b8fa00d76f2cc4eea2f03daa80f4" + integrity sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w== "@sindresorhus/is@^0.7.0": version "0.7.0" @@ -1077,6 +1416,11 @@ dependencies: defer-to-connect "^2.0.0" +"@types/aws-lambda@^8.10.83": + version "8.10.152" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.152.tgz#f68424a8175f0a54a2a941e65b76c3f51f3bd89d" + integrity sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw== + "@types/cacheable-request@^6.0.1": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" @@ -1087,6 +1431,13 @@ "@types/node" "*" "@types/responselike" "^1.0.0" +"@types/chai@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.2.tgz#6f14cea18180ffc4416bc0fd12be05fdd73bdd6b" + integrity sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg== + dependencies: + "@types/deep-eql" "*" + "@types/decompress@*": version "4.2.7" resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.7.tgz#604f69b69d519ecb74dea1ea0829f159b85e1332" @@ -1094,6 +1445,11 @@ dependencies: "@types/node" "*" +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + "@types/dompurify@^3.0.5": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7" @@ -1123,7 +1479,12 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.6": +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -1154,7 +1515,7 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== -"@types/json-schema@*", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -1244,6 +1605,21 @@ dependencies: "@types/node" "*" +"@typescript-eslint/eslint-plugin@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz#9f251d4e85ec5089e7cccb09257ce93dbf0d7744" + integrity sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.45.0" + "@typescript-eslint/type-utils" "8.45.0" + "@typescript-eslint/utils" "8.45.0" + "@typescript-eslint/visitor-keys" "8.45.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + "@typescript-eslint/eslint-plugin@^5.54.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -1260,6 +1636,17 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/parser@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.45.0.tgz#571660c98824aefb4a6ec3b3766655d1348520a4" + integrity sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ== + dependencies: + "@typescript-eslint/scope-manager" "8.45.0" + "@typescript-eslint/types" "8.45.0" + "@typescript-eslint/typescript-estree" "8.45.0" + "@typescript-eslint/visitor-keys" "8.45.0" + debug "^4.3.4" + "@typescript-eslint/parser@^5.54.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" @@ -1270,6 +1657,15 @@ "@typescript-eslint/typescript-estree" "5.62.0" debug "^4.3.4" +"@typescript-eslint/project-service@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.45.0.tgz#f83dda1bca31dae2fd6821f9131daf1edebfd46c" + integrity sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.45.0" + "@typescript-eslint/types" "^8.45.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" @@ -1278,6 +1674,19 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" +"@typescript-eslint/scope-manager@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz#59615ba506a9e3479d1efb0d09d6ab52f2a19142" + integrity sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA== + dependencies: + "@typescript-eslint/types" "8.45.0" + "@typescript-eslint/visitor-keys" "8.45.0" + +"@typescript-eslint/tsconfig-utils@8.45.0", "@typescript-eslint/tsconfig-utils@^8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz#63d38282790a2566c571bad423e7c1cad1f3d64c" + integrity sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w== + "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -1288,11 +1697,27 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz#04004bdf2598844faa29fb936fb6b0ee10d6d3f3" + integrity sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A== + dependencies: + "@typescript-eslint/types" "8.45.0" + "@typescript-eslint/typescript-estree" "8.45.0" + "@typescript-eslint/utils" "8.45.0" + debug "^4.3.4" + ts-api-utils "^2.1.0" + "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== +"@typescript-eslint/types@8.45.0", "@typescript-eslint/types@^8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.45.0.tgz#fc01cd2a4690b9713b02f895e82fb43f7d960684" + integrity sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA== + "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -1306,6 +1731,22 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz#3498500f109a89b104d2770497c707e56dfe062d" + integrity sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA== + dependencies: + "@typescript-eslint/project-service" "8.45.0" + "@typescript-eslint/tsconfig-utils" "8.45.0" + "@typescript-eslint/types" "8.45.0" + "@typescript-eslint/visitor-keys" "8.45.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + "@typescript-eslint/utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -1320,6 +1761,16 @@ eslint-scope "^5.1.1" semver "^7.3.7" +"@typescript-eslint/utils@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.45.0.tgz#6e68e92d99019fdf56018d0e6664c76a70470c95" + integrity sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.45.0" + "@typescript-eslint/types" "8.45.0" + "@typescript-eslint/typescript-estree" "8.45.0" + "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -1328,69 +1779,74 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@typescript-eslint/visitor-keys@8.45.0": + version "8.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz#4e3bcc55da64ac61069ebfe62ca240567ac7d784" + integrity sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag== + dependencies: + "@typescript-eslint/types" "8.45.0" + eslint-visitor-keys "^4.2.1" -"@vitest/expect@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1" - integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw== +"@vitest/expect@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" + integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== dependencies: - "@vitest/spy" "2.1.8" - "@vitest/utils" "2.1.8" - chai "^5.1.2" - tinyrainbow "^1.2.0" + "@types/chai" "^5.2.2" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" -"@vitest/mocker@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73" - integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA== +"@vitest/mocker@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3" + integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== dependencies: - "@vitest/spy" "2.1.8" + "@vitest/spy" "3.2.4" estree-walker "^3.0.3" - magic-string "^0.30.12" + magic-string "^0.30.17" -"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca" - integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ== +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" + integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== dependencies: - tinyrainbow "^1.2.0" + tinyrainbow "^2.0.0" -"@vitest/runner@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f" - integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg== +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== dependencies: - "@vitest/utils" "2.1.8" - pathe "^1.1.2" + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" -"@vitest/snapshot@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de" - integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg== +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== dependencies: - "@vitest/pretty-format" "2.1.8" - magic-string "^0.30.12" - pathe "^1.1.2" + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" -"@vitest/spy@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447" - integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg== +"@vitest/spy@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599" + integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== dependencies: - tinyspy "^3.0.2" + tinyspy "^4.0.3" -"@vitest/utils@2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388" - integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA== +"@vitest/utils@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea" + integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== dependencies: - "@vitest/pretty-format" "2.1.8" - loupe "^3.1.2" - tinyrainbow "^1.2.0" + "@vitest/pretty-format" "3.2.4" + loupe "^3.1.4" + tinyrainbow "^2.0.0" "@vue/compiler-core@3.4.27": version "3.4.27" @@ -1481,6 +1937,11 @@ acorn@^8.10.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" @@ -1501,6 +1962,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -1508,6 +1974,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + archive-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" @@ -1555,6 +2026,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +before-after-hook@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-4.0.0.tgz#cf1447ab9160df6a40f3621da64d6ffc36050cb9" + integrity sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ== + bl@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" @@ -1586,6 +2062,11 @@ boolean@^3.0.1: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== +bottleneck@^2.15.3: + version "2.19.5" + resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" + integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1594,6 +2075,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + brace@^0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" @@ -1606,6 +2094,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -1678,10 +2173,10 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -chai@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" - integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== +chai@^5.2.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06" + integrity sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw== dependencies: assertion-error "^2.0.1" check-error "^2.1.1" @@ -1811,10 +2306,10 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1833,22 +2328,27 @@ css-tree@^2.3.1: mdn-data "2.0.30" source-map-js "^1.0.1" +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + csstype@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^4.3.7: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== +debug@^4.3.1, debug@^4.3.2, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -1988,13 +2488,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dompurify@^3.0.1: version "3.1.3" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.3.tgz#cfe3ce4232c216d923832f68f2aa18b2fb9bd223" @@ -2022,6 +2515,11 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + electron@^30.0.2: version "30.0.6" resolved "https://registry.yarnpkg.com/electron/-/electron-30.0.6.tgz#9ddea5f68396ecca88ad7c2c466a30fc9c16144b" @@ -2036,6 +2534,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -2065,10 +2568,10 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" - integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== es6-error@^4.1.1: version "4.1.1" @@ -2133,34 +2636,37 @@ esbuild@^0.17.10: "@esbuild/win32-ia32" "0.17.19" "@esbuild/win32-x64" "0.17.19" -esbuild@^0.21.3: - version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" - integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== +esbuild@^0.25.0: + version "0.25.10" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.10.tgz#37f5aa5cd14500f141be121c01b096ca83ac34a9" + integrity sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ== optionalDependencies: - "@esbuild/aix-ppc64" "0.21.5" - "@esbuild/android-arm" "0.21.5" - "@esbuild/android-arm64" "0.21.5" - "@esbuild/android-x64" "0.21.5" - "@esbuild/darwin-arm64" "0.21.5" - "@esbuild/darwin-x64" "0.21.5" - "@esbuild/freebsd-arm64" "0.21.5" - "@esbuild/freebsd-x64" "0.21.5" - "@esbuild/linux-arm" "0.21.5" - "@esbuild/linux-arm64" "0.21.5" - "@esbuild/linux-ia32" "0.21.5" - "@esbuild/linux-loong64" "0.21.5" - "@esbuild/linux-mips64el" "0.21.5" - "@esbuild/linux-ppc64" "0.21.5" - "@esbuild/linux-riscv64" "0.21.5" - "@esbuild/linux-s390x" "0.21.5" - "@esbuild/linux-x64" "0.21.5" - "@esbuild/netbsd-x64" "0.21.5" - "@esbuild/openbsd-x64" "0.21.5" - "@esbuild/sunos-x64" "0.21.5" - "@esbuild/win32-arm64" "0.21.5" - "@esbuild/win32-ia32" "0.21.5" - "@esbuild/win32-x64" "0.21.5" + "@esbuild/aix-ppc64" "0.25.10" + "@esbuild/android-arm" "0.25.10" + "@esbuild/android-arm64" "0.25.10" + "@esbuild/android-x64" "0.25.10" + "@esbuild/darwin-arm64" "0.25.10" + "@esbuild/darwin-x64" "0.25.10" + "@esbuild/freebsd-arm64" "0.25.10" + "@esbuild/freebsd-x64" "0.25.10" + "@esbuild/linux-arm" "0.25.10" + "@esbuild/linux-arm64" "0.25.10" + "@esbuild/linux-ia32" "0.25.10" + "@esbuild/linux-loong64" "0.25.10" + "@esbuild/linux-mips64el" "0.25.10" + "@esbuild/linux-ppc64" "0.25.10" + "@esbuild/linux-riscv64" "0.25.10" + "@esbuild/linux-s390x" "0.25.10" + "@esbuild/linux-x64" "0.25.10" + "@esbuild/netbsd-arm64" "0.25.10" + "@esbuild/netbsd-x64" "0.25.10" + "@esbuild/openbsd-arm64" "0.25.10" + "@esbuild/openbsd-x64" "0.25.10" + "@esbuild/openharmony-arm64" "0.25.10" + "@esbuild/sunos-x64" "0.25.10" + "@esbuild/win32-arm64" "0.25.10" + "@esbuild/win32-ia32" "0.25.10" + "@esbuild/win32-x64" "0.25.10" esbuild@latest: version "0.21.3" @@ -2206,6 +2712,22 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +eslint-plugin-svelte@^3.12.4: + version "3.12.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-svelte/-/eslint-plugin-svelte-3.12.4.tgz#a6b540b4d5e9045303e3d38eb5f4171472dae828" + integrity sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw== + dependencies: + "@eslint-community/eslint-utils" "^4.6.1" + "@jridgewell/sourcemap-codec" "^1.5.0" + esutils "^2.0.3" + globals "^16.0.0" + known-css-properties "^0.37.0" + postcss "^8.4.49" + postcss-load-config "^3.1.4" + postcss-safe-parser "^7.0.0" + semver "^7.6.3" + svelte-eslint-parser "^1.3.0" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -2214,76 +2736,78 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.2.0, eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.35.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" +eslint-visitor-keys@^4.0.0, eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + +eslint@^9.37.0: + version "9.37.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.37.0.tgz#ac0222127f76b09c0db63036f4fe289562072d74" + integrity sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.0" + "@eslint/config-helpers" "^0.4.0" + "@eslint/core" "^0.16.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.37.0" + "@eslint/plugin-kit" "^0.4.0" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.6" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== +espree@^10.0.0, espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== dependencies: - acorn "^8.9.0" + acorn "^8.15.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" + eslint-visitor-keys "^4.2.1" -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -2316,15 +2840,15 @@ estree-walker@^3.0.0, estree-walker@^3.0.3: dependencies: "@types/estree" "^1.0.0" -esutils@^2.0.2: +esutils@^2.0.2, esutils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -expect-type@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" - integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== +expect-type@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3" + integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA== ext-list@^2.0.0: version "2.2.2" @@ -2352,6 +2876,11 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" +fast-content-type-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz#5590b6c807cc598be125e6740a9fde589d2b7afb" + integrity sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2368,6 +2897,17 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -2399,17 +2939,22 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + fflate@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - flat-cache "^3.0.4" + flat-cache "^4.0.0" file-type@^11.1.0: version "11.1.0" @@ -2457,6 +3002,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -2514,19 +3066,26 @@ firebase@^9.19.0: "@firebase/storage-compat" "0.3.2" "@firebase/util" "1.9.3" -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +foreground-child@^3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" form-data@^2.5.0: version "2.5.2" @@ -2652,6 +3211,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@^11.0.1: + version "11.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== + dependencies: + foreground-child "^3.3.1" + jackspeak "^4.1.1" + minimatch "^10.0.3" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2676,12 +3247,15 @@ global-agent@^3.0.0: semver "^7.3.2" serialize-error "^7.0.1" -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^16.0.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" + integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== globalthis@^1.0.1: version "1.0.4" @@ -2844,10 +3418,15 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +ignore@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -2915,11 +3494,6 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -2960,6 +3534,23 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" +jackspeak@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" + integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + +jiti@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== + +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -3006,7 +3597,7 @@ keyv@3.0.0: dependencies: json-buffer "3.0.0" -keyv@^4.0.0, keyv@^4.5.3: +keyv@^4.0.0, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -3018,6 +3609,11 @@ kleur@^3.0.0: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +known-css-properties@^0.37.0: + version "0.37.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.37.0.tgz#10ebe49b9dbb6638860ff8a002fb65a053f4aec5" + integrity sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -3026,6 +3622,11 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lilconfig@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + local-access@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/local-access/-/local-access-1.1.0.tgz#e007c76ba2ca83d5877ba1a125fc8dfe23ba4798" @@ -3070,11 +3671,16 @@ long@^5.0.0: resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== -loupe@^3.1.0, loupe@^3.1.2: +loupe@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== +loupe@^3.1.4: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.1.tgz#0095cf56dc5b7a9a7c08ff5b1a8796ec8ad17e76" + integrity sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ== + lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -3090,6 +3696,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^11.0.0: + version "11.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" + integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== + magic-string@^0.30.10, magic-string@^0.30.5: version "0.30.10" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" @@ -3097,12 +3708,12 @@ magic-string@^0.30.10, magic-string@^0.30.5: dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" -magic-string@^0.30.12: - version "0.30.15" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.15.tgz#d5474a2c4c5f35f041349edaba8a5cb02733ed3c" - integrity sha512-zXeaYRgZ6ldS1RJJUrMrYgNJ4fdwnyI6tVqoiIhyCyv5IVTK9BU8Ic2l253GGETQHxI4HNUwhJ3fjDhKqEoaAw== +magic-string@^0.30.17: + version "0.30.19" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.19.tgz#cebe9f104e565602e5d2098c5f2e79a77cc86da9" + integrity sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw== dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/sourcemap-codec" "^1.5.5" magic-string@^0.30.4: version "0.30.17" @@ -3152,10 +3763,10 @@ matcher@^3.0.0: dependencies: escape-string-regexp "^4.0.0" -mc-build@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/mc-build/-/mc-build-3.5.1.tgz#684bf069701d62b85c8d698be6679bb92f5d4044" - integrity sha512-bySCg9VA+V0++SqfJrBcPxU9t8UrifcecGpXAR/21kqV4uWItoo12MqBSN3bI6f51j3vbVIrdOAH+PiWBp5S7w== +mc-build@^3.6.7: + version "3.6.7" + resolved "https://registry.yarnpkg.com/mc-build/-/mc-build-3.6.7.tgz#f22bd9e7eaf0e565ad5bb04f5b5eb631b3c6ecb4" + integrity sha512-Cd0g/20e3h5/atUzj80689TjOkE7nZuMZg4YPwPzQ8oHRrKnlxnxDwumUfzEwFW6xw8Z52rfC06N8In7Xg3ang== md5@^2.3.0: version "2.3.0" @@ -3184,6 +3795,14 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -3216,18 +3835,37 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + +minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -3260,6 +3898,11 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -3282,6 +3925,14 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +node-modules-vscode-problems-patch@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/node-modules-vscode-problems-patch/-/node-modules-vscode-problems-patch-1.0.9.tgz#8b099c72bb051c056d0a021ed506e8ce47c1e618" + integrity sha512-PYS8ozhWFbPvksJBIKbWrdrKOD2e+MZaxl75amn/oaVAzObgmGQeYnk4VT5tT/d5KHhWiuUwzWWSgbQ57GVMfA== + dependencies: + esbuild "^0.25.0" + glob "^11.0.1" + normalize-url@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" @@ -3306,6 +3957,23 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +octokit@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-5.0.3.tgz#1e4f110e28218ab9676c28da5f28ab403fe5b643" + integrity sha512-+bwYsAIRmYv30NTmBysPIlgH23ekVDriB07oRxlPIAH5PI0yTMSxg5i5Xy0OetcnZw+nk/caD4szD7a9YZ3QyQ== + dependencies: + "@octokit/app" "^16.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-app" "^8.0.1" + "@octokit/plugin-paginate-graphql" "^6.0.0" + "@octokit/plugin-paginate-rest" "^13.0.0" + "@octokit/plugin-rest-endpoint-methods" "^16.0.0" + "@octokit/plugin-retry" "^8.0.1" + "@octokit/plugin-throttling" "^11.0.1" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^14.0.0" + "@octokit/webhooks" "^14.0.0" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3392,6 +4060,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + pako@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" @@ -3419,15 +4092,23 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pathe@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" - integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== pathval@^2.0.0: version "2.0.0" @@ -3463,6 +4144,11 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3497,6 +4183,32 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== + dependencies: + lilconfig "^2.0.5" + yaml "^1.10.2" + +postcss-safe-parser@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz#36e4f7e608111a0ca940fd9712ce034718c40ec0" + integrity sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A== + +postcss-scss@^4.0.9: + version "4.0.9" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.9.tgz#a03c773cd4c9623cb04ce142a52afcec74806685" + integrity sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A== + +postcss-selector-parser@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz#4d6af97eba65d73bc4d84bcb343e865d7dd16262" + integrity sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss@^8.4.38: version "8.4.38" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" @@ -3506,12 +4218,12 @@ postcss@^8.4.38: picocolors "^1.0.0" source-map-js "^1.2.0" -postcss@^8.4.43: - version "8.4.49" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" - integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== +postcss@^8.4.49, postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== dependencies: - nanoid "^3.3.7" + nanoid "^3.3.11" picocolors "^1.1.1" source-map-js "^1.2.1" @@ -3675,13 +4387,6 @@ rimraf@^2.5.2: dependencies: glob "^7.1.3" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - roarr@^2.15.3: version "2.15.4" resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" @@ -3694,32 +4399,35 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" -rollup@^4.20.0: - version "4.28.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.28.1.tgz#7718ba34d62b449dfc49adbfd2f312b4fe0df4de" - integrity sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg== +rollup@^4.43.0: + version "4.52.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.52.4.tgz#71e64cce96a865fcbaa6bb62c6e82807f4e378a1" + integrity sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ== dependencies: - "@types/estree" "1.0.6" + "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.28.1" - "@rollup/rollup-android-arm64" "4.28.1" - "@rollup/rollup-darwin-arm64" "4.28.1" - "@rollup/rollup-darwin-x64" "4.28.1" - "@rollup/rollup-freebsd-arm64" "4.28.1" - "@rollup/rollup-freebsd-x64" "4.28.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.28.1" - "@rollup/rollup-linux-arm-musleabihf" "4.28.1" - "@rollup/rollup-linux-arm64-gnu" "4.28.1" - "@rollup/rollup-linux-arm64-musl" "4.28.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.28.1" - "@rollup/rollup-linux-powerpc64le-gnu" "4.28.1" - "@rollup/rollup-linux-riscv64-gnu" "4.28.1" - "@rollup/rollup-linux-s390x-gnu" "4.28.1" - "@rollup/rollup-linux-x64-gnu" "4.28.1" - "@rollup/rollup-linux-x64-musl" "4.28.1" - "@rollup/rollup-win32-arm64-msvc" "4.28.1" - "@rollup/rollup-win32-ia32-msvc" "4.28.1" - "@rollup/rollup-win32-x64-msvc" "4.28.1" + "@rollup/rollup-android-arm-eabi" "4.52.4" + "@rollup/rollup-android-arm64" "4.52.4" + "@rollup/rollup-darwin-arm64" "4.52.4" + "@rollup/rollup-darwin-x64" "4.52.4" + "@rollup/rollup-freebsd-arm64" "4.52.4" + "@rollup/rollup-freebsd-x64" "4.52.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.52.4" + "@rollup/rollup-linux-arm-musleabihf" "4.52.4" + "@rollup/rollup-linux-arm64-gnu" "4.52.4" + "@rollup/rollup-linux-arm64-musl" "4.52.4" + "@rollup/rollup-linux-loong64-gnu" "4.52.4" + "@rollup/rollup-linux-ppc64-gnu" "4.52.4" + "@rollup/rollup-linux-riscv64-gnu" "4.52.4" + "@rollup/rollup-linux-riscv64-musl" "4.52.4" + "@rollup/rollup-linux-s390x-gnu" "4.52.4" + "@rollup/rollup-linux-x64-gnu" "4.52.4" + "@rollup/rollup-linux-x64-musl" "4.52.4" + "@rollup/rollup-openharmony-arm64" "4.52.4" + "@rollup/rollup-win32-arm64-msvc" "4.52.4" + "@rollup/rollup-win32-ia32-msvc" "4.52.4" + "@rollup/rollup-win32-x64-gnu" "4.52.4" + "@rollup/rollup-win32-x64-msvc" "4.52.4" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -3788,6 +4496,11 @@ semver@^7.3.2, semver@^7.3.7: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +semver@^7.6.0, semver@^7.6.3: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -3812,6 +4525,11 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sirv-cli@^1.0.0: version "1.0.14" resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-1.0.14.tgz#4bc60421b3de9caea80ccd292b5004aca4ce3c81" @@ -3891,16 +4609,25 @@ stackback@0.0.2: resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== -std-env@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" - integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== +std-env@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" + integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -3910,6 +4637,15 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -3917,6 +4653,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3924,6 +4667,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" @@ -3943,6 +4693,13 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-literal@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032" + integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + dependencies: + js-tokens "^9.0.1" + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -3990,6 +4747,18 @@ svelte-dnd-action@^0.9.38: resolved "https://registry.yarnpkg.com/svelte-dnd-action/-/svelte-dnd-action-0.9.47.tgz#f275b042aa23ea82054f2d797c8fd1b2a15f8821" integrity sha512-Z343HCJfrGHgK98LCrp0ziGUwwWOd8UrpXk7b7A61OQErrB8nUS2tLVRZlaGzhMfE1UCivuqTGFLqxJ14KehfQ== +svelte-eslint-parser@^1.3.0, svelte-eslint-parser@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/svelte-eslint-parser/-/svelte-eslint-parser-1.3.3.tgz#6ca9010e35ba2f870acbbe9da9396d5767a468d3" + integrity sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q== + dependencies: + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.0.0" + espree "^10.0.0" + postcss "^8.4.49" + postcss-scss "^4.0.9" + postcss-selector-parser "^7.0.0" + svelte-multiselect@^11.0.0-rc.1: version "11.0.0-rc.1" resolved "https://registry.yarnpkg.com/svelte-multiselect/-/svelte-multiselect-11.0.0-rc.1.tgz#1238a6b768902afbdde23165e6dc922555f27342" @@ -4051,11 +4820,6 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - three@^0.134.0: version "0.134.0" resolved "https://registry.yarnpkg.com/three/-/three-0.134.0.tgz#d7ad4d85d050da0861bf39749b06ddfb5f17157f" @@ -4091,25 +4855,33 @@ tinydate@^1.0.0: resolved "https://registry.yarnpkg.com/tinydate/-/tinydate-1.3.0.tgz#e6ca8e5a22b51bb4ea1c3a2a4fd1352dbd4c57fb" integrity sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w== -tinyexec@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98" - integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== -tinypool@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" - integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== +tinyglobby@^0.2.14, tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" -tinyrainbow@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" - integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== -tinyspy@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" - integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.4.tgz#d77a002fb53a88aa1429b419c1c92492e0c81f78" + integrity sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q== to-buffer@^1.1.1: version "1.1.1" @@ -4128,6 +4900,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toad-cache@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/toad-cache/-/toad-cache-3.7.0.tgz#b9b63304ea7c45ec34d91f1d2fa513517025c441" + integrity sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw== + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" @@ -4145,6 +4922,11 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -4174,10 +4956,15 @@ type-fest@^0.13.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +typescript-eslint@^8.45.0: + version "8.45.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.45.0.tgz#98ab164234dc04c112747ec0a4ae29a94efe123b" + integrity sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg== + dependencies: + "@typescript-eslint/eslint-plugin" "8.45.0" + "@typescript-eslint/parser" "8.45.0" + "@typescript-eslint/typescript-estree" "8.45.0" + "@typescript-eslint/utils" "8.45.0" typescript@^4.5.5, typescript@^4.9.5: version "4.9.5" @@ -4197,6 +4984,16 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +universal-github-app-jwt@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz#38537e5a7d154085a35f97601a5e30e9e17717df" + integrity sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw== + +universal-user-agent@^7.0.0, universal-user-agent@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz#c05870a58125a2dc00431f2df815a77fe69736be" + integrity sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -4221,57 +5018,63 @@ url-to-options@^1.0.1: resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== -util-deprecate@~1.0.1: +util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -vite-node@2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5" - integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg== +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== dependencies: cac "^6.7.14" - debug "^4.3.7" - es-module-lexer "^1.5.4" - pathe "^1.1.2" - vite "^5.0.0" - -vite@^5.0.0: - version "5.4.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" - integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== - dependencies: - esbuild "^0.21.3" - postcss "^8.4.43" - rollup "^4.20.0" + debug "^4.4.1" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": + version "7.1.9" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.9.tgz#ba844410e5d0c0f2a4eaf17a52af60ebea322cbf" + integrity sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg== + dependencies: + esbuild "^0.25.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" optionalDependencies: fsevents "~2.3.3" -vitest@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa" - integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ== - dependencies: - "@vitest/expect" "2.1.8" - "@vitest/mocker" "2.1.8" - "@vitest/pretty-format" "^2.1.8" - "@vitest/runner" "2.1.8" - "@vitest/snapshot" "2.1.8" - "@vitest/spy" "2.1.8" - "@vitest/utils" "2.1.8" - chai "^5.1.2" - debug "^4.3.7" - expect-type "^1.1.0" - magic-string "^0.30.12" - pathe "^1.1.2" - std-env "^3.8.0" +vitest@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/expect" "3.2.4" + "@vitest/mocker" "3.2.4" + "@vitest/pretty-format" "^3.2.4" + "@vitest/runner" "3.2.4" + "@vitest/snapshot" "3.2.4" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + debug "^4.4.1" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + picomatch "^4.0.2" + std-env "^3.9.0" tinybench "^2.9.0" - tinyexec "^0.3.1" - tinypool "^1.0.1" - tinyrainbow "^1.2.0" - vite "^5.0.0" - vite-node "2.1.8" + tinyexec "^0.3.2" + tinyglobby "^0.2.14" + tinypool "^1.1.1" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node "3.2.4" why-is-node-running "^2.3.0" vue@^3.2.45: @@ -4341,6 +5144,15 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -4350,6 +5162,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -4365,6 +5186,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"