Files
TiedUp-/src/main/java/com/tiedup/remake/util/RestraintEffectUtils.java
NotEvil a71093ba9c Remove internal phase comments and format code
Strip all Phase references, TODO/FUTURE roadmap notes, and internal
planning comments from the codebase. Run Prettier for consistent
formatting across all Java files.
2026-04-12 01:25:55 +02:00

442 lines
16 KiB
Java

package com.tiedup.remake.util;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IPlayerLeashAccess;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.FenceBlock;
import net.minecraft.world.level.block.WallBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
/**
*
* Manages attribute modifiers for movement speed reduction and other effects.
*/
public class RestraintEffectUtils {
// UUID for the movement speed modifier (must be consistent)
private static final UUID BIND_SPEED_MODIFIER_UUID = UUID.fromString(
"7f3c7c8e-9d4e-4c7a-8e5f-1a2b3c4d5e6f"
);
private static final String BIND_SPEED_MODIFIER_NAME = "tiedup.bind_speed";
// Speed reduction: -0.09 (90% reduction) when tied up
// Player base speed is 0.10, so this reduces them to 0.01 (10% speed)
private static final double BIND_SPEED_REDUCTION = -0.09;
// Full immobilization: -0.10 (100% reduction) for WRAP/LATEX_SACK
// Player can only move by jumping
private static final double FULL_IMMOBILIZATION_REDUCTION = -0.10;
private static final boolean DEBUG = false;
/**
* Apply movement speed reduction to a tied entity.
*
* @param entity The living entity to apply the effect to
* @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
* which handles speed via tick-based resolution. This method remains for NPC entities
* (Damsel, MCA villagers) that are not managed by MovementStyleManager.
* Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static void applyBindSpeedReduction(LivingEntity entity) {
applyBindSpeedReduction(entity, false);
}
/**
* Apply movement speed reduction to a tied entity.
*
* @param entity The living entity to apply the effect to
* @param fullImmobilization If true, applies 100% speed reduction (for WRAP/LATEX_SACK)
* @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
* which handles speed via tick-based resolution. This method remains for NPC entities
* (Damsel, MCA villagers) that are not managed by MovementStyleManager.
* Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static void applyBindSpeedReduction(
LivingEntity entity,
boolean fullImmobilization
) {
if (entity == null) {
TiedUpMod.LOGGER.warn(
"[RESTRAINT-UTIL] Cannot apply speed reduction - entity is null"
);
return;
}
AttributeInstance movementSpeed = entity.getAttribute(
Attributes.MOVEMENT_SPEED
);
if (movementSpeed == null) {
TiedUpMod.LOGGER.error(
"[RESTRAINT-UTIL] Entity {} has no MOVEMENT_SPEED attribute!",
entity.getName().getString()
);
return;
}
// Remove existing modifier if present (to avoid duplicates)
removeBindSpeedReduction(entity);
// Choose reduction amount based on immobilization type
double reduction = fullImmobilization
? FULL_IMMOBILIZATION_REDUCTION
: BIND_SPEED_REDUCTION;
// Create and apply new modifier
AttributeModifier modifier = new AttributeModifier(
BIND_SPEED_MODIFIER_UUID,
BIND_SPEED_MODIFIER_NAME,
reduction,
AttributeModifier.Operation.ADDITION
);
movementSpeed.addPermanentModifier(modifier);
if (DEBUG) {
TiedUpMod.LOGGER.info(
"[RESTRAINT-UTIL] Applied speed reduction to {} (base: {}, modified: {}, full: {})",
entity.getName().getString(),
movementSpeed.getBaseValue(),
movementSpeed.getValue(),
fullImmobilization
);
}
}
/**
* Remove movement speed reduction from an entity.
*
* @param entity The living entity to remove the effect from
* @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
* which handles speed cleanup via tick-based resolution. This method remains for
* NPC entities (Damsel, MCA villagers) that are not managed by MovementStyleManager.
* Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static void removeBindSpeedReduction(LivingEntity entity) {
if (entity == null) {
TiedUpMod.LOGGER.warn(
"[RESTRAINT-UTIL] Cannot remove speed reduction - entity is null"
);
return;
}
AttributeInstance movementSpeed = entity.getAttribute(
Attributes.MOVEMENT_SPEED
);
if (movementSpeed == null) {
TiedUpMod.LOGGER.error(
"[RESTRAINT-UTIL] Entity {} has no MOVEMENT_SPEED attribute!",
entity.getName().getString()
);
return;
}
// Remove modifier if present
if (movementSpeed.getModifier(BIND_SPEED_MODIFIER_UUID) != null) {
movementSpeed.removeModifier(BIND_SPEED_MODIFIER_UUID);
if (DEBUG) {
TiedUpMod.LOGGER.info(
"[RESTRAINT-UTIL] Removed speed reduction from {} (restored speed: {})",
entity.getName().getString(),
movementSpeed.getValue()
);
}
} else {
if (DEBUG) {
TiedUpMod.LOGGER.debug(
"[RESTRAINT-UTIL] No speed modifier found on {} (already removed or never applied)",
entity.getName().getString()
);
}
}
}
/**
* Check if an entity currently has the bind speed reduction applied.
*
* @param entity The living entity to check
* @return true if the modifier is active
* @deprecated For players, movement style is tracked by {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}.
* Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static boolean hasBindSpeedReduction(LivingEntity entity) {
if (entity == null) return false;
AttributeInstance movementSpeed = entity.getAttribute(
Attributes.MOVEMENT_SPEED
);
if (movementSpeed == null) return false;
return movementSpeed.getModifier(BIND_SPEED_MODIFIER_UUID) != null;
}
/**
* Re-apply speed reduction if needed (called on login/respawn).
*
* @param entity The living entity
* @param shouldBeSlowed Whether the entity should have reduced speed
* @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
* which re-resolves on every tick. Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static void updateBindSpeedReduction(
LivingEntity entity,
boolean shouldBeSlowed
) {
updateBindSpeedReduction(entity, shouldBeSlowed, false);
}
/**
* Re-apply speed reduction if needed (called on login/respawn).
*
* @param entity The living entity
* @param shouldBeSlowed Whether the entity should have reduced speed
* @param fullImmobilization If true, applies 100% speed reduction (for WRAP/LATEX_SACK)
* @deprecated For players, use {@link com.tiedup.remake.v2.bondage.movement.MovementStyleManager}
* which re-resolves on every tick. Scheduled for removal once NPC movement is migrated to V2.
*/
@Deprecated(forRemoval = true)
public static void updateBindSpeedReduction(
LivingEntity entity,
boolean shouldBeSlowed,
boolean fullImmobilization
) {
boolean currentlySlowed = hasBindSpeedReduction(entity);
if (shouldBeSlowed && !currentlySlowed) {
// Need to apply
applyBindSpeedReduction(entity, fullImmobilization);
if (DEBUG) {
TiedUpMod.LOGGER.info(
"[RESTRAINT-UTIL] Re-applied speed reduction to {} on login/respawn (full={})",
entity.getName().getString(),
fullImmobilization
);
}
} else if (shouldBeSlowed && currentlySlowed) {
// Already slowed - but might need to change immobilization level
// Re-apply with correct level
applyBindSpeedReduction(entity, fullImmobilization);
} else if (!shouldBeSlowed && currentlySlowed) {
// Need to remove
removeBindSpeedReduction(entity);
if (DEBUG) {
TiedUpMod.LOGGER.info(
"[RESTRAINT-UTIL] Removed speed reduction from {} on login/respawn",
entity.getName().getString()
);
}
}
}
// POLE BINDING UTILITIES
/**
* Find the closest fence or wall block within a radius.
*
* @param level The level to search in
* @param center The center position to search from
* @param radius The search radius in blocks
* @return The position of the closest fence/wall, or null if none found
*/
@Nullable
public static BlockPos findClosestFence(
Level level,
BlockPos center,
int radius
) {
if (level == null || center == null) return null;
BlockPos closestFence = null;
double closestDistance = Double.MAX_VALUE;
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
BlockPos checkPos = center.offset(x, y, z);
BlockState state = level.getBlockState(checkPos);
if (
state.getBlock() instanceof FenceBlock ||
state.getBlock() instanceof WallBlock
) {
double dist = center.distSqr(checkPos);
if (dist < closestDistance) {
closestDistance = dist;
closestFence = checkPos;
}
}
}
}
}
return closestFence;
}
/**
* Tie an entity to the closest fence or wall block.
* Works for both Players (via LeashProxy) and NPCs (via vanilla leash).
*
* @param entity The entity to tie
* @param searchRadius The search radius for fence blocks
* @return true if successfully tied, false otherwise
*/
public static boolean tieToClosestPole(
LivingEntity entity,
int searchRadius
) {
if (entity == null || entity.level().isClientSide) return false;
if (!(entity.level() instanceof ServerLevel serverLevel)) return false;
BlockPos entityPos = entity.blockPosition();
BlockPos closestFence = findClosestFence(
serverLevel,
entityPos,
searchRadius
);
if (closestFence == null) {
if (DEBUG) {
TiedUpMod.LOGGER.debug(
"[RESTRAINT-UTIL] No fence found within {} blocks of {}",
searchRadius,
entity.getName().getString()
);
}
return false;
}
// Get or create a LeashFenceKnotEntity at the fence position
LeashFenceKnotEntity fenceKnot = LeashFenceKnotEntity.getOrCreateKnot(
serverLevel,
closestFence
);
if (fenceKnot == null) {
TiedUpMod.LOGGER.warn(
"[RESTRAINT-UTIL] Failed to create fence knot at {}",
closestFence
);
return false;
}
// Handle differently based on entity type
if (entity instanceof Player player) {
// Player: use LeashProxy system
if (player instanceof IPlayerLeashAccess access) {
access.tiedup$attachLeash(fenceKnot);
TiedUpMod.LOGGER.debug(
"[RESTRAINT-UTIL] Tied player {} to pole at {}",
player.getName().getString(),
closestFence
);
return true;
} else {
TiedUpMod.LOGGER.error(
"[RESTRAINT-UTIL] Player {} does not implement IPlayerLeashAccess!",
player.getName().getString()
);
return false;
}
} else if (entity instanceof Mob mob) {
// NPC (Mob): use vanilla leash mechanics
mob.setLeashedTo(fenceKnot, true);
TiedUpMod.LOGGER.debug(
"[RESTRAINT-UTIL] Tied mob {} to pole at {}",
mob.getName().getString(),
closestFence
);
return true;
}
return false;
}
// CHLOROFORM UTILITIES
/**
* Apply chloroform effects to an entity.
* Effects: Slowness, Mining Fatigue, Blindness, Jump Boost (all at max amplifier).
*
* @param entity The entity to affect
* @param durationSeconds Duration in seconds
*/
public static void applyChloroformEffects(
LivingEntity entity,
int durationSeconds
) {
if (entity == null || entity.level().isClientSide) return;
int tickDuration = durationSeconds * GameConstants.TICKS_PER_SECOND;
entity.addEffect(
new MobEffectInstance(
MobEffects.MOVEMENT_SLOWDOWN,
tickDuration,
GameConstants.CHLOROFORM_SLOWDOWN_AMPLIFIER,
false,
false
)
);
entity.addEffect(
new MobEffectInstance(
MobEffects.DIG_SLOWDOWN,
tickDuration,
GameConstants.CHLOROFORM_DIG_SLOWDOWN_AMPLIFIER,
false,
false
)
);
entity.addEffect(
new MobEffectInstance(
MobEffects.BLINDNESS,
tickDuration,
GameConstants.CHLOROFORM_BLINDNESS_AMPLIFIER,
false,
false
)
);
entity.addEffect(
new MobEffectInstance(
MobEffects.JUMP,
tickDuration,
GameConstants.CHLOROFORM_JUMP_AMPLIFIER,
false,
false
)
);
// Stop navigation for mobs
if (entity instanceof Mob mob) {
mob.getNavigation().stop();
}
if (DEBUG) {
TiedUpMod.LOGGER.debug(
"[RESTRAINT-UTIL] Applied chloroform to {} for {} seconds",
entity.getName().getString(),
durationSeconds
);
}
}
}