diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 13d38b84..370ab932 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,13 +1,13 @@
# These are supported funding model platforms
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
+# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+# patreon: # Replace with a single Patreon username
+# open_collective: # Replace with a single Open Collective username
ko_fi: snavesutit
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+# liberapay: # Replace with a single Liberapay username
+# issuehunt: # Replace with a single IssueHunt username
+# otechie: # Replace with a single Otechie username
+# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/TODO.md b/TODO.md
index 06f09465..0f231bce 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,146 +1,19 @@
# Blockbench
-- [x] ~~Add a root NBT blueprint setting~~ Add a custom summon commands setting.
-- [x] Custom animation properties dialog
-- [x] Prevent the user setting their export namespace to 'global' or 'minecraft'
-- [x] Add affected bones list to variant, and animation properties.
- - [x] Variant Properties
- - [x] Animation Properties
-- [x] Implement .ajmodel to blueprint conversion.
-- [x] Change .ajmeta to use relative file paths.
-- [x] Resolve env variables in blueprint settings.
-- [x] Add renderbox options to the blueprint settings.
-- [x] Render and handle invalid cubes with a red outline.
-- [x] Force animations to be at least 1 tick long
-- [x] Respect Variant inheritance when applying variants in the animation preview.
-- [x] Add a transparency option to the variant texture map selection. (And don't export completely transparent bones)
-- [x] Locators
- - [x] Custom command keyframes.
- - [x] Locator config.
- - [x] Data Pack Compiler support.
-- [x] Natively support step keyframes.
-- [x] When upgrading old ajmodels, if they have command keyframes in the effect animator, create a locator at 0 0 0 with those keyframes.
-- [x] Add support for Text Displays
- - [x] Basic rendering
- - [x] Word wrapping - Thanks Fetchbot!
- - [x] Italic
- - [x] Bold
- - [x] Underline
- - [x] Strikethrough
- - [x] Array style inheritance
- - [x] Support vanilla fonts
- - [x] minecraft:default
- - [x] minecraft:alt
- - [x] minecraft:illageralt
- - [x] User interface
- - [x] Figure out a nice way to configure text displays...
- - [x] Include an option to change the text.
- - [x] Include an option to change the max line width.
- - [x] Animation
- - [x] Make sure the text display is animatable.
- - [x] Add a TextDisplay config.
- - [x] Add support for billboarding to TextDisplays.
- - [ ] Add an option to change the alignment of the text.
-- [x] Respect inheritance in the bone config.
-- [x] Change font rendering to use a geometry for each character instead of a single plane for the entire text display. This will open the possibility of loading custom fonts and spacing.
-- [x] Add vanilla block displays
- - [x] Create a custom element type for block displays.
- - [x] Add rendering for vanilla block models.
- - [x] Use Blockstates to select models.
- - [x] Parent model inheritance
- - [x] block/block
- - [x] Add overrides for entity-based block models.
- - [x] chest
- - [x] ender_chest
- - [x] mob heads
- - [x] shulker boxes
- - [x] beds
- - [x] multi-parts like walls throw an intneral error if they don't have any elements.
-- [x] Add an option to Locators to use the old entity-based functionality.
-- [x] Add an about page.
-- [x] Camera Plugin Support
- - [x] Data Pack Compiler support.
-- [x] Add vanilla item displays
- - [x] Create a custom element type for item displays.
- - [x] Add rendering for vanilla item models.
- - [x] Parent model inheritance
- - [x] item/generated
- - [x] item/handheld
- - [x] item/handheld_rod
- - [x] item/handheld_mace
- - [ ] Add overrides for entity-based models.
- - [x] conduit
- - [x] decorated_pot
- - [x] template_banner
- - [x] template_shulker_box
- - [x] template_skull
- - [x] banners
- - [x] shield
- - [x] trident
-- [ ] Change the Collection setting type to allow single-click swapping of items between lists.
-- [ ] Look into adding a color picker for tintable vanilla items.
-- [ ] Add Variants to the UndoSystem (Blocked by vanilla Blockbench not supporting custom undo actions).
-- [ ] Remove `easingArgs` and `easingMode` from saved keyframes if `easingType` is `linear`.
-- [ ] Add an option to generate a `damage_flash` variant for mob-type entities.
-- [ ] Add a fix for 360 rotation snap by using `set_frame` instead of `apply_frame` for the first frame of the animation.
+- [ ] Add a view_range option to DisplayEntityConfig, and a default view_range in the Rig page of Blueprint Settings
+- [ ] Look into adding a color picker for tintable vanilla items.
+- [ ] Add Variants to the UndoSystem (Blocked by vanilla Blockbench not supporting custom undo actions).
+- [ ] Add an option to generate a `damage_flash` variant for mob-type entities.
+- [ ] Add a fix for 360 rotation snap by using `set_frame` instead of `apply_frame` for the first frame of the animation.
# Data Pack Compiler
-- [x] Merge on_tick and on_load function tags
-- [x] When merging the new minecraft:tick tag with old one, try and find any old style function references (AKA animated_java:my_project/zzzzzz/tick), and remove them.
-- [x] Animation Tweening
-- [x] Implement animation loop mode tech.
-- [x] Write files after compilaion is done by using a queue system.
-- [x] Make data pack compiler as async as possible.
-- [x] Actually respect variant config options.
-- [x] Warn the user when a previously summoned rig needs to be re-summoned due to changes in the blueprint.
-- [x] Figure out how to add repeating functionality to command keyframes.
-- [x] Add toggles to command keyframes to allow continuously running the commands in the keyframe instead of only once when the keyframe is reached.
-- [x] Teleport the rig to the execution location of the summon command.
-- [x] Rotate the bones with the root entity.
-- [x] Add default saved Locator positions to the summoned rig.
-- [x] Add support for text displays.
-- [x] Add support for vanilla item displays.
-- [x] Add support for vanilla block displays.
-- [x] Locator rotation inheritance support - looks like they've supported it all this time...
-- [x] Apply variant keyframes in animations.
-- [x] Figure out how cameras will work.
-- [x] See how much swapping to a static list of UUIDs for selecting bones effects performance.
-- [x] Split up animation storage data command to avoid command length limit.
-- [x] Check for references to non-existant functions in merged function tags, and remove them.
-- [ ] When applying variants, remove / replace any bones that have / had no elements with textured faces.
+- [ ] When applying variants, remove / replace any bones that have / had no elements with textured faces.
-- [x] Add a toast notification for when the model has invalid rotations.
-# Resource Pack
-
-- [x] Warn the user when they have custom elements in their model, but have disabled the resource pack export.
-
-# Plugin Exporter
-
-- [x] Add an option to export a JSON file.
-
-# List of numbers to track
-
-- [ ] Total exports
- - Stored in amount per day
-- [ ] Total functions created by the data pack compiler
- - Stored in amount per day
-
-# Github
-
-- [x] Reorganize the repo's branches.
- - [x] Create a `release` branch.
- - [x] Create a `dev` branch.
- - [x] Create a `legacy-beta` tag.
- - [x] Create a `legacy-armorstands` tag.
-- [ ] Reorganize the repo's tags.
- - [ ] Create a `v1.0.0` tag.
- - [x] Create a `legacy-beta` tag.
- - [x] Create a `legacy-armorstands` tag.
+- [x] Add a toast notification for when the model has invalid rotations.
# Post 1.0.0
-- [ ] Add support for [block-display.com's API](https://wiki.block-display.com/api/get-api)
-- [ ] Add support for custom fonts in TextDisplays.
-- [ ] Add support and previewing for interaction entity based locators.
-- [ ] Add support for previewing player skins on heads.
+- [ ] Add support for [block-display.com's API](https://wiki.block-display.com/api/get-api)
+- [ ] Add support for custom fonts in TextDisplays.
+- [ ] Add support for previewing player skins on heads.
diff --git a/package.json b/package.json
index 7b084fc2..1694a8ab 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"title": "Animated Java",
"icon": "icon.svg",
"description": "Effortlessly craft complex animations for Minecraft: Java Edition",
- "version": "1.10.0-beta.7",
+ "version": "1.10.0",
"min_blockbench_version": "5.1.4",
"variant": "desktop",
"tags": [
diff --git a/src/assets/vanillaAssetOverrides/minecraft/models/item/template_banner.json b/src/assets/vanillaAssetOverrides/minecraft/models/item/template_banner.json
index 04d180ed..657b3caf 100644
--- a/src/assets/vanillaAssetOverrides/minecraft/models/item/template_banner.json
+++ b/src/assets/vanillaAssetOverrides/minecraft/models/item/template_banner.json
@@ -2,8 +2,8 @@
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
- "0": "entity/banner_base",
- "tint": "entity/banner_base"
+ "0": "entity/banner/base",
+ "tint": "entity/banner/base"
},
"elements": [
{
diff --git a/src/dialogs/blueprintSettings/blueprintSettings.ts b/src/dialogs/blueprintSettings/blueprintSettings.ts
index 92222760..b1584feb 100644
--- a/src/dialogs/blueprintSettings/blueprintSettings.ts
+++ b/src/dialogs/blueprintSettings/blueprintSettings.ts
@@ -22,7 +22,7 @@ const localize = createScopedTranslator('dialog.blueprint_settings')
export function openBlueprintSettings() {
const dialog = new SvelteDialogSidebar({
id: `animated_java_blueprint_settings`,
- title: 'Blueprint Settings',
+ title: localize('title'),
pages: {
general: {
component: GeneralComponent,
@@ -79,7 +79,7 @@ export function openBlueprintSettings() {
width: 1024,
defaultPage: 'general',
disableKeybinds: true,
- buttons: ['Close'],
+ buttons: [tl('dialog.close')],
onClose: () => {
updateRotationConstraints()
updateAllCubeOutlines()
diff --git a/src/dialogs/blueprintSettings/footer.svelte b/src/dialogs/blueprintSettings/footer.svelte
index dcd74fa2..c1a8eb6e 100644
--- a/src/dialogs/blueprintSettings/footer.svelte
+++ b/src/dialogs/blueprintSettings/footer.svelte
@@ -1,4 +1,8 @@
-
* = Required
+
+
+* = {localize('dialog.blueprint_settings.required_footer')}
diff --git a/src/dialogs/entityCount/entityCount.ts b/src/dialogs/entityCount/entityCount.ts
new file mode 100644
index 00000000..d7ad8d1e
--- /dev/null
+++ b/src/dialogs/entityCount/entityCount.ts
@@ -0,0 +1,17 @@
+import { SvelteDialog } from 'svelte-patching-tools/blockbench'
+import { PACKAGE } from '../../constants'
+import { localize as translate } from '../../util/lang'
+import EntityCountDialog from './entityCount.svelte'
+
+export const DIALOG_ID = `${PACKAGE.name}:entityCountDialog`
+
+export function openEntityCountDialog() {
+ new SvelteDialog({
+ id: DIALOG_ID,
+ title: translate('dialog.entity_count_dialog.title'),
+ width: 400,
+ component: EntityCountDialog,
+ buttons: [tl('dialog.close')],
+ disableKeybinds: true,
+ }).show()
+}
diff --git a/src/interface/keyframeEasings.ts b/src/interface/keyframeEasings.ts
deleted file mode 100644
index 4560ee12..00000000
--- a/src/interface/keyframeEasings.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { registerPatch } from 'blockbench-patch-manager'
-import { injectComponent } from 'svelte-patching-tools'
-import { activeProjectIsBlueprintFormat } from '../formats/blueprint'
-import KeyframeEasingsSvelte from '../svelteComponents/keyframeEasings.svelte'
-import EVENTS from '../util/events'
-
-function isFirstKeyframe(kf: _Keyframe) {
- return (
- kf.animator.keyframes
- .filter(k => k.channel === kf.channel)
- .sort((a, b) => a.time - b.time)[0] === kf
- )
-}
-
-let unmountCallback: (() => Promise) | null = null
-let currentUpdatePromise: Promise | null = null
-
-const updatePanel = () => {
- if (currentUpdatePromise) {
- return currentUpdatePromise.then(() => {
- void updatePanel()
- })
- }
-
- currentUpdatePromise = new Promise(async resolve => {
- await unmountCallback?.()
- if (!activeProjectIsBlueprintFormat()) return
-
- const selectedKeyframe = Timeline.selected.at(0)
- if (selectedKeyframe && !isFirstKeyframe(selectedKeyframe)) {
- unmountCallback = injectComponent({
- component: KeyframeEasingsSvelte,
- props: { selectedKeyframe },
- elementSelector() {
- return Panels.keyframe.node
- },
- postMount() {
- currentUpdatePromise = null
- resolve()
- },
- })
- } else {
- currentUpdatePromise = null
- resolve()
- }
- })
-}
-
-registerPatch({
- id: 'animated_java:mounted-svelte/keyframe-easings',
- apply() {
- const unsub = EVENTS.UPDATE_KEYFRAME_SELECTION.subscribe(() => void updatePanel())
-
- return { unsub }
- },
-
- async revert({ unsub }) {
- unsub()
- await unmountCallback?.()
- },
-})
diff --git a/src/lang/en.yml b/src/lang/en.yml
index 98f7a64b..542288d8 100644
--- a/src/lang/en.yml
+++ b/src/lang/en.yml
@@ -123,8 +123,26 @@ animated_java:
description: Click to copy the error message to the clipboard.
paragraph: 'Please report this error by joining our %s and creating a thread in the #animated-java-support channel, or by creating an issue on our {1}. Thank you!'
+ entity_count_dialog:
+ title: Entity Count
+ description: |-
+ [Markdown]
+ This dialog shows what nodes in your Blueprint will create entities in-game, and how many entities will be created in total.
+
+ Large numbers of entities will cause performance issues in-game. It's recommended to keep the total number of entities as low as possible.
+
+ For more information on Rig Entities, see [Animated Java Docs / Core Concepts / Rigs](https://animated-java.dev/docs/core-concepts/rigs)
+ root_entities: Root Entity
+ group_entities: Group Entities
+ display_entities: Display Entities
+ locator_entities: Locator Entities
+ camera_entities: Camera Entities
+ interaction_entities: Interaction Entities
+ total_entities: Total Entities
+
blueprint_settings:
title: Blueprint Settings
+ required_footer: Required
pages:
general.title: General
@@ -808,8 +826,11 @@ animated_java:
Number of ticks between command repeats.
Set to 1 to run every tick of the animation.
+
+ easings:
+ title: Keyframe Easing
easing_type:
- title: Easing Type
+ title: Curve
description: The type of easing to apply to the keyframe.
options:
linear: Linear
@@ -824,14 +845,13 @@ animated_java:
back: Back
bounce: Bounce
easing_mode:
- title: Easing Mode
+ title: Mode
description: The easing mode to apply to the keyframe.
options:
in: In
out: Out
inout: In-Out
easing_args:
- title: Easing Args
description: The arguments to apply to the easing function.
easing_arg:
elastic:
@@ -845,8 +865,16 @@ animated_java:
description: The bounciness of the easing function.
nonlinear_interpolation: |-
[Markdown]
- Advanced easing options are disabled.
- Change the keyframe's interpolation mode to `linear` to enable them.
+ Only keyframes with `linear` interpolation can use advanced easing options.
+ no_keyframe_selected: |-
+ [Markdown]
+ No keyframe selected.
+
+ Please select a keyframe to edit its easing options.
+ first_keyframe_easing: |-
+ [Markdown]
+ The first keyframe in an animation cannot change its easing options, as it has no previous keyframe to interpolate from.
+
text_display:
title: Text Display
vanilla_item_display:
diff --git a/src/lang/zh_cn.yml b/src/lang/zh_cn.yml
index 2458bcf6..18f849cd 100644
--- a/src/lang/zh_cn.yml
+++ b/src/lang/zh_cn.yml
@@ -1,587 +1,1077 @@
-### Actions
-animated_java.action.open_blueprint_settings.name: 蓝图设置
-animated_java.action.open_documentation.name: 文档
-animated_java.action.open_about.name: 关于
-animated_java.action.open_bone_config.name: 骨骼配置
-animated_java.action.open_locator_config.name: 定位器配置
-animated_java.action.open_text_display_config.name: 文本展示配置
-animated_java.action.export.name: 导出
-animated_java.action.create_text_display.title: 添加文本展示实体
-animated_java.action.create_vanilla_item_display.title: 添加物品展示实体
-animated_java.action.create_vanilla_block_display.title: 添加方块展示实体
-animated_java.action.open_vanilla_item_display_config.name: 物品展示配置
-animated_java.action.open_vanilla_block_display_config.name: 方块展示配置
-
-### Popups
-animated_java.popup.loading.loading: Animated Java 加载中...
-animated_java.popup.loading.success: Animated Java 加载完成!
-animated_java.popup.loading.offline: |-
- Animated Java 连接失败!
- 部分功能将不可用。
-
-animated_java.dialog.installed_popup.title: 感谢安装!
-animated_java.dialog.installed_popup.close_button: 是时候动起来了!
-
-### Dialogs
-animated_java.dialog.reset: 重置为默认值
-
-## About
-animated_java.dialog.about.title: 关于 Animated Java
-animated_java.dialog.about.close_button: 关闭
-
-## Unexpected Error Dialog
-animated_java.dialog.unexpected_error.title: 发生了意外错误!
-animated_java.dialog.unexpected_error.close_button: 关闭
-animated_java.dialog.unexpected_error.copy_error_message_button.message: 已复制错误信息!
-animated_java.dialog.unexpected_error.copy_error_message_button.description: 点击保存错误信息到剪切板。
-animated_java.dialog.unexpected_error.paragraph: '可加入 Discord 服务器 {0} 并在 #animated-java-support 频道创建讨论串来报告该错误信息,或在 {1} 下创建一个issue。谢谢!'
-
-## Blueprint Settings Dialog
-animated_java.dialog.blueprint_settings.title: 蓝图设置
-animated_java.dialog.blueprint_settings.advanced_settings_warning: 仅在十分必要时使用高级设置!
-animated_java.dialog.blueprint_settings.blueprint_name.title: 蓝图名称
-animated_java.dialog.blueprint_settings.blueprint_name.description: 蓝图的名称,仅用于识别工作区中的项目。
-
-animated_java.dialog.blueprint_settings.texture_size.title: 纹理尺寸
-animated_java.dialog.blueprint_settings.texture_size.description: UV编辑器的分辨率,应当与最大的纹理尺寸一致。为达到更好的游戏效果,建议使用长宽相等且为2的幂次方的贴图。
-animated_java.dialog.blueprint_settings.texture_size.warning.not_square: 为达到最佳效果,纹理的长与宽应该相等。
-animated_java.dialog.blueprint_settings.texture_size.warning.not_a_power_of_2: 为达到最佳效果,纹理的尺寸应为2的幂次方。
-animated_java.dialog.blueprint_settings.texture_size.warning.does_not_match_largest_texture: 纹理的尺寸应当与最大纹理的尺寸一致。
-
-# Export Settings
-animated_java.dialog.blueprint_settings.export_settings.title: 导出设置
-
-animated_java.dialog.blueprint_settings.export_namespace.title: 导出命名空间
-animated_java.dialog.blueprint_settings.export_namespace.description: 项目导出时所使用的名称,或者说是导出为资源包与数据包时所使用的命名空间。
-animated_java.dialog.blueprint_settings.export_namespace.error.empty: 导出命名空间不能为空!
-animated_java.dialog.blueprint_settings.export_namespace.error.reserved: 导出命名空间 “{0}” 仅用于内部运作!请使用其他命名空间。
-animated_java.dialog.blueprint_settings.export_namespace.error.invalid_characters: 导出命名空间的名称包含了非法字符!请仅使用英文字符、数字和下划线。
-
-animated_java.dialog.blueprint_settings.enable_plugin_mode.title: 插件模式
-animated_java.dialog.blueprint_settings.enable_plugin_mode.description: 是否开启插件模式,计划使用基于插件而非资源包/数据包的话启用该模式。
-
-animated_java.dialog.blueprint_settings.resource_pack_export_mode.title: 资源包导出模式
-animated_java.dialog.blueprint_settings.resource_pack_export_mode.description: |-
- 资源包的导出方式。
- 原始 - 资源包以文件夹的方式导出。
- 压缩 - 资源包以 .zip 压缩包的方式导出。
- 无 - 关闭对资源包的导出。
-animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.raw: 原始
-animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.zip: 压缩
-animated_java.dialog.blueprint_settings.resource_pack_export_mode.options.none: 无
-
-animated_java.dialog.blueprint_settings.data_pack_export_mode.title: 数据包导出模式
-animated_java.dialog.blueprint_settings.data_pack_export_mode.description: |-
- 数据包的导出方式。
- 原始 - 数据包以文件夹的方式导出。
- 压缩 - 数据包以 .zip 压缩包的方式导出。
- 无 - 关闭对数据包的导出。
-animated_java.dialog.blueprint_settings.data_pack_export_mode.options.raw: 原始
-animated_java.dialog.blueprint_settings.data_pack_export_mode.options.zip: 压缩
-animated_java.dialog.blueprint_settings.data_pack_export_mode.options.none: 无
-
-animated_java.dialog.blueprint_settings.show_bounding_box.title: 显示边界框
-animated_java.dialog.blueprint_settings.show_bounding_box.description: 是否在编辑模式下显示边界框。
-
-animated_java.dialog.blueprint_settings.auto_bounding_box.title: 自动边界框
-animated_java.dialog.blueprint_settings.auto_bounding_box.description: |-
- 是否根据模型的形状自动计算边界框。
- 注:自动边界框并不会根据动画时骨骼的偏移进行计算,所以可能导致比预想中要小的情况。
-
-animated_java.dialog.blueprint_settings.bounding_box.title: 边界框
-animated_java.dialog.blueprint_settings.bounding_box.description: 确定模型的剔除框,当该框离开屏幕时将停止该模型的渲染。
-
-# Resource Pack Settings
-animated_java.dialog.blueprint_settings.resource_pack_settings.title: 资源包设置
-
-animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_settings.title: 高级设置
-animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_settings.description: 是否启用资源包的高级设置。
-
-animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_folders.title: 高级文件夹
-animated_java.dialog.blueprint_settings.enable_advanced_resource_pack_folders.description: 是否启用高级资源包文件夹设置。
-
-animated_java.dialog.blueprint_settings.display_item.title: 承载物
-animated_java.dialog.blueprint_settings.display_item.description: 游戏中用于显示蓝图内模型的物品。多个蓝图使用相同的物品时会被自动合并。
-animated_java.dialog.blueprint_settings.display_item.error.no_item_selected: 未指定物品!
-animated_java.dialog.blueprint_settings.display_item.error.invalid_item_id.no_namespace: 所提供的物品ID无效!物品ID的填写格式应为 命名空间:物品id 。
-animated_java.dialog.blueprint_settings.display_item.error.invalid_item_id.whitespace: 所提供的物品ID无效!物品ID不应包含空格。
-animated_java.dialog.blueprint_settings.display_item.warning.item_does_not_exist: 原版不存在所指定的物品!
-animated_java.dialog.blueprint_settings.display_item.warning.item_model_not_generated: 所选物品并未使用 'minecraft:item/generated' 为父类。可能会导致游戏中的模型出现问题。
-animated_java.dialog.blueprint_settings.display_item.error.item_model_not_found: 所选物品在原版资源包中不存在模型文件!
-
-animated_java.dialog.blueprint_settings.custom_model_data_offset.title: CMD 偏位
-animated_java.dialog.blueprint_settings.custom_model_data_offset.description: 承载物的 Custom Model Data 所使用的偏移量。允许互相独立的资源包内的多个蓝图使用相同的物品为承载物。
-
-animated_java.dialog.blueprint_settings.resource_pack.title: 资源包
-animated_java.dialog.blueprint_settings.resource_pack.description: 项目导出到的资源包的根文件夹。
-animated_java.dialog.blueprint_settings.resource_pack.error.no_folder_selected: 未指定文件夹!
-animated_java.dialog.blueprint_settings.resource_pack.error.folder_does_not_exist: 所指定的文件夹不存在!
-animated_java.dialog.blueprint_settings.resource_pack.error.not_a_folder: 所指定的路径并非文件夹!
-animated_java.dialog.blueprint_settings.resource_pack.error.missing_pack_mcmeta: 所指定的文件夹缺少 pack.mcmeta 文件!
-animated_java.dialog.blueprint_settings.resource_pack.error.missing_assets_folder: 所指定的资源包缺少 assets 文件夹!
-
-animated_java.dialog.blueprint_settings.resource_pack_zip.title: 压缩资源包
-animated_java.dialog.blueprint_settings.resource_pack_zip.description: 项目导出到 .zip 文件的路径。
-animated_java.dialog.blueprint_settings.resource_pack_zip.error.no_file_selected: 未指定文件!
-animated_java.dialog.blueprint_settings.resource_pack_zip.error.not_a_file: 所指定的路径并非文件!
-
-animated_java.dialog.blueprint_settings.display_item_path.title: 承载物路径
-animated_java.dialog.blueprint_settings.display_item_path.description: 所有承载物的存放位置。应为资源包内到 .json 文件的路径。
-animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.no_file_selected: 未指定文件!
-animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.file_does_not_exist: 所指定的文件不存在!
-animated_java.dialog.blueprint_settings.advanced_resource_pack_file.error.not_a_file: 所指定的路径并非文件!
-
-animated_java.dialog.blueprint_settings.model_folder.title: 模型文件夹
-animated_java.dialog.blueprint_settings.model_folder.description: 所有导出模型的存放位置。应为资源包内到文件夹的路径。
-animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.no_folder_selected: 未指定文件夹!
-animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.folder_does_not_exist: 所指定的文件夹不存在!
-animated_java.dialog.blueprint_settings.advanced_resource_pack_folder.error.not_a_folder: 所指定的路径并非文件夹!
-
-animated_java.dialog.blueprint_settings.texture_folder.title: 纹理文件夹
-animated_java.dialog.blueprint_settings.texture_folder.description: 所有导出纹理的存放位置。应为资源包内到文件夹的路径。
-
-# Data Pack Settings
-animated_java.dialog.blueprint_settings.data_pack_settings.title: 数据包设置
-
-animated_java.dialog.blueprint_settings.enable_advanced_data_pack_settings.title: 启用高级设置
-animated_java.dialog.blueprint_settings.enable_advanced_data_pack_settings.description: 是否启用数据包的高级设置。
-
-animated_java.dialog.blueprint_settings.data_pack.title: 数据包
-animated_java.dialog.blueprint_settings.data_pack.description: 项目导出到的数据包的根文件夹。
-animated_java.dialog.blueprint_settings.data_pack.error.no_folder_selected: 未指定文件夹!
-animated_java.dialog.blueprint_settings.data_pack.error.folder_does_not_exist: 所指定的文件夹不存在!
-animated_java.dialog.blueprint_settings.data_pack.error.not_a_folder: 所指定的路径并非文件夹!
-animated_java.dialog.blueprint_settings.data_pack.error.missing_pack_mcmeta: 所指定的文件夹缺少 pack.mcmeta 文件!
-animated_java.dialog.blueprint_settings.data_pack.error.missing_data_folder: 所指定的数据包缺少 data 文件夹!
-
-animated_java.dialog.blueprint_settings.data_pack_zip.title: 压缩数据包
-animated_java.dialog.blueprint_settings.data_pack_zip.description: 项目导出到 .zip 文件的路径。
-animated_java.dialog.blueprint_settings.data_pack_zip.error.no_file_selected: 未指定文件!
-animated_java.dialog.blueprint_settings.data_pack_zip.error.not_a_file: 所指定的路径并非文件!
-
-animated_java.dialog.blueprint_settings.summon_commands.title: 进场命令
-animated_java.dialog.blueprint_settings.summon_commands.description: |-
- 被 summon 时以根实体执行的命令。
- 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
-
-animated_java.dialog.blueprint_settings.ticking_commands.title: 高频命令
-animated_java.dialog.blueprint_settings.ticking_commands.description: |-
- 在根实体位置高频执行的命令。
- 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
- 该命令将在动画逻辑之后执行。
-
-animated_java.dialog.blueprint_settings.interpolation_duration.title: 插值时间
-animated_java.dialog.blueprint_settings.interpolation_duration.description: 以tick为单位,关键帧之间平滑过渡的持续时间。是模型过渡到下一个关键帧所用的时间。较高的插值时间将导致动画失去精度,因此该值一般建议为1或2。
-
-animated_java.dialog.blueprint_settings.teleportation_duration.title: 传送时间
-animated_java.dialog.blueprint_settings.teleportation_duration.description: 以tick为单位,关键帧之间传送的持续时间。是模型在视觉上从上一个位置过渡到新位置所用的时间。较高的传送时间将导致动画失去精度。
-
-animated_java.dialog.blueprint_settings.use_storage_for_animation.title: 使用 storage 存储动画
-animated_java.dialog.blueprint_settings.use_storage_for_animation.description: |-
- 是否使用 NBT storage 代替函数进行动画数据存储。
- 将会大大减少数据包生成时的函数文件数量,相对的这会比使用函数存储的办法慢42%。
-
-# Plugin Settings
-animated_java.dialog.blueprint_settings.baked_animations.title: 烘培动画
-animated_java.dialog.blueprint_settings.baked_animations.description: |-
- 是否烘培导出的动画。
- 已烘培动画的帧会预先计算并存储在导出的 JSON 文件中,从而降低在游戏中渲染模型时的复杂性。
- 部分插件可能需要开启这项以确保正常运作。
-
-animated_java.dialog.blueprint_settings.json_file.title: JSON 文件
-animated_java.dialog.blueprint_settings.json_file.description: 项目导出到 JSON 文件的路径。
-animated_java.dialog.blueprint_settings.json_file.error.no_file_selected: 未指定文件!
-animated_java.dialog.blueprint_settings.json_file.error.not_a_file: 所指定的路径并非文件!
-
-## Bone Config Dialog
-animated_java.dialog.bone_config.title: 骨骼配置
-
-animated_java.dialog.bone_config.selected_variant: “已选变体:{0}”
-animated_java.dialog.bone_config.default_variant_subtitle: 以下设置将默认应用到该骨骼。
-animated_java.dialog.bone_config.selected_variant_subtitle: 以下设置仅在应用该变体时应用到该骨骼。
-
-animated_java.dialog.bone_config.use_nbt.title: 使用 NBT
-animated_java.dialog.bone_config.use_nbt.description: 是否使用 NBT 取代设置进行骨骼配置。
-animated_java.dialog.bone_config.use_nbt.use_nbt_warning: 使用 NBT 将会覆盖所有其他设置,任何更改也不会在编辑器中可见。仅在熟悉此功能的情况下使用!
-
-animated_java.dialog.bone_config.inherit_settings.title: 继承设置
-animated_java.dialog.bone_config.inherit_settings.description: 是否从父级骨骼继承设置。
-
-animated_java.dialog.bone_config.custom_name.title: 自定义名称
-animated_java.dialog.bone_config.custom_name.description: 骨骼的自定义名称。
-animated_java.dialog.bone_config.custom_name.invalid_json.error: |-
- 无效的 JSON 文本!
- {0}
-
-animated_java.dialog.bone_config.custom_name_visible.title: 自定义名称可见性
-animated_java.dialog.bone_config.custom_name_visible.description: 自定义名称是否总是可见。
-
-animated_java.dialog.bone_config.glowing.title: 发光
-animated_java.dialog.bone_config.glowing.description: 骨骼是否在游戏中发光。
-
-animated_java.dialog.bone_config.override_glow_color.title: 修改发光颜色
-animated_java.dialog.bone_config.override_glow_color.description: 是否改变发光的默认颜色。
-
-animated_java.dialog.bone_config.glow_color.title: 发光颜色
-animated_java.dialog.bone_config.glow_color.description: 发光的颜色。
-
-animated_java.dialog.bone_config.shadow_radius.title: 阴影半径
-animated_java.dialog.bone_config.shadow_radius.description: 阴影的半径。
-
-animated_java.dialog.bone_config.shadow_strength.title: 阴影强度
-animated_java.dialog.bone_config.shadow_strength.description: 阴影的强度。
-
-animated_java.dialog.bone_config.override_brightness.title: 修改亮度
-animated_java.dialog.bone_config.override_brightness.description: 是否修改亮度的默认等级。
-
-animated_java.dialog.bone_config.brightness_override.title: 亮度
-animated_java.dialog.bone_config.brightness_override.description: 骨骼的亮度。取值范围为0至15之间。
-
-animated_java.dialog.bone_config.use_custom_brightness.title: 使用自定义亮度
-animated_java.dialog.bone_config.use_custom_brightness.description: 骨骼是否启用自定义亮度。
-
-animated_java.dialog.bone_config.custom_brightness.title: 自定义亮度
-animated_java.dialog.bone_config.custom_brightness.description: 骨骼的自定义亮度。取值范围为0至15之间。
-
-animated_java.dialog.bone_config.enchanted.title: 附魔
-animated_java.dialog.bone_config.enchanted.description: 骨骼是否为附魔状态效果。
-
-animated_java.dialog.bone_config.invisible.title: 隐形
-animated_java.dialog.bone_config.invisible.description: 骨骼是否隐形。
-
-animated_java.dialog.bone_config.nbt.title: NBT
-animated_java.dialog.bone_config.nbt.description: 应用到骨骼的NBT。
-animated_java.dialog.bone_config.nbt.invalid_nbt.not_compound: 填写的NBT应为复合标签!例:{CustomName:'"你的名字"'}
-animated_java.dialog.bone_config.nbt.invalid_nbt.error: |-
- 无效的NBT!
- {0}
-
-animated_java.dialog.bone_config.billboard.title: 广告牌
-animated_java.dialog.bone_config.billboard.description: 控制此骨骼在渲染时如何旋转以面向玩家。可设置为固定(固定垂直和水平轴,无旋转),垂直(固定垂直轴),水平(固定水平轴),和中心(按照中心旋转跟随玩家视角)。
-animated_java.dialog.bone_config.billboard.options.fixed: 固定
-animated_java.dialog.bone_config.billboard.options.vertical: 垂直
-animated_java.dialog.bone_config.billboard.options.horizontal: 水平
-animated_java.dialog.bone_config.billboard.options.center: 中心
-
-## Locator Config Dialog
-animated_java.dialog.locator_config.title: 定位器配置
-
-animated_java.dialog.locator_config.plugin_mode_warning: |-
- 插件模式已启用!该模式下没有定位器配置。
- 请使用插件API为定位器添加自定义功能。
- 详情请查看官方的插件API文档。
-
-animated_java.dialog.locator_config.use_entity.title: 使用实体
-animated_java.dialog.locator_config.use_entity.description: 是否在定位器处附加跟随实体。
-
-animated_java.dialog.locator_config.entity_type.title: 实体类型
-animated_java.dialog.locator_config.entity_type.description: 附加到定位器的实体类型。
-animated_java.dialog.locator_config.entity_type.error.empty: 实体类型不能为空!
-animated_java.dialog.locator_config.entity_type.warning.invalid: 所选的实体类型 {0} 不存在于Minecraft中。
-
-animated_java.dialog.locator_config.summon_commands.title: 进场命令
-animated_java.dialog.locator_config.summon_commands.description: |-
- 被 summon 时以定位器实体执行的命令。
- 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
-
-animated_java.dialog.locator_config.ticking_commands.title: 高频命令
-animated_java.dialog.locator_config.ticking_commands.description: |-
- 在定位器位置高频执行的命令。
- 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
-
-## Text Display Config Dialog
-animated_java.dialog.text_display_config.title: 文本展示配置
-
-animated_java.dialog.bone_config.vanilla_item_model.title: 原版物品模型
-animated_java.dialog.bone_config.vanilla_item_model.description: |-
- 若设置,此骨骼将显示为原版物品模型。
- 并且覆盖骨骼现有的方块。
-
-animated_java.dialog.text_display_config.use_nbt.title: 使用 NBT
-animated_java.dialog.text_display_config.use_nbt.description: 是否使用 NBT 取代设置进行文本展示配置。
-animated_java.dialog.text_display_config.use_nbt.use_nbt_warning: 使用 NBT 将会覆盖所有其他设置,任何更改也不会在编辑器中可见。仅在熟悉此功能的情况下使用!
-
-animated_java.dialog.text_display_config.inherit_settings.title: 继承设置
-animated_java.dialog.text_display_config.inherit_settings.description: 是否从父级文本展示继承设置。
-
-animated_java.dialog.text_display_config.glowing.title: 发光
-animated_java.dialog.text_display_config.glowing.description: 文本展示是否在游戏中发光。
-
-animated_java.dialog.text_display_config.override_glow_color.title: 修改发光颜色
-animated_java.dialog.text_display_config.override_glow_color.description: 是否改变发光的默认颜色。
-
-animated_java.dialog.text_display_config.glow_color.title: 发光颜色
-animated_java.dialog.text_display_config.glow_color.description: 发光的颜色
-
-animated_java.dialog.text_display_config.shadow_radius.title: 阴影半径
-animated_java.dialog.text_display_config.shadow_radius.description: 阴影的半径。
-
-animated_java.dialog.text_display_config.shadow_strength.title: 阴影强度
-animated_java.dialog.text_display_config.shadow_strength.description: 阴影的强度
-
-animated_java.dialog.text_display_config.override_brightness.title: 修改亮度
-animated_java.dialog.text_display_config.override_brightness.description: 是否修改亮度的默认等级。
-
-animated_java.dialog.text_display_config.brightness_override.title: 亮度
-animated_java.dialog.text_display_config.brightness_override.description: 文本展示的亮度。取值范围为0至15之间。
-
-animated_java.dialog.text_display_config.use_custom_brightness.title: 使用自定义亮度
-animated_java.dialog.text_display_config.use_custom_brightness.description: 骨骼是否启用自定义亮度。
-
-animated_java.dialog.text_display_config.custom_brightness.title: 自定义亮度
-animated_java.dialog.text_display_config.custom_brightness.description: 骨骼的自定义亮度。取值范围为0至15之间。
-
-animated_java.dialog.text_display_config.invisible.title: 隐形
-animated_java.dialog.text_display_config.invisible.description: 文本展示是否隐形。
-
-animated_java.dialog.text_display_config.nbt.title: NBT
-animated_java.dialog.text_display_config.nbt.description: 应用到文本展示的NBT。
-
-animated_java.dialog.text_display_config.billboard.title: 广告牌
-animated_java.dialog.text_display_config.billboard.description: 控制此文本展示在渲染时如何旋转以面向玩家。可设置为固定(固定垂直和水平轴,无旋转),垂直(固定垂直轴),水平(固定水平轴),和中心(按照中心旋转跟随玩家视角)。
-animated_java.dialog.text_display_config.billboard.options.fixed: 固定
-animated_java.dialog.text_display_config.billboard.options.vertical: 垂直
-animated_java.dialog.text_display_config.billboard.options.horizontal: 水平
-animated_java.dialog.text_display_config.billboard.options.center: 中心
-
-## Block Display Config Dialog
-animated_java.dialog.vanilla_block_display_config.title: 方块展示配置
-animated_java.dialog.vanilla_block_display.custom_name.title: 自定义名称
-animated_java.dialog.vanilla_block_display.custom_name.description: 方块展示的自定义名称。
-animated_java.dialog.vanilla_block_display.custom_name.invalid_json.error: |-
- 无效的 JSON 文本!
- {0}
-
-animated_java.dialog.vanilla_block_display.custom_name_visible.title: 自定义名称可见性
-animated_java.dialog.vanilla_block_display.custom_name_visible.description: 自定义名称是否总是可见。
-
-## Item Display Config Dialog
-animated_java.dialog.vanilla_item_display_config.title: 物品展示配置
-animated_java.dialog.vanilla_item_display.custom_name.title: 自定义名称
-animated_java.dialog.vanilla_item_display.custom_name.description: 物品展示的自定义名称。
-animated_java.dialog.vanilla_item_display.custom_name.invalid_json.error: |-
- 无效的 JSON 文本!
- {0}
-
-animated_java.dialog.vanilla_item_display.custom_name_visible.title: 自定义名称可见性
-animated_java.dialog.vanilla_item_display.custom_name_visible.description: 自定义名称是否总是可见。
-
-## Variant Config Dialog
-animated_java.dialog.variant_config.title: 变体配置
-
-animated_java.dialog.variant_config.variant_display_name: 显示名称
-animated_java.dialog.variant_config.variant_display_name.description: 用于在编辑器和错误信息中识别变体。
-
-animated_java.dialog.variant_config.generate_name_from_display_name: 生成显示名称
-animated_java.dialog.variant_config.generate_name_from_display_name.description: 是否根据显示名称自动生成名称。
-
-animated_java.dialog.variant_config.variant_name: 名称
-animated_java.dialog.variant_config.variant_name.description: 用于在导出的资源包和数据包中识别变体。
-
-animated_java.dialog.variant_config.texture_map.title: 纹理映射
-animated_java.dialog.variant_config.texture_map.description: 应用该变体时所使用的贴图。
-animated_java.dialog.variant_config.texture_map.create_new_mapping: 创建新的映射
-animated_java.dialog.variant_config.texture_map.no_mappings: 变体不存在映射的纹理。
-
-animated_java.dialog.variant_config.bone_lists.description: 包含或排除在变体的节点列表。包含的节点将会被变体修改,排除的节点则会被节点忽略。
-animated_java.dialog.variant_config.excluded_nodes.title: 排除节点
-animated_java.dialog.variant_config.excluded_nodes.description: 排除在变体的节点列表。该节点将不会被变体修改。
-animated_java.dialog.variant_config.included_nodes.title: 包含节点
-animated_java.dialog.variant_config.included_nodes.description: 包含在变体的节点列表。该节点将会被变体修改。
-animated_java.dialog.variant_config.swap_columns_button.tooltip: 互换列表
-
-## Old AJModel Loader Dialog
-animated_java.action.upgrade_old_aj_model_loader.name: 更新 .ajmodel
-animated_java.dialog.upgrade_old_aj_model_loader.title: 更新 .ajmodel
-animated_java.action.upgrade_old_aj_model_loader.select_file: 选择 .ajmodel 文件
-animated_java.action.upgrade_old_aj_model_loader.body: 升级旧版 .ajmodel 文件为新的 .ajblueprint 格式文件。
-animated_java.action.upgrade_old_aj_model_loader.button: 选择需要更新的 .ajmodel 文件
-
-## Animation Properties Dialog
-animated_java.dialog.animation_properties.title: 动画属性 ({0})
-
-animated_java.dialog.animation_properties.animation_name.title: 动画名称
-animated_java.dialog.animation_properties.animation_name.description: 动画的名称。
-
-animated_java.dialog.animation_properties.loop_mode.title: 循环模式
-animated_java.dialog.animation_properties.loop_mode.description: 动画的循环方式。单次 - 动画播放一次后停止。停留 - 动画播放一次后停留在最后帧。循环 - 动画重复播放。
-animated_java.dialog.animation_properties.loop_mode.options.once: 单次
-animated_java.dialog.animation_properties.loop_mode.options.hold: 停留
-animated_java.dialog.animation_properties.loop_mode.options.loop: 循环
-animated_java.dialog.animation_properties.animation_name.error.empty: 动画名称不能为空!
-animated_java.dialog.animation_properties.animation_name.error.invalid_characters: 动画的名称包含了非法字符!请仅使用英文字符、数字、下划线和英文点号。
-
-animated_java.dialog.animation_properties.loop_delay.title: 循环延迟
-animated_java.dialog.animation_properties.loop_delay.description: 循环的延迟。动画结束后到再次播放的间隔时间。仅在循环模式为循环时适用。
-
-animated_java.dialog.animation_properties.bone_lists.description: 包含或排除在动画中的节点列表。包含的节点将会被动画修改,排除的节点则会被动画忽略。
-animated_java.dialog.animation_properties.excluded_nodes.title: 排除节点
-animated_java.dialog.animation_properties.excluded_nodes.description: 排除在动画中的节点列表。该节点将不会被动画修改。
-animated_java.dialog.animation_properties.included_nodes.title: 包含节点
-animated_java.dialog.animation_properties.included_nodes.description: 包含在动画中的节点列表。该节点将会被动画修改。
-animated_java.dialog.animation_properties.swap_columns_button.tooltip: 互换列表
-
-## Export Progress Dialog
-animated_java.dialog.export_progress.title: 导出中...
-
-## Blueprint Loading Dialog
-animated_java.dialog.blueprint_loading.title: 蓝图加载中...
-
-### Panels
-
-## Variants Panel
-animated_java.panel.variants.title: 变体
-animated_java.panel.variants.tool.create_new_variant: 创建新变体
-animated_java.panel.variants.tool.edit_variant: 编辑变体
-animated_java.panel.variants.tool.duplicate_selected_variant: 复制所选变体
-animated_java.panel.variants.tool.delete_selected_variant: 删除所选变体
-animated_java.panel.variants.tool.variant_visible: 已选择变体
-animated_java.panel.variants.tool.variant_not_visible: 未选择变体
-animated_java.panel.variants.tool.cannot_delete_default_variant: 不可删除默认变体!
-
-animated_java.action.variants.create: 创建变体
-animated_java.action.variants.duplicate: 复制变体
-animated_java.action.variants.open_config: 打开变体配置
-animated_java.action.variants.delete: 删除变体
-
-### Animator
-
-## Properties
-animated_java.animation.excluded_nodes: 排除节点
-animated_java.animation.invert_excluded_nodes: 反转排除节点
-
-## Timeline
-animated_java.effect_animator.timeline.variant: 变体
-animated_java.effect_animator.timeline.commands: 命令
-
-## Keyframes
-animated_java.effect_animator.keyframes.variant: 变体
-animated_java.effect_animator.keyframes.commands: 命令
-animated_java.effect_animator.keyframes.execute_condition: 执行条件
-
-# Keyframe Panel
-animated_java.panel.keyframe.keyframe_title: 关键帧 ({0})
-
-animated_java.panel.keyframe.variant.title: 变体
-animated_java.panel.keyframe.variant.description: 应用到关键帧的变体。
-
-animated_java.panel.keyframe.commands.title: 命令
-animated_java.panel.keyframe.commands.description: |-
- 到达目标关键帧时运行的命令。
- 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
-animated_java.panel.keyframe.execute_condition.title: 执行条件
-animated_java.panel.keyframe.execute_condition.description: 关键帧执行命令时必须满足的条件。可视输入的文本为 execute 命令的子命令。
-
-animated_java.panel.keyframe.repeat.title: 重复执行?
-animated_java.panel.keyframe.repeat.description: |-
- 是否在该关键帧重复执行命令。
- 若启用,则会以相应的频率执行命令。
-
-animated_java.panel.keyframe.repeat_frequency.title: 重复频率
-animated_java.panel.keyframe.repeat_frequency.description: |-
- 该关键帧再次执行命令的等待刻数。
- 设置为1则在每一刻执行一次命令。
- 注意:若动画不能被该值整除,则动画循环时间隔可能会略有偏差。
- 若设值大于动画的长度,命令则会视为重复被禁止时执行。(到达关键帧时执行一次)
-
-animated_java.panel.keyframe.easing_type.title: 缓动类型
-animated_java.panel.keyframe.easing_type.description: 应用到关键帧的缓动类型。
-animated_java.panel.keyframe.easing_type.options.linear: 线性
-animated_java.panel.keyframe.easing_type.options.sine: 正弦
-animated_java.panel.keyframe.easing_type.options.quad: 二次方
-animated_java.panel.keyframe.easing_type.options.cubic: 三次方
-animated_java.panel.keyframe.easing_type.options.quart: 四次方
-animated_java.panel.keyframe.easing_type.options.quint: 五次方
-animated_java.panel.keyframe.easing_type.options.expo: 指数
-animated_java.panel.keyframe.easing_type.options.circ: 圆
-animated_java.panel.keyframe.easing_type.options.elastic: 弹性
-animated_java.panel.keyframe.easing_type.options.back: 后视图
-animated_java.panel.keyframe.easing_type.options.bounce: 弹跳
-
-animated_java.panel.keyframe.easing_mode.title: 缓动模式
-animated_java.panel.keyframe.easing_mode.description: 应用到关键帧的缓动模式。
-animated_java.panel.keyframe.easing_mode.options.in: 淡入
-animated_java.panel.keyframe.easing_mode.options.out: 淡出
-animated_java.panel.keyframe.easing_mode.options.inout: 淡入淡出
-
-animated_java.panel.keyframe.easing_args.title: 缓动参数
-animated_java.panel.keyframe.easing_args.description: 缓动函数使用的参数。
-animated_java.panel.keyframe.easing_args.easing_arg.elastic.title: 弹性
-animated_java.panel.keyframe.easing_args.easing_arg.elastic.description: 缓动函数的弹性。
-animated_java.panel.keyframe.easing_args.easing_arg.back.title: 过冲
-animated_java.panel.keyframe.easing_args.easing_arg.back.description: 缓动函数中的过冲次数。
-animated_java.panel.keyframe.easing_args.easing_arg.bounce.title: 弹跳
-animated_java.panel.keyframe.easing_args.easing_arg.bounce.description: 缓动函数的弹跳性。
-
-animated_java.panel.keyframe.nonlinear_interpolation: |-
- 当前禁用高级缓动设置。
- 修改关键帧的插值模式为“线性”以启用。
-
-# Text Display Panel
-animated_java.panel.text_display.title: 文本展示
-
-animated_java.tool.text_display.line_width.title: 行宽
-animated_java.tool.text_display.line_width.description: 文本展示的行宽,以像素为单位。
-
-animated_java.tool.text_display.background_color.title: 背景颜色
-animated_java.tool.text_display.background_color.description: 文本展示的背景颜色。
-
-animated_java.tool.text_display.text_shadow.title: 文字阴影
-animated_java.tool.text_display.text_shadow.description: 是否在显示的文字后面显示阴影。
-
-animated_java.tool.text_display.text_alignment.title: 文本对齐
-animated_java.tool.text_display.text_alignment.description: 文本的对齐方式。
-animated_java.tool.text_display.text_alignment.options.left: 靠左
-animated_java.tool.text_display.text_alignment.options.center: 居中
-animated_java.tool.text_display.text_alignment.options.right: 靠右
-
-animated_java.tool.text_display.see_through.title: 穿透
-animated_java.tool.text_display.see_through.description: 文本展示是否在方块后依然可见。
-
-# Item Display Panel
-animated_java.panel.vanilla_item_display.title: 显示物品
-animated_java.panel.vanilla_item_display.description: 所显示的物品。
-animated_java.tool.item_display.item_display.title: 物品显示模式
-animated_java.tool.item_display.item_display.description: 应用到物品模型的变化。(参考自 JSON 模型中 display 的定义)
-animated_java.tool.item_display.item_display.options.none: 无
-animated_java.tool.item_display.item_display.options.thirdperson_lefthand: 第三人称左手
-animated_java.tool.item_display.item_display.options.thirdperson_righthand: 第三人称右手
-animated_java.tool.item_display.item_display.options.firstperson_lefthand: 第一人称左手
-animated_java.tool.item_display.item_display.options.firstperson_righthand: 第一人称右手
-animated_java.tool.item_display.item_display.options.head: 头部
-animated_java.tool.item_display.item_display.options.gui: GUI
-animated_java.tool.item_display.item_display.options.ground: 地面
-animated_java.tool.item_display.item_display.options.fixed: 固定
-
-# Block Display Panel
-animated_java.panel.vanilla_block_display.title: 显示方块
-animated_java.panel.vanilla_block_display.description: 所显示的方块。支持输入方块状态!
-
-### Custom Elements
-## Vanilla Item Display
-animated_java.vanilla_item_display.title: 原版物品展示
-
-## Vanilla Block Display
-
-### Misc
-
-# Blueprint Setting Errors - Failed to Export Message Box
-animated_java.misc.failed_to_export.title: 导出失败
-animated_java.misc.failed_to_export.custom_models.message: 当前设置为不导出资源包,但项目中存在自定义模型!请启用资源包导出或移除自定义模型后再次尝试导出。
-animated_java.misc.failed_to_export.blueprint_settings.message: 蓝图设置存在错误!请在修正后再次尝试导出。
-animated_java.misc.failed_to_export.blueprint_settings.error_item: '找到错误 {0}:'
-animated_java.misc.failed_to_export.button: 好
-
-# Format Category
-animated_java.format_category.animated_java: Animated Java
-
-# Model Manager Warnings
-animated_java.block_model_manager.fluid_warning: 方块展示下不会渲染流体。
-animated_java.block_model_manager.mob_head_warning: 方块展示下不会渲染生物头颅,请改用物品展示。
-animated_java.block_model_manager.facing_warning: 方块展示不支持方块状态 “facing”。
-
-# Project Errors
-animated_java.error.blueprint_export_path_doesnt_exist.title: 蓝图导出路径不存在
-animated_java.error.blueprint_export_path_doesnt_exist.description: |-
- 不存在所指定的导出路径 '{0}' !
- 请在确保路径下存在指定的文件夹后再次尝试。
\ No newline at end of file
+animated_java:
+ menubar:
+ label: Animated Java
+ format:
+ blueprint:
+ name: 蓝图
+ require:
+ fs: Animated Java 需要 fs 模块,才能在导出时导出数据包和资源包、保存和加载蓝图、验证蓝图设置,以及缓存 Minecraft 资源。
+ outliner:
+ structure_group:
+ title: 结构组(不会创建实体)
+ action:
+ open_blueprint_settings:
+ name: 蓝图设置
+ open_documentation:
+ name: 文档
+ open_changelog:
+ name: 更新日志
+ open_about:
+ name: 关于
+ open_display_entity_config:
+ name: 展示实体配置
+ open_locator_config:
+ name: 定位器配置
+ open_interaction_config:
+ name: 交互实体配置
+ export:
+ name: 导出
+ export_debug:
+ name: 导出(调试)
+ export_all:
+ name: 全部导出
+ description: 导出所有已打开的蓝图项目。
+ export_all_debug:
+ name: 全部导出(调试)
+ description: 以调试模式导出所有已打开的蓝图项目。
+ extract:
+ name: 提取
+ confirm: 确认提取
+ create_text_display:
+ title: 添加文本展示实体
+ create_item_display:
+ title: 添加物品展示实体
+ create_block_display:
+ title: 添加方块展示实体
+ copy_display_entity_config:
+ name: 复制展示实体配置
+ message: 已从 "%s" 复制展示实体配置
+ paste_display_entity_config:
+ name: 粘贴展示实体配置
+ message: 已从 "%s" 粘贴展示实体配置
+ variants:
+ create: 创建变体
+ duplicate: 复制变体
+ open_config: 打开变体配置
+ delete: 删除变体
+ upgrade_old_aj_model_loader:
+ name: 更新 .ajmodel
+ select_file: 选择 .ajmodel 文件
+ body: 升级旧版 .ajmodel 文件为新的 .ajblueprint 格式文件。
+ button: 选择需要更新的 .ajmodel 文件
+ open_bone_config:
+ name: 骨骼配置
+ open_text_display_config:
+ name: 文本展示配置
+ create_vanilla_item_display:
+ title: 添加物品展示实体
+ create_vanilla_block_display:
+ title: 添加方块展示实体
+ open_vanilla_item_display_config:
+ name: 物品展示配置
+ open_vanilla_block_display_config:
+ name: 方块展示配置
+ popup:
+ loading:
+ loading: Animated Java 加载中...
+ success: Animated Java 加载完成!
+ offline: |-
+ Animated Java 连接失败!
+ 部分功能将不可用。
+ installed_popup:
+ title: 感谢安装!
+ close_button: 开始制作动画吧!
+ incompatability_popup:
+ title: 检测到 Animated Java 不兼容
+ description: |-
+ 你安装了一些已知会与 Animated Java 产生问题的插件。
+ 请禁用或卸载以下插件,并重启 Blockbench 后再使用 Animated Java:
+ disable_button: 禁用插件
+ button:
+ disable_all: 禁用所有不兼容插件
+ ignore: 忽略并继续(不推荐)
+ plugin_dialog:
+ incompatability_notice: |-
+ 此插件已知会与 Animated Java 产生问题。
+
+ 安装了 Animated Java 时,将无法安装此插件。
+ dialog:
+ reset: 重置为默认值
+ about:
+ title: 关于 Animated Java
+ close_button: 关闭
+ changelog_dialog:
+ title: Animated Java 更新日志
+ unexpected_error:
+ title: 发生了意外错误!
+ close_button: 关闭
+ copy_error_message_button:
+ message: 已复制错误信息!
+ description: 点击保存错误信息到剪切板。
+ paragraph: '可加入我们的 %s,并在 #animated-java-support 频道创建讨论串来报告该错误信息,或在我们的 %s 创建 issue。谢谢!'
+ blueprint_settings:
+ title: 蓝图设置
+ pages:
+ general.title: 常规
+ resource_pack.title: 资源包
+ datapack.title: 数据包
+ rig.title: 骨架
+ event_functions.title: 事件函数
+ plugin.title: 插件
+ misc.title: 杂项
+ project_settings:
+ title: 项目
+ advanced_settings_warning: 仅在十分必要时使用高级设置!
+ project_name:
+ title: 名称
+ description: |-
+ 蓝图的文件名。
+
+ 如果用其他名称保存蓝图,该值会被覆盖。
+ texture_size:
+ title: 纹理尺寸
+ description: UV编辑器的分辨率,应当与最大的纹理尺寸一致。为达到更好的游戏效果,建议使用长宽相等且为2的幂次方的贴图。
+ warning:
+ does_not_match_largest_width: 纹理宽度与最大纹理宽度(%s)不一致,可能导致渲染问题。
+ does_not_match_largest_height: 纹理高度与最大纹理高度(%s)不一致,可能导致渲染问题。
+ not_square: 为达到最佳效果,纹理的长与宽应该相等。
+ not_a_power_of_2: 为达到最佳效果,纹理的尺寸应为2的幂次方。
+ does_not_match_largest_texture: 纹理的尺寸应当与最大纹理的尺寸一致。
+ export_settings:
+ title: 导出设置
+ blueprint_id:
+ title: 蓝图 ID
+ description: |-
+ 生成资源包和数据包时使用的 ID。
+
+ 建议使用唯一 ID,以避免与其他资源包和数据包冲突。
+ error:
+ empty_path: 蓝图 ID 路径不能为空。
+ minecraft_namespace: |-
+ [Markdown]
+ `minecraft` 命名空间为保留命名空间,不能使用。
+ animated_java_global_id: |-
+ [Markdown]
+ `animated_java:global` 是保留 ID,不能使用。
+ invalid_characters: 蓝图 ID 只能包含小写字母、数字和下划线。
+ show_render_box:
+ title: 显示渲染框
+ description: 启用后,渲染框会在编辑器中可见。
+ auto_render_box:
+ title: 自动调整渲染框大小
+ description: 启用后,会根据模型几何体自动计算渲染框。
+ render_box:
+ title: 渲染框尺寸
+ description: |-
+ [Markdown]
+ 骨架展示实体的[宽度和高度](https://minecraft.wiki/w/Display#Entity_data)。
+ view_range:
+ title: 可见范围
+ description: |-
+ [Markdown]
+ 骨架默认的[可见范围](https://minecraft.wiki/w/Display#Entity_data)。
+ enable_plugin_mode:
+ title: 插件模式
+ description: 是否开启插件模式,计划使用基于插件而非资源包/数据包的话启用该模式。
+ resource_pack_export_mode:
+ title: 资源包导出模式
+ options:
+ folder: 文件夹
+ zip: 压缩
+ none: 无
+ raw: 原始
+ description: |-
+ 资源包的导出方式。
+ 原始 - 资源包以文件夹的方式导出。
+ 压缩 - 资源包以 .zip 压缩包的方式导出。
+ 无 - 关闭对资源包的导出。
+ data_pack_export_mode:
+ title: 数据包导出模式
+ options:
+ folder:
+ title: 文件夹
+ description: 将数据包导出为文件夹,并自动与现有文件合并。
+ zip:
+ title: Zip
+ description: 将数据包导出为 .zip 文件。
+ none:
+ title: 无
+ description: 不导出数据包。
+ raw: 原始
+ description: |-
+ 数据包的导出方式。
+ 原始 - 数据包以文件夹的方式导出。
+ 压缩 - 数据包以 .zip 压缩包的方式导出。
+ 无 - 关闭对数据包的导出。
+ target_minecraft_version:
+ title: 目标 Minecraft 版本
+ description: |-
+ [Markdown]
+ 你面向的 Minecraft 版本,例如 `26.1.1` 或 `1.20.4`。
+
+ 该设置会影响可用功能以及导出的结构。
+ error:
+ invalid_version_format: |-
+ [Markdown]
+ 提供的 Minecraft 版本无效!Minecraft 版本格式应为 `major.minor.patch`,例如 `1.20.4` 或 `26.1.1`。
+ early_versions_not_supported: 不支持低于 1.20.4 的版本。
+ resource_pack_settings:
+ title: 资源包设置
+ enable_advanced_resource_pack_settings:
+ title: 高级设置
+ description: 是否启用资源包的高级设置。
+ enable_advanced_resource_pack_folders:
+ title: 高级文件夹
+ description: 是否启用高级资源包文件夹设置。
+ display_item:
+ title: 承载物
+ description: 游戏中用于显示蓝图内模型的物品。多个蓝图使用相同的物品时会被自动合并。
+ error:
+ no_item_selected: 未指定物品!
+ invalid_item_id:
+ no_namespace: 所提供的物品ID无效!物品ID的填写格式应为 命名空间:物品id 。
+ whitespace: 所提供的物品ID无效!物品ID不应包含空格。
+ item_model_not_found: 所选物品在原版资源包中不存在模型文件!
+ warning:
+ item_does_not_exist: 原版不存在所指定的物品!
+ item_model_not_generated: 所选物品并未使用 'minecraft:item/generated' 为父类。可能会导致游戏中的模型出现问题。
+ custom_model_data_offset:
+ title: CMD 偏位
+ description: 承载物的 Custom Model Data 所使用的偏移量。允许互相独立的资源包内的多个蓝图使用相同的物品为承载物。
+ resource_pack:
+ title: 资源包
+ description: 项目导出到的资源包的根文件夹。
+ warning:
+ no_assets: 所选文件夹不包含 assets 文件夹。
+ error:
+ empty: 资源包文件夹为必填项。
+ invalid_path: 路径解析失败:%s
+ does_not_exist: 所选路径不存在。
+ not_a_dir: 所选路径不是文件夹。
+ no_pack_mcmeta: 所选文件夹不包含 pack.mcmeta 文件。
+ no_folder_selected: 未指定文件夹!
+ folder_does_not_exist: 所指定的文件夹不存在!
+ not_a_folder: 所指定的路径并非文件夹!
+ missing_pack_mcmeta: 所指定的文件夹缺少 pack.mcmeta 文件!
+ missing_assets_folder: 所指定的资源包缺少 assets 文件夹!
+ data_pack_settings:
+ title: 数据包设置
+ data_pack:
+ folder:
+ title: 数据包
+ description: 数据包要导出到的文件夹。
+ warning:
+ no_data: 所选文件夹不包含 data 文件夹。
+ error:
+ empty: 数据包文件夹为必填项。
+ invalid_path: 路径解析失败:%s
+ does_not_exist: 所选路径不存在。
+ not_a_dir: 所选路径不是文件夹。
+ no_pack_mcmeta: 所选文件夹不包含 pack.mcmeta 文件。
+ zip:
+ title: 数据包
+ description: 数据包 zip 文件要导出到的位置。
+ error:
+ empty: 值不能为空!
+ invalid_path: 路径解析失败:%s
+ require_zip_extension: 所选文件必须以 .zip 扩展名结尾。
+ title: 数据包
+ description: 项目导出到的数据包的根文件夹。
+ error:
+ no_folder_selected: 未指定文件夹!
+ folder_does_not_exist: 所指定的文件夹不存在!
+ not_a_folder: 所指定的路径并非文件夹!
+ missing_pack_mcmeta: 所指定的文件夹缺少 pack.mcmeta 文件!
+ missing_data_folder: 所指定的数据包缺少 data 文件夹!
+ on_summon_function:
+ title: 召唤时函数
+ description: |-
+ [Markdown]
+ 召唤时以根实体身份并在根实体位置执行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_remove_function:
+ title: 移除时函数
+ description: |-
+ [Markdown]
+ 移除时以根实体身份并在根实体位置运行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_pre_tick_function:
+ title: Tick 前函数
+ description: |-
+ [Markdown]
+ 每 tick 在 Animated Java 自身 tick 逻辑*之前*,以根实体身份并在根实体位置运行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_post_tick_function:
+ title: Tick 后函数
+ description: |-
+ [Markdown]
+ 每 tick 在 Animated Java 自身 tick 逻辑*之后*,以根实体身份并在根实体位置运行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+ custom_rig_entity_tags:
+ title: 自定义骨架实体标签
+ description: |-
+ [Markdown]
+ 召唤时要添加到骨架中***每个实体***上的标签列表(用逗号分隔)。
+
+ 例如:`tag1, tag2, tag3`
+ interpolation_duration:
+ title: 插值时间
+ description: 以tick为单位,关键帧之间平滑过渡的持续时间。是模型过渡到下一个关键帧所用的时间。较高的插值时间将导致动画失去精度,因此该值一般建议为1或2。
+ teleportation_duration:
+ title: 传送时间
+ description: 以tick为单位,关键帧之间传送的持续时间。是模型在视觉上从上一个位置过渡到新位置所用的时间。较高的传送时间将导致动画失去精度。
+ use_entity_stacking:
+ title: 使用实体堆叠
+ description: |-
+ [Markdown]
+ 是否对骨架的骨骼实体使用实体堆叠。
+
+ **启用时**:
+ - 所有骨骼实体都会骑乘在根实体上。
+ - 骨骼旋转精度较低。(限制为整数角度)
+ - 以根实体身份执行命令时,可以通过 `on passengers` 选择骨骼实体。
+
+ **禁用时**:
+ - 骨骼实体不会骑乘在根实体上。
+ - 骨骼旋转会*精确得多*,在传送骨架时能实现更平滑的移动。
+ - 骨骼实体不能通过 `on passengers` 选择,但仍可通过 `as_node` 访问。
+ auto_update_rig_orientation:
+ title: 自动更新骨架朝向
+ description: |-
+ [Markdown]
+ 同步节点位置和旋转的开销***非常***高。
+ 此选项用于选择是否每 tick 自动让骨架的位置和旋转与根实体同步。
+
+ **启用时**,骨架会自动同步到根实体的位置和旋转。
+
+ **禁用时**,必须使用 move 函数来传送骨架:
+
+ ```
+ execute as @e[tag=aj._root] \
+ positioned rotated run \
+ function animated_java:/move
+ ```
+
+ 对于不需要每 tick 移动的骨架,建议**禁用**此选项。
+ animation_system:
+ title: 动画系统
+ description: 动画数据的存储方式。
+ options:
+ functions:
+ label: 函数
+ description: 将动画数据存储在函数文件中。性能最好,但会创建大量文件。
+ storage:
+ label: Storage
+ description: 将动画数据存储在命令 storage 中。速度较慢,但创建的文件更少。
+ baked_animations:
+ title: 烘培动画
+ description: |-
+ 是否烘培导出的动画。
+ 已烘培动画的帧会预先计算并存储在导出的 JSON 文件中,从而降低在游戏中渲染模型时的复杂性。
+ 部分插件可能需要开启这项以确保正常运作。
+ json_file:
+ title: JSON 文件
+ description: 项目导出到 JSON 文件的路径。
+ error:
+ invalid_path: 文件路径无效!
+ no_file_selected: 未指定文件!
+ not_a_file: 所指定的路径并非文件!
+ blueprint_name:
+ title: 蓝图名称
+ description: 蓝图的名称,仅用于识别工作区中的项目。
+ export_namespace:
+ title: 导出命名空间
+ description: 项目导出时所使用的名称,或者说是导出为资源包与数据包时所使用的命名空间。
+ error:
+ empty: 导出命名空间不能为空!
+ reserved: 导出命名空间 “{0}” 仅用于内部运作!请使用其他命名空间。
+ invalid_characters: 导出命名空间的名称包含了非法字符!请仅使用英文字符、数字和下划线。
+ show_bounding_box:
+ title: 显示边界框
+ description: 是否在编辑模式下显示边界框。
+ auto_bounding_box:
+ title: 自动边界框
+ description: |-
+ 是否根据模型的形状自动计算边界框。
+ 注:自动边界框并不会根据动画时骨骼的偏移进行计算,所以可能导致比预想中要小的情况。
+ bounding_box:
+ title: 边界框
+ description: 确定模型的剔除框,当该框离开屏幕时将停止该模型的渲染。
+ resource_pack_zip:
+ title: 压缩资源包
+ description: 项目导出到 .zip 文件的路径。
+ error:
+ no_file_selected: 未指定文件!
+ not_a_file: 所指定的路径并非文件!
+ display_item_path:
+ title: 承载物路径
+ description: 所有承载物的存放位置。应为资源包内到 .json 文件的路径。
+ advanced_resource_pack_file:
+ error:
+ no_file_selected: 未指定文件!
+ file_does_not_exist: 所指定的文件不存在!
+ not_a_file: 所指定的路径并非文件!
+ model_folder:
+ title: 模型文件夹
+ description: 所有导出模型的存放位置。应为资源包内到文件夹的路径。
+ advanced_resource_pack_folder:
+ error:
+ no_folder_selected: 未指定文件夹!
+ folder_does_not_exist: 所指定的文件夹不存在!
+ not_a_folder: 所指定的路径并非文件夹!
+ texture_folder:
+ title: 纹理文件夹
+ description: 所有导出纹理的存放位置。应为资源包内到文件夹的路径。
+ enable_advanced_data_pack_settings:
+ title: 启用高级设置
+ description: 是否启用数据包的高级设置。
+ data_pack_zip:
+ title: 压缩数据包
+ description: 项目导出到 .zip 文件的路径。
+ error:
+ no_file_selected: 未指定文件!
+ not_a_file: 所指定的路径并非文件!
+ summon_commands:
+ title: 进场命令
+ description: |-
+ 被 summon 时以根实体执行的命令。
+ 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
+ ticking_commands:
+ title: 高频命令
+ description: |-
+ 在根实体位置高频执行的命令。
+ 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
+ 该命令将在动画逻辑之后执行。
+ use_storage_for_animation:
+ title: 使用 storage 存储动画
+ description: |-
+ 是否使用 NBT storage 代替函数进行动画数据存储。
+ 将会大大减少数据包生成时的函数文件数量,相对的这会比使用函数存储的办法慢42%。
+ display_entity:
+ title: 展示实体 "%s" 的配置
+ node_options:
+ title: 节点
+ per_variant_options:
+ title: 按变体
+ on_summon_function:
+ title: 召唤时函数
+ description: |-
+ [Markdown]
+ 召唤时以该展示实体身份运行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_apply_function:
+ title: 应用时函数
+ description: |-
+ [Markdown]
+ 应用变体时以该展示实体身份运行的命令。
+
+ 请将此输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ custom_name:
+ title: 自定义名称
+ description: 节点的自定义名称。
+ invalid_json:
+ error: |-
+ 无效的 JSON 文本!
+ %s
+ custom_name_visible:
+ title: 自定义名称可见
+ description: 自定义名称是否始终可见。
+ glowing:
+ title: 发光
+ description: 节点在游戏中是否发光。
+ override_glow_color:
+ title: 覆盖发光颜色
+ description: 是否覆盖默认发光颜色。
+ glow_color:
+ title: 发光颜色
+ description: 发光的颜色。
+ shadow_radius:
+ title: 阴影半径
+ description: 阴影的半径。
+ shadow_strength:
+ title: 阴影强度
+ description: 阴影的强度。
+ override_brightness:
+ title: 覆盖亮度
+ description: 是否覆盖默认亮度。
+ brightness_override:
+ title: 亮度
+ description: 节点的亮度。该值应在 0 到 15 之间。
+ use_custom_brightness:
+ title: 使用自定义亮度
+ description: 是否为节点启用自定义亮度覆盖。
+ custom_brightness:
+ title: 自定义亮度
+ description: 节点的自定义亮度。该值应在 0 到 15 之间。
+ enchanted:
+ title: 附魔效果
+ description: 节点是否显示附魔效果。
+ invisible:
+ title: 隐形
+ description: 节点是否隐形。
+ billboard:
+ title: 广告牌
+ description: |-
+ [Markdown]
+ 控制此节点模型在渲染时是否转向玩家。
+ - **固定**:垂直和水平角度均固定。
+ - **垂直**:围绕垂直轴旋转。
+ - **水平**:围绕水平轴旋转。
+ - **中心**:围绕中心点旋转。
+ options:
+ fixed: 固定
+ vertical: 垂直
+ horizontal: 水平
+ center: 中心
+ locator_config:
+ title: 定位器配置
+ plugin_mode_warning: |-
+ 插件模式已启用!该模式下没有定位器配置。
+ 请使用插件API为定位器添加自定义功能。
+ 详情请查看官方的插件API文档。
+ use_entity:
+ title: 使用实体
+ description: 是否在定位器处附加跟随实体。
+ entity_type:
+ title: 实体类型
+ description: 附加到定位器的实体类型。
+ error:
+ empty: 实体类型不能为空!
+ warning:
+ invalid: 所选的实体类型 %s 不存在于 Minecraft 中。
+ sync_passenger_rotation:
+ title: 同步乘客旋转
+ description: 自动同步定位器乘客实体的旋转。
+ on_summon_function:
+ title: 召唤时函数
+ description: |-
+ [Markdown]
+ 骨架被召唤时,以根实体身份并在定位器位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ description_with_use_entity: |-
+ [Markdown]
+ 召唤时以定位器实体身份并在其位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_remove_function:
+ title: 移除时函数
+ description: |-
+ [Markdown]
+ 骨架被移除时,以根实体身份并在定位器位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ description_with_use_entity: |-
+ [Markdown]
+ 移除时以定位器实体身份并在其位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_tick_function:
+ title: Tick 函数
+ description: |-
+ [Markdown]
+ 每 tick 在定位器位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ description_with_use_entity: |-
+ [Markdown]
+ 每 tick 以定位器实体身份并在其位置运行的命令。
+ summon_commands:
+ title: 进场命令
+ description: |-
+ 被 summon 时以定位器实体执行的命令。
+ 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
+ ticking_commands:
+ title: 高频命令
+ description: |-
+ 在定位器位置高频执行的命令。
+ 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
+ interaction_config:
+ title: 交互实体配置
+ plugin_mode_warning: |-
+ 插件模式已启用!交互实体在插件模式下没有配置项。
+ 请改用插件 API 为交互实体添加自定义功能。
+ 更多信息请参考官方插件 API 文档。
+ response:
+ title: 响应
+ description: 与此交互实体交互时是否触发响应(手部动画 / 攻击音效)。
+ on_summon_function:
+ title: 召唤时函数
+ description: |-
+ [Markdown]
+ 召唤时以此交互实体身份并在其位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_interaction_function:
+ title: 交互时函数
+ description: |-
+ [Markdown]
+ 玩家与此交互实体交互时,以此交互实体身份并在其位置运行的命令。
+
+ 你可以使用 `execute on target` 选择发起交互的玩家。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_attack_function:
+ title: 攻击时函数
+ description: |-
+ [Markdown]
+ 玩家攻击此交互实体时,以此交互实体身份并在其位置运行的命令。
+
+ 你可以使用 `execute on attacker` 选择发起攻击的玩家。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_remove_function:
+ title: 移除时函数
+ description: |-
+ [Markdown]
+ 移除此交互实体时,以此交互实体身份并在其位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ on_tick_function:
+ title: Tick 函数
+ description: |-
+ [Markdown]
+ 每 tick 以此交互实体身份并在其位置运行的命令。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ text_display_config:
+ title: 文本展示配置
+ use_nbt:
+ title: 使用 NBT
+ description: 是否使用 NBT 取代设置进行文本展示配置。
+ use_nbt_warning: 使用 NBT 将会覆盖所有其他设置,任何更改也不会在编辑器中可见。仅在熟悉此功能的情况下使用!
+ inherit_settings:
+ title: 继承设置
+ description: 是否从父级文本展示继承设置。
+ glowing:
+ title: 发光
+ description: 文本展示是否在游戏中发光。
+ override_glow_color:
+ title: 修改发光颜色
+ description: 是否改变发光的默认颜色。
+ glow_color:
+ title: 发光颜色
+ description: 发光的颜色
+ shadow_radius:
+ title: 阴影半径
+ description: 阴影的半径。
+ shadow_strength:
+ title: 阴影强度
+ description: 阴影的强度
+ override_brightness:
+ title: 修改亮度
+ description: 是否修改亮度的默认等级。
+ brightness_override:
+ title: 亮度
+ description: 文本展示的亮度。取值范围为0至15之间。
+ use_custom_brightness:
+ title: 使用自定义亮度
+ description: 骨骼是否启用自定义亮度。
+ custom_brightness:
+ title: 自定义亮度
+ description: 骨骼的自定义亮度。取值范围为0至15之间。
+ invisible:
+ title: 隐形
+ description: 文本展示是否隐形。
+ nbt:
+ title: NBT
+ description: 应用到文本展示的NBT。
+ billboard:
+ title: 广告牌
+ options:
+ fixed: 固定
+ vertical: 垂直
+ horizontal: 水平
+ center: 中心
+ bone_config:
+ vanilla_item_model:
+ title: 原版物品模型
+ description: |-
+ 若设置,此骨骼将显示为原版物品模型。
+ 并且覆盖骨骼现有的方块。
+ title: 骨骼配置
+ selected_variant: “已选变体:{0}”
+ default_variant_subtitle: 以下设置将默认应用到该骨骼。
+ selected_variant_subtitle: 以下设置仅在应用该变体时应用到该骨骼。
+ use_nbt:
+ title: 使用 NBT
+ description: 是否使用 NBT 取代设置进行骨骼配置。
+ use_nbt_warning: 使用 NBT 将会覆盖所有其他设置,任何更改也不会在编辑器中可见。仅在熟悉此功能的情况下使用!
+ inherit_settings:
+ title: 继承设置
+ description: 是否从父级骨骼继承设置。
+ custom_name:
+ title: 自定义名称
+ description: 骨骼的自定义名称。
+ invalid_json:
+ error: |-
+ 无效的 JSON 文本!
+ {0}
+ custom_name_visible:
+ title: 自定义名称可见性
+ description: 自定义名称是否总是可见。
+ glowing:
+ title: 发光
+ description: 骨骼是否在游戏中发光。
+ override_glow_color:
+ title: 修改发光颜色
+ description: 是否改变发光的默认颜色。
+ glow_color:
+ title: 发光颜色
+ description: 发光的颜色。
+ shadow_radius:
+ title: 阴影半径
+ description: 阴影的半径。
+ shadow_strength:
+ title: 阴影强度
+ description: 阴影的强度。
+ override_brightness:
+ title: 修改亮度
+ description: 是否修改亮度的默认等级。
+ brightness_override:
+ title: 亮度
+ description: 骨骼的亮度。取值范围为0至15之间。
+ use_custom_brightness:
+ title: 使用自定义亮度
+ description: 骨骼是否启用自定义亮度。
+ custom_brightness:
+ title: 自定义亮度
+ description: 骨骼的自定义亮度。取值范围为0至15之间。
+ enchanted:
+ title: 附魔
+ description: 骨骼是否为附魔状态效果。
+ invisible:
+ title: 隐形
+ description: 骨骼是否隐形。
+ nbt:
+ title: NBT
+ description: 应用到骨骼的NBT。
+ invalid_nbt:
+ not_compound: 填写的NBT应为复合标签!例:{CustomName:'"你的名字"'}
+ error: |-
+ 无效的NBT!
+ {0}
+ billboard:
+ title: 广告牌
+ description: 控制此骨骼在渲染时如何旋转以面向玩家。可设置为固定(固定垂直和水平轴,无旋转),垂直(固定垂直轴),水平(固定水平轴),和中心(按照中心旋转跟随玩家视角)。
+ options:
+ fixed: 固定
+ vertical: 垂直
+ horizontal: 水平
+ center: 中心
+ vanilla_block_display_config:
+ title: 方块展示配置
+ vanilla_block_display:
+ custom_name:
+ title: 自定义名称
+ description: 方块展示的自定义名称。
+ invalid_json:
+ error: |-
+ 无效的 JSON 文本!
+ %s
+ custom_name_visible:
+ title: 自定义名称可见性
+ description: 自定义名称是否总是可见。
+ vanilla_item_display_config:
+ title: 物品展示配置
+ vanilla_item_display:
+ custom_name:
+ title: 自定义名称
+ description: 物品展示的自定义名称。
+ invalid_json:
+ error: |-
+ 无效的 JSON 文本!
+ %s
+ custom_name_visible:
+ title: 自定义名称可见性
+ description: 自定义名称是否总是可见。
+ variant_config:
+ title: 变体配置
+ variant_display_name: 显示名称
+ variant_display_name.description: 用于在编辑器和错误信息中识别变体。
+ generate_name_from_display_name: 生成显示名称
+ generate_name_from_display_name.description: 是否根据显示名称自动生成名称。
+ variant_name: 名称
+ variant_name.description: 用于在导出的资源包和数据包中识别变体。
+ texture_map:
+ title: 纹理映射
+ description: 应用该变体时所使用的贴图。
+ create_new_mapping: 创建新的映射
+ no_mappings: 变体不存在映射的纹理。
+ bone_lists:
+ description: 包含或排除在变体的节点列表。包含的节点将会被变体修改,排除的节点则会被节点忽略。
+ excluded_nodes:
+ title: 排除节点
+ description: 排除在变体的节点列表。该节点将不会被变体修改。
+ included_nodes:
+ title: 包含节点
+ description: 包含在变体的节点列表。该节点将会被变体修改。
+ swap_columns_button:
+ tooltip: 互换列表
+ upgrade_old_aj_model_loader:
+ title: 更新 .ajmodel
+ animation_properties:
+ title: 动画属性 (%s)
+ animation_name:
+ title: 动画名称
+ description: 动画的名称。
+ error:
+ empty: 动画名称不能为空!
+ invalid_characters: 动画的名称包含了非法字符!请仅使用英文字符、数字、下划线和英文点号。
+ loop_mode:
+ title: 循环模式
+ description: 动画的循环方式。单次 - 动画播放一次后停止。停留 - 动画播放一次后停留在最后帧。循环 - 动画重复播放。
+ options:
+ once: 单次
+ hold: 停留
+ loop: 循环
+ loop_delay:
+ title: 循环延迟
+ description: 循环的延迟。动画结束后到再次播放的间隔时间。仅在循环模式为循环时适用。
+ bone_lists:
+ description: 包含或排除在动画中的节点列表。包含的节点将会被动画修改,排除的节点则会被动画忽略。
+ excluded_nodes:
+ title: 排除节点
+ description: 排除在动画中的节点列表。该节点将不会被动画修改。
+ included_nodes:
+ title: 包含节点
+ description: 包含在动画中的节点列表。该节点将会被动画修改。
+ swap_columns_button:
+ tooltip: 互换列表
+ export_progress:
+ title: 导出中...
+ blueprint_loading:
+ title: 蓝图加载中...
+ installed_popup:
+ title: 感谢安装!
+ close_button: 是时候动起来了!
+ panel:
+ variants:
+ title: 变体
+ tool:
+ create_new_variant: 创建新变体
+ edit_variant: 编辑变体
+ duplicate_selected_variant: 复制所选变体
+ delete_selected_variant: 删除所选变体
+ variant_visible: 已选择变体
+ variant_not_visible: 未选择变体
+ cannot_delete_default_variant: 不可删除默认变体!
+ cannot_edit_default_variant: 不能编辑默认变体!
+ keyframe:
+ keyframe_title: 关键帧 (%s)
+ variant:
+ title: 变体
+ description: 应用到关键帧的变体。
+ function:
+ title: 函数
+ description: |-
+ [Markdown]
+ 到达关键帧时运行的命令。
+
+ 请将此文本输入视为 `.mcfunction` 文件。
+
+ 支持 [MC-Build](https://mcbuild.dev) 语法。
+ execute_condition:
+ title: 执行条件
+ description: 关键帧执行命令时必须满足的条件。可视输入的文本为 execute 命令的子命令。
+ repeat:
+ title: 重复执行?
+ description: |-
+ 是否在该关键帧重复执行命令。
+ 若启用,则会以相应的频率执行命令。
+ repeat_frequency:
+ title: 重复频率
+ description: |-
+ 该关键帧再次执行命令的等待刻数。
+ 设置为1则在每一刻执行一次命令。
+ 注意:若动画不能被该值整除,则动画循环时间隔可能会略有偏差。
+ 若设值大于动画的长度,命令则会视为重复被禁止时执行。(到达关键帧时执行一次)
+ easing_type:
+ title: 缓动类型
+ description: 应用到关键帧的缓动类型。
+ options:
+ linear: 线性
+ sine: 正弦
+ quad: 二次方
+ cubic: 三次方
+ quart: 四次方
+ quint: 五次方
+ expo: 指数
+ circ: 圆
+ elastic: 弹性
+ back: 后视图
+ bounce: 弹跳
+ easing_mode:
+ title: 缓动模式
+ description: 应用到关键帧的缓动模式。
+ options:
+ in: 淡入
+ out: 淡出
+ inout: 淡入淡出
+ easing_args:
+ title: 缓动参数
+ description: 缓动函数使用的参数。
+ easing_arg:
+ elastic:
+ title: 弹性
+ description: 缓动函数的弹性。
+ back:
+ title: 过冲
+ description: 缓动函数中的过冲次数。
+ bounce:
+ title: 弹跳
+ description: 缓动函数的弹跳性。
+ nonlinear_interpolation: |-
+ 当前禁用高级缓动设置。
+ 修改关键帧的插值模式为“线性”以启用。
+ commands:
+ title: 命令
+ description: |-
+ 到达目标关键帧时运行的命令。
+ 该位置的文本输入可视为在 .mcfunction 中运行。(也支持 MC-Build 语法!)
+ text_display:
+ title: 文本展示
+ vanilla_item_display:
+ title: 显示物品
+ description: 所显示的物品。
+ vanilla_block_display:
+ title: 显示方块
+ description: 所显示的方块。支持输入方块状态!
+ animation:
+ excluded_nodes: 排除节点
+ invert_excluded_nodes: 反转排除节点
+ effect_animator:
+ timeline:
+ variant: 变体
+ function: 函数
+ commands: 命令
+ keyframe_data_point:
+ variant: 变体
+ function: 函数
+ execute_condition: 执行条件
+ repeat: 重复
+ repeat_frequency: 重复频率
+ keyframes:
+ variant: 变体
+ commands: 命令
+ execute_condition: 执行条件
+ tool:
+ text_display:
+ line_width:
+ title: 行宽
+ description: 文本展示的行宽,以像素为单位。
+ background_color:
+ title: 背景颜色
+ description: 文本展示的背景颜色。
+ text_shadow:
+ title: 文字阴影
+ description: 是否在显示的文字后面显示阴影。
+ text_alignment:
+ title: 文本对齐
+ description: 文本的对齐方式。
+ options:
+ left: 靠左
+ center: 居中
+ right: 靠右
+ see_through:
+ title: 穿透
+ description: 文本展示是否在方块后依然可见。
+ copy_text:
+ title: 复制导出的文本组件
+ description: 将导出的文本组件复制到剪贴板。
+ copied: 文本已复制到剪贴板!
+ clipboard_module_access_request: Animated Java 需要访问剪贴板 API,才能将导出的文本组件复制到剪贴板。
+ clipboard_module_access_denied: 剪贴板 API 访问被拒绝。请授予访问权限后重试。
+ failed: 无法将文本复制到剪贴板。
+ item_display:
+ item_display:
+ title: 物品显示模式
+ description: 应用到物品模型的变化。(参考自 JSON 模型中 display 的定义)
+ options:
+ none: 无
+ thirdperson_lefthand: 第三人称左手
+ thirdperson_righthand: 第三人称右手
+ firstperson_lefthand: 第一人称左手
+ firstperson_righthand: 第一人称右手
+ head: 头部
+ gui: GUI
+ ground: 地面
+ fixed: 固定
+ vanilla_item_display:
+ title: 原版物品展示
+ misc:
+ failed_to_export:
+ title: 导出失败
+ custom_models:
+ message: 当前设置为不导出资源包,但项目中存在自定义模型!请启用资源包导出或移除自定义模型后再次尝试导出。
+ blueprint_settings:
+ message: 蓝图设置存在错误!请在修正后再次尝试导出。
+ error_item: 发现 %s 存在问题:
+ button: 好
+ invalid_rotation:
+ message: |-
+ 模型中有一些立方体的旋转无效!
+
+ 面向 Minecraft 1.21.6 之前的版本时,立方体旋转只能为 -45、-22.5、0、22.5 或 45 度,并且每次只能沿单个轴旋转。
+
+ 如果你想更精确地旋转立方体,或沿多个轴旋转,请将它放入骨骼中并改为旋转骨骼。
+
+ 所有旋转无效的立方体都会在编辑器中以红色描边。请在导出前修正它们。
+ message_post_1_21_6: |-
+ 模型中有一些立方体的旋转无效!
+
+ 面向 Minecraft 1.21.6 到 1.21.10 时,立方体每次只能沿单个轴旋转。
+
+ 如果你想沿多个轴旋转立方体,请将它放入骨骼中并改为旋转骨骼。
+
+ 所有旋转无效的立方体都会在编辑器中以红色描边。请在导出前修正它们。
+ rig_has_textures_but_no_custom_models:
+ message: |-
+ 你的模型应用了纹理,但没有可使用这些纹理的自定义模型(立方体)!
+ 请创建一些立方体来使用这些纹理,或在导出前移除这些纹理。
+ rig_has_custom_models_but_no_textures:
+ message: |-
+ 你的模型中有自定义模型(立方体),但没有给它们应用纹理!
+ 请为立方体应用纹理,或在导出前移除这些立方体。
+ toast:
+ invalid_rotations: |-
+ 立方体旋转无效!
+
+ 立方体旋转必须为:45、22.5、0、-22.5 或 -45,并且每次只能沿单个轴旋转。
+
+ 所有旋转无效的立方体都会以红色描边。
+ invalid_rotations_post_1_21_6: |-
+ 立方体旋转无效!
+
+ 面向 Minecraft 1.21.6 或更高版本时,立方体每次只能沿单个轴旋转。
+
+ 所有旋转无效的立方体都会以红色描边。
+ format_category:
+ animated_java: Animated Java
+ block_model_manager:
+ fluid_warning: 方块展示下不会渲染流体。
+ mob_head_warning: 方块展示下不会渲染生物头颅,请改用物品展示。
+ facing_warning: 方块展示不支持方块状态 “facing”。
+ error:
+ blueprint_export_path_doesnt_exist:
+ title: 蓝图导出路径不存在
+ description: |-
+ [Markdown]
+ 导出路径 '%s' 不存在!
+
+ 请确认要保存到的文件夹存在后重试。
diff --git a/src/mods/cameraPlugin.ts b/src/mods/cameraPlugin.ts
index 32cda3bd..f4f524ee 100644
--- a/src/mods/cameraPlugin.ts
+++ b/src/mods/cameraPlugin.ts
@@ -26,11 +26,50 @@ registerPluginPatch({
return originalSanitize.call(this)
}
- return { camera, originalRename, originalSanitize }
+ // @ts-expect-error
+ const cameraAnimator = OutlinerElement.types.camera.animator as BoneAnimator
+ const originalDisplayRotation = cameraAnimator.prototype.displayRotation
+ cameraAnimator.prototype.displayRotation = function (
+ this: BoneAnimator,
+ arr?: ArrayVector3 | ArrayVector4,
+ multiplier?: number
+ ) {
+ originalDisplayRotation.call(this, arr, multiplier)
+ if (activeProjectIsBlueprintFormat()) {
+ console.log('Sanitizing camera rotation for blueprint format')
+ const mesh = this.getElement().mesh as THREE.Mesh
+ // Cameras in AJ don't support roll.
+ mesh.updateMatrixWorld(true)
+ mesh.getWorldQuaternion(Reusable.quat1)
+
+ const euler = Reusable.euler1.setFromQuaternion(Reusable.quat1, 'YXZ')
+ euler.z = 0 // Remove roll
+
+ const noRollWorldQuat = Reusable.quat1.setFromEuler(euler)
+
+ if (mesh.parent) {
+ const parentWorldQuat = Reusable.quat2
+ mesh.parent.getWorldQuaternion(parentWorldQuat)
+ noRollWorldQuat.premultiply(parentWorldQuat.invert())
+ }
+
+ mesh.setRotationFromQuaternion(noRollWorldQuat)
+ mesh.updateMatrix()
+ }
+ }
+
+ return { camera, originalRename, originalSanitize, cameraAnimator, originalDisplayRotation }
},
- revert: ({ camera, originalRename, originalSanitize }) => {
+ revert: ({
+ camera,
+ originalRename,
+ originalSanitize,
+ cameraAnimator,
+ originalDisplayRotation,
+ }) => {
camera.prototype.rename = originalRename
camera.prototype.sanitizeName = originalSanitize
+ cameraAnimator.prototype.displayRotation = originalDisplayRotation
},
})
diff --git a/src/mods/entityCounter.ts b/src/mods/entityCounter.ts
new file mode 100644
index 00000000..54a482ec
--- /dev/null
+++ b/src/mods/entityCounter.ts
@@ -0,0 +1,53 @@
+import { registerDeletableHandlerPatch, registerPatch } from 'blockbench-patch-manager'
+import { openEntityCountDialog } from '../dialogs/entityCount/entityCount'
+import { Interaction } from '../outliner/interaction'
+import { TextDisplay } from '../outliner/textDisplay'
+import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay'
+import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay'
+
+export function getTotalEntityCount() {
+ let count = 1 // Start with 1 for the root entity
+ for (const node of Outliner.nodes) {
+ switch (true) {
+ case node instanceof Group:
+ if (node.children.some(child => child instanceof Cube)) count++
+ break
+ case node instanceof TextDisplay:
+ case node instanceof VanillaBlockDisplay:
+ case node instanceof VanillaItemDisplay:
+ case node instanceof Interaction:
+ case node.type === 'camera':
+ count++
+ }
+ }
+ return count
+}
+
+registerDeletableHandlerPatch({
+ id: `animated_java:toolbar/entity_counter`,
+ create() {
+ const widget = new BarText(`animated_java:toolbar/entity_counter`, {
+ text: `E: 0`,
+ onUpdate() {
+ widget.set(`E: ${getTotalEntityCount()}`)
+ },
+ click: openEntityCountDialog,
+ })
+ Toolbars.outliner.add(widget) // Add the widget to the outliner toolbar
+ return widget
+ },
+})
+
+registerPatch({
+ id: `animated_java:toolbar/entity_counter/updateEvent`,
+ apply() {
+ const triggerUpdate = () => {
+ ;(BarItems[`animated_java:toolbar/entity_counter`] as BarText).onUpdate()
+ }
+ Blockbench.on('update_selection', triggerUpdate)
+ return { triggerUpdate }
+ },
+ revert({ triggerUpdate }) {
+ Blockbench.removeListener('update_selection', triggerUpdate)
+ },
+})
diff --git a/src/nodeConfigs.ts b/src/nodeConfigs.ts
index 25ad2f57..8f7e231a 100644
--- a/src/nodeConfigs.ts
+++ b/src/nodeConfigs.ts
@@ -147,7 +147,51 @@ export class DisplayEntityConfig {
this.__shadowStrength = value
}
- checkIfEqual(other: DisplayEntityConfig) {
+ checkIfEqual(other: DisplayEntityConfig, ignoreUndefined = false): boolean {
+ if (ignoreUndefined) {
+ if (
+ this.__onApplyFunction !== undefined &&
+ this.__onApplyFunction !== other.__onApplyFunction
+ )
+ return false
+ if (this.__billboard !== undefined && this.__billboard !== other.__billboard)
+ return false
+ if (
+ this.__overrideBrightness !== undefined &&
+ this.__overrideBrightness !== other.__overrideBrightness
+ )
+ return false
+ if (
+ this.__skyBrightness !== undefined &&
+ this.__skyBrightness !== other.__skyBrightness
+ )
+ return false
+ if (
+ this.__blockBrightness !== undefined &&
+ this.__blockBrightness !== other.__blockBrightness
+ )
+ return false
+ if (this.__enchanted !== undefined && this.__enchanted !== other.__enchanted)
+ return false
+ if (this.__glowing !== undefined && this.__glowing !== other.__glowing) return false
+ if (
+ this.__overrideGlowColor !== undefined &&
+ this.__overrideGlowColor !== other.__overrideGlowColor
+ )
+ return false
+ if (this.__glowColor !== undefined && this.__glowColor !== other.__glowColor)
+ return false
+ if (this.__invisible !== undefined && this.__invisible !== other.__invisible)
+ return false
+ if (this.__shadowRadius !== undefined && this.__shadowRadius !== other.__shadowRadius)
+ return false
+ if (
+ this.__shadowStrength !== undefined &&
+ this.__shadowStrength !== other.__shadowStrength
+ )
+ return false
+ return true
+ }
return (
this.__onApplyFunction === other.__onApplyFunction &&
this.__billboard === other.__billboard &&
@@ -165,7 +209,7 @@ export class DisplayEntityConfig {
}
isDefault(): boolean {
- return this.checkIfEqual(DisplayEntityConfig.getDefault())
+ return this.checkIfEqual(DisplayEntityConfig.getDefault(), true)
}
toJSON(): IBlueprintDisplayEntityConfigJSON {
diff --git a/src/panels/customKeyframe/commandsKeyframe.svelte b/src/panels/customKeyframe/commandsKeyframe.svelte
index 057f9669..29575698 100644
--- a/src/panels/customKeyframe/commandsKeyframe.svelte
+++ b/src/panels/customKeyframe/commandsKeyframe.svelte
@@ -28,7 +28,7 @@
>
{translate('panel.keyframe.function.title')}
-
+
@@ -40,7 +40,11 @@
>
{translate('panel.keyframe.execute_condition.title')}
-
+
diff --git a/src/panels/customKeyframe/variantKeyframe.svelte b/src/panels/customKeyframe/variantKeyframe.svelte
index 8e9c394d..84ede48b 100644
--- a/src/panels/customKeyframe/variantKeyframe.svelte
+++ b/src/panels/customKeyframe/variantKeyframe.svelte
@@ -60,7 +60,11 @@
>
{translate('panel.keyframe.execute_condition.title')}
-
+
diff --git a/src/panels/easings/easings.ts b/src/panels/easings/easings.ts
new file mode 100644
index 00000000..70584cec
--- /dev/null
+++ b/src/panels/easings/easings.ts
@@ -0,0 +1,30 @@
+import { SveltePanel } from 'svelte-patching-tools/blockbench'
+import { BLUEPRINT_FORMAT_ID } from '../../formats/blueprint'
+import { createScopedTranslator } from '../../util/lang'
+import EasingsPanelComponent from './easings.svelte'
+
+const localize = createScopedTranslator('animated_java.panel.easings')
+
+export const EASINGS_PANEL = new SveltePanel({
+ id: `animated_java:panel/easings`,
+ name: localize('title'),
+ component: EasingsPanelComponent,
+ expand_button: false,
+ icon: 'timeline',
+ condition: {
+ formats: [BLUEPRINT_FORMAT_ID],
+ modes: [Modes.options.animate.id],
+ },
+ default_position: {
+ slot: 'left_bar',
+ folded: false,
+ float_position: [0, 0],
+ float_size: [200, 200],
+ height: 200,
+ // @ts-expect-error - Missing types
+ attached_to: 'transform',
+ attached_index: 1,
+ sidebar_index: 2,
+ },
+ default_side: 'left',
+})
diff --git a/src/pluginPackage/changelog.json b/src/pluginPackage/changelog.json
index be84e196..67477465 100644
--- a/src/pluginPackage/changelog.json
+++ b/src/pluginPackage/changelog.json
@@ -754,5 +754,103 @@
]
}
]
+ },
+ "1.10.0": {
+ "title": "v1.10.0",
+ "author": "Titus Evans (SnaveSutit)",
+ "date": "2026-06-24",
+ "categories": [
+ {
+ "title": "Changes",
+ "list": [
+ "[BREAKING] Removed `data` entity in favor of much faster score-indexed storage. Thanks @Xanbelor!",
+ "[BREAKING] Added support for custom namespaces. The `Export Namespace` setting has been renamed to `Blueprint ID` and now supports namespaces. E.g. `my_cool_pack:rigs/my_awesome_blueprint`. Tags will also use the `Blueprint ID` as their prefix: `aj.my_rig.root` -> `my_cool_pack.rigs.my_awesome_blueprint`. The default namespace is now `aj:` to keep tags prefixes consistent with previous versions.",
+ "[BREAKING] `as_locator` & `as_all_locators` no longer execute `at` the targeted locators.",
+
+ "Added Interaction element",
+ "Added functions for interacting with Interactions, similar to the locator functions, such as `as_interaction` and `as_all_interactions`.",
+ "Split Display Entity config option `brightnessOverride` into two separate properties: `blockBrightness` and `skyBrightness`.",
+ "Added marker color support to Display entity elements.",
+ "Made `Use Entity Stacking` default to true.",
+ "Added `Item Model Config` to Groups with cube children.",
+ "Added support for adding custom constant tints to item models.",
+
+ "Added support for Minecraft 26.2 predicate changes.",
+ "Added syntax highlighting to function keyframes, and keyframe execute conditions.",
+ "Added Entity Counter dialog and toolbar element.",
+ "Added `zh_cn` localization - Thanks jiuwu02!",
+ "Disabled camera roll in Blockbench preview to better match in-game behavior.",
+ "Moved keyframe easings into their own panel, and improved reactivity.",
+
+ "Added `Rig` page to Blueprint settings.",
+ "Added `Custom Rig Entity Tags` option to Blueprint settings.",
+ "Added `Use Entity Stacking` option to Blueprint settings.",
+
+ "Overhauled Blueprint Settings UI to be more user friendly and easier to navigate.",
+ "Added `.zip` export format for Data Pack and Resource Pack.",
+ "Added visualization for Structure Groups.",
+ "Added syntax highlighting for settings.",
+ "Updated Plugin exporter to support modern Minecraft's free element rotations.",
+ "Added translation string and keybind resolution for JSON text components. Translation strings from vanilla will automatically be resolved to the correct text for your target version when previewing Text Displays.",
+ "Added display names to all entities in the rig to make debugging easier.",
+ "Switched to using `blockbench-patch-manager` for safer handling of Blockbench monkey-patches.",
+ "Moved JSON text component system into its own library called `book-and-quill`.",
+ "Improved MC font loading handling and caching.",
+ "Improved internal MC asset handling and caching.",
+ "Updated to Blockbench 5.1.4.",
+ "Updated to Svelte 5.",
+ "Updated to MCB 4.0.5.",
+
+ "Updated Russian translations to improve consistency and punctuation. - Thanks Koishem!",
+ "Re-enabled Plugin mode - WARNING: This is experimental, and is using a new plugin JSON schema that will likely change in the future. - Thanks Meekiavelique!",
+ "Added Bisect hosting partnership banner.",
+
+ "Improved error messages for `as_all_locators` and similar functions.",
+
+ "Added `as_at_locator` & `as_at_all_locators` functions to replace previous `as_locator` and `as_all_locators` positional functionality.",
+ "Made camera and rig rotation much more precise for 1.21.4 and above.",
+ "Added support for the removal of item model rotation restrictions in 1.21.11.",
+ "Added `as_node` function."
+ ]
+ },
+ {
+ "title": "Fixes",
+ "list": [
+ "Fixed interactions being left behind when moving a Rig.",
+ "Fixed [#515](https://github.com/Animated-Java/animated-java/issues/515)",
+ "Fixed [#505](https://github.com/Animated-Java/animated-java/issues/505)",
+ "Fixed [#506](https://github.com/Animated-Java/animated-java/issues/506)",
+ "Fixed [#516](https://github.com/Animated-Java/animated-java/issues/516)",
+ "Fixed [#512](https://github.com/Animated-Java/animated-java/issues/512)",
+ "Fixed [#514](https://github.com/Animated-Java/animated-java/issues/514)",
+
+ "Fixed Item Display, Block Display, and Text Display elements ignoring flip actions.",
+ "Fixed custom element size not being rendered in element panel.",
+ "Fixed [#509](https://github.com/Animated-Java/animated-java/issues/509)",
+ "Fixed [#507](https://github.com/Animated-Java/animated-java/issues/507)",
+ "Fixed [#510](https://github.com/Animated-Java/animated-java/issues/510)",
+ "Fixed [#505](https://github.com/Animated-Java/animated-java/issues/505)",
+ "Fixed [#495](https://github.com/Animated-Java/animated-java/issues/495)",
+
+ "Fixed Blueprints failing to save due to missing `fs` global.",
+ "Fixed Animated Java failing to reload correctly when plugins are reloaded.",
+ "Fixed JSON Text Components failing to add quotes around strings that start with numeric characters.",
+ "Fixed [#491](https://github.com/Animated-Java/animated-java/issues/491)",
+ "Fixed [#490](https://github.com/Animated-Java/animated-java/issues/490)",
+
+ "Fixed \"infinite loading popup\" issue.",
+
+ "Fixed locators failing to tick, and display entities being left behind when moving the root (either by `tp` or `move` function).",
+
+ "Fixed [#475](https://github.com/Animated-Java/animated-java/issues/475)",
+ "Fixed [#474](https://github.com/Animated-Java/animated-java/issues/474)",
+ "Fixed [#472](https://github.com/Animated-Java/animated-java/issues/472)",
+ "Fixed [#471](https://github.com/Animated-Java/animated-java/issues/471)",
+ "Fixed [#469](https://github.com/Animated-Java/animated-java/issues/469)",
+ "Fixed [#468](https://github.com/Animated-Java/animated-java/issues/468)",
+ "Fixed `move` function not transforming floating entities."
+ ]
+ }
+ ]
}
}
diff --git a/src/prism/mcfunctionPrism.ts b/src/prism/mcfunctionPrism.ts
index d2889ec2..b015f737 100644
--- a/src/prism/mcfunctionPrism.ts
+++ b/src/prism/mcfunctionPrism.ts
@@ -1,4 +1,3 @@
-// @ts-expect-error - Broken BB types
Prism.languages.mcfunction = {
// Multi-line documentation style comment blocks beginning with #>, #!, or ##.
'comment-block': {
@@ -17,9 +16,9 @@ Prism.languages.mcfunction = {
},
},
- // Regular comments (line start and inline).
+ // Regular comments (line start only).
comment: {
- pattern: /#.*/,
+ pattern: /^\s*#(?![>!#]).*/m,
greedy: true,
},
@@ -79,7 +78,11 @@ Prism.languages.mcfunction = {
number: [/[+-]?\d*\.?\d+(?:[eE][+-]?\d+)?[df]?\b/, /[+-]?\d+(?:[bBlLsS])?\b/],
- range: /\.\./,
+ // Int Range: ..0, 0.., or 0..1
+ range: {
+ pattern: /\b\d*\.\.\d*\b/,
+ alias: 'number',
+ },
// Command words at line start and after run.
keyword: [
@@ -128,5 +131,4 @@ Prism.languages.mcfunction = {
},
}
-// @ts-expect-error - Broken BB types
Prism.languages.bolt = Prism.languages.mcfunction
diff --git a/src/svelteComponents/keyframeEasings.svelte b/src/svelteComponents/keyframeEasings.svelte
deleted file mode 100644
index dce3b8ff..00000000
--- a/src/svelteComponents/keyframeEasings.svelte
+++ /dev/null
@@ -1,227 +0,0 @@
-
-
-
-
-{#if selectedKeyframe?.interpolation === 'linear'}
-
-
- {translate('panel.keyframe.easing_type.title')}
-
- {#key easingType}
-
- {/key}
-
- {#if selectedKeyframe.easing !== 'linear'}
-
-
- {translate('panel.keyframe.easing_mode.title')}
-
- {#key easingFunction}
-
- {/key}
-
- {/if}
- {#if hasArgs(easingFunction)}
-
-
- {translate(`panel.keyframe.easing_args.easing_arg.${easingType}.title`)}
-
-
-
- {/if}
-{:else}
-
- {translate('panel.keyframe.nonlinear_interpolation')}
-
-{/if}
-
-
diff --git a/src/systems/animationRenderer.ts b/src/systems/animationRenderer.ts
index c67be4b1..3fb2ba39 100644
--- a/src/systems/animationRenderer.ts
+++ b/src/systems/animationRenderer.ts
@@ -14,14 +14,18 @@ import { eulerFromQuaternion, roundToNth, scrubUndefined } from '../util/misc'
import type { AnyRenderedNode, IRenderedRig } from './rigRenderer'
import { sleepForAnimationFrame } from './util'
+function getMainPreview() {
+ return Preview.all.find(p => p.id === 'main')
+}
+
export function correctSceneAngle() {
- main_preview.controls.rotateLeft(Math.PI)
- scene.setRotationFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)
+ getMainPreview()?.controls.rotateLeft(Math.PI)
+ Canvas.scene.setRotationFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)
}
export function restoreSceneAngle() {
- main_preview.controls.rotateLeft(-Math.PI)
- scene.setRotationFromAxisAngle(new THREE.Vector3(0, 1, 0), 0)
+ getMainPreview()?.controls.rotateLeft(-Math.PI)
+ Canvas.scene.setRotationFromAxisAngle(new THREE.Vector3(0, 1, 0), 0)
}
function getNodeMatrix(node: OutlinerElement, scale: number) {
@@ -218,6 +222,8 @@ export function getFrame(
// lastFrameCache.set(uuid, { matrix, keyframe })
break
}
+ case 'null_object':
+ updatePreview(animation, time)
case 'camera':
case 'struct': {
transform.matrix = getNodeMatrix(outlinerNode, 1)
@@ -299,7 +305,6 @@ export function updatePreview(animation: _Animation, time: number) {
Animator.resetLastValues()
Canvas.scene.updateMatrixWorld(true)
if (animation.effects) animation.effects.displayFrame()
- // Blockbench.dispatchEvent('display_animation_frame')
}
function renderAnimation(animation: _Animation, rig: IRenderedRig) {
@@ -319,6 +324,7 @@ function renderAnimation(animation: _Animation, rig: IRenderedRig) {
for (let time = 0; time <= animation.length; time = roundToNth(time + 0.05, 20)) {
updatePreview(animation, time)
+ updatePreview(animation, time) // IK doesn't work unless I call this twice for some reason...
const frame: IRenderedFrame = getFrame(animation, rig.nodes, time)
Object.keys(frame.node_transforms).forEach(n => includedNodes.add(n))
rendered.frames.push(frame)
@@ -366,6 +372,7 @@ export function hashAnimations(animations: IRenderedAnimation[]) {
export function getAnimatableNodes(): OutlinerElement[] {
return [
+ ...NullObject.all,
...Group.all,
...Locator.all,
...Interaction.all,
diff --git a/src/systems/datapackCompiler/1.20.4/main.mcb b/src/systems/datapackCompiler/1.20.4/main.mcb
index fc06bfd9..ab5c9c9d 100644
--- a/src/systems/datapackCompiler/1.20.4/main.mcb
+++ b/src/systems/datapackCompiler/1.20.4/main.mcb
@@ -171,7 +171,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
execute at @s on passengers run tp @s ~ ~ ~ ~ ~
@@ -455,8 +455,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -493,7 +494,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -504,7 +505,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -514,7 +515,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -530,7 +531,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -540,7 +541,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -556,7 +557,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -569,16 +570,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -592,17 +593,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1636,12 +1638,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.tag.CustomModelData set value <%variant.models[node.uuid].custom_model_data%>
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1662,26 +1664,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1690,26 +1682,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1720,7 +1736,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1738,7 +1754,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1760,7 +1776,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1771,7 +1787,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/1.20.5/main.mcb b/src/systems/datapackCompiler/1.20.5/main.mcb
index a5214f05..713934b6 100644
--- a/src/systems/datapackCompiler/1.20.5/main.mcb
+++ b/src/systems/datapackCompiler/1.20.5/main.mcb
@@ -174,7 +174,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
execute at @s on passengers run tp @s ~ ~ ~ ~ ~
@@ -458,8 +458,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -496,7 +497,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -507,7 +508,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -517,7 +518,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -533,7 +534,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -543,7 +544,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -559,7 +560,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -572,16 +573,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -595,17 +596,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1639,12 +1641,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.components."minecraft:custom_model_data" set value <%variant.models[node.uuid].custom_model_data%>
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1665,26 +1667,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1693,26 +1685,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1723,7 +1739,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1741,7 +1757,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1763,7 +1779,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1774,7 +1790,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/1.21.0/main.mcb b/src/systems/datapackCompiler/1.21.0/main.mcb
index 9eb1f89e..9970d1cd 100644
--- a/src/systems/datapackCompiler/1.21.0/main.mcb
+++ b/src/systems/datapackCompiler/1.21.0/main.mcb
@@ -175,7 +175,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
execute at @s on passengers run tp @s ~ ~ ~ ~ ~
@@ -459,8 +459,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -497,7 +498,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -508,7 +509,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -518,7 +519,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -534,7 +535,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -544,7 +545,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -560,7 +561,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -573,16 +574,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -596,17 +597,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1640,12 +1642,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.components."minecraft:item_model" set value "<%variant.models[node.uuid].item_model%>"
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1666,26 +1668,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1694,26 +1686,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1724,7 +1740,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1742,7 +1758,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1764,7 +1780,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1775,7 +1791,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/1.21.2/main.mcb b/src/systems/datapackCompiler/1.21.2/main.mcb
index 2b4e92c8..f15230dc 100644
--- a/src/systems/datapackCompiler/1.21.2/main.mcb
+++ b/src/systems/datapackCompiler/1.21.2/main.mcb
@@ -174,7 +174,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
execute at @s on passengers run rotate @s ~ ~
@@ -458,8 +458,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -496,7 +497,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -507,7 +508,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -517,7 +518,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -533,7 +534,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -543,7 +544,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -559,7 +560,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -572,16 +573,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -595,17 +596,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1639,12 +1641,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.components."minecraft:item_model" set value "<%variant.models[node.uuid].item_model%>"
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1665,26 +1667,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1693,26 +1685,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1723,7 +1739,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1741,7 +1757,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1763,7 +1779,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1774,7 +1790,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/1.21.4/main.mcb b/src/systems/datapackCompiler/1.21.4/main.mcb
index 84795994..0ebd7ccd 100644
--- a/src/systems/datapackCompiler/1.21.4/main.mcb
+++ b/src/systems/datapackCompiler/1.21.4/main.mcb
@@ -1,7 +1,7 @@
# TODO - Move all internal functions into an internal namespace, and only have user-facing functions in the main `animated_java:<%export_namespace%>` namespace.
# DIFFERENCES FROM 1.21.2:
-# - node entities are no longer passengers of the root entity for smooth rotation fix.
+# - node entities can be floating (not passengers) of the root entity for smooth rotation fix.
# - minecraft:custom_model_data is now more powerful than minecraft:item_model: item.components."minecraft:item_model" -> item.components."minecraft:custom_model_data".strings[0]
function on_load {
@@ -207,7 +207,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
IF (use_entity_stacking) {
@@ -430,7 +430,7 @@ IF (has_animations) {
function apply_frame {
#ARGS: {frame: int}
-
+ $data modify storage <%temp_storage%> entry.data.uuids_by_name.frame set value $(frame)
block { with storage <%temp_storage%> entry.data.uuids_by_name
REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node {
IF (BONE_TYPES.includes(node.type)) {
@@ -546,8 +546,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -584,7 +585,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -595,7 +596,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -605,7 +606,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -621,7 +622,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -631,7 +632,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -647,7 +648,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -660,16 +661,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -683,17 +684,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1728,12 +1730,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>"
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1756,26 +1758,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1784,26 +1776,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1814,7 +1830,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1830,7 +1846,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1850,7 +1866,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1861,7 +1877,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/1.21.5/main.mcb b/src/systems/datapackCompiler/1.21.5/main.mcb
index bf7cddf0..78b5af29 100644
--- a/src/systems/datapackCompiler/1.21.5/main.mcb
+++ b/src/systems/datapackCompiler/1.21.5/main.mcb
@@ -206,7 +206,7 @@ IF (!auto_update_rig_orientation) {
tp @s ~ ~ ~ ~ ~
data_manager prep read
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function <%blueprint_id%>/root/on_tick/transform_floating_entities
}
IF (use_entity_stacking) {
@@ -429,7 +429,7 @@ IF (has_animations) {
function apply_frame {
#ARGS: {frame: int}
-
+ $data modify storage <%temp_storage%> entry.data.uuids_by_name.frame set value $(frame)
block { with storage <%temp_storage%> entry.data.uuids_by_name
REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node {
IF (BONE_TYPES.includes(node.type)) {
@@ -545,8 +545,9 @@ IF (has_animations) {
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: {}, interactions: {}}
+ const toMerge = {cameras: {}, locators: {}, interactions: {}}
let frameFunc = ``;
+ let frameFuncEnd = ``;
for (const node of modifiedNodes) {
const transform = frame.node_transforms[node.uuid]
// Skip if the node doesn't have a transform for this frame.
@@ -583,7 +584,7 @@ IF (has_animations) {
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] = {
+ toMerge.locators[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -594,7 +595,7 @@ IF (has_animations) {
if (transform.function) {
if (node.config?.use_entity) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -604,7 +605,7 @@ IF (has_animations) {
+ `${transform.function}`
+ `\n}`
} else {
- frameFunc +=
+ frameFuncEnd +=
`\nexecute 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)} `
@@ -620,7 +621,7 @@ IF (has_animations) {
const lastFrame = lastActiveFrame[node.uuid]
lastActiveFrame[node.uuid] = transform
;if (!lastFrame || matrixToNbtFloatArray(transform.matrix).toString() !== matrixToNbtFloatArray(lastFrame.matrix).toString()) {
- to_merge.interactions[node.storage_name] = {
+ toMerge.interactions[node.storage_name] = {
px: roundTo(transform.pos[0], 10),
py: roundTo(transform.pos[1], 10),
pz: roundTo(transform.pos[2], 10),
@@ -630,7 +631,7 @@ IF (has_animations) {
}
if (transform.function) {
- frameFunc +=
+ frameFuncEnd +=
`\n$execute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${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)} `
@@ -646,7 +647,7 @@ IF (has_animations) {
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] = {
+ toMerge.cameras[node.storage_name] = {
px: transform.pos[0],
py: transform.pos[1],
pz: transform.pos[2],
@@ -659,16 +660,16 @@ IF (has_animations) {
}
}
- const updatedLocators = Object.keys(to_merge.locators).length > 0;
- const updatedCameras = Object.keys(to_merge.cameras).length > 0;
- const updatedInteractions = Object.keys(to_merge.interactions).length > 0;
+ const updatedLocators = Object.keys(toMerge.locators).length > 0;
+ const updatedCameras = Object.keys(toMerge.cameras).length > 0;
+ const updatedInteractions = Object.keys(toMerge.interactions).length > 0;
- if (!updatedCameras) delete to_merge.cameras;
- if (!updatedLocators) delete to_merge.locators;
- if (!updatedInteractions) delete to_merge.interactions;
+ if (!updatedCameras) delete toMerge.cameras;
+ if (!updatedLocators) delete toMerge.locators;
+ if (!updatedInteractions) delete toMerge.interactions;
if (updatedLocators || updatedCameras || updatedInteractions) {
- frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(to_merge)}`
+ frameFunc += `\ndata modify storage ${temp_storage} entry.data merge value ${JSON.stringify(toMerge)}`
frameFunc += `\ndata_manager prep write`
if (!auto_update_rig_orientation) {
frameFunc += `\nfunction ${blueprint_id}/root/on_tick/transform_floating_entities`
@@ -682,17 +683,18 @@ IF (has_animations) {
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 unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
+ frameFuncEnd += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] ${execute_condition}run function ${blueprint_id}/variants/${variant.name}/apply`
;hasFunction = true
}
// Root function keyframes.
if (frame.function) {
const execute_condition = frame.function_execute_condition ? frame.function_execute_condition + ' ' : ''
- frameFunc += `\nexecute unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] at @s ${execute_condition}run block ${frameIndex}_root_function%NEWLINE_PATCH%{\n${frame.function}\n}`
+ frameFuncEnd += `\nexecute 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 += frameFuncEnd;
+ if (frameFunc.length > 0) {
frameFunc = `function ${frameIndex}%NEWLINE_PATCH%{${frameFunc}\n}`
emit.mcb(frameFunc.replaceAll(/%NEWLINE_PATCH%\n?/g, ' '))
}
@@ -1727,12 +1729,12 @@ IF (Object.keys(rig.variants).length > 1) {
data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "<%variant.name%>"
}
}
- IF (node.configs.variants[variant.uuid]) {
+ IF (node.configs.variants[variant.uuid] || variant.is_default && node.configs.default) {
<%%
- global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid])
+ global.config = DisplayEntityConfig.fromJSON(node.configs.variants[variant.uuid] ?? node.configs.default)
%%>
IF (!global.config.isDefault()) {
- data merge entity @s <%global.config.toNBT(undefined, variant.is_default)%>
+ data merge entity @s <%global.config.toNBT(undefined)%>
}
IF (global.config.onApplyFunction) {
<%%
@@ -1755,26 +1757,16 @@ IF (Object.keys(rig.variants).length > 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 {
- IF (locator.config?.use_entity) {
- execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
- $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 storage <%temp_storage%> entry.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)%> \
- }
- }
- } ELSE {
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'locator')) as locator {
+ IF (locator.config?.use_entity) {
+ execute at @s run block set_default_pose/as_locator_<%locator.storage_name%> { with storage <%temp_storage%> entry.data.locators.<%locator.storage_name%>
+ $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 storage <%temp_storage%> entry.data.locators.<%locator.storage_name%> merge value { \
px: <%roundTo(locator.default_transform.pos[0], 10)%>, \
py: <%roundTo(locator.default_transform.pos[1], 10)%>, \
@@ -1783,26 +1775,50 @@ IF (has_locators || has_cameras) {
rx: <%roundTo(locator.default_transform.head_rot[0], 10)%> \
}
}
+ } ELSE {
+ data modify storage <%temp_storage%> entry.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 run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
- }
+ REPEAT(Object.values(rig.nodes).filter(node => node.type === 'interaction')) as interaction {
+ execute at @s run block set_default_pose/as_interaction_<%interaction.storage_name%> { with storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%>
+ $tp $(uuid) \
+ ^<%roundTo(interaction.default_transform.pos[0], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[1], 10)%> \
+ ^<%roundTo(interaction.default_transform.pos[2], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[1], 10)%> \
+ ~<%roundTo(interaction.default_transform.head_rot[0], 10)%>
+ data modify storage <%temp_storage%> entry.data.interactions.<%interaction.storage_name%> merge value { \
+ px: <%roundTo(interaction.default_transform.pos[0], 10)%>, \
+ py: <%roundTo(interaction.default_transform.pos[1], 10)%>, \
+ pz: <%roundTo(interaction.default_transform.pos[2], 10)%>, \
+ ry: <%roundTo(interaction.default_transform.head_rot[1], 10)%>, \
+ rx: <%roundTo(interaction.default_transform.head_rot[0], 10)%> \
+ }
+ }
+ }
+
+ REPEAT (Object.values(rig.nodes).filter(node => node.type === 'camera')) as camera {
+ execute at @s run block set_default_pose/as_camera_<%camera.storage_name%> { with storage <%temp_storage%> entry.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 storage <%temp_storage%> entry.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)%> \
}
}
}
@@ -1813,7 +1829,7 @@ IF (has_locators || has_cameras) {
dir zzz {
IF (has_animations) {
function apply_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1829,7 +1845,7 @@ dir zzz {
}
function set_default_pose {
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
function ../zzz/reset_floating_entities
}
@@ -1849,7 +1865,7 @@ IF (has_animations) {
# Changes the pose of the rig to the the default pose with interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/apply_default_pose
@@ -1860,7 +1876,7 @@ function set_default_pose {
# Changes the pose of the rig to the the default pose without interpolation
debug assert executed_as_root_entity <%TAGS.PROJECT_ROOT(blueprint_id)%>
- IF (has_locators || has_cameras) {
+ IF (has_locators || has_cameras || has_interactions) {
data_manager prep read
}
function ./zzz/set_default_pose
diff --git a/src/systems/datapackCompiler/26.2/global.mcb b/src/systems/datapackCompiler/26.2/global.mcb
new file mode 100644
index 00000000..b3df6418
--- /dev/null
+++ b/src/systems/datapackCompiler/26.2/global.mcb
@@ -0,0 +1,334 @@
+# DIFFERENCES FROM 1.21.5:
+# - Predicates use `entity_type` instead of `type`.
+
+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
+
+ # Enforce loading order.
+ function *global/gu/on_load
+ function *global/data_manager/on_load
+ function #*global/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 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 {
+ <%blueprint_id%>/on_load
+ }
+
+ dir root {
+ function on_tick {
+ IF (debug_mode) {
+ execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function *global/root/on_load
+ }
+ data_manager prep read
+ function *global/root/run_project_tick with storage <%temp_storage%> entry.data
+ }
+
+ function run_project_tick {
+ $function $(blueprint_id)/root/on_tick
+ }
+
+ IF (debug_mode) {
+ 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
+
+ # No need to read here, since is_rig_outdated already reads the data.
+ # data_manager prep read
+
+ # Get export namespace
+ data modify storage <%temp_storage%> args.blueprint_id set from storage <%temp_storage%> entry.data.blueprint_id
+ # Overwrite the rig hash so the warning doesn't show again.
+ data modify storage <%temp_storage%> entry.data.rig_hash set value '<%rig_hash%>'
+ data_manager write
+
+ 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 {
+ data_manager prep read
+
+ 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 {
+ data_manager prep read
+ block { with storage <%temp_storage%> entry.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 blueprint ID instead of hardcoding it.
+ $execute if data storage <%project_storage.replace(blueprint_id, '$(blueprint_id)')%> {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)"
+ }
+ }
+ }
+
+ # Storage-based per-entity NBT storage. Thanks Xanbelor for the concept!
+ dir data_manager {
+ function on_load {
+ scoreboard players add #aj.data_manager.index <%OBJECTIVES.I()%> 0
+ }
+
+ function on_tick minecraft:tick {
+ execute store result storage <%temp_storage%> args.id int 1 run scoreboard players get #aj.data_manager.index <%OBJECTIVES.I()%>
+ block { with storage <%temp_storage%> args
+ $data modify storage <%temp_storage%> args.uuid set from storage <%data_storage%> "$(id)".uuid
+ scoreboard players set #success <%OBJECTIVES.I()%> 0
+ block { with storage <%temp_storage%> args
+ $execute store success score #success <%OBJECTIVES.I()%> if score $(uuid) <%OBJECTIVES.ID()%> matches 1..
+ $execute if score #success <%OBJECTIVES.I()%> matches 0 run data remove storage <%data_storage%> "$(id)"
+ }
+ }
+ scoreboard players add #aj.data_manager.index <%OBJECTIVES.I()%> 1
+ execute \
+ if score #aj.data_manager.index <%OBJECTIVES.I()%> > aj.last_id <%OBJECTIVES.ID()%> \
+ run scoreboard players set #aj.data_manager.index <%OBJECTIVES.I()%> 0
+ }
+
+ function create_entry {
+ $data modify storage <%data_storage%> "$(id)" set value {uuid: "$(uuid)", data: {}}
+ }
+
+ function read {
+ data remove storage <%temp_storage%> entry
+ $data modify storage <%temp_storage%> entry set from storage <%data_storage%> "$(id)"
+ }
+
+ function write {
+ $data modify storage <%data_storage%> "$(id)" set from storage <%temp_storage%> entry
+ }
+ }
+
+ dir interactions {
+ REPEAT (['interaction', 'attack']) as direction {
+ dir <%direction%> {
+ advancement trigger {
+ "criteria": {
+ "player_<%direction%>_clicked_interaction_entity": {
+ "trigger": "<%direction === 'interaction' ? 'minecraft:player_interacted_with_entity' : 'minecraft:player_hurt_entity'%>",
+ "conditions": {
+ "entity": {
+ "entity_type": "minecraft:interaction"
+ }
+ }
+ }
+ },
+ "rewards": {
+ "function": "<%context.namespace + ':' + context.path.join('/')%>/on"
+ }
+ }
+
+ function on {
+ advancement revoke @s only <%context.namespace + ':' + context.path.join('/')%>/trigger
+ tag @s add <%TAGS.INTERACTING_PLAYER()%>
+ execute \
+ as @e[type=interaction, tag=<%TAGS.GLOBAL_INTERACTION()%>, distance=..8] \
+ if data entity @s <%direction%> \
+ run function ./check
+ tag @s remove <%TAGS.INTERACTING_PLAYER()%>
+ }
+
+ function check {
+ execute store result score #gametime <%OBJECTIVES.I()%> run time query gametime
+ execute \
+ store result score #timestamp <%OBJECTIVES.I()%> \
+ run data get entity @s <%direction%>.timestamp
+
+ # Check that the interaction has a command to run, and
+ # if this interaction is from the player being processed.
+ scoreboard players set #check <%OBJECTIVES.I()%> 0
+ execute \
+ if score #timestamp <%OBJECTIVES.I()%> = #gametime <%OBJECTIVES.I()%> \
+ if data entity @s data.animated_java.on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function \
+ store success score #check <%OBJECTIVES.I()%> \
+ on <%direction === 'interaction' ? 'target' : 'attacker'%> \
+ if entity @s[tag=<%TAGS.INTERACTING_PLAYER()%>]
+
+ execute \
+ if score #check <%OBJECTIVES.I()%> matches 1 \
+ run function ./do
+ }
+
+ function do {
+ scoreboard players set #check <%OBJECTIVES.I()%> 0
+ block execute { with entity @s data.animated_java
+ $$(on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function)
+ scoreboard players set #check <%OBJECTIVES.I()%> 1
+ }
+ execute if score #check <%OBJECTIVES.I()%> matches 0 run tellraw @a [ \
+ {text: '', color: red}, \
+ {text:'ɪɴᴛᴇʀᴀᴄᴛɪᴏɴꜱ', color:'light_purple'}, \
+ {text:'\n(<%global.root%>.mcb)', color:'dark_gray'}, \
+ '\n→ ᴇʀʀᴏʀ:\n\sFailed to handle <%direction === 'interaction' ? 'right' : 'left'%> click interaction from ', \
+ {selector: '@p[tag=<%TAGS.INTERACTING_PLAYER()%>]', color: gold}, \
+ ':\n\s\sInvalid command: ', \
+ {nbt: 'data.animated_java.on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function', entity: '@s', color: aqua}, \
+ '\n\s\sfrom ', \
+ {text: 'data.animated_java.on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function ', color: yellow}, \
+ ]
+ # Only remove the interaction if it's consumed.
+ data remove entity @s <%direction%>
+ }
+ }
+ }
+ }
+}
diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts
index a0eba207..4bdeebe4 100644
--- a/src/systems/datapackCompiler/index.ts
+++ b/src/systems/datapackCompiler/index.ts
@@ -47,7 +47,7 @@ async function generateRootEntityPassengers(version: string, rig: IRenderedRig)
const passengers: NbtList = new NbtList()
for (const [uuid, node] of Object.entries(rig.nodes)) {
- if (node.type === 'struct') continue
+ if (node.type === 'struct' || node.type === 'null_object') continue
const passenger = new NbtCompound()
diff --git a/src/systems/datapackCompiler/mcbFiles.ts b/src/systems/datapackCompiler/mcbFiles.ts
index 26f73d16..beb5d369 100644
--- a/src/systems/datapackCompiler/mcbFiles.ts
+++ b/src/systems/datapackCompiler/mcbFiles.ts
@@ -15,6 +15,8 @@ import MAIN_1_21_4 from './1.21.4/main.mcb'
import GLOBAL_1_21_5 from './1.21.5/global.mcb'
import MAIN_1_21_5 from './1.21.5/main.mcb'
+import GLOBAL_26_2 from './26.2/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.
@@ -26,6 +28,14 @@ interface MCBFiles {
export function getMCBFilesByVersion(version: string): MCBFiles {
switch (true) {
+ case VersionUtil.compare(version, '>=', '26.2'): {
+ return {
+ main: MAIN_1_21_5,
+ global: GLOBAL_26_2,
+ globalTemplates: GLOBAL_TEMPLATES_1_20_4,
+ }
+ }
+
case VersionUtil.compare(version, '>=', '1.21.11'): {
return {
main: MAIN_1_21_5,
diff --git a/src/systems/minecraft/assetManager.ts b/src/systems/minecraft/assetManager.ts
index 1e9043f7..7e0925cc 100644
--- a/src/systems/minecraft/assetManager.ts
+++ b/src/systems/minecraft/assetManager.ts
@@ -1,6 +1,7 @@
import type { Unzipped } from 'fflate'
import ky from 'ky'
import { dirname, join } from 'node:path'
+import index from '../../assets/vanillaAssetOverrides/index.json'
import { getFsModule } from '../../constants'
import { unzip } from '../util'
import { getVersionById, getVersionDownloadUrl } from './versionManager'
@@ -11,6 +12,8 @@ const ASSETS_CACHE = new Map()
const FOLDER_CACHE = new Map>()
const ACTIVE_DOWNLOAD_PROMISES = new Map>()
+const ASSET_OVERRIDES = index as unknown as Record
+
async function downloadFile(url: string, savePath: string) {
const response = await ky(url, {
method: 'GET',
@@ -80,7 +83,13 @@ export async function hasAsset(versionId: string, assetPath: string) {
export async function getRawAsset(versionId: string, assetPath: string) {
const assets = await getAssets(versionId)
+
+ if (ASSET_OVERRIDES[assetPath]) {
+ return Buffer.from(ASSET_OVERRIDES[assetPath])
+ }
+
const asset = assets[assetPath]
+
if (!asset) {
throw new Error(`Asset '${assetPath}' not found in Minecraft ${versionId} client jar!`)
}
@@ -116,7 +125,11 @@ export async function getFolder(versionId: string, folderPath: string) {
const folderAssets: Record = {}
for (const assetPath in assets) {
if (assetPath.startsWith(folderPath)) {
- folderAssets[assetPath] = Buffer.from(assets[assetPath])
+ if (ASSET_OVERRIDES[assetPath]) {
+ folderAssets[assetPath] = Buffer.from(ASSET_OVERRIDES[assetPath])
+ } else {
+ folderAssets[assetPath] = Buffer.from(assets[assetPath])
+ }
}
}
diff --git a/src/systems/minecraft/itemModelManager.ts b/src/systems/minecraft/itemModelManager.ts
index 01e8591c..6250c393 100644
--- a/src/systems/minecraft/itemModelManager.ts
+++ b/src/systems/minecraft/itemModelManager.ts
@@ -26,7 +26,10 @@ export async function getItemModel(
result = await parseItemModel(getItemResourceLocation(item), itemDisplay)
ITEM_MODEL_CACHE.set(cacheKey, result)
}
- if (!result) return undefined
+ if (!result) {
+ console.warn(`Failed to load item model for ${item} with display mode ${itemDisplay}`)
+ return undefined
+ }
result = {
mesh: result.mesh.clone(true),
outline: result.outline.clone(true),
diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts
index 30c75325..5e0a0577 100644
--- a/src/systems/rigRenderer.ts
+++ b/src/systems/rigRenderer.ts
@@ -145,6 +145,9 @@ export interface IRenderedNodes {
Struct: IRenderedNode & {
type: 'struct'
}
+ NullObject: IRenderedNode & {
+ type: 'null_object'
+ }
Camera: IRenderedNode & {
type: 'camera'
/** The maximum distance this node travels away from the root entity while animating. */
@@ -374,6 +377,19 @@ function getNodeBoundingBox(node: Group | TextDisplay | VanillaItemDisplay | Van
return box
}
+function renderNullObject(nullObject: NullObject, rig: IRenderedRig) {
+ const parent = nullObject.parent instanceof Group ? nullObject.parent.uuid : undefined
+ const renderedNullObject: IRenderedNodes['NullObject'] = {
+ type: 'null_object',
+ name: nullObject.name,
+ uuid: nullObject.uuid,
+ parent,
+ default_transform: {} as INodeTransform,
+ storage_name: sanitizeStorageKey(nullObject.name),
+ }
+ rig.nodes[nullObject.uuid] = renderedNullObject
+}
+
function renderGroup(
group: Group,
rig: IRenderedRig,
@@ -457,6 +473,10 @@ function renderGroup(
rig.includes_custom_models = true
break
}
+ case node instanceof NullObject: {
+ renderNullObject(node, rig)
+ break
+ }
default:
console.warn(`Encountered unknown node type:`, node)
}
@@ -614,24 +634,31 @@ function renderLocator(locator: Locator, rig: IRenderedRig) {
rig.nodes[locator.uuid] = renderedLocator
}
-function renderInteraction(box: Interaction, rig: IRenderedRig) {
- if (!box.export) return
- const parentId = box.parent instanceof Group ? box.parent.uuid : undefined
+function renderInteraction(interaction: Interaction, rig: IRenderedRig) {
+ if (!interaction.export) return
+
+ if (VersionUtil.compare(Project.animated_java.target_minecraft_version, '<', '1.21.5')) {
+ throw new IntentionalExportError(
+ "Interactions are only supported when targeting Minecraft 1.21.5 and above. Please update your project's target Minecraft version to 1.21.5 or higher to use interactions."
+ )
+ }
+
+ const parentId = interaction.parent instanceof Group ? interaction.parent.uuid : undefined
const renderedInteraction: IRenderedNodes['Interaction'] = {
type: 'interaction',
- name: box.name,
- storage_name: sanitizeStorageKey(box.name),
- uuid: box.uuid,
+ name: interaction.name,
+ storage_name: sanitizeStorageKey(interaction.name),
+ uuid: interaction.uuid,
parent: parentId,
- config: structuredClone(box.config),
+ config: structuredClone(interaction.config),
max_distance: 0,
default_transform: {} as INodeTransform,
- width: box.scale[0] / 16,
- height: box.scale[1] / 16,
+ width: interaction.scale[0] / 16,
+ height: interaction.scale[1] / 16,
}
- rig.nodes[box.uuid] = renderedInteraction
+ rig.nodes[interaction.uuid] = renderedInteraction
}
function renderCamera(camera: ICamera, rig: IRenderedRig) {
@@ -778,7 +805,8 @@ function getDefaultTransforms(rig: IRenderedRig) {
const anim = new Blockbench.Animation()
correctSceneAngle()
updatePreview(anim, 0)
- const transforms = getFrame(anim, rig.nodes).node_transforms
+ updatePreview(anim, 0) // IK doesn't work unless I call this twice for some reason...
+ const transforms = getFrame(anim, rig.nodes, 0).node_transforms
restoreSceneAngle()
return transforms
}
@@ -835,6 +863,10 @@ export function renderRig(modelExportFolder: string, textureExportFolder: string
renderInteraction(node, rig)
break
}
+ case node instanceof NullObject: {
+ renderNullObject(node, rig)
+ break
+ }
case node instanceof Cube: {
throw new IntentionalExportError(
`Cubes cannot be exported as root nodes. Please parent them to a bone. (Found '${node.name}' outside of a bone)`