diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/datapack.ts b/exporters/datapackExporter/exporter/gen-1.20.2/datapack.ts index 911c0405..2d7b4639 100644 --- a/exporters/datapackExporter/exporter/gen-1.20.2/datapack.ts +++ b/exporters/datapackExporter/exporter/gen-1.20.2/datapack.ts @@ -1,6 +1,6 @@ import { fileExists, loadJsonFile, recursivelyRemoveEmptyFolders } from '../util' -import { generateEntityTypes } from './entity_types' -import { generateTags } from './function_tags' +import { generateEntityTypes } from './entityTypes' +import { generateTags } from './functionTags' import { generateFunctions } from './functions' import { Globals as G, util } from './globals' diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/entity_tags.ts b/exporters/datapackExporter/exporter/gen-1.20.2/entityTags.ts similarity index 100% rename from exporters/datapackExporter/exporter/gen-1.20.2/entity_tags.ts rename to exporters/datapackExporter/exporter/gen-1.20.2/entityTags.ts diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/entity_types.ts b/exporters/datapackExporter/exporter/gen-1.20.2/entityTypes.ts similarity index 100% rename from exporters/datapackExporter/exporter/gen-1.20.2/entity_types.ts rename to exporters/datapackExporter/exporter/gen-1.20.2/entityTypes.ts diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/flow.mcb b/exporters/datapackExporter/exporter/gen-1.20.2/flow.mcb index 07ca5b82..61dc0baa 100644 --- a/exporters/datapackExporter/exporter/gen-1.20.2/flow.mcb +++ b/exporters/datapackExporter/exporter/gen-1.20.2/flow.mcb @@ -3,24 +3,99 @@ function entry { } function set_frame { - $data modify storage animated_java:temp frame merge value {animation: $(animation), frame:$(frame)} - $data modify storage animated_java:temp uuids set from animated_java:project_name animations.$(animation).nodes - $data modify storage animated_kava:temp merge set value {transformation: false, interpolation_duration: $(duration), start_interpolation: 0} + $data modify storage animated_java:temp {} merge value {animation: $(animation), frame:$(frame)} + $data modify storage animated_java:temp nodes set from animated_java:project_name animations.$(animation).nodes data modify storage animated_java:temp uuid_map set from entity @s uuid_map + # For each uuid in nodes + execute store result score #count aj.i if data storage animated_java:temp nodes[] + { + data modify storage animated_java:temp uuid merge from storage animated_java:temp nodes[-1] + + function map_node_uuid_to_entity_uuid with storage animated_java:temp + scoreboard players set #success aj.i 0 + execute store success score #success aj.i if data storage animated_java:temp {type:'bone'} run function animated_java:set_bone_transformation with storage animated_java:temp + execute if score #success aj.i matches 0 run function animated_java:set_locator_transformation with storage animated_java:temp + + data remove storage animated_java:temp nodes[-1] + + scoreboard players remove #count aj.i 1 + execute if score #count aj.i matches 1.. run function $block + } +} + +function set_frame_animation_specific { + $data modify storage animated_java:temp {} merge value {animation: <%animation.name%>, frame:$(frame)} + data modify storage animated_java:temp uuid_map set from entity @s uuid_map + + # AJ only includes nodes modified by this animation while generating this function LOOP(nodes, node) { - data modify storage animated_java:temp uuid set from storage animated_java:temp uuids[-1] - data remove storage animated_java:temp uuids[-1] - function map_bone_uuid_to_entity_uuid with storage animated_java:temp - function modify_bone_transformation with storage animated_java:temp + data modify storage animated_java:temp uuidset from storage animated_java:project_name animations.<%animation.name%>.frames[$(frame)].nodes[{uuid: <%node.uuid%>}] + function map_node_uuid_to_entity_uuid with storage animated_java:temp + function animated_java:set_bone_transformation with storage animated_java:temp } } -function modify_bone_transformation { - $data modify storage animated_java:temp merge.transformation set from storage animated_java:project_name animations.$(animation).frames[$(frame)].bones[$(uuid)].transformation - $execute as $(uuid) run data modify entity @s {} merge from storage animated_java:temp merge +function set_bone_transformation { + $data modify storage animated_java:project_name animations.$(animation).frames[$(frame)].nodes[$(uuid)].duration set value $(duration) + $execute as $(uuid) run data modify entity @s {} merge from storage animated_java:project_name animations.$(animation).frames[$(frame)].nodes[$(uuid)] +} + +function set_locator_transformation { + $execute as $(uuid) run data modify entity @s {} merge from storage animated_java:project_name animations.$(animation).frames[$(frame)].nodes[$(uuid)] } -function map_bone_uuid_to_entity_uuid { +function map_node_uuid_to_entity_uuid { $data modify storage animated_java:temp uuid set from storage animated_java:temp uuid_map[$(uuid)] } + +loot storage { + "animated_java:project_name": { + "animations": { + "$(animation)": { + "nodes": [ + { + "type": "bone", + "uuid": "$(uuid)", + }, + { + "type": "locator", + "uuid": "$(uuid)", + } + ], + "frames": [ + { + "nodes": { + "$(bone_uuid)": { + "transformation": {}, + "start_interpolation": 0, + "duration": 1, + }, + "$(locator_uuid)": { + "Position": [], + "Rotation": [], + } + } + } + ] + } + } + }, + "animated_java:temp": { + "animation": "walk", + "frame": 0, + "nodes": [ + { + "type": "bone", + "uuid": "$(uuid)", + }, + { + "type": "locator", + "uuid": "$(uuid)", + } + ], + "uuid_map": { + "$(node_uuid)": "$(entity_uuid)" + } + } +} \ No newline at end of file diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/function_tags.ts b/exporters/datapackExporter/exporter/gen-1.20.2/functionTags.ts similarity index 100% rename from exporters/datapackExporter/exporter/gen-1.20.2/function_tags.ts rename to exporters/datapackExporter/exporter/gen-1.20.2/functionTags.ts diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/functions-old.ts b/exporters/datapackExporter/exporter/gen-1.20.2/functions-old.ts new file mode 100644 index 00000000..60f71b8c --- /dev/null +++ b/exporters/datapackExporter/exporter/gen-1.20.2/functions-old.ts @@ -0,0 +1,833 @@ +import { wrapNum } from '../util' +import { type IFolders } from './datapack' +import { Globals as G, JsonText, deepslate, formatStr as f, util } from './globals' +import { loadStorageGenerator } from './storageGenerator' + +type NbtCompound = InstanceType + +function getExportVersionId() { + return Math.round(Math.random() * 2 ** 31 - 1 - (Math.random() * 2 ** 31 - 1)) +} + +function generateBonePassenger(uuid: string, bone: AnimatedJava.IRenderedNodes['Bone']) { + const passenger = deepslate.NbtTag.fromString(bone.nbt) as NbtCompound + const default_pose = G.exportData.rig.defaultPose.find(pose => pose.uuid === uuid) + + passenger.set('id', new deepslate.NbtString('minecraft:item_display')) + + if (!passenger.get('Tags')) passenger.set('Tags', new deepslate.NbtList()) + const tags = passenger.get('Tags') as InstanceType + tags.add(new deepslate.NbtString(G.TAGS.new)) + tags.add(new deepslate.NbtString(G.TAGS.rigEntity)) + tags.add(new deepslate.NbtString(G.TAGS.boneEntity)) + tags.add(new deepslate.NbtString(f(G.TAGS.namedBoneEntity, [bone.name]))) + + passenger + .set('transformation', util.matrixToNbtFloatArray(default_pose.matrix)) + .set('interpolation_duration', new deepslate.NbtInt(G.DEFAULT_INTERPOLATION_DURATION)) + .set('item_display', new deepslate.NbtString('head')) + .set('teleport_duration', new deepslate.NbtInt(1)) + + if (!passenger.get('item')) passenger.set('item', new deepslate.NbtCompound()) + const item = passenger.get('item') as InstanceType + item.set('id', new deepslate.NbtString(G.RIG_ITEM)) + .set('Count', new deepslate.NbtByte(1)) + .set( + 'tag', + new deepslate.NbtCompound().set( + 'CustomModelData', + new deepslate.NbtInt(bone.customModelData) + ) + ) + + if (!passenger.get('CustomName')) + passenger.set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `bone`, color: 'white' }, + `[`, + { text: `${bone.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + + // FIXME - This doesn't account for animations, and it SHOULD + const maxHeight = Math.max(Math.abs(bone.boundingBox.min.y), Math.abs(bone.boundingBox.max.y)) + const maxWidth = Math.max( + Math.abs(bone.boundingBox.min.x), + Math.abs(bone.boundingBox.max.x), + Math.abs(bone.boundingBox.min.z), + Math.abs(bone.boundingBox.max.z) + ) + passenger + .set('height', new deepslate.NbtFloat(maxHeight)) + .set('width', new deepslate.NbtFloat(maxWidth)) + + return passenger +} + +function generateLocatorPassenger( + uuid: string, + locator: AnimatedJava.IRenderedNodes['Locator'], + internalSummonFolder: AnimatedJava.VirtualFolder +) { + const { roundToN } = AnimatedJava.API + const passenger = new deepslate.NbtCompound() + // const passenger = deepslate.NbtTag.fromString(locator.nbt) as NbtCompound + + passenger + .set('id', new deepslate.NbtString('minecraft:snowball')) + .set( + 'Tags', + new deepslate.NbtList([ + new deepslate.NbtString(G.TAGS.new), + new deepslate.NbtString(G.TAGS.rigEntity), + new deepslate.NbtString(G.TAGS.locatorOrigin), + new deepslate.NbtString(f(G.TAGS.namedLocatorOrigin, [locator.name])), + ]) + ) + .set( + 'Item', + new deepslate.NbtCompound() + .set('id', new deepslate.NbtString(G.RIG_ITEM)) + .set('Count', new deepslate.NbtByte(1)) + .set( + 'tag', + new deepslate.NbtCompound().set('CustomModelData', new deepslate.NbtInt(1)) + ) + ) + .set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `locator`, color: 'white' }, + `[`, + { text: `${locator.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + + const locatorEntityNbt = deepslate.NbtTag.fromString(locator.nbt) as NbtCompound + if (!locatorEntityNbt.get('Tags')) locatorEntityNbt.set('Tags', new deepslate.NbtList()) + const tags = locatorEntityNbt.get('Tags') as InstanceType + + tags.add(new deepslate.NbtString(G.TAGS.locatorEntity)) + tags.add(new deepslate.NbtString(f(G.TAGS.namedLocatorEntity, [locator.name]))) + tags.add(new deepslate.NbtString(G.TAGS.new)) + + if (!locatorEntityNbt.get('CustomName')) + locatorEntityNbt.set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `locatorEntity`, color: 'white' }, + `[`, + { text: `${locator.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + + function locatorToString(node: AnimatedJava.IAnimationNode) { + const pos = node.pos + const euler = new THREE.Euler().setFromQuaternion(node.rot, 'YXZ') + const rot = new THREE.Vector3(euler.x, euler.y, euler.z).multiplyScalar(180 / Math.PI) + return `tp @s ^${roundToN(pos.x, 100000)} ^${roundToN(pos.y, 100000)} ^${roundToN( + pos.z, + 100000 + )} ~${roundToN(wrapNum(-rot.y - 180, -180, 180), 100000)} ~${roundToN(-rot.x, 100000)}` + } + internalSummonFolder + .newFolder('locator_' + locator.name) + // ANCHOR - function G.PROJECT_PATH/summon/locator_/as_origin + .chainNewFile('as_origin.mcfunction', [ + // `say Locator Origin`, + `summon ${locator.entity_type} ~ ~ ~ ${locatorEntityNbt.toString()}`, + `execute as @e[type=${locator.entity_type},tag=${f(G.TAGS.namedLocatorEntity, [ + locator.name, + ])},tag=${G.TAGS.new},limit=1,distance=..1] run function ${ + G.INTERNAL_PATH + }/summon/locator_${locator.name}/as_entity`, + `data modify entity @s Owner set from storage animated_java Owner`, + `data remove storage animated_java Owner`, + ]) + // ANCHOR - function G.PROJECT_PATH/summon/locator_/as_entity + .chainNewFile('as_entity.mcfunction', [ + // `say Locator Entity`, + locatorToString(G.exportData.rig.defaultPose.find(v => v.uuid === uuid)), + `data modify storage animated_java Owner set from entity @s UUID`, + `tag @s remove ${G.TAGS.new}`, + `function #${G.PROJECT_PATH}/on_summon/as_locator_entities`, + ]) + + return passenger +} + +function generateCameraPassenger( + uuid: string, + camera: AnimatedJava.IRenderedNodes['Camera'], + internalSummonFolder: AnimatedJava.VirtualFolder +) { + const { roundToN } = AnimatedJava.API + const passenger = deepslate.NbtTag.fromString(camera.nbt) as NbtCompound + // const default_pose = G.exportData.rig.defaultPose.find(pose => pose.uuid === uuid) + + passenger + .set('id', new deepslate.NbtString('minecraft:snowball')) + .set( + 'Tags', + new deepslate.NbtList([ + new deepslate.NbtString(G.TAGS.new), + new deepslate.NbtString(G.TAGS.rigEntity), + new deepslate.NbtString(G.TAGS.cameraOrigin), + new deepslate.NbtString(f(G.TAGS.namedCameraOrigin, [camera.name])), + ]) + ) + .set( + 'Item', + new deepslate.NbtCompound() + .set('id', new deepslate.NbtString(G.RIG_ITEM)) + .set('Count', new deepslate.NbtByte(1)) + .set( + 'tag', + new deepslate.NbtCompound().set('CustomModelData', new deepslate.NbtInt(1)) + ) + ) + .set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `camera`, color: 'white' }, + `[`, + { text: `${camera.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + + const cameraEntityNbt = deepslate.NbtTag.fromString(camera.nbt) as NbtCompound + if (!cameraEntityNbt.get('Tags')) cameraEntityNbt.set('Tags', new deepslate.NbtList()) + const tags = cameraEntityNbt.get('Tags') as InstanceType + + tags.add(new deepslate.NbtString(G.TAGS.cameraEntity)) + tags.add(new deepslate.NbtString(f(G.TAGS.namedCameraEntity, [camera.name]))) + tags.add(new deepslate.NbtString(G.TAGS.new)) + + if (!cameraEntityNbt.get('CustomName')) + cameraEntityNbt + .set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `cameraEntity`, color: 'white' }, + `[`, + { text: `${camera.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + .set('teleport_duration', new deepslate.NbtInt(1)) + + function cameraToString(node: AnimatedJava.IAnimationNode) { + const pos = node.pos + const euler = new THREE.Euler().setFromQuaternion(node.rot, 'YXZ') + const rot = new THREE.Vector3(euler.x, euler.y, euler.z).multiplyScalar(180 / Math.PI) + return `tp @s ^${roundToN(pos.x, 100000)} ^${roundToN(pos.y, 100000)} ^${roundToN( + pos.z, + 100000 + )} ~${roundToN(wrapNum(-rot.y - 180, -180, 180), 100000)} ~${roundToN(-rot.x, 100000)}` + } + internalSummonFolder + .newFolder('camera_' + camera.name) + // ANCHOR - function G.PROJECT_PATH/summon/camera_/as_origin + .chainNewFile('as_origin.mcfunction', [ + // `say Camera Origin`, + `summon ${camera.entity_type} ~ ~ ~ ${cameraEntityNbt.toString()}`, + `execute as @e[type=${camera.entity_type},tag=${f(G.TAGS.namedCameraEntity, [ + camera.name, + ])},tag=${G.TAGS.new},limit=1,distance=..1] run function ${ + G.INTERNAL_PATH + }/summon/camera_${camera.name}/as_entity`, + `data modify entity @s Owner set from storage animated_java Owner`, + `data remove storage animated_java Owner`, + ]) + // ANCHOR - function G.PROJECT_PATH/summon/camera_/as_entity + .chainNewFile('as_entity.mcfunction', [ + // `say Camera Entity`, + cameraToString(G.exportData.rig.defaultPose.find(v => v.uuid === uuid)), + `data modify storage animated_java Owner set from entity @s UUID`, + `tag @s remove ${G.TAGS.new}`, + `function #${G.PROJECT_PATH}/on_summon/as_camera_entities`, + ]) + + return passenger +} + +function generateSummonFunction(internalSummonFolder: AnimatedJava.VirtualFolder) { + let rootNbt = deepslate.NbtTag.fromString( + G.exportData.exporterSettings.root_entity_nbt.value + ) as NbtCompound + const passengers = new deepslate.NbtList() + + for (const [uuid, bone] of Object.entries(G.exportData.rig.nodeMap)) { + switch (bone.type) { + case 'bone': + passengers.add(generateBonePassenger(uuid, bone)) + break + case 'locator': + passengers.add(generateLocatorPassenger(uuid, bone, internalSummonFolder)) + break + case 'camera': + passengers.add(generateCameraPassenger(uuid, bone, internalSummonFolder)) + break + default: + // @ts-ignore + throw new Error(`Unknown bone type: ${bone.type}`) + } + } + + if (passengers.length === 1 && G.exportData.renderedAnimations.length === 0) { + rootNbt = passengers.get(0) as NbtCompound + } else { + rootNbt.set('Passengers', passengers) + } + + if (!rootNbt.get('Tags')) rootNbt.set('Tags', new deepslate.NbtList()) + const tags = rootNbt.get('Tags') as InstanceType + tags.add(new deepslate.NbtString(G.TAGS.new)) + tags.add(new deepslate.NbtString(G.TAGS.rigEntity)) + tags.add(new deepslate.NbtString(G.TAGS.rootEntity)) + tags.add(new deepslate.NbtString(G.TAGS.globalRigRoot)) + + if (!rootNbt.get('CustomName')) + rootNbt.set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `root`, color: 'white' }, + ], + ]).toString() + ) + ) + + rootNbt.set('teleport_duration', new deepslate.NbtInt(1)) + + // ANCHOR - function G.PROJECT_PATH/summon + return [ + `summon minecraft:item_display ~ ~ ~ ${rootNbt.toString()}`, + `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity},tag=${G.TAGS.new},limit=1,distance=..0.1] run function ${G.INTERNAL_PATH}/summon/as_root`, + ] +} + +export function generateFunctions(folders: IFolders) { + const {} = AnimatedJava.API + const { generateStorage } = loadStorageGenerator() + const cameraCount = Object.values(G.exportData.rig.nodeMap).filter( + v => v.type === 'camera' + ).length + const locatorCount = Object.values(G.exportData.rig.nodeMap).filter( + v => v.type === 'locator' + ).length + + // ------------------------ + // SECTION - Load functions + // ------------------------ + + folders.project.internalFunctions + // ANCHOR - function G.PROJECT_PATH/load + .chainNewFile('load.mcfunction', [ + // Scoreboard objectives + ...Object.values(G.SCOREBOARD) + .filter(s => !s.includes('%s')) + .map(s => `scoreboard objectives add ${s} dummy`), + // prettier-ignore + ...G.exportData.renderedAnimations.map(a => `scoreboard objectives add ${f(G.SCOREBOARD.localAnimTime, [a.name])} dummy`), + // prettier-ignore + ...G.exportData.renderedAnimations.map(a => `scoreboard objectives add ${f(G.SCOREBOARD.loopMode, [a.name])} dummy`), + // prettier-ignore + ...G.exportData.renderedAnimations.map((a, i) => `scoreboard players set $aj.${G.PROJECT_NAME}.animation.${a.name} ${G.SCOREBOARD.id} ${i}`), + // prettier-ignore + ...G.VARIANTS.map((v, i) => `scoreboard players set $aj.${G.PROJECT_NAME}.variant.${v.name} ${G.SCOREBOARD.id} ${i}`), + // Variable initialization + `scoreboard players add .aj.last_id ${G.SCOREBOARD.id} 0`, + // prettier-ignore + ...G.LOOP_MODES.map((mode, i) => `scoreboard players set $aj.loop_mode.${mode} ${G.SCOREBOARD.i} ${i}`), + // version ID + `scoreboard players set ${G.SCOREBOARD.exportVersion} ${ + G.SCOREBOARD.i + } ${getExportVersionId()}`, + // load function tag + `scoreboard players reset * ${G.SCOREBOARD.rigLoaded}`, + `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/on_load`, + // load storage + `data modify storage animated_java:${ + G.PROJECT_NAME + } set value ${generateStorage().toString()}`, + ]) + // ANCHOR - func G.INTERNAL_PATH/on_load + .chainNewFile('on_load.mcfunction', [ + `scoreboard players set @s ${G.SCOREBOARD.rigLoaded} 1`, + G.OUTDATED_RIG_WARNING_ENABLED + ? `execute unless score @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i} at @s run function ${G.INTERNAL_PATH}/mark_outdated_rig` + : undefined, + ]) + + if (G.exportData.exporterSettings.include_uninstall_function.value === true) { + // ANCHOR - function NAMESPACE:uninstall + folders.project.functions.newFile('uninstall.mcfunction', [ + // Scoreboard objectives + ...Object.values(G.SCOREBOARD) + .filter(s => !s.includes('%s')) + .map(s => `scoreboard objectives remove ${s}`), + // prettier-ignore + ...G.exportData.renderedAnimations.map(a => `scoreboard objectives remove ${f(G.SCOREBOARD.localAnimTime, [a.name])}`), + // prettier-ignore + ...G.exportData.renderedAnimations.map(a => `scoreboard objectives remove ${f(G.SCOREBOARD.loopMode, [a.name])}`), + `tellraw @a ${G.TEXT.uninstallMessage.toString()}`, + ]) + } + + if (G.OUTDATED_RIG_WARNING_ENABLED) + folders.project.internalFunctions + // ANCHOR - func G.INTERNAL_PATH/mark_outdated_rig + .newFile('mark_outdated_rig.mcfunction', [ + `scoreboard players operation @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i}`, + `data modify entity @s Glowing set value 1`, + `data modify entity @s glow_color_override set value 16711680`, + ...(G.IS_SINGLE_ENTITY_RIG + ? [ + `data modify entity @s Glowing set value 1`, + `data modify entity @s glow_color_override set value 16711680`, + ] + : [ + `execute on passengers run data modify entity @s Glowing set value 1`, + `execute on passengers run data modify entity @s glow_color_override set value 16711680`, + ]), + `tellraw @a ${G.TEXT.errorOutOfDateRig}`, + ]) + + // !SECTION + + // ------------------------ + // SECTION - Tick functions + // ------------------------ + + folders.animatedJava.functions.newFile('tick.mcfunction', [ + `execute as @e[type=minecraft:item_display,tag=${G.TAGS.globalRigRoot}] run function #animated_java:rig_tick`, + ]) + + folders.project.internalFunctions + // ANCHOR - function G.INTERNAL_FUNCTIONS/tick + .chainNewFile('tick.mcfunction', [ + `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/tick_as_root`, + ]) + // ANCHOR - function G.INTERNAL_FUNCTIONS/tick_as_root + .chainNewFile('tick_as_root.mcfunction', [ + `execute unless score @s ${G.SCOREBOARD.rigLoaded} = @s ${G.SCOREBOARD.rigLoaded} run function ${G.INTERNAL_PATH}/on_load`, + `scoreboard players add @s ${G.SCOREBOARD.lifeTime} 1`, + `execute at @s on passengers run tp @s ~ ~ ~ ~ ~`, + `function ${G.INTERNAL_PATH}/animations/tick`, + `function #${G.PROJECT_PATH}/on_tick/as_root`, + ]) + + // !SECTION + + // -------------------------- + // SECTION - Summon Functions + // -------------------------- + + const internalSummonFolder = folders.project.internalFunctions.newFolder('summon') + folders.project.functions.newFile( + 'summon.mcfunction', + generateSummonFunction(internalSummonFolder) + ) + + internalSummonFolder + // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_root + .chainNewFile('as_root.mcfunction', [ + // Default argument values + `execute unless score #frame ${G.SCOREBOARD.i} = #frame ${G.SCOREBOARD.i} run scoreboard players set #frame ${G.SCOREBOARD.i} 0`, + `execute unless score #variant ${G.SCOREBOARD.i} = #variant ${ + G.SCOREBOARD.i + } run scoreboard players set #variant ${G.SCOREBOARD.i} ${G.VARIANTS.findIndex( + v => v.default + )}`, + `execute unless score #animation ${G.SCOREBOARD.i} = #animation ${G.SCOREBOARD.i} run scoreboard players set #animation ${G.SCOREBOARD.i} -1`, + + `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, + `scoreboard players set @s ${G.SCOREBOARD.rigLoaded} 1`, + `scoreboard players operation @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i}`, + `execute store result score @s ${G.SCOREBOARD.id} run scoreboard players add .aj.last_id ${G.SCOREBOARD.id} 1`, + `tp @s ~ ~ ~ ~ ~`, + G.IS_SINGLE_ENTITY_RIG + ? `execute at @s run function ${G.INTERNAL_PATH}/summon/as_rig_entities` + : `execute at @s on passengers run function ${G.INTERNAL_PATH}/summon/as_rig_entities`, + ...G.VARIANTS.map( + v => + `execute if score #variant ${G.SCOREBOARD.i} = $aj.${G.PROJECT_NAME}.variant.${v.name} ${G.SCOREBOARD.id} run function ${G.INTERNAL_PATH}/apply_variant/${v.name}/as_root` + ), + `execute if score #animation ${G.SCOREBOARD.i} matches 0.. run scoreboard players operation @s ${G.SCOREBOARD.animTime} = #frame ${G.SCOREBOARD.i}`, + ...G.exportData.renderedAnimations + .map(a => [ + `execute if score #animation ${G.SCOREBOARD.i} = $aj.${G.PROJECT_NAME}.animation.${a.name} ${G.SCOREBOARD.id} run function ${G.INTERNAL_PATH}/animations/${a.name}/apply_frame_as_root`, + `execute if score #animation ${G.SCOREBOARD.i} = $aj.${ + G.PROJECT_NAME + }.animation.${a.name} ${ + G.SCOREBOARD.id + } run scoreboard players operation @s ${f(G.SCOREBOARD.localAnimTime, [ + a.name, + ])} = #frame ${G.SCOREBOARD.i}`, + ]) + .reduce((a, b) => a.concat(b), []), + `execute at @s run function #${G.PROJECT_PATH}/on_summon/as_root`, + `tag @s remove ${G.TAGS.new}`, + // Reset scoreboard arguemnts + `scoreboard players reset #frame ${G.SCOREBOARD.i}`, + `scoreboard players reset #variant ${G.SCOREBOARD.i}`, + `scoreboard players reset #animation ${G.SCOREBOARD.i}`, + ]) + // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_rig_entities + .chainNewFile('as_rig_entities.mcfunction', [ + `scoreboard players operation @s ${G.SCOREBOARD.id} = .aj.last_id ${G.SCOREBOARD.id}`, + `tag @s remove ${G.TAGS.new}`, + `function #${G.PROJECT_PATH}/on_summon/as_rig_entities`, + `execute if entity @s[tag=${G.TAGS.boneEntity}] run function #${G.INTERNAL_PATH}/on_summon/as_bones`, + locatorCount > 0 + ? `execute if entity @s[tag=${G.TAGS.locatorOrigin}] run function ${G.INTERNAL_PATH}/summon/as_locator_origins` + : undefined, + cameraCount > 0 + ? `execute if entity @s[tag=${G.TAGS.cameraOrigin}] run function ${G.INTERNAL_PATH}/summon/as_camera_origins` + : undefined, + ]) + + if (locatorCount > 0) + internalSummonFolder + // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_locator_origins + .chainNewFile('as_locator_origins.mcfunction', [ + ...Object.values(G.exportData.rig.nodeMap) + .map(locator => + locator.type === 'locator' + ? `execute if entity @s[tag=${f(G.TAGS.namedLocatorOrigin, [ + locator.name, + ])}] run function ${G.INTERNAL_PATH}/summon/locator_${ + locator.name + }/as_origin` + : '' + ) + .filter(v => v), + `function #${G.PROJECT_PATH}/on_summon/as_locator_origins`, + ]) + + if (cameraCount > 0) + internalSummonFolder + // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_camera_origins + .chainNewFile('as_camera_origins.mcfunction', [ + ...Object.values(G.exportData.rig.nodeMap) + .map(camera => + camera.type === 'camera' + ? `execute if entity @s[tag=${f(G.TAGS.namedCameraOrigin, [ + camera.name, + ])}] run function ${G.INTERNAL_PATH}/summon/camera_${ + camera.name + }/as_origin` + : '' + ) + .filter(v => v), + `function #${G.PROJECT_PATH}/on_summon/as_camera_origins`, + ]) + + // ANCHOR - function G.PROJECT_PATH/summon/ + if (G.exportData.exporterSettings.include_variant_summon_functions.value === true) { + const variantSummonFolder = folders.project.functions.newFolder('summon') + for (const variant of G.VARIANTS) { + if (variant.default) continue + variantSummonFolder.newFile(`${variant.name}.mcfunction`, [ + `scoreboard players set #variant ${G.SCOREBOARD.i} ${G.VARIANTS.indexOf(variant)}`, + `function ${G.PROJECT_PATH}/summon`, + ]) + } + } + + // !SECTION + + // --------------------------------- + // SECTION - Apply Variant Functions + // --------------------------------- + + if (G.exportData.exporterSettings.include_apply_variant_functions.value === true) { + const applyVariantsFolder = folders.project.functions.newFolder('apply_variant') + const internalApplyVariantsFolder = + folders.project.internalFunctions.newFolder('apply_variant') + for (const variant of G.VARIANTS) { + // ANCHOR - function G.PROJECT_PATH/apply_variant/ + applyVariantsFolder.newFile(`${variant.name}.mcfunction`, [ + `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_root`, + `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( + G.TEXT.errorMustBeRunAsRoot.toString(), + [`${G.PROJECT_PATH}/apply_variant/${variant.name}`] + )}`, + ]) + internalApplyVariantsFolder + .newFolder(variant.name) + // ANCHOR - function G.INTERNAL_PATH/apply_variant//as_root + .chainNewFile(`as_root.mcfunction`, [ + G.IS_SINGLE_ENTITY_RIG + ? `function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_bone` + : `execute on passengers run function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_bone`, + ]) + // ANCHOR - function G.INTERNAL_PATH/apply_variant//as_bone + .chainNewFile(`as_bone.mcfunction`, [ + ...Object.entries(G.exportData.rig.nodeMap).map(([uuid, node]) => { + if (node.type !== 'bone') return + const included = variant.affectedBones.find(v => v.value === uuid) + if (!included && variant.affectedBonesIsAWhitelist) return + if (included && !variant.affectedBonesIsAWhitelist) return + + const variantBone = variant.default + ? node + : G.exportData.rig.variantModels[variant.name][uuid] + + return `execute if entity @s[tag=${f(G.TAGS.namedBoneEntity, [ + node.name, + ])}] run data modify entity @s item.tag.CustomModelData set value ${ + variantBone.customModelData + }` + }), + ]) + } + } + + // !SECTION + + // -------------------------- + // SECTION - Remove functions + // -------------------------- + + const removeFolder = folders.project.functions + .newFolder('remove') + // ANCHOR - function G.PROJECT_PATH/remove/this + .chainNewFile('this.mcfunction', [ + `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, + `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( + G.TEXT.errorMustBeRunAsRoot.toString(), + [`${G.PROJECT_PATH}/remove/this`] + )}`, + ]) + if (G.exportData.exporterSettings.include_remove_rigs_function.value === true) { + // ANCHOR - function G.PROJECT_PATH/remove/rigs + removeFolder.newFile('rigs.mcfunction', [ + `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, + ]) + } + if (G.exportData.exporterSettings.include_remove_all_function.value === true) { + // ANCHOR - function G.PROJECT_PATH/remove/all + removeFolder.newFile('all.mcfunction', [ + `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, + `kill @e[tag=${G.TAGS.rigEntity}]`, + ]) + } + folders.project.internalFunctions + .newFolder('remove') + // ANCHOR - function G.INTERNAL_PATH/remove/as_root + .newFile('as_root.mcfunction', [ + `execute at @s run function #${G.PROJECT_PATH}/on_remove/as_root`, + G.IS_SINGLE_ENTITY_RIG ? undefined : `execute on passengers on origin run kill @s`, + G.IS_SINGLE_ENTITY_RIG ? undefined : `execute on passengers run kill @s`, + `kill @s`, + ]) + + // !SECTION + + // ----------------------------- + // SECTION - Animation functions + // ----------------------------- + + const animationsFolder = folders.project.functions.newFolder('animations') + const internalAnimationsFolder = folders.project.internalFunctions.newFolder('animations') + + for (const anim of G.exportData.renderedAnimations) { + animationsFolder + .newFolder(anim.name) + // ANCHOR - func G.PROJECT_PATH:animations//play + .chainNewFile('play.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/play`), + `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, + `scoreboard players set @s ${f(G.SCOREBOARD.localAnimTime, [anim.name])} 0`, + `$scoreboard players set @s ${G.SCOREBOARD.tweenTime} $(tween_time)`, + ...(G.IS_SINGLE_ENTITY_RIG + ? [ + `data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `data modify entity @s interpolation_duration set value 1`, + ] + : [ + `execute on passengers run data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `execute on passengers run data modify entity @s interpolation_duration set value 1`, + ]), + `tag @s add ${f(G.TAGS.activeAnim, [anim.name])}`, + ]) + // ANCHOR - func G.PROJECT_PATH:animations//resume + .chainNewFile('resume.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), + `tag @s add ${f(G.TAGS.activeAnim, [anim.name])}`, + ]) + // ANCHOR - func G.PROJECT_PATH:animations//set_frame + .chainNewFile('set_frame.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), + ...(G.IS_SINGLE_ENTITY_RIG + ? [ + `data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `data modify entity @s interpolation_duration set value 1`, + ] + : [ + `execute on passengers run data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `execute on passengers run data modify entity @s interpolation_duration set value 1`, + ]), + ]) + // ANCHOR - func G.PROJECT_PATH:animations//next_frame + .chainNewFile('next_frame.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), + `execute store result storage animated_java:args frame int 1 run scoreboard players get @s ${G.SCOREBOARD.animTime}`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + ]) + // ANCHOR - func G.PROJECT_PATH:animations//pause + .chainNewFile('pause.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/pause`), + `tag @s remove ${f(G.TAGS.activeAnim, [anim.name])}`, + ]) + // ANCHOR - func G.PROJECT_PATH:animations//stop + .chainNewFile('stop.mcfunction', [ + mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/stop`), + `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, + `scoreboard players set @s ${f(G.SCOREBOARD.localAnimTime, [anim.name])} 0`, + `tag @s add ${f(G.TAGS.disableCommandKeyframes)}`, + ...(G.IS_SINGLE_ENTITY_RIG + ? [ + `data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `data modify entity @s interpolation_duration set value 1`, + ] + : [ + `execute on passengers run data modify entity @s interpolation_duration set value 0`, + `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, + `execute on passengers run data modify entity @s interpolation_duration set value 1`, + ]), + `tag @s remove ${f(G.TAGS.disableCommandKeyframes)}`, + `tag @s remove ${f(G.TAGS.activeAnim, [anim.name])}`, + ]) + } + + if ( + !G.IS_SINGLE_ENTITY_RIG && + G.exportData.exporterSettings.include_pause_all_animations_function.value === true + ) { + animationsFolder + // ANCHOR - function G.PROJECT_PATH:animations/pause_all + .chainNewFile('pause_all.mcfunction', [ + `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/animations/pause_all_as_root`, + `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( + G.TEXT.errorMustBeRunAsRoot.toString(), + [`${G.PROJECT_PATH}/animations/pause_all`] + )}`, + ]) + internalAnimationsFolder + // ANCHOR - function G.INTERNAL_PATH:animations/pause_all_as_root + .chainNewFile('pause_all_as_root.mcfunction', [ + ...G.exportData.renderedAnimations.map( + a => `function ${G.INTERNAL_PATH}/animations/${a.name}/pause_as_root` + ), + ]) + } + + // ANCHOR - function G.INTERNAL_PATH/animations/tick + internalAnimationsFolder.newFile('tick.mcfunction', [ + ...G.exportData.renderedAnimations.map( + anim => + `execute if entity @s[tag=${f(G.TAGS.activeAnim, [anim.name])}] run function ${ + G.INTERNAL_PATH + }/animations/${anim.name}/tick` + ), + ]) + + // ----------------------------------- + // SECTION - Animation Tree Generation + // ----------------------------------- + + function mustBeRootWarning(functionPath: string) { + return `execute if entity @s[tag=!${G.TAGS.rootEntity}] run return run tellraw @a ${f( + G.TEXT.errorMustBeRunAsRoot.toString(), + [functionPath] + )}` + } + + for (const anim of G.exportData.renderedAnimations) { + internalAnimationsFolder + .newFolder(`${anim.name}`) + // ANCHOR - func G.INTERNAL_PATH:animations//tick + .chainNewFile('tick.mcfunction', []) + // ANCHOR - func G.INTERNAL_PATH:animations//tick_tween + .chainNewFile('tick_tween.mcfunction', []) + // ANCHOR - func G.INTERNAL_PATH:animations//tick_animation + .chainNewFile('tick_animation.mcfunction', []) + // ANCHOR - func G.INTERNAL_PATH:animations//end + .chainNewFile('end.mcfunction', []) + // ANCHOR - func G.INTERNAL_PATH:animations//end_loop + .chainNewFile('end_loop.mcfunction', []) + } + // !SECTION + + // !SECTION +} diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/functions.ts b/exporters/datapackExporter/exporter/gen-1.20.2/functions.ts index 8dda9914..7366313d 100644 --- a/exporters/datapackExporter/exporter/gen-1.20.2/functions.ts +++ b/exporters/datapackExporter/exporter/gen-1.20.2/functions.ts @@ -1,833 +1,32 @@ -import { wrapNum } from '../util' -import { type IFolders } from './datapack' -import { Globals as G, JsonText, deepslate, formatStr as f, util } from './globals' -import { loadStorageGenerator } from './storageGenerator' +import { IFolders } from './datapack' +import { generateSummonFunction } from './functions/summonFunction' -type NbtCompound = InstanceType +export class MCFunction { + static all: MCFunction[] = [] -function getExportVersionId() { - return Math.round(Math.random() * 2 ** 31 - 1 - (Math.random() * 2 ** 31 - 1)) -} - -function generateBonePassenger(uuid: string, bone: AnimatedJava.IRenderedNodes['Bone']) { - const passenger = deepslate.NbtTag.fromString(bone.nbt) as NbtCompound - const default_pose = G.exportData.rig.defaultPose.find(pose => pose.uuid === uuid) - - passenger.set('id', new deepslate.NbtString('minecraft:item_display')) - - if (!passenger.get('Tags')) passenger.set('Tags', new deepslate.NbtList()) - const tags = passenger.get('Tags') as InstanceType - tags.add(new deepslate.NbtString(G.TAGS.new)) - tags.add(new deepslate.NbtString(G.TAGS.rigEntity)) - tags.add(new deepslate.NbtString(G.TAGS.boneEntity)) - tags.add(new deepslate.NbtString(f(G.TAGS.namedBoneEntity, [bone.name]))) - - passenger - .set('transformation', util.matrixToNbtFloatArray(default_pose.matrix)) - .set('interpolation_duration', new deepslate.NbtInt(G.DEFAULT_INTERPOLATION_DURATION)) - .set('item_display', new deepslate.NbtString('head')) - .set('teleport_duration', new deepslate.NbtInt(1)) - - if (!passenger.get('item')) passenger.set('item', new deepslate.NbtCompound()) - const item = passenger.get('item') as InstanceType - item.set('id', new deepslate.NbtString(G.RIG_ITEM)) - .set('Count', new deepslate.NbtByte(1)) - .set( - 'tag', - new deepslate.NbtCompound().set( - 'CustomModelData', - new deepslate.NbtInt(bone.customModelData) - ) - ) - - if (!passenger.get('CustomName')) - passenger.set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `bone`, color: 'white' }, - `[`, - { text: `${bone.name}`, color: 'yellow' }, - `]`, - ], - ]).toString() - ) - ) - - // FIXME - This doesn't account for animations, and it SHOULD - const maxHeight = Math.max(Math.abs(bone.boundingBox.min.y), Math.abs(bone.boundingBox.max.y)) - const maxWidth = Math.max( - Math.abs(bone.boundingBox.min.x), - Math.abs(bone.boundingBox.max.x), - Math.abs(bone.boundingBox.min.z), - Math.abs(bone.boundingBox.max.z) - ) - passenger - .set('height', new deepslate.NbtFloat(maxHeight)) - .set('width', new deepslate.NbtFloat(maxWidth)) - - return passenger -} - -function generateLocatorPassenger( - uuid: string, - locator: AnimatedJava.IRenderedNodes['Locator'], - internalSummonFolder: AnimatedJava.VirtualFolder -) { - const { roundToN } = AnimatedJava.API - const passenger = new deepslate.NbtCompound() - // const passenger = deepslate.NbtTag.fromString(locator.nbt) as NbtCompound - - passenger - .set('id', new deepslate.NbtString('minecraft:snowball')) - .set( - 'Tags', - new deepslate.NbtList([ - new deepslate.NbtString(G.TAGS.new), - new deepslate.NbtString(G.TAGS.rigEntity), - new deepslate.NbtString(G.TAGS.locatorOrigin), - new deepslate.NbtString(f(G.TAGS.namedLocatorOrigin, [locator.name])), - ]) - ) - .set( - 'Item', - new deepslate.NbtCompound() - .set('id', new deepslate.NbtString(G.RIG_ITEM)) - .set('Count', new deepslate.NbtByte(1)) - .set( - 'tag', - new deepslate.NbtCompound().set('CustomModelData', new deepslate.NbtInt(1)) - ) - ) - .set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `locator`, color: 'white' }, - `[`, - { text: `${locator.name}`, color: 'yellow' }, - `]`, - ], - ]).toString() - ) - ) - - const locatorEntityNbt = deepslate.NbtTag.fromString(locator.nbt) as NbtCompound - if (!locatorEntityNbt.get('Tags')) locatorEntityNbt.set('Tags', new deepslate.NbtList()) - const tags = locatorEntityNbt.get('Tags') as InstanceType - - tags.add(new deepslate.NbtString(G.TAGS.locatorEntity)) - tags.add(new deepslate.NbtString(f(G.TAGS.namedLocatorEntity, [locator.name]))) - tags.add(new deepslate.NbtString(G.TAGS.new)) - - if (!locatorEntityNbt.get('CustomName')) - locatorEntityNbt.set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `locatorEntity`, color: 'white' }, - `[`, - { text: `${locator.name}`, color: 'yellow' }, - `]`, - ], - ]).toString() - ) - ) - - function locatorToString(node: AnimatedJava.IAnimationNode) { - const pos = node.pos - const euler = new THREE.Euler().setFromQuaternion(node.rot, 'YXZ') - const rot = new THREE.Vector3(euler.x, euler.y, euler.z).multiplyScalar(180 / Math.PI) - return `tp @s ^${roundToN(pos.x, 100000)} ^${roundToN(pos.y, 100000)} ^${roundToN( - pos.z, - 100000 - )} ~${roundToN(wrapNum(-rot.y - 180, -180, 180), 100000)} ~${roundToN(-rot.x, 100000)}` - } - internalSummonFolder - .newFolder('locator_' + locator.name) - // ANCHOR - function G.PROJECT_PATH/summon/locator_/as_origin - .chainNewFile('as_origin.mcfunction', [ - // `say Locator Origin`, - `summon ${locator.entity_type} ~ ~ ~ ${locatorEntityNbt.toString()}`, - `execute as @e[type=${locator.entity_type},tag=${f(G.TAGS.namedLocatorEntity, [ - locator.name, - ])},tag=${G.TAGS.new},limit=1,distance=..1] run function ${ - G.INTERNAL_PATH - }/summon/locator_${locator.name}/as_entity`, - `data modify entity @s Owner set from storage animated_java Owner`, - `data remove storage animated_java Owner`, - ]) - // ANCHOR - function G.PROJECT_PATH/summon/locator_/as_entity - .chainNewFile('as_entity.mcfunction', [ - // `say Locator Entity`, - locatorToString(G.exportData.rig.defaultPose.find(v => v.uuid === uuid)), - `data modify storage animated_java Owner set from entity @s UUID`, - `tag @s remove ${G.TAGS.new}`, - `function #${G.PROJECT_PATH}/on_summon/as_locator_entities`, - ]) - - return passenger -} + file: AnimatedJava.VirtualFile -function generateCameraPassenger( - uuid: string, - camera: AnimatedJava.IRenderedNodes['Camera'], - internalSummonFolder: AnimatedJava.VirtualFolder -) { - const { roundToN } = AnimatedJava.API - const passenger = deepslate.NbtTag.fromString(camera.nbt) as NbtCompound - // const default_pose = G.exportData.rig.defaultPose.find(pose => pose.uuid === uuid) - - passenger - .set('id', new deepslate.NbtString('minecraft:snowball')) - .set( - 'Tags', - new deepslate.NbtList([ - new deepslate.NbtString(G.TAGS.new), - new deepslate.NbtString(G.TAGS.rigEntity), - new deepslate.NbtString(G.TAGS.cameraOrigin), - new deepslate.NbtString(f(G.TAGS.namedCameraOrigin, [camera.name])), - ]) - ) - .set( - 'Item', - new deepslate.NbtCompound() - .set('id', new deepslate.NbtString(G.RIG_ITEM)) - .set('Count', new deepslate.NbtByte(1)) - .set( - 'tag', - new deepslate.NbtCompound().set('CustomModelData', new deepslate.NbtInt(1)) - ) - ) - .set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `camera`, color: 'white' }, - `[`, - { text: `${camera.name}`, color: 'yellow' }, - `]`, - ], - ]).toString() - ) - ) - - const cameraEntityNbt = deepslate.NbtTag.fromString(camera.nbt) as NbtCompound - if (!cameraEntityNbt.get('Tags')) cameraEntityNbt.set('Tags', new deepslate.NbtList()) - const tags = cameraEntityNbt.get('Tags') as InstanceType - - tags.add(new deepslate.NbtString(G.TAGS.cameraEntity)) - tags.add(new deepslate.NbtString(f(G.TAGS.namedCameraEntity, [camera.name]))) - tags.add(new deepslate.NbtString(G.TAGS.new)) - - if (!cameraEntityNbt.get('CustomName')) - cameraEntityNbt - .set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `cameraEntity`, color: 'white' }, - `[`, - { text: `${camera.name}`, color: 'yellow' }, - `]`, - ], - ]).toString() - ) - ) - .set('teleport_duration', new deepslate.NbtInt(1)) - - function cameraToString(node: AnimatedJava.IAnimationNode) { - const pos = node.pos - const euler = new THREE.Euler().setFromQuaternion(node.rot, 'YXZ') - const rot = new THREE.Vector3(euler.x, euler.y, euler.z).multiplyScalar(180 / Math.PI) - return `tp @s ^${roundToN(pos.x, 100000)} ^${roundToN(pos.y, 100000)} ^${roundToN( - pos.z, - 100000 - )} ~${roundToN(wrapNum(-rot.y - 180, -180, 180), 100000)} ~${roundToN(-rot.x, 100000)}` + constructor( + public folder: AnimatedJava.VirtualFolder, + public name: string, + public content: string[] = [] + ) { + this.file = folder.newFile(`${name}.mcfunction`, this.content) + MCFunction.all.push(this) } - internalSummonFolder - .newFolder('camera_' + camera.name) - // ANCHOR - function G.PROJECT_PATH/summon/camera_/as_origin - .chainNewFile('as_origin.mcfunction', [ - // `say Camera Origin`, - `summon ${camera.entity_type} ~ ~ ~ ${cameraEntityNbt.toString()}`, - `execute as @e[type=${camera.entity_type},tag=${f(G.TAGS.namedCameraEntity, [ - camera.name, - ])},tag=${G.TAGS.new},limit=1,distance=..1] run function ${ - G.INTERNAL_PATH - }/summon/camera_${camera.name}/as_entity`, - `data modify entity @s Owner set from storage animated_java Owner`, - `data remove storage animated_java Owner`, - ]) - // ANCHOR - function G.PROJECT_PATH/summon/camera_/as_entity - .chainNewFile('as_entity.mcfunction', [ - // `say Camera Entity`, - cameraToString(G.exportData.rig.defaultPose.find(v => v.uuid === uuid)), - `data modify storage animated_java Owner set from entity @s UUID`, - `tag @s remove ${G.TAGS.new}`, - `function #${G.PROJECT_PATH}/on_summon/as_camera_entities`, - ]) - - return passenger -} -function generateSummonFunction(internalSummonFolder: AnimatedJava.VirtualFolder) { - let rootNbt = deepslate.NbtTag.fromString( - G.exportData.exporterSettings.root_entity_nbt.value - ) as NbtCompound - const passengers = new deepslate.NbtList() - - for (const [uuid, bone] of Object.entries(G.exportData.rig.nodeMap)) { - switch (bone.type) { - case 'bone': - passengers.add(generateBonePassenger(uuid, bone)) - break - case 'locator': - passengers.add(generateLocatorPassenger(uuid, bone, internalSummonFolder)) - break - case 'camera': - passengers.add(generateCameraPassenger(uuid, bone, internalSummonFolder)) - break - default: - // @ts-ignore - throw new Error(`Unknown bone type: ${bone.type}`) - } + getResourceLocation() { + const parsed = AnimatedJava.API.minecraft.parseResourcePath(this.file.path) + if (!parsed) + throw new Error(`Failed to parse resource path for mcfunction at '${this.file.path}'`) + return parsed.resourceLocation } - if (passengers.length === 1 && G.exportData.renderedAnimations.length === 0) { - rootNbt = passengers.get(0) as NbtCompound - } else { - rootNbt.set('Passengers', passengers) + toString() { + return `function ${this.getResourceLocation()}` } - - if (!rootNbt.get('Tags')) rootNbt.set('Tags', new deepslate.NbtList()) - const tags = rootNbt.get('Tags') as InstanceType - tags.add(new deepslate.NbtString(G.TAGS.new)) - tags.add(new deepslate.NbtString(G.TAGS.rigEntity)) - tags.add(new deepslate.NbtString(G.TAGS.rootEntity)) - tags.add(new deepslate.NbtString(G.TAGS.globalRigRoot)) - - if (!rootNbt.get('CustomName')) - rootNbt - .set( - 'CustomName', - new deepslate.NbtString( - new JsonText([ - { text: '[', color: 'gray' }, - { text: 'AJ', color: 'aqua' }, - `] `, - [ - '', - { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, - `.`, - { text: `root`, color: 'white' }, - ], - ]).toString() - ) - ) - .set('teleport_duration', new deepslate.NbtInt(1)) - - // ANCHOR - function G.PROJECT_PATH/summon - return [ - `summon minecraft:item_display ~ ~ ~ ${rootNbt.toString()}`, - `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity},tag=${G.TAGS.new},limit=1,distance=..0.1] run function ${G.INTERNAL_PATH}/summon/as_root`, - ] } export function generateFunctions(folders: IFolders) { - const { generateSearchTree } = AnimatedJava.API - const { generateStorage } = loadStorageGenerator() - const cameraCount = Object.values(G.exportData.rig.nodeMap).filter( - v => v.type === 'camera' - ).length - const locatorCount = Object.values(G.exportData.rig.nodeMap).filter( - v => v.type === 'locator' - ).length - - // ------------------------ - // SECTION - Load functions - // ------------------------ - - folders.project.internalFunctions - // ANCHOR - function G.PROJECT_PATH/load - .chainNewFile('load.mcfunction', [ - // Scoreboard objectives - ...Object.values(G.SCOREBOARD) - .filter(s => !s.includes('%s')) - .map(s => `scoreboard objectives add ${s} dummy`), - // prettier-ignore - ...G.exportData.renderedAnimations.map(a => `scoreboard objectives add ${f(G.SCOREBOARD.localAnimTime, [a.name])} dummy`), - // prettier-ignore - ...G.exportData.renderedAnimations.map(a => `scoreboard objectives add ${f(G.SCOREBOARD.loopMode, [a.name])} dummy`), - // prettier-ignore - ...G.exportData.renderedAnimations.map((a, i) => `scoreboard players set $aj.${G.PROJECT_NAME}.animation.${a.name} ${G.SCOREBOARD.id} ${i}`), - // prettier-ignore - ...G.VARIANTS.map((v, i) => `scoreboard players set $aj.${G.PROJECT_NAME}.variant.${v.name} ${G.SCOREBOARD.id} ${i}`), - // Variable initialization - `scoreboard players add .aj.last_id ${G.SCOREBOARD.id} 0`, - // prettier-ignore - ...G.LOOP_MODES.map((mode, i) => `scoreboard players set $aj.loop_mode.${mode} ${G.SCOREBOARD.i} ${i}`), - // version ID - `scoreboard players set ${G.SCOREBOARD.exportVersion} ${ - G.SCOREBOARD.i - } ${getExportVersionId()}`, - // load function tag - `scoreboard players reset * ${G.SCOREBOARD.rigLoaded}`, - `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/on_load`, - // load storage - `data modify storage animated_java:${ - G.PROJECT_NAME - } set value ${generateStorage().toString()}`, - ]) - // ANCHOR - func G.INTERNAL_PATH/on_load - .chainNewFile('on_load.mcfunction', [ - `scoreboard players set @s ${G.SCOREBOARD.rigLoaded} 1`, - G.OUTDATED_RIG_WARNING_ENABLED - ? `execute unless score @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i} at @s run function ${G.INTERNAL_PATH}/mark_outdated_rig` - : undefined, - ]) - - if (G.exportData.exporterSettings.include_uninstall_function.value === true) { - // ANCHOR - function NAMESPACE:uninstall - folders.project.functions.newFile('uninstall.mcfunction', [ - // Scoreboard objectives - ...Object.values(G.SCOREBOARD) - .filter(s => !s.includes('%s')) - .map(s => `scoreboard objectives remove ${s}`), - // prettier-ignore - ...G.exportData.renderedAnimations.map(a => `scoreboard objectives remove ${f(G.SCOREBOARD.localAnimTime, [a.name])}`), - // prettier-ignore - ...G.exportData.renderedAnimations.map(a => `scoreboard objectives remove ${f(G.SCOREBOARD.loopMode, [a.name])}`), - `tellraw @a ${G.TEXT.uninstallMessage.toString()}`, - ]) - } - - if (G.OUTDATED_RIG_WARNING_ENABLED) - folders.project.internalFunctions - // ANCHOR - func G.INTERNAL_PATH/mark_outdated_rig - .newFile('mark_outdated_rig.mcfunction', [ - `scoreboard players operation @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i}`, - `data modify entity @s Glowing set value 1`, - `data modify entity @s glow_color_override set value 16711680`, - ...(G.IS_SINGLE_ENTITY_RIG - ? [ - `data modify entity @s Glowing set value 1`, - `data modify entity @s glow_color_override set value 16711680`, - ] - : [ - `execute on passengers run data modify entity @s Glowing set value 1`, - `execute on passengers run data modify entity @s glow_color_override set value 16711680`, - ]), - `tellraw @a ${G.TEXT.errorOutOfDateRig}`, - ]) - - // !SECTION - - // ------------------------ - // SECTION - Tick functions - // ------------------------ - - folders.animatedJava.functions.newFile('tick.mcfunction', [ - `execute as @e[type=minecraft:item_display,tag=${G.TAGS.globalRigRoot}] run function #animated_java:rig_tick`, - ]) - - folders.project.internalFunctions - // ANCHOR - function G.INTERNAL_FUNCTIONS/tick - .chainNewFile('tick.mcfunction', [ - `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/tick_as_root`, - ]) - // ANCHOR - function G.INTERNAL_FUNCTIONS/tick_as_root - .chainNewFile('tick_as_root.mcfunction', [ - `execute unless score @s ${G.SCOREBOARD.rigLoaded} = @s ${G.SCOREBOARD.rigLoaded} run function ${G.INTERNAL_PATH}/on_load`, - `scoreboard players add @s ${G.SCOREBOARD.lifeTime} 1`, - `execute at @s on passengers run tp @s ~ ~ ~ ~ ~`, - `function ${G.INTERNAL_PATH}/animations/tick`, - `function #${G.PROJECT_PATH}/on_tick/as_root`, - ]) - - // !SECTION - - // -------------------------- - // SECTION - Summon Functions - // -------------------------- - - const internalSummonFolder = folders.project.internalFunctions.newFolder('summon') - folders.project.functions.newFile( - 'summon.mcfunction', - generateSummonFunction(internalSummonFolder) - ) - - internalSummonFolder - // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_root - .chainNewFile('as_root.mcfunction', [ - // Default argument values - `execute unless score #frame ${G.SCOREBOARD.i} = #frame ${G.SCOREBOARD.i} run scoreboard players set #frame ${G.SCOREBOARD.i} 0`, - `execute unless score #variant ${G.SCOREBOARD.i} = #variant ${ - G.SCOREBOARD.i - } run scoreboard players set #variant ${G.SCOREBOARD.i} ${G.VARIANTS.findIndex( - v => v.default - )}`, - `execute unless score #animation ${G.SCOREBOARD.i} = #animation ${G.SCOREBOARD.i} run scoreboard players set #animation ${G.SCOREBOARD.i} -1`, - - `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, - `scoreboard players set @s ${G.SCOREBOARD.rigLoaded} 1`, - `scoreboard players operation @s ${G.SCOREBOARD.exportVersion} = ${G.SCOREBOARD.exportVersion} ${G.SCOREBOARD.i}`, - `execute store result score @s ${G.SCOREBOARD.id} run scoreboard players add .aj.last_id ${G.SCOREBOARD.id} 1`, - `tp @s ~ ~ ~ ~ ~`, - G.IS_SINGLE_ENTITY_RIG - ? `execute at @s run function ${G.INTERNAL_PATH}/summon/as_rig_entities` - : `execute at @s on passengers run function ${G.INTERNAL_PATH}/summon/as_rig_entities`, - ...G.VARIANTS.map( - v => - `execute if score #variant ${G.SCOREBOARD.i} = $aj.${G.PROJECT_NAME}.variant.${v.name} ${G.SCOREBOARD.id} run function ${G.INTERNAL_PATH}/apply_variant/${v.name}/as_root` - ), - `execute if score #animation ${G.SCOREBOARD.i} matches 0.. run scoreboard players operation @s ${G.SCOREBOARD.animTime} = #frame ${G.SCOREBOARD.i}`, - ...G.exportData.renderedAnimations - .map(a => [ - `execute if score #animation ${G.SCOREBOARD.i} = $aj.${G.PROJECT_NAME}.animation.${a.name} ${G.SCOREBOARD.id} run function ${G.INTERNAL_PATH}/animations/${a.name}/apply_frame_as_root`, - `execute if score #animation ${G.SCOREBOARD.i} = $aj.${ - G.PROJECT_NAME - }.animation.${a.name} ${ - G.SCOREBOARD.id - } run scoreboard players operation @s ${f(G.SCOREBOARD.localAnimTime, [ - a.name, - ])} = #frame ${G.SCOREBOARD.i}`, - ]) - .reduce((a, b) => a.concat(b), []), - `execute at @s run function #${G.PROJECT_PATH}/on_summon/as_root`, - `tag @s remove ${G.TAGS.new}`, - // Reset scoreboard arguemnts - `scoreboard players reset #frame ${G.SCOREBOARD.i}`, - `scoreboard players reset #variant ${G.SCOREBOARD.i}`, - `scoreboard players reset #animation ${G.SCOREBOARD.i}`, - ]) - // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_rig_entities - .chainNewFile('as_rig_entities.mcfunction', [ - `scoreboard players operation @s ${G.SCOREBOARD.id} = .aj.last_id ${G.SCOREBOARD.id}`, - `tag @s remove ${G.TAGS.new}`, - `function #${G.PROJECT_PATH}/on_summon/as_rig_entities`, - `execute if entity @s[tag=${G.TAGS.boneEntity}] run function #${G.INTERNAL_PATH}/on_summon/as_bones`, - locatorCount > 0 - ? `execute if entity @s[tag=${G.TAGS.locatorOrigin}] run function ${G.INTERNAL_PATH}/summon/as_locator_origins` - : undefined, - cameraCount > 0 - ? `execute if entity @s[tag=${G.TAGS.cameraOrigin}] run function ${G.INTERNAL_PATH}/summon/as_camera_origins` - : undefined, - ]) - - if (locatorCount > 0) - internalSummonFolder - // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_locator_origins - .chainNewFile('as_locator_origins.mcfunction', [ - ...Object.values(G.exportData.rig.nodeMap) - .map(locator => - locator.type === 'locator' - ? `execute if entity @s[tag=${f(G.TAGS.namedLocatorOrigin, [ - locator.name, - ])}] run function ${G.INTERNAL_PATH}/summon/locator_${ - locator.name - }/as_origin` - : '' - ) - .filter(v => v), - `function #${G.PROJECT_PATH}/on_summon/as_locator_origins`, - ]) - - if (cameraCount > 0) - internalSummonFolder - // ANCHOR - function G.INTERNAL_FUNCTIONS/summon/as_camera_origins - .chainNewFile('as_camera_origins.mcfunction', [ - ...Object.values(G.exportData.rig.nodeMap) - .map(camera => - camera.type === 'camera' - ? `execute if entity @s[tag=${f(G.TAGS.namedCameraOrigin, [ - camera.name, - ])}] run function ${G.INTERNAL_PATH}/summon/camera_${ - camera.name - }/as_origin` - : '' - ) - .filter(v => v), - `function #${G.PROJECT_PATH}/on_summon/as_camera_origins`, - ]) - - // ANCHOR - function G.PROJECT_PATH/summon/ - if (G.exportData.exporterSettings.include_variant_summon_functions.value === true) { - const variantSummonFolder = folders.project.functions.newFolder('summon') - for (const variant of G.VARIANTS) { - if (variant.default) continue - variantSummonFolder.newFile(`${variant.name}.mcfunction`, [ - `scoreboard players set #variant ${G.SCOREBOARD.i} ${G.VARIANTS.indexOf(variant)}`, - `function ${G.PROJECT_PATH}/summon`, - ]) - } - } - - // !SECTION - - // --------------------------------- - // SECTION - Apply Variant Functions - // --------------------------------- - - if (G.exportData.exporterSettings.include_apply_variant_functions.value === true) { - const applyVariantsFolder = folders.project.functions.newFolder('apply_variant') - const internalApplyVariantsFolder = - folders.project.internalFunctions.newFolder('apply_variant') - for (const variant of G.VARIANTS) { - // ANCHOR - function G.PROJECT_PATH/apply_variant/ - applyVariantsFolder.newFile(`${variant.name}.mcfunction`, [ - `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_root`, - `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( - G.TEXT.errorMustBeRunAsRoot.toString(), - [`${G.PROJECT_PATH}/apply_variant/${variant.name}`] - )}`, - ]) - internalApplyVariantsFolder - .newFolder(variant.name) - // ANCHOR - function G.INTERNAL_PATH/apply_variant//as_root - .chainNewFile(`as_root.mcfunction`, [ - G.IS_SINGLE_ENTITY_RIG - ? `function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_bone` - : `execute on passengers run function ${G.INTERNAL_PATH}/apply_variant/${variant.name}/as_bone`, - ]) - // ANCHOR - function G.INTERNAL_PATH/apply_variant//as_bone - .chainNewFile(`as_bone.mcfunction`, [ - ...Object.entries(G.exportData.rig.nodeMap).map(([uuid, node]) => { - if (node.type !== 'bone') return - const included = variant.affectedBones.find(v => v.value === uuid) - if (!included && variant.affectedBonesIsAWhitelist) return - if (included && !variant.affectedBonesIsAWhitelist) return - - const variantBone = variant.default - ? node - : G.exportData.rig.variantModels[variant.name][uuid] - - return `execute if entity @s[tag=${f(G.TAGS.namedBoneEntity, [ - node.name, - ])}] run data modify entity @s item.tag.CustomModelData set value ${ - variantBone.customModelData - }` - }), - ]) - } - } - - // !SECTION - - // -------------------------- - // SECTION - Remove functions - // -------------------------- - - const removeFolder = folders.project.functions - .newFolder('remove') - // ANCHOR - function G.PROJECT_PATH/remove/this - .chainNewFile('this.mcfunction', [ - `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, - `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( - G.TEXT.errorMustBeRunAsRoot.toString(), - [`${G.PROJECT_PATH}/remove/this`] - )}`, - ]) - if (G.exportData.exporterSettings.include_remove_rigs_function.value === true) { - // ANCHOR - function G.PROJECT_PATH/remove/rigs - removeFolder.newFile('rigs.mcfunction', [ - `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, - ]) - } - if (G.exportData.exporterSettings.include_remove_all_function.value === true) { - // ANCHOR - function G.PROJECT_PATH/remove/all - removeFolder.newFile('all.mcfunction', [ - `execute as @e[type=minecraft:item_display,tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/remove/as_root`, - `kill @e[tag=${G.TAGS.rigEntity}]`, - ]) - } - folders.project.internalFunctions - .newFolder('remove') - // ANCHOR - function G.INTERNAL_PATH/remove/as_root - .newFile('as_root.mcfunction', [ - `execute at @s run function #${G.PROJECT_PATH}/on_remove/as_root`, - G.IS_SINGLE_ENTITY_RIG ? undefined : `execute on passengers on origin run kill @s`, - G.IS_SINGLE_ENTITY_RIG ? undefined : `execute on passengers run kill @s`, - `kill @s`, - ]) - - // !SECTION - - // ----------------------------- - // SECTION - Animation functions - // ----------------------------- - - const animationsFolder = folders.project.functions.newFolder('animations') - const internalAnimationsFolder = folders.project.internalFunctions.newFolder('animations') - - for (const anim of G.exportData.renderedAnimations) { - animationsFolder - .newFolder(anim.name) - // ANCHOR - func G.PROJECT_PATH:animations//play - .chainNewFile('play.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/play`), - `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, - `scoreboard players set @s ${f(G.SCOREBOARD.localAnimTime, [anim.name])} 0`, - `$scoreboard players set @s ${G.SCOREBOARD.tweenTime} $(tween_time)`, - ...(G.IS_SINGLE_ENTITY_RIG - ? [ - `data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `data modify entity @s interpolation_duration set value 1`, - ] - : [ - `execute on passengers run data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `execute on passengers run data modify entity @s interpolation_duration set value 1`, - ]), - `tag @s add ${f(G.TAGS.activeAnim, [anim.name])}`, - ]) - // ANCHOR - func G.PROJECT_PATH:animations//resume - .chainNewFile('resume.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), - `tag @s add ${f(G.TAGS.activeAnim, [anim.name])}`, - ]) - // ANCHOR - func G.PROJECT_PATH:animations//set_frame - .chainNewFile('set_frame.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), - ...(G.IS_SINGLE_ENTITY_RIG - ? [ - `data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `data modify entity @s interpolation_duration set value 1`, - ] - : [ - `execute on passengers run data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `execute on passengers run data modify entity @s interpolation_duration set value 1`, - ]), - ]) - // ANCHOR - func G.PROJECT_PATH:animations//next_frame - .chainNewFile('next_frame.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/resume`), - `execute store result storage animated_java:args frame int 1 run scoreboard players get @s ${G.SCOREBOARD.animTime}`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - ]) - // ANCHOR - func G.PROJECT_PATH:animations//pause - .chainNewFile('pause.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/pause`), - `tag @s remove ${f(G.TAGS.activeAnim, [anim.name])}`, - ]) - // ANCHOR - func G.PROJECT_PATH:animations//stop - .chainNewFile('stop.mcfunction', [ - mustBeRootWarning(`${G.PROJECT_PATH}/animations/${anim.name}/stop`), - `scoreboard players set @s ${G.SCOREBOARD.animTime} 0`, - `scoreboard players set @s ${f(G.SCOREBOARD.localAnimTime, [anim.name])} 0`, - `tag @s add ${f(G.TAGS.disableCommandKeyframes)}`, - ...(G.IS_SINGLE_ENTITY_RIG - ? [ - `data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `data modify entity @s interpolation_duration set value 1`, - ] - : [ - `execute on passengers run data modify entity @s interpolation_duration set value 0`, - `$function ${G.PROJECT_PATH}/animations/${anim.name}/set_frame with storage animated_java:${G.PROJECT_NAME} animation.${anim.name}[$(frame)]`, - `execute on passengers run data modify entity @s interpolation_duration set value 1`, - ]), - `tag @s remove ${f(G.TAGS.disableCommandKeyframes)}`, - `tag @s remove ${f(G.TAGS.activeAnim, [anim.name])}`, - ]) - } - - if ( - !G.IS_SINGLE_ENTITY_RIG && - G.exportData.exporterSettings.include_pause_all_animations_function.value === true - ) { - animationsFolder - // ANCHOR - function G.PROJECT_PATH:animations/pause_all - .chainNewFile('pause_all.mcfunction', [ - `execute if entity @s[tag=${G.TAGS.rootEntity}] run function ${G.INTERNAL_PATH}/animations/pause_all_as_root`, - `execute if entity @s[tag=!${G.TAGS.rootEntity}] run tellraw @a ${f( - G.TEXT.errorMustBeRunAsRoot.toString(), - [`${G.PROJECT_PATH}/animations/pause_all`] - )}`, - ]) - internalAnimationsFolder - // ANCHOR - function G.INTERNAL_PATH:animations/pause_all_as_root - .chainNewFile('pause_all_as_root.mcfunction', [ - ...G.exportData.renderedAnimations.map( - a => `function ${G.INTERNAL_PATH}/animations/${a.name}/pause_as_root` - ), - ]) - } - - // ANCHOR - function G.INTERNAL_PATH/animations/tick - internalAnimationsFolder.newFile('tick.mcfunction', [ - ...G.exportData.renderedAnimations.map( - anim => - `execute if entity @s[tag=${f(G.TAGS.activeAnim, [anim.name])}] run function ${ - G.INTERNAL_PATH - }/animations/${anim.name}/tick` - ), - ]) - - // ----------------------------------- - // SECTION - Animation Tree Generation - // ----------------------------------- - - function mustBeRootWarning(functionPath: string) { - return `execute if entity @s[tag=!${G.TAGS.rootEntity}] run return run tellraw @a ${f( - G.TEXT.errorMustBeRunAsRoot.toString(), - [functionPath] - )}` - } - - for (const anim of G.exportData.renderedAnimations) { - internalAnimationsFolder - .newFolder(`${anim.name}`) - // ANCHOR - func G.INTERNAL_PATH:animations//tick - .chainNewFile('tick.mcfunction', []) - // ANCHOR - func G.INTERNAL_PATH:animations//tick_tween - .chainNewFile('tick_tween.mcfunction', []) - // ANCHOR - func G.INTERNAL_PATH:animations//tick_animation - .chainNewFile('tick_animation.mcfunction', []) - // ANCHOR - func G.INTERNAL_PATH:animations//end - .chainNewFile('end.mcfunction', []) - // ANCHOR - func G.INTERNAL_PATH:animations//end_loop - .chainNewFile('end_loop.mcfunction', []) - } - // !SECTION - - // !SECTION + generateSummonFunction(folders) } diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/functions/summonFunction.ts b/exporters/datapackExporter/exporter/gen-1.20.2/functions/summonFunction.ts new file mode 100644 index 00000000..90b38a44 --- /dev/null +++ b/exporters/datapackExporter/exporter/gen-1.20.2/functions/summonFunction.ts @@ -0,0 +1,133 @@ +import { IFolders } from '../datapack' +import { MCFunction } from '../functions' +import { Globals as G, JsonText, deepslate, formatStr as f, util } from '../globals' + +type NbtCompound = InstanceType +type NbtList = InstanceType + +const { NbtCompound, NbtTag, NbtList, NbtString, NbtInt, NbtFloat } = deepslate + +export function generateSummonFunction(folders: IFolders) { + const passengers = new NbtList() + + for (const [uuid, node] of Object.entries(G.exportData.rig.nodeMap)) { + switch (node.type) { + case 'bone': + passengers.add(generateBonePassenger(uuid, node)) + break + default: + throw new Error(`Unsupported node type '${node.type}'`) + } + } + + let rootEntityNbt = NbtTag.fromString( + G.exportData.exporterSettings.root_entity_nbt.value + ) as NbtCompound + + // Single entity export mode. + if (G.IS_SINGLE_ENTITY_RIG) { + rootEntityNbt = passengers.get(0) as NbtCompound + } else { + rootEntityNbt.set('Passengers', passengers) + } + + // Root Tags + let rootTags = rootEntityNbt.get('Tags') as NbtList | undefined + if (!rootTags) { + rootTags = new NbtList() + rootEntityNbt.set('Tags', rootTags) + } + rootTags.add(new NbtString(G.TAGS.new)) + rootTags.add(new NbtString(G.TAGS.rigEntity)) + rootTags.add(new NbtString(G.TAGS.globalRigRoot)) + rootTags.add(new NbtString(G.TAGS.rootEntity)) + + // Custom Name + if (!rootEntityNbt.get('CustomName')) { + rootEntityNbt.set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + ['', { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, `.root`], + ]).toString() + ) + ) + } + + rootEntityNbt.set('teleport_duration', new deepslate.NbtInt(1)) + + new MCFunction(folders.project.functions, 'summon', [ + `summon minecraft:item_display ~ ~ ~ ${rootEntityNbt.toString()}`, + `execute as @e[type=minecraft:item_display,distance=..0.01,tag=${G.TAGS.new},limit=1] run function ${G.INTERNAL_PATH}/summon/as_root`, + ]) +} + +function generateBonePassenger(uuid: string, node: AnimatedJava.IRenderedNodes['Bone']) { + const passenger = NbtTag.fromString(node.nbt) as NbtCompound + const defaultPose = G.exportData.rig.defaultPose.find(pose => pose.uuid === uuid) + if (!defaultPose) throw new Error(`Failed to find default pose for bone '${uuid}'`) + + passenger.set('id', new NbtString('minecraft:item_display')) + + let tags = passenger.get('Tags') as NbtList | undefined + if (!tags) { + tags = new NbtList() + passenger.set('Tags', tags) + } + tags.add(new NbtString(G.TAGS.new)) + tags.add(new NbtString(G.TAGS.rigEntity)) + tags.add(new NbtString(G.TAGS.boneEntity)) + tags.add(new NbtString(f(G.TAGS.namedBoneEntity, [node.name]))) + + passenger + .set('transformation', util.matrixToNbtFloatArray(defaultPose.matrix)) + .set('interpolation_duration', new NbtInt(G.DEFAULT_INTERPOLATION_DURATION)) + .set('item_display', new NbtString('head')) + .set('teleport_duration', new NbtInt(1)) + + let item = passenger.get('item') as NbtCompound | undefined + if (!item) { + item = new NbtCompound() + passenger.set('item', item) + } + item.set('id', new NbtString(G.RIG_ITEM)) + .set('Count', new NbtInt(1)) + .set('tag', new NbtCompound().set('CustomModelData', new NbtInt(node.customModelData))) + + if (!passenger.get('CustomName')) { + passenger.set( + 'CustomName', + new deepslate.NbtString( + new JsonText([ + { text: '[', color: 'gray' }, + { text: 'AJ', color: 'aqua' }, + `] `, + [ + '', + { text: `${G.PROJECT_NAME}`, color: 'light_purple' }, + `.`, + { text: `bone`, color: 'white' }, + `[`, + { text: `${node.name}`, color: 'yellow' }, + `]`, + ], + ]).toString() + ) + ) + } + + // FIXME - This doesn't account for animations, and it SHOULD + const maxHeight = Math.max(Math.abs(node.boundingBox.min.y), Math.abs(node.boundingBox.max.y)) + const maxWidth = Math.max( + Math.abs(node.boundingBox.min.x), + Math.abs(node.boundingBox.max.x), + Math.abs(node.boundingBox.min.z), + Math.abs(node.boundingBox.max.z) + ) + passenger.set('height', new NbtFloat(maxHeight)).set('width', new NbtFloat(maxWidth)) + + return passenger +} diff --git a/exporters/datapackExporter/exporter/gen-1.20.2/globals.ts b/exporters/datapackExporter/exporter/gen-1.20.2/globals.ts index 42ba741d..599ddf09 100644 --- a/exporters/datapackExporter/exporter/gen-1.20.2/globals.ts +++ b/exporters/datapackExporter/exporter/gen-1.20.2/globals.ts @@ -1,6 +1,6 @@ import { loadJSONText } from './text' import { getScoreboards } from './scoreboards' -import { getTags } from './entity_tags' +import { getTags } from './entityTags' import { loadUtil } from '../util' export let deepslate = AnimatedJava.API.deepslate diff --git a/src/minecraft/util.ts b/src/minecraft/util.ts index 6b38fd09..629ae91f 100644 --- a/src/minecraft/util.ts +++ b/src/minecraft/util.ts @@ -18,11 +18,11 @@ export function isValidDataPackMcMeta(path: string) { } export function isValidResourcePackPath(path: string) { - const parsed = parseResourcePackPath(path) + const parsed = parseResourcePath(path) return parsed && parsed.namespace && parsed.resourcePath } -export function parseResourcePackPath(path: string) { +export function parseResourcePath(path: string) { path = path.replace(/[\\/]/g, PathModule.sep) const parts = path.split(PathModule.sep) diff --git a/src/mods/animationMod.ts b/src/mods/animationMod.ts index 23514d7c..30d58446 100644 --- a/src/mods/animationMod.ts +++ b/src/mods/animationMod.ts @@ -1,6 +1,6 @@ import { ajModelFormat } from '../modelFormat' import { roundToN } from '../util/misc' -import { createBlockbenchMod, createPropertySubscribable } from '../util/moddingTools' +import { createBlockbenchMod } from '../util/moddingTools' createBlockbenchMod( 'animated_java:animation/affected_bones', diff --git a/src/projectSettings.ts b/src/projectSettings.ts index e6754bc4..57e2d9ab 100644 --- a/src/projectSettings.ts +++ b/src/projectSettings.ts @@ -184,8 +184,8 @@ export function getDefaultProjectSettings(): IAnimatedJavaProjectSettings { defaultValue: 0, options: [ { - name: '1.20.1', - value: '1.20.1', + name: '1.20.2', + value: '1.20.2', }, { name: '1.20', diff --git a/src/rendering/modelRenderer.ts b/src/rendering/modelRenderer.ts index 926227b5..6d3fdbc1 100644 --- a/src/rendering/modelRenderer.ts +++ b/src/rendering/modelRenderer.ts @@ -1,4 +1,4 @@ -import { parseResourcePackPath, safeFunctionName } from '../minecraft/util' +import { parseResourcePath, safeFunctionName } from '../minecraft/util' import { ProgressBarController } from '../util/progress' import { Variant } from '../variants' import { @@ -247,12 +247,12 @@ function renderCube(cube: Cube, rig: IRenderedRig, model: IRenderedModel) { export function getTextureResourceLocation(texture: Texture, rig: IRenderedRig) { if (texture.path && fs.existsSync(texture.path)) { - const parsed = parseResourcePackPath(texture.path) + const parsed = parseResourcePath(texture.path) if (parsed) return parsed } const path = PathModule.join(rig.textureExportFolder, safeFunctionName(texture.name) + '.png') - const parsed = parseResourcePackPath(path) + const parsed = parseResourcePath(path) if (parsed) return parsed console.error(texture) @@ -287,7 +287,7 @@ function renderGroup(group: Group, rig: IRenderedRig) { const parentId = (group.parent instanceof Group ? group.parent.uuid : group.parent)! const path = PathModule.join(rig.modelExportFolder, group.name + `.json`) - const parsed = parseResourcePackPath(path) + const parsed = parseResourcePath(path) if (!parsed) { console.error(group) @@ -430,7 +430,7 @@ function renderVariantModels(variant: Variant, rig: IRenderedRig) { const parsed = PathModule.parse(bone.modelPath) const modelPath = PathModule.join(parsed.dir, variant.name, `${bone.name}.json`) - const parsedModelPath = parseResourcePackPath(modelPath) + const parsedModelPath = parseResourcePath(modelPath) if (!parsedModelPath) throw new Error(`Invalid variant model path: ${modelPath}`) bones[uuid] = { diff --git a/test_ajmodels/armor_stand.ajmodel b/test_ajmodels/armor_stand.ajmodel index 8e434836..657a6ef2 100644 --- a/test_ajmodels/armor_stand.ajmodel +++ b/test_ajmodels/armor_stand.ajmodel @@ -502,6 +502,7 @@ "to": [5, 32, 5], "autouv": 0, "color": 0, + "visibility": false, "origin": [0, 23, 0], "faces": { "north": { @@ -543,6 +544,7 @@ "to": [5, 23.75, 3], "autouv": 0, "color": 0, + "visibility": false, "origin": [0, 24, 0], "faces": { "north": { @@ -584,6 +586,7 @@ "to": [-3, 25, 3], "autouv": 0, "color": 0, + "visibility": false, "origin": [-6, 23, 0], "faces": { "north": { @@ -625,6 +628,7 @@ "to": [9, 25, 3], "autouv": 0, "color": 0, + "visibility": false, "origin": [6, 23, 0], "faces": { "north": { @@ -666,6 +670,7 @@ "to": [4.5, 17, 2.5], "autouv": 0, "color": 0, + "visibility": false, "origin": [0, 24, 0], "faces": { "north": { @@ -707,6 +712,7 @@ "to": [4.25, 12, 2.375], "autouv": 0, "color": 0, + "visibility": false, "origin": [2, 12, 0], "faces": { "north": { @@ -748,6 +754,7 @@ "to": [0.5, 12, 2.375], "autouv": 0, "color": 0, + "visibility": false, "origin": [-2, 12, 0], "faces": { "north": { @@ -789,6 +796,7 @@ "to": [4.75, 6, 3], "autouv": 0, "color": 0, + "visibility": false, "origin": [2, 12, 0], "faces": { "north": { @@ -830,6 +838,7 @@ "to": [1, 6, 3], "autouv": 0, "color": 0, + "visibility": false, "origin": [-2, 12, 0], "faces": { "north": { @@ -970,7 +979,7 @@ "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", "export": true, "mirror_uv": false, - "isOpen": false, + "isOpen": true, "locked": false, "visibility": true, "autouv": 0, @@ -998,7 +1007,7 @@ "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", "export": true, "mirror_uv": false, - "isOpen": false, + "isOpen": true, "locked": false, "visibility": true, "autouv": 0,