Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
350d137d9e |
No files matched your search
+1
-1
@@ -36,7 +36,7 @@ mod_name=Rivencraft
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
# The mod version. Rivencraft uses the x.y.z.b policy documented in README.md.
|
||||||
mod_version=0.6.2.4
|
mod_version=0.6.2.5
|
||||||
immersive_portals_version=6.0.7
|
immersive_portals_version=6.0.7
|
||||||
terrablender_version=4.1.0.8
|
terrablender_version=4.1.0.8
|
||||||
serene_seasons_version_id=B6nTjh4b
|
serene_seasons_version_id=B6nTjh4b
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import net.dnorberg.rivencraft.portal.PortalTravelCooldown;
|
|||||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
@@ -24,6 +25,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
import net.minecraft.world.level.material.Fluid;
|
import net.minecraft.world.level.material.Fluid;
|
||||||
import net.neoforged.fml.ModList;
|
import net.neoforged.fml.ModList;
|
||||||
|
|
||||||
@@ -32,15 +34,16 @@ public final class CrystalPortalBlock extends BaseEntityBlock {
|
|||||||
public static final MapCodec<CrystalPortalBlock> CODEC = simpleCodec(CrystalPortalBlock::new);
|
public static final MapCodec<CrystalPortalBlock> CODEC = simpleCodec(CrystalPortalBlock::new);
|
||||||
// Retained so portal blocks already saved with this state still deserialize.
|
// Retained so portal blocks already saved with this state still deserialize.
|
||||||
public static final BooleanProperty LIVE_VIEW = BooleanProperty.create("live_view");
|
public static final BooleanProperty LIVE_VIEW = BooleanProperty.create("live_view");
|
||||||
|
public static final EnumProperty<Direction.Axis> AXIS = EnumProperty.create("axis", Direction.Axis.class);
|
||||||
private static final String COOLDOWN = "RivencraftCrystalPortalCooldown";
|
private static final String COOLDOWN = "RivencraftCrystalPortalCooldown";
|
||||||
|
|
||||||
public CrystalPortalBlock(Properties properties) {
|
public CrystalPortalBlock(Properties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
registerDefaultState(stateDefinition.any().setValue(LIVE_VIEW, false));
|
registerDefaultState(stateDefinition.any().setValue(LIVE_VIEW, false).setValue(AXIS, Direction.Axis.Z));
|
||||||
}
|
}
|
||||||
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
|
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
|
||||||
@Override protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
@Override protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
builder.add(LIVE_VIEW);
|
builder.add(LIVE_VIEW, AXIS);
|
||||||
}
|
}
|
||||||
@Override protected RenderShape getRenderShape(BlockState state) {
|
@Override protected RenderShape getRenderShape(BlockState state) {
|
||||||
// The live-view flag can lag behind portal-entity creation or fail to
|
// The live-view flag can lag behind portal-entity creation or fail to
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.dnorberg.rivencraft.gametest;
|
|||||||
|
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
import net.dnorberg.rivencraft.block.BookReceptacleBlock;
|
import net.dnorberg.rivencraft.block.BookReceptacleBlock;
|
||||||
|
import net.dnorberg.rivencraft.block.CrystalPortalBlock;
|
||||||
import net.dnorberg.rivencraft.block.entity.BookReceptacleBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.BookReceptacleBlockEntity;
|
||||||
import net.dnorberg.rivencraft.block.entity.CrystalPortalBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.CrystalPortalBlockEntity;
|
||||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||||
@@ -49,6 +50,8 @@ public final class CrystalPortalGameTests {
|
|||||||
BlockPos center = helper.absolutePos(new BlockPos(1, 2, 1));
|
BlockPos center = helper.absolutePos(new BlockPos(1, 2, 1));
|
||||||
helper.assertTrue(level.getBlockEntity(center) instanceof CrystalPortalBlockEntity portal
|
helper.assertTrue(level.getBlockEntity(center) instanceof CrystalPortalBlockEntity portal
|
||||||
&& holderPos.equals(portal.receptacle()), "The portal must point to its one book holder");
|
&& holderPos.equals(portal.receptacle()), "The portal must point to its one book holder");
|
||||||
|
helper.assertValueEqual(level.getBlockState(center).getValue(CrystalPortalBlock.AXIS), Direction.Axis.Z,
|
||||||
|
"A north/south opening must only show its two player-facing sides");
|
||||||
CrystalPortalNetwork.shutdown(level, holderPos);
|
CrystalPortalNetwork.shutdown(level, holderPos);
|
||||||
helper.assertTrue(level.getBlockState(center).isAir(), "Removing the book must release the portal");
|
helper.assertTrue(level.getBlockState(center).isAir(), "Removing the book must release the portal");
|
||||||
helper.succeed();
|
helper.succeed();
|
||||||
@@ -58,6 +61,48 @@ public final class CrystalPortalGameTests {
|
|||||||
return helper.absolutePos(new BlockPos(1, 2, 1));
|
return helper.absolutePos(new BlockPos(1, 2, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void eastWestOpeningOrientsFallbackSurface(GameTestHelper helper) {
|
||||||
|
var level = helper.getLevel();
|
||||||
|
for (int z = 1; z <= 3; z++) for (int y = 1; y <= 3; y++) {
|
||||||
|
if (z == 2 && y == 2) continue;
|
||||||
|
level.setBlock(helper.absolutePos(new BlockPos(1, y, z)),
|
||||||
|
ModBlocks.CRYSTAL_BLOCK.get().defaultBlockState(), 3);
|
||||||
|
}
|
||||||
|
BlockPos holderPos = helper.absolutePos(new BlockPos(1, 1, 0));
|
||||||
|
level.setBlock(holderPos, ModBlocks.BOOK_RECEPTACLE.get().defaultBlockState()
|
||||||
|
.setValue(BookReceptacleBlock.FACING, Direction.NORTH), 3);
|
||||||
|
((BookReceptacleBlockEntity) level.getBlockEntity(holderPos))
|
||||||
|
.setBook(new ItemStack(ModItems.LINKING_BOOK.get()));
|
||||||
|
helper.assertTrue(CrystalPortalNetwork.activate(level, holderPos, CrystalPortalNetwork.plan(level, holderPos)),
|
||||||
|
"The east/west crystal opening must activate");
|
||||||
|
helper.assertValueEqual(level.getBlockState(helper.absolutePos(new BlockPos(1, 2, 2)))
|
||||||
|
.getValue(CrystalPortalBlock.AXIS), Direction.Axis.X,
|
||||||
|
"The fallback surface must face east and west, not north and south");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "empty")
|
||||||
|
public static void horizontalOpeningOrientsFallbackSurface(GameTestHelper helper) {
|
||||||
|
var level = helper.getLevel();
|
||||||
|
for (int x = 0; x < 3; x++) for (int z = 0; z < 3; z++) {
|
||||||
|
if (x == 1 && z == 1) continue;
|
||||||
|
level.setBlock(helper.absolutePos(new BlockPos(x, 1, z)),
|
||||||
|
ModBlocks.CRYSTAL_BLOCK.get().defaultBlockState(), 3);
|
||||||
|
}
|
||||||
|
BlockPos holderPos = helper.absolutePos(new BlockPos(0, 2, 1));
|
||||||
|
level.setBlock(holderPos, ModBlocks.BOOK_RECEPTACLE.get().defaultBlockState()
|
||||||
|
.setValue(BookReceptacleBlock.FACING, Direction.UP), 3);
|
||||||
|
((BookReceptacleBlockEntity) level.getBlockEntity(holderPos))
|
||||||
|
.setBook(new ItemStack(ModItems.LINKING_BOOK.get()));
|
||||||
|
helper.assertTrue(CrystalPortalNetwork.activate(level, holderPos, CrystalPortalNetwork.plan(level, holderPos)),
|
||||||
|
"The horizontal crystal opening must activate");
|
||||||
|
helper.assertValueEqual(level.getBlockState(helper.absolutePos(new BlockPos(1, 1, 1)))
|
||||||
|
.getValue(CrystalPortalBlock.AXIS), Direction.Axis.Y,
|
||||||
|
"The fallback surface must face up and down");
|
||||||
|
helper.succeed();
|
||||||
|
}
|
||||||
|
|
||||||
@GameTest(template = "empty")
|
@GameTest(template = "empty")
|
||||||
public static void incompleteFrameCannotActivate(GameTestHelper helper) {
|
public static void incompleteFrameCannotActivate(GameTestHelper helper) {
|
||||||
var level = helper.getLevel();
|
var level = helper.getLevel();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import net.dnorberg.rivencraft.block.BookReceptacleBlock;
|
import net.dnorberg.rivencraft.block.BookReceptacleBlock;
|
||||||
|
import net.dnorberg.rivencraft.block.CrystalPortalBlock;
|
||||||
import net.dnorberg.rivencraft.block.entity.BookReceptacleBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.BookReceptacleBlockEntity;
|
||||||
import net.dnorberg.rivencraft.block.entity.CrystalPortalBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.CrystalPortalBlockEntity;
|
||||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||||
@@ -119,6 +120,16 @@ public final class CrystalPortalNetwork {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// All cells are now present, so the same bracing rule used for the
|
||||||
|
// Immersive Portals surface can orient the two-sided fallback model.
|
||||||
|
for (CrystalPortalSurfaces.Surface surface : CrystalPortalSurfaces.group(level, plan)) {
|
||||||
|
for (BlockPos pos : surface.cells()) {
|
||||||
|
BlockState state = level.getBlockState(pos);
|
||||||
|
Direction.Axis axis = surface.normal().getAxis();
|
||||||
|
if (state.getValue(CrystalPortalBlock.AXIS) != axis)
|
||||||
|
level.setBlock(pos, state.setValue(CrystalPortalBlock.AXIS, axis), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"variants":{"live_view=false":{"model":"rivencraft:block/crystal_portal"},"live_view=true":{"model":"rivencraft:block/crystal_portal"}}}
|
{"variants":{"axis=x,live_view=false":{"model":"rivencraft:block/crystal_portal","y":90},"axis=x,live_view=true":{"model":"rivencraft:block/crystal_portal","y":90},"axis=y,live_view=false":{"model":"rivencraft:block/crystal_portal","x":90},"axis=y,live_view=true":{"model":"rivencraft:block/crystal_portal","x":90},"axis=z,live_view=false":{"model":"rivencraft:block/crystal_portal"},"axis=z,live_view=true":{"model":"rivencraft:block/crystal_portal"}}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"parent":"minecraft:block/cube_all","textures":{"all":"rivencraft:block/crystal_portal"}}
|
{"textures":{"portal":"rivencraft:block/crystal_portal"},"elements":[{"from":[0,0,7.5],"to":[16,16,8.5],"shade":false,"faces":{"north":{"texture":"#portal"},"south":{"texture":"#portal"}}}]}
|
||||||
Reference in new issue
Block a user