-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add tree decoration * Allow players to remove ornaments * Allow players to interact with ornaments to see additional details * Restrict ornament placement positions that would intersect with a block * Allow customizing the block tag that ornaments can be placed on
- Loading branch information
Showing
11 changed files
with
546 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/main/java/xyz/nucleoid/extras/lobby/NEPointOfInterestTypes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/main/java/xyz/nucleoid/extras/lobby/block/TreeDecorationBlock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
181 changes: 181 additions & 0 deletions
181
src/main/java/xyz/nucleoid/extras/lobby/block/TreeDecorationBlockEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.