Files
TiedUp-/src/main/java/com/tiedup/remake/entities/DamselRewardTracker.java
NotEvil 4e136cff96 centralize all ModConfig.SERVER reads through SettingsAccessor
No more direct ModConfig.SERVER access outside SettingsAccessor.
32 new accessor methods, 21 consumer files rerouted.
2026-04-16 13:16:05 +02:00

223 lines
6.6 KiB
Java

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.
*
* <p>Anti-abuse features:
* <ul>
* <li>No reward if already rewarded (prevents infinite reward exploit)</li>
* <li>No reward if savior is the one who tied (prevents self-rescue exploit)</li>
* </ul>
*
* <p>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");
}
}