fix(P1+P2): swarm review — UX fixes + V1 dead code cleanup
P1 — UX visible: - Add 4 missing translations (leather_mittens, ball_gag_3d, taser, debug_wand) - Fix earplugs resistance bucket: "blindfold" → "earplug" - Split mixin config: core (require=1) + compat MCA (require=0) - Bump PROTOCOL_VERSION from "1" to "2" - Add debug_wand item model (no texture — debug only) - Fix 3 display_name inconsistencies (chain, vine_seed, shock_collar_auto) P2 — Dead code cleanup: - Delete IHas3DModelConfig + Model3DConfig (zero imports) - Remove MovementStyleResolver.resolveV1Fallback() + V1Fallback record + V1 constants - Remove AnimationTickHandler V1 fallback block + buildAnimationId() - Document PlayerEquipment.equipInRegion() bypass as intentional (force-equip paths)
This commit is contained in:
@@ -7,16 +7,12 @@ 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;
|
||||
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.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.util.HumanChairHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -207,28 +203,9 @@ public class AnimationTickHandler {
|
||||
);
|
||||
// Clear V1 tracking so transition back works
|
||||
AnimationStateRegistry.getLastAnimId().remove(uuid);
|
||||
} else {
|
||||
// V1 fallback
|
||||
if (GltfAnimationApplier.hasActiveState(player)) {
|
||||
GltfAnimationApplier.clearV2Animation(player);
|
||||
}
|
||||
String animId = buildAnimationId(player, state);
|
||||
String lastId = AnimationStateRegistry.getLastAnimId().get(
|
||||
uuid
|
||||
);
|
||||
if (!animId.equals(lastId)) {
|
||||
boolean success = BondageAnimationManager.playAnimation(
|
||||
player,
|
||||
animId
|
||||
);
|
||||
if (success) {
|
||||
AnimationStateRegistry.getLastAnimId().put(
|
||||
uuid,
|
||||
animId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// No V2 items with GLB models — nothing to animate.
|
||||
// V1 items without data-driven definitions are not animated.
|
||||
} else if (wasTied) {
|
||||
// Was tied, now free - stop all animations
|
||||
if (GltfAnimationApplier.hasActiveState(player)) {
|
||||
@@ -242,53 +219,6 @@ public class AnimationTickHandler {
|
||||
AnimationStateRegistry.getLastTiedState().put(uuid, isTied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build animation ID from player's current state (V1 path).
|
||||
*/
|
||||
private static String buildAnimationId(
|
||||
Player player,
|
||||
PlayerBindState state
|
||||
) {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bind);
|
||||
|
||||
// Human chair mode: override DOG pose to HUMAN_CHAIR (straight limbs)
|
||||
poseType = HumanChairHelper.resolveEffectivePose(poseType, bind);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// V1 fallback: if no V2 regions are set but player is tied, derive from bind mode NBT
|
||||
if (!armsBound && !legsBound && BindModeHelper.isBindItem(bind)) {
|
||||
armsBound = BindModeHelper.hasArmsBound(bind);
|
||||
legsBound = BindModeHelper.hasLegsBound(bind);
|
||||
}
|
||||
|
||||
boolean isStruggling = state.isStruggling();
|
||||
boolean isSneaking = player.isCrouching();
|
||||
boolean isMoving =
|
||||
player.getDeltaMovement().horizontalDistanceSqr() > 1e-6;
|
||||
|
||||
// Build animation ID with sneak and movement support
|
||||
return AnimationIdBuilder.build(
|
||||
poseType,
|
||||
armsBound,
|
||||
legsBound,
|
||||
null,
|
||||
isStruggling,
|
||||
true,
|
||||
isSneaking,
|
||||
isMoving
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Player logout event - cleanup animation data.
|
||||
*/
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.tiedup.remake.items.bondage3d;
|
||||
|
||||
/**
|
||||
* Interface for items that have a 3D model configuration.
|
||||
* Implement this to provide custom position, scale, and rotation for 3D rendering.
|
||||
*/
|
||||
public interface IHas3DModelConfig {
|
||||
/**
|
||||
* Get the 3D model configuration for rendering.
|
||||
* @return The Model3DConfig with position, scale, and rotation offsets
|
||||
*/
|
||||
Model3DConfig getModelConfig();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.tiedup.remake.items.bondage3d;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Configuration immutable for a 3D item.
|
||||
* Contains all parameters necessary for rendering.
|
||||
*/
|
||||
public record Model3DConfig(
|
||||
String objPath, // "tiedup:models/obj/ball_gag/model.obj"
|
||||
String texturePath, // "tiedup:models/obj/ball_gag/texture.png" (or null = use MTL)
|
||||
float posOffsetX, // Horizontal offset
|
||||
float posOffsetY, // Vertical offset (negative = lower)
|
||||
float posOffsetZ, // Depth offset (positive = forward)
|
||||
float scale, // Scale (1.0 = normal size)
|
||||
float rotOffsetX, // Additional X rotation
|
||||
float rotOffsetY, // Additional Y rotation
|
||||
float rotOffsetZ, // Additional Z rotation
|
||||
Set<String> tintMaterials // Material names to apply color tint (e.g., "Ball")
|
||||
) {
|
||||
/** Config without tinting */
|
||||
public static Model3DConfig simple(String objPath, String texturePath) {
|
||||
return new Model3DConfig(
|
||||
objPath,
|
||||
texturePath,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1.0f,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Set.of()
|
||||
);
|
||||
}
|
||||
|
||||
/** Config with position/rotation but no tinting */
|
||||
public Model3DConfig(
|
||||
String objPath,
|
||||
String texturePath,
|
||||
float posOffsetX,
|
||||
float posOffsetY,
|
||||
float posOffsetZ,
|
||||
float scale,
|
||||
float rotOffsetX,
|
||||
float rotOffsetY,
|
||||
float rotOffsetZ
|
||||
) {
|
||||
this(
|
||||
objPath,
|
||||
texturePath,
|
||||
posOffsetX,
|
||||
posOffsetY,
|
||||
posOffsetZ,
|
||||
scale,
|
||||
rotOffsetX,
|
||||
rotOffsetY,
|
||||
rotOffsetZ,
|
||||
Set.of()
|
||||
);
|
||||
}
|
||||
|
||||
/** Check if this item supports color tinting */
|
||||
public boolean supportsTinting() {
|
||||
return tintMaterials != null && !tintMaterials.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ import net.minecraftforge.network.simple.SimpleChannel;
|
||||
*/
|
||||
public class ModNetwork {
|
||||
|
||||
private static final String PROTOCOL_VERSION = "1";
|
||||
private static final String PROTOCOL_VERSION = "2";
|
||||
|
||||
public static final SimpleChannel CHANNEL =
|
||||
NetworkRegistry.newSimpleChannel(
|
||||
|
||||
@@ -424,7 +424,14 @@ public class PlayerEquipment {
|
||||
|
||||
/**
|
||||
* Low-level equip: writes to V2 capability, calls IBondageItem lifecycle hooks, syncs.
|
||||
* Uses setInRegion directly because V1 items do not implement IV2BondageItem.
|
||||
*
|
||||
* <p>INTENTIONALLY bypasses V2EquipmentManager.tryEquip() because:
|
||||
* <ul>
|
||||
* <li>V1 items (IBondageItem) do not implement IV2BondageItem, so tryEquip() rejects them</li>
|
||||
* <li>All callers (commands, NPC AI, NBT restore) are force-equip paths that should not
|
||||
* be subject to V2 conflict resolution (SMELL-003 reviewed — bypass is by design)</li>
|
||||
* </ul>
|
||||
* <p>Basic safety (isRegionOccupied, canEquip) is still checked here.</p>
|
||||
*/
|
||||
private void equipInRegion(BodyRegionV2 region, ItemStack stack) {
|
||||
Player player = host.getPlayer();
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package com.tiedup.remake.v2.bondage.movement;
|
||||
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import java.util.Map;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Resolves the winning movement style from a player's equipped bondage items.
|
||||
@@ -18,32 +14,15 @@ import org.jetbrains.annotations.Nullable;
|
||||
*
|
||||
* <p>The winning item's {@link MovementModifier} (if present) overrides the style's
|
||||
* default speed/jump values. Modifiers from lower-severity items are ignored.</p>
|
||||
*
|
||||
* <h3>V1 Compatibility (H6 fix)</h3>
|
||||
* <p>V1 items (ItemBind) stored in V2 capability
|
||||
* do not have data-driven definitions. This resolver provides a fallback that
|
||||
* maps V1 bind mode + pose type to a {@link MovementStyle} with speed values matching
|
||||
* the original V1 behavior, preventing double stacking between the legacy
|
||||
* {@code RestraintEffectUtils} attribute modifier and the V2 modifier.</p>
|
||||
*/
|
||||
public final class MovementStyleResolver {
|
||||
|
||||
private MovementStyleResolver() {}
|
||||
|
||||
// --- V1 fallback speed values ---
|
||||
// V1 used ADDITION(-0.09) on base 0.10 = 0.01 effective = 10% speed
|
||||
// Expressed as MULTIPLY_BASE fraction: 0.10
|
||||
private static final float V1_STANDARD_SPEED = 0.10f;
|
||||
|
||||
// V1 used ADDITION(-0.10) on base 0.10 = 0.00 effective = 0% speed
|
||||
// Expressed as MULTIPLY_BASE fraction: 0.0 (fully immobile)
|
||||
private static final float V1_IMMOBILIZED_SPEED = 0.0f;
|
||||
|
||||
/**
|
||||
* Resolve the winning movement style from all equipped items.
|
||||
*
|
||||
* <p>Checks V2 data-driven definitions first, then falls back to V1 {@link ItemBind}
|
||||
* introspection for items without data-driven definitions.</p>
|
||||
* <p>Uses V2 data-driven definitions to determine movement style.</p>
|
||||
*
|
||||
* @param equipped map of region to ItemStack (from {@code IV2BondageEquipment.getAllEquipped()})
|
||||
* @return the resolved movement, or {@link ResolvedMovement#NONE} if no styled items
|
||||
@@ -66,7 +45,6 @@ public final class MovementStyleResolver {
|
||||
ItemStack stack = entry.getValue();
|
||||
if (stack.isEmpty()) continue;
|
||||
|
||||
// --- Try V2 data-driven definition first ---
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
if (def != null && def.movementStyle() != null) {
|
||||
MovementStyle style = def.movementStyle();
|
||||
@@ -90,26 +68,6 @@ public final class MovementStyleResolver {
|
||||
bestSeverity = severity;
|
||||
bestRegionOrdinal = regionOrdinal;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- V1 fallback: ItemBind without data-driven definition ---
|
||||
V1Fallback fallback = resolveV1Fallback(stack);
|
||||
if (fallback != null) {
|
||||
int severity = fallback.style.getSeverity();
|
||||
int regionOrdinal = region.ordinal();
|
||||
|
||||
if (
|
||||
severity > bestSeverity ||
|
||||
(severity == bestSeverity &&
|
||||
regionOrdinal < bestRegionOrdinal)
|
||||
) {
|
||||
bestStyle = fallback.style;
|
||||
bestSpeed = fallback.speed;
|
||||
bestJumpDisabled = fallback.jumpDisabled;
|
||||
bestSeverity = severity;
|
||||
bestRegionOrdinal = regionOrdinal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,56 +77,4 @@ public final class MovementStyleResolver {
|
||||
|
||||
return new ResolvedMovement(bestStyle, bestSpeed, bestJumpDisabled);
|
||||
}
|
||||
|
||||
// ==================== V1 Fallback ====================
|
||||
|
||||
/**
|
||||
* Attempt to derive a movement style from a V1 bind item.
|
||||
*
|
||||
* <p>Only items with legs bound produce a movement style. The mapping preserves
|
||||
* the original V1 speed values:</p>
|
||||
* <ul>
|
||||
* <li>WRAP / LATEX_SACK: SHUFFLE at 0% speed (full immobilization), jump disabled</li>
|
||||
* <li>DOG / HUMAN_CHAIR: CRAWL at V1 standard speed (10%), jump disabled</li>
|
||||
* <li>STANDARD / STRAITJACKET: SHUFFLE at 10% speed, jump disabled</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param stack the ItemStack to inspect
|
||||
* @return fallback resolution, or null if the item is not a V1 bind or legs are not bound
|
||||
*/
|
||||
@Nullable
|
||||
private static V1Fallback resolveV1Fallback(ItemStack stack) {
|
||||
if (!BindModeHelper.isBindItem(stack)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!BindModeHelper.hasLegsBound(stack)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(stack);
|
||||
|
||||
return switch (poseType) {
|
||||
case WRAP, LATEX_SACK -> new V1Fallback(
|
||||
MovementStyle.SHUFFLE,
|
||||
V1_IMMOBILIZED_SPEED,
|
||||
true
|
||||
);
|
||||
case DOG, HUMAN_CHAIR -> new V1Fallback(
|
||||
MovementStyle.CRAWL,
|
||||
V1_STANDARD_SPEED,
|
||||
true
|
||||
);
|
||||
default ->
|
||||
// STANDARD, STRAITJACKET: shuffle at V1 standard speed
|
||||
new V1Fallback(MovementStyle.SHUFFLE, V1_STANDARD_SPEED, true);
|
||||
};
|
||||
}
|
||||
|
||||
/** Internal holder for V1 fallback resolution result. */
|
||||
private record V1Fallback(
|
||||
MovementStyle style,
|
||||
float speed,
|
||||
boolean jumpDisabled
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ description='''${mod_description}'''
|
||||
[[mixins]]
|
||||
config="tiedup.mixins.json"
|
||||
|
||||
[[mixins]]
|
||||
config="tiedup-compat.mixins.json"
|
||||
|
||||
# Features are specific properties of the game environment, that you may want to declare you require. This example declares
|
||||
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
|
||||
# stop your mod loading on the server for example.
|
||||
|
||||
@@ -963,5 +963,10 @@
|
||||
"command.tiedup.debt.set": "Set %1$s's total debt to %2$s emeralds.",
|
||||
"command.tiedup.debt.added": "Added %1$s emeralds to %2$s's debt. Remaining: %3$s",
|
||||
"command.tiedup.debt.removed": "Removed %1$s emeralds from %2$s's debt. Remaining: %3$s",
|
||||
"command.tiedup.debt.removed_paid": "Removed %1$s emeralds from %2$s's debt. Remaining: %3$s (PAID OFF!)"
|
||||
"command.tiedup.debt.removed_paid": "Removed %1$s emeralds from %2$s's debt. Remaining: %3$s (PAID OFF!)",
|
||||
|
||||
"item.tiedup.leather_mittens": "Leather Mittens",
|
||||
"item.tiedup.ball_gag_3d": "Ball Gag",
|
||||
"item.tiedup.taser": "Taser",
|
||||
"item.tiedup.debug_wand": "Debug Wand"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "tiedup:item/debug_wand"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "tiedup:bondage_item",
|
||||
"display_name": "Chains",
|
||||
"display_name": "Chain",
|
||||
"translation_key": "item.tiedup.chain",
|
||||
"model": "tiedup:models/gltf/v2/binds/chain.glb",
|
||||
"regions": [
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"components": {
|
||||
"lockable": {},
|
||||
"resistance": {
|
||||
"id": "blindfold"
|
||||
"id": "earplug"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"body"
|
||||
]
|
||||
},
|
||||
"display_name": "Auto Shock Collar",
|
||||
"display_name": "Automatic Shock Collar",
|
||||
"translation_key": "item.tiedup.shock_collar_auto",
|
||||
"model": "tiedup:models/gltf/v2/collars/shock_collar_auto.glb",
|
||||
"components": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "tiedup:bondage_item",
|
||||
"display_name": "Vine Bind",
|
||||
"display_name": "Vine Seed",
|
||||
"translation_key": "item.tiedup.vine_seed",
|
||||
"model": "tiedup:models/gltf/v2/binds/vine_seed.glb",
|
||||
"regions": [
|
||||
|
||||
22
src/main/resources/tiedup-compat.mixins.json
Normal file
22
src/main/resources/tiedup-compat.mixins.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"required": false,
|
||||
"minVersion": "0.8",
|
||||
"package": "com.tiedup.remake.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"refmap": "tiedup.refmap.json",
|
||||
"mixins": [
|
||||
"MixinMCAVillagerInteraction",
|
||||
"MixinMCAVillagerLeash",
|
||||
"MixinMCAOpenAIChatAI",
|
||||
"MixinMCAMessenger"
|
||||
],
|
||||
"client": [
|
||||
"client/MixinVillagerEntityBaseModelMCA",
|
||||
"client/MixinVillagerEntityMCAAnimated",
|
||||
"client/MixinMCASpeechManager",
|
||||
"client/MixinMCAPlayerExtendedModel"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 0
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,14 @@
|
||||
"refmap": "tiedup.refmap.json",
|
||||
"mixins": [
|
||||
"MixinServerPlayer",
|
||||
"MixinMCAVillagerInteraction",
|
||||
"MixinMCAVillagerLeash",
|
||||
"MixinMCAOpenAIChatAI",
|
||||
"MixinMCAMessenger",
|
||||
"MixinLivingEntityBodyRot"
|
||||
],
|
||||
"client": [
|
||||
"client/MixinVillagerEntityBaseModelMCA",
|
||||
"client/MixinVillagerEntityMCAAnimated",
|
||||
"client/MixinMCASpeechManager",
|
||||
"client/MixinMCAPlayerExtendedModel",
|
||||
"client/MixinPlayerModel",
|
||||
"client/MixinCamera",
|
||||
"client/MixinLivingEntitySleeping"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 0
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user