Phase 2.8 : V2 player rendering cleanup + QA checklist doc
Cut wholesale du pipeline V2 player-anim suite au bascule RIG (pas d'opt-in, pas de rollback — accepté par le mainteneur). - rm MixinPlayerModel : le renderer RIG patched ne passe plus par PlayerModel.setupAnim, donc l'injection @TAIL devenait dead code. Les features dog pose head compensation seront ré-exprimées en StaticAnimation pose_dog.json (V3-REW-07). - Strip tiedup.mixins.json : retiré client/MixinPlayerModel, restent MixinCamera + MixinLivingEntitySleeping. - BondageAnimationManager.init() : retiré les 3 PlayerAnimationFactory registrations (context / item / furniture), le path joueur n'en dépend plus. Factory IDs conservés car les getPlayerLayer*() sont tolérantes au null retour via try/catch existants — et restent utilisées par le cache fallback remote. Les NPCs continuent d'utiliser cette classe via l'accès direct animation stack (IAnimatedPlayer.getAnimationStack().addAnimLayer), inchangé. - TiedUpMod.onClientSetup : suppression de l'appel BondageAnimationManager.init() (la méthode est maintenant un log no-op, conservée pour la signature publique + doc du changement). - AnimationTickHandler.onClientTick : retrait de la boucle mc.level.players() + updatePlayerAnimation + tickFurnitureSafety + cold-cache furniture retry. Les joueurs sont ticked par RigAnimationTickHandler (Phase 2.7). Conservé : le cleanup périodique ClothesClientCache (hygiène mémoire indépendante), le hook onPlayerLogout (cleanup per-UUID des caches NPC restants), et le hook onWorldUnload (caches V2 encore utilisés par NpcAnimationTickHandler). Imports unused strippés. - DogPoseHelper : mis à jour la javadoc pour refléter le retrait du path player (NPCs only désormais). Compile GREEN. 20/20 tests rig GREEN. QA runtime : cf. docs/plans/rig/PHASE2_QA.md (non commit — fichier working doc sous docs/plans/ gitignored par convention repo). Net LOC : -276.
This commit is contained in:
@@ -90,41 +90,30 @@ public class BondageAnimationManager {
|
||||
|
||||
/**
|
||||
* Initialize the animation system.
|
||||
* Must be called during client setup to register the player animation factory.
|
||||
*
|
||||
* <p>Phase 2.8 RIG cleanup — les 3 {@link PlayerAnimationFactory}
|
||||
* (context / item / furniture) ont été supprimées : le renderer RIG
|
||||
* patched ne passe plus par le pipeline PlayerAnimator pour le joueur,
|
||||
* donc les factories devenaient dead code (aucun call site n'atteint
|
||||
* jamais la map associée côté joueur).</p>
|
||||
*
|
||||
* <p>Les NPCs continuent d'être animés via cette classe : le chemin
|
||||
* {@link #getOrCreateLayer} pour les entités {@code IAnimatedPlayer}
|
||||
* non-joueur utilise {@code animated.getAnimationStack().addAnimLayer(...)}
|
||||
* en direct — ça ne dépend d'aucune factory. Voir {@code NpcAnimationTickHandler}
|
||||
* pour le consumer. Le path player dans {@link #getOrCreateLayer} est
|
||||
* laissé en place volontairement : il retombe proprement sur null
|
||||
* (PlayerAnimationAccess throw → catch → null) et laisse le tick RIG
|
||||
* s'occuper du joueur.</p>
|
||||
*
|
||||
* <p>Conservé comme méthode publique pour ne pas casser les call sites
|
||||
* externes et pour documenter la bascule. Rework V3 (player anim
|
||||
* natives RIG) : voir V3-REW-01 dans {@code docs/plans/rig/V3_REWORK_BACKLOG.md}.</p>
|
||||
*/
|
||||
public static void init() {
|
||||
LOGGER.info("BondageAnimationManager initializing...");
|
||||
|
||||
// Context layer: lower priority = evaluated first, overridable by item layer.
|
||||
// In AnimationStack, layers are sorted ascending by priority and evaluated in order.
|
||||
// Higher priority layers override lower ones.
|
||||
PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(
|
||||
CONTEXT_FACTORY_ID,
|
||||
CONTEXT_LAYER_PRIORITY,
|
||||
player -> new ModifierLayer<>()
|
||||
);
|
||||
|
||||
// Item layer: higher priority = evaluated last, overrides context layer
|
||||
PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(
|
||||
FACTORY_ID,
|
||||
ITEM_LAYER_PRIORITY,
|
||||
player -> new ModifierLayer<>()
|
||||
);
|
||||
|
||||
// Furniture layer: highest priority = overrides item layer on blocked bones.
|
||||
// Non-blocked bones are disabled via FurnitureAnimationContext so items
|
||||
// can still animate free regions (gag, blindfold, etc.).
|
||||
PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(
|
||||
FURNITURE_FACTORY_ID,
|
||||
FURNITURE_LAYER_PRIORITY,
|
||||
player -> new ModifierLayer<>()
|
||||
);
|
||||
|
||||
LOGGER.info(
|
||||
"BondageAnimationManager: Factories registered — context (pri {}), item (pri {}), furniture (pri {})",
|
||||
CONTEXT_LAYER_PRIORITY,
|
||||
ITEM_LAYER_PRIORITY,
|
||||
FURNITURE_LAYER_PRIORITY
|
||||
"BondageAnimationManager: player-side factories no-op (Phase 2.8 RIG cleanup). " +
|
||||
"NPC-side animation stack access untouched."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,29 +3,17 @@ package com.tiedup.remake.client.animation.tick;
|
||||
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.events.CellHighlightHandler;
|
||||
import com.tiedup.remake.client.events.LeashProxyClientHandler;
|
||||
import com.tiedup.remake.client.gltf.GltfAnimationApplier;
|
||||
import com.tiedup.remake.client.state.ClothesClientCache;
|
||||
import com.tiedup.remake.client.state.MovementStyleClientState;
|
||||
import com.tiedup.remake.client.state.PetBedClientState;
|
||||
import com.tiedup.remake.util.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 java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
@@ -35,16 +23,29 @@ import net.minecraftforge.fml.common.Mod;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Event handler for player animation tick updates.
|
||||
* Event handler for animation tick updates.
|
||||
*
|
||||
* <p>Simplified handler that:
|
||||
* <p><b>Phase 2.8 RIG cleanup</b> : le ticking <i>player</i> V2 (boucle
|
||||
* {@code mc.level.players()} + {@code updatePlayerAnimation}) est entièrement
|
||||
* désactivé. Les joueurs sont désormais pilotés par
|
||||
* {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler} via le pipeline
|
||||
* RIG (capability {@code LivingEntityPatch} + {@code Animator} natif EF).
|
||||
* Les features V2 qui dépendaient du tick player sont trackées dans
|
||||
* {@code docs/plans/rig/V3_REWORK_BACKLOG.md} (V3-REW-01/02/03/07).</p>
|
||||
*
|
||||
* <p>Restent actifs ici :
|
||||
* <ul>
|
||||
* <li>Tracks tied/struggling/sneaking state for players</li>
|
||||
* <li>Plays animations via BondageAnimationManager when state changes</li>
|
||||
* <li>Handles cleanup on logout/world unload</li>
|
||||
* <li>Nettoyage périodique de {@code ClothesClientCache} (cache remote
|
||||
* players, hygiène mémoire indépendante du pipeline de rendu)</li>
|
||||
* <li>Cleanup logout / world unload (caches V2 encore utilisés par les
|
||||
* NPCs ticked par {@link NpcAnimationTickHandler})</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Registered on the FORGE event bus (not MOD bus).
|
||||
* <p>Le ticking NPC est assuré par {@link NpcAnimationTickHandler}. Ce
|
||||
* handler ne tick plus les NPCs directement — il ne gère que les hooks
|
||||
* lifecycle globaux (logout + world unload).</p>
|
||||
*
|
||||
* <p>Registered on the FORGE event bus (not MOD bus).</p>
|
||||
*/
|
||||
@Mod.EventBusSubscriber(
|
||||
modid = "tiedup",
|
||||
@@ -83,8 +84,20 @@ public class AnimationTickHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Client tick event - called every tick on the client.
|
||||
* Updates animations for all players when their bondage state changes.
|
||||
* Client tick event — called every tick on the client.
|
||||
*
|
||||
* <p>Phase 2.8 : la boucle {@code mc.level.players()} qui appelait
|
||||
* {@code updatePlayerAnimation}, {@code tickFurnitureSafety} et le
|
||||
* cold-cache retry furniture a été entièrement supprimée. Les joueurs
|
||||
* sont désormais ticked par {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler}
|
||||
* via le pipeline RIG (capability {@code LivingEntityPatch} +
|
||||
* {@code Animator}). Les régressions visuelles (V2 bondage layer cassé,
|
||||
* furniture seat pose sur joueur cassée, pet bed pose cassée) sont
|
||||
* listées dans {@code docs/plans/rig/V3_REWORK_BACKLOG.md}.</p>
|
||||
*
|
||||
* <p>Seul le nettoyage périodique de {@link ClothesClientCache} reste
|
||||
* — c'est de l'hygiène mémoire sur un cache indexé UUID joueur,
|
||||
* indépendant du pipeline de rendu.</p>
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
||||
@@ -97,193 +110,17 @@ public class AnimationTickHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process pending animations first (retry failed animations for remote players)
|
||||
PendingAnimationManager.processPending(mc.level);
|
||||
|
||||
// Periodic cleanup of stale cache entries (every 60 seconds = 1200 ticks)
|
||||
// Periodic cleanup of stale clothes cache entries (every 60 seconds = 1200 ticks).
|
||||
// Indépendant du rendu V2/RIG — c'est juste un cache UUID→ClothesData qui
|
||||
// doit libérer la mémoire des joueurs déconnectés depuis >5min.
|
||||
if (++cleanupTickCounter >= 1200) {
|
||||
cleanupTickCounter = 0;
|
||||
ClothesClientCache.cleanupStale();
|
||||
}
|
||||
|
||||
// Then update all player animations
|
||||
for (Player player : mc.level.players()) {
|
||||
if (player instanceof AbstractClientPlayer clientPlayer) {
|
||||
updatePlayerAnimation(clientPlayer);
|
||||
}
|
||||
// Safety: remove stale furniture animations for players no longer on seats
|
||||
BondageAnimationManager.tickFurnitureSafety(player);
|
||||
// Cold-cache retry: if the player is seated on furniture but has no
|
||||
// active pose (GLB was not yet loaded at mount time, or the GLB cache
|
||||
// entry was a transient failure), retry until the cache warms.
|
||||
// FurnitureGltfCache memoizes failures via Optional.empty(), so
|
||||
// retries after a genuine parse failure return instantly with no
|
||||
// reparse. Bounded at MAX_FURNITURE_RETRIES so a legacy V1-only
|
||||
// GLB (no Player_* armature → seatSkeleton==null → no animation
|
||||
// ever possible) doesn't spam retries at 20 Hz forever.
|
||||
// Single read of getVehicle() — avoids a re-read where the
|
||||
// vehicle could change between instanceof and cast.
|
||||
com.tiedup.remake.v2.furniture.EntityFurniture furniture =
|
||||
player.getVehicle() instanceof
|
||||
com.tiedup.remake.v2.furniture.EntityFurniture f ? f : null;
|
||||
boolean hasAnim = BondageAnimationManager.hasFurnitureAnimation(
|
||||
player
|
||||
);
|
||||
UUID playerUuid = player.getUUID();
|
||||
if (furniture != null && !hasAnim) {
|
||||
int retries = furnitureRetryCounters.getOrDefault(
|
||||
playerUuid,
|
||||
0
|
||||
);
|
||||
if (retries < MAX_FURNITURE_RETRIES) {
|
||||
furnitureRetryCounters.put(playerUuid, retries + 1);
|
||||
com.tiedup.remake.v2.furniture.client.FurnitureClientAnimator
|
||||
.start(furniture, player);
|
||||
if (retries + 1 == MAX_FURNITURE_RETRIES) {
|
||||
LOGGER.debug(
|
||||
"[FurnitureAnim] Giving up on furniture animation retry for {} after {} attempts — GLB likely has no Player_* armature.",
|
||||
player.getName().getString(),
|
||||
MAX_FURNITURE_RETRIES
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dismounted or successfully applied — drop the counter so a
|
||||
// later re-mount starts fresh.
|
||||
furnitureRetryCounters.remove(playerUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update animation for a single player.
|
||||
*/
|
||||
private static void updatePlayerAnimation(AbstractClientPlayer player) {
|
||||
// Safety check: skip for removed/dead players
|
||||
if (player.isRemoved() || !player.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
UUID uuid = player.getUUID();
|
||||
|
||||
// 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 wasTied =
|
||||
AnimationStateRegistry.getLastTiedState().getOrDefault(uuid, false);
|
||||
|
||||
// Pet bed animations take priority over bondage animations
|
||||
if (PetBedClientState.get(uuid) != 0) {
|
||||
// Lock body rotation to bed facing (prevents camera from rotating the model)
|
||||
float lockedRot = PetBedClientState.getFacing(uuid);
|
||||
player.yBodyRot = lockedRot;
|
||||
player.yBodyRotO = lockedRot;
|
||||
|
||||
// Clamp head rotation to ±50° from body (like vehicle)
|
||||
float headRot = player.getYHeadRot();
|
||||
float clamped =
|
||||
lockedRot +
|
||||
net.minecraft.util.Mth.clamp(
|
||||
net.minecraft.util.Mth.wrapDegrees(headRot - lockedRot),
|
||||
-50f,
|
||||
50f
|
||||
);
|
||||
player.setYHeadRot(clamped);
|
||||
player.yHeadRotO = clamped;
|
||||
|
||||
AnimationStateRegistry.getLastTiedState().put(uuid, isTied);
|
||||
return;
|
||||
}
|
||||
|
||||
// Human chair: clamp 1st-person camera only (body lock handled by MixinLivingEntityBodyRot)
|
||||
// NO return — animation HUMAN_CHAIR must continue playing below
|
||||
if (isTied && state != null) {
|
||||
ItemStack chairBind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (HumanChairHelper.isActive(chairBind)) {
|
||||
// 1st person only: clamp yRot so player can't look behind
|
||||
// 3rd person: yRot untouched → camera orbits freely 360°
|
||||
if (
|
||||
player == Minecraft.getInstance().player &&
|
||||
Minecraft.getInstance().options.getCameraType() ==
|
||||
net.minecraft.client.CameraType.FIRST_PERSON
|
||||
) {
|
||||
float lockedRot = HumanChairHelper.getFacing(chairBind);
|
||||
float camClamped =
|
||||
lockedRot +
|
||||
net.minecraft.util.Mth.clamp(
|
||||
net.minecraft.util.Mth.wrapDegrees(
|
||||
player.getYRot() - lockedRot
|
||||
),
|
||||
-90f,
|
||||
90f
|
||||
);
|
||||
player.setYRot(camClamped);
|
||||
player.yRotO =
|
||||
lockedRot +
|
||||
net.minecraft.util.Mth.clamp(
|
||||
net.minecraft.util.Mth.wrapDegrees(
|
||||
player.yRotO - lockedRot
|
||||
),
|
||||
-90f,
|
||||
90f
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isTied) {
|
||||
// Resolve V2 equipped items
|
||||
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 =
|
||||
RegionBoneMapper.resolveAllV2Items(equipped);
|
||||
|
||||
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
|
||||
);
|
||||
} else if (GltfAnimationApplier.hasActiveState(player)) {
|
||||
// Clear any residual V2 composite animation when the player
|
||||
// is still isTiedUp() but has no GLB-bearing items — e.g.
|
||||
// a non-GLB item keeps the tied state, or a GLB item was
|
||||
// removed while another V2 item remains on a non-animated
|
||||
// region. Leaving the composite in place locks the arms in
|
||||
// the pose of an item the player no longer wears.
|
||||
GltfAnimationApplier.clearV2Animation(player);
|
||||
}
|
||||
} else if (wasTied) {
|
||||
// Was tied, now free - stop all animations
|
||||
if (GltfAnimationApplier.hasActiveState(player)) {
|
||||
GltfAnimationApplier.clearV2Animation(player);
|
||||
} else {
|
||||
BondageAnimationManager.stopAnimation(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AnimationStateRegistry.getLastTiedState().put(uuid, isTied);
|
||||
// Le tick per-player V2 (updatePlayerAnimation, tickFurnitureSafety,
|
||||
// cold-cache furniture retry) est délégué à RigAnimationTickHandler
|
||||
// Phase 2.7+. Rien à faire ici.
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,22 +15,14 @@ import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
* <li>Head yaw: convert to zRot (roll) since yRot axis is sideways</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Architecture: Players vs NPCs</h2>
|
||||
* <pre>
|
||||
* ┌─────────────────────────────────────────────────────────────────┐
|
||||
* │ PLAYERS │
|
||||
* ├─────────────────────────────────────────────────────────────────┤
|
||||
* │ 1. PlayerArmHideEventHandler.onRenderPlayerPre() │
|
||||
* │ - Offset vertical (-6 model units) │
|
||||
* │ - Rotation Y lissée (dogPoseState tracking) │
|
||||
* │ │
|
||||
* │ 2. Animation (PlayerAnimator) │
|
||||
* │ - body.pitch = -90° → appliqué au PoseStack automatiquement │
|
||||
* │ │
|
||||
* │ 3. MixinPlayerModel.setupAnim() @TAIL │
|
||||
* │ - Uses DogPoseHelper.applyHeadCompensationClamped() │
|
||||
* └─────────────────────────────────────────────────────────────────┘
|
||||
* <h2>Architecture — NPCs only (Phase 2.8 RIG cleanup)</h2>
|
||||
* <p>Le path PLAYER (ex-{@code MixinPlayerModel.setupAnim @TAIL}) a été retiré
|
||||
* Phase 2.8 : le renderer RIG patched ne passe plus par {@code PlayerModel.setupAnim},
|
||||
* donc le mixin devenait dead code. La compensation head dog pose sera ré-exprimée
|
||||
* nativement en StaticAnimation {@code pose_dog.json} (cf. V3-REW-07 dans
|
||||
* {@code docs/plans/rig/V3_REWORK_BACKLOG.md}).</p>
|
||||
*
|
||||
* <pre>
|
||||
* ┌─────────────────────────────────────────────────────────────────┐
|
||||
* │ NPCs │
|
||||
* ├─────────────────────────────────────────────────────────────────┤
|
||||
@@ -48,25 +40,13 @@ import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
* └─────────────────────────────────────────────────────────────────┘
|
||||
* </pre>
|
||||
*
|
||||
* <h2>Key Differences</h2>
|
||||
* <table>
|
||||
* <tr><th>Aspect</th><th>Players</th><th>NPCs</th></tr>
|
||||
* <tr><td>Rotation X application</td><td>Auto by PlayerAnimator</td><td>Manual in setupRotations()</td></tr>
|
||||
* <tr><td>Rotation Y smoothing</td><td>PlayerArmHideEventHandler</td><td>EntityDamsel.tick() via RotationSmoother</td></tr>
|
||||
* <tr><td>Head compensation</td><td>MixinPlayerModel</td><td>DamselModel.setupAnim()</td></tr>
|
||||
* <tr><td>Reset body.xRot</td><td>Not needed</td><td>Yes (prevents double rotation)</td></tr>
|
||||
* <tr><td>Vertical offset</td><td>-6 model units</td><td>-7 model units</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* <h2>Usage</h2>
|
||||
* <p>Used by:
|
||||
* <ul>
|
||||
* <li>MixinPlayerModel - for player head compensation</li>
|
||||
* <li>DamselModel - for NPC head compensation</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see RotationSmoother for Y rotation smoothing
|
||||
* @see com.tiedup.remake.mixin.client.MixinPlayerModel
|
||||
* @see com.tiedup.remake.client.model.DamselModel
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
|
||||
@@ -196,9 +196,11 @@ public class TiedUpMod {
|
||||
// RIG Phase 2 — override client dispatch PLAYER → Local/Client/ServerPlayerPatch
|
||||
com.tiedup.remake.rig.patch.EntityPatchProvider.registerEntityPatchesClient();
|
||||
|
||||
// Initialize unified BondageAnimationManager
|
||||
com.tiedup.remake.client.animation.BondageAnimationManager.init();
|
||||
LOGGER.info("BondageAnimationManager initialized");
|
||||
// Phase 2.8 RIG cleanup : BondageAnimationManager.init() (factory
|
||||
// registrations PlayerAnimator côté joueur) a été supprimé — le RIG
|
||||
// prend le relai pour les joueurs via RigAnimationTickHandler.
|
||||
// Les NPCs continuent d'être animés via BondageAnimationManager en
|
||||
// accès direct animation stack (cf. NpcAnimationTickHandler).
|
||||
|
||||
// Initialize OBJ model registry for 3D bondage items
|
||||
com.tiedup.remake.client.renderer.obj.ObjModelRegistry.init();
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.tiedup.remake.mixin.client;
|
||||
|
||||
import com.tiedup.remake.client.animation.render.DogPoseRenderHandler;
|
||||
import com.tiedup.remake.client.animation.util.DogPoseHelper;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import net.minecraft.client.model.PlayerModel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Mixin for PlayerModel to handle DOG pose head adjustments.
|
||||
*
|
||||
* When in DOG pose (body horizontal):
|
||||
* - Head pitch offset so player looks forward
|
||||
* - Head yaw converted to zRot (roll) since yRot axis is sideways when body is horizontal
|
||||
*/
|
||||
@Mixin(PlayerModel.class)
|
||||
public class MixinPlayerModel {
|
||||
|
||||
@Inject(method = "setupAnim", at = @At("TAIL"))
|
||||
private void tiedup$adjustDogPose(
|
||||
LivingEntity entity,
|
||||
float limbSwing,
|
||||
float limbSwingAmount,
|
||||
float ageInTicks,
|
||||
float netHeadYaw,
|
||||
float headPitch,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
if (!(entity instanceof AbstractClientPlayer player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (bind.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PoseTypeHelper.getPoseType(bind) != PoseType.DOG) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerModel<?> model = (PlayerModel<?>) (Object) this;
|
||||
|
||||
// === HEAD ROTATION FOR HORIZONTAL BODY ===
|
||||
// Body is at -90° pitch (horizontal, face down)
|
||||
// We apply a rotation delta to the poseStack in PlayerArmHideEventHandler
|
||||
// The head needs to compensate for this transformation
|
||||
|
||||
float rotationDelta = DogPoseRenderHandler.getAppliedRotationDelta(
|
||||
player.getUUID()
|
||||
);
|
||||
boolean moving = DogPoseRenderHandler.isDogPoseMoving(player.getUUID());
|
||||
|
||||
// netHeadYaw is head relative to vanilla body (yHeadRot - yBodyRot)
|
||||
// We rotated the model by rotationDelta, so compensate:
|
||||
// effectiveHeadYaw = netHeadYaw + rotationDelta
|
||||
float headYaw = netHeadYaw + rotationDelta;
|
||||
|
||||
// Clamp based on movement state and apply head compensation
|
||||
float maxYaw = moving ? 60f : 90f;
|
||||
DogPoseHelper.applyHeadCompensationClamped(
|
||||
model.head,
|
||||
model.hat,
|
||||
headPitch,
|
||||
headYaw,
|
||||
maxYaw
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
"MixinLivingEntityBodyRot"
|
||||
],
|
||||
"client": [
|
||||
"client/MixinPlayerModel",
|
||||
"client/MixinCamera",
|
||||
"client/MixinLivingEntitySleeping"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user