package com.tiedup.remake.entities; import com.tiedup.remake.core.SettingsAccessor; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.dialogue.EntityDialogueManager; import java.util.UUID; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import org.jetbrains.annotations.Nullable; /** * Tracks savior and reward state for EntityDamsel. * Handles the emerald reward system when players rescue damsels. * *

Anti-abuse features: *

* *

This system IS persisted to NBT. */ public class DamselRewardTracker { private final EntityDamsel damsel; /** Player who rescued this damsel (won't flee from them) */ @Nullable private UUID saviorUUID; /** Player who tied this damsel (for anti-abuse checks) */ @Nullable private UUID tiedByUUID; /** Whether a reward has been given (prevents re-exploitation) */ private boolean hasGivenReward = false; public DamselRewardTracker(EntityDamsel damsel) { this.damsel = damsel; } // SAVIOR GETTERS/SETTERS /** * Set the player who rescued this damsel. * @param player The savior (or null to clear) */ public void setSavior(@Nullable Player player) { this.saviorUUID = player != null ? player.getUUID() : null; } /** * Check if the given entity is this damsel's savior. * @param entity The entity to check * @return true if this is the savior */ public boolean isSavior(Entity entity) { if (this.saviorUUID == null || entity == null) { return false; } return this.saviorUUID.equals(entity.getUUID()); } /** * Get the savior's UUID. * @return The savior UUID or null if no savior */ @Nullable public UUID getSaviorUUID() { return this.saviorUUID; } // ANTI-ABUSE TRACKING /** * Set the player who tied this damsel (for reward anti-abuse). * @param player The player who tied this damsel (or null to clear) */ public void setTiedBy(@Nullable Player player) { this.tiedByUUID = player != null ? player.getUUID() : null; } /** * Get the UUID of the player who tied this damsel. * @return The tiedBy UUID or null */ @Nullable public UUID getTiedByUUID() { return this.tiedByUUID; } /** * Check if this damsel has already given a reward. * @return true if reward already given */ public boolean hasGivenReward() { return this.hasGivenReward; } /** * Reset the reward state (for respawn/new capture cycle). */ public void reset() { this.hasGivenReward = false; this.tiedByUUID = null; } // REWARD LOGIC /** * Reward the player who rescued this damsel. * Gives emeralds and marks the player as savior (won't flee from them). * * @param savior The player who freed this damsel */ public void rewardSavior(Player savior) { // Anti-abuse: Don't reward if already rewarded if (this.hasGivenReward) { TiedUpMod.LOGGER.debug( "[DamselRewardTracker] {} already gave reward, skipping", damsel.getName().getString() ); // Still set as savior (won't flee from them) this.setSavior(savior); return; } // Anti-abuse: Don't reward if savior is the one who tied if ( this.tiedByUUID != null && this.tiedByUUID.equals(savior.getUUID()) ) { TiedUpMod.LOGGER.debug( "[DamselRewardTracker] {} was tied by {}, no reward for self-rescue", damsel.getName().getString(), savior.getName().getString() ); // Still set as savior (won't flee) but no reward this.setSavior(savior); this.hasGivenReward = true; // Prevent future reward attempts too return; } // Mark as rewarded BEFORE giving reward (prevents race conditions) this.hasGivenReward = true; // Mark as savior (damsel won't flee from this player anymore) this.setSavior(savior); // Create reward (1-3 emeralds) int emeraldCount = 1 + damsel.getRandom().nextInt(3); ItemStack reward = new ItemStack(Items.EMERALD, emeraldCount); // Drop at damsel's location (player can pick it up) ItemEntity itemEntity = new ItemEntity( damsel.level(), damsel.getX(), damsel.getY() + 0.5, damsel.getZ(), reward ); // Give slight velocity toward savior double dx = savior.getX() - damsel.getX(); double dz = savior.getZ() - damsel.getZ(); double dist = Math.sqrt(dx * dx + dz * dz); if (dist > 0.1) { itemEntity.setDeltaMovement( (dx / dist) * 0.2, 0.2, (dz / dist) * 0.2 ); } damsel.level().addFreshEntity(itemEntity); // Thank the savior via dialogue damsel.talkToPlayersInRadius( EntityDialogueManager.DialogueCategory.DAMSEL_FREED, SettingsAccessor.getDialogueRadius() ); TiedUpMod.LOGGER.info( "[DamselRewardTracker] {} rewarded {} with {} emeralds", damsel.getName().getString(), savior.getName().getString(), emeraldCount ); } // NBT PERSISTENCE /** * Save reward tracker state to NBT. * @param tag The tag to save to */ public void save(CompoundTag tag) { if (saviorUUID != null) { tag.putUUID("SaviorUUID", saviorUUID); } if (tiedByUUID != null) { tag.putUUID("TiedByUUID", tiedByUUID); } tag.putBoolean("HasGivenReward", hasGivenReward); } /** * Load reward tracker state from NBT. * @param tag The tag to load from */ public void load(CompoundTag tag) { if (tag.contains("SaviorUUID")) { this.saviorUUID = tag.getUUID("SaviorUUID"); } if (tag.contains("TiedByUUID")) { this.tiedByUUID = tag.getUUID("TiedByUUID"); } this.hasGivenReward = tag.getBoolean("HasGivenReward"); } }