package com.tiedup.remake.state.components; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.state.hosts.IPlayerBindStateHost; import com.tiedup.remake.state.struggle.StruggleBinds; import com.tiedup.remake.state.struggle.StruggleCollar; import net.minecraft.world.entity.player.Player; /** * Component responsible for struggle mechanics and resistance management. * * Single Responsibility: Struggle state and resistance tracking * Complexity: MEDIUM (volatile fields, animation coordination) * Risk: MEDIUM (thread-safety requirements) * * Threading: Uses host's volatile fields for animation state (isStruggling, struggleStartTick) * * Note: StruggleBinds and StruggleCollar require PlayerBindState parameter. * Since PlayerBindState implements IPlayerBindStateHost, we can safely cast. */ public class PlayerStruggle { private final IPlayerBindStateHost host; private final PlayerBindState state; // Cast reference for struggle system private final StruggleBinds struggleBindState; private final StruggleCollar struggleCollarState; public PlayerStruggle(IPlayerBindStateHost host) { this.host = host; // Safe cast: PlayerBindState implements IPlayerBindStateHost this.state = (PlayerBindState) host; // Initialize sub-states for struggling this.struggleBindState = new StruggleBinds(); this.struggleCollarState = new StruggleCollar(); } // ========== Struggle Methods ========== /** * Entry point for the Struggle logic (Key R). * Distributes effort between Binds and Collar. * * Thread Safety: Must be called from the server thread (packet handlers use enqueueWork). */ public void struggle() { if (struggleBindState != null) struggleBindState.struggle(state); if (struggleCollarState != null) struggleCollarState.struggle(state); } /** * Restores resistance to base values when a master tightens the ties. * * Thread Safety: Must be called from the server thread. */ public void tighten(Player tightener) { if (struggleBindState != null) struggleBindState.tighten( tightener, state ); if (struggleCollarState != null) struggleCollarState.tighten( tightener, state ); } /** * Get the StruggleBinds instance for external access (mini-game system). */ public StruggleBinds getStruggleBinds() { return struggleBindState; } /** * Set a cooldown on struggle attempts (used after mini-game exhaustion). * @param seconds Cooldown duration in seconds */ public void setStruggleCooldown(int seconds) { if ( struggleBindState != null && host.getPlayer() != null && host.getLevel() != null ) { struggleBindState.setExternalCooldown(seconds, host.getLevel()); } } /** * v2.5: Check if struggle cooldown is active. * @return true if cooldown is active (cannot struggle yet) */ public boolean isStruggleCooldownActive() { return ( struggleBindState != null && struggleBindState.isCooldownActive() ); } /** * v2.5: Get remaining struggle cooldown in seconds. * @return Remaining seconds, or 0 if no cooldown */ public int getStruggleCooldownRemaining() { return struggleBindState != null ? struggleBindState.getRemainingCooldownSeconds() : 0; } // ========== Animation Control ========== // Note: Animation state (isStruggling, struggleStartTick) is managed by host // via IPlayerBindStateHost interface (volatile fields for thread-safety) /** * Check if struggle animation should stop (duration expired). * @param currentTick Current game time tick * @return True if animation has been playing for >= 80 ticks */ public boolean shouldStopStruggling(long currentTick) { if (!host.isStruggling()) return false; return ( (currentTick - host.getStruggleStartTick()) >= com.tiedup.remake.util.GameConstants.STRUGGLE_ANIMATION_DURATION_TICKS ); } }