backrooms 2

This commit is contained in:
2026-03-18 16:19:06 +01:00
parent 0d6f6a4fd8
commit 63369076ae
27 changed files with 477 additions and 52 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 mod_version=26.3.18.1
maven_group=dev.tggamesyt maven_group=dev.tggamesyt
archives_base_name=szar archives_base_name=szar
# Dependencies # Dependencies

View File

@@ -362,6 +362,7 @@ public class SzarClient implements ClientModInitializer {
}); });
} }
); );
BlockEntityRendererFactories.register(Szar.WALL_BLOCK_ENTITY, WallBlockRenderer::new);
BlockEntityRendererFactories.register( BlockEntityRendererFactories.register(
Szar.TRACKER_BLOCK_ENTITY, Szar.TRACKER_BLOCK_ENTITY,
TGTrackerBlockRenderer::new TGTrackerBlockRenderer::new

View File

@@ -0,0 +1,91 @@
package dev.tggamesyt.szar.client;
import dev.tggamesyt.szar.Szar;
import dev.tggamesyt.szar.WallBlockEntity;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.client.render.WorldRenderer;
import org.joml.Matrix4f;
public class WallBlockRenderer implements BlockEntityRenderer<WallBlockEntity> {
static String base = "textures/walltext/";
static String end = ".png";
private static final Identifier[] TEXTURES = {
new Identifier(Szar.MOD_ID, base + "arrow1" + end),
new Identifier(Szar.MOD_ID, base + "arrow2" + end),
new Identifier(Szar.MOD_ID, base + "arrow3" + end),
new Identifier(Szar.MOD_ID, base + "arrow4" + end),
new Identifier(Szar.MOD_ID, base + "door_drawing" + end),
new Identifier(Szar.MOD_ID, base + "wall_small_1" + end),
new Identifier(Szar.MOD_ID, base + "wall_small_2" + end),
new Identifier(Szar.MOD_ID, base + "window_drawing" + end),
};
public WallBlockRenderer(BlockEntityRendererFactory.Context ctx) {}
@Override
public void render(WallBlockEntity entity, float tickDelta, MatrixStack matrices,
VertexConsumerProvider vertexConsumers, int light, int overlay) {
if (entity.drawingIndex < 0) return; // no drawing on this block
BlockPos pos = entity.getPos();
int lightLevel = WorldRenderer.getLightmapCoordinates(entity.getWorld(), pos);
Identifier texture = TEXTURES[entity.drawingIndex];
VertexConsumer consumer = vertexConsumers.getBuffer(
RenderLayer.getEntityCutoutNoCull(texture));
matrices.push();
// Offset and rotate based on which face the drawing is on
// 0=north, 1=south, 2=west, 3=east
switch (entity.drawingFace) {
//case 0 -> matrices.translate(0.5, 0.5, -0.001);
case 1 -> { // South face (positive Z)
matrices.translate(0.5, 0.5, 1.001);
matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_Y.rotationDegrees(180));
}
case 2 -> { // West face (negative X)
matrices.translate(-0.001, 0.5, 0.5);
matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_Y.rotationDegrees(90));
}
case 3 -> { // East face (positive X)
matrices.translate(1.001, 0.5, 0.5);
matrices.multiply(net.minecraft.util.math.RotationAxis.POSITIVE_Y.rotationDegrees(-90));
}
default -> matrices.translate(0.5, 0.5, -0.001);
}
// Scale to fit on the face (0.8 leaves a small border)
matrices.scale(0.8f, 0.8f, 0.8f);
Matrix4f matrix = matrices.peek().getPositionMatrix();
// Draw a simple quad
float u0 = 0f, u1 = 1f, v0 = 0f, v1 = 1f;
float s = 0.5f; // half size
consumer.vertex(matrix, -s, -s, 0).color(255, 255, 255, 255)
.texture(u0, v1).overlay(overlay).light(lightLevel)
.normal(0, 0, -1).next();
consumer.vertex(matrix, -s, s, 0).color(255, 255, 255, 255)
.texture(u0, v0).overlay(overlay).light(lightLevel)
.normal(0, 0, -1).next();
consumer.vertex(matrix, s, s, 0).color(255, 255, 255, 255)
.texture(u1, v0).overlay(overlay).light(lightLevel)
.normal(0, 0, -1).next();
consumer.vertex(matrix, s, -s, 0).color(255, 255, 255, 255)
.texture(u1, v1).overlay(overlay).light(lightLevel)
.normal(0, 0, -1).next();
matrices.pop();
}
}

View File

@@ -3,6 +3,7 @@ package dev.tggamesyt.szar;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import dev.tggamesyt.szar.Szar; import dev.tggamesyt.szar.Szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.registry.RegistryCodecs; import net.minecraft.registry.RegistryCodecs;
@@ -12,6 +13,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkRegion; import net.minecraft.world.ChunkRegion;
import net.minecraft.world.HeightLimitView; import net.minecraft.world.HeightLimitView;
import net.minecraft.world.Heightmap; import net.minecraft.world.Heightmap;
import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeKeys; import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.biome.source.BiomeAccess; import net.minecraft.world.biome.source.BiomeAccess;
@@ -113,6 +115,8 @@ 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);
} }
} }
@@ -232,4 +236,62 @@ public class BackroomsChunkGenerator extends ChunkGenerator {
@Override @Override
public void getDebugHudText(List<String> text, NoiseConfig noiseConfig, public void getDebugHudText(List<String> text, NoiseConfig noiseConfig,
BlockPos pos) {} BlockPos pos) {}
@Override
public void generateFeatures(StructureWorldAccess world, Chunk chunk,
StructureAccessor structureAccessor) {
// Initialize wall block entities after chunk is placed
BlockPos.Mutable mutable = new BlockPos.Mutable();
int chunkX = chunk.getPos().getStartX();
int chunkZ = chunk.getPos().getStartZ();
for (int lx = 0; lx < 16; lx++) {
for (int lz = 0; lz < 16; lz++) {
for (int y = 0; y < 64; y++) {
mutable.set(chunkX + lx, y, chunkZ + lz);
if (world.getBlockState(mutable).getBlock() instanceof WallBlock) {
if (world.getBlockEntity(mutable) == null) {
// Force block entity creation by re-setting the block
world.setBlockState(mutable, Szar.WALL_BLOCK.getDefaultState(),
Block.NOTIFY_LISTENERS);
}
if (world.getBlockEntity(mutable) instanceof WallBlockEntity wall) {
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(),
Block.NOTIFY_ALL);
}
if (world.getBlockEntity(mutable) 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;
// Tracker hangs from ceiling (Y=8, top wall block level)
// Portal is 4 blocks below at Y=4 (floor level, replacing floor block)
// Gap between: Y=5, Y=6, Y=7 = 3 air blocks + tracker at 8 = exactly 4 apart
BlockPos trackerPos = new BlockPos(lx, CEILING_Y - 1, lz); // Y=8
BlockPos portalPos = trackerPos.down(4); // Y=4
chunk.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(), false);
chunk.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(), false);
}
} }

View File

@@ -1,21 +0,0 @@
package dev.tggamesyt.szar;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
public class BackroomsChunkGeneratorConfig {
public static final Codec<BackroomsChunkGeneratorConfig> CODEC =
RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("floor_y").forGetter(c -> c.floorY)
).apply(instance, BackroomsChunkGeneratorConfig::new));
public static final BackroomsChunkGeneratorConfig DEFAULT =
new BackroomsChunkGeneratorConfig(4);
public final int floorY;
public BackroomsChunkGeneratorConfig(int floorY) {
this.floorY = floorY;
}
}

View File

@@ -0,0 +1,39 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
public class CanOfBeansItem extends Item {
public CanOfBeansItem(Settings settings) {
super(settings.maxCount(1).maxDamage(6));
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
if (world.isClient) return TypedActionResult.success(stack);
// Give one bean
ItemStack bean = new ItemStack(Szar.BEAN);
if (!player.getInventory().insertStack(bean)) {
player.dropItem(bean, false);
}
// Play a little sound
world.playSound(null, player.getBlockPos(),
SoundEvents.ITEM_BOTTLE_EMPTY, SoundCategory.PLAYERS, 1.0f, 1.0f);
// Damage the item by 1 — if it breaks, replace with empty hand
stack.damage(1, player, p -> p.sendToolBreakStatus(hand));
return TypedActionResult.success(stack);
}
}

View File

@@ -0,0 +1,50 @@
package dev.tggamesyt.szar;
import com.mojang.serialization.Codec;
import dev.tggamesyt.szar.PortalBlock;
import dev.tggamesyt.szar.Szar;
import dev.tggamesyt.szar.TrackerBlock;
import dev.tggamesyt.szar.TrackerBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.feature.util.FeatureContext;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
public class OverworldPortalFeature extends Feature<DefaultFeatureConfig> {
public OverworldPortalFeature(Codec<DefaultFeatureConfig> codec) {
super(codec);
}
@Override
public boolean generate(FeatureContext<DefaultFeatureConfig> ctx) {
var world = ctx.getWorld();
BlockPos origin = ctx.getOrigin();
int surfaceY = world.getTopY(
net.minecraft.world.Heightmap.Type.MOTION_BLOCKING_NO_LEAVES,
origin.getX(), origin.getZ());
BlockPos trackerPos = new BlockPos(origin.getX(), surfaceY, origin.getZ());
BlockPos portalPos = trackerPos.down(4);
BlockState original = world.getBlockState(portalPos);
world.setBlockState(trackerPos, Szar.TRACKER_BLOCK.getDefaultState(),
net.minecraft.block.Block.NOTIFY_ALL);
if (world.getBlockEntity(trackerPos) instanceof TrackerBlockEntity te) {
te.placedByPlayer = false;
te.originalPortalBlock = original;
te.markDirty();
}
world.setBlockState(portalPos, Szar.PORTAL_BLOCK.getDefaultState(),
net.minecraft.block.Block.NOTIFY_ALL);
return true;
}
}

View File

@@ -28,7 +28,6 @@ public class PortalBlock extends Block {
// Cooldown tracker so players don't teleport 20x per second // Cooldown tracker so players don't teleport 20x per second
private static final java.util.Map<java.util.UUID, Long> cooldowns = new java.util.HashMap<>(); private static final java.util.Map<java.util.UUID, Long> cooldowns = new java.util.HashMap<>();
public PortalBlock(Settings settings) { public PortalBlock(Settings settings) {
super(settings); super(settings);
} }
@@ -63,42 +62,47 @@ public class PortalBlock extends Block {
teleportToOverworld(player, tracker, server); teleportToOverworld(player, tracker, server);
} }
} else { } else {
// Non-player entity — just teleport, no inventory or tracker registration // Non-player entity — teleport only, no inventory or tracker registration
if (world.getRegistryKey() == World.OVERWORLD) { if (world.getRegistryKey() == World.OVERWORLD) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY); ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
if (backrooms == null) return; if (backrooms == null) return;
// Save overworld entry coords for this entity
PortalDataState.getOrCreate(server.getWorld(World.OVERWORLD))
.saveEntityEntry(entity.getUuid(), entity.getX(), entity.getY() + 6, entity.getZ());
double safeY = findSafeY(backrooms, (int) entity.getX(), (int) entity.getZ()); double safeY = findSafeY(backrooms, (int) entity.getX(), (int) entity.getZ());
entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(), entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(),
java.util.Set.of(), entity.getYaw(), entity.getPitch()); java.util.Set.of(), entity.getYaw(), entity.getPitch());
} else {
// Non-player entity — just teleport, no inventory or tracker registration
if (world.getRegistryKey() == World.OVERWORLD) {
ServerWorld backrooms = server.getWorld(Szar.BACKROOMS_KEY);
if (backrooms == null) return;
double safeY = findSafeY(backrooms, (int) entity.getX(), (int) entity.getZ());
entity.teleport(backrooms, entity.getX(), safeY, entity.getZ(),
java.util.Set.of(), entity.getYaw(), entity.getPitch());
} else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) {
ServerWorld overworld = server.getWorld(World.OVERWORLD);
if (overworld == null) return;
double baseX = tracker.returnX; } else if (world.getRegistryKey() == Szar.BACKROOMS_KEY) {
double baseY = tracker.returnY; ServerWorld overworld = server.getWorld(World.OVERWORLD);
double baseZ = tracker.returnZ; if (overworld == null) return;
// If no player has used this portal yet, fallback double rx, ry, rz;
if (baseX == 0 && baseY == 0 && baseZ == 0) {
double safeY = findSafeY(overworld, (int) entity.getX(), (int) entity.getZ());
entity.teleport(overworld, entity.getX(), safeY, entity.getZ(),
java.util.Set.of(), entity.getYaw(), entity.getPitch());
return;
}
// Search up to 10 blocks offset in XZ and Y for a safe spot not above a portal double[] saved = PortalDataState.getOrCreate(server.getWorld(World.OVERWORLD))
BlockPos safePos = findSafeSpotNearEntry(overworld, baseX, baseY, baseZ); .getAndRemoveEntityEntry(entity.getUuid());
entity.teleport(overworld, safePos.getX() + 0.5, safePos.getY(), safePos.getZ() + 0.5, if (saved != null) {
java.util.Set.of(), entity.getYaw(), entity.getPitch()); // Use entity's own saved entry coords
rx = saved[0];
ry = saved[1];
rz = saved[2];
} else if (tracker.returnX != 0 || tracker.returnY != 0 || tracker.returnZ != 0) {
// Fall back to last player's entry coords on this tracker
rx = tracker.returnX;
ry = tracker.returnY;
rz = tracker.returnZ;
} else {
// Fall back to entity's current position + 6
rx = entity.getX();
ry = entity.getY() + 6;
rz = entity.getZ();
} }
BlockPos safePos = findSafeSpotNearEntry(overworld, rx, ry, rz);
entity.teleport(overworld, safePos.getX() + 0.5, safePos.getY(), safePos.getZ() + 0.5,
java.util.Set.of(), entity.getYaw(), entity.getPitch());
} }
} }
} }

View File

@@ -43,4 +43,27 @@ public class PortalDataState extends PersistentState {
return overworld.getPersistentStateManager() return overworld.getPersistentStateManager()
.getOrCreate(PortalDataState::fromNbt, PortalDataState::new, KEY); .getOrCreate(PortalDataState::fromNbt, PortalDataState::new, KEY);
} }
public void saveEntityEntry(UUID uuid, double x, double y, double z) {
NbtCompound entry = new NbtCompound();
entry.putDouble("X", x);
entry.putDouble("Y", y);
entry.putDouble("Z", z);
data.put("entity_" + uuid, entry);
markDirty();
}
public double[] getAndRemoveEntityEntry(UUID uuid) {
String key = "entity_" + uuid;
if (!data.contains(key)) return null;
NbtCompound entry = data.getCompound(key);
double[] coords = new double[]{
entry.getDouble("X"),
entry.getDouble("Y"),
entry.getDouble("Z")
};
data.remove(key);
markDirty();
return coords;
}
} }

View File

@@ -368,6 +368,8 @@ public class Szar implements ModInitializer {
entries.add(Szar.WALL_BOTTOM_ITEM); entries.add(Szar.WALL_BOTTOM_ITEM);
entries.add(Szar.CEILING_ITEM); entries.add(Szar.CEILING_ITEM);
entries.add(Szar.PLASTIC_ITEM); entries.add(Szar.PLASTIC_ITEM);
entries.add(Szar.BEAN);
entries.add(Szar.CAN_OF_BEANS);
// crazy weponary // crazy weponary
entries.add(Szar.BULLET_ITEM); entries.add(Szar.BULLET_ITEM);
entries.add(Szar.AK47); entries.add(Szar.AK47);
@@ -1079,6 +1081,12 @@ public class Szar implements ModInitializer {
p -> p.sendToolBreakStatus(player.getActiveHand())); p -> p.sendToolBreakStatus(player.getActiveHand()));
}); });
}); });
BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(),
GenerationStep.Feature.SURFACE_STRUCTURES,
RegistryKey.of(RegistryKeys.PLACED_FEATURE,
new Identifier(MOD_ID, "overworld_portal"))
);
} }
// Blocks // Blocks
public static final TrackerBlock TRACKER_BLOCK = Registry.register( public static final TrackerBlock TRACKER_BLOCK = Registry.register(
@@ -1115,9 +1123,13 @@ public class Szar implements ModInitializer {
new Identifier(MOD_ID, "tracker"), new Identifier(MOD_ID, "tracker"),
FabricBlockEntityTypeBuilder.create(TrackerBlockEntity::new, TRACKER_BLOCK).build() FabricBlockEntityTypeBuilder.create(TrackerBlockEntity::new, TRACKER_BLOCK).build()
); );
public static final Block WALL_BLOCK = Registry.register( public static final WallBlock WALL_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "wall"), Registries.BLOCK, new Identifier(MOD_ID, "wall"),
new Block(AbstractBlock.Settings.create()) new WallBlock(AbstractBlock.Settings.create())
);
public static final BlockEntityType<WallBlockEntity> WALL_BLOCK_ENTITY = Registry.register(
Registries.BLOCK_ENTITY_TYPE, new Identifier(MOD_ID, "wall"),
FabricBlockEntityTypeBuilder.create(WallBlockEntity::new, WALL_BLOCK).build()
); );
public static final Block WALL_BOTTOM_BLOCK = Registry.register( public static final Block WALL_BOTTOM_BLOCK = Registry.register(
Registries.BLOCK, new Identifier(MOD_ID, "wall_bottom"), Registries.BLOCK, new Identifier(MOD_ID, "wall_bottom"),
@@ -1317,6 +1329,11 @@ public class Szar implements ModInitializer {
new Identifier(MOD_ID, "island"), new Identifier(MOD_ID, "island"),
() -> IslandStructure.CODEC () -> IslandStructure.CODEC
); );
public static Feature<DefaultFeatureConfig> OVERWORLD_PORTAL_FEATURE = Registry.register(
Registries.FEATURE,
new Identifier(MOD_ID, "overworld_portal"),
new OverworldPortalFeature(DefaultFeatureConfig.CODEC)
);
static VoxelShape shape0 = VoxelShapes.cuboid(0.1875f, 0f, 0.625f, 0.6875f, 0.5f, 1.125f); static VoxelShape shape0 = VoxelShapes.cuboid(0.1875f, 0f, 0.625f, 0.6875f, 0.5f, 1.125f);
static VoxelShape shape1 = VoxelShapes.cuboid(0.1875f, 1.5f, 0.625f, 0.6875f, 2f, 1.125f); static VoxelShape shape1 = VoxelShapes.cuboid(0.1875f, 1.5f, 0.625f, 0.6875f, 2f, 1.125f);
static VoxelShape shape2 = VoxelShapes.cuboid(0.5625f, 0f, 0.25f, 1.0625f, 2f, 0.75f); static VoxelShape shape2 = VoxelShapes.cuboid(0.5625f, 0f, 0.25f, 1.0625f, 2f, 0.75f);
@@ -1685,6 +1702,18 @@ public class Szar implements ModInitializer {
hunger((Math.random() < 0.5) ? 6 : 7) // SIX OR SEVEN hunger((Math.random() < 0.5) ? 6 : 7) // SIX OR SEVEN
.build()), 217) .build()), 217)
); );
public static final Item BEAN = Registry.register(
Registries.ITEM,
new Identifier(MOD_ID, "bean"),
new Item(new Item.Settings()
.food(new FoodComponent.Builder().saturationModifier(0.6f).hunger(1).build()))
);
public static final Item CAN_OF_BEANS = Registry.register(
Registries.ITEM,
new Identifier(MOD_ID, "can_of_beans"),
new CanOfBeansItem(new Item.Settings())
);
public static final SoundEvent BAITER = public static final SoundEvent BAITER =
SoundEvent.of(new Identifier(MOD_ID, "baiter")); SoundEvent.of(new Identifier(MOD_ID, "baiter"));
public static final Item BAITER_DISC = Registry.register( public static final Item BAITER_DISC = Registry.register(

View File

@@ -0,0 +1,33 @@
package dev.tggamesyt.szar;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class WallBlock extends Block implements BlockEntityProvider {
public WallBlock(Settings settings) {
super(settings);
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new WallBlockEntity(pos, state);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(
World world, BlockState state, BlockEntityType<T> type) {
if (world.isClient) return null;
return type == Szar.WALL_BLOCK_ENTITY
? (w, pos, s, be) -> WallBlockEntity.tick(w, pos, s, (WallBlockEntity) be)
: null;
}
}

View File

@@ -0,0 +1,76 @@
package dev.tggamesyt.szar;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class WallBlockEntity extends BlockEntity {
// -1 = no drawing, 0-7 = which drawing
public int drawingIndex = -1;
// Which face the drawing is on (0=north, 1=south, 2=west, 3=east)
public int drawingFace = 0;
public boolean initialized = false;
public WallBlockEntity(BlockPos pos, BlockState state) {
super(Szar.WALL_BLOCK_ENTITY, pos, state);
}
public void initializeIfNeeded() {
if (initialized) return;
initialized = true;
java.util.Random rand = new java.util.Random(
// Seed by position so it's deterministic per block
pos.getX() * 341873128712L ^ pos.getZ() * 132897987541L ^ pos.getY()
);
if (rand.nextInt(25) == 0) {
drawingIndex = rand.nextInt(8);
drawingFace = rand.nextInt(4);
}
markDirty();
}
@Override
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
nbt.putInt("DrawingIndex", drawingIndex);
nbt.putInt("DrawingFace", drawingFace);
nbt.putBoolean("Initialized", initialized);
}
@Override
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);
drawingIndex = nbt.getInt("DrawingIndex");
drawingFace = nbt.getInt("DrawingFace");
initialized = nbt.getBoolean("Initialized");
}
public static void tick(World world, BlockPos pos, BlockState state, WallBlockEntity entity) {
if (!world.isClient && !entity.initialized) {
entity.initializeIfNeeded();
}
}
@Override
public Packet<ClientPlayPacketListener> toUpdatePacket() {
return BlockEntityUpdateS2CPacket.create(this);
}
// In WallBlockEntity.java — override this to auto-init when chunk data is requested
@Override
public NbtCompound toInitialChunkDataNbt() {
if (!initialized) {
initializeIfNeeded();
}
return createNbt();
}
}

View File

@@ -144,5 +144,8 @@
"block.szar.plastic": "Plastic", "block.szar.plastic": "Plastic",
"block.szar.ceiling": "Ceiling", "block.szar.ceiling": "Ceiling",
"block.szar.wall": "Wall", "block.szar.wall": "Wall",
"block.szar.wall_bottom": "Wall Bottom" "block.szar.wall_bottom": "Wall Bottom",
"item.szar.bean": "Bean",
"item.szar.can_of_beans": "Can of Beans"
} }

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "szar:item/bean"
}
}

View File

@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "szar:item/can_of_beans"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,4 @@
{
"type": "szar:overworld_portal",
"config": {}
}

View File

@@ -0,0 +1,19 @@
{
"feature": "szar:overworld_portal",
"placement": [
{
"type": "minecraft:rarity_filter",
"chance": 20
},
{
"type": "minecraft:in_square"
},
{
"type": "minecraft:heightmap",
"heightmap": "MOTION_BLOCKING_NO_LEAVES"
},
{
"type": "minecraft:biome"
}
]
}