This commit is contained in:
2026-03-18 18:29:31 +01:00
parent 12ad7d6f04
commit 4f455ead5f
4 changed files with 250 additions and 37 deletions

View File

@@ -6,7 +6,7 @@ minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10 yarn_mappings=1.20.1+build.10
loader_version=0.18.3 loader_version=0.18.3
# Mod Properties # Mod Properties
mod_version=26.3.18.2 mod_version=26.3.18.3
maven_group=dev.tggamesyt maven_group=dev.tggamesyt
archives_base_name=szar archives_base_name=szar
# Dependencies # Dependencies

View File

@@ -0,0 +1,215 @@
package dev.tggamesyt.szar;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.block.entity.BarrelBlockEntity;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import java.util.*;
public class BackroomsBarrelManager {
private static final Map<UUID, Double> lastX = new HashMap<>();
private static final Map<UUID, Double> lastZ = new HashMap<>();
private static final Map<UUID, Double> walkAccumulator = new HashMap<>();
private static final Map<UUID, Integer> walkThreshold = new HashMap<>();
private static final Map<UUID, Set<BlockPos>> trackerBarrels = new HashMap<>();
private static final Map<UUID, Set<BlockPos>> foodBarrels = new HashMap<>();
public static void register() {
ServerTickEvents.END_SERVER_TICK.register(BackroomsBarrelManager::tick);
}
private static void tick(MinecraftServer server) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
if (backrooms == null) return;
for (ServerPlayerEntity player : backrooms.getPlayers()) {
UUID uuid = player.getUuid();
// --- Walk tracking ---
double px = player.getX();
double pz = player.getZ();
if (lastX.containsKey(uuid)) {
double dx = px - lastX.get(uuid);
double dz = pz - lastZ.get(uuid);
double dist = Math.sqrt(dx * dx + dz * dz);
walkAccumulator.merge(uuid, dist, Double::sum);
}
lastX.put(uuid, px);
lastZ.put(uuid, pz);
if (!walkThreshold.containsKey(uuid)) {
walkThreshold.put(uuid, randomThreshold(backrooms.random));
}
// --- Check if tracker barrels need clearing ---
if (trackerBarrels.containsKey(uuid)) {
checkAndClearTrackerBarrels(backrooms, uuid);
}
// --- Check if food barrels need clearing ---
if (foodBarrels.containsKey(uuid)) {
checkAndClearFoodBarrels(backrooms, uuid);
}
// --- Hunger check (every 20 ticks) ---
if (backrooms.getTime() % 20 == 0) {
HungerManager hunger = player.getHungerManager();
if (hunger.getFoodLevel() <= 10 && !hasAnyFood(player)) {
if (!foodBarrels.containsKey(uuid)) {
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
if (!nearby.isEmpty()) {
Set<BlockPos> positions = new HashSet<>();
for (BarrelBlockEntity barrel : nearby) {
placeItemInBarrel(barrel, new ItemStack(Szar.CAN_OF_BEANS));
positions.add(barrel.getPos().toImmutable());
}
foodBarrels.put(uuid, positions);
}
}
}
}
// --- Walk threshold check ---
double walked = walkAccumulator.getOrDefault(uuid, 0.0);
if (walked >= walkThreshold.getOrDefault(uuid, 500)) {
List<BarrelBlockEntity> nearby = getNearbyBarrels(backrooms, player, 16);
if (!nearby.isEmpty()) {
Set<BlockPos> positions = new HashSet<>();
for (BarrelBlockEntity barrel : nearby) {
placeItemInBarrel(barrel, new ItemStack(Szar.TRACKER_BLOCK_ITEM));
positions.add(barrel.getPos().toImmutable());
}
trackerBarrels.put(uuid, positions);
}
walkAccumulator.put(uuid, 0.0);
walkThreshold.put(uuid, randomThreshold(backrooms.random));
}
}
// Clean up data for players no longer in backrooms
Set<UUID> activePlayers = new HashSet<>();
for (ServerPlayerEntity p : backrooms.getPlayers()) activePlayers.add(p.getUuid());
lastX.keySet().retainAll(activePlayers);
lastZ.keySet().retainAll(activePlayers);
walkAccumulator.keySet().retainAll(activePlayers);
walkThreshold.keySet().retainAll(activePlayers);
foodBarrels.keySet().retainAll(activePlayers);
trackerBarrels.keySet().retainAll(activePlayers);
}
private static void checkAndClearTrackerBarrels(ServerWorld world, UUID uuid) {
Set<BlockPos> positions = trackerBarrels.get(uuid);
if (positions == null) return;
boolean anyTaken = false;
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
if (!barrelHasItem(barrel, Szar.TRACKER_BLOCK_ITEM.asItem())) {
anyTaken = true;
break;
}
}
}
if (anyTaken) {
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
removeItemFromBarrel(barrel, Szar.TRACKER_BLOCK_ITEM.asItem());
}
}
trackerBarrels.remove(uuid);
}
}
private static void checkAndClearFoodBarrels(ServerWorld world, UUID uuid) {
Set<BlockPos> positions = foodBarrels.get(uuid);
if (positions == null) return;
boolean anyTaken = false;
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
if (!barrelHasItem(barrel, Szar.CAN_OF_BEANS.asItem())) {
anyTaken = true;
break;
}
}
}
if (anyTaken) {
for (BlockPos pos : positions) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
removeItemFromBarrel(barrel, Szar.CAN_OF_BEANS.asItem());
}
}
foodBarrels.remove(uuid);
}
}
private static boolean barrelHasItem(BarrelBlockEntity barrel, Item item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isOf(item)) return true;
}
return false;
}
private static void removeItemFromBarrel(BarrelBlockEntity barrel, Item item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isOf(item)) {
barrel.setStack(i, ItemStack.EMPTY);
barrel.markDirty();
return;
}
}
}
private static boolean hasAnyFood(ServerPlayerEntity player) {
for (int i = 0; i < player.getInventory().size(); i++) {
ItemStack stack = player.getInventory().getStack(i);
if (!stack.isEmpty() && (stack.isFood()
|| stack.isOf(Szar.CAN_OF_BEANS))) {
return true;
}
}
return false;
}
private static void placeItemInBarrel(BarrelBlockEntity barrel, ItemStack item) {
for (int i = 0; i < barrel.size(); i++) {
if (barrel.getStack(i).isEmpty()) {
barrel.setStack(i, item.copy());
barrel.markDirty();
return;
}
}
}
private static List<BarrelBlockEntity> getNearbyBarrels(ServerWorld world,
ServerPlayerEntity player,
int radius) {
List<BarrelBlockEntity> result = new ArrayList<>();
Box box = player.getBoundingBox().expand(radius);
BlockPos min = BlockPos.ofFloored(box.minX, box.minY, box.minZ);
BlockPos max = BlockPos.ofFloored(box.maxX, box.maxY, box.maxZ);
for (BlockPos pos : BlockPos.iterate(min, max)) {
if (world.getBlockEntity(pos) instanceof BarrelBlockEntity barrel) {
result.add(barrel);
}
}
return result;
}
private static int randomThreshold(net.minecraft.util.math.random.Random random) {
return 500 + random.nextInt(501);
}
}

View File

@@ -101,11 +101,18 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
} }
if (isOpen) { if (isOpen) {
// Air inside the room
for (int y = WALL_BASE_Y; y <= WALL_TOP_Y; y++) { for (int y = WALL_BASE_Y; y <= WALL_TOP_Y; y++) {
chunk.setBlockState(new BlockPos(lx, y, lz), chunk.setBlockState(new BlockPos(lx, y, lz),
Blocks.AIR.getDefaultState(), false); Blocks.AIR.getDefaultState(), false);
} }
// 1 in 40 chance of a barrel on the floor
// With this — 1 per ~40x40 block area:
long barrelHash = hash(worldX * 31 + 17, worldZ * 29 + 11);
if ((barrelHash % 1600) == 0) {
chunk.setBlockState(new BlockPos(lx, FLOOR_Y + 1, lz),
Blocks.BARREL.getDefaultState(), false);
}
} else { } else {
// Wall column // Wall column
chunk.setBlockState(new BlockPos(lx, WALL_BASE_Y, lz), chunk.setBlockState(new BlockPos(lx, WALL_BASE_Y, lz),
@@ -115,8 +122,6 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
Szar.WALL_BLOCK.getDefaultState(), false); Szar.WALL_BLOCK.getDefaultState(), false);
} }
} }
// After the lx/lz loop, still inside populateNoise:
placeBackroomsPortals(chunk, chunkX, chunkZ);
} }
} }
@@ -240,55 +245,47 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
@Override @Override
public void generateFeatures(StructureWorldAccess world, Chunk chunk, public void generateFeatures(StructureWorldAccess world, Chunk chunk,
StructureAccessor structureAccessor) { StructureAccessor structureAccessor) {
// Initialize wall block entities after chunk is placed
BlockPos.Mutable mutable = new BlockPos.Mutable(); BlockPos.Mutable mutable = new BlockPos.Mutable();
int chunkX = chunk.getPos().getStartX(); int chunkX = chunk.getPos().getStartX();
int chunkZ = chunk.getPos().getStartZ(); int chunkZ = chunk.getPos().getStartZ();
// Initialize wall block entities
for (int lx = 0; lx < 16; lx++) { for (int lx = 0; lx < 16; lx++) {
for (int lz = 0; lz < 16; lz++) { for (int lz = 0; lz < 16; lz++) {
for (int y = 0; y < 64; y++) { for (int y = 0; y < 64; y++) {
mutable.set(chunkX + lx, y, chunkZ + lz); mutable.set(chunkX + lx, y, chunkZ + lz);
if (world.getBlockState(mutable).getBlock() instanceof WallBlock) { if (world.getBlockState(mutable).getBlock() instanceof WallBlock) {
if (world.getBlockEntity(mutable) == null) { world.setBlockState(mutable, Szar.WALL_BLOCK.getDefaultState(),
// Force block entity creation by re-setting the block Block.NOTIFY_ALL);
world.setBlockState(mutable, Szar.WALL_BLOCK.getDefaultState(),
Block.NOTIFY_LISTENERS);
}
if (world.getBlockEntity(mutable) instanceof WallBlockEntity wall) { if (world.getBlockEntity(mutable) instanceof WallBlockEntity wall) {
wall.initializeIfNeeded(); wall.initializeIfNeeded();
} }
} }
} }
mutable.set(chunkX + lx, CEILING_Y - 1, chunkZ + lz); // Y=8 }
if (world.getBlockState(mutable).getBlock() instanceof TrackerBlock) { }
if (world.getBlockEntity(mutable) == null) {
world.setBlockState(mutable, Szar.TRACKER_BLOCK.getDefaultState(), // Place tracker portals
Block.NOTIFY_ALL); long chunkHash = hash((chunkX / 16) * 7 + 3, (chunkZ / 16) * 13 + 7);
} if ((chunkHash % 20) == 0) {
if (world.getBlockEntity(mutable) instanceof TrackerBlockEntity te) { int lx = 4 + (int)(chunkHash >> 8 & 0x7);
te.placedByPlayer = false; int lz = 4 + (int)(chunkHash >> 12 & 0x7);
te.originalPortalBlock = Szar.PLASTIC.getDefaultState();
te.markDirty(); if (isOpenSpace(chunkX + lx, chunkZ + lz)) {
} BlockPos trackerPos = new BlockPos(chunkX + lx, FLOOR_Y + 1, chunkZ + lz);
BlockPos portalPos = trackerPos.down(4);
world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
world.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(),
Block.NOTIFY_ALL);
if (world.getBlockEntity(trackerPos) instanceof TrackerBlockEntity te) {
te.placedByPlayer = false;
te.originalPortalBlock = Szar.PLASTIC.getDefaultState();
te.markDirty();
} }
} }
} }
} }
private void placeBackroomsPortals(Chunk chunk, int chunkX, int chunkZ) {
long chunkHash = hash(chunkX * 7 + 3, chunkZ * 13 + 7);
if ((chunkHash % 20) != 0) return;
int lx = 4 + (int)(chunkHash >> 8 & 0x7);
int lz = 4 + (int)(chunkHash >> 12 & 0x7);
if (!isOpenSpace(chunkX * 16 + lx, chunkZ * 16 + lz)) return;
BlockPos trackerPos = new BlockPos(lx, FLOOR_Y + 1, lz); // Y=5
BlockPos portalPos = trackerPos.down(4); // Y=1, underground
chunk.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(), false);
chunk.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(), false);
}
} }

View File

@@ -1087,6 +1087,7 @@ public class Szar implements ModInitializer {
RegistryKey.of(RegistryKeys.PLACED_FEATURE, RegistryKey.of(RegistryKeys.PLACED_FEATURE,
new Identifier(MOD_ID, "overworld_portal")) new Identifier(MOD_ID, "overworld_portal"))
); );
BackroomsBarrelManager.register();
} }
// Blocks // Blocks
public static final TrackerBlock TRACKER_BLOCK = Registry.register( public static final TrackerBlock TRACKER_BLOCK = Registry.register(