Remove internal phase comments and format code

Strip all Phase references, TODO/FUTURE roadmap notes, and internal
planning comments from the codebase. Run Prettier for consistent
formatting across all Java files.
This commit is contained in:
NotEvil
2026-04-12 01:24:49 +02:00
parent 73d70e212d
commit a71093ba9c
482 changed files with 8500 additions and 5155 deletions

View File

@@ -48,8 +48,10 @@ public class BondageAnimationManager {
new ConcurrentHashMap<>();
/** Cache of furniture ModifierLayers for NPC entities */
private static final Map<UUID, ModifierLayer<IAnimation>> npcFurnitureLayers =
new ConcurrentHashMap<>();
private static final Map<
UUID,
ModifierLayer<IAnimation>
> npcFurnitureLayers = new ConcurrentHashMap<>();
/** Factory ID for PlayerAnimator item layer (players only) */
private static final ResourceLocation FACTORY_ID =
@@ -83,7 +85,8 @@ public class BondageAnimationManager {
*
* <p>Uses ConcurrentHashMap for safe access from both client tick and render thread.</p>
*/
private static final Map<UUID, Integer> furnitureGraceTicks = new ConcurrentHashMap<>();
private static final Map<UUID, Integer> furnitureGraceTicks =
new ConcurrentHashMap<>();
/**
* Initialize the animation system.
@@ -119,13 +122,13 @@ public class BondageAnimationManager {
LOGGER.info(
"BondageAnimationManager: Factories registered — context (pri {}), item (pri {}), furniture (pri {})",
CONTEXT_LAYER_PRIORITY, ITEM_LAYER_PRIORITY, FURNITURE_LAYER_PRIORITY
CONTEXT_LAYER_PRIORITY,
ITEM_LAYER_PRIORITY,
FURNITURE_LAYER_PRIORITY
);
}
// ========================================
// PLAY ANIMATION
// ========================================
/**
* Play an animation on any entity (player or NPC).
@@ -235,7 +238,10 @@ public class BondageAnimationManager {
* @param anim The KeyframeAnimation to play
* @return true if animation started successfully
*/
public static boolean playDirect(LivingEntity entity, KeyframeAnimation anim) {
public static boolean playDirect(
LivingEntity entity,
KeyframeAnimation anim
) {
if (entity == null || anim == null || !entity.level().isClientSide()) {
return false;
}
@@ -255,9 +261,7 @@ public class BondageAnimationManager {
return false;
}
// ========================================
// STOP ANIMATION
// ========================================
/**
* Stop any currently playing animation on an entity.
@@ -276,9 +280,7 @@ public class BondageAnimationManager {
}
}
// ========================================
// LAYER MANAGEMENT
// ========================================
/**
* Get the ModifierLayer for an entity (without creating).
@@ -398,9 +400,7 @@ public class BondageAnimationManager {
return npcLayers.get(player.getUUID());
}
// ========================================
// CONTEXT LAYER (lower priority, for sit/kneel/sneak)
// ========================================
/**
* Get the context animation layer for a player from PlayerAnimationAccess.
@@ -431,14 +431,13 @@ public class BondageAnimationManager {
LivingEntity entity
) {
if (entity instanceof IAnimatedPlayer animated) {
return npcContextLayers.computeIfAbsent(
entity.getUUID(),
k -> {
ModifierLayer<IAnimation> layer = new ModifierLayer<>();
animated.getAnimationStack().addAnimLayer(CONTEXT_LAYER_PRIORITY, layer);
return layer;
}
);
return npcContextLayers.computeIfAbsent(entity.getUUID(), k -> {
ModifierLayer<IAnimation> layer = new ModifierLayer<>();
animated
.getAnimationStack()
.addAnimLayer(CONTEXT_LAYER_PRIORITY, layer);
return layer;
});
}
return null;
}
@@ -496,9 +495,7 @@ public class BondageAnimationManager {
}
}
// ========================================
// FURNITURE LAYER (highest priority, for seat poses)
// ========================================
/**
* Play a furniture animation on the furniture layer (highest priority).
@@ -512,8 +509,15 @@ public class BondageAnimationManager {
* @param animation the KeyframeAnimation from FurnitureAnimationContext
* @return true if animation started successfully
*/
public static boolean playFurniture(Player player, KeyframeAnimation animation) {
if (player == null || animation == null || !player.level().isClientSide()) {
public static boolean playFurniture(
Player player,
KeyframeAnimation animation
) {
if (
player == null ||
animation == null ||
!player.level().isClientSide()
) {
return false;
}
@@ -522,11 +526,17 @@ public class BondageAnimationManager {
layer.setAnimation(new KeyframeAnimationPlayer(animation));
// Reset grace ticks since we just started/refreshed the animation
furnitureGraceTicks.remove(player.getUUID());
LOGGER.debug("Playing furniture animation on player: {}", player.getName().getString());
LOGGER.debug(
"Playing furniture animation on player: {}",
player.getName().getString()
);
return true;
}
LOGGER.warn("Furniture layer not available for player: {}", player.getName().getString());
LOGGER.warn(
"Furniture layer not available for player: {}",
player.getName().getString()
);
return false;
}
@@ -545,7 +555,10 @@ public class BondageAnimationManager {
layer.setAnimation(null);
}
furnitureGraceTicks.remove(player.getUUID());
LOGGER.debug("Stopped furniture animation on player: {}", player.getName().getString());
LOGGER.debug(
"Stopped furniture animation on player: {}",
player.getName().getString()
);
}
/**
@@ -573,9 +586,11 @@ public class BondageAnimationManager {
private static ModifierLayer<IAnimation> getFurnitureLayer(Player player) {
if (player instanceof AbstractClientPlayer clientPlayer) {
try {
ModifierLayer<IAnimation> layer = (ModifierLayer<IAnimation>)
PlayerAnimationAccess.getPlayerAssociatedData(clientPlayer)
.get(FURNITURE_FACTORY_ID);
ModifierLayer<IAnimation> layer = (ModifierLayer<
IAnimation
>) PlayerAnimationAccess.getPlayerAssociatedData(
clientPlayer
).get(FURNITURE_FACTORY_ID);
if (layer != null) {
return layer;
}
@@ -628,17 +643,18 @@ public class BondageAnimationManager {
// Player has furniture anim but no seat -- increment grace
int ticks = furnitureGraceTicks.merge(uuid, 1, Integer::sum);
if (ticks >= FURNITURE_GRACE_TICKS) {
LOGGER.info("Removing stale furniture animation for player {} "
+ "(not riding ISeatProvider for {} ticks)",
player.getName().getString(), ticks);
LOGGER.info(
"Removing stale furniture animation for player {} " +
"(not riding ISeatProvider for {} ticks)",
player.getName().getString(),
ticks
);
stopFurniture(player);
}
}
}
// ========================================
// FALLBACK ANIMATION HANDLING
// ========================================
/**
* Try to find a fallback animation ID when the requested one doesn't exist.
@@ -696,9 +712,7 @@ public class BondageAnimationManager {
return null;
}
// ========================================
// CLEANUP
// ========================================
/**
* Clean up animation layer for an NPC when it's removed.
@@ -706,9 +720,8 @@ public class BondageAnimationManager {
* @param entityId UUID of the removed entity
*/
/** All NPC layer caches, for bulk cleanup operations. */
private static final Map<UUID, ModifierLayer<IAnimation>>[] ALL_NPC_CACHES = new Map[] {
npcLayers, npcContextLayers, npcFurnitureLayers
};
private static final Map<UUID, ModifierLayer<IAnimation>>[] ALL_NPC_CACHES =
new Map[] { npcLayers, npcContextLayers, npcFurnitureLayers };
public static void cleanup(UUID entityId) {
for (Map<UUID, ModifierLayer<IAnimation>> cache : ALL_NPC_CACHES) {
@@ -733,5 +746,4 @@ public class BondageAnimationManager {
furnitureGraceTicks.clear();
LOGGER.info("Cleared all NPC animation layers");
}
}

View File

@@ -17,7 +17,6 @@ import net.minecraftforge.api.distmarker.OnlyIn;
*/
@OnlyIn(Dist.CLIENT)
public enum AnimationContext {
STAND_IDLE("stand_idle", false),
STAND_WALK("stand_walk", false),
STAND_SNEAK("stand_sneak", false),

View File

@@ -50,15 +50,21 @@ public final class AnimationContextResolver {
* @param activeStyle the active movement style from client state, or null
* @return the resolved animation context, never null
*/
public static AnimationContext resolve(Player player, @Nullable PlayerBindState state,
@Nullable MovementStyle activeStyle) {
public static AnimationContext resolve(
Player player,
@Nullable PlayerBindState state,
@Nullable MovementStyle activeStyle
) {
boolean sitting = PetBedClientState.get(player.getUUID()) != 0;
boolean struggling = state != null && state.isStruggling();
boolean sneaking = player.isCrouching();
boolean moving = player.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;
boolean moving =
player.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;
if (sitting) {
return struggling ? AnimationContext.SIT_STRUGGLE : AnimationContext.SIT_IDLE;
return struggling
? AnimationContext.SIT_STRUGGLE
: AnimationContext.SIT_IDLE;
}
if (struggling) {
return AnimationContext.STAND_STRUGGLE;
@@ -78,12 +84,23 @@ public final class AnimationContextResolver {
/**
* Map a movement style + moving flag to the appropriate AnimationContext.
*/
private static AnimationContext resolveStyleContext(MovementStyle style, boolean moving) {
private static AnimationContext resolveStyleContext(
MovementStyle style,
boolean moving
) {
return switch (style) {
case SHUFFLE -> moving ? AnimationContext.SHUFFLE_WALK : AnimationContext.SHUFFLE_IDLE;
case HOP -> moving ? AnimationContext.HOP_WALK : AnimationContext.HOP_IDLE;
case WADDLE -> moving ? AnimationContext.WADDLE_WALK : AnimationContext.WADDLE_IDLE;
case CRAWL -> moving ? AnimationContext.CRAWL_MOVE : AnimationContext.CRAWL_IDLE;
case SHUFFLE -> moving
? AnimationContext.SHUFFLE_WALK
: AnimationContext.SHUFFLE_IDLE;
case HOP -> moving
? AnimationContext.HOP_WALK
: AnimationContext.HOP_IDLE;
case WADDLE -> moving
? AnimationContext.WADDLE_WALK
: AnimationContext.WADDLE_IDLE;
case CRAWL -> moving
? AnimationContext.CRAWL_MOVE
: AnimationContext.CRAWL_IDLE;
};
}
@@ -99,13 +116,18 @@ public final class AnimationContextResolver {
boolean sitting = entity.isSitting();
boolean kneeling = entity.isKneeling();
boolean struggling = entity.isStruggling();
boolean moving = entity.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;
boolean moving =
entity.getDeltaMovement().horizontalDistanceSqr() > 1.0E-6;
if (sitting) {
return struggling ? AnimationContext.SIT_STRUGGLE : AnimationContext.SIT_IDLE;
return struggling
? AnimationContext.SIT_STRUGGLE
: AnimationContext.SIT_IDLE;
}
if (kneeling) {
return struggling ? AnimationContext.KNEEL_STRUGGLE : AnimationContext.KNEEL_IDLE;
return struggling
? AnimationContext.KNEEL_STRUGGLE
: AnimationContext.KNEEL_IDLE;
}
if (struggling) {
return AnimationContext.STAND_STRUGGLE;

View File

@@ -6,10 +6,10 @@ import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
/**
@@ -42,13 +42,15 @@ public final class ContextAnimationFactory {
* Cache keyed by "contextSuffix|ownedPartsHashCode".
* Null values are stored as sentinels for missing animations to avoid repeated lookups.
*/
private static final Map<String, KeyframeAnimation> CACHE = new ConcurrentHashMap<>();
private static final Map<String, KeyframeAnimation> CACHE =
new ConcurrentHashMap<>();
/**
* Sentinel set used to track cache keys where the base animation was not found,
* so we don't log the same warning repeatedly.
*/
private static final Set<String> MISSING_WARNED = ConcurrentHashMap.newKeySet();
private static final Set<String> MISSING_WARNED =
ConcurrentHashMap.newKeySet();
private ContextAnimationFactory() {}
@@ -65,8 +67,14 @@ public final class ContextAnimationFactory {
* @return the context animation with disabled parts suppressed, or null if not found
*/
@Nullable
public static KeyframeAnimation create(AnimationContext context, Set<String> disabledParts) {
String cacheKey = context.getAnimationSuffix() + "|" + String.join(",", new java.util.TreeSet<>(disabledParts));
public static KeyframeAnimation create(
AnimationContext context,
Set<String> disabledParts
) {
String cacheKey =
context.getAnimationSuffix() +
"|" +
String.join(",", new java.util.TreeSet<>(disabledParts));
// computeIfAbsent cannot store null values, so we handle the missing case
// by checking the MISSING_WARNED set to avoid redundant work.
KeyframeAnimation cached = CACHE.get(cacheKey);
@@ -77,7 +85,10 @@ public final class ContextAnimationFactory {
return null;
}
KeyframeAnimation result = buildContextAnimation(context, disabledParts);
KeyframeAnimation result = buildContextAnimation(
context,
disabledParts
);
if (result != null) {
CACHE.put(cacheKey, result);
} else {
@@ -100,8 +111,10 @@ public final class ContextAnimationFactory {
* </ol>
*/
@Nullable
private static KeyframeAnimation buildContextAnimation(AnimationContext context,
Set<String> disabledParts) {
private static KeyframeAnimation buildContextAnimation(
AnimationContext context,
Set<String> disabledParts
) {
String suffix = context.getAnimationSuffix();
// Priority 1: GLB-based context animation from ContextGlbRegistry
@@ -110,13 +123,17 @@ public final class ContextAnimationFactory {
// Priority 2: JSON-based context animation from PlayerAnimationRegistry
if (baseAnim == null) {
ResourceLocation animId = ResourceLocation.fromNamespaceAndPath(
NAMESPACE, "context_" + suffix
NAMESPACE,
"context_" + suffix
);
baseAnim = PlayerAnimationRegistry.getAnimation(animId);
}
if (baseAnim == null) {
LOGGER.warn("[V2Animation] Context animation not found for suffix: {}", suffix);
LOGGER.warn(
"[V2Animation] Context animation not found for suffix: {}",
suffix
);
return null;
}
@@ -140,8 +157,10 @@ public final class ContextAnimationFactory {
* <p>Unknown part names are silently ignored -- this can happen if the disabled parts set
* includes future bone names not present in the current context animation.</p>
*/
private static void disableParts(KeyframeAnimation.AnimationBuilder builder,
Set<String> disabledParts) {
private static void disableParts(
KeyframeAnimation.AnimationBuilder builder,
Set<String> disabledParts
) {
for (String partName : disabledParts) {
KeyframeAnimation.StateCollection part = builder.getPart(partName);
if (part != null) {

View File

@@ -8,7 +8,6 @@ import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
@@ -16,6 +15,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* Registry for context animations loaded from GLB files.
@@ -70,10 +70,15 @@ public final class ContextGlbRegistry {
public static void reload(ResourceManager resourceManager) {
Map<String, KeyframeAnimation> newRegistry = new HashMap<>();
Map<ResourceLocation, Resource> resources = resourceManager.listResources(
DIRECTORY, loc -> loc.getPath().endsWith(".glb"));
Map<ResourceLocation, Resource> resources =
resourceManager.listResources(DIRECTORY, loc ->
loc.getPath().endsWith(".glb")
);
for (Map.Entry<ResourceLocation, Resource> entry : resources.entrySet()) {
for (Map.Entry<
ResourceLocation,
Resource
> entry : resources.entrySet()) {
ResourceLocation loc = entry.getKey();
Resource resource = entry.getValue();
@@ -89,15 +94,26 @@ public final class ContextGlbRegistry {
KeyframeAnimation anim = GltfPoseConverter.convert(data);
newRegistry.put(suffix, anim);
LOGGER.info("[GltfPipeline] Loaded context GLB: '{}' -> suffix '{}'", loc, suffix);
LOGGER.info(
"[GltfPipeline] Loaded context GLB: '{}' -> suffix '{}'",
loc,
suffix
);
} catch (Exception e) {
LOGGER.error("[GltfPipeline] Failed to load context GLB: {}", loc, e);
LOGGER.error(
"[GltfPipeline] Failed to load context GLB: {}",
loc,
e
);
}
}
// Atomic swap: render thread never sees a partially populated registry
REGISTRY = Collections.unmodifiableMap(newRegistry);
LOGGER.info("[ContextGlb] Loaded {} context GLB animations", newRegistry.size());
LOGGER.info(
"[ContextGlb] Loaded {} context GLB animations",
newRegistry.size()
);
}
/**

View File

@@ -6,10 +6,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
/**
* Resolves which named animation to play from a GLB file based on the current
@@ -42,9 +42,12 @@ public final class GlbAnimationResolver {
* @return parsed GLB data, or null if loading failed
*/
@Nullable
public static GltfData resolveAnimationData(ResourceLocation itemModelLoc,
@Nullable ResourceLocation animationSource) {
ResourceLocation source = animationSource != null ? animationSource : itemModelLoc;
public static GltfData resolveAnimationData(
ResourceLocation itemModelLoc,
@Nullable ResourceLocation animationSource
) {
ResourceLocation source =
animationSource != null ? animationSource : itemModelLoc;
return GltfCache.get(source);
}
@@ -66,8 +69,8 @@ public final class GlbAnimationResolver {
*/
@Nullable
public static String resolve(GltfData data, AnimationContext context) {
String prefix = context.getGlbContextPrefix(); // "Sit", "Kneel", "Sneak", "Walk", ""
String variant = context.getGlbVariant(); // "Idle" or "Struggle"
String prefix = context.getGlbContextPrefix(); // "Sit", "Kneel", "Sneak", "Walk", ""
String variant = context.getGlbVariant(); // "Idle" or "Struggle"
// 1. Exact match: "FullSitIdle" then "SitIdle" (with variants)
String exact = prefix + variant;
@@ -146,6 +149,8 @@ public final class GlbAnimationResolver {
if (candidates.isEmpty()) return null;
if (candidates.size() == 1) return candidates.get(0);
return candidates.get(ThreadLocalRandom.current().nextInt(candidates.size()));
return candidates.get(
ThreadLocalRandom.current().nextInt(candidates.size())
);
}
}

View File

@@ -6,11 +6,11 @@ import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
import java.util.*;
import org.jetbrains.annotations.Nullable;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;
/**
* Maps V2 body regions to PlayerAnimator part names.
@@ -29,7 +29,12 @@ public final class RegionBoneMapper {
/** All PlayerAnimator part names for the player model. */
public static final Set<String> ALL_PARTS = Set.of(
"head", "body", "rightArm", "leftArm", "rightLeg", "leftLeg"
"head",
"body",
"rightArm",
"leftArm",
"rightLeg",
"leftLeg"
);
/**
@@ -46,7 +51,6 @@ public final class RegionBoneMapper {
* the other item takes precedence (the bone goes to {@code otherParts}).</p>
*/
public record BoneOwnership(Set<String> thisParts, Set<String> otherParts) {
/**
* Parts not owned by any item. These are "free" and can be animated
* by the winning item IF the GLB contains keyframes for them.
@@ -84,20 +88,20 @@ public final class RegionBoneMapper {
static {
Map<BodyRegionV2, Set<String>> map = new EnumMap<>(BodyRegionV2.class);
map.put(BodyRegionV2.HEAD, Set.of("head"));
map.put(BodyRegionV2.EYES, Set.of("head"));
map.put(BodyRegionV2.EARS, Set.of("head"));
map.put(BodyRegionV2.MOUTH, Set.of("head"));
map.put(BodyRegionV2.NECK, Set.of());
map.put(BodyRegionV2.TORSO, Set.of("body"));
map.put(BodyRegionV2.ARMS, Set.of("rightArm", "leftArm"));
map.put(BodyRegionV2.HANDS, Set.of("rightArm", "leftArm"));
map.put(BodyRegionV2.HEAD, Set.of("head"));
map.put(BodyRegionV2.EYES, Set.of("head"));
map.put(BodyRegionV2.EARS, Set.of("head"));
map.put(BodyRegionV2.MOUTH, Set.of("head"));
map.put(BodyRegionV2.NECK, Set.of());
map.put(BodyRegionV2.TORSO, Set.of("body"));
map.put(BodyRegionV2.ARMS, Set.of("rightArm", "leftArm"));
map.put(BodyRegionV2.HANDS, Set.of("rightArm", "leftArm"));
map.put(BodyRegionV2.FINGERS, Set.of());
map.put(BodyRegionV2.WAIST, Set.of("body"));
map.put(BodyRegionV2.LEGS, Set.of("rightLeg", "leftLeg"));
map.put(BodyRegionV2.FEET, Set.of("rightLeg", "leftLeg"));
map.put(BodyRegionV2.TAIL, Set.of());
map.put(BodyRegionV2.WINGS, Set.of());
map.put(BodyRegionV2.WAIST, Set.of("body"));
map.put(BodyRegionV2.LEGS, Set.of("rightLeg", "leftLeg"));
map.put(BodyRegionV2.FEET, Set.of("rightLeg", "leftLeg"));
map.put(BodyRegionV2.TAIL, Set.of());
map.put(BodyRegionV2.WINGS, Set.of());
REGION_TO_PARTS = Collections.unmodifiableMap(map);
}
@@ -123,7 +127,9 @@ public final class RegionBoneMapper {
* @param equipped map from representative region to equipped ItemStack
* @return unmodifiable set of owned part name strings
*/
public static Set<String> computeOwnedParts(Map<BodyRegionV2, ItemStack> equipped) {
public static Set<String> computeOwnedParts(
Map<BodyRegionV2, ItemStack> equipped
) {
Set<String> owned = new HashSet<>();
for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
ItemStack stack = entry.getValue();
@@ -151,14 +157,18 @@ public final class RegionBoneMapper {
* @param winningItemStack the ItemStack of the highest-priority V2 item with a GLB model
* @return BoneOwnership describing this item's parts vs other items' parts
*/
public static BoneOwnership computePerItemParts(Map<BodyRegionV2, ItemStack> equipped,
ItemStack winningItemStack) {
public static BoneOwnership computePerItemParts(
Map<BodyRegionV2, ItemStack> equipped,
ItemStack winningItemStack
) {
Set<String> thisParts = new HashSet<>();
Set<String> otherParts = new HashSet<>();
// Track which ItemStacks we've already processed to avoid duplicate work
// (multiple regions can map to the same ItemStack)
Set<ItemStack> processed = Collections.newSetFromMap(new IdentityHashMap<>());
Set<ItemStack> processed = Collections.newSetFromMap(
new IdentityHashMap<>()
);
for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
ItemStack stack = entry.getValue();
@@ -199,8 +209,11 @@ public final class RegionBoneMapper {
* @param winningItem the actual ItemStack reference (for identity comparison in
* {@link #computePerItemParts})
*/
public record GlbModelResult(ResourceLocation modelLoc, @Nullable ResourceLocation animSource,
ItemStack winningItem) {}
public record GlbModelResult(
ResourceLocation modelLoc,
@Nullable ResourceLocation animSource,
ItemStack winningItem
) {}
/**
* Animation info for a single equipped V2 item.
@@ -213,9 +226,13 @@ public final class RegionBoneMapper {
* @param animationBones per-animation bone whitelist from the data-driven definition.
* Empty map for hardcoded items (no filtering applied).
*/
public record V2ItemAnimInfo(ResourceLocation modelLoc, @Nullable ResourceLocation animSource,
Set<String> ownedParts, int posePriority,
Map<String, Set<String>> animationBones) {}
public record V2ItemAnimInfo(
ResourceLocation modelLoc,
@Nullable ResourceLocation animSource,
Set<String> ownedParts,
int posePriority,
Map<String, Set<String>> animationBones
) {}
/**
* Find the highest-priority V2 item with a GLB model in the equipped map.
@@ -230,7 +247,9 @@ public final class RegionBoneMapper {
* @return the winning item's model info, or null if no V2 item has a GLB model (V1 fallback)
*/
@Nullable
public static GlbModelResult resolveWinningItem(Map<BodyRegionV2, ItemStack> equipped) {
public static GlbModelResult resolveWinningItem(
Map<BodyRegionV2, ItemStack> equipped
) {
ItemStack bestStack = null;
ResourceLocation bestModel = null;
int bestPriority = Integer.MIN_VALUE;
@@ -238,7 +257,10 @@ public final class RegionBoneMapper {
ItemStack stack = entry.getValue();
if (stack.getItem() instanceof IV2BondageItem v2Item) {
ResourceLocation model = v2Item.getModelLocation(stack);
if (model != null && v2Item.getPosePriority(stack) > bestPriority) {
if (
model != null &&
v2Item.getPosePriority(stack) > bestPriority
) {
bestPriority = v2Item.getPosePriority(stack);
bestModel = model;
bestStack = stack;
@@ -252,7 +274,9 @@ public final class RegionBoneMapper {
// (the model's own animations are used).
ResourceLocation animSource = null;
if (bestStack.getItem() instanceof DataDrivenBondageItem) {
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(bestStack);
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(
bestStack
);
if (def != null) {
animSource = def.animationSource();
}
@@ -273,13 +297,23 @@ public final class RegionBoneMapper {
* @return list of V2ItemAnimInfo, sorted by priority descending. Empty if no V2 items.
* The first element (if any) is the free-bone donor.
*/
public static List<V2ItemAnimInfo> resolveAllV2Items(Map<BodyRegionV2, ItemStack> equipped) {
record ItemEntry(ItemStack stack, IV2BondageItem v2Item, ResourceLocation model,
@Nullable ResourceLocation animSource, Set<String> rawParts, int priority,
Map<String, Set<String>> animationBones) {}
public static List<V2ItemAnimInfo> resolveAllV2Items(
Map<BodyRegionV2, ItemStack> equipped
) {
record ItemEntry(
ItemStack stack,
IV2BondageItem v2Item,
ResourceLocation model,
@Nullable ResourceLocation animSource,
Set<String> rawParts,
int priority,
Map<String, Set<String>> animationBones
) {}
List<ItemEntry> entries = new ArrayList<>();
Set<ItemStack> seen = Collections.newSetFromMap(new IdentityHashMap<>());
Set<ItemStack> seen = Collections.newSetFromMap(
new IdentityHashMap<>()
);
for (Map.Entry<BodyRegionV2, ItemStack> entry : equipped.entrySet()) {
ItemStack stack = entry.getValue();
@@ -299,15 +333,26 @@ public final class RegionBoneMapper {
ResourceLocation animSource = null;
Map<String, Set<String>> animBones = Map.of();
if (stack.getItem() instanceof DataDrivenBondageItem) {
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(
stack
);
if (def != null) {
animSource = def.animationSource();
animBones = def.animationBones();
}
}
entries.add(new ItemEntry(stack, v2Item, model, animSource, rawParts,
v2Item.getPosePriority(stack), animBones));
entries.add(
new ItemEntry(
stack,
v2Item,
model,
animSource,
rawParts,
v2Item.getPosePriority(stack),
animBones
)
);
}
}
@@ -323,8 +368,15 @@ public final class RegionBoneMapper {
ownedParts.removeAll(claimed);
if (ownedParts.isEmpty()) continue;
claimed.addAll(ownedParts);
result.add(new V2ItemAnimInfo(e.model(), e.animSource(),
Collections.unmodifiableSet(ownedParts), e.priority(), e.animationBones()));
result.add(
new V2ItemAnimInfo(
e.model(),
e.animSource(),
Collections.unmodifiableSet(ownedParts),
e.priority(),
e.animationBones()
)
);
}
return Collections.unmodifiableList(result);

View File

@@ -1,11 +1,11 @@
package com.tiedup.remake.client.animation.render;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.state.HumanChairHelper;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.client.player.AbstractClientPlayer;

View File

@@ -1,12 +1,12 @@
package com.tiedup.remake.client.animation.render;
import com.tiedup.remake.client.state.PetBedClientState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.state.HumanChairHelper;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

View File

@@ -1,12 +1,12 @@
package com.tiedup.remake.client.animation.render;
import com.tiedup.remake.client.renderer.layers.ClothesRenderHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.items.clothes.ClothesProperties;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.client.model.PlayerModel;
@@ -48,7 +48,7 @@ public class PlayerArmHideEventHandler {
/**
* Before player render:
* - Hide arms for wrap/latex_sack poses
* - Hide outer layers based on clothes settings (Phase 19)
* - Hide outer layers based on clothes settings
*/
@SubscribeEvent
public static void onRenderPlayerPre(RenderPlayerEvent.Pre event) {
@@ -88,7 +88,7 @@ public class PlayerArmHideEventHandler {
}
}
// === HIDE WEARER LAYERS (clothes settings) - Phase 19 ===
// === HIDE WEARER LAYERS (clothes settings) ===
ItemStack clothes = state.getEquipment(BodyRegionV2.TORSO);
if (!clothes.isEmpty()) {
ClothesProperties props =
@@ -126,7 +126,7 @@ public class PlayerArmHideEventHandler {
model.leftSleeve.visible = true;
model.rightSleeve.visible = true;
// === RESTORE WEARER LAYERS - Phase 19 ===
// === RESTORE WEARER LAYERS ===
boolean[] savedLayers = storedLayers.remove(player.getId());
if (savedLayers != null) {
ClothesRenderHelper.restoreWearerLayers(model, savedLayers);

View File

@@ -4,6 +4,9 @@ import com.mojang.logging.LogUtils;
import com.tiedup.remake.client.animation.AnimationStateRegistry;
import com.tiedup.remake.client.animation.BondageAnimationManager;
import com.tiedup.remake.client.animation.PendingAnimationManager;
import com.tiedup.remake.client.animation.context.AnimationContext;
import com.tiedup.remake.client.animation.context.AnimationContextResolver;
import com.tiedup.remake.client.animation.context.RegionBoneMapper;
import com.tiedup.remake.client.animation.util.AnimationIdBuilder;
import com.tiedup.remake.client.events.CellHighlightHandler;
import com.tiedup.remake.client.events.LeashProxyClientHandler;
@@ -13,15 +16,12 @@ import com.tiedup.remake.client.state.MovementStyleClientState;
import com.tiedup.remake.client.state.PetBedClientState;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.state.HumanChairHelper;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.v2.bondage.movement.MovementStyle;
import com.tiedup.remake.client.animation.context.AnimationContext;
import com.tiedup.remake.client.animation.context.AnimationContextResolver;
import com.tiedup.remake.client.animation.context.RegionBoneMapper;
import com.tiedup.remake.state.HumanChairHelper;
import com.tiedup.remake.state.PlayerBindState;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Minecraft;
@@ -109,8 +109,9 @@ public class AnimationTickHandler {
// Check if player has ANY V2 bondage item equipped (not just ARMS).
// isTiedUp() only checks ARMS, but items on LEGS, HEAD, etc. also need animation.
boolean isTied = state != null && (state.isTiedUp()
|| V2EquipmentHelper.hasAnyEquipment(player));
boolean isTied =
state != null &&
(state.isTiedUp() || V2EquipmentHelper.hasAnyEquipment(player));
boolean wasTied =
AnimationStateRegistry.getLastTiedState().getOrDefault(uuid, false);
@@ -175,9 +176,11 @@ public class AnimationTickHandler {
if (isTied) {
// Resolve V2 equipped items
IV2BondageEquipment equipment = V2EquipmentHelper.getEquipment(player);
Map<BodyRegionV2, ItemStack> equipped = equipment != null
? equipment.getAllEquipped() : Map.of();
IV2BondageEquipment equipment = V2EquipmentHelper.getEquipment(
player
);
Map<BodyRegionV2, ItemStack> equipped =
equipment != null ? equipment.getAllEquipped() : Map.of();
// Resolve ALL V2 items with GLB models and per-item bone ownership
java.util.List<RegionBoneMapper.V2ItemAnimInfo> v2Items =
@@ -185,10 +188,22 @@ public class AnimationTickHandler {
if (!v2Items.isEmpty()) {
// V2 path: multi-item composite animation
java.util.Set<String> allOwnedParts = RegionBoneMapper.computeAllOwnedParts(v2Items);
MovementStyle activeStyle = MovementStyleClientState.get(player.getUUID());
AnimationContext context = AnimationContextResolver.resolve(player, state, activeStyle);
GltfAnimationApplier.applyMultiItemV2Animation(player, v2Items, context, allOwnedParts);
java.util.Set<String> allOwnedParts =
RegionBoneMapper.computeAllOwnedParts(v2Items);
MovementStyle activeStyle = MovementStyleClientState.get(
player.getUUID()
);
AnimationContext context = AnimationContextResolver.resolve(
player,
state,
activeStyle
);
GltfAnimationApplier.applyMultiItemV2Animation(
player,
v2Items,
context,
allOwnedParts
);
// Clear V1 tracking so transition back works
AnimationStateRegistry.getLastAnimId().remove(uuid);
} else {
@@ -197,11 +212,19 @@ public class AnimationTickHandler {
GltfAnimationApplier.clearV2Animation(player);
}
String animId = buildAnimationId(player, state);
String lastId = AnimationStateRegistry.getLastAnimId().get(uuid);
String lastId = AnimationStateRegistry.getLastAnimId().get(
uuid
);
if (!animId.equals(lastId)) {
boolean success = BondageAnimationManager.playAnimation(player, animId);
boolean success = BondageAnimationManager.playAnimation(
player,
animId
);
if (success) {
AnimationStateRegistry.getLastAnimId().put(uuid, animId);
AnimationStateRegistry.getLastAnimId().put(
uuid,
animId
);
}
}
}
@@ -236,8 +259,14 @@ public class AnimationTickHandler {
}
// Derive bound state from V2 regions (works client-side, synced via capability)
boolean armsBound = V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS);
boolean legsBound = V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.LEGS);
boolean armsBound = V2EquipmentHelper.isRegionOccupied(
player,
BodyRegionV2.ARMS
);
boolean legsBound = V2EquipmentHelper.isRegionOccupied(
player,
BodyRegionV2.LEGS
);
// V1 fallback: if no V2 regions are set but player is tied, derive from ItemBind NBT
if (!armsBound && !legsBound && bind.getItem() instanceof ItemBind) {

View File

@@ -15,8 +15,8 @@ import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
@@ -45,7 +45,8 @@ import net.minecraftforge.fml.common.Mod;
public class NpcAnimationTickHandler {
/** Track last animation ID per NPC to avoid redundant updates */
private static final Map<UUID, String> lastNpcAnimId = new ConcurrentHashMap<>();
private static final Map<UUID, String> lastNpcAnimId =
new ConcurrentHashMap<>();
/**
* Client tick: update animations for all loaded AbstractTiedUpNpc instances.
@@ -91,18 +92,31 @@ public class NpcAnimationTickHandler {
if (inPose) {
// Resolve V2 equipment map
IV2BondageEquipment equipment = V2EquipmentHelper.getEquipment(entity);
Map<BodyRegionV2, net.minecraft.world.item.ItemStack> equipped = equipment != null
? equipment.getAllEquipped() : Map.of();
RegionBoneMapper.GlbModelResult glbResult = RegionBoneMapper.resolveWinningItem(equipped);
IV2BondageEquipment equipment = V2EquipmentHelper.getEquipment(
entity
);
Map<BodyRegionV2, net.minecraft.world.item.ItemStack> equipped =
equipment != null ? equipment.getAllEquipped() : Map.of();
RegionBoneMapper.GlbModelResult glbResult =
RegionBoneMapper.resolveWinningItem(equipped);
if (glbResult != null) {
// V2 path: dual-layer animation with per-item bone ownership
RegionBoneMapper.BoneOwnership ownership =
RegionBoneMapper.computePerItemParts(equipped, glbResult.winningItem());
AnimationContext context = AnimationContextResolver.resolveNpc(entity);
GltfAnimationApplier.applyV2Animation(entity, glbResult.modelLoc(),
glbResult.animSource(), context, ownership);
RegionBoneMapper.computePerItemParts(
equipped,
glbResult.winningItem()
);
AnimationContext context = AnimationContextResolver.resolveNpc(
entity
);
GltfAnimationApplier.applyV2Animation(
entity,
glbResult.modelLoc(),
glbResult.animSource(),
context,
ownership
);
lastNpcAnimId.remove(uuid);
} else {
// V1 fallback: JSON-based PlayerAnimator animations
@@ -118,7 +132,10 @@ public class NpcAnimationTickHandler {
}
}
} else {
if (lastNpcAnimId.containsKey(uuid) || GltfAnimationApplier.hasActiveState(entity)) {
if (
lastNpcAnimId.containsKey(uuid) ||
GltfAnimationApplier.hasActiveState(entity)
) {
if (GltfAnimationApplier.hasActiveState(entity)) {
GltfAnimationApplier.clearV2Animation(entity);
} else {
@@ -141,7 +158,9 @@ public class NpcAnimationTickHandler {
positionPrefix = "kneel";
}
net.minecraft.world.item.ItemStack bind = entity.getEquipment(BodyRegionV2.ARMS);
net.minecraft.world.item.ItemStack bind = entity.getEquipment(
BodyRegionV2.ARMS
);
PoseType poseType = PoseType.STANDARD;
boolean hasBind = false;
@@ -151,8 +170,14 @@ public class NpcAnimationTickHandler {
}
// Derive bound state from V2 regions (AbstractTiedUpNpc implements IV2EquipmentHolder)
boolean armsBound = V2EquipmentHelper.isRegionOccupied(entity, BodyRegionV2.ARMS);
boolean legsBound = V2EquipmentHelper.isRegionOccupied(entity, BodyRegionV2.LEGS);
boolean armsBound = V2EquipmentHelper.isRegionOccupied(
entity,
BodyRegionV2.ARMS
);
boolean legsBound = V2EquipmentHelper.isRegionOccupied(
entity,
BodyRegionV2.LEGS
);
// V1 fallback: if no V2 regions set but NPC has a bind, derive from ItemBind NBT
if (!armsBound && !legsBound && bind.getItem() instanceof ItemBind) {

View File

@@ -82,9 +82,7 @@ public final class AnimationIdBuilder {
return poseType.getBindTypeName();
}
// ========================================
// Unified Build Method
// ========================================
/**
* Build animation ID string for entities.