Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
852
src/main/java/com/tiedup/remake/entities/EntityDamsel.java
Normal file
852
src/main/java/com/tiedup/remake/entities/EntityDamsel.java
Normal file
@@ -0,0 +1,852 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
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.items.base.ItemCollar;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
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.
|
||||
*
|
||||
* Phase 3 audit: Now extends AbstractTiedUpNpc instead of PathfinderMob.
|
||||
* 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).
|
||||
* Phase 1 component extraction.
|
||||
*/
|
||||
private final DamselAppearance appearance;
|
||||
|
||||
/**
|
||||
* Manages all personality-related systems.
|
||||
* Phase 6: Extracted from EntityDamsel (~700 lines)
|
||||
*/
|
||||
private DamselPersonalitySystem personalitySystem;
|
||||
|
||||
/**
|
||||
* Manages all AI-related systems.
|
||||
* Phase 5: Extracted from EntityDamsel (~450 lines)
|
||||
*/
|
||||
private DamselAIController aiController;
|
||||
|
||||
/**
|
||||
* Manages all dialogue-related systems.
|
||||
* Phase 4: Extracted from EntityDamsel (~180 lines)
|
||||
*/
|
||||
private DamselDialogueHandler dialogueHandler;
|
||||
|
||||
/**
|
||||
* Orchestrates NBT serialization for all components.
|
||||
* Phase 8: Final refactoring phase.
|
||||
*/
|
||||
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);
|
||||
|
||||
// Phase 1: Initialize appearance component
|
||||
this.appearance = new DamselAppearance(this);
|
||||
|
||||
// Phase 6: Initialize personality component
|
||||
this.personalitySystem = new DamselPersonalitySystem(
|
||||
this,
|
||||
new com.tiedup.remake.entities.damsel.hosts.PersonalityTickContextHost(this)
|
||||
);
|
||||
|
||||
// Phase 5: Initialize AI controller component
|
||||
this.aiController = new DamselAIController(
|
||||
this,
|
||||
new com.tiedup.remake.entities.damsel.hosts.AIHost(this)
|
||||
);
|
||||
|
||||
// Phase 3: Initialize animation controller component
|
||||
// (already done in AbstractTiedUpNpc constructor)
|
||||
|
||||
// Phase 4: Initialize dialogue handler component
|
||||
this.dialogueHandler = new DamselDialogueHandler(
|
||||
new com.tiedup.remake.entities.damsel.hosts.DialogueHost(this)
|
||||
);
|
||||
|
||||
// Phase 8: Initialize data serializer
|
||||
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);
|
||||
// Phase 1: Invalidate variant cache when variant ID changes (from server sync)
|
||||
if (DATA_VARIANT_ID.equals(key)) {
|
||||
this.appearance.invalidateVariantCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddedToWorld() {
|
||||
super.onAddedToWorld();
|
||||
|
||||
// Phase 1: Server-side only: Select deterministic variant based on UUID on spawn
|
||||
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.
|
||||
* Phase 5: Delegated to DamselAIController component.
|
||||
*
|
||||
* 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() {
|
||||
// Phase 5: Call for help when being led as captive
|
||||
aiController.tickCallForHelp();
|
||||
|
||||
// Phase 5: Leash traction system (teleport if stuck)
|
||||
aiController.tickLeashTraction();
|
||||
|
||||
// Phase 6: Personality system tick (needs decay, mood updates)
|
||||
personalitySystem.tickPersonality();
|
||||
|
||||
// Phase 6: Idle dialogue system
|
||||
personalitySystem.tickIdleDialogue();
|
||||
|
||||
// Phase 6: Approach detection (player enters radius)
|
||||
personalitySystem.tickApproachDetection();
|
||||
|
||||
// Phase 6: Environmental dialogue (weather, time)
|
||||
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 (!(collar.getItem() instanceof ItemCollar collarItem)) return false;
|
||||
if (!collarItem.getOwners(collar).contains(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.literal(
|
||||
"This NPC needs a collar before you can feed them."
|
||||
).withStyle(ChatFormatting.RED),
|
||||
true
|
||||
);
|
||||
}
|
||||
return net.minecraft.world.InteractionResult.FAIL;
|
||||
}
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (!collarItem.isOwner(collar, player)) {
|
||||
if (player instanceof net.minecraft.server.level.ServerPlayer sp) {
|
||||
sp.displayClientMessage(
|
||||
Component.literal("You don't own this NPC's collar.")
|
||||
.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.literal("This NPC can't eat that right 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 (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (
|
||||
collarItem.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.PrisonerService.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 (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (collarItem.isOwner(collar, player)) {
|
||||
return "master";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user