Clean repo for open source release

Remove build artifacts, dev tool configs, unused dependencies,
and third-party source dumps. Add proper README, update .gitignore,
clean up Makefile.
This commit is contained in:
NotEvil
2026-04-12 00:51:22 +02:00
parent 2e7a1d403b
commit f6466360b6
1947 changed files with 238025 additions and 1 deletions

View File

@@ -0,0 +1,81 @@
package com.tiedup.remake.mixin.client;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.state.HumanChairHelper;
import com.tiedup.remake.state.PlayerBindState;
import net.minecraft.client.Camera;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin for Camera to lower first-person view when in DOG pose.
*
* In DOG pose, the player model is horizontal (like a dog), so the camera
* should be lowered to match the eye level being closer to the ground.
* Normal eye height is ~1.62 blocks, we lower it by ~0.6 blocks.
*/
@Mixin(Camera.class)
public abstract class MixinCamera {
@Shadow
private Vec3 position;
@Shadow
protected abstract void setPosition(Vec3 pos);
@Inject(method = "setup", at = @At("TAIL"))
private void tiedup$lowerCameraForDogPose(
BlockGetter level,
Entity entity,
boolean detached,
boolean thirdPersonReverse,
float partialTick,
CallbackInfo ci
) {
// Only affect first-person view
if (detached) {
return;
}
if (!(entity instanceof Player player)) {
return;
}
PlayerBindState state = PlayerBindState.getInstance(player);
if (state == null) {
return;
}
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
if (bind.isEmpty() || !(bind.getItem() instanceof ItemBind itemBind)) {
return;
}
if (itemBind.getPoseType() != PoseType.DOG) {
return;
}
// Lower camera by 0.6 blocks to match the horizontal body position
// Normal eye height is ~1.62, DOG pose should be around ~1.0
setPosition(position.add(0, -0.6, 0));
// Human chair: move camera forward into the head
if (HumanChairHelper.isActive(bind)) {
float facing = HumanChairHelper.getFacing(bind);
float facingRad = (float) Math.toRadians(facing);
double fwdX = -Math.sin(facingRad) * 0.6;
double fwdZ = Math.cos(facingRad) * 0.6;
setPosition(position.add(fwdX, 0, fwdZ));
}
}
}

View File

@@ -0,0 +1,30 @@
package com.tiedup.remake.mixin.client;
import com.tiedup.remake.client.state.PetBedClientState;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
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.CallbackInfoReturnable;
/**
* Client-side mixin: prevent vanilla sleeping visuals (laying flat) for
* players on a pet bed in SLEEP mode. Server-side isSleeping() is unaffected,
* so night skip still works.
*/
@Mixin(LivingEntity.class)
public abstract class MixinLivingEntitySleeping {
@Inject(method = "isSleeping", at = @At("HEAD"), cancellable = true)
private void tiedup$hidePetBedSleeping(
CallbackInfoReturnable<Boolean> cir
) {
LivingEntity self = (LivingEntity) (Object) this;
if (self.level().isClientSide() && self instanceof Player player) {
if (PetBedClientState.get(player.getUUID()) == 2) {
cir.setReturnValue(false);
}
}
}
}

View File

@@ -0,0 +1,106 @@
package com.tiedup.remake.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.tiedup.remake.compat.wildfire.WildfireCompat;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin for MCA's PlayerEntityExtendedModel to hide breasts when Wildfire is loaded.
*
* <p>MCA adds breasts to players through PlayerEntityExtendedModel. When Wildfire
* is also installed, we disable MCA's breasts to avoid double-rendering (Wildfire
* renders its own breasts with physics).
*
* <p>Uses @Pseudo annotation - mixin is optional and will be skipped if MCA is not installed.
*
* <p>Target class: forge.net.mca.client.model.PlayerEntityExtendedModel
*/
@Pseudo
@Mixin(
targets = "forge.net.mca.client.model.PlayerEntityExtendedModel",
remap = false
)
public class MixinMCAPlayerExtendedModel<T extends LivingEntity> {
/**
* Shadow the breasts ModelPart to control visibility.
*/
@Shadow(remap = false)
public ModelPart breasts;
/**
* Shadow the breastsWear ModelPart (overlay layer).
*/
@Shadow(remap = false)
public ModelPart breastsWear;
/**
* Inject at the end of setAngles (m_6973_) to hide MCA breasts when Wildfire is loaded.
*
* <p>This runs after applyVillagerDimensions() which sets breast visibility.
*/
@Inject(method = "m_6973_", at = @At("TAIL"), remap = false)
private void tiedup$hideBreastsInSetAngles(
T entity,
float limbSwing,
float limbSwingAmount,
float ageInTicks,
float netHeadYaw,
float headPitch,
CallbackInfo ci
) {
tiedup$hideBreastsIfWildfire();
}
/**
* Inject at the end of copyVisibility to prevent it from re-enabling breasts.
*
* <p>MCA's copyVisibility sets breasts.visible = model.body.visible.
*/
@Inject(method = "copyVisibility", at = @At("TAIL"), remap = false)
private void tiedup$hideBreastsAfterCopyVisibility(CallbackInfo ci) {
tiedup$hideBreastsIfWildfire();
}
/**
* Inject at the end of render to hide breasts after breastsWear visibility is set.
*
* <p>MCA's render() sets breastsWear.visible = jacket.visible before rendering.
*/
@Inject(method = "m_7695_", at = @At("HEAD"), remap = false)
private void tiedup$hideBreastsBeforeRender(
PoseStack matrices,
VertexConsumer vertices,
int light,
int overlay,
float red,
float green,
float blue,
float alpha,
CallbackInfo ci
) {
tiedup$hideBreastsIfWildfire();
}
/**
* Helper method to hide both breast parts when Wildfire is loaded.
*/
private void tiedup$hideBreastsIfWildfire() {
if (WildfireCompat.isLoaded()) {
if (breasts != null) {
breasts.visible = false;
}
if (breastsWear != null) {
breastsWear.visible = false;
}
}
}
}

View File

@@ -0,0 +1,83 @@
package com.tiedup.remake.mixin.client;
import com.tiedup.remake.compat.mca.MCACompat;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IBondageState;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin for MCA's SpeechManager to block TTS when villager is gagged.
*
* <p>MCA's speech system works via TTS (Text-to-Speech) on the client side:
* <ol>
* <li>Server sends VillagerMessage packet to client</li>
* <li>ClientInteractionManagerImpl.handleVillagerMessage() receives it</li>
* <li>SpeechManager.onChatMessage() is called</li>
* <li>TTS plays the sound</li>
* </ol>
*
* <p>This mixin intercepts onChatMessage and cancels it if the villager is gagged.
*
* <p>Uses @Pseudo for soft dependency - only applies if MCA is present.
* CLIENT SIDE ONLY.
*/
@Pseudo
@Mixin(targets = "forge.net.mca.client.tts.SpeechManager", remap = false)
public class MixinMCASpeechManager {
/**
* Inject at HEAD of onChatMessage to cancel TTS for gagged villagers.
*
* <p>MCA signature: void onChatMessage(Text text, UUID sender)
* <p>Note: Text = net.minecraft.network.chat.Component (Yarn mapping)
*/
@Inject(
method = "onChatMessage",
at = @At("HEAD"),
cancellable = true,
require = 0
)
private void tiedup$cancelGaggedSpeech(
Component text,
UUID sender,
CallbackInfo ci
) {
try {
ClientLevel level = Minecraft.getInstance().level;
if (level == null) return;
// Find entity by UUID in rendered entities
for (Entity entity : level.entitiesForRendering()) {
if (
entity.getUUID().equals(sender) &&
entity instanceof LivingEntity living
) {
IBondageState state = MCACompat.getKidnappedState(living);
if (state != null && state.isGagged()) {
TiedUpMod.LOGGER.debug(
"[MCA] Blocked TTS for gagged villager: {}",
living.getName().getString()
);
ci.cancel();
}
break;
}
}
} catch (Exception e) {
TiedUpMod.LOGGER.debug(
"[MCA] TTS cancellation check failed: {}",
e.getMessage()
);
}
}
}

View File

@@ -0,0 +1,83 @@
package com.tiedup.remake.mixin.client;
import com.tiedup.remake.client.animation.render.DogPoseRenderHandler;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.client.animation.util.DogPoseHelper;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.state.PlayerBindState;
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() || !(bind.getItem() instanceof ItemBind itemBind)) {
return;
}
if (itemBind.getPoseType() != 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.getId()
);
boolean moving = DogPoseRenderHandler.isDogPoseMoving(player.getId());
// 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
);
}
}

View File

@@ -0,0 +1,198 @@
package com.tiedup.remake.mixin.client;
import com.tiedup.remake.client.animation.BondageAnimationManager;
import com.tiedup.remake.client.animation.StaticPoseApplier;
import com.tiedup.remake.client.animation.util.AnimationIdBuilder;
import com.tiedup.remake.compat.mca.MCACompat;
import com.tiedup.remake.items.base.ItemBind;
import com.tiedup.remake.items.base.PoseType;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.state.IBondageState;
import dev.kosmx.playerAnim.impl.IAnimatedPlayer;
import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
import java.util.UUID;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin for MCA's VillagerEntityBaseModelMCA to apply tied poses.
*
* <p>This mixin injects at the end of setupAnim (m_6973_) to override MCA's default
* animations when a villager is tied up. Without this, the bondage render layer
* shows the tied pose but the underlying MCA model still shows normal walking/idle
* animations.
*
* <p>Uses @Pseudo annotation - mixin is optional and will be skipped if MCA is not installed.
*
* <p>Target class: net.mca.client.model.VillagerEntityBaseModelMCA
* <p>Target method: setupAnim (MCP name, remapped from Yarn by Architectury)
*/
@Pseudo
@Mixin(
targets = "forge.net.mca.client.model.VillagerEntityBaseModelMCA",
remap = false
)
public class MixinVillagerEntityBaseModelMCA<T extends LivingEntity> {
// Note: Tick tracking moved to MCAAnimationTickCache for cleanup on world unload
/**
* Inject at the end of setupAnim to apply tied pose after MCA has set its animations.
*
* <p>This completely overrides arm/leg positions when the villager is tied up.
*
* <p>Method signature: void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch)
* <p>Note: MCA uses Architectury which remaps Yarn's method names to Forge/MCP names
*/
@Inject(method = "m_6973_", at = @At("TAIL"), remap = false)
private void tiedup$applyTiedPose(
T villager,
float limbSwing,
float limbSwingAmount,
float ageInTicks,
float netHeadYaw,
float headPitch,
CallbackInfo ci
) {
// Only process on client side
if (villager.level() == null || !villager.level().isClientSide()) {
return;
}
// Check if MCA is loaded and this villager is tied
if (!MCACompat.isMCALoaded()) {
return;
}
IBondageState state = MCACompat.getKidnappedState(villager);
if (state == null || !state.isTiedUp()) {
return;
}
// Get pose info from bind item
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
PoseType poseType = PoseType.STANDARD;
if (bind.getItem() instanceof ItemBind itemBind) {
poseType = itemBind.getPoseType();
}
// Derive bound state from V2 regions, fallback to V1 bind mode NBT
boolean armsBound = V2EquipmentHelper.isRegionOccupied(villager, BodyRegionV2.ARMS);
boolean legsBound = V2EquipmentHelper.isRegionOccupied(villager, BodyRegionV2.LEGS);
if (!armsBound && !legsBound && bind.getItem() instanceof ItemBind) {
armsBound = ItemBind.hasArmsBound(bind);
legsBound = ItemBind.hasLegsBound(bind);
}
// MCA doesn't track struggling state - use false for now
// TODO: Add struggling support to MCA integration
boolean isStruggling = false;
// Cast this mixin to HumanoidModel to apply pose
// MCA's VillagerEntityBaseModelMCA extends HumanoidModel
@SuppressWarnings("unchecked")
HumanoidModel<?> model = (HumanoidModel<?>) (Object) this;
// Check if villager supports PlayerAnimator (via our mixin)
if (villager instanceof IAnimatedPlayer animated) {
// Build animation ID and play animation
String animId = AnimationIdBuilder.build(
poseType,
armsBound,
legsBound,
null,
isStruggling,
true
);
BondageAnimationManager.playAnimation(villager, animId);
// Tick the animation stack only once per game tick (not every render frame)
// ageInTicks increments by 1 each game tick, with fractional values between ticks
int currentTick = (int) ageInTicks;
UUID entityId = villager.getUUID();
int lastTick =
com.tiedup.remake.client.animation.tick.MCAAnimationTickCache.getLastTick(
entityId
);
if (lastTick != currentTick) {
// New game tick - tick the animation
animated.getAnimationStack().tick();
com.tiedup.remake.client.animation.tick.MCAAnimationTickCache.setLastTick(
entityId,
currentTick
);
}
// Apply animation transforms to model parts
AnimationApplier emote = animated.playerAnimator_getAnimation();
if (emote != null && emote.isActive()) {
// Use correct PlayerAnimator part names (torso, not body)
emote.updatePart("head", model.head);
emote.updatePart("torso", model.body);
emote.updatePart("leftArm", model.leftArm);
emote.updatePart("rightArm", model.rightArm);
emote.updatePart("leftLeg", model.leftLeg);
emote.updatePart("rightLeg", model.rightLeg);
// Force rotations using setRotation to ensure they're applied
model.rightArm.setRotation(
model.rightArm.xRot,
model.rightArm.yRot,
model.rightArm.zRot
);
model.leftArm.setRotation(
model.leftArm.xRot,
model.leftArm.yRot,
model.leftArm.zRot
);
model.rightLeg.setRotation(
model.rightLeg.xRot,
model.rightLeg.yRot,
model.rightLeg.zRot
);
model.leftLeg.setRotation(
model.leftLeg.xRot,
model.leftLeg.yRot,
model.leftLeg.zRot
);
model.body.setRotation(
model.body.xRot,
model.body.yRot,
model.body.zRot
);
model.head.setRotation(
model.head.xRot,
model.head.yRot,
model.head.zRot
);
} else {
// Fallback to static poses if animation not active
StaticPoseApplier.applyStaticPose(
model,
poseType,
armsBound,
legsBound
);
}
} else {
// Fallback: entity doesn't support PlayerAnimator, use static poses
StaticPoseApplier.applyStaticPose(model, poseType, armsBound, legsBound);
}
// Hide arms for WRAP/LATEX_SACK poses (like DamselModel does)
if (poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK) {
model.leftArm.visible = false;
model.rightArm.visible = false;
}
}
}

View File

@@ -0,0 +1,121 @@
package com.tiedup.remake.mixin.client;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
import dev.kosmx.playerAnim.api.layered.IAnimation;
import dev.kosmx.playerAnim.impl.IAnimatedPlayer;
import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Mixin to inject IAnimatedPlayer support into MCA villagers.
*
* <p>This allows MCA villagers to use PlayerAnimator animations for bondage poses,
* instead of just static pose rotations.
*
* <p>Uses @Pseudo annotation - mixin is optional and will be skipped if MCA is not installed.
*
* <p>Target class: net.mca.entity.VillagerEntityMCA
*/
@Pseudo
@Mixin(targets = "forge.net.mca.entity.VillagerEntityMCA", remap = false)
public abstract class MixinVillagerEntityMCAAnimated
implements IAnimatedPlayer
{
/**
* Animation stack for layered animations.
*/
@Unique
private AnimationStack tiedup$animationStack;
/**
* Animation applier for applying animations to model parts.
*/
@Unique
private AnimationApplier tiedup$animationApplier;
/**
* Storage for named animations.
*/
@Unique
private final Map<ResourceLocation, IAnimation> tiedup$storedAnimations =
new HashMap<>();
/**
* Track if animation system has been initialized.
*/
@Unique
private boolean tiedup$animInitialized = false;
/**
* Initialize animation system after entity construction.
*/
@Inject(method = "<init>*", at = @At("RETURN"), remap = false)
private void tiedup$initAnimations(CallbackInfo ci) {
tiedup$ensureAnimationInit();
}
/**
* Lazy initialization of animation system.
* Called on first access to ensure system is ready.
* Only initializes on CLIENT side!
*/
@Unique
private void tiedup$ensureAnimationInit() {
if (!tiedup$animInitialized) {
// Only create animation stack on client side
net.minecraft.world.entity.LivingEntity self =
(net.minecraft.world.entity.LivingEntity) (Object) this;
if (self.level() == null || !self.level().isClientSide()) {
return; // Don't initialize on server
}
this.tiedup$animationStack = new AnimationStack();
this.tiedup$animationApplier = new AnimationApplier(
this.tiedup$animationStack
);
tiedup$animInitialized = true;
}
}
// ========================================
// IAnimatedPlayer Implementation
// ========================================
@Override
public AnimationStack getAnimationStack() {
tiedup$ensureAnimationInit();
return this.tiedup$animationStack;
}
@Override
public AnimationApplier playerAnimator_getAnimation() {
tiedup$ensureAnimationInit();
return this.tiedup$animationApplier;
}
@Override
public IAnimation playerAnimator_getAnimation(ResourceLocation id) {
return this.tiedup$storedAnimations.get(id);
}
@Override
public IAnimation playerAnimator_setAnimation(
ResourceLocation id,
IAnimation animation
) {
if (animation == null) {
return this.tiedup$storedAnimations.remove(id);
} else {
return this.tiedup$storedAnimations.put(id, animation);
}
}
}