Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tree decoration #164

Merged
merged 5 commits into from
Dec 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/xyz/nucleoid/extras/NucleoidExtras.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void onInitialize() {
NEItems.register();
NEEntities.register();
NECriteria.register();
NEPointOfInterestTypes.register();

ChatFilter.register();
CommandAliases.register();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/xyz/nucleoid/extras/lobby/NEBlocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class NEBlocks {
public static final Block IRON_LAUNCH_PAD = new LaunchPadBlock(AbstractBlock.Settings.copy(Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE).strength(100).noCollision(), Blocks.HEAVY_WEIGHTED_PRESSURE_PLATE);

public static final Block CONTRIBUTOR_STATUE = new ContributorStatueBlock(AbstractBlock.Settings.copy(Blocks.SMOOTH_STONE).strength(100));
public static final Block TREE_DECORATION = new TreeDecorationBlock(AbstractBlock.Settings.copy(Blocks.COARSE_DIRT).strength(100));

public static final Block INFINITE_DISPENSER = new InfiniteDispenserBlock(AbstractBlock.Settings.copy(Blocks.DISPENSER).strength(100));
public static final Block INFINITE_DROPPER = new InfiniteDropperBlock(AbstractBlock.Settings.copy(Blocks.DROPPER).strength(100));
Expand Down Expand Up @@ -428,6 +429,7 @@ public class NEBlocks {

public static final BlockEntityType<LaunchPadBlockEntity> LAUNCH_PAD_ENTITY = FabricBlockEntityTypeBuilder.create(LaunchPadBlockEntity::new, GOLD_LAUNCH_PAD, IRON_LAUNCH_PAD).build();
public static final BlockEntityType<ContributorStatueBlockEntity> CONTRIBUTOR_STATUE_ENTITY = FabricBlockEntityTypeBuilder.create(ContributorStatueBlockEntity::new, CONTRIBUTOR_STATUE).build();
public static final BlockEntityType<TreeDecorationBlockEntity> TREE_DECORATION_ENTITY = FabricBlockEntityTypeBuilder.create(TreeDecorationBlockEntity::new, TREE_DECORATION).build();
public static final BlockEntityType<TateroidBlockEntity> TATEROID_ENTITY = FabricBlockEntityTypeBuilder.create(TateroidBlockEntity::new, TATEROID, RED_TATEROID, ORANGE_TATEROID, YELLOW_TATEROID, GREEN_TATEROID, BLUE_TATEROID, PURPLE_TATEROID).build();
public static final BlockEntityType<DaylightDetectorTaterBlockEntity> DAYLIGHT_DETECTOR_TATER_ENTITY = FabricBlockEntityTypeBuilder.create(DaylightDetectorTaterBlockEntity::new, DAYLIGHT_DETECTOR_TATER, INVERTED_DAYLIGHT_DETECTOR_TATER).build();
public static final BlockEntityType<BellTaterBlockEntity> BELL_TATER_ENTITY = FabricBlockEntityTypeBuilder.create(BellTaterBlockEntity::new, BELL_TATER).build();
Expand Down Expand Up @@ -526,6 +528,7 @@ public static void register() {
register("gold_launch_pad", GOLD_LAUNCH_PAD);
register("iron_launch_pad", IRON_LAUNCH_PAD);
register("contributor_statue", CONTRIBUTOR_STATUE);
register("tree_decoration", TREE_DECORATION);
register("infinite_dispenser", INFINITE_DISPENSER);
register("infinite_dropper", INFINITE_DROPPER);
register("snake_block", SNAKE_BLOCK);
Expand Down Expand Up @@ -859,6 +862,7 @@ public static void register() {

registerBlockEntity("launch_pad", LAUNCH_PAD_ENTITY);
registerBlockEntity("contributor_statue", CONTRIBUTOR_STATUE_ENTITY);
registerBlockEntity("tree_decoration", TREE_DECORATION_ENTITY);
registerBlockEntity("tateroid", TATEROID_ENTITY);
registerBlockEntity("daylight_detector_tater", DAYLIGHT_DETECTOR_TATER_ENTITY);
registerBlockEntity("bell_tater", BELL_TATER_ENTITY);
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/xyz/nucleoid/extras/lobby/NEItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@
import net.minecraft.item.Items;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import xyz.nucleoid.extras.NucleoidExtras;
import xyz.nucleoid.extras.NucleoidExtrasConfig;
import xyz.nucleoid.extras.lobby.block.TreeDecorationBlockEntity;
import xyz.nucleoid.extras.lobby.block.tater.TinyPotatoBlock;
import xyz.nucleoid.extras.lobby.item.*;
import xyz.nucleoid.extras.lobby.item.tater.CreativeTaterBoxItem;
Expand All @@ -53,6 +58,7 @@ public class NEItems {
entries.add(NEItems.GOLD_LAUNCH_PAD);
entries.add(NEItems.IRON_LAUNCH_PAD);
entries.add(NEItems.CONTRIBUTOR_STATUE);
entries.add(NEItems.TREE_DECORATION);
entries.add(NEItems.INFINITE_DISPENSER);
entries.add(NEItems.INFINITE_DROPPER);
entries.add(NEItems.SNAKE_BLOCK);
Expand Down Expand Up @@ -103,6 +109,7 @@ public class NEItems {
public static final Item IRON_LAUNCH_PAD = createSimple(NEBlocks.IRON_LAUNCH_PAD, Items.HEAVY_WEIGHTED_PRESSURE_PLATE);

public static final Item CONTRIBUTOR_STATUE = createSimple(NEBlocks.CONTRIBUTOR_STATUE, Items.SMOOTH_STONE);
public static final Item TREE_DECORATION = createSimple(NEBlocks.TREE_DECORATION, Items.COARSE_DIRT);

public static final Item INFINITE_DISPENSER = createSimple(NEBlocks.INFINITE_DISPENSER, Items.DISPENSER);
public static final Item INFINITE_DROPPER = createSimple(NEBlocks.INFINITE_DROPPER, Items.DROPPER);
Expand Down Expand Up @@ -462,6 +469,7 @@ public static void register() {
register("gold_launch_pad", GOLD_LAUNCH_PAD);
register("iron_launch_pad", IRON_LAUNCH_PAD);
register("contributor_statue", CONTRIBUTOR_STATUE);
register("tree_decoration", TREE_DECORATION);
register("infinite_dispenser", INFINITE_DISPENSER);
register("infinite_dropper", INFINITE_DROPPER);
register("snake_block", SNAKE_BLOCK);
Expand Down Expand Up @@ -843,11 +851,19 @@ private static void onPlayerJoin(ServerPlayNetworkHandler handler, PacketSender

private static ActionResult onUseBlock(PlayerEntity player, World world, Hand hand, BlockHitResult hitResult) {
if (!player.getWorld().isClient() && hitResult != null && hand == Hand.MAIN_HAND) {
ItemStack stack = player.getStackInHand(hand);
BlockPos pos = hitResult.getBlockPos();
var stack = player.getStackInHand(hand);
var pos = hitResult.getBlockPos();

PlayerLobbyState state = PlayerLobbyState.get(player);
state.collectTaterFromBlock(world, pos, stack, player);
var lobbyState = PlayerLobbyState.get(player);

if (lobbyState.collectTaterFromBlock(world, pos, stack, player) == ActionResult.PASS && !(stack.getItem() instanceof TaterBoxItem)) {
var serverWorld = (ServerWorld) world;
var blockEntity = TreeDecorationBlockEntity.findNearestTreeDecoration(serverWorld, pos);

if (blockEntity.isPresent() && blockEntity.get().placeOrnament((ServerPlayerEntity) player, serverWorld, hand, hitResult)) {
return ActionResult.SUCCESS;
}
}
}

return ActionResult.PASS;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package xyz.nucleoid.extras.lobby;

import net.fabricmc.fabric.api.object.builder.v1.world.poi.PointOfInterestHelper;
import net.minecraft.block.Block;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.world.poi.PointOfInterestType;
import xyz.nucleoid.extras.NucleoidExtras;

public class NEPointOfInterestTypes {
public static final RegistryKey<PointOfInterestType> TREE_DECORATION = of("tree_decoration");

public static void register() {
register(TREE_DECORATION, 0, 1, NEBlocks.TREE_DECORATION);
}

private static RegistryKey<PointOfInterestType> of(String id) {
return RegistryKey.of(RegistryKeys.POINT_OF_INTEREST_TYPE, NucleoidExtras.identifier(id));
}

private static PointOfInterestType register(RegistryKey<PointOfInterestType> key, int ticketCount, int searchDistance, Block... blocks) {
return PointOfInterestHelper.register(key.getValue(), ticketCount, searchDistance, blocks);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package xyz.nucleoid.extras.lobby.block;

import eu.pb4.polymer.core.api.block.PolymerBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.Blocks;
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 xyz.nucleoid.extras.lobby.NEBlocks;

public class TreeDecorationBlock extends BlockWithEntity implements PolymerBlock {
public TreeDecorationBlock(Settings settings) {
super(settings);
}

@Override
public Block getPolymerBlock(BlockState state) {
return Blocks.COARSE_DIRT;
}

@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new TreeDecorationBlockEntity(pos, state);
}

@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
return world.isClient() ? null : BlockWithEntity.checkType(type, NEBlocks.TREE_DECORATION_ENTITY, TreeDecorationBlockEntity::tick);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package xyz.nucleoid.extras.lobby.block;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.common.base.Predicates;

import eu.pb4.polymer.virtualentity.api.ElementHolder;
import eu.pb4.polymer.virtualentity.api.attachment.BlockBoundAttachment;
import eu.pb4.polymer.virtualentity.api.attachment.HolderAttachment;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtOps;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
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.math.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.event.GameEvent;
import net.minecraft.world.poi.PointOfInterestStorage;
import xyz.nucleoid.extras.lobby.NEBlocks;
import xyz.nucleoid.extras.lobby.NEPointOfInterestTypes;
import xyz.nucleoid.extras.lobby.item.tater.TaterBoxItem;
import xyz.nucleoid.extras.lobby.tree.Ornament;
import xyz.nucleoid.extras.lobby.tree.OrnamentModel;
import xyz.nucleoid.extras.lobby.tree.TreeDecoration;

public class TreeDecorationBlockEntity extends BlockEntity {
private static final Logger LOGGER = LogManager.getLogger(TreeDecorationBlockEntity.class);

private static final String DECORATION_KEY = "decoration";

private static final int SEARCH_RADIUS = 32;

private TreeDecoration data = TreeDecoration.createEmpty();
private final Map<Ornament, OrnamentModel> ornamentsToModels = new HashMap<>();

private final ElementHolder holder = new ElementHolder();
private HolderAttachment attachment;

public TreeDecorationBlockEntity(BlockPos pos, BlockState state) {
super(NEBlocks.TREE_DECORATION_ENTITY, pos, state);
}

private void setData(TreeDecoration data) {
this.data = data;

// Add models that have been added to the data
for (var ornament : data.getOrnaments()) {
if (!this.ornamentsToModels.containsKey(ornament)) {
var model = new OrnamentModel(this, ornament);

model.addToHolder(this.holder);
this.ornamentsToModels.put(ornament, model);
}
}

// Remove models that are no longer in the data
this.ornamentsToModels.entrySet().removeIf(entry -> {
if (!data.getOrnaments().contains(entry.getKey())) {
entry.getValue().removeFromHolder(this.holder);
return true;
}

return false;
});
}

private void addOrnament(Ornament ornament) {
this.setData(this.data.withOrnament(ornament));
this.markDirty();
}

public void removeOrnament(Ornament ornament) {
this.setData(this.data.exceptOrnament(ornament));
this.markDirty();

var pos = this.pos.toCenterPos().add(ornament.offset());

float pitch = 1.3f + world.getRandom().nextFloat() * 0.2f;
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_CHAIN_BREAK, SoundCategory.BLOCKS, 0.5f, pitch);
}

public boolean placeOrnament(ServerPlayerEntity player, ServerWorld world, Hand hand, BlockHitResult hitResult) {
var item = TaterBoxItem.getPrimaryCollectedTater(player).asItem();
if (item == Items.AIR) return false;

var blockPos = hitResult.getBlockPos();

var state = world.getBlockState(blockPos);
if (!state.isIn(this.data.getSupportedBlocks())) return false;

var pos = hitResult.getPos();
var side = hitResult.getSide();

if (side == Direction.UP) {
return false;
} else if (side != Direction.DOWN) {
var belowPos = blockPos.add(side.getOffsetX(), side.getOffsetY() - 1, side.getOffsetZ());

if (world.getBlockState(belowPos).isFullCube(world, belowPos)) {
double minY = blockPos.getY() + OrnamentModel.HEIGHT;
pos = pos.withAxis(Direction.Axis.Y, Math.max(minY, pos.getY()));
}
}

var offset = pos.subtract(this.pos.toCenterPos());

float yaw = player.getYaw() - 180;
float hookYaw = MathHelper.wrapDegrees(player.getRandom().nextFloat() * 360);

this.addOrnament(new Ornament(item, offset, yaw, hookYaw, player.getUuid()));

world.emitGameEvent(player, GameEvent.ENTITY_PLACE, pos);

float pitch = 1.3f + player.getRandom().nextFloat() * 0.2f;
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_CHAIN_PLACE, SoundCategory.BLOCKS, 0.5f, pitch);

return true;
}

@Override
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);

if (nbt.contains(DECORATION_KEY, NbtElement.COMPOUND_TYPE)) {
TreeDecoration.CODEC.parse(NbtOps.INSTANCE, nbt.getCompound(DECORATION_KEY))
.resultOrPartial(LOGGER::error)
.ifPresent(this::setData);
}
}

@Override
protected void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);

TreeDecoration.CODEC.encodeStart(NbtOps.INSTANCE, this.data)
.resultOrPartial(LOGGER::error)
.ifPresent(element -> {
nbt.put(DECORATION_KEY, element);
});
}

@Override
public void markRemoved() {
super.markRemoved();
this.holder.destroy();
}

public static void tick(World world, BlockPos pos, BlockState state, TreeDecorationBlockEntity blockEntity) {
for (var model : blockEntity.ornamentsToModels.values()) {
model.tick();
}

if (blockEntity.attachment == null && world instanceof ServerWorld serverWorld) {
blockEntity.attachment = BlockBoundAttachment.ofTicking(blockEntity.holder, serverWorld, pos);
}
}

public static Optional<TreeDecorationBlockEntity> findNearestTreeDecoration(ServerWorld world, BlockPos pos) {
return world.getPointOfInterestStorage()
.getNearestPosition(poiType -> {
return poiType.matchesKey(NEPointOfInterestTypes.TREE_DECORATION);
}, Predicates.alwaysTrue(), pos, SEARCH_RADIUS, PointOfInterestStorage.OccupationStatus.ANY)
.flatMap(decorationPos -> {
return world.getBlockEntity(decorationPos, NEBlocks.TREE_DECORATION_ENTITY);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.extras.NucleoidExtras;
import xyz.nucleoid.extras.lobby.NEBlocks;
import xyz.nucleoid.extras.lobby.PlayerLobbyState;
import xyz.nucleoid.extras.lobby.block.tater.CubicPotatoBlock;
import xyz.nucleoid.extras.lobby.block.tater.TinyPotatoBlock;
Expand Down Expand Up @@ -234,4 +235,28 @@ public static void setSelectedTater(ItemStack stack, @Nullable Identifier select
tag.putString(SELECTED_TATER_KEY, selectedTaterId.toString());
}
}

public static Block getPrimaryCollectedTater(ServerPlayerEntity player) {
var inventory = player.getInventory();

// Check any tater boxes in the inventory for a selected tater
for (int slot = 0; slot < inventory.size(); slot++) {
var stack = inventory.getStack(slot);

if (stack.getItem() instanceof TaterBoxItem) {
var selectedTater = getSelectedTater(stack);
if (selectedTater != null) return selectedTater;
}
}

var collectedTaters = new ArrayList<>(PlayerLobbyState.get(player).collectedTaters);

if (collectedTaters.isEmpty()) {
// If no taters collected at all, fall back to tiny potato
return NEBlocks.TINY_POTATO;
} else {
// Use a random tater that has already been collected
return Util.getRandom(collectedTaters, player.getRandom());
}
}
}
Loading
Loading