diff --git a/src/main/java/ru/betterend/blocks/BlockRespawnObelisk.java b/src/main/java/ru/betterend/blocks/BlockRespawnObelisk.java new file mode 100644 index 00000000..5560afbb --- /dev/null +++ b/src/main/java/ru/betterend/blocks/BlockRespawnObelisk.java @@ -0,0 +1,174 @@ +package ru.betterend.blocks; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ShapeContext; +import net.minecraft.client.color.block.BlockColorProvider; +import net.minecraft.client.color.item.ItemColorProvider; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.loot.context.LootContext; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import net.minecraft.world.WorldAccess; +import net.minecraft.world.WorldView; +import ru.betterend.blocks.BlockProperties.TripleShape; +import ru.betterend.blocks.basis.BlockBase; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IColorProvider; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.registry.EndBlocks; +import ru.betterend.registry.EndItems; +import ru.betterend.util.BlocksHelper; +import ru.betterend.util.MHelper; + +public class BlockRespawnObelisk extends BlockBase implements IColorProvider, IRenderTypeable { + private static final VoxelShape VOXEL_SHAPE_BOTTOM = Block.createCuboidShape(1, 0, 1, 15, 16, 15); + private static final VoxelShape VOXEL_SHAPE_MIDDLE_TOP = Block.createCuboidShape(2, 0, 2, 14, 16, 14); + + public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; + + public BlockRespawnObelisk() { + super(FabricBlockSettings.copyOf(Blocks.END_STONE).luminance((state) -> { + return (state.get(SHAPE) == TripleShape.BOTTOM) ? 0 : 15; + })); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ePos) { + return (state.get(SHAPE) == TripleShape.BOTTOM) ? VOXEL_SHAPE_BOTTOM : VOXEL_SHAPE_MIDDLE_TOP; + } + + @Override + protected void appendProperties(StateManager.Builder stateManager) { + stateManager.add(SHAPE); + } + + @Override + public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) { + for (int i = 0; i < 3; i++) { + if (!world.getBlockState(pos.up(i)).getMaterial().isReplaceable()) { + return false; + } + } + return true; + } + + @Override + public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) { + state = this.getDefaultState(); + BlocksHelper.setWithUpdate(world, pos, state.with(SHAPE, TripleShape.BOTTOM)); + BlocksHelper.setWithUpdate(world, pos.up(), state.with(SHAPE, TripleShape.MIDDLE)); + BlocksHelper.setWithUpdate(world, pos.up(2), state.with(SHAPE, TripleShape.TOP)); + } + + @Override + public BlockState getStateForNeighborUpdate(BlockState state, Direction facing, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { + TripleShape shape = state.get(SHAPE); + if (shape == TripleShape.BOTTOM) { + if (world.getBlockState(pos.up()).isOf(this)) { + return state; + } + else { + return Blocks.AIR.getDefaultState(); + } + } + else if (shape == TripleShape.MIDDLE) { + if (world.getBlockState(pos.up()).isOf(this) && world.getBlockState(pos.down()).isOf(this)) { + return state; + } + else { + return Blocks.AIR.getDefaultState(); + } + } + else { + if (world.getBlockState(pos.down()).isOf(this)) { + return state; + } + else { + return Blocks.AIR.getDefaultState(); + } + } + } + + @Override + public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { + if (player.isCreative()) { + TripleShape shape = state.get(SHAPE); + if (shape == TripleShape.MIDDLE) { + BlocksHelper.setWithUpdate(world, pos.down(), Blocks.AIR); + } + else if (shape == TripleShape.TOP) { + BlocksHelper.setWithUpdate(world, pos.down(2), Blocks.AIR); + } + } + super.onBreak(world, pos, state, player); + } + + @Override + public List getDroppedStacks(BlockState state, LootContext.Builder builder) { + if (state.get(SHAPE) == TripleShape.BOTTOM) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.TRANSLUCENT; + } + + @Override + public BlockColorProvider getProvider() { + return ((IColorProvider) EndBlocks.AURORA_CRYSTAL).getProvider(); + } + + @Override + public ItemColorProvider getItemProvider() { + return (stack, tintIndex) -> { + return MHelper.color(255, 255, 255); + }; + } + + @Override + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + ItemStack itemStack = player.getStackInHand(hand); + boolean canActivate = itemStack.getItem() == EndItems.AMBER_GEM && itemStack.getCount() > 3; + if (hand != Hand.MAIN_HAND || !canActivate) { + return ActionResult.FAIL; + } + if (!world.isClient) { + ServerPlayerEntity serverPlayerEntity = (ServerPlayerEntity) player; + serverPlayerEntity.setSpawnPoint(world.getRegistryKey(), pos, 0.0F, false, true); + serverPlayerEntity.sendMessage(new TranslatableText("message.betterend.set_spawn"), true); + world.playSound(null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, SoundEvents.BLOCK_RESPAWN_ANCHOR_SET_SPAWN, SoundCategory.BLOCKS, 1.0F, 1.0F); + if (!player.isCreative()) { + itemStack.decrement(4); + } + } + return player.isCreative() ? ActionResult.PASS : ActionResult.success(world.isClient); + } +} diff --git a/src/main/java/ru/betterend/mixin/common/PlayerEntityMixin.java b/src/main/java/ru/betterend/mixin/common/PlayerEntityMixin.java new file mode 100644 index 00000000..e00fa670 --- /dev/null +++ b/src/main/java/ru/betterend/mixin/common/PlayerEntityMixin.java @@ -0,0 +1,55 @@ +package ru.betterend.mixin.common; + +import java.util.Optional; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.blocks.BlockProperties.TripleShape; +import ru.betterend.registry.EndBlocks; +import ru.betterend.util.BlocksHelper; +import ru.betterend.util.MHelper; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin { + private static Direction[] HORIZONTAL; + + @Inject(method = "findRespawnPosition", at = @At(value = "HEAD"), cancellable = true) + private static void statueRespawn(ServerWorld world, BlockPos pos, float f, boolean bl, boolean bl2, CallbackInfoReturnable> info) { + BlockState blockState = world.getBlockState(pos); + if (blockState.isOf(EndBlocks.RESPAWN_OBELISK)) { + info.setReturnValue(beObeliskRespawnPosition(world, pos, blockState)); + info.cancel(); + } + } + + private static Optional beObeliskRespawnPosition(ServerWorld world, BlockPos pos, BlockState state) { + if (state.get(BlockProperties.TRIPLE_SHAPE) == TripleShape.TOP) { + pos = pos.down(2); + } + else if (state.get(BlockProperties.TRIPLE_SHAPE) == TripleShape.MIDDLE) { + pos = pos.down(); + } + if (HORIZONTAL == null) { + HORIZONTAL = BlocksHelper.makeHorizontal(); + } + MHelper.shuffle(HORIZONTAL, world.getRandom()); + for (Direction dir: HORIZONTAL) { + BlockPos p = pos.offset(dir); + BlockState state2 = world.getBlockState(p); + if (!state2.getMaterial().blocksMovement() && state2.getCollisionShape(world, pos).isEmpty()) { + return Optional.of(Vec3d.of(p).add(0.5, 0, 0.5)); + } + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/betterend/registry/EndBlocks.java b/src/main/java/ru/betterend/registry/EndBlocks.java index 5fd62b0c..ffac7064 100644 --- a/src/main/java/ru/betterend/registry/EndBlocks.java +++ b/src/main/java/ru/betterend/registry/EndBlocks.java @@ -54,6 +54,7 @@ import ru.betterend.blocks.BlockMurkweed; import ru.betterend.blocks.BlockNeedlegrass; import ru.betterend.blocks.BlockPath; import ru.betterend.blocks.BlockPythadendronSapling; +import ru.betterend.blocks.BlockRespawnObelisk; import ru.betterend.blocks.BlockShadowBerry; import ru.betterend.blocks.BlockShadowGrass; import ru.betterend.blocks.BlockSulphurCrystal; @@ -245,6 +246,8 @@ public class EndBlocks { public static final Block AURORA_CRYSTAL = registerBlock("aurora_crystal", new AuroraCrystalBlock()); public static final Block AMBER_BLOCK = registerBlock("amber_block", new BlockAmber()); + public static final Block RESPAWN_OBELISK = registerBlock("respawn_obelisk", new BlockRespawnObelisk()); + // Lanterns public static final Block ANDESITE_LANTERN = registerBlock("andesite_lantern", new BlockStoneLantern(Blocks.ANDESITE)); public static final Block DIORITE_LANTERN = registerBlock("diorite_lantern", new BlockStoneLantern(Blocks.DIORITE)); diff --git a/src/main/resources/assets/betterend/blockstates/respawn_obelisk.json b/src/main/resources/assets/betterend/blockstates/respawn_obelisk.json new file mode 100644 index 00000000..b7ccb4d2 --- /dev/null +++ b/src/main/resources/assets/betterend/blockstates/respawn_obelisk.json @@ -0,0 +1,7 @@ +{ + "variants": { + "shape=top": { "model": "betterend:block/respawn_obelisk_top" }, + "shape=middle": { "model": "betterend:block/respawn_obelisk_middle" }, + "shape=bottom": { "model": "betterend:block/respawn_obelisk_bottom" } + } +} diff --git a/src/main/resources/assets/betterend/materialmaps/block/respawn_obelisk.json b/src/main/resources/assets/betterend/materialmaps/block/respawn_obelisk.json new file mode 100644 index 00000000..31a6585c --- /dev/null +++ b/src/main/resources/assets/betterend/materialmaps/block/respawn_obelisk.json @@ -0,0 +1,10 @@ +{ + "defaultMap": { + "spriteMap": [ + { + "sprite": "betterend:block/aurora_crystal", + "material": "betterend:glow_all" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/betterend/models/block/respawn_obelisk_bottom.json b/src/main/resources/assets/betterend/models/block/respawn_obelisk_bottom.json new file mode 100644 index 00000000..78495db5 --- /dev/null +++ b/src/main/resources/assets/betterend/models/block/respawn_obelisk_bottom.json @@ -0,0 +1,47 @@ +{ + "__comment": "Designed by Paulevs with Cubik Studio - https://cubik.studio", + "textures": { + "particle": "betterend:block/amber_block", + "texture": "betterend:block/amber_block", + "side": "betterend:block/respawn_obelisk_bottom_side" + }, + "elements": [ + { + "__comment": "Box1", + "from": [ 1, 0, 1 ], + "to": [ 15, 4, 15 ], + "faces": { + "down": { "uv": [ 1, 1, 15, 15 ], "texture": "#texture", "cullface": "down" }, + "up": { "uv": [ 1, 1, 15, 15 ], "texture": "#texture" }, + "north": { "uv": [ 1, 12, 15, 16 ], "texture": "#side" }, + "south": { "uv": [ 1, 12, 15, 16 ], "texture": "#side" }, + "west": { "uv": [ 1, 12, 15, 16 ], "texture": "#side" }, + "east": { "uv": [ 1, 12, 15, 16 ], "texture": "#side" } + } + }, + { + "__comment": "Box1", + "from": [ 2, 4, 2 ], + "to": [ 14, 14, 14 ], + "faces": { + "north": { "uv": [ 2, 2, 14, 12 ], "texture": "#side" }, + "south": { "uv": [ 2, 2, 14, 12 ], "texture": "#side" }, + "west": { "uv": [ 2, 2, 14, 12 ], "texture": "#side" }, + "east": { "uv": [ 2, 2, 14, 12 ], "texture": "#side" } + } + }, + { + "__comment": "Box1", + "from": [ 1, 14, 1 ], + "to": [ 15, 16, 15 ], + "faces": { + "down": { "uv": [ 1, 1, 15, 15 ], "texture": "#texture" }, + "up": { "uv": [ 1, 1, 15, 15 ], "texture": "#texture" }, + "north": { "uv": [ 1, 0, 15, 2 ], "texture": "#side" }, + "south": { "uv": [ 1, 0, 15, 2 ], "texture": "#side" }, + "west": { "uv": [ 1, 0, 15, 2 ], "texture": "#side" }, + "east": { "uv": [ 1, 0, 15, 2 ], "texture": "#side" } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/betterend/models/block/respawn_obelisk_middle.json b/src/main/resources/assets/betterend/models/block/respawn_obelisk_middle.json new file mode 100644 index 00000000..499c8183 --- /dev/null +++ b/src/main/resources/assets/betterend/models/block/respawn_obelisk_middle.json @@ -0,0 +1,34 @@ +{ + "__comment": "Designed by Paulevs with Cubik Studio - https://cubik.studio", + "textures": { + "particle": "betterend:block/amber_block", + "texture": "betterend:block/respawn_obelisk_top_and_side", + "crystal": "betterend:block/aurora_crystal" + }, + "elements": [ + { + "__comment": "Box1", + "from": [ 2, 0, 2 ], + "to": [ 14, 2, 14 ], + "faces": { + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture" }, + "north": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture" }, + "south": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture" }, + "west": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture" }, + "east": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture" } + } + }, + { + "__comment": "Box1", + "from": [ 3, 2, 3 ], + "to": [ 13, 16, 13 ], + "shade": false, + "faces": { + "north": { "uv": [ 3, 0, 13, 14 ], "texture": "#crystal", "tintindex": 0 }, + "south": { "uv": [ 3, 0, 13, 14 ], "texture": "#crystal", "tintindex": 0 }, + "west": { "uv": [ 3, 0, 13, 14 ], "texture": "#crystal", "tintindex": 0 }, + "east": { "uv": [ 3, 0, 13, 14 ], "texture": "#crystal", "tintindex": 0 } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/betterend/models/block/respawn_obelisk_top.json b/src/main/resources/assets/betterend/models/block/respawn_obelisk_top.json new file mode 100644 index 00000000..63a80682 --- /dev/null +++ b/src/main/resources/assets/betterend/models/block/respawn_obelisk_top.json @@ -0,0 +1,47 @@ +{ + "__comment": "Designed by Paulevs with Cubik Studio - https://cubik.studio", + "textures": { + "particle": "betterend:block/amber_block", + "texture": "betterend:block/respawn_obelisk_top_and_side", + "crystal": "betterend:block/aurora_crystal" + }, + "elements": [ + { + "__comment": "Box1", + "from": [ 2, 12, 2 ], + "to": [ 14, 14, 14 ], + "faces": { + "down": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture" }, + "up": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture" }, + "north": { "uv": [ 2, 0, 14, 2 ], "texture": "#texture" }, + "south": { "uv": [ 2, 0, 14, 2 ], "texture": "#texture" }, + "west": { "uv": [ 2, 0, 14, 2 ], "texture": "#texture" }, + "east": { "uv": [ 2, 0, 14, 2 ], "texture": "#texture" } + } + }, + { + "__comment": "Box1", + "from": [ 3, 0, 3 ], + "to": [ 13, 12, 13 ], + "shade": false, + "faces": { + "north": { "uv": [ 3, 4, 13, 16 ], "texture": "#crystal", "tintindex": 0 }, + "south": { "uv": [ 3, 4, 13, 16 ], "texture": "#crystal", "tintindex": 0 }, + "west": { "uv": [ 3, 4, 13, 16 ], "texture": "#crystal", "tintindex": 0 }, + "east": { "uv": [ 3, 4, 13, 16 ], "texture": "#crystal", "tintindex": 0 } + } + }, + { + "__comment": "Box1", + "from": [ 3, 14, 3 ], + "to": [ 13, 16, 13 ], + "faces": { + "up": { "uv": [ 3, 3, 13, 13 ], "texture": "#texture", "cullface": "up" }, + "north": { "uv": [ 3, 0, 13, 2 ], "texture": "#texture" }, + "south": { "uv": [ 3, 0, 13, 2 ], "texture": "#texture" }, + "west": { "uv": [ 3, 0, 13, 2 ], "texture": "#texture" }, + "east": { "uv": [ 3, 0, 13, 2 ], "texture": "#texture" } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/betterend/models/item/respawn_obelisk.json b/src/main/resources/assets/betterend/models/item/respawn_obelisk.json new file mode 100644 index 00000000..e7f6febb --- /dev/null +++ b/src/main/resources/assets/betterend/models/item/respawn_obelisk.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "betterend:item/respawn_obelisk" + } +} diff --git a/src/main/resources/assets/betterend/textures/block/aurora_crystal.png b/src/main/resources/assets/betterend/textures/block/aurora_crystal.png index 560e74b5..1998fb7e 100644 Binary files a/src/main/resources/assets/betterend/textures/block/aurora_crystal.png and b/src/main/resources/assets/betterend/textures/block/aurora_crystal.png differ diff --git a/src/main/resources/assets/betterend/textures/block/respawn_obelisk_bottom_side.png b/src/main/resources/assets/betterend/textures/block/respawn_obelisk_bottom_side.png new file mode 100644 index 00000000..6e528cf4 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/block/respawn_obelisk_bottom_side.png differ diff --git a/src/main/resources/assets/betterend/textures/block/respawn_obelisk_top_and_side.png b/src/main/resources/assets/betterend/textures/block/respawn_obelisk_top_and_side.png new file mode 100644 index 00000000..56269207 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/block/respawn_obelisk_top_and_side.png differ diff --git a/src/main/resources/assets/betterend/textures/item/respawn_obelisk.png b/src/main/resources/assets/betterend/textures/item/respawn_obelisk.png new file mode 100644 index 00000000..05b978d6 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/item/respawn_obelisk.png differ diff --git a/src/main/resources/betterend.mixins.common.json b/src/main/resources/betterend.mixins.common.json index 9308e018..1d7539f8 100644 --- a/src/main/resources/betterend.mixins.common.json +++ b/src/main/resources/betterend.mixins.common.json @@ -24,6 +24,7 @@ "HostileEntityMixin", "LivingEntityMixin", "BoneMealItemMixin", + "PlayerEntityMixin", "SlimeEntityMixin", "BrewingAccessor", "EntityMixin"