PrisonerService 1057L -> 474L lifecycle + 616L EscapeMonitorService EntityKidnapper 2035L -> 1727L via LootManager, Dialogue, CaptivePriority extraction
834 lines
24 KiB
Java
834 lines
24 KiB
Java
package com.tiedup.remake.entities;
|
|
|
|
import com.tiedup.remake.core.SettingsAccessor;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.entities.damsel.components.*;
|
|
import com.tiedup.remake.entities.skins.DamselSkinManager;
|
|
import com.tiedup.remake.entities.skins.Gender;
|
|
import com.tiedup.remake.v2.bondage.CollarHelper;
|
|
import com.tiedup.remake.state.ICaptor;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import java.util.UUID;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.*;
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
|
|
/**
|
|
* EntityDamsel - Capturable female NPC with full bondage support.
|
|
*
|
|
* All shared NPC functionality (bondage, equipment, animation, pose, name, gender)
|
|
* has been extracted to AbstractTiedUpNpc.
|
|
*
|
|
* <p><b>Damsel-specific features (remain here):</b></p>
|
|
* <ul>
|
|
* <li>DamselAppearance (variant/skin system)</li>
|
|
* <li>DamselPersonalitySystem (personality, mood, training, commands)</li>
|
|
* <li>DamselAIController (damsel-specific AI goals)</li>
|
|
* <li>DamselDialogueHandler (dialogue cooldowns)</li>
|
|
* <li>DamselRewardTracker (savior/reward system)</li>
|
|
* <li>MenuProvider (inventory GUI)</li>
|
|
* <li>Personality-related IDialogueSpeaker methods</li>
|
|
* </ul>
|
|
*/
|
|
public class EntityDamsel
|
|
extends AbstractTiedUpNpc
|
|
implements net.minecraft.world.MenuProvider
|
|
{
|
|
|
|
// DAMSEL-SPECIFIC DATA SYNC
|
|
|
|
/**
|
|
* Personality type name (e.g., "TIMID", "FIERCE").
|
|
* Synced to client for UI display.
|
|
*/
|
|
public static final EntityDataAccessor<String> DATA_PERSONALITY_TYPE =
|
|
SynchedEntityData.defineId(
|
|
EntityDamsel.class,
|
|
EntityDataSerializers.STRING
|
|
);
|
|
|
|
/**
|
|
* Current active command (e.g., "FOLLOW", "STAY", "NONE").
|
|
* Synced to client for UI display.
|
|
*/
|
|
public static final EntityDataAccessor<String> DATA_ACTIVE_COMMAND =
|
|
SynchedEntityData.defineId(
|
|
EntityDamsel.class,
|
|
EntityDataSerializers.STRING
|
|
);
|
|
|
|
// DAMSEL-SPECIFIC COMPONENTS
|
|
|
|
/**
|
|
* Manages visual appearance (variant, skin, gender, name, slim arms).
|
|
*/
|
|
private final DamselAppearance appearance;
|
|
|
|
/**
|
|
* Manages all personality-related systems.
|
|
*/
|
|
private DamselPersonalitySystem personalitySystem;
|
|
|
|
/**
|
|
* Manages all AI-related systems.
|
|
*/
|
|
private DamselAIController aiController;
|
|
|
|
/**
|
|
* Manages all dialogue-related systems.
|
|
*/
|
|
private DamselDialogueHandler dialogueHandler;
|
|
|
|
/**
|
|
* Orchestrates NBT serialization for all components.
|
|
*/
|
|
private DamselDataSerializer serializer;
|
|
|
|
/** Tracks savior and reward state */
|
|
private final DamselRewardTracker rewardTracker = new DamselRewardTracker(
|
|
this
|
|
);
|
|
|
|
// CONSTRUCTOR
|
|
|
|
@Override
|
|
protected IBondageHost createBondageHost() {
|
|
return new com.tiedup.remake.entities.damsel.hosts.BondageHost(this);
|
|
}
|
|
|
|
public EntityDamsel(EntityType<? extends EntityDamsel> type, Level level) {
|
|
super(type, level);
|
|
this.appearance = new DamselAppearance(this);
|
|
|
|
this.personalitySystem = new DamselPersonalitySystem(
|
|
this,
|
|
new com.tiedup.remake.entities.damsel.hosts.PersonalityTickContextHost(
|
|
this
|
|
)
|
|
);
|
|
|
|
this.aiController = new DamselAIController(
|
|
this,
|
|
new com.tiedup.remake.entities.damsel.hosts.AIHost(this)
|
|
);
|
|
|
|
// (already done in AbstractTiedUpNpc constructor)
|
|
|
|
this.dialogueHandler = new DamselDialogueHandler(
|
|
new com.tiedup.remake.entities.damsel.hosts.DialogueHost(this)
|
|
);
|
|
|
|
this.serializer = new DamselDataSerializer(this);
|
|
|
|
// CRITICAL: Register AI goals now that aiController is initialized
|
|
// (registerGoals() was called by Mob constructor but skipped due to null check)
|
|
this.registerGoals();
|
|
}
|
|
|
|
// ENTITY INITIALIZATION
|
|
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
super.defineSynchedData();
|
|
// Damsel-specific personality data
|
|
this.entityData.define(DATA_PERSONALITY_TYPE, "UNKNOWN");
|
|
this.entityData.define(DATA_ACTIVE_COMMAND, "NONE");
|
|
}
|
|
|
|
@Override
|
|
public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
|
|
super.onSyncedDataUpdated(key);
|
|
if (DATA_VARIANT_ID.equals(key)) {
|
|
this.appearance.invalidateVariantCache();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAddedToWorld() {
|
|
super.onAddedToWorld();
|
|
|
|
if (
|
|
!this.level().isClientSide &&
|
|
this.appearance.getVariantId().isEmpty()
|
|
) {
|
|
Gender preferredGender = SettingsAccessor.getPreferredSpawnGender(
|
|
this.level().getGameRules()
|
|
);
|
|
|
|
DamselVariant variant = DamselSkinManager.CORE.getVariantForEntity(
|
|
this.getUUID(),
|
|
preferredGender
|
|
);
|
|
this.setVariant(variant);
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[EntityDamsel] Spawned with variant: {}",
|
|
variant.id()
|
|
);
|
|
}
|
|
|
|
// Server-side only: Initialize personality if not already set (new spawn)
|
|
if (
|
|
!this.level().isClientSide &&
|
|
this.personalitySystem.getPersonalityState() == null
|
|
) {
|
|
this.personalitySystem.initializePersonality();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register all AI goals for this entity.
|
|
*
|
|
* CRITICAL: This method is called by Mob constructor BEFORE our constructor finishes.
|
|
* We must null-check aiController and defer goal registration until after initialization.
|
|
*/
|
|
@Override
|
|
protected void registerGoals() {
|
|
if (aiController == null) {
|
|
// Called during super() construction, before aiController is initialized
|
|
// Goals will be registered manually after component initialization
|
|
return;
|
|
}
|
|
aiController.registerGoals(this.goalSelector, this.targetSelector);
|
|
}
|
|
|
|
/**
|
|
* Not used directly -- registerGoals() delegates to aiController.
|
|
* Required by AbstractTiedUpNpc contract.
|
|
*/
|
|
@Override
|
|
protected void registerNpcGoals() {
|
|
// Handled by registerGoals() override above
|
|
}
|
|
|
|
/**
|
|
* Create attribute modifiers for EntityDamsel.
|
|
* Called during entity type registration.
|
|
*/
|
|
public static AttributeSupplier.Builder createAttributes() {
|
|
return Mob.createMobAttributes()
|
|
.add(Attributes.MAX_HEALTH, 20.0)
|
|
.add(Attributes.MOVEMENT_SPEED, 0.25)
|
|
.add(Attributes.KNOCKBACK_RESISTANCE, 0.7)
|
|
.add(Attributes.FOLLOW_RANGE, 60.0);
|
|
}
|
|
|
|
// TICK (damsel-specific extensions)
|
|
|
|
/**
|
|
* Damsel-specific tick logic.
|
|
* Called by AbstractTiedUpNpc.tick() after animation and bondage restoration.
|
|
*/
|
|
@Override
|
|
protected void tickSubclass() {
|
|
aiController.tickCallForHelp();
|
|
|
|
aiController.tickLeashTraction();
|
|
|
|
personalitySystem.tickPersonality();
|
|
|
|
personalitySystem.tickIdleDialogue();
|
|
|
|
personalitySystem.tickApproachDetection();
|
|
|
|
personalitySystem.tickEnvironmentDialogue();
|
|
}
|
|
|
|
// VARIANT SYSTEM (Delegated to DamselAppearance)
|
|
|
|
@Nullable
|
|
public DamselVariant getVariant() {
|
|
return this.appearance.getVariant();
|
|
}
|
|
|
|
public void setVariant(DamselVariant variant) {
|
|
this.appearance.setVariant(variant);
|
|
}
|
|
|
|
public String getVariantId() {
|
|
return this.appearance.getVariantId();
|
|
}
|
|
|
|
// SKIN TEXTURE (ISkinnedEntity implementation)
|
|
|
|
@Override
|
|
public ResourceLocation getSkinTexture() {
|
|
return this.appearance.getSkinTexture();
|
|
}
|
|
|
|
// NAME SYSTEM OVERRIDE
|
|
// (DamselAppearance also sets custom name visible, delegate to it)
|
|
|
|
@Override
|
|
public String getNpcName() {
|
|
return this.appearance.getNpcName();
|
|
}
|
|
|
|
@Override
|
|
public void setNpcName(String name) {
|
|
this.appearance.setNpcName(name);
|
|
}
|
|
|
|
// GENDER/SLIMARMS OVERRIDE
|
|
// (DamselAppearance manages these for damsels)
|
|
|
|
@Override
|
|
public boolean hasSlimArms() {
|
|
return this.appearance.hasSlimArms();
|
|
}
|
|
|
|
@Override
|
|
public void setGender(Gender gender) {
|
|
this.appearance.setGender(gender);
|
|
}
|
|
|
|
@Override
|
|
public Gender getGender() {
|
|
return this.appearance.getGender();
|
|
}
|
|
|
|
// COMPONENT ACCESSORS
|
|
|
|
public DamselAppearance getAppearance() {
|
|
return appearance;
|
|
}
|
|
|
|
public DamselPersonalitySystem getPersonalitySystem() {
|
|
return personalitySystem;
|
|
}
|
|
|
|
public DamselRewardTracker getRewardTracker() {
|
|
return rewardTracker;
|
|
}
|
|
|
|
// NBT PERSISTENCE (damsel-specific)
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag tag) {
|
|
super.addAdditionalSaveData(tag);
|
|
serializer.save(tag);
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag tag) {
|
|
super.readAdditionalSaveData(tag);
|
|
serializer.load(tag);
|
|
}
|
|
|
|
// BONDAGE SERVICE OVERRIDES
|
|
|
|
/**
|
|
* Override hurt to intercept player attacks for bondage service.
|
|
*/
|
|
@Override
|
|
public boolean hurt(DamageSource source, float amount) {
|
|
if (
|
|
!this.level().isClientSide &&
|
|
getBondageManager().handleDamageWithService(source, amount)
|
|
) {
|
|
return false;
|
|
}
|
|
return super.hurt(source, amount);
|
|
}
|
|
|
|
/**
|
|
* Override display name to show violet color when bondage service is active.
|
|
*/
|
|
@Override
|
|
public Component getDisplayName() {
|
|
if (getBondageManager().isBondageServiceEnabled()) {
|
|
return Component.literal(this.getNpcName()).withStyle(
|
|
ChatFormatting.LIGHT_PURPLE
|
|
);
|
|
}
|
|
return super.getDisplayName();
|
|
}
|
|
|
|
// DIALOGUE SYSTEM
|
|
|
|
public void talkTo(Player player, String message) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.talkTo(
|
|
this,
|
|
player,
|
|
message
|
|
);
|
|
}
|
|
|
|
public void talkTo(
|
|
Player player,
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category
|
|
) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.talkTo(
|
|
this,
|
|
player,
|
|
category
|
|
);
|
|
}
|
|
|
|
public void actionTo(Player player, String action) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.actionTo(
|
|
this,
|
|
player,
|
|
action
|
|
);
|
|
}
|
|
|
|
public void actionTo(
|
|
Player player,
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category
|
|
) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.actionTo(
|
|
this,
|
|
player,
|
|
category
|
|
);
|
|
}
|
|
|
|
public void talkToPlayersInRadius(String message, int radius) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.talkToNearby(
|
|
this,
|
|
message,
|
|
radius
|
|
);
|
|
}
|
|
|
|
public void talkToPlayersInRadius(
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
|
|
int radius
|
|
) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.talkToNearby(
|
|
this,
|
|
category,
|
|
radius
|
|
);
|
|
}
|
|
|
|
public boolean talkToPlayersInRadiusWithCooldown(
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
|
|
int radius
|
|
) {
|
|
return dialogueHandler.talkToPlayersInRadiusWithCooldown(
|
|
category,
|
|
radius
|
|
);
|
|
}
|
|
|
|
public void actionToPlayersInRadius(String action, int radius) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.actionToNearby(
|
|
this,
|
|
action,
|
|
radius
|
|
);
|
|
}
|
|
|
|
public void actionToPlayersInRadius(
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory category,
|
|
int radius
|
|
) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.actionToNearby(
|
|
this,
|
|
category,
|
|
radius
|
|
);
|
|
}
|
|
|
|
// SAVIOR & REWARD SYSTEM
|
|
|
|
public void setSavior(@Nullable Player player) {
|
|
rewardTracker.setSavior(player);
|
|
}
|
|
|
|
public boolean isSavior(Entity entity) {
|
|
return rewardTracker.isSavior(entity);
|
|
}
|
|
|
|
@Nullable
|
|
public UUID getSaviorUUID() {
|
|
return rewardTracker.getSaviorUUID();
|
|
}
|
|
|
|
public void setTiedBy(@Nullable Player player) {
|
|
rewardTracker.setTiedBy(player);
|
|
}
|
|
|
|
@Nullable
|
|
public UUID getTiedByUUID() {
|
|
return rewardTracker.getTiedByUUID();
|
|
}
|
|
|
|
public boolean hasGivenReward() {
|
|
return rewardTracker.hasGivenReward();
|
|
}
|
|
|
|
public void resetRewardState() {
|
|
rewardTracker.reset();
|
|
}
|
|
|
|
public void rewardSavior(Player savior) {
|
|
rewardTracker.rewardSavior(savior);
|
|
}
|
|
|
|
// PERSONALITY SYSTEM ACCESS
|
|
|
|
@Nullable
|
|
public com.tiedup.remake.personality.PersonalityState getPersonalityState() {
|
|
if (
|
|
this.personalitySystem.getPersonalityState() == null &&
|
|
!this.level().isClientSide
|
|
) {
|
|
personalitySystem.initializePersonality();
|
|
}
|
|
return this.personalitySystem.getPersonalityState();
|
|
}
|
|
|
|
public com.tiedup.remake.personality.PersonalityType getPersonalityType() {
|
|
return personalitySystem.getPersonalityType();
|
|
}
|
|
|
|
public void setPersonalityType(
|
|
com.tiedup.remake.personality.PersonalityType newType
|
|
) {
|
|
personalitySystem.setPersonalityType(newType);
|
|
}
|
|
|
|
public com.tiedup.remake.personality.NpcCommand getActiveCommand() {
|
|
return personalitySystem.getActiveCommand();
|
|
}
|
|
|
|
// ANTI-FLEE SYSTEM
|
|
|
|
public long getLastWhipTime() {
|
|
return personalitySystem.getLastWhipTime();
|
|
}
|
|
|
|
public void setLastWhipTime(long time) {
|
|
personalitySystem.setLastWhipTime(time);
|
|
}
|
|
|
|
// COMMAND SYSTEM
|
|
|
|
public boolean giveCommand(
|
|
Player commander,
|
|
com.tiedup.remake.personality.NpcCommand command,
|
|
@Nullable net.minecraft.core.BlockPos targetPos
|
|
) {
|
|
if (!this.hasCollar()) return false;
|
|
|
|
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
|
if (!CollarHelper.isCollar(collar)) return false;
|
|
if (!CollarHelper.isOwner(collar, commander.getUUID())) {
|
|
if (!this.isGagged()) {
|
|
com.tiedup.remake.dialogue.EntityDialogueManager.talkByDialogueId(
|
|
this,
|
|
commander,
|
|
"command.reject.not_master"
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (this.personalitySystem.getPersonalityState() != null) {
|
|
if (
|
|
!this.personalitySystem.getPersonalityState().willObeyCommand(
|
|
commander,
|
|
command
|
|
)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
this.personalitySystem.getPersonalityState().setActiveCommand(
|
|
command,
|
|
commander.getUUID(),
|
|
targetPos
|
|
);
|
|
personalitySystem.syncPersonalityData();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean giveCommandWithTwoTargets(
|
|
Player commander,
|
|
com.tiedup.remake.personality.NpcCommand command,
|
|
@Nullable net.minecraft.core.BlockPos targetPos,
|
|
@Nullable net.minecraft.core.BlockPos targetPos2
|
|
) {
|
|
boolean success = giveCommand(commander, command, targetPos);
|
|
if (
|
|
success &&
|
|
targetPos2 != null &&
|
|
this.personalitySystem.getPersonalityState() != null
|
|
) {
|
|
this.personalitySystem.getPersonalityState().setCommandTarget2(
|
|
targetPos2
|
|
);
|
|
personalitySystem.syncPersonalityData();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
public void cancelCommand() {
|
|
if (this.personalitySystem.getPersonalityState() != null) {
|
|
this.personalitySystem.getPersonalityState().clearCommand();
|
|
personalitySystem.syncPersonalityData();
|
|
}
|
|
}
|
|
|
|
// NPC INVENTORY ACCESS
|
|
|
|
public net.minecraft.core.NonNullList<ItemStack> getNpcInventory() {
|
|
return this.getInventoryManager().getNpcInventory();
|
|
}
|
|
|
|
public int getNpcInventorySize() {
|
|
return this.getInventoryManager().getNpcInventorySize();
|
|
}
|
|
|
|
public void setNpcInventorySize(int newSize) {
|
|
this.getInventoryManager().setNpcInventorySize(newSize);
|
|
}
|
|
|
|
public boolean hasEdibleInInventory() {
|
|
return this.getInventoryManager().hasEdibleInInventory();
|
|
}
|
|
|
|
public boolean tryEatFromInventory() {
|
|
return this.getInventoryManager().tryEatFromInventory(
|
|
this.personalitySystem.getPersonalityState()
|
|
);
|
|
}
|
|
|
|
public boolean tryEatFromChest(net.minecraft.core.BlockPos chestPos) {
|
|
return this.getInventoryManager().tryEatFromChest(
|
|
chestPos,
|
|
this.personalitySystem.getPersonalityState()
|
|
);
|
|
}
|
|
|
|
// DIRECT FEEDING SYSTEM
|
|
|
|
public boolean feedByPlayer(Player player, ItemStack foodStack) {
|
|
return this.getInventoryManager().feedByPlayer(
|
|
player,
|
|
foodStack,
|
|
this.personalitySystem.getPersonalityState()
|
|
);
|
|
}
|
|
|
|
// INTERACTION
|
|
|
|
@Override
|
|
protected net.minecraft.world.InteractionResult mobInteract(
|
|
Player player,
|
|
net.minecraft.world.InteractionHand hand
|
|
) {
|
|
ItemStack heldItem = player.getItemInHand(hand);
|
|
|
|
// Check if holding edible item
|
|
if (heldItem.getItem().isEdible()) {
|
|
if (!this.hasCollar()) {
|
|
if (
|
|
player instanceof net.minecraft.server.level.ServerPlayer sp
|
|
) {
|
|
sp.displayClientMessage(
|
|
Component.translatable(
|
|
"entity.tiedup.damsel.needs_collar_to_feed"
|
|
).withStyle(ChatFormatting.RED),
|
|
true
|
|
);
|
|
}
|
|
return net.minecraft.world.InteractionResult.FAIL;
|
|
}
|
|
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
|
if (CollarHelper.isCollar(collar)) {
|
|
if (!CollarHelper.isOwner(collar, player)) {
|
|
if (
|
|
player instanceof
|
|
net.minecraft.server.level.ServerPlayer sp
|
|
) {
|
|
sp.displayClientMessage(
|
|
Component.translatable(
|
|
"entity.tiedup.damsel.not_collar_owner"
|
|
).withStyle(ChatFormatting.RED),
|
|
true
|
|
);
|
|
}
|
|
return net.minecraft.world.InteractionResult.FAIL;
|
|
}
|
|
if (this.feedByPlayer(player, heldItem)) {
|
|
return net.minecraft.world.InteractionResult.SUCCESS;
|
|
}
|
|
if (
|
|
player instanceof net.minecraft.server.level.ServerPlayer sp
|
|
) {
|
|
sp.displayClientMessage(
|
|
Component.translatable(
|
|
"entity.tiedup.damsel.cant_eat_now"
|
|
).withStyle(ChatFormatting.RED),
|
|
true
|
|
);
|
|
}
|
|
return net.minecraft.world.InteractionResult.FAIL;
|
|
}
|
|
}
|
|
|
|
// Shift + empty hand on collared NPC = open conversation
|
|
if (
|
|
!this.level().isClientSide() &&
|
|
player.isShiftKeyDown() &&
|
|
heldItem.isEmpty() &&
|
|
this.hasCollar()
|
|
) {
|
|
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
|
if (CollarHelper.isCollar(collar)) {
|
|
if (
|
|
CollarHelper.isOwner(collar, player) &&
|
|
player instanceof
|
|
net.minecraft.server.level.ServerPlayer serverPlayer
|
|
) {
|
|
if (
|
|
com.tiedup.remake.dialogue.conversation.ConversationManager.openConversation(
|
|
this,
|
|
serverPlayer
|
|
)
|
|
) {
|
|
return net.minecraft.world.InteractionResult.SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Leash detach: if player right-clicks their leashed damsel, detach leash
|
|
if (
|
|
!this.level().isClientSide() &&
|
|
this.isLeashed() &&
|
|
this.getLeashHolder() == player
|
|
) {
|
|
this.dropLeash(true, !player.getAbilities().instabuild);
|
|
|
|
ICaptor currentCaptor = getBondageManager().getCaptor();
|
|
if (currentCaptor != null) {
|
|
currentCaptor.removeCaptive(this, false);
|
|
getBondageManager().clearCaptor();
|
|
}
|
|
|
|
return net.minecraft.world.InteractionResult.SUCCESS;
|
|
}
|
|
|
|
return super.mobInteract(player, hand);
|
|
}
|
|
|
|
// MENU PROVIDER
|
|
|
|
@Override
|
|
@javax.annotation.Nullable
|
|
public net.minecraft.world.inventory.AbstractContainerMenu createMenu(
|
|
int containerId,
|
|
net.minecraft.world.entity.player.Inventory playerInventory,
|
|
Player player
|
|
) {
|
|
return this.getInventoryManager().createMenu(
|
|
containerId,
|
|
playerInventory,
|
|
player
|
|
);
|
|
}
|
|
|
|
// LIFECYCLE - DEATH
|
|
|
|
@Override
|
|
public void die(DamageSource damageSource) {
|
|
if (
|
|
!this.level().isClientSide &&
|
|
this.level() instanceof
|
|
net.minecraft.server.level.ServerLevel serverLevel
|
|
) {
|
|
UUID uuid = this.getUUID();
|
|
|
|
com.tiedup.remake.cells.CellRegistryV2 cellRegistry =
|
|
com.tiedup.remake.cells.CellRegistryV2.get(serverLevel);
|
|
cellRegistry.releasePrisonerFromAllCells(uuid);
|
|
|
|
com.tiedup.remake.prison.PrisonerManager manager =
|
|
com.tiedup.remake.prison.PrisonerManager.get(serverLevel);
|
|
com.tiedup.remake.prison.PrisonerState state = manager.getState(
|
|
uuid
|
|
);
|
|
|
|
if (
|
|
state == com.tiedup.remake.prison.PrisonerState.IMPRISONED ||
|
|
state == com.tiedup.remake.prison.PrisonerState.WORKING
|
|
) {
|
|
com.tiedup.remake.prison.service.EscapeMonitorService.get().escape(
|
|
serverLevel,
|
|
uuid,
|
|
"player_death"
|
|
);
|
|
}
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[EntityDamsel] {} died, cleaned up registries",
|
|
getNpcName()
|
|
);
|
|
}
|
|
super.die(damageSource);
|
|
}
|
|
|
|
// IDIALOGUESPEAKER - Personality-specific methods
|
|
|
|
@Override
|
|
public com.tiedup.remake.dialogue.SpeakerType getSpeakerType() {
|
|
return com.tiedup.remake.dialogue.SpeakerType.DAMSEL;
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public com.tiedup.remake.personality.PersonalityType getSpeakerPersonality() {
|
|
if (
|
|
personalitySystem != null &&
|
|
personalitySystem.getPersonalityState() != null
|
|
) {
|
|
return personalitySystem.getPersonalityState().getPersonality();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int getSpeakerMood() {
|
|
if (
|
|
personalitySystem != null &&
|
|
personalitySystem.getPersonalityState() != null
|
|
) {
|
|
return (int) personalitySystem.getPersonalityState().getMood();
|
|
}
|
|
return 50;
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public String getTargetRelation(Player player) {
|
|
if (hasCollar()) {
|
|
ItemStack collar = getEquipment(BodyRegionV2.NECK);
|
|
if (CollarHelper.isCollar(collar)) {
|
|
if (CollarHelper.isOwner(collar, player)) {
|
|
return "master";
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|