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:
395
src/main/java/com/tiedup/remake/items/ItemLockpick.java
Normal file
395
src/main/java/com/tiedup/remake/items/ItemLockpick.java
Normal file
@@ -0,0 +1,395 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.core.ModConfig;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Lockpick item for picking locks on bondage restraints.
|
||||
*
|
||||
* Phase 21: Revamped Lockpick System
|
||||
*
|
||||
* Behavior:
|
||||
* - 25% chance of success per attempt
|
||||
* - SUCCESS: Instant unlock, padlock PRESERVED (lockable=true)
|
||||
* - FAIL:
|
||||
* - 2.5% chance to JAM the lock (blocks future lockpick attempts)
|
||||
* - 15% chance to break the lockpick
|
||||
* - If shock collar equipped: SHOCK + notify owners
|
||||
* - Cannot be used while wearing mittens
|
||||
* - Durability: 10 uses
|
||||
*/
|
||||
public class ItemLockpick extends Item {
|
||||
|
||||
private static final Random random = new Random();
|
||||
|
||||
public ItemLockpick() {
|
||||
super(new Item.Properties().durability(5)); // 5 tentatives max
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
|
||||
tooltip.add(
|
||||
Component.translatable("item.tiedup.lockpick.tooltip").withStyle(
|
||||
ChatFormatting.GRAY
|
||||
)
|
||||
);
|
||||
|
||||
int remaining = stack.getMaxDamage() - stack.getDamageValue();
|
||||
tooltip.add(
|
||||
Component.literal(
|
||||
"Uses: " + remaining + "/" + stack.getMaxDamage()
|
||||
).withStyle(ChatFormatting.DARK_GRAY)
|
||||
);
|
||||
|
||||
// LOW FIX: Removed server config access from client tooltip (desync issue)
|
||||
// Success/break chances depend on server config, not client config
|
||||
// Displaying client config values here would be misleading in multiplayer
|
||||
tooltip.add(
|
||||
Component.literal("Success/break chances: Check server config")
|
||||
.withStyle(ChatFormatting.GRAY)
|
||||
.withStyle(ChatFormatting.ITALIC)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* v2.5: Right-click with lockpick opens the struggle choice screen.
|
||||
* This allows the player to choose which locked item to pick.
|
||||
*/
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(
|
||||
Level level,
|
||||
Player player,
|
||||
InteractionHand hand
|
||||
) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) {
|
||||
return InteractionResultHolder.pass(stack);
|
||||
}
|
||||
|
||||
// Block mittens
|
||||
if (state.hasMittens()) {
|
||||
if (!level.isClientSide) {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.CANT_USE_ITEM_MITTENS
|
||||
);
|
||||
}
|
||||
return InteractionResultHolder.fail(stack);
|
||||
}
|
||||
|
||||
// Client side: open the unified bondage screen
|
||||
if (level.isClientSide) {
|
||||
openUnifiedBondageScreen();
|
||||
return InteractionResultHolder.success(stack);
|
||||
}
|
||||
|
||||
return InteractionResultHolder.consume(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-only method to open the unified bondage screen.
|
||||
* Separated to avoid classloading issues on server.
|
||||
* Uses fully qualified names to prevent class loading on server.
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void openUnifiedBondageScreen() {
|
||||
net.minecraft.client.Minecraft.getInstance().setScreen(
|
||||
new com.tiedup.remake.client.gui.screens.UnifiedBondageScreen()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this lockpick can be used (has durability remaining).
|
||||
*/
|
||||
public static boolean canUse(ItemStack stack) {
|
||||
if (stack.isEmpty() || !(stack.getItem() instanceof ItemLockpick)) {
|
||||
return false;
|
||||
}
|
||||
return stack.getDamageValue() < stack.getMaxDamage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of a lockpick attempt.
|
||||
*/
|
||||
public enum PickResult {
|
||||
/** Successfully picked the lock - item unlocked, padlock preserved */
|
||||
SUCCESS,
|
||||
/** Failed but lock still pickable */
|
||||
FAIL,
|
||||
/** Failed and jammed the lock - lockpick no longer usable on this item */
|
||||
JAMMED,
|
||||
/** Lockpick broke during attempt */
|
||||
BROKE,
|
||||
/** Cannot attempt - mittens equipped */
|
||||
BLOCKED_MITTENS,
|
||||
/** Cannot attempt - lock is jammed */
|
||||
BLOCKED_JAMMED,
|
||||
/** Cannot attempt - item not locked */
|
||||
NOT_LOCKED,
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to pick a lock on a target item.
|
||||
*
|
||||
* @param player The player attempting to pick
|
||||
* @param state The player's bind state
|
||||
* @param lockpickStack The lockpick being used
|
||||
* @param targetStack The item to pick
|
||||
* @param targetRegion The V2 body region of the target item
|
||||
* @return The result of the pick attempt
|
||||
*/
|
||||
public static PickResult attemptPick(
|
||||
Player player,
|
||||
PlayerBindState state,
|
||||
ItemStack lockpickStack,
|
||||
ItemStack targetStack,
|
||||
BodyRegionV2 targetRegion
|
||||
) {
|
||||
// Check if lockpick is usable
|
||||
if (!canUse(lockpickStack)) {
|
||||
return PickResult.BROKE;
|
||||
}
|
||||
|
||||
// Check if wearing mittens
|
||||
if (state.hasMittens()) {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.CANT_USE_ITEM_MITTENS
|
||||
);
|
||||
return PickResult.BLOCKED_MITTENS;
|
||||
}
|
||||
|
||||
// Check if target is lockable and locked
|
||||
if (!(targetStack.getItem() instanceof ILockable lockable)) {
|
||||
return PickResult.NOT_LOCKED;
|
||||
}
|
||||
|
||||
if (!lockable.isLocked(targetStack)) {
|
||||
return PickResult.NOT_LOCKED;
|
||||
}
|
||||
|
||||
// Check if lock is jammed
|
||||
if (lockable.isJammed(targetStack)) {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
"This lock is jammed! Use struggle instead."
|
||||
);
|
||||
return PickResult.BLOCKED_JAMMED;
|
||||
}
|
||||
|
||||
// Roll for success
|
||||
boolean success =
|
||||
random.nextInt(100) < ModConfig.SERVER.lockpickSuccessChance.get();
|
||||
|
||||
if (success) {
|
||||
// SUCCESS: Unlock the item, PRESERVE the padlock
|
||||
lockable.setLockedByKeyUUID(targetStack, null); // Unlock
|
||||
lockable.clearLockResistance(targetStack); // Clear struggle progress
|
||||
// lockable stays true - padlock preserved!
|
||||
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
"Lock picked!",
|
||||
ChatFormatting.GREEN
|
||||
);
|
||||
|
||||
// Damage lockpick
|
||||
damageLockpick(lockpickStack);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[LOCKPICK] {} successfully picked lock on {} ({})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion
|
||||
);
|
||||
|
||||
return PickResult.SUCCESS;
|
||||
} else {
|
||||
// FAIL: Various bad things can happen
|
||||
|
||||
// 1. Check for shock collar and trigger shock
|
||||
triggerShockIfCollar(player, state);
|
||||
|
||||
// 2. Check for jam
|
||||
boolean jammed =
|
||||
random.nextDouble() * 100 <
|
||||
ModConfig.SERVER.lockpickJamChance.get();
|
||||
if (jammed) {
|
||||
lockable.setJammed(targetStack, true);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
"The lock jammed! Only struggle can open it now."
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[LOCKPICK] {} jammed the lock on {} ({})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion
|
||||
);
|
||||
|
||||
// Damage lockpick
|
||||
boolean broke = damageLockpick(lockpickStack);
|
||||
return broke ? PickResult.BROKE : PickResult.JAMMED;
|
||||
}
|
||||
|
||||
// 3. Check for break
|
||||
boolean broke =
|
||||
random.nextInt(100) <
|
||||
ModConfig.SERVER.lockpickBreakChance.get();
|
||||
if (broke) {
|
||||
lockpickStack.shrink(1);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
"Lockpick broke!"
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[LOCKPICK] {}'s lockpick broke while picking {} ({})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion
|
||||
);
|
||||
|
||||
return PickResult.BROKE;
|
||||
}
|
||||
|
||||
// 4. Normal fail - just damage lockpick
|
||||
damageLockpick(lockpickStack);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.WARNING,
|
||||
"Lockpick slipped..."
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[LOCKPICK] {} failed to pick lock on {} ({})",
|
||||
player.getName().getString(),
|
||||
targetStack.getDisplayName().getString(),
|
||||
targetRegion
|
||||
);
|
||||
|
||||
return PickResult.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Damage the lockpick by 1 use.
|
||||
* @return true if the lockpick broke (ran out of durability)
|
||||
*/
|
||||
private static boolean damageLockpick(ItemStack stack) {
|
||||
stack.setDamageValue(stack.getDamageValue() + 1);
|
||||
if (stack.getDamageValue() >= stack.getMaxDamage()) {
|
||||
stack.shrink(1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger shock collar if player has one equipped.
|
||||
* Also notifies the collar owners.
|
||||
*/
|
||||
private static void triggerShockIfCollar(
|
||||
Player player,
|
||||
PlayerBindState state
|
||||
) {
|
||||
ItemStack collar = V2EquipmentHelper.getInRegion(
|
||||
player,
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (collar.isEmpty()) return;
|
||||
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.ItemShockCollar shockCollar
|
||||
) {
|
||||
// Shock the player
|
||||
state.shockKidnapped(" (Failed lockpick attempt)", 2.0f);
|
||||
|
||||
// Notify owners
|
||||
notifyOwnersLockpickAttempt(player, collar, shockCollar);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[LOCKPICK] {} was shocked for failed lockpick attempt",
|
||||
player.getName().getString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify shock collar owners about the lockpick attempt.
|
||||
*/
|
||||
private static void notifyOwnersLockpickAttempt(
|
||||
Player player,
|
||||
ItemStack collar,
|
||||
com.tiedup.remake.items.ItemShockCollar shockCollar
|
||||
) {
|
||||
if (player.getServer() == null) return;
|
||||
|
||||
Component warning = Component.literal("ALERT: ")
|
||||
.withStyle(ChatFormatting.RED, ChatFormatting.BOLD)
|
||||
.append(
|
||||
Component.literal(
|
||||
player.getName().getString() + " tried to pick a lock!"
|
||||
).withStyle(ChatFormatting.GOLD)
|
||||
);
|
||||
|
||||
List<UUID> owners = shockCollar.getOwners(collar);
|
||||
for (UUID ownerId : owners) {
|
||||
ServerPlayer owner = player
|
||||
.getServer()
|
||||
.getPlayerList()
|
||||
.getPlayer(ownerId);
|
||||
if (owner != null) {
|
||||
owner.sendSystemMessage(warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a lockpick in the player's inventory.
|
||||
* @return The first usable lockpick found, or EMPTY if none
|
||||
*/
|
||||
public static ItemStack findLockpickInInventory(Player player) {
|
||||
for (ItemStack stack : player.getInventory().items) {
|
||||
if (canUse(stack)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user