Phase 1 (state): PlayerBindState, PlayerCaptorManager, PlayerEquipment, PlayerDataRetrieval, PlayerLifecycle, PlayerShockCollar, StruggleAccessory Phase 2 (client): AnimationTickHandler, NpcAnimationTickHandler, 5 render handlers, DamselModel, 3 client mixins, SelfBondageInputHandler, SlaveManagementScreen, ActionPanel, SlaveEntryWidget, ModKeybindings Phase 3 (entities): 28 entity/AI files migrated to CollarHelper, BindModeHelper, PoseTypeHelper, createStack() Phase 4 (network): PacketSlaveAction, PacketMasterEquip, PacketAssignCellToCollar, PacketNpcCommand, PacketFurnitureForcemount Phase 5 (events): RestraintTaskTickHandler, PetPlayRestrictionHandler, PlayerEnslavementHandler, ChatEventHandler, LaborAttackPunishmentHandler Phase 6 (commands): BondageSubCommand, CollarCommand, NPCCommand, KidnapSetCommand Phase 7 (compat): MCAKidnappedAdapter, MCA mixins Phase 8 (misc): GagTalkManager, PetRequestManager, HangingCagePiece, BondageItemBlockEntity, TrappedChestBlockEntity, DispenserBehaviors, BondageItemLoaderUtility, RestraintApplicator, StruggleSessionManager, MovementStyleResolver, CampLifecycleManager Some files retain dual V1/V2 checks (instanceof V1 || V2Helper) for coexistence — V1-only branches removed in Branch D.
148 lines
5.4 KiB
Java
148 lines
5.4 KiB
Java
package com.tiedup.remake.client;
|
|
|
|
import com.mojang.blaze3d.vertex.PoseStack;
|
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
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 com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.model.PlayerModel;
|
|
import net.minecraft.client.model.geom.ModelPart;
|
|
import net.minecraft.client.player.AbstractClientPlayer;
|
|
import net.minecraft.client.renderer.MultiBufferSource;
|
|
import net.minecraft.client.renderer.RenderType;
|
|
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.entity.HumanoidArm;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
import net.minecraftforge.client.event.RenderArmEvent;
|
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
|
|
/**
|
|
* Renders mittens on the player's arms in first-person view.
|
|
*
|
|
* Uses RenderArmEvent which fires specifically when a player's arm
|
|
* is being rendered in first person. This is more targeted than RenderHandEvent.
|
|
*
|
|
* @see <a href="https://nekoyue.github.io/ForgeJavaDocs-NG/javadoc/1.18.2/net/minecraftforge/client/event/RenderArmEvent.html">RenderArmEvent Documentation</a>
|
|
*/
|
|
@OnlyIn(Dist.CLIENT)
|
|
@Mod.EventBusSubscriber(
|
|
modid = TiedUpMod.MOD_ID,
|
|
bus = Mod.EventBusSubscriber.Bus.FORGE,
|
|
value = Dist.CLIENT
|
|
)
|
|
public class FirstPersonMittensRenderer {
|
|
|
|
private static final ResourceLocation MITTENS_TEXTURE =
|
|
ResourceLocation.fromNamespaceAndPath(
|
|
TiedUpMod.MOD_ID,
|
|
"textures/models/bondage/mittens/mittens.png"
|
|
);
|
|
|
|
/**
|
|
* Render mittens overlay on the player's arm in first-person view.
|
|
*
|
|
* This event fires after the arm is set up for rendering but we can add
|
|
* our own rendering on top of it.
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onRenderArm(RenderArmEvent event) {
|
|
AbstractClientPlayer player = event.getPlayer();
|
|
|
|
// Get player's bind state
|
|
PlayerBindState state = PlayerBindState.getInstance(player);
|
|
if (state == null) return;
|
|
|
|
// If tied up, arms are hidden by FirstPersonHandHideHandler - don't render mittens
|
|
if (state.isTiedUp()) return;
|
|
|
|
// Check if player has mittens
|
|
if (!state.hasMittens()) return;
|
|
|
|
// Hide mittens when player is in a wrap or latex sack (hands are covered)
|
|
if (isBindHidingMittens(player)) return;
|
|
|
|
// Render mittens on this arm
|
|
renderMittensOnArm(event);
|
|
}
|
|
|
|
/**
|
|
* Render the mittens overlay on the arm.
|
|
*/
|
|
private static void renderMittensOnArm(RenderArmEvent event) {
|
|
PoseStack poseStack = event.getPoseStack();
|
|
MultiBufferSource buffer = event.getMultiBufferSource();
|
|
int packedLight = event.getPackedLight();
|
|
AbstractClientPlayer player = event.getPlayer();
|
|
HumanoidArm arm = event.getArm();
|
|
|
|
// Get the player's model to access the arm ModelPart
|
|
Minecraft mc = Minecraft.getInstance();
|
|
var renderer = mc.getEntityRenderDispatcher().getRenderer(player);
|
|
if (!(renderer instanceof PlayerRenderer playerRenderer)) return;
|
|
|
|
PlayerModel<AbstractClientPlayer> playerModel =
|
|
playerRenderer.getModel();
|
|
|
|
poseStack.pushPose();
|
|
|
|
// Get the appropriate arm from the player model
|
|
ModelPart armPart = (arm == HumanoidArm.RIGHT)
|
|
? playerModel.rightArm
|
|
: playerModel.leftArm;
|
|
ModelPart sleevePart = (arm == HumanoidArm.RIGHT)
|
|
? playerModel.rightSleeve
|
|
: playerModel.leftSleeve;
|
|
|
|
// The arm is already positioned by the game's first-person renderer
|
|
// We just need to render our mittens texture on top
|
|
|
|
// Use a slightly scaled version to appear on top (avoid z-fighting)
|
|
poseStack.scale(1.001F, 1.001F, 1.001F);
|
|
|
|
// Render the arm with mittens texture
|
|
VertexConsumer vertexConsumer = buffer.getBuffer(
|
|
RenderType.entitySolid(MITTENS_TEXTURE)
|
|
);
|
|
|
|
// Render the arm part with mittens texture
|
|
armPart.render(
|
|
poseStack,
|
|
vertexConsumer,
|
|
packedLight,
|
|
OverlayTexture.NO_OVERLAY
|
|
);
|
|
|
|
// Also render the sleeve part if visible
|
|
if (sleevePart.visible) {
|
|
sleevePart.render(
|
|
poseStack,
|
|
vertexConsumer,
|
|
packedLight,
|
|
OverlayTexture.NO_OVERLAY
|
|
);
|
|
}
|
|
|
|
poseStack.popPose();
|
|
}
|
|
|
|
/**
|
|
* Check if the player's current bind variant hides mittens.
|
|
* WRAP and LATEX_SACK cover the entire body including hands.
|
|
*/
|
|
private static boolean isBindHidingMittens(AbstractClientPlayer player) {
|
|
net.minecraft.world.item.ItemStack bindStack =
|
|
V2EquipmentHelper.getInRegion(player, BodyRegionV2.ARMS);
|
|
if (bindStack.isEmpty()) return false;
|
|
PoseType poseType = PoseTypeHelper.getPoseType(bindStack);
|
|
return poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK;
|
|
}
|
|
}
|