Public Access
Match Legacy handheld book drops with model entity (#85)
Build / build (push) Canceled after 0s
Build / build (push) Canceled after 0s
This commit is contained in:
1 parent
2e5d6e0427
commit
d0af75239a
15 files changed
+331
-12
No files matched your search
@@ -54,6 +54,7 @@ public final class Rivencraft {
|
||||
ModBiomeSources.register(modEventBus);
|
||||
ModChunkGenerators.register(modEventBus);
|
||||
ModBlocks.register(modEventBus);
|
||||
net.dnorberg.rivencraft.registry.ModEntities.register(modEventBus);
|
||||
ModBlockEntities.register(modEventBus);
|
||||
ModItems.register(modEventBus);
|
||||
ModMenus.register(modEventBus);
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package net.dnorberg.rivencraft.book;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.entity.DroppedBookEntity;
|
||||
import net.dnorberg.rivencraft.registry.ModEntities;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.neoforged.neoforge.gametest.GameTestHolder;
|
||||
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
|
||||
|
||||
@GameTestHolder(Rivencraft.MOD_ID)
|
||||
@PrefixGameTestTemplate(false)
|
||||
public final class HandheldBookTravelGameTests {
|
||||
private HandheldBookTravelGameTests() {
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void successfulHandheldTravelLeavesOnlyActiveBookAtOrigin(GameTestHelper helper) {
|
||||
var player = helper.makeMockServerPlayerInLevel();
|
||||
ItemStack book = new ItemStack(ModItems.LINKING_BOOK.get());
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, book);
|
||||
player.getInventory().setItem(1, new ItemStack(Items.DIAMOND));
|
||||
var source = player.position();
|
||||
BlockPos destination = helper.absolutePos(new BlockPos(2, 1, 2));
|
||||
helper.getLevel().setBlock(destination, Blocks.AIR.defaultBlockState(), 2);
|
||||
helper.getLevel().setBlock(destination.above(), Blocks.AIR.defaultBlockState(), 2);
|
||||
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), destination), 0, 0);
|
||||
|
||||
helper.assertValueEqual(LinkingService.travelHandheld(player, InteractionHand.MAIN_HAND, book, target,
|
||||
new LinkProperties(LinkProperties.INTRA_LINKING), false), LinkingService.LinkResult.SUCCESS,
|
||||
"A valid intra-dimensional handheld link must succeed");
|
||||
helper.assertTrue(player.getMainHandItem().isEmpty(), "Only the active book must leave the hand");
|
||||
helper.assertTrue(player.getInventory().getItem(1).is(Items.DIAMOND),
|
||||
"An unrelated inventory item must remain with the player");
|
||||
var drops = helper.getLevel().getEntitiesOfClass(DroppedBookEntity.class, new AABB(source, source).inflate(2.0),
|
||||
entity -> entity.getItem().is(ModItems.LINKING_BOOK.get()));
|
||||
helper.assertValueEqual(drops.size(), 1, "Exactly one recoverable book must remain at the departure point");
|
||||
helper.assertTrue(drops.getFirst().getItem() == book,
|
||||
"The model-rendered entity must retain the exact active book stack");
|
||||
helper.assertTrue(drops.getFirst().copy() instanceof DroppedBookEntity,
|
||||
"The pickup-animation copy must keep the custom renderer's entity type");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void rejectedHandheldTravelKeepsBook(GameTestHelper helper) {
|
||||
var player = helper.makeMockServerPlayerInLevel();
|
||||
ItemStack book = new ItemStack(ModItems.LINKING_BOOK.get());
|
||||
player.setItemInHand(InteractionHand.OFF_HAND, book);
|
||||
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), player.blockPosition()), 0, 0);
|
||||
|
||||
helper.assertValueEqual(LinkingService.travelHandheld(player, InteractionHand.OFF_HAND, book, target,
|
||||
LinkProperties.NONE, false), LinkingService.LinkResult.INTRA_LINKING_REQUIRED,
|
||||
"A same-dimension link without Intra Linking must be rejected");
|
||||
helper.assertTrue(player.getOffhandItem() == book, "The exact rejected book must be restored to its hand");
|
||||
helper.assertTrue(helper.getLevel().getEntitiesOfClass(ItemEntity.class,
|
||||
new AABB(player.position(), player.position()).inflate(2.0),
|
||||
entity -> entity.getItem().is(ModItems.LINKING_BOOK.get())).isEmpty(),
|
||||
"A rejected link must not leave a second copy at the origin");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void disarmDropsOtherInventoryWithoutDuplicatingActiveBook(GameTestHelper helper) {
|
||||
var player = helper.makeMockServerPlayerInLevel();
|
||||
ItemStack book = new ItemStack(ModItems.LINKING_BOOK.get());
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, book);
|
||||
player.getInventory().setItem(1, new ItemStack(Items.DIAMOND));
|
||||
var source = player.position();
|
||||
BlockPos destination = helper.absolutePos(new BlockPos(2, 1, 2));
|
||||
helper.getLevel().setBlock(destination, Blocks.AIR.defaultBlockState(), 2);
|
||||
helper.getLevel().setBlock(destination.above(), Blocks.AIR.defaultBlockState(), 2);
|
||||
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), destination), 0, 0);
|
||||
|
||||
helper.assertValueEqual(LinkingService.travelHandheld(player, InteractionHand.MAIN_HAND, book, target,
|
||||
new LinkProperties(LinkProperties.INTRA_LINKING | LinkProperties.DISARM), false),
|
||||
LinkingService.LinkResult.SUCCESS, "Disarm must not block handheld travel");
|
||||
var drops = helper.getLevel().getEntitiesOfClass(ItemEntity.class, new AABB(source, source).inflate(2.0));
|
||||
helper.assertValueEqual(drops.stream().filter(entity -> entity instanceof DroppedBookEntity).count(),
|
||||
1L, "Disarm must not create a second copy of the active book");
|
||||
helper.assertValueEqual(drops.stream().filter(entity -> entity.getItem().is(Items.DIAMOND)).count(),
|
||||
1L, "Disarm must still drop unrelated inventory items");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void originBookUsesSlowDecayWithoutVanillaDespawn(GameTestHelper helper) {
|
||||
DroppedBookEntity book = new DroppedBookEntity(helper.getLevel(),
|
||||
net.minecraft.world.phys.Vec3.ZERO, new ItemStack(ModItems.LINKING_BOOK.get()));
|
||||
CompoundTag seeded = new CompoundTag();
|
||||
book.saveWithoutId(seeded);
|
||||
seeded.putInt("BookDecayTicks", 9_999);
|
||||
book.readAdditionalSaveData(seeded);
|
||||
book.tick();
|
||||
CompoundTag saved = new CompoundTag();
|
||||
book.saveWithoutId(saved);
|
||||
helper.assertValueEqual(saved.getShort("Age"), (short)-32_768,
|
||||
"The dropped book must not expire after vanilla's five minutes");
|
||||
helper.assertValueEqual(saved.getShort("Health"), (short)4,
|
||||
"A dropped book must lose one item-health point after 10,000 loaded ticks");
|
||||
helper.assertValueEqual(saved.getInt("BookDecayTicks"), 0,
|
||||
"Slow decay must reset its interval and persist across a restart");
|
||||
DroppedBookEntity restored = new DroppedBookEntity(ModEntities.DROPPED_BOOK.get(), helper.getLevel());
|
||||
restored.readAdditionalSaveData(saved);
|
||||
CompoundTag resaved = new CompoundTag();
|
||||
restored.saveWithoutId(resaved);
|
||||
helper.assertTrue(restored.getItem().is(ModItems.LINKING_BOOK.get()),
|
||||
"The custom book entity must retain its book on reload");
|
||||
helper.assertValueEqual(resaved.getShort("Age"), (short)-32_768,
|
||||
"The custom book must remain non-despawning after reload");
|
||||
helper.assertValueEqual(resaved.getShort("Health"), (short)4,
|
||||
"Decay damage must persist after reload");
|
||||
helper.assertTrue(restored.copy().getItem().is(ModItems.LINKING_BOOK.get()),
|
||||
"A pickup-animation copy must retain the book data after reload");
|
||||
helper.succeed();
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,13 @@ import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import net.dnorberg.rivencraft.compat.ImmersivePortalsChunkLoaders;
|
||||
import net.dnorberg.rivencraft.entity.DroppedBookEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
@@ -32,6 +35,36 @@ public final class LinkingService {
|
||||
return travel(player, target, false, properties, false);
|
||||
}
|
||||
|
||||
/** Removes only the active handheld book after a successful link, leaving it at the departure point. */
|
||||
public static LinkResult travelHandheld(ServerPlayer player, InteractionHand hand, ItemStack activeBook,
|
||||
LinkTarget target, LinkProperties properties, boolean requiresFooting) {
|
||||
if (player.getItemInHand(hand) != activeBook || activeBook.isEmpty()) {
|
||||
return LinkResult.NO_SAFE_DESTINATION;
|
||||
}
|
||||
|
||||
ServerLevel sourceLevel = player.serverLevel();
|
||||
Vec3 sourcePosition = player.position();
|
||||
player.setItemInHand(hand, ItemStack.EMPTY);
|
||||
boolean dropped = false;
|
||||
try {
|
||||
LinkResult result = requiresFooting
|
||||
? travelWithFooting(player, target, properties)
|
||||
: travel(player, target, properties);
|
||||
if (result != LinkResult.SUCCESS) return result;
|
||||
|
||||
DroppedBookEntity book = new DroppedBookEntity(sourceLevel, sourcePosition, activeBook);
|
||||
dropped = sourceLevel.addFreshEntity(book);
|
||||
return result;
|
||||
} finally {
|
||||
// Failed validation or a rejected entity spawn must never eat the book.
|
||||
if (!dropped) {
|
||||
if (player.getItemInHand(hand).isEmpty()) player.setItemInHand(hand, activeBook);
|
||||
else if (!player.getInventory().add(activeBook)) player.drop(activeBook, false);
|
||||
}
|
||||
player.containerMenu.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/** Portal travel uses the vanilla transfer if its destination is not yet loaded. */
|
||||
public static LinkResult travelPortal(ServerPlayer player, LinkTarget target, LinkProperties properties) {
|
||||
return travel(player, target, false, properties, true);
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.dnorberg.rivencraft.registry.ModEntities;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
@@ -34,6 +35,7 @@ public final class ClientEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) {
|
||||
event.registerEntityRenderer(ModEntities.DROPPED_BOOK.get(), DroppedBookRenderer::new);
|
||||
event.registerBlockEntityRenderer(ModBlockEntities.BOOK_STAND.get(), BookStandRenderer::new);
|
||||
event.registerBlockEntityRenderer(ModBlockEntities.WRITING_DESK.get(), WritingDeskRenderer::new);
|
||||
event.registerBlockEntityRenderer(ModBlockEntities.STAR_FISSURE.get(), StarFissureRenderer::new);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.dnorberg.rivencraft.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Axis;
|
||||
import net.dnorberg.rivencraft.entity.DroppedBookEntity;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.minecraft.client.model.BookModel;
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/** Renders the same open book model and textures as Rivencraft's book stands. */
|
||||
public final class DroppedBookRenderer extends EntityRenderer<DroppedBookEntity> {
|
||||
private static final ResourceLocation LINKING_TEXTURE = ResourceLocation.fromNamespaceAndPath(
|
||||
"rivencraft", "textures/entity/linking_book.png");
|
||||
private static final ResourceLocation DESCRIPTIVE_TEXTURE = ResourceLocation.fromNamespaceAndPath(
|
||||
"rivencraft", "textures/entity/descriptive_book.png");
|
||||
private final BookModel model;
|
||||
|
||||
public DroppedBookRenderer(EntityRendererProvider.Context context) {
|
||||
super(context);
|
||||
model = new BookModel(context.bakeLayer(ModelLayers.BOOK));
|
||||
shadowRadius = 0.25F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DroppedBookEntity entity, float yaw, float partialTick, PoseStack poseStack,
|
||||
MultiBufferSource buffers, int packedLight) {
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.0F, 0.0625F, 0.0F);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(yaw + 90.0F));
|
||||
// The opened model's covers rotate toward the YZ plane. As in Legacy's
|
||||
// dropped-book renderer, a Z-axis quarter-turn lays the pages skyward.
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(90.0F));
|
||||
poseStack.scale(0.8F, 0.8F, 0.8F);
|
||||
model.setupAnim(0.0F, 0.0F, 0.0F, 1.2F);
|
||||
VertexConsumer vertices = buffers.getBuffer(RenderType.entitySolid(getTextureLocation(entity)));
|
||||
model.render(poseStack, vertices, packedLight, net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY, -1);
|
||||
poseStack.popPose();
|
||||
super.render(entity, yaw, partialTick, poseStack, buffers, packedLight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(DroppedBookEntity entity) {
|
||||
return entity.getItem().is(ModItems.DESCRIPTIVE_BOOK.get()) ? DESCRIPTIVE_TEXTURE : LINKING_TEXTURE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.dnorberg.rivencraft.entity;
|
||||
|
||||
import net.dnorberg.rivencraft.registry.ModEntities;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/** A recoverable, model-rendered book left behind by handheld linking. */
|
||||
public final class DroppedBookEntity extends ItemEntity {
|
||||
private static final String DECAY_TICKS_TAG = "BookDecayTicks";
|
||||
private static final int SLOW_DECAY_INTERVAL = 10_000;
|
||||
private int decayTicks;
|
||||
|
||||
public DroppedBookEntity(EntityType<DroppedBookEntity> type, Level level) {
|
||||
super(type, level);
|
||||
}
|
||||
|
||||
public DroppedBookEntity(Level level, Vec3 position, ItemStack stack) {
|
||||
this(ModEntities.DROPPED_BOOK.get(), level);
|
||||
setPos(position.x, position.y + 0.25, position.z);
|
||||
setDeltaMovement(Vec3.ZERO);
|
||||
setItem(stack);
|
||||
setUnlimitedLifetime();
|
||||
setPickUpDelay(20);
|
||||
}
|
||||
|
||||
/** Vanilla's pickup particle calls ItemEntity.copy() and renders the returned entity. */
|
||||
@Override
|
||||
public DroppedBookEntity copy() {
|
||||
DroppedBookEntity copy = new DroppedBookEntity(ModEntities.DROPPED_BOOK.get(), level());
|
||||
copy.copyPosition(this);
|
||||
copy.setItem(getItem().copy());
|
||||
copy.setUnlimitedLifetime();
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (level().isClientSide || isRemoved()) return;
|
||||
if (++decayTicks >= SLOW_DECAY_INTERVAL) {
|
||||
decayTicks = 0;
|
||||
hurt(damageSources().starve(), 1.0F);
|
||||
}
|
||||
if (!isRemoved() && isInWaterOrRain()) hurt(damageSources().drown(), 1.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt(DECAY_TICKS_TAG, decayTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
decayTicks = Math.max(0, Math.min(tag.getInt(DECAY_TICKS_TAG), SLOW_DECAY_INTERVAL - 1));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
@@ -41,14 +42,18 @@ public final class DescriptiveBookItem extends Item {
|
||||
}
|
||||
|
||||
public static boolean useWrittenBook(ServerPlayer player, ItemStack stack) {
|
||||
return useWrittenBook(player, stack, false);
|
||||
return useWrittenBook(player, stack, false, null);
|
||||
}
|
||||
|
||||
public static boolean useHandheldBook(ServerPlayer player, InteractionHand hand) {
|
||||
return useWrittenBook(player, player.getItemInHand(hand), false, hand);
|
||||
}
|
||||
|
||||
public static boolean usePortalBook(ServerPlayer player, ItemStack stack) {
|
||||
return useWrittenBook(player, stack, true);
|
||||
return useWrittenBook(player, stack, true, null);
|
||||
}
|
||||
|
||||
private static boolean useWrittenBook(ServerPlayer player, ItemStack stack, boolean portal) {
|
||||
private static boolean useWrittenBook(ServerPlayer player, ItemStack stack, boolean portal, InteractionHand hand) {
|
||||
AgeReference age = stack.get(ModDataComponents.AGE_REFERENCE.get());
|
||||
if (age == null) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.unwritten"), true);
|
||||
@@ -68,7 +73,8 @@ public final class DescriptiveBookItem extends Item {
|
||||
.forDescriptiveBook();
|
||||
LinkingService.LinkResult result = portal
|
||||
? LinkingService.travelDescriptivePortal(player, ageTarget, properties)
|
||||
: LinkingService.travelWithFooting(player, ageTarget, properties);
|
||||
: hand == null ? LinkingService.travelWithFooting(player, ageTarget, properties)
|
||||
: LinkingService.travelHandheld(player, hand, stack, ageTarget, properties, true);
|
||||
if (result != LinkingService.LinkResult.SUCCESS) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.unsafe"), true);
|
||||
return false;
|
||||
|
||||
@@ -43,14 +43,19 @@ public final class LinkingBookItem extends Item {
|
||||
}
|
||||
|
||||
public static InteractionResultHolder<ItemStack> useBoundBook(ServerPlayer player, ItemStack stack) {
|
||||
return useBoundBook(player, stack, false);
|
||||
return useBoundBook(player, stack, false, null);
|
||||
}
|
||||
|
||||
public static InteractionResultHolder<ItemStack> useHandheldBook(ServerPlayer player, InteractionHand hand) {
|
||||
return useBoundBook(player, player.getItemInHand(hand), false, hand);
|
||||
}
|
||||
|
||||
public static InteractionResultHolder<ItemStack> usePortalBook(ServerPlayer player, ItemStack stack) {
|
||||
return useBoundBook(player, stack, true);
|
||||
return useBoundBook(player, stack, true, null);
|
||||
}
|
||||
|
||||
private static InteractionResultHolder<ItemStack> useBoundBook(ServerPlayer player, ItemStack stack, boolean portal) {
|
||||
private static InteractionResultHolder<ItemStack> useBoundBook(ServerPlayer player, ItemStack stack, boolean portal,
|
||||
InteractionHand hand) {
|
||||
LinkTarget target = stack.get(ModDataComponents.LINK_TARGET.get());
|
||||
if (target == null) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.linking_book.unbound"), true);
|
||||
@@ -62,7 +67,8 @@ public final class LinkingBookItem extends Item {
|
||||
LinkProperties properties = stack.getOrDefault(ModDataComponents.LINK_PROPERTIES.get(), LinkProperties.NONE);
|
||||
LinkingService.LinkResult result = portal
|
||||
? LinkingService.travelPortal(player, target, properties)
|
||||
: LinkingService.travel(player, target, properties);
|
||||
: hand == null ? LinkingService.travel(player, target, properties)
|
||||
: LinkingService.travelHandheld(player, hand, stack, target, properties, false);
|
||||
if (result == LinkingService.LinkResult.SUCCESS) {
|
||||
LinkVisualEffects.play(sourceLevel, sourcePosition, player, properties, portal);
|
||||
ProgressionAdvancements.awardAgeTravel(player);
|
||||
|
||||
@@ -211,7 +211,7 @@ public final class ModNetworking {
|
||||
|
||||
ItemStack stack = player.getItemInHand(hand(payload.offhand()));
|
||||
if (stack.is(ModItems.LINKING_BOOK.get()) && stack.has(ModDataComponents.LINK_TARGET.get())) {
|
||||
LinkingBookItem.useBoundBook(player, stack);
|
||||
LinkingBookItem.useHandheldBook(player, hand(payload.offhand()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,8 @@ public final class ModNetworking {
|
||||
}
|
||||
|
||||
if (stack.is(ModItems.DESCRIPTIVE_BOOK.get())) {
|
||||
DescriptiveBookItem.useWrittenBook(player, stack);
|
||||
if (payload.fromStand()) DescriptiveBookItem.useWrittenBook(player, stack);
|
||||
else DescriptiveBookItem.useHandheldBook(player, hand(payload.offhand()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.dnorberg.rivencraft.registry;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.entity.DroppedBookEntity;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
public final class ModEntities {
|
||||
private static final DeferredRegister<EntityType<?>> ENTITIES =
|
||||
DeferredRegister.create(Registries.ENTITY_TYPE, Rivencraft.MOD_ID);
|
||||
|
||||
public static final DeferredHolder<EntityType<?>, EntityType<DroppedBookEntity>> DROPPED_BOOK =
|
||||
ENTITIES.register("dropped_book", () -> EntityType.Builder
|
||||
.<DroppedBookEntity>of(DroppedBookEntity::new, MobCategory.MISC)
|
||||
.sized(0.65F, 0.4F)
|
||||
.clientTrackingRange(10)
|
||||
.updateInterval(20)
|
||||
.build(Rivencraft.MOD_ID + ":dropped_book"));
|
||||
|
||||
private ModEntities() {
|
||||
}
|
||||
|
||||
public static void register(IEventBus bus) {
|
||||
ENTITIES.register(bus);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user