fixes
This commit is contained in:
+58
-13
@@ -149,6 +149,37 @@
|
||||
setProjectStringMap(ACTOR_NAMES_PROPERTY, names);
|
||||
}
|
||||
|
||||
function remapActorReferences(previousNames, nextNames, indices, metadata) {
|
||||
const labels = new Map(indices.map((index) => [previousNames[index] || `actor${index}`,
|
||||
nextNames[index] || `actor${index}`]));
|
||||
const nextLabels = [...labels.values()];
|
||||
if (new Set(nextLabels).size !== nextLabels.length) throw new Error('Actor export names must be unique.');
|
||||
const rename = (label) => labels.get(label) ?? label;
|
||||
const targets = (value) => Array.isArray(value) ? value.map(rename) : typeof value === 'string' ? rename(value) : value;
|
||||
for (const entry of Object.values(metadata)) {
|
||||
if (entry.injects_into) entry.injects_into = targets(entry.injects_into);
|
||||
}
|
||||
const definitions = JSON.parse(Project[ANIMDEF_DEFINITIONS_PROPERTY] || '{}');
|
||||
for (const definition of Object.values(definitions)) {
|
||||
for (const actor of definition.actors || []) {
|
||||
actor.label = rename(actor.label);
|
||||
if (actor.injects_into) actor.injects_into = targets(actor.injects_into);
|
||||
}
|
||||
if (definition.position_anchor_actor) definition.position_anchor_actor = rename(definition.position_anchor_actor);
|
||||
for (const stage of definition.stages || []) {
|
||||
if (stage.props && typeof stage.props === 'object') {
|
||||
stage.props = Object.fromEntries(Object.entries(stage.props).map(([label, value]) => [rename(label), value]));
|
||||
}
|
||||
}
|
||||
}
|
||||
const previews = Object.fromEntries(Object.values(readPropPreviews()).map((entry) => {
|
||||
const updated = { ...entry, actor: rename(entry.actor) };
|
||||
return [propEntryKey(updated.animation, updated.actor, updated.slot, updated.stage), updated];
|
||||
}));
|
||||
Project[ANIMDEF_DEFINITIONS_PROPERTY] = JSON.stringify(definitions);
|
||||
Project[PROP_PREVIEWS_PROPERTY] = JSON.stringify(previews);
|
||||
}
|
||||
|
||||
function getProjectActorTextures() {
|
||||
return getProjectStringMap(ACTOR_TEXTURES_PROPERTY);
|
||||
}
|
||||
@@ -392,6 +423,9 @@
|
||||
for (const label of actorLabels) {
|
||||
if (label !== actorLabel) targetOptions[label] = label;
|
||||
}
|
||||
for (const label of metadata.injects_into || []) {
|
||||
if (!Object.hasOwn(targetOptions, label)) targetOptions[label] = `${label} (unavailable - remove or replace)`;
|
||||
}
|
||||
form[`${base}_heading`] = {
|
||||
type: 'info',
|
||||
text: `#### Actor ${actorIndex} (${actorLabel})`,
|
||||
@@ -1732,12 +1766,14 @@
|
||||
const aspects = { elements: Cube.all.slice(), uv_mode: true, actor_preferences_edit: true };
|
||||
Undo.initEdit(aspects);
|
||||
editing = true;
|
||||
remapActorReferences(names, outNames, actorIndices, outAnimdefMetadata);
|
||||
setProjectActorNames(outNames);
|
||||
setProjectActorTextures(outTextures);
|
||||
setProjectActorAnimdefMetadata(outAnimdefMetadata);
|
||||
setProjectBoneTextureOverrides(outBoneTextureOverrides);
|
||||
applyAllActorTexturePreviews(true);
|
||||
Undo.finishEdit('Edit Actor Preferences', { ...aspects, elements: Cube.all.slice() });
|
||||
void refreshPropPreviews(true);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (editing) Undo.cancelEdit();
|
||||
@@ -3371,9 +3407,9 @@
|
||||
const cycleSeconds = parseRequiredPositiveNumber(result?.[`${base}_cycle_seconds`], `Stage p${stage.stageNumber} cycle seconds`);
|
||||
const speed = parseOptionalNumber(result?.[`${base}_speed`], `Stage p${stage.stageNumber} speed`, (value) => value > 0);
|
||||
const midpointOffset = parseOptionalNumber(
|
||||
result?.[`${base}_midpoint_offset`],
|
||||
loop ? result?.[`${base}_midpoint_offset`] : '',
|
||||
`Stage p${stage.stageNumber} midpoint offset`,
|
||||
() => loop
|
||||
() => true
|
||||
);
|
||||
const stageSeconds = parseOptionalNumber(
|
||||
result?.[`${base}_stage_seconds`],
|
||||
@@ -3381,12 +3417,12 @@
|
||||
(value) => Number.isInteger(value) && (value === -1 || (value >= 1 && value <= 300))
|
||||
);
|
||||
const durationMultiplier = parseOptionalNumber(
|
||||
result?.[`${base}_duration_multiplier`],
|
||||
loop ? result?.[`${base}_duration_multiplier`] : '',
|
||||
`Stage p${stage.stageNumber} duration multiplier`,
|
||||
(value) => loop && value >= 0.05 && value <= 20
|
||||
(value) => value >= 0.05 && value <= 20
|
||||
);
|
||||
const useStage = parseOptionalNumber(
|
||||
result?.[`${base}_use_stage`],
|
||||
result?.[`${base}_use_stage`] === 'own' ? '' : result?.[`${base}_use_stage`],
|
||||
`Stage p${stage.stageNumber} playback stage`,
|
||||
(value) => Number.isInteger(value) && value > 0
|
||||
);
|
||||
@@ -3403,13 +3439,13 @@
|
||||
? !!result[`${base}_allow_join`]
|
||||
: !isLastStage,
|
||||
};
|
||||
if (midpointOffset !== null) output.cycle_midpoint_offset_seconds = midpointOffset;
|
||||
if (loop && midpointOffset !== null) output.cycle_midpoint_offset_seconds = midpointOffset;
|
||||
if (useStage !== null && useStage !== stage.stageNumber) output.use_stage = useStage;
|
||||
if (speed !== null) output.speed = speed;
|
||||
if (peaked) output.non_peak = true;
|
||||
if (escapable !== 'default') output.escapable = escapable === 'true';
|
||||
if (stageSeconds !== null) output.stage_seconds = stageSeconds;
|
||||
if (durationMultiplier !== null) output.stage_duration_multiplier = durationMultiplier;
|
||||
if (loop && durationMultiplier !== null) output.stage_duration_multiplier = durationMultiplier;
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -3829,6 +3865,9 @@
|
||||
const label = analysis.actorLabels.get(actorIndex);
|
||||
anchorOptions[label] = label;
|
||||
}
|
||||
if (saved.position_anchor_actor && !Object.hasOwn(anchorOptions, saved.position_anchor_actor)) {
|
||||
anchorOptions[saved.position_anchor_actor] = `${saved.position_anchor_actor} (unavailable - replace)`;
|
||||
}
|
||||
form.position_anchor_actor = {
|
||||
label: 'Position anchor actor', type: 'select', value: saved.position_anchor_actor || '', options: anchorOptions,
|
||||
description: 'Prefer this actor as the animation placement anchor.',
|
||||
@@ -3929,15 +3968,18 @@
|
||||
label: 'Play animation from stage',
|
||||
type: 'select',
|
||||
options: Object.fromEntries([
|
||||
['', `Own stage (p${stage.stageNumber})`],
|
||||
['own', `Own stage (p${stage.stageNumber})`],
|
||||
...analysis.stages.map((availableStage) => [
|
||||
String(availableStage.stageNumber), `Stage p${availableStage.stageNumber}`,
|
||||
]),
|
||||
]),
|
||||
value: savedStage.use_stage == null ? '' : String(savedStage.use_stage),
|
||||
value: savedStage.use_stage == null ? 'own' : String(savedStage.use_stage),
|
||||
description: 'Optional stage number whose GeckoLib clip should be played instead.',
|
||||
condition: showAdvanced,
|
||||
};
|
||||
if (savedStage.use_stage != null && !Object.hasOwn(form[`${base}_use_stage`].options, String(savedStage.use_stage))) {
|
||||
form[`${base}_use_stage`].options[String(savedStage.use_stage)] = `Stage p${savedStage.use_stage} (unavailable - replace)`;
|
||||
}
|
||||
form[`${base}_stage_seconds`] = {
|
||||
label: 'Stage seconds',
|
||||
type: 'text',
|
||||
@@ -4041,6 +4083,7 @@
|
||||
const water = String(result?.water || 'none');
|
||||
if (water !== 'none') definition.water = water;
|
||||
const anchor = String(result?.position_anchor_actor || '');
|
||||
if (anchor && !actorsByLabel.has(anchor)) throw new Error(`Position anchor "${anchor}" is unavailable. Choose another actor or Automatic anchor.`);
|
||||
if (anchor) definition.position_anchor_actor = anchor;
|
||||
const camera = unmanagedAnimdefFields(saved.camera, new Set(['torso_tracking', 'block_requirement_collision']));
|
||||
if (result?.camera_torso_tracking === 'continuous') camera.torso_tracking = 'continuous';
|
||||
@@ -6656,7 +6699,7 @@
|
||||
Plugin.register(PLUGIN_ID, {
|
||||
title: 'AnimationDirector',
|
||||
author: 'L1Z0',
|
||||
icon: 'cinematic_blur',
|
||||
icon: 'diversity_4',
|
||||
description: 'Multi-actor animation in Blockbench without bone-name clashes.',
|
||||
about: `AnimationDirector is a Blockbench plugin for creating multi-actor animations for Needs of Nature.
|
||||
|
||||
@@ -6671,6 +6714,7 @@ Build, preview, and export multi-actor animations with tools for actor models, a
|
||||
- Edit block requirements with live previews of required blocks and clearance areas.
|
||||
- Preview item props using Minecraft assets or custom textures. <sup>1</sup>
|
||||
- Browse and audition sounds with the NoN Sound Library.
|
||||
- Browse particle sprites and assign cues and locators with the NoN Particle Library, including approximate timeline previews for supported NoN particles. <sup>1</sup>
|
||||
- Export multi-actor, multi-stage animations and GeckoLib models.
|
||||
- Replace actor models while preserving matching animation tracks.
|
||||
- Transfer added geometry into vanilla CEM presets to create JEM models, with optional energy-dependent visibility. <sup>2</sup>
|
||||
@@ -6679,11 +6723,11 @@ Build, preview, and export multi-actor animations with tools for actor models, a
|
||||
|
||||
**Addon Notes**
|
||||
|
||||
<sup>1</sup> Asset Browser - optional; its cached Minecraft assets can be used for textures and item previews.
|
||||
<sup>1</sup> Asset Browser - optional; its cached Minecraft assets can be used for textures, item previews, and the particle library.
|
||||
|
||||
<sup>2</sup> CEM Template Loader - required for JEM creation.
|
||||
|
||||
Features without a reference need no additional addon. Whole-animation import and local asset tools require desktop Blockbench. The sound library uses locally available NoN/pack sound files.`,
|
||||
Features without a reference need no additional addon. Whole-animation import and local asset tools require desktop Blockbench. The sound library uses locally available NoN/pack sound files; NoN particle previews require their texture assets from a NoN JAR or extracted assets folder.`,
|
||||
version: '2.0',
|
||||
min_version: '5.2.1',
|
||||
variant: 'both',
|
||||
@@ -6821,7 +6865,8 @@ Features without a reference need no additional addon. Whole-animation import an
|
||||
if (aspects.actor_texture_edit) save[ACTOR_TEXTURES_PROPERTY] = Project[ACTOR_TEXTURES_PROPERTY] || '{}';
|
||||
if (aspects.actor_preferences_edit) {
|
||||
for (const property of [ACTOR_NAMES_PROPERTY, ACTOR_TEXTURES_PROPERTY, ACTOR_ANIMDEF_METADATA_PROPERTY,
|
||||
BONE_TEXTURE_OVERRIDES_PROPERTY, ACTOR_UV_BASIS_PROPERTY]) save[property] = Project[property] || '{}';
|
||||
BONE_TEXTURE_OVERRIDES_PROPERTY, ACTOR_UV_BASIS_PROPERTY, ANIMDEF_DEFINITIONS_PROPERTY,
|
||||
PROP_PREVIEWS_PROPERTY]) save[property] = Project[property] || '{}';
|
||||
}
|
||||
if (aspects.automatic_animation_import) {
|
||||
for (const property of IMPORT_METADATA) save[property] = Project[property] ?? (property === ANIMDEF_ANIMATORS_PROPERTY ? '' : '{}');
|
||||
|
||||
Reference in New Issue
Block a user