Files
TiedUp-/src/main/java/com/tiedup/remake/events/camp/CampManagementHandler.java
NotEvil f4aa5ffdc5 split PrisonerService + decompose EntityKidnapper
PrisonerService 1057L -> 474L lifecycle + 616L EscapeMonitorService
EntityKidnapper 2035L -> 1727L via LootManager, Dialogue, CaptivePriority extraction
2026-04-16 14:08:52 +02:00

298 lines
9.0 KiB
Java

package com.tiedup.remake.events.camp;
import com.tiedup.remake.cells.CampMaidManager;
import com.tiedup.remake.cells.CampOwnership;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityMaid;
import com.tiedup.remake.entities.EntitySlaveTrader;
import com.tiedup.remake.entities.ModEntities;
// Prison system v2
import com.tiedup.remake.prison.PrisonerManager;
import com.tiedup.remake.prison.service.EscapeMonitorService;
import java.util.List;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
/**
* Handles camp management tasks including maid respawn.
*
* When a maid dies:
* - Camp remains alive (trader still exists)
* - Prisoners are paused (no new tasks)
* - After 5 minutes (6000 ticks), a new maid spawns
* - Prisoners are notified and labor resumes
*/
@Mod.EventBusSubscriber(
modid = TiedUpMod.MOD_ID,
bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class CampManagementHandler {
// Check every 100 ticks (5 seconds) - optimized for performance
private static final int CHECK_INTERVAL_TICKS = 100;
private static int tickCounter = 0;
/**
* Periodically check for camps needing maid respawn.
*/
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
tickCounter++;
if (tickCounter < CHECK_INTERVAL_TICKS) return;
tickCounter = 0;
// Process all worlds
for (ServerLevel level : event.getServer().getAllLevels()) {
processCampManagement(level);
}
}
/**
* Process camp management for a specific level.
*/
private static void processCampManagement(ServerLevel level) {
// IMPORTANT: Always use server-level registry to avoid dimension fragmentation
CampOwnership ownership = CampOwnership.get(level.getServer());
long currentTime = level.getGameTime();
// Get camps that need maid respawn
List<UUID> campsNeedingMaid =
CampMaidManager.getCampsNeedingMaidRespawn(currentTime, level);
for (UUID campId : campsNeedingMaid) {
spawnNewMaidForCamp(level, ownership, campId);
}
// Prison system v2 - tick escape service (handles escape detection)
EscapeMonitorService.get().tick(level.getServer(), currentTime);
// Prison system v2 - tick protection expiry
PrisonerManager.get(level).tickProtectionExpiry(currentTime);
}
/**
* Spawn a new maid for a camp that needs one.
*
* @param level The server level
* @param ownership The camp ownership data
* @param campId The camp UUID
*/
private static void spawnNewMaidForCamp(
ServerLevel level,
CampOwnership ownership,
UUID campId
) {
CampOwnership.CampData campData = ownership.getCamp(campId);
if (campData == null || !campData.isAlive()) {
return;
}
BlockPos center = campData.getCenter();
if (center == null) {
TiedUpMod.LOGGER.warn(
"[CampManagementHandler] Cannot spawn maid - camp {} has no center position",
campId.toString().substring(0, 8)
);
return;
}
UUID traderUUID = campData.getTraderUUID();
if (traderUUID == null) {
TiedUpMod.LOGGER.warn(
"[CampManagementHandler] Cannot spawn maid - camp {} has no trader",
campId.toString().substring(0, 8)
);
return;
}
// Find the trader entity
Entity traderEntity = level.getEntity(traderUUID);
EntitySlaveTrader trader = null;
if (traderEntity instanceof EntitySlaveTrader t) {
trader = t;
} else {
// Trader not loaded - search near camp center
trader = findTraderNearPosition(level, center, 50);
}
if (trader == null) {
TiedUpMod.LOGGER.warn(
"[CampManagementHandler] Cannot spawn maid - trader not found for camp {}",
campId.toString().substring(0, 8)
);
return;
}
// Create new maid
EntityMaid maid = ModEntities.MAID.get().create(level);
if (maid == null) {
TiedUpMod.LOGGER.error(
"[CampManagementHandler] Failed to create maid entity for camp {}",
campId.toString().substring(0, 8)
);
return;
}
// Find spawn position near trader (slightly offset)
BlockPos spawnPos = findSafeSpawnPosition(
level,
trader.blockPosition()
);
// Position the maid
maid.moveTo(
spawnPos.getX() + 0.5,
spawnPos.getY(),
spawnPos.getZ() + 0.5,
trader.getYRot() + 180, // Face opposite direction from trader
0
);
// Link maid to trader
maid.setMasterTraderUUID(trader.getUUID());
// Add to world
level.addFreshEntity(maid);
// Update camp ownership with new maid
CampMaidManager.assignNewMaid(campId, maid.getUUID(), level);
// Update trader's maid reference
trader.setMaidUUID(maid.getUUID());
TiedUpMod.LOGGER.info(
"[CampManagementHandler] Spawned replacement maid {} for camp {} at {}",
maid.getNpcName(),
campId.toString().substring(0, 8),
spawnPos.toShortString()
);
// Notify prisoners
notifyPrisonersOfNewMaid(level, campId, maid.getNpcName());
}
/**
* Find a trader near the given position.
*/
private static EntitySlaveTrader findTraderNearPosition(
ServerLevel level,
BlockPos pos,
int radius
) {
List<EntitySlaveTrader> traders = level.getEntitiesOfClass(
EntitySlaveTrader.class,
new net.minecraft.world.phys.AABB(pos).inflate(radius)
);
return traders.isEmpty() ? null : traders.get(0);
}
/**
* Find a safe position to spawn the maid.
* Tries positions around the target, prioritizing nearby valid spots.
*/
private static BlockPos findSafeSpawnPosition(
ServerLevel level,
BlockPos targetPos
) {
// Try offsets in a spiral pattern
int[][] offsets = {
{ 1, 0 },
{ 0, 1 },
{ -1, 0 },
{ 0, -1 },
{ 1, 1 },
{ -1, 1 },
{ -1, -1 },
{ 1, -1 },
{ 2, 0 },
{ 0, 2 },
{ -2, 0 },
{ 0, -2 },
};
for (int[] offset : offsets) {
BlockPos testPos = targetPos.offset(offset[0], 0, offset[1]);
// Check if position is safe (solid ground, air above)
if (isValidSpawnPosition(level, testPos)) {
return testPos;
}
// Try one block down
BlockPos downPos = testPos.below();
if (isValidSpawnPosition(level, downPos)) {
return downPos;
}
// Try one block up
BlockPos upPos = testPos.above();
if (isValidSpawnPosition(level, upPos)) {
return upPos;
}
}
// Fallback: use target position
return targetPos;
}
/**
* Check if a position is valid for spawning.
*/
private static boolean isValidSpawnPosition(
ServerLevel level,
BlockPos pos
) {
// Ground must be solid
if (!level.getBlockState(pos.below()).isSolid()) {
return false;
}
// Position and above must be passable (air or similar)
if (!level.getBlockState(pos).isAir()) {
return false;
}
if (!level.getBlockState(pos.above()).isAir()) {
return false;
}
return true;
}
/**
* Notify all prisoners of this camp that a new maid has arrived.
*/
private static void notifyPrisonersOfNewMaid(
ServerLevel level,
UUID campId,
String maidName
) {
PrisonerManager manager = PrisonerManager.get(level);
for (UUID prisonerId : manager.getPrisonersInCamp(campId)) {
ServerPlayer player = level
.getServer()
.getPlayerList()
.getPlayer(prisonerId);
if (player != null) {
player.sendSystemMessage(
Component.translatable(
"msg.tiedup.event.new_maid_arrived",
maidName
).withStyle(ChatFormatting.GOLD)
);
}
}
}
}