Files
TiedUp-/src/main/java/com/tiedup/remake/worldgen/KidnapperCampPiece.java
NotEvil f6466360b6 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.
2026-04-12 00:51:22 +02:00

400 lines
13 KiB
Java

package com.tiedup.remake.worldgen;
import com.tiedup.remake.cells.CampOwnership;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.EntityKidnapper;
import com.tiedup.remake.entities.EntityKidnapperElite;
import com.tiedup.remake.entities.EntityMaid;
import com.tiedup.remake.entities.EntitySlaveTrader;
import com.tiedup.remake.entities.ModEntities;
import java.util.UUID;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.TemplateStructurePiece;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceType;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
/**
* Structure piece for kidnapper camp tents.
*
* Phase: Kidnapper Revamp - Cell System
*
* Handles data markers:
* - "kidnapper" - Spawns a regular kidnapper
* - "kidnapper_elite" - Spawns an elite kidnapper
* - "slave_trader" - Spawns a slave trader (brain of the camp)
* - "maid" - Spawns a maid (linked to trader)
* - "loot" - Places a loot chest
*/
public class KidnapperCampPiece extends TemplateStructurePiece {
/** Temporary storage for trader UUID during structure generation */
@Nullable
private UUID spawnedTraderUUID;
/** Temporary storage for camp data during structure generation */
@Nullable
private CampOwnership.CampData currentCamp;
public KidnapperCampPiece(
StructureTemplateManager templateManager,
ResourceLocation templateLocation,
BlockPos pos,
Rotation rotation
) {
super(
ModStructures.CAMP_PIECE.get(),
0,
templateManager,
templateLocation,
templateLocation.toString(),
makeSettings(rotation),
pos
);
}
public KidnapperCampPiece(
StructureTemplateManager templateManager,
CompoundTag tag
) {
super(ModStructures.CAMP_PIECE.get(), tag, templateManager, location ->
makeSettings(Rotation.NONE)
);
}
private static StructurePlaceSettings makeSettings(Rotation rotation) {
return new StructurePlaceSettings()
.setRotation(rotation)
.setMirror(Mirror.NONE)
.setRotationPivot(BlockPos.ZERO)
.addProcessor(BlockIgnoreProcessor.STRUCTURE_BLOCK)
.addProcessor(new MarkerProcessor());
}
@Override
protected void handleDataMarker(
String marker,
BlockPos pos,
ServerLevelAccessor level,
RandomSource random,
BoundingBox box
) {
// Handle data markers from structure blocks
// Data markers are placed using jigsaw blocks or structure blocks with "Data" mode
if (!(level instanceof ServerLevel serverLevel)) {
return;
}
switch (marker.toLowerCase()) {
case "kidnapper" -> {
// Spawn a kidnapper at this position
spawnKidnapper(serverLevel, pos, random, false);
}
case "kidnapper_elite" -> {
// Spawn elite kidnapper at this position
spawnKidnapper(serverLevel, pos, random, true);
}
case "slave_trader" -> {
// Spawn slave trader (brain of camp)
spawnSlaveTrader(serverLevel, pos, random);
}
case "maid" -> {
// Spawn maid (linked to trader)
spawnMaid(serverLevel, pos, random);
}
case "loot" -> {
// TODO: Place loot chest
}
default -> {
TiedUpMod.LOGGER.debug(
"[KidnapperCampPiece] Unknown data marker: {} at {}",
marker,
pos.toShortString()
);
}
}
}
/**
* Spawn a kidnapper at the given position.
*
* @param level The server level
* @param pos Position to spawn at
* @param random Random source
* @param elite Whether to spawn an elite kidnapper
*/
private void spawnKidnapper(
ServerLevel level,
BlockPos pos,
RandomSource random,
boolean elite
) {
EntityKidnapper kidnapper;
if (elite) {
kidnapper = ModEntities.KIDNAPPER_ELITE.get().create(level);
} else {
kidnapper = ModEntities.KIDNAPPER.get().create(level);
}
if (kidnapper != null) {
kidnapper.moveTo(
pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
random.nextFloat() * 360F,
0.0F
);
kidnapper.finalizeSpawn(
level,
level.getCurrentDifficultyAt(pos),
MobSpawnType.STRUCTURE,
null,
null
);
level.addFreshEntity(kidnapper);
TiedUpMod.LOGGER.info(
"[KidnapperCampPiece] Spawned {} at {} from data marker",
elite ? "elite kidnapper" : "kidnapper",
pos.toShortString()
);
}
}
/**
* Spawn a slave trader at the given position.
* Creates/updates the camp data in CampOwnership.
*
* @param level The server level
* @param pos Position to spawn at
* @param random Random source
*/
private void spawnSlaveTrader(
ServerLevel level,
BlockPos pos,
RandomSource random
) {
EntitySlaveTrader trader = ModEntities.SLAVE_TRADER.get().create(level);
if (trader == null) {
TiedUpMod.LOGGER.warn(
"[KidnapperCampPiece] Failed to create SlaveTrader entity"
);
return;
}
trader.moveTo(
pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
random.nextFloat() * 360F,
0.0F
);
trader.finalizeSpawn(
level,
level.getCurrentDifficultyAt(pos),
MobSpawnType.STRUCTURE,
null,
null
);
// Create or get the camp in CampOwnership registry
CampOwnership registry = CampOwnership.get(level);
CampOwnership.CampData camp = registry.findNearestAliveCamp(pos, 50);
if (camp == null) {
// Create a new camp
UUID campId = UUID.randomUUID();
registry.registerCamp(campId, trader.getUUID(), null, pos);
camp = registry.getCamp(campId);
} else {
// Update existing camp with this trader
camp.setTraderUUID(trader.getUUID());
camp.setCenter(pos);
registry.setDirty();
}
// Link trader to camp
if (camp != null) {
trader.setCampUUID(camp.getCampId());
this.currentCamp = camp;
}
// Store for maid linking
this.spawnedTraderUUID = trader.getUUID();
level.addFreshEntity(trader);
TiedUpMod.LOGGER.info(
"[KidnapperCampPiece] Spawned slave trader {} at {} from data marker, camp={}",
trader.getNpcName(),
pos.toShortString(),
camp != null ? camp.getCampId().toString().substring(0, 8) : "null"
);
}
/**
* Spawn a maid at the given position.
* Must be called AFTER trader spawns to establish link.
*
* @param level The server level
* @param pos Position to spawn at
* @param random Random source
*/
private void spawnMaid(
ServerLevel level,
BlockPos pos,
RandomSource random
) {
EntityMaid maid = ModEntities.MAID.get().create(level);
if (maid == null) {
TiedUpMod.LOGGER.warn(
"[KidnapperCampPiece] Failed to create Maid entity"
);
return;
}
maid.moveTo(
pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
random.nextFloat() * 360F,
0.0F
);
maid.finalizeSpawn(
level,
level.getCurrentDifficultyAt(pos),
MobSpawnType.STRUCTURE,
null,
null
);
// Link to trader (must be spawned after trader)
boolean linkedSuccessfully = false;
if (this.spawnedTraderUUID != null) {
maid.setMasterTraderUUID(this.spawnedTraderUUID);
// Find the trader entity and update its maid reference
Entity traderEntity = level.getEntity(this.spawnedTraderUUID);
if (traderEntity instanceof EntitySlaveTrader trader) {
trader.setMaidUUID(maid.getUUID());
linkedSuccessfully = true;
TiedUpMod.LOGGER.debug(
"[KidnapperCampPiece] Linked maid {} to trader {}",
maid.getNpcName(),
trader.getNpcName()
);
}
// Link maid to camp
if (this.currentCamp != null) {
this.currentCamp.setMaidUUID(maid.getUUID());
CampOwnership.get(level).setDirty();
}
}
// Fallback: If no trader link established, search for nearby trader/camp
if (!linkedSuccessfully) {
TiedUpMod.LOGGER.debug(
"[KidnapperCampPiece] Maid at {} - no direct trader link, searching nearby...",
pos.toShortString()
);
// Try to find a nearby camp first
CampOwnership registry = CampOwnership.get(level);
CampOwnership.CampData camp = registry.findNearestAliveCamp(
pos,
50
);
if (camp != null && camp.getTraderUUID() != null) {
// Found a camp with a trader - link to it
maid.setMasterTraderUUID(camp.getTraderUUID());
camp.setMaidUUID(maid.getUUID());
registry.setDirty();
// Try to update the trader entity too
Entity traderEntity = level.getEntity(camp.getTraderUUID());
if (traderEntity instanceof EntitySlaveTrader trader) {
trader.setMaidUUID(maid.getUUID());
linkedSuccessfully = true;
TiedUpMod.LOGGER.info(
"[KidnapperCampPiece] Maid {} linked to trader {} via camp fallback",
maid.getNpcName(),
trader.getNpcName()
);
}
}
// Last resort: search for nearby trader entities
if (!linkedSuccessfully) {
java.util.List<EntitySlaveTrader> nearbyTraders =
level.getEntitiesOfClass(
EntitySlaveTrader.class,
maid.getBoundingBox().inflate(50)
);
if (!nearbyTraders.isEmpty()) {
EntitySlaveTrader trader = nearbyTraders.get(0);
maid.setMasterTraderUUID(trader.getUUID());
trader.setMaidUUID(maid.getUUID());
linkedSuccessfully = true;
// Update camp if trader has one
if (trader.getCampUUID() != null) {
CampOwnership.CampData traderCamp = registry.getCamp(
trader.getCampUUID()
);
if (traderCamp != null) {
traderCamp.setMaidUUID(maid.getUUID());
registry.setDirty();
}
}
TiedUpMod.LOGGER.info(
"[KidnapperCampPiece] Maid {} linked to nearby trader {} via entity search fallback",
maid.getNpcName(),
trader.getNpcName()
);
}
}
}
if (!linkedSuccessfully) {
TiedUpMod.LOGGER.warn(
"[KidnapperCampPiece] Maid spawned at {} but no trader found! Maid will be orphaned.",
pos.toShortString()
);
}
level.addFreshEntity(maid);
TiedUpMod.LOGGER.info(
"[KidnapperCampPiece] Spawned maid {} at {} from data marker, linked to trader={}",
maid.getNpcName(),
pos.toShortString(),
this.spawnedTraderUUID != null
? this.spawnedTraderUUID.toString().substring(0, 8)
: "null"
);
}
}