diff --git a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemDefinition.java b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemDefinition.java
index ce1a4db..da1e07c 100644
--- a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemDefinition.java
+++ b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemDefinition.java
@@ -103,7 +103,8 @@ public record DataDrivenItemDefinition(
* is not present in this map (or null), the item falls back to its full
* {@code ownedParts}.
*
- * This field is required in the JSON definition. Never null, never empty.
+ * Optional in JSON. When absent, defaults to empty map (= all owned bones
+ * enabled for all animations, no filtering).
*/
Map> animationBones,
@@ -111,9 +112,10 @@ public record DataDrivenItemDefinition(
Map componentConfigs
) {
- /** Compact constructor: default null componentConfigs to empty immutable map. */
+ /** Compact constructor: default null maps to empty immutable maps. */
public DataDrivenItemDefinition {
if (componentConfigs == null) componentConfigs = Map.of();
+ if (animationBones == null) animationBones = Map.of();
}
/** Check whether this definition declares a given component type. */
diff --git a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemParser.java b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemParser.java
index 91f5d91..a8b9a03 100644
--- a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemParser.java
+++ b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenItemParser.java
@@ -261,17 +261,27 @@ public final class DataDrivenItemParser {
// Optional: creator (author name for tooltip)
String creator = getStringOrNull(root, "creator");
- // Required: animation_bones (per-animation bone whitelist)
- Map> animationBones = parseAnimationBones(
- root,
- fileId
- );
- if (animationBones == null) {
- LOGGER.error(
- "[DataDrivenItems] Skipping {}: missing or invalid 'animation_bones'",
+ // Optional: animation_bones (per-animation bone whitelist)
+ // When absent, all owned bones are enabled for all animations.
+ Map> animationBones;
+ if (!root.has("animation_bones")) {
+ // Not an error — absent means "permissive" (all owned bones, all animations)
+ animationBones = null;
+ LOGGER.info(
+ "[DataDrivenItems] {}: animation_bones not declared — all owned bones enabled for all animations",
fileId
);
- return null;
+ // null is fine — the record constructor defaults it to Map.of()
+ } else {
+ animationBones = parseAnimationBones(root, fileId);
+ if (animationBones == null) {
+ // Field was present but malformed — parseAnimationBones already logged details
+ LOGGER.error(
+ "[DataDrivenItems] Skipping {}: 'animation_bones' is present but invalid",
+ fileId
+ );
+ return null;
+ }
}
// Optional: components (per-component JSON configs)