diff --git a/src/main/java/dev/hephaestus/glowcase/Glowcase.java b/src/main/java/dev/hephaestus/glowcase/Glowcase.java index ef6ef5d..e0339b7 100644 --- a/src/main/java/dev/hephaestus/glowcase/Glowcase.java +++ b/src/main/java/dev/hephaestus/glowcase/Glowcase.java @@ -2,22 +2,8 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import dev.hephaestus.glowcase.block.HyperlinkBlock; -import dev.hephaestus.glowcase.block.ItemAcceptorBlock; -import dev.hephaestus.glowcase.block.ItemDisplayBlock; -import dev.hephaestus.glowcase.block.OutlineBlock; -import dev.hephaestus.glowcase.block.ParticleDisplayBlock; -import dev.hephaestus.glowcase.block.PopupBlock; -import dev.hephaestus.glowcase.block.SpriteBlock; -import dev.hephaestus.glowcase.block.TextBlock; -import dev.hephaestus.glowcase.block.entity.HyperlinkBlockEntity; -import dev.hephaestus.glowcase.block.entity.ItemAcceptorBlockEntity; -import dev.hephaestus.glowcase.block.entity.ItemDisplayBlockEntity; -import dev.hephaestus.glowcase.block.entity.OutlineBlockEntity; -import dev.hephaestus.glowcase.block.entity.ParticleDisplayBlockEntity; -import dev.hephaestus.glowcase.block.entity.PopupBlockEntity; -import dev.hephaestus.glowcase.block.entity.SpriteBlockEntity; -import dev.hephaestus.glowcase.block.entity.TextBlockEntity; +import dev.hephaestus.glowcase.block.*; +import dev.hephaestus.glowcase.block.entity.*; import dev.hephaestus.glowcase.compat.PolydexCompatibility; import dev.hephaestus.glowcase.item.LockItem; import net.fabricmc.api.ModInitializer; @@ -79,6 +65,10 @@ public class Glowcase implements ModInitializer { public static final Supplier LOCK_ITEM = registerItem("lock", () -> new LockItem(new Item.Settings())); + public static final Supplier ENTITY_DISPLAY_BLOCK = registerBlock("entity_display_block", EntityDisplayBlock::new); + public static final Supplier ENTITY_DISPLAY_BLOCK_ITEM = registerItem("entity_display_block", () -> new BlockItem(ENTITY_DISPLAY_BLOCK.get(), new Item.Settings())); + public static final Supplier> ENTITY_DISPLAY_BLOCK_ENTITY = registerBlockEntity("entity_display_block", () -> BlockEntityType.Builder.create(EntityDisplayBlockEntity::new, ENTITY_DISPLAY_BLOCK.get()).build(null)); + public static final Supplier ITEM_GROUP = registerItemGroup("items", () -> FabricItemGroup.builder() .displayName(Text.translatable("itemGroup.glowcase.items")) .icon(() -> new ItemStack(Items.GLOWSTONE)) @@ -87,6 +77,7 @@ public class Glowcase implements ModInitializer { entries.add(SPRITE_BLOCK_ITEM.get()); entries.add(OUTLINE_BLOCK_ITEM.get()); entries.add(PARTICLE_DISPLAY_ITEM.get()); + entries.add(ENTITY_DISPLAY_BLOCK_ITEM.get()); entries.add(ITEM_DISPLAY_BLOCK_ITEM.get()); entries.add(ITEM_ACCEPTOR_BLOCK_ITEM.get()); entries.add(HYPERLINK_BLOCK_ITEM.get()); diff --git a/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java b/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java index 59efac1..ce9ba99 100644 --- a/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java +++ b/src/main/java/dev/hephaestus/glowcase/GlowcaseCommonProxy.java @@ -42,4 +42,8 @@ public void openParticleDisplayBlockEditScreen(BlockPos pos) { public void openItemAcceptorBlockEditScreen(BlockPos pos) { //No-op } + + public void openEntityDisplayBlockEditScreen(BlockPos pos) { + //No-op + } } diff --git a/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java b/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java index 4a4ff36..b8f5dcb 100644 --- a/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java +++ b/src/main/java/dev/hephaestus/glowcase/GlowcaseNetworking.java @@ -1,13 +1,6 @@ package dev.hephaestus.glowcase; -import dev.hephaestus.glowcase.packet.C2SEditHyperlinkBlock; -import dev.hephaestus.glowcase.packet.C2SEditItemAcceptorBlock; -import dev.hephaestus.glowcase.packet.C2SEditItemDisplayBlock; -import dev.hephaestus.glowcase.packet.C2SEditOutlineBlock; -import dev.hephaestus.glowcase.packet.C2SEditParticleDisplayBlock; -import dev.hephaestus.glowcase.packet.C2SEditPopupBlock; -import dev.hephaestus.glowcase.packet.C2SEditSpriteBlock; -import dev.hephaestus.glowcase.packet.C2SEditTextBlock; +import dev.hephaestus.glowcase.packet.*; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -21,6 +14,7 @@ public static void init() { PayloadTypeRegistry.playC2S().register(C2SEditOutlineBlock.ID, C2SEditOutlineBlock.PACKET_CODEC); PayloadTypeRegistry.playC2S().register(C2SEditParticleDisplayBlock.ID, C2SEditParticleDisplayBlock.PACKET_CODEC); PayloadTypeRegistry.playC2S().register(C2SEditItemAcceptorBlock.ID, C2SEditItemAcceptorBlock.PACKET_CODEC); + PayloadTypeRegistry.playC2S().register(C2SEditEntityDisplayBlock.ID, C2SEditEntityDisplayBlock.PACKET_CODEC); ServerPlayNetworking.registerGlobalReceiver(C2SEditHyperlinkBlock.ID, C2SEditHyperlinkBlock::receive); ServerPlayNetworking.registerGlobalReceiver(C2SEditItemDisplayBlock.ID, C2SEditItemDisplayBlock::receive); @@ -30,5 +24,6 @@ public static void init() { ServerPlayNetworking.registerGlobalReceiver(C2SEditOutlineBlock.ID, C2SEditOutlineBlock::receive); ServerPlayNetworking.registerGlobalReceiver(C2SEditParticleDisplayBlock.ID, C2SEditParticleDisplayBlock::receive); ServerPlayNetworking.registerGlobalReceiver(C2SEditItemAcceptorBlock.ID, C2SEditItemAcceptorBlock::receive); + ServerPlayNetworking.registerGlobalReceiver(C2SEditEntityDisplayBlock.ID, C2SEditEntityDisplayBlock::receive); } } diff --git a/src/main/java/dev/hephaestus/glowcase/block/EntityDisplayBlock.java b/src/main/java/dev/hephaestus/glowcase/block/EntityDisplayBlock.java new file mode 100644 index 0000000..735ee32 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/block/EntityDisplayBlock.java @@ -0,0 +1,115 @@ +package dev.hephaestus.glowcase.block; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.EntityDisplayBlockEntity; +import net.minecraft.block.Block; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShapeContext; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemStack; +import net.minecraft.item.SpawnEggItem; +import net.minecraft.item.tooltip.TooltipType; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.Properties; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Formatting; +import net.minecraft.util.Hand; +import net.minecraft.util.ItemActionResult; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class EntityDisplayBlock extends GlowcaseBlock implements BlockEntityProvider { + private static final VoxelShape OUTLINE = VoxelShapes.cuboid(0.25, 0.25, 0.25, 0.75, 0.75, 0.75); + + public EntityDisplayBlock() { + super(); + this.setDefaultState(this.getDefaultState().with(Properties.ROTATION, 0)); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder); + builder.add(Properties.ROTATION); + } + + @Override + public BlockState getPlacementState(ItemPlacementContext ctx) { + return this.getDefaultState().with(Properties.ROTATION, MathHelper.floor((double) ((ctx.getPlayerYaw()) * 16.0F / 360.0F) + 0.5D) & 15); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return OUTLINE; + } + + @Override + protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { + if (!(world.getBlockEntity(pos) instanceof EntityDisplayBlockEntity be)) return ActionResult.CONSUME; + + if (be.canGiveTo(player)) { + if (!world.isClient) be.giveTo(player); + return ActionResult.SUCCESS; + } + + return ActionResult.CONSUME; + } + + @Override + protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (!(world.getBlockEntity(pos) instanceof EntityDisplayBlockEntity be)) return ItemActionResult.CONSUME; + + if (canEditGlowcase(player, pos)) { + boolean holdingGlowcaseItem = stack.isIn(Glowcase.ITEM_TAG); + boolean holdingSameAsDisplay = ItemStack.areItemsEqual(be.getDisplayedStack(), stack); + boolean isSpawnEgg = stack.getItem() instanceof SpawnEggItem; + + if (!be.hasItem() && isSpawnEgg) { + if (!world.isClient) be.setStack(stack); + return ItemActionResult.SUCCESS; + } else if (holdingSameAsDisplay) { + if (world.isClient) Glowcase.proxy.openEntityDisplayBlockEditScreen(pos); + return ItemActionResult.SUCCESS; + } else if (holdingGlowcaseItem) { + if (!world.isClient) be.setStack(ItemStack.EMPTY); + return ItemActionResult.SUCCESS; + } + } + + return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + @Nullable + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new EntityDisplayBlockEntity(pos, state); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { + return checkType(type, Glowcase.ENTITY_DISPLAY_BLOCK_ENTITY.get(), EntityDisplayBlockEntity::tick); + } + + @Override + public void appendTooltip(ItemStack itemStack, Item.TooltipContext context, List tooltip, TooltipType options) { + tooltip.add(Text.translatable("block.glowcase.entity_display_block.tooltip.0").formatted(Formatting.GRAY)); + tooltip.add(Text.translatable("block.glowcase.entity_display_block.tooltip.1").formatted(Formatting.DARK_GRAY)); + tooltip.add(Text.translatable("block.glowcase.entity_display_block.tooltip.2").formatted(Formatting.DARK_GRAY)); + } + +} diff --git a/src/main/java/dev/hephaestus/glowcase/block/entity/EntityDisplayBlockEntity.java b/src/main/java/dev/hephaestus/glowcase/block/entity/EntityDisplayBlockEntity.java new file mode 100644 index 0000000..0ef36a0 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/block/entity/EntityDisplayBlockEntity.java @@ -0,0 +1,105 @@ +package dev.hephaestus.glowcase.block.entity; + +import dev.hephaestus.glowcase.Glowcase; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.SpawnEggItem; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec2f; +import net.minecraft.world.World; + +public class EntityDisplayBlockEntity extends ItemDisplayBlockEntity { + + private Entity displayEntity = null; + + public boolean tickEntity = false; + public float displayScale = 0.5f; + public boolean shouldEditScale = true; + + public EntityDisplayBlockEntity(BlockPos pos, BlockState state) { + super(Glowcase.ENTITY_DISPLAY_BLOCK_ENTITY.get(), pos, state); + } + + @Override + public void writeNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { + super.writeNbt(tag, registryLookup); + + tag.putBoolean("tick_entity", this.tickEntity); + tag.putFloat("display_scale", this.displayScale); + tag.putBoolean("scale_edited", this.shouldEditScale); + } + + @Override + public void readNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) { + this.clearDisplayEntity(); + super.readNbt(tag, registryLookup); + + this.tickEntity = tag.getBoolean("tick_entity"); + this.displayScale = tag.getFloat("display_scale"); + this.shouldEditScale = tag.getBoolean("scale_edited"); + } + + @Override + public void setStack(ItemStack stack) { + super.setStack(stack); + this.setShouldEditScale(true); + this.clearDisplayEntity(); + this.markDirty(); + } + + public void setShouldEditScale(boolean shouldEditScale) { + this.shouldEditScale = shouldEditScale; + this.markDirty(); + } + + private void clearDisplayEntity() { + this.displayEntity = null; + } + + public Entity getDisplayEntity() { + if (this.displayEntity == null && this.world != null && this.stack.getItem() instanceof SpawnEggItem eggItem) { + this.displayEntity = eggItem.getEntityType(this.stack).create(this.world); + if(shouldEditScale && this.displayEntity != null) { + //make the default scale of the entity the same size as the block, + //but only if the player hasn't edited the scale value themselves + float calcScale = displayEntity.getHeight() >= displayEntity.getWidth() ? 1F / displayEntity.getHeight() : 0.5F; + float roundedScale = (float) Math.round(calcScale / 0.125f) * 0.125f; + if(Math.abs(roundedScale) > 3) { + roundedScale = 0.5f; //just in case + } + this.displayScale = roundedScale; + } + } + + return this.displayEntity; + } + + public static Vec2f getPitchAndYaw(Entity camera, EntityDisplayBlockEntity entityDisplay, float delta) { + BlockPos pos = entityDisplay.getPos(); + float displayScale = entityDisplay.displayScale; + float displayedEntityHeight = entityDisplay.displayEntity == null ? 0 : entityDisplay.displayEntity.getHeight(); + double d = pos.getX() - camera.getLerpedPos(delta).x + 0.5; + double e = pos.getY() + (displayScale * displayedEntityHeight - 0.5) - camera.getEyeY() + 0.5; + double f = pos.getZ() - camera.getLerpedPos(delta).z + 0.5; + double g = MathHelper.sqrt((float) (d * d + f * f)); + + float pitch = (float) ((-MathHelper.atan2(e, g))); + float yaw = (float) (-MathHelper.atan2(f, d) + Math.PI / 2); + + return new Vec2f(pitch, yaw); + } + + public static void tick(World world, BlockPos blockPos, BlockState state, EntityDisplayBlockEntity blockEntity) { + if(blockEntity.getDisplayEntity() != null) { + ++blockEntity.displayEntity.age; +// if(blockEntity.tickEntity) { +// blockEntity.displayEntity.tick(); +// } + } + } + +} diff --git a/src/main/java/dev/hephaestus/glowcase/block/entity/ItemDisplayBlockEntity.java b/src/main/java/dev/hephaestus/glowcase/block/entity/ItemDisplayBlockEntity.java index 05194f7..de95daa 100644 --- a/src/main/java/dev/hephaestus/glowcase/block/entity/ItemDisplayBlockEntity.java +++ b/src/main/java/dev/hephaestus/glowcase/block/entity/ItemDisplayBlockEntity.java @@ -3,6 +3,7 @@ import dev.hephaestus.glowcase.Glowcase; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; @@ -20,13 +21,14 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec2f; +import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.Set; import java.util.UUID; public class ItemDisplayBlockEntity extends BlockEntity implements Inventory { - private ItemStack stack = ItemStack.EMPTY; + protected ItemStack stack = ItemStack.EMPTY; public RotationType rotationType = RotationType.TRACKING; public GivesItem givesItem = GivesItem.YES; @@ -36,8 +38,12 @@ public class ItemDisplayBlockEntity extends BlockEntity implements Inventory { public float yaw; public Set givenTo = new HashSet<>(); + public ItemDisplayBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + public ItemDisplayBlockEntity(BlockPos pos, BlockState state) { - super(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY.get(), pos, state); + this(Glowcase.ITEM_DISPLAY_BLOCK_ENTITY.get(), pos, state); } @Override @@ -202,6 +208,10 @@ public static Vec2f getPitchAndYaw(Entity camera, BlockPos pos, float delta) { return new Vec2f(pitch, yaw); } + public static void tick(World world, BlockPos blockPos, BlockState state, ItemDisplayBlockEntity blockEntity) { + //does nothing right now + } + public enum RotationType { LOCKED, TRACKING, HORIZONTAL, BILLBOARD } diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java index a8a9636..3e0fae4 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClient.java @@ -1,15 +1,7 @@ package dev.hephaestus.glowcase.client; import dev.hephaestus.glowcase.Glowcase; -import dev.hephaestus.glowcase.client.render.block.entity.BakedBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.HyperlinkBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.ItemAcceptorBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.ItemDisplayBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.OutlineBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.ParticleDisplayBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.PopupBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.SpriteBlockEntityRenderer; -import dev.hephaestus.glowcase.client.render.block.entity.TextBlockEntityRenderer; +import dev.hephaestus.glowcase.client.render.block.entity.*; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.rendering.v1.InvalidateRenderStateCallback; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; @@ -28,6 +20,7 @@ public void onInitializeClient() { BlockEntityRendererFactories.register(Glowcase.OUTLINE_BLOCK_ENTITY.get(), OutlineBlockEntityRenderer::new); BlockEntityRendererFactories.register(Glowcase.PARTICLE_DISPLAY_BLOCK_ENTITY.get(), ParticleDisplayBlockEntityRenderer::new); BlockEntityRendererFactories.register(Glowcase.ITEM_ACCEPTOR_BLOCK_ENTITY.get(), ItemAcceptorBlockEntityRenderer::new); + BlockEntityRendererFactories.register(Glowcase.ENTITY_DISPLAY_BLOCK_ENTITY.get(), EntityDisplayBlockEntityRenderer::new); WorldRenderEvents.AFTER_TRANSLUCENT.register(BakedBlockEntityRenderer.Manager::render); InvalidateRenderStateCallback.EVENT.register(BakedBlockEntityRenderer.Manager::reset); diff --git a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java index 6545111..68fc06d 100644 --- a/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java +++ b/src/main/java/dev/hephaestus/glowcase/client/GlowcaseClientProxy.java @@ -97,4 +97,12 @@ public void openItemAcceptorBlockEditScreen(BlockPos pos) { MinecraftClient.getInstance().setScreen(new ItemAcceptorBlockEditScreen(be)); } } + + @Override + public void openEntityDisplayBlockEditScreen(BlockPos pos) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world != null && client.world.getBlockEntity(pos) instanceof EntityDisplayBlockEntity be) { + MinecraftClient.getInstance().setScreen(new EntityDisplayEditScreen(be)); + } + } } diff --git a/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/EntityDisplayEditScreen.java b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/EntityDisplayEditScreen.java new file mode 100644 index 0000000..d69a8b3 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/gui/screen/ingame/EntityDisplayEditScreen.java @@ -0,0 +1,111 @@ +package dev.hephaestus.glowcase.client.gui.screen.ingame; + +import dev.hephaestus.glowcase.block.entity.EntityDisplayBlockEntity; +import dev.hephaestus.glowcase.packet.C2SEditEntityDisplayBlock; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec2f; + +public class EntityDisplayEditScreen extends GlowcaseScreen { + private final EntityDisplayBlockEntity displayBlock; + + private ButtonWidget givesItemButton; + private ButtonWidget rotationTypeButton; + private ButtonWidget showNameButton; + private ButtonWidget offsetButton; + private ButtonWidget tickEntityButton; + + private int scaleTextX = 0; + private int scaleTextY = 0; + + public EntityDisplayEditScreen(EntityDisplayBlockEntity displayBlock) { + this.displayBlock = displayBlock; + } + + @Override + public void init() { + super.init(); + + if (this.client != null) { + int padding = width / 100; + int individualPadding = padding / 2; + int centerW = width / 2; + int centerH = height / 2; + + this.givesItemButton = ButtonWidget.builder(Text.stringifiedTranslatable("gui.glowcase.gives_item", this.displayBlock.givesItem), (action) -> { + this.displayBlock.cycleGiveType(); + this.givesItemButton.setMessage(Text.stringifiedTranslatable("gui.glowcase.gives_item", this.displayBlock.givesItem)); + editItemDisplayBlock(true); + }).dimensions(centerW - 75, centerH - 60 - individualPadding * 2, 150, 20).build(); + + this.rotationTypeButton = ButtonWidget.builder(Text.stringifiedTranslatable("gui.glowcase.rotation_type", this.displayBlock.rotationType), (action) -> { + this.displayBlock.cycleRotationType(this.client.player); + this.rotationTypeButton.setMessage(Text.stringifiedTranslatable("gui.glowcase.rotation_type", this.displayBlock.rotationType)); + editItemDisplayBlock(true); + }).dimensions(centerW - 75, centerH - 40 - individualPadding, 150, 20).build(); + + this.showNameButton = ButtonWidget.builder(Text.translatable("gui.glowcase.show_name", this.displayBlock.showName), (action) -> { + this.displayBlock.showName = !this.displayBlock.showName; + this.showNameButton.setMessage(Text.translatable("gui.glowcase.show_name", this.displayBlock.showName)); + editItemDisplayBlock(false); + }).dimensions(centerW - 75, centerH - 20, 150, 20).build(); + + this.offsetButton = ButtonWidget.builder(Text.stringifiedTranslatable("gui.glowcase.offset_value", this.displayBlock.offset), (action) -> { + this.displayBlock.cycleOffset(); + this.offsetButton.setMessage(Text.stringifiedTranslatable("gui.glowcase.offset_value", this.displayBlock.offset)); + editItemDisplayBlock(true); + }).dimensions(centerW - 75, centerH + individualPadding, 150, 20).build(); + + this.tickEntityButton = ButtonWidget.builder(Text.translatable("gui.glowcase.tick_entity", this.displayBlock.tickEntity), (action) -> { + this.displayBlock.tickEntity = !this.displayBlock.tickEntity; + this.tickEntityButton.setMessage(Text.translatable("gui.glowcase.tick_entity", this.displayBlock.tickEntity)); + editItemDisplayBlock(false); + }).dimensions(centerW - 75, centerH + 20 + padding, 150, 20).build(); + + this.scaleTextX = centerW - 75; +// this.scaleTextY = centerH + 40 + padding * 2; + this.scaleTextY = centerH + 20 + padding; //adjust to no tick entity button + + ButtonWidget decreaseSize = ButtonWidget.builder(Text.literal("-"), action -> { + if(this.displayBlock.displayScale > -3) { + this.displayBlock.displayScale -= (float) Math.max(0, 0.125); + editItemDisplayBlock(true); + } + }).dimensions(scaleTextX + 110, scaleTextY, 20, 20).build(); + //150 (button length) - 40 (change size button length) + + ButtonWidget increaseSize = ButtonWidget.builder(Text.literal("+"), action -> { + if(this.displayBlock.displayScale < 3) { + this.displayBlock.displayScale += 0.125F; + editItemDisplayBlock(true); + } + }).dimensions(scaleTextX + 132, scaleTextY, 20, 20).build(); + //150 (button length) - 20 (change size button length) + 2 (padding) + + this.addDrawableChild(this.givesItemButton); + this.addDrawableChild(this.rotationTypeButton); + this.addDrawableChild(this.showNameButton); + this.addDrawableChild(this.offsetButton); +// this.addDrawableChild(this.tickEntityButton); + this.addDrawableChild(decreaseSize); + this.addDrawableChild(increaseSize); + } + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + context.drawTextWithShadow(client.textRenderer, Text.translatable("gui.glowcase.scale_value", this.displayBlock.displayScale), scaleTextX, scaleTextY + 6, 0xFFFFFFFF); + } + + private void editItemDisplayBlock(boolean updatePitchAndYaw) { + if (updatePitchAndYaw && MinecraftClient.getInstance().getCameraEntity() != null) { + Vec2f pitchAndYaw = EntityDisplayBlockEntity.getPitchAndYaw(MinecraftClient.getInstance().getCameraEntity(), displayBlock, 0); + displayBlock.pitch = pitchAndYaw.x; + displayBlock.yaw = pitchAndYaw.y; + } + C2SEditEntityDisplayBlock.of(displayBlock).send(); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/EntityDisplayBlockEntityRenderer.java b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/EntityDisplayBlockEntityRenderer.java new file mode 100644 index 0000000..2ed6e35 --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/client/render/block/entity/EntityDisplayBlockEntityRenderer.java @@ -0,0 +1,117 @@ +package dev.hephaestus.glowcase.client.render.block.entity; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.EntityDisplayBlockEntity; +import dev.hephaestus.glowcase.block.entity.ItemDisplayBlockEntity; +import dev.hephaestus.glowcase.client.util.BlockEntityRenderUtil; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer.TextLayerType; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.state.property.Properties; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.RotationAxis; +import net.minecraft.util.math.Vec2f; + +public record EntityDisplayBlockEntityRenderer(BlockEntityRendererFactory.Context context) implements BlockEntityRenderer { + public static Identifier ITEM_TEXTURE = Glowcase.id("textures/item/entity_display_block.png"); + + @Override + public void render(EntityDisplayBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { + Entity camera = MinecraftClient.getInstance().getCameraEntity(); + + if (camera == null) return; + + matrices.push(); + matrices.translate(0.5D, 0D, 0.5D); + + float yaw = 0F; + float pitch = 0F; + + switch (entity.rotationType) { + case TRACKING -> { + Vec2f pitchAndYaw = EntityDisplayBlockEntity.getPitchAndYaw(camera, entity, tickDelta); + pitch = pitchAndYaw.x; + yaw = pitchAndYaw.y; + matrices.multiply(RotationAxis.POSITIVE_Y.rotation(yaw)); + } + case BILLBOARD -> { + pitch = (float) Math.toRadians(camera.getPitch()); + yaw = (float) Math.toRadians(-camera.getYaw()); + matrices.multiply(RotationAxis.POSITIVE_Y.rotation(yaw)); + } + case HORIZONTAL -> { + var rotation = -(entity.getCachedState().get(Properties.ROTATION) * 2 * Math.PI) / 16.0F; + matrices.multiply(RotationAxis.POSITIVE_Y.rotation((float) rotation)); + } + case LOCKED -> { + pitch = entity.pitch; + yaw = entity.yaw; + matrices.multiply(RotationAxis.POSITIVE_Y.rotation(yaw)); + } + } + + switch (entity.offset) { + case FRONT -> matrices.translate(0D, Math.sin(pitch) * 0.4, -0.4D); + case BACK -> matrices.translate(0D, Math.sin(pitch) * -0.4, 0.4D); + } + + ItemStack stack = entity.getDisplayedStack(); + Text name; + matrices.push(); + Entity renderEntity = entity.getDisplayEntity(); + if (renderEntity != null) { + name = stack.get(DataComponentTypes.CUSTOM_NAME); + if (name == null) { + name = renderEntity.getName(); + } + + float scale = entity.displayScale; + matrices.scale(scale, scale, scale); + + float entityPitch = -pitch * 57.2957763671875F; + renderEntity.setPitch(entityPitch); + renderEntity.prevPitch = entityPitch; + renderEntity.setHeadYaw(yaw); + if(renderEntity instanceof LivingEntity livingRenderEntity) livingRenderEntity.prevHeadYaw = yaw; + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180)); + EntityRenderer entityRenderer = context.getEntityRenderDispatcher().getRenderer(renderEntity); + entityRenderer.render(renderEntity, 0, entity.tickEntity ? tickDelta : 1f, matrices, vertexConsumers, light); + } else { + name = stack.isEmpty() ? Text.translatable("gui.glowcase.none") : (Text.literal("")).append(stack.getName()).formatted(stack.getRarity().getFormatting()); + } + matrices.pop(); + matrices.translate(0, 0.125F, 0); + + if (entity.showName) { + HitResult hitResult = MinecraftClient.getInstance().crosshairTarget; + if (hitResult instanceof BlockHitResult && ((BlockHitResult) hitResult).getBlockPos().equals(entity.getPos())) { + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180)); + matrices.translate(0, 0, -0.4); + + float scale = 0.025F; + matrices.scale(scale, scale, scale); + + int color = name.getStyle().getColor() == null ? 0xFFFFFF : name.getStyle().getColor().getRgb(); + matrices.translate(-context.getTextRenderer().getWidth(name) / 2F, -4, 0); + context.getTextRenderer().draw(name, 0, 0, color, false, matrices.peek().getPositionMatrix(), vertexConsumers, TextLayerType.NORMAL, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE); + } + } + + matrices.pop(); + + if (!entity.hasItem() || BlockEntityRenderUtil.shouldRenderPlaceholder(entity.getPos())) BlockEntityRenderUtil.renderPlaceholder(entity, ITEM_TEXTURE, 1.0F, RotationAxis.POSITIVE_Y.rotationDegrees(180), matrices, vertexConsumers, context.getRenderDispatcher().camera); + } +} diff --git a/src/main/java/dev/hephaestus/glowcase/packet/C2SEditEntityDisplayBlock.java b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditEntityDisplayBlock.java new file mode 100644 index 0000000..999b55d --- /dev/null +++ b/src/main/java/dev/hephaestus/glowcase/packet/C2SEditEntityDisplayBlock.java @@ -0,0 +1,69 @@ +package dev.hephaestus.glowcase.packet; + +import dev.hephaestus.glowcase.Glowcase; +import dev.hephaestus.glowcase.block.entity.EntityDisplayBlockEntity; +import dev.hephaestus.glowcase.block.entity.ItemDisplayBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RotationPropertyHelper; + +public record C2SEditEntityDisplayBlock(BlockPos pos, ItemDisplayBlockEntity.RotationType rotationType, ItemDisplayBlockEntity.GivesItem givesItem, ItemDisplayBlockEntity.Offset offset, EntityDisplayBlockValues values) implements C2SEditBlockEntity { + public static final Id ID = new Id<>(Glowcase.id("channel.entity_display")); + public static final PacketCodec PACKET_CODEC = PacketCodec.tuple( + BlockPos.PACKET_CODEC, C2SEditEntityDisplayBlock::pos, + PacketCodecs.BYTE.xmap(index -> ItemDisplayBlockEntity.RotationType.values()[index], rotation -> (byte) rotation.ordinal()), C2SEditEntityDisplayBlock::rotationType, + PacketCodecs.BYTE.xmap(index -> ItemDisplayBlockEntity.GivesItem.values()[index], givesItem -> (byte) givesItem.ordinal()), C2SEditEntityDisplayBlock::givesItem, + PacketCodecs.BYTE.xmap(index -> ItemDisplayBlockEntity.Offset.values()[index], offset -> (byte) offset.ordinal()), C2SEditEntityDisplayBlock::offset, + EntityDisplayBlockValues.PACKET_CODEC, C2SEditEntityDisplayBlock::values, + C2SEditEntityDisplayBlock::new + ); + + public static C2SEditEntityDisplayBlock of(EntityDisplayBlockEntity be) { + return new C2SEditEntityDisplayBlock(be.getPos(), be.rotationType, be.givesItem, be.offset, new EntityDisplayBlockValues(be.tickEntity, be.displayScale, be.getCachedState().get(Properties.ROTATION), be.showName, be.pitch, be.yaw)); + } + + @Override + public Id getId() { + return ID; + } + + @Override + public void receive(ServerWorld world, BlockEntity blockEntity) { + if (!(blockEntity instanceof EntityDisplayBlockEntity be)) return; + if (this.values().rotation() < 0 || this.values().rotation() >= RotationPropertyHelper.getMax()) return; + + be.givesItem = this.givesItem(); + be.rotationType = this.rotationType(); + be.offset = this.offset(); + be.pitch = this.values().pitch(); + be.yaw = this.values().yaw(); + be.showName = this.values().showName(); + be.tickEntity = this.values().tickEntity(); + be.displayScale = this.values().scale(); + be.setShouldEditScale(false); + + world.setBlockState(this.pos(), world.getBlockState(this.pos()).with(Properties.ROTATION, this.values().rotation())); + + be.markDirty(); + be.dispatch(); + } + + // separated for tuple call + public record EntityDisplayBlockValues(boolean tickEntity, float scale, int rotation, boolean showName, float pitch, float yaw) { + public static final PacketCodec PACKET_CODEC = PacketCodec.tuple( + PacketCodecs.BOOL, EntityDisplayBlockValues::tickEntity, + PacketCodecs.FLOAT, EntityDisplayBlockValues::scale, + PacketCodecs.INTEGER, EntityDisplayBlockValues::rotation, + PacketCodecs.BOOL, EntityDisplayBlockValues::showName, + PacketCodecs.FLOAT, EntityDisplayBlockValues::pitch, + PacketCodecs.FLOAT, EntityDisplayBlockValues::yaw, + EntityDisplayBlockValues::new + ); + } +} diff --git a/src/main/resources/assets/glowcase/blockstates/entity_display_block.json b/src/main/resources/assets/glowcase/blockstates/entity_display_block.json new file mode 100644 index 0000000..b2b5c34 --- /dev/null +++ b/src/main/resources/assets/glowcase/blockstates/entity_display_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "glowcase:block/glowcase_block" + } + } +} diff --git a/src/main/resources/assets/glowcase/lang/en_us.json b/src/main/resources/assets/glowcase/lang/en_us.json index 43fe6e6..69e6a45 100644 --- a/src/main/resources/assets/glowcase/lang/en_us.json +++ b/src/main/resources/assets/glowcase/lang/en_us.json @@ -8,6 +8,7 @@ "block.glowcase.sprite_block": "Sprite Block", "block.glowcase.outline_block": "Outline Block", "block.glowcase.item_acceptor_block": "Item Acceptor Block", + "block.glowcase.entity_display_block": "Entity Display Block", "gui.glowcase.scale_value": "Scale: %d", "gui.glowcase.alignment": "Alignment: %s", "gui.glowcase.gives_item": "Gives Item: %s", @@ -15,6 +16,7 @@ "gui.glowcase.rotation_type": "Rotation Type: %s", "gui.glowcase.show_name": "Show Name: %s", "gui.glowcase.offset_value": "Offset: %s", + "gui.glowcase.tick_entity": "Tick Entity: %s", "gui.glowcase.none": "(None)", "item.glowcase.text_block": "Text Block", "item.glowcase.hyperlink_block": "Hyperlink Block", @@ -57,5 +59,8 @@ "block.glowcase.item_acceptor_block.tooltip.1": " Pushes the item stack to an inventory", "block.glowcase.item_acceptor_block.tooltip.2": " Emits a redstone pulse", "block.glowcase.item_acceptor_block.tooltip.3": "Interact in Creative mode to edit", + "block.glowcase.entity_display_block.tooltip.0": "Displays an entity from a spawn egg", + "block.glowcase.entity_display_block.tooltip.1": "Interact with a spawn egg to set it", + "block.glowcase.entity_display_block.tooltip.2": "Interact with the same spawn egg to edit", "item.glowcase.lock.tooltip.0": "Locks and unlocks containers" } diff --git a/src/main/resources/assets/glowcase/models/item/entity_display_block.json b/src/main/resources/assets/glowcase/models/item/entity_display_block.json new file mode 100644 index 0000000..316a527 --- /dev/null +++ b/src/main/resources/assets/glowcase/models/item/entity_display_block.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "glowcase:item/entity_display_block" + } +} diff --git a/src/main/resources/assets/glowcase/textures/item/entity_display_block.png b/src/main/resources/assets/glowcase/textures/item/entity_display_block.png new file mode 100644 index 0000000..1d17564 Binary files /dev/null and b/src/main/resources/assets/glowcase/textures/item/entity_display_block.png differ diff --git a/src/main/resources/data/glowcase/tags/item/items.json b/src/main/resources/data/glowcase/tags/item/items.json index e0cbe38..44ea5ab 100644 --- a/src/main/resources/data/glowcase/tags/item/items.json +++ b/src/main/resources/data/glowcase/tags/item/items.json @@ -8,6 +8,7 @@ "glowcase:sprite_block", "glowcase:outline_block", "glowcase:particle_display", - "glowcase:item_acceptor_block" + "glowcase:item_acceptor_block", + "glowcase:entity_display_block" ] }