backrooms 2
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
src/main/java/dev/tggamesyt/szar/CanOfBeansItem.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/java/dev/tggamesyt/szar/OverworldPortalFeature.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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(
|
||||||
|
|||||||
33
src/main/java/dev/tggamesyt/szar/WallBlock.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/main/java/dev/tggamesyt/szar/WallBlockEntity.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/main/resources/assets/szar/models/item/bean.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "szar:item/bean"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "szar:item/can_of_beans"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/assets/szar/textures/item/bean.png
Normal file
|
After Width: | Height: | Size: 804 B |
BIN
src/main/resources/assets/szar/textures/item/can_of_beans.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"type": "szar:overworld_portal",
|
||||||
|
"config": {}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||