286 lines
9.3 KiB
Java
286 lines
9.3 KiB
Java
package com.tiedup.remake.dialogue.conversation;
|
|
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.dialogue.DialogueBridge;
|
|
import com.tiedup.remake.entities.EntityMaster;
|
|
import com.tiedup.remake.entities.ai.master.MasterPlaceBlockGoal;
|
|
import com.tiedup.remake.entities.ai.master.MasterState;
|
|
import com.tiedup.remake.network.ModNetwork;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import com.tiedup.remake.network.master.PacketOpenPetRequestMenu;
|
|
import com.tiedup.remake.state.PlayerBindState;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.world.item.ItemStack;
|
|
|
|
/**
|
|
* Manager for handling pet requests to their Master NPC.
|
|
*
|
|
* This is the server-side handler for player-initiated requests in pet play mode.
|
|
* Utilizes existing systems:
|
|
* - PlayerEquipment.equip(BodyRegionV2.ARMS, bind) / takeBindOff() for tie/untie
|
|
* - MasterState.DOGWALK for walk mode
|
|
* - MasterPlaceBlockGoal for feeding/resting
|
|
* - Leash physics via mixin for pulling the player
|
|
*/
|
|
public class PetRequestManager {
|
|
|
|
/** Maximum distance for pet request interaction */
|
|
private static final double MAX_DISTANCE = 6.0;
|
|
|
|
/**
|
|
* Open the pet request menu for a pet player.
|
|
* Sends a packet to the client to display the GUI.
|
|
*
|
|
* @param master The master entity
|
|
* @param pet The pet player
|
|
*/
|
|
public static void openRequestMenu(EntityMaster master, ServerPlayer pet) {
|
|
if (!master.isPetPlayer(pet)) {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PetRequestManager] {} is not a pet of {}",
|
|
pet.getName().getString(),
|
|
master.getNpcName()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check distance
|
|
double dist = master.distanceTo(pet);
|
|
if (dist > MAX_DISTANCE) {
|
|
pet.sendSystemMessage(
|
|
Component.translatable("entity.tiedup.pet.too_far_to_talk")
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Send packet to open GUI on client
|
|
ModNetwork.sendToPlayer(
|
|
new PacketOpenPetRequestMenu(master.getId(), master.getNpcName()),
|
|
pet
|
|
);
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[PetRequestManager] Opened request menu for {} with {}",
|
|
pet.getName().getString(),
|
|
master.getNpcName()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handle a request from the pet player.
|
|
* Called when the player selects an option from the pet request menu.
|
|
*
|
|
* @param master The master entity
|
|
* @param pet The pet player making the request
|
|
* @param request The request type
|
|
*/
|
|
public static void handleRequest(
|
|
EntityMaster master,
|
|
ServerPlayer pet,
|
|
PetRequest request
|
|
) {
|
|
if (!master.isPetPlayer(pet)) {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PetRequestManager] Rejected request from non-pet {} to {}",
|
|
pet.getName().getString(),
|
|
master.getNpcName()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check distance
|
|
double dist = master.distanceTo(pet);
|
|
if (dist > MAX_DISTANCE) {
|
|
pet.sendSystemMessage(
|
|
Component.translatable("entity.tiedup.pet.too_far_from_master")
|
|
);
|
|
return;
|
|
}
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[PetRequestManager] {} requested {} from {}",
|
|
pet.getName().getString(),
|
|
request.name(),
|
|
master.getNpcName()
|
|
);
|
|
|
|
// Display what the player "says"
|
|
pet.sendSystemMessage(
|
|
Component.translatable("entity.tiedup.pet.you_say", request.getPlayerText())
|
|
);
|
|
|
|
// Handle specific request
|
|
switch (request) {
|
|
case REQUEST_FOOD -> triggerFeeding(master, pet);
|
|
case REQUEST_SLEEP -> triggerResting(master, pet);
|
|
case REQUEST_WALK_PASSIVE -> triggerDogwalk(master, pet, false);
|
|
case REQUEST_WALK_ACTIVE -> triggerDogwalk(master, pet, true);
|
|
case REQUEST_TIE -> triggerTie(master, pet);
|
|
case REQUEST_UNTIE -> triggerUntie(master, pet);
|
|
case END_CONVERSATION -> endConversation(master, pet);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger feeding action - Master places bowl for pet.
|
|
*/
|
|
private static void triggerFeeding(EntityMaster master, ServerPlayer pet) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.feeding");
|
|
|
|
MasterPlaceBlockGoal goal = master.getPlaceBlockGoal();
|
|
if (goal != null) {
|
|
goal.triggerFeeding();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger resting action - Master places pet bed for pet.
|
|
*/
|
|
private static void triggerResting(EntityMaster master, ServerPlayer pet) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.resting");
|
|
|
|
MasterPlaceBlockGoal goal = master.getPlaceBlockGoal();
|
|
if (goal != null) {
|
|
goal.triggerResting();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger dogwalk mode.
|
|
* Puts dogbind on player and attaches leash.
|
|
*
|
|
* @param masterLeads If true, Master walks and pulls pet. If false, Master follows pet.
|
|
*/
|
|
private static void triggerDogwalk(
|
|
EntityMaster master,
|
|
ServerPlayer pet,
|
|
boolean masterLeads
|
|
) {
|
|
// Get player bind state
|
|
PlayerBindState state = PlayerBindState.getInstance(pet);
|
|
if (state == null) {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PetRequestManager] Could not get PlayerBindState for {}",
|
|
pet.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Put dogbind on player (if not already tied)
|
|
if (!state.isTiedUp()) {
|
|
ItemStack dogbind = DataDrivenBondageItem.createStack(ResourceLocation.fromNamespaceAndPath("tiedup", "dogbinder"));
|
|
state.equip(BodyRegionV2.ARMS, dogbind);
|
|
TiedUpMod.LOGGER.debug(
|
|
"[PetRequestManager] Equipped dogbind on {} for walk",
|
|
pet.getName().getString()
|
|
);
|
|
}
|
|
|
|
// Attach leash
|
|
master.attachLeashToPet();
|
|
|
|
// Set dogwalk mode
|
|
master.setDogwalkMode(masterLeads);
|
|
master.setMasterState(MasterState.DOGWALK);
|
|
|
|
String dialogueId = masterLeads
|
|
? "petplay.walk_active"
|
|
: "petplay.walk_passive";
|
|
DialogueBridge.talkTo(master, pet, dialogueId);
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[PetRequestManager] {} entered DOGWALK mode with {} (masterLeads={})",
|
|
master.getNpcName(),
|
|
pet.getName().getString(),
|
|
masterLeads
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Trigger tie request - Master ties up the pet.
|
|
*/
|
|
private static void triggerTie(EntityMaster master, ServerPlayer pet) {
|
|
// Don't allow tie requests during dogwalk
|
|
if (master.getStateManager().getCurrentState() == MasterState.DOGWALK) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.busy");
|
|
return;
|
|
}
|
|
|
|
// Get player bind state
|
|
PlayerBindState state = PlayerBindState.getInstance(pet);
|
|
if (state == null) {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PetRequestManager] Could not get PlayerBindState for {}",
|
|
pet.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check if already tied
|
|
if (state.isTiedUp()) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.already_tied");
|
|
return;
|
|
}
|
|
|
|
// Master equips armbinder on pet (classic pet play restraint)
|
|
ItemStack bind = DataDrivenBondageItem.createStack(ResourceLocation.fromNamespaceAndPath("tiedup", "armbinder"));
|
|
state.equip(BodyRegionV2.ARMS, bind);
|
|
|
|
DialogueBridge.talkTo(master, pet, "petplay.tie_accept");
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[PetRequestManager] {} tied up {} with armbinder",
|
|
master.getNpcName(),
|
|
pet.getName().getString()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Trigger untie request - Master unties the pet.
|
|
*/
|
|
private static void triggerUntie(EntityMaster master, ServerPlayer pet) {
|
|
// Don't allow untie requests during dogwalk
|
|
if (master.getStateManager().getCurrentState() == MasterState.DOGWALK) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.busy");
|
|
return;
|
|
}
|
|
|
|
// Get player bind state
|
|
PlayerBindState state = PlayerBindState.getInstance(pet);
|
|
if (state == null) {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PetRequestManager] Could not get PlayerBindState for {}",
|
|
pet.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Check if actually tied
|
|
if (!state.isTiedUp()) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.not_tied");
|
|
return;
|
|
}
|
|
|
|
// Master removes bind from pet
|
|
state.unequip(BodyRegionV2.ARMS);
|
|
|
|
DialogueBridge.talkTo(master, pet, "petplay.untie_accept");
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[PetRequestManager] {} untied {}",
|
|
master.getNpcName(),
|
|
pet.getName().getString()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* End the conversation gracefully.
|
|
*/
|
|
private static void endConversation(EntityMaster master, ServerPlayer pet) {
|
|
DialogueBridge.talkTo(master, pet, "petplay.dismiss");
|
|
}
|
|
}
|