From 4de4797a096e754657757dbbd8b6d33c3cc3e93c Mon Sep 17 00:00:00 2001
From: Fff
Date: Sat, 19 Sep 2026 21:55:09 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=89=A9=E5=B1=95=E6=8A=A4?=
=?UTF-8?q?=E7=AC=A6=E4=B8=8E=E8=A3=85=E5=A4=87=E8=83=BD=E5=8A=9B=E5=B9=B6?=
=?UTF-8?q?=E5=AE=8C=E5=96=84=E8=93=9D=E5=9B=BE=E5=BB=BA=E9=80=A0=E5=92=8C?=
=?UTF-8?q?=E4=BB=93=E5=82=A8=E4=BA=A4=E4=BA=92?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增犰狳护符及配方,支持驱离蜘蛛和潜行抗性,调整自然护符的组合效果
- 扩展现有护符的增益、伤害防护、猫狼驯服及铁傀儡友好行为
- 为耐候头盔增加稳定夜视,支持耐候靴潜行时在气泡柱中下沉
- 将结构蓝图导入导出迁移至服务端存档目录,支持单人游戏 Shift 打开目录
- 修正结构磁盘自动旋转设置、智能放置器布局与蓝图预览渲染
- 为建筑杖增加实体阻挡检测及三秒高亮提示,完善放置交互与子空间命中兼容
- 修复方块搬运后的红石信号更新及世界边界监听器遍历问题
- 修复仓储菜单盔甲与口袋槽位同步,完善创造模式口袋校验和状态效果布局及 JEI 避让
- 增加仓储分类筛选一键重置和创造物品变体标记,调整仓储端口与溜槽交互
- 新增余烬金属块与寒霜金属块时移配方,提高部分寒霜金属配方产量
- 调整超级电容与多相超限物质配方,为天体锻造相关物品和无尽收集器添加永恒属性
- 为远古海礁增加含水支持,将伐木附魔每级默认额外破坏原木数调整为四个
- 修正重戟与共振器的属性加成适用范围,统一物品提示及翻译参数并同步生成资源与双语手册
---
.../assets/anvilcraft/lang/en_ud.json | 69 ++++---
.../assets/anvilcraft/lang/en_us.json | 69 ++++---
.../models/item/armadillo_amulet.json | 6 +
.../jewel_crafting/armadillo_amulet.json | 21 ++
.../recipes/misc/multiphase_transcendium.json | 6 +-
.../time_warp/ember_metal_block_0.json | 21 ++
.../time_warp/ember_metal_block_1.json | 21 ++
.../time_warp/ember_metal_block_2.json | 21 ++
.../time_warp/ember_metal_block_3.json | 21 ++
.../time_warp/frost_metal_block_0.json | 32 +++
.../time_warp/frost_metal_block_1.json | 43 ++++
.../time_warp/frost_metal_block_2.json | 43 ++++
.../time_warp/frost_metal_block_3.json | 43 ++++
.../amulet_definition/armadillo.json | 12 ++
.../four_to_one_smithing/nature_amulet.json | 2 +-
.../recipe/item_compress/supercapacitor.json | 2 +-
.../item_compress/supercapacitor_empty.json | 2 +-
.../jewel_crafting/armadillo_amulet.json | 72 +++++++
.../recipe/multiphase_transcendium.json | 15 +-
.../recipe/permutation/nature_amulets.json | 4 +-
.../recipe/time_warp/ember_metal_block_0.json | 18 ++
.../recipe/time_warp/ember_metal_block_1.json | 21 ++
.../recipe/time_warp/ember_metal_block_2.json | 22 ++
.../recipe/time_warp/ember_metal_block_3.json | 22 ++
.../recipe/time_warp/frost_metal_block_0.json | 16 ++
.../recipe/time_warp/frost_metal_block_1.json | 20 ++
.../recipe/time_warp/frost_metal_block_2.json | 20 ++
.../recipe/time_warp/frost_metal_block_3.json | 20 ++
.../recipe/time_warp/frost_metal_ingot_2.json | 2 +-
.../recipe/time_warp/frost_metal_ingot_3.json | 2 +-
.../tags/entity_type/amulet_valid.json | 4 +
.../entity_type/amulet_valid/armadillo.json | 6 +
.../data/anvilcraft/tags/item/amulet.json | 1 +
.../anvilcraft/api/pointer/BlockPointer.java | 14 ++
.../anvilcraft/api/thought/Thinkable.java | 34 ----
.../api/tooltip/FluidTankItemTooltip.java | 70 -------
.../api/tooltip/ItemTooltipManager.java | 160 +++++++--------
.../anvilcraft/block/AncientSeaReefBlock.java | 48 +++++
.../entity/SmartBlockPlacerBlockEntity.java | 5 +
.../block/entity/StoragePortBlockEntity.java | 4 +-
.../anvilcraft/block/item/ChuteBlockItem.java | 7 +-
.../block/item/HasMobBlockItem.java | 3 +-
.../building/BuildingRodObstructions.java | 43 ++++
.../building/BuildingRodService.java | 10 +-
.../client/building/BlueprintClientFiles.java | 191 +++++++++---------
.../client/building/BlueprintRenderView.java | 16 +-
.../BuildingRodObstructionHighlight.java | 32 +++
.../building/BuildingRodRenderTypes.java | 16 +-
.../LargeBlockPlacePreviewEventListener.java | 7 +-
.../gui/component/PocketEffectLayout.java | 64 ++++++
.../StructureScannerButtonState.java | 9 +-
.../gui/component/category/CategoryList.java | 38 ++--
.../gui/screen/SmartBlockPlacerScreen.java | 9 +-
.../client/gui/screen/StorageMenu.java | 44 ++--
.../gui/screen/StructureScannerScreen.java | 41 +++-
.../support/StructureDiskPreviewSupport.java | 3 +-
.../config/AnvilCraftServerConfig.java | 2 +-
.../anvilcraft/data/lang/CategoryLang.java | 1 +
.../anvilcraft/data/lang/ItemTooltipLang.java | 37 +---
.../anvilcraft/data/lang/ScreenLang.java | 1 +
.../data/recipe/ItemCompressRecipeLoader.java | 4 +-
.../MultipleToOneSmithingRecipeLoader.java | 2 +-
.../data/recipe/PermutationRecipeLoader.java | 4 +-
.../recipe/RegistrumItemRecipeLoader.java | 13 +-
.../data/recipe/TimeWarpRecipeLoader.java | 77 ++++++-
.../data/tags/EntityTypeTagLoader.java | 5 +
.../event/LivingEntityEventListener.java | 10 +-
.../anvilcraft/init/block/ModBlocks.java | 17 +-
.../init/entity/ModEntityTypeTags.java | 1 +
.../init/item/ModAmuletDefinitions.java | 7 +
.../anvilcraft/init/item/ModAmulets.java | 3 +-
.../dubhe/anvilcraft/init/item/ModItems.java | 8 +
.../init/item/tabs/ItemsSections.java | 1 +
.../init/item/tabs/ToolsAndUtilities.java | 1 +
.../category/anvil/ItemCompressCategory.java | 3 +-
.../inventory/StructureScannerMenu.java | 24 ---
.../anvilcraft/item/AmuletAbilities.java | 164 +++++++++++++++
.../dubhe/anvilcraft/item/CannedFoodItem.java | 2 +-
.../dev/dubhe/anvilcraft/item/DiskItem.java | 12 +-
.../anvilcraft/item/EquipmentAbilities.java | 15 +-
.../anvilcraft/item/EquipmentArmorItem.java | 37 ----
.../item/FluidTankMinecartItem.java | 26 ++-
.../dubhe/anvilcraft/item/GuideBookItem.java | 27 ++-
.../anvilcraft/item/HeavyHalberdItem.java | 15 --
.../item/IonocraftBackpackItem.java | 11 -
.../dubhe/anvilcraft/item/ResonatorItem.java | 14 --
.../item/SpectralSlingshotItem.java | 16 +-
.../anvilcraft/item/StructureDiskItem.java | 3 +-
.../anvilcraft/item/StructureToolItem.java | 20 +-
.../anvilcraft/item/amulet/AmuletBoxItem.java | 13 ++
.../item/amulet/ComradeAmuletItem.java | 47 +++++
.../item/property/component/Comrades.java | 32 +--
.../item/property/component/Ferocious.java | 6 +-
.../item/property/component/Merciless.java | 6 +-
.../item/weapon/AnvilRailgunItem.java | 2 +-
.../CreativeModeInventoryScreenMixin.java | 41 +++-
.../mixin/CreativePocketPacketMixin.java | 22 +-
.../dubhe/anvilcraft/mixin/EntityMixin.java | 5 +
.../anvilcraft/mixin/GameRendererMixin.java | 8 +
.../anvilcraft/mixin/ItemStackMixin.java | 10 +-
.../mixin/JeiPocketEffectAreaMixin.java | 23 +++
.../anvilcraft/mixin/LivingEntityMixin.java | 9 +-
.../mixin/MinecraftClientMixin.java | 7 +
.../NearestAttackableTargetGoalMixin.java | 2 +
.../mixin/PocketEffectScreenMixin.java | 68 +++++++
.../dubhe/anvilcraft/mixin/SpiderMixin.java | 28 +++
.../network/BuildingRodObstructionPacket.java | 28 +++
.../network/StructureScannerFilePacket.java | 14 +-
.../util/BlockPlacementPicking.java | 5 +-
.../anvilcraft/util/BlockPlacementUtil.java | 23 ++-
.../dubhe/anvilcraft/util/GravityManager.java | 11 +-
.../util/PlacementInteractions.java | 6 +
.../util/StructureBlueprintFiles.java | 80 ++++++++
.../anvilcraft/util/StructureLoadUtil.java | 11 +-
.../util/StructureScannerFiles.java | 48 +++--
.../OverworldLikeGenerationBootstrap.java | 3 +-
src/main/resources/anvilcraft.mixins.json | 3 +
.../ageratum/en_us/001_feature/001_spawner.md | 4 +-
.../en_us/005_tool/001_amethyst_tools.md | 2 +-
.../ageratum/en_us/008_recipe/210_spawner.md | 2 +-
.../ageratum/zh_cn/001_feature/001_spawner.md | 4 +-
.../zh_cn/005_tool/001_amethyst_tools.md | 2 +-
.../ageratum/zh_cn/008_recipe/210_spawner.md | 2 +-
.../gui/machine/structure_scanner/export.png | Bin 290 -> 981 bytes
.../gui/machine/structure_scanner/import.png | Bin 283 -> 962 bytes
125 files changed, 1987 insertions(+), 740 deletions(-)
create mode 100644 src/generated/resources/assets/anvilcraft/models/item/armadillo_amulet.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/jewel_crafting/armadillo_amulet.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_0.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_1.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_2.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_3.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_0.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_1.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_2.json
create mode 100644 src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_3.json
create mode 100644 src/generated/resources/data/anvilcraft/anvilcraft/amulet_definition/armadillo.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/jewel_crafting/armadillo_amulet.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_0.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_1.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_2.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_3.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_0.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_1.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_2.json
create mode 100644 src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_3.json
create mode 100644 src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid/armadillo.json
create mode 100644 src/main/java/dev/dubhe/anvilcraft/block/AncientSeaReefBlock.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/building/BuildingRodObstructions.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodObstructionHighlight.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/gui/component/PocketEffectLayout.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/item/AmuletAbilities.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/item/amulet/ComradeAmuletItem.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/mixin/JeiPocketEffectAreaMixin.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/mixin/PocketEffectScreenMixin.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/mixin/SpiderMixin.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/network/BuildingRodObstructionPacket.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/util/StructureBlueprintFiles.java
diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json
index 6d28f18065..6d88db5b52 100644
--- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json
+++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json
@@ -1072,6 +1072,7 @@
"item.anvilcraft.anvil_amulet": "ʇǝꞁnɯⱯ ꞁᴉʌuⱯ",
"item.anvilcraft.anvil_hammer": "ɹǝɯɯɐH ꞁᴉʌuⱯ",
"item.anvilcraft.anvil_railgun": "unᵷꞁᴉɐᴚ ꞁᴉʌuⱯ",
+ "item.anvilcraft.armadillo_amulet": "ʇǝꞁnɯⱯ oꞁꞁᴉpɐɯɹⱯ",
"item.anvilcraft.beef_mushroom_stew": "ʍǝʇS ɯooɹɥsnW ɟǝǝᗺ",
"item.anvilcraft.black_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ ʞɔɐꞁᗺ",
"item.anvilcraft.blue_cement_bucket": "ʇǝʞɔnᗺ ʇuǝɯǝƆ ǝnꞁᗺ",
@@ -1256,7 +1257,7 @@
"item.anvilcraft.stellar_ring_component": "ʇuǝuodɯoƆ ᵷuᴉᴚ ɹɐꞁꞁǝʇS",
"item.anvilcraft.structure_disk": "ʞsᴉᗡ ǝɹnʇɔnɹʇS",
"item.anvilcraft.structure_disk.fit_placer": "ɹǝɔɐꞁԀ ʞɔoꞁᗺ ʇɹɐɯS ʎq pǝɔɐꞁd ǝq uɐƆ ✓",
- "item.anvilcraft.structure_disk.size": "%s :ǝzᴉS",
+ "item.anvilcraft.structure_disk.size": "%s x %s x %s :ǝzᴉS",
"item.anvilcraft.structure_disk.structure": "%s :ǝɹnʇɔnɹʇS",
"item.anvilcraft.structure_disk.too_large_for_placer": "(ϛxϛxϛ xɐɯ) ɹǝɔɐꞁԀ ʞɔoꞁᗺ ʇɹɐɯS ɹoɟ ǝᵷɹɐꞁ oo⟘ ✗",
"item.anvilcraft.structure_tool": "ꞁoo⟘ ǝɹnʇɔnɹʇS",
@@ -1807,6 +1808,7 @@
"screen.anvilcraft.storage.category.mode.unlimited": "pǝʇᴉɯᴉꞁu∩",
"screen.anvilcraft.storage.category.name": "%s :ǝɯɐN",
"screen.anvilcraft.storage.category.setting.title": "ᵷuᴉʇʇǝS ʎɹoᵷǝʇɐƆ",
+ "screen.anvilcraft.storage.category.setting.tooltip": "sɹǝʇꞁᴉɟ ʇǝsǝɹ oʇ ʞɔᴉꞁɔ ʇɥᵷᴉɹ 'ǝɹnᵷᴉɟuoɔ oʇ ʞɔᴉꞁɔ ʇɟǝꞀ",
"screen.anvilcraft.storage.category.tooltip": "doʇ oʇ uᴉd oʇ ʞɔᴉꞁɔ ʇɥᵷᴉɹ 'sǝʇɐuɹǝʇꞁɐ oʇ ǝʌoɯ oʇ ʞɔᴉꞁɔ ʇɟǝꞀ",
"screen.anvilcraft.storage.count": "%s :ʇunoƆ",
"screen.anvilcraft.storage.crafting.auto_fill": "%s :ꞁꞁᴉɟǝᴚ oʇnⱯ",
@@ -1850,6 +1852,7 @@
"screen.anvilcraft.structure_scanner.marker": "(ɹɐǝꞁɔ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ ⸵pǝʇɹoddns ᵷɐɹp IƎՐ) ɯǝʇᴉ ʎɐꞁdsᴉᗡ",
"screen.anvilcraft.structure_scanner.name": "ǝɯɐu ǝɹnʇɔnɹʇS",
"screen.anvilcraft.structure_scanner.no_files": "sǝꞁᴉɟ ᵷuᴉɥɔʇɐɯ oN",
+ "screen.anvilcraft.structure_scanner.open_folder": "ɹǝpꞁoɟ ǝɹnʇɔnɹʇs uǝdO",
"screen.anvilcraft.structure_scanner.ready": "ʎpɐǝɹ uɐɔs ǝɹnʇɔnɹʇS",
"screen.anvilcraft.structure_scanner.scan": "ǝɹnʇɔnɹʇs uɐɔs oʇ ʞɔᴉꞁƆ",
"screen.anvilcraft.structure_scanner.scanning": "ᵷuᴉuuɐɔS",
@@ -1939,21 +1942,16 @@
"tooltip.anvilcraft.charger.jade.working_progress": "%s :ssǝɹᵷoɹԀ ᵷuᴉᵷɹɐɥƆ",
"tooltip.anvilcraft.collector.jade.cooldown": "%s :uʍopꞁooƆ ʇɔǝꞁꞁoƆ",
"tooltip.anvilcraft.collector.jade.range": "%s :ǝᵷuɐᴚ ʇɔǝꞁꞁoƆ",
+ "tooltip.anvilcraft.comrade_amulet.player": "%s ·",
"tooltip.anvilcraft.control_valve.jade.filter": "%s :ɹǝʇꞁᴉℲ",
"tooltip.anvilcraft.control_valve.jade.rate": "%s :ǝʇɐᴚ xɐW",
+ "tooltip.anvilcraft.count": "%sx ",
"tooltip.anvilcraft.crab_trap.jade.fishing": "%s :sʇdɯǝʇʇⱯ ᵷuᴉɥsᴉℲ",
"tooltip.anvilcraft.crab_trap.state_name": "ǝʇɐʇS dɐɹ⟘ qɐɹƆ",
"tooltip.anvilcraft.creative_crate.item": ":ɯǝʇI",
"tooltip.anvilcraft.discharger.jade.energy": "%2$s / %1$s",
"tooltip.anvilcraft.discharger.jade.time": "%2$s / %1$s",
"tooltip.anvilcraft.discharger.jade.working_progress": "%s :ssǝɹᵷoɹԀ ᵷuᴉᵷɹɐɥɔsᴉᗡ",
- "tooltip.anvilcraft.equipment.breathing": "sǝᴉʇꞁɐuǝd ᵷuᴉuᴉɯ ɹǝʇɐʍɹǝpun sǝʌoɯǝᴚ\nɯnnɔɐʌ uᴉ puɐ ɹǝʇɐʍɹǝpun uǝᵷʎxo sǝᴉꞁddnS",
- "tooltip.anvilcraft.equipment.buffer_boots": "ʇɥᵷᴉǝɥ sʞɔoꞁq ߈ oʇ dn 'dɯnɾ ɐ ǝᵷɹɐɥɔ oʇ ʞɐǝus pꞁoH\nǝᵷɐɯɐp ꞁꞁɐɟ oʇ ǝunɯɯI",
- "tooltip.anvilcraft.equipment.clear_vision": "spᴉnꞁɟ ꞁꞁɐ uᴉ uoᴉsᴉʌ ɹɐǝꞁƆ",
- "tooltip.anvilcraft.equipment.fluid_walking": "ɹǝʇsɐɟ puǝɔsǝp oʇ ʞɐǝus pꞁoɥ 'ǝᵷɹǝɯqns oʇ ʞɐǝus ⸵sǝɔɐɟɹns pᴉnꞁɟ ꞁꞁᴉʇs uo ʞꞁɐM",
- "tooltip.anvilcraft.equipment.full_suit": "pᴉoʌ ǝɥʇ oʇuᴉ ᵷuᴉꞁꞁɐɟ sʇuǝʌǝɹd ⸵pᴉoʌ ǝɥʇ ʇdǝɔxǝ ǝᵷɐɯɐp ꞁɐʇuǝɯuoɹᴉʌuǝ oʇ ǝunɯɯᴉ :ʇᴉns ꞁꞁnℲ",
- "tooltip.anvilcraft.equipment.pockets": "sᵷuᴉᵷᵷǝꞁ ᵷuᴉʌoɯǝɹ ǝɹoɟǝq sʇǝʞɔod ʎʇdɯƎ\npuɐɥɟɟo ɹnoʎ ɥʇᴉʍ dɐʍs oʇ ʎǝʞ ʇǝʞɔod ǝɥʇ ǝs∩\nsʇoꞁs ʇǝʞɔod %s",
- "tooltip.anvilcraft.equipment.recharge": "sɹoʇᴉɔɐdɐɔ ɹo spᴉɹᵷ ɯoɹɟ ǝꞁqɐǝᵷɹɐɥɔǝᴚ\npuoɔǝs ɹǝd ƎℲʞ 00⥝ sǝs∩",
"tooltip.anvilcraft.fe_collector.energy": "ƎℲʞ %d/%d ",
"tooltip.anvilcraft.fe_collector.low_energy": "ƎℲ ʇuǝᴉɔᴉɟɟnsuI",
"tooltip.anvilcraft.fe_collector.title": "ʎᵷɹǝuƎ ǝᵷɹoℲ",
@@ -1963,6 +1961,7 @@
"tooltip.anvilcraft.fluid_tank.capacity.value": "%s / %s ",
"tooltip.anvilcraft.fluid_tank.capacity.value.infinity": "∞ / %s ",
"tooltip.anvilcraft.fluid_tank.fluid": ":pᴉnꞁℲ",
+ "tooltip.anvilcraft.fluid_tank.fluid.value": "%s %s ",
"tooltip.anvilcraft.fluid_tank.more": "˙˙˙ǝɹoɯ %s puɐ",
"tooltip.anvilcraft.grid_information.consumer_stats": ":sʇɐʇS ɹǝɯnsuoƆ ɹǝʍoԀ",
"tooltip.anvilcraft.grid_information.input_power": "%d :uoᴉʇdɯnsuoƆ ɹǝʍoԀ ",
@@ -1985,7 +1984,6 @@
"tooltip.anvilcraft.heat.tier.redhot": "ʇoHpǝᴚ",
"tooltip.anvilcraft.heat.title": ":sʇɐʇS ʇɐǝH",
"tooltip.anvilcraft.heat_collector.not_work": "ᵷuᴉʞɹoʍ ʇou sᴉ ɹoʇɔǝꞁꞁoƆ ʇɐǝH",
- "tooltip.anvilcraft.heavy_halberd.desc": "sǝpoɯ ɥɔʇᴉʍs oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.heliostats.adjacent_heliostats": "˙pǝʇɔǝʇǝp sʇɐʇsoᴉꞁǝɥ ʇuǝɔɐɾpⱯ",
"tooltip.anvilcraft.heliostats.no_rotation_angle": "ʇǝᵷɹɐʇ ǝɥʇ ǝʇɐᴉpɐɹɹᴉ oʇ ǝꞁᵷuɐ uoᴉʇɔǝɹᴉp ǝꞁqᴉssod oN ",
"tooltip.anvilcraft.heliostats.no_sun": "ʇɥᵷᴉꞁuns oN ",
@@ -1994,8 +1992,9 @@
"tooltip.anvilcraft.heliostats.too_far": "ɹɐɟ ooʇ sᴉ sod uoᴉʇɐuᴉɯnꞁꞁᴉ ǝɥ⟘ ",
"tooltip.anvilcraft.heliostats.unknown": "uosɐǝɹ uʍouʞu∩ ",
"tooltip.anvilcraft.heliostats.unspecified_irradiation_block": "ʞɔoꞁq uoᴉʇɐᴉpɐɹɹᴉ pǝᴉɟᴉɔǝdsu∩ ",
+ "tooltip.anvilcraft.indented": "%s ",
"tooltip.anvilcraft.infinity": "ʎʇᴉuᴉɟuI",
- "tooltip.anvilcraft.item.abnormal_amulet": "sɯǝʇᴉ pꞁo⅁ pǝsɹnƆ 'ɹǝpʍoԀ ᵷuᴉʇɐoꞁℲ 'ɯnᴉuoʇnꞁԀ 'ɯnᴉuɐɹ∩ ᵷuᴉʎɹɹɐɔ ɯoɹɟ ǝᵷɐɯɐp sʇuǝʌǝɹԀ",
+ "tooltip.anvilcraft.item.abnormal_amulet": "sɯǝʇᴉ pꞁo⅁ pǝsɹnƆ ɹo 'ɹǝpʍoԀ ᵷuᴉʇɐoꞁℲ 'ɯnᴉuoʇnꞁԀ 'ɯnᴉuɐɹ∩ ᵷuᴉʎɹɹɐɔ ɯoɹɟ puɐ pooɟ ɯoɹɟ sʇɔǝɟɟǝ ꞁnɟɯɹɐɥ sʇuǝʌǝɹԀ",
"tooltip.anvilcraft.item.acceleration_ring": "ɹǝɯɯɐH ꞁᴉʌuⱯ ɥʇᴉʍ sɹǝʎɐꞁd ɹo 'sǝꞁᴉʇɔǝɾoɹd 'sꞁᴉʌuɐ ɹoɟ pꞁǝᴉɟ uoᴉʇɐɹǝꞁǝɔɔɐ sǝʇɐǝɹƆ",
"tooltip.anvilcraft.item.activator_sliding_rail": "ʇᴉ ɹǝʌo ᵷuᴉpᴉꞁs sʞɔoꞁq sǝʇɐʌᴉʇɔⱯ",
"tooltip.anvilcraft.item.active_silencer": "spunos ʎqɹɐǝu pǝʇɔǝꞁǝs ǝʇɐuᴉɯᴉꞁƎ",
@@ -2004,10 +2003,11 @@
"tooltip.anvilcraft.item.amulet_box.fullness": "%2$d / %1$d :ssǝuꞁꞁnℲ",
"tooltip.anvilcraft.item.amulet_box.shift": "ʇɟᴉᵷ ʇǝɹɔǝs ɐ ǝʌᴉǝɔǝɹ ʎɐɯ noʎ 'ɯǝʇoʇ ǝɥʇ ᵷuᴉɯnsuoɔ ɹǝʇɟɐ puɐ 'pǝpǝǝu uǝɥʍ xoq ǝɥʇ uᴉ sɯǝʇoʇ ǝɥʇ ǝɯnsuoɔ 'ᵷuᴉpꞁoɥ uǝɥM\n⸵sɯǝʇoʇ ǝɥʇ ǝʌǝᴉɹʇǝɹ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ-ʇɟᴉɥs puɐ 'ʎɹoʇuǝʌuᴉ ɹnoʎ uo ᵷuᴉʎpu∩ ɟo sɯǝʇo⟘ ǝɥʇ ǝɹoʇs oʇ ʞɔᴉꞁɔ ʇɥᵷᴉᴚ\nsɯǝʇoʇ ɹo sʇǝꞁnɯɐ ǝʌᴉʇɔɐ ǝꞁdᴉʇꞁnɯ sǝɹoʇS",
"tooltip.anvilcraft.item.ancient_sea_reef": "ǝɹnsɐǝɹʇ ǝɯos ᵷuᴉpᴉɥ s,ʇᴉ ǝʞᴉꞁ sʞooꞁ – ɟǝǝɹ ɐǝs ɟo ʞunɥɔ Ɐ",
- "tooltip.anvilcraft.item.anvil_amulet": "ǝᵷɐɯɐp ꞁᴉʌuɐ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.anvil_amulet": "ꞁᴉʌuⱯ ᵷuᴉᵷɹoℲ ꞁɐᴉʇsǝꞁǝƆ ǝɥʇ ɯoɹɟ ʎʇᴉʌɐɹᵷ ꞁɐᴉʇsǝꞁǝɔ puɐ 'uoᴉʇɐʇᴉʌǝꞀ 'ʞɔɐqʞɔouʞ 'ǝᵷɐɯɐp ꞁᴉʌuɐ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.anvil_hammer": "ǝɔɐɯ ɐ puɐ 'sǝꞁᵷᵷoᵷ 'ɥɔuǝɹʍ ɐ 'ꞁᴉʌuɐ uɐ 'ɹǝɯɯɐɥ ɐ s,ʇI",
"tooltip.anvilcraft.item.anvil_railgun": "ꞁᴉʌuɐ pǝǝds-ɥᵷᴉɥ ɐ ɥɔunɐꞁ puɐ dn ǝᵷɹɐɥɔ oʇ ɹǝʍod sǝɯnsuoɔ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ pꞁoH",
"tooltip.anvilcraft.item.anvil_railgun.shift": "ɹoᴉʌɐɥǝq ᵷuᴉɹᴉɟ sʇᴉ ǝᵷuɐɥɔ ɹo 'ǝɯᴉʇ ǝᵷɹɐɥɔ ǝɔnpǝɹ 'ǝᵷɐɯɐp ʇsooq uɐɔ unᵷꞁᴉɐᴚ ꞁᴉʌuⱯ ǝɥʇ ᵷuᴉʇuɐɥɔuƎ\nǝᵷɐɯɐp puɐ pǝǝds ɹǝɥᵷᴉɥ uᴉ sʇꞁnsǝɹ ᵷuᴉᵷɹɐɥɔ ɹǝᵷuoꞀ\nꞁᴉʌuɐ ǝɥʇ ɥɔunɐꞁ oʇ ǝsɐǝꞁǝɹ puɐ 'dn ǝᵷɹɐɥɔ oʇ uᴉɐᵷɐ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ pꞁoɥ uǝɥ⟘\nunᵷꞁᴉɐɹ ǝɥʇ oʇuᴉ puɐɥɟɟo ɹnoʎ ɯoɹɟ ꞁᴉʌuɐ uɐ pɐoꞁ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ pꞁoH",
+ "tooltip.anvilcraft.item.armadillo_amulet": "ʇɟᴉɥS ᵷuᴉpꞁoɥ ǝꞁᴉɥʍ II ǝɔuɐʇsᴉsǝᴚ sʇuɐɹᵷ ⸵sɹǝpᴉdS ʎɐʍɐ sǝɹɐɔS",
"tooltip.anvilcraft.item.auto_enchanting_table": "ʇuǝɯʇuɐɥɔuǝ pᴉnbᴉꞁ ɹo 'sɹǝɯᴉɹd 'pᴉnꞁɟ ǝɔuǝᴉɹǝdxǝ ɥʇᴉʍ sɯǝʇᴉ ʇuɐɥɔuǝ ʎꞁꞁɐɔᴉʇɐɯoʇnⱯ",
"tooltip.anvilcraft.item.auto_enchanting_table.shift": "ǝuᴉɥɔɐɯ ǝɥʇ sǝsnɐd ꞁɐuᵷᴉs ǝuoʇspǝᴚ\n(ʇuǝɯʇuɐɥɔuǝ ᵷuᴉʇsᴉxǝ ɹǝd Mʞ ߈9 snꞁd Mʞ ߈9) ϛ⥝ ꞁǝʌǝꞁ oʇ dn ʇuǝɯʇuɐɥɔuǝ uǝsoɥɔ ɐ ʎꞁddɐ oʇ ʇuǝɯʇuɐɥɔuǝ pᴉnbᴉꞁ pǝʇuɐɥɔuǝ sǝɯnsuoɔ :ǝpoɯ ʇuǝɯʇuɐɥɔuǝ pᴉnbᴉꞀ\n(Mʞ ߈9) pᴉnꞁɟ ǝɔuǝᴉɹǝdxǝ sǝɯnsuoɔ 'sʇuǝɯʇuɐɥɔuǝ ǝꞁqɐʇɔǝꞁǝs ʇsᴉꞁ oʇ ɹǝɯᴉɹd ɐ sǝsn :ǝpoɯ ɹǝɯᴉɹԀ\n(Mʞ 9⥝) ꞁǝʌǝꞁ ɟꞁǝɥsʞooq ʎqɹɐǝu uo pǝsɐq sʇuɐɥɔuǝ 'pᴉnꞁɟ ǝɔuǝᴉɹǝdxǝ sǝɯnsuoɔ :ǝpoɯ ɯopuɐᴚ\npᴉɹᵷ ǝɥʇ ɯoɹɟ ɹǝʍod ɥʇᴉʍ sɯǝʇᴉ sʇuɐɥɔuǝ ʎꞁꞁɐɔᴉʇɐɯoʇnⱯ",
"tooltip.anvilcraft.item.batch_crafter": "Mʞ ߈ ɟo uoᴉʇdɯnsuoɔ ɹǝʍod ɐ ɥʇᴉʍ 'ǝɔuo ʇɐ sɯǝʇᴉ ꞁɐuɹǝʇuᴉ ꞁꞁɐ pǝʇɟɐɹɔ puɐ ꞁɐuᵷᴉs ǝuoʇspǝɹ ɐ pǝʌᴉǝɔǝᴚ",
@@ -2030,20 +2030,21 @@
"tooltip.anvilcraft.item.brass_ingot": "ʎoꞁꞁɐ ɔuᴉz-ɹǝddoɔ ʇuɐʇsᴉsǝɹ-uoᴉsoɹɹoɔ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.brass_nugget": "ʎoꞁꞁɐ ɔuᴉz-ɹǝddoɔ ʇuɐʇsᴉsǝɹ-uoᴉsoɹɹoɔ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.brass_pressure_plate": "ǝʇɐꞁd ssɐɹq ɐ osꞁɐ 'ǝʌoqɐ ɹǝʎɐꞁd ɟo ǝǝɹᵷǝp ᵷuᴉꞁꞁᴉɟ s,ʎɹoʇuǝʌuᴉ ǝɥʇ uo pǝsɐq ꞁɐuᵷᴉs ǝuoʇspǝɹ ɐ ʇndʇnO",
+ "tooltip.anvilcraft.item.breathing_helmet": "sǝᴉʇꞁɐuǝd ᵷuᴉuᴉɯ ɹǝʇɐʍɹǝpun sǝʌoɯǝᴚ\nɯnnɔɐʌ uᴉ puɐ ɹǝʇɐʍɹǝpun uǝᵷʎxo sǝᴉꞁddnS",
"tooltip.anvilcraft.item.bronze_block": "ʎoꞁꞁɐ uᴉʇ-ɹǝddoɔ ǝꞁqɐɹnp ɟo ʞɔoꞁq ǝᵷɹɐꞁ Ɐ",
"tooltip.anvilcraft.item.bronze_ingot": "ʎoꞁꞁɐ uᴉʇ-ɹǝddoɔ ǝꞁqɐɹnp ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.bronze_nugget": "ʎoꞁꞁɐ uᴉʇ-ɹǝddoɔ ǝꞁqɐɹnp ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.bronze_pressure_plate": "ǝʇɐꞁd ǝzuoɹq ɐ osꞁɐ 'ǝʌoqɐ ʎʇǝᴉʇɐs ɹǝʎɐꞁd uo pǝsɐq ꞁɐuᵷᴉs ǝuoʇspǝɹ ɐ ʇndʇnO",
"tooltip.anvilcraft.item.brown_cement_bucket": "ʇuǝɯǝƆ ʎɐɹ⅁ ᵷuᴉǝʎp ʎq pǝɔnpoɹԀ",
- "tooltip.anvilcraft.item.building_rod": "\npǝᴉɹɹɐɔ ǝꞁᴉɥʍ ɥɔɐǝɹ ʍɐꞁɔ qɐɹɔ sʇuɐɹ⅁\nʞꞁnq uᴉ sʇuᴉɹdǝnꞁq puɐ sʞɔoꞁq ǝɔɐꞁԀ",
- "tooltip.anvilcraft.item.building_rod.shift": "\nsɹoʇᴉɔɐdɐɔ ɯoɹɟ pǝᵷɹɐɥɔǝɹ 'ʞɔoꞁq ɹǝd ƎℲ 00⥝ ǝɯnsuoƆ\nꞁꞁᴉɟ ɹǝd ᗺ ᘔ 'sʞɔoꞁq ᵷoꞁɹǝʇɐʍ puɐ sɐǝɹɐ ꞁꞁᴉɟ sʇǝʞɔnᗺ\nʇuǝɯǝɔɐꞁd ʇsɐꞁ ǝɥʇ sǝopun Z+ꞁɹʇƆ\nʇuᴉɹdǝnꞁq ɐ ɟo ʇɹɐd ǝꞁqɐꞁᴉɐʌɐ ǝɥʇ sǝɔɐꞁd oʇ ʇɟᴉɥS pꞁoɥ ' ʇsᴉꞁ ꞁɐᴉɹǝʇɐɯ-ᵷuᴉssᴉɯ ɐ ɹoɟ ʞooq ɐ ʎɹɹɐƆ\nɯǝɥʇ ǝɔɐꞁd oʇ ǝsɐǝꞁǝɹ uǝɥʇ 'sʞɔoꞁq ɟo xoq ɐ ʇɔǝꞁǝs oʇ ᵷɐɹp puɐ ǝsn pꞁoH\nʞsᴉp ǝɹnʇɔnɹʇs ɐ ɹo ʇǝʞɔnq pᴉnꞁɟ ɐ 'ɹǝʇꞁᴉɟ ɐ 'sʞɔoꞁq ǝpᴉsᵷuoꞁɐ puɐɥ ɹǝɥʇᴉǝ uᴉ pꞁoH",
+ "tooltip.anvilcraft.item.buffer_boots": "ʇɥᵷᴉǝɥ sʞɔoꞁq ߈ oʇ dn 'dɯnɾ ɐ ǝᵷɹɐɥɔ oʇ ʞɐǝus pꞁoH\nǝᵷɐɯɐp ꞁꞁɐɟ oʇ ǝunɯɯI",
+ "tooltip.anvilcraft.item.building_rod": "pǝᴉɹɹɐɔ ǝꞁᴉɥʍ ɥɔɐǝɹ sǝsɐǝɹɔuᴉ ⸵ʎꞁʞɔᴉnb sʇuᴉɹdǝnꞁq pꞁᴉnq puɐ ʞꞁnq uᴉ sʞɔoꞁq ǝɔɐꞁԀ",
"tooltip.anvilcraft.item.burning_heater": "ǝɯᴉʇ uɹnq ɟo spuoɔǝs 0߈ᘔ sǝɯnsuoɔ ᵷuᴉʇɟɐɹɔ ɥɔɐƎ\nǝʌoqɐ ʞɔoꞁq ǝɥʇ ʇɐǝɥ oʇ ꞁǝnɟ ǝɯnsuoƆ",
"tooltip.anvilcraft.item.cake_base_block": "ʇᴉ ʇɐǝ oʇ uoods ɐ sɐ ꞁǝʌoɥs ɐ ǝsn 'ǝsɐq ǝʞɐɔ ɟo ʞɔoꞁq Ɐ",
"tooltip.anvilcraft.item.cake_block": "ʇᴉ ʇɐǝ oʇ uoods ɐ sɐ ꞁǝʌoɥs ɐ ǝsn 'ǝʞɐɔ ɯɐǝɹɔ ɟo ʞɔoꞁq Ɐ",
"tooltip.anvilcraft.item.canned_food": "pooɟ ʇuɐʇsuᴉ ǝꞁqɐʞɔɐʇS",
"tooltip.anvilcraft.item.capacitor": "pǝɹoʇs ƎℲW 8",
"tooltip.anvilcraft.item.capacitor_empty": "ʎʇᴉɔɐdɐɔ ƎℲW 8",
- "tooltip.anvilcraft.item.cat_amulet": "sɯoʇuɐɥԀ puɐ sɹǝdǝǝɹƆ ʎɐʍɐ sǝɹɐɔS",
+ "tooltip.anvilcraft.item.cat_amulet": "uoᴉʇɔɐɹǝʇuᴉ puɐɥ-ʎʇdɯǝ ǝuo ɥʇᴉʍ sʇɐƆ pꞁᴉʍ ǝɯɐʇ ⸵sɯoʇuɐɥԀ puɐ sɹǝdǝǝɹƆ ʎɐʍɐ sǝɹɐɔS",
"tooltip.anvilcraft.item.celestial_forging_anvil": "sǝɹnʇɔnɹʇsɐᵷǝɯ pꞁᴉnq 'sǝᴉpoq ꞁɐᴉʇsǝꞁǝɔ ǝᵷɹoℲ",
"tooltip.anvilcraft.item.celestial_forging_anvil.shift": "sꞁᴉɐʇǝp ɹoɟ uoʇʇnq ǝɹnʇɔnɹʇsɐᵷǝɯ ǝɥʇ ʞɔǝɥɔ — sǝsodɹnd ʇuǝɹǝɟɟᴉp ǝʌɹǝs sǝɹnʇɔnɹʇsɐᵷǝɯ ʇuǝɹǝɟɟᴉᗡ\nǝɹnʇɔnɹʇsɐᵷǝɯ ǝɥʇ ʎoɹʇsǝp ꞁꞁᴉʍ ʎpoq ꞁɐᴉʇsǝꞁǝɔ ǝɥʇ ᵷuᴉʞɔoꞁu∩\nǝɹnʇɔnɹʇsɐᵷǝɯ ɐ pꞁᴉnq oʇ ʇᴉ ʞɔoꞁ 'punoɟ sᴉ ʎpoq ꞁɐᴉʇsǝꞁǝɔ ǝꞁqɐʇᴉns ɐ ǝɔuO\nsɹǝʇǝɯɐɹɐd ꞁɐᴉʇsǝꞁǝɔ ǝuᴉɯɹǝʇǝp oʇ ꞁᴉʌuɐ uɐ ǝɔɐꞁԀ",
"tooltip.anvilcraft.item.celestial_forging_anvil_amplifier": "sǝɹnʇɔnɹʇsɐᵷǝɯ puɐ sǝᴉpoq ꞁɐᴉʇsǝꞁǝɔ ɹǝᵷɹɐꞁ ʇɹoddns uɐɔ ʇᴉ os ꞁᴉʌuⱯ ᵷuᴉᵷɹoℲ ꞁɐᴉʇsǝꞁǝƆ ǝɥʇ sǝᴉɟᴉꞁdɯⱯ",
@@ -2133,7 +2134,7 @@
"tooltip.anvilcraft.item.discharger": "ʇnduᴉ pǝʇɐɯoʇnɐ ɹo ꞁɐnuɐɯ sʇɹoddns 'sɹoʇᴉɔɐdɐɔ sǝᵷɹɐɥɔsᴉᗡ",
"tooltip.anvilcraft.item.disk.clear": "pǝɹoʇs ɐʇɐp ɹɐǝꞁɔ oʇ ʞɔᴉꞁɔ ʇɥᵷᴉᴚ + ʇɟᴉɥS",
"tooltip.anvilcraft.item.disk.store": "ᵷuᴉʇʇǝs sʇᴉ ʎdoɔ oʇ ʞɔoꞁq ʞɔᴉꞁɔ ʇɥᵷᴉᴚ",
- "tooltip.anvilcraft.item.dog_amulet": "suoʇǝꞁǝʞS ʎɐʍɐ sǝɹɐɔS",
+ "tooltip.anvilcraft.item.dog_amulet": "uoᴉʇɔɐɹǝʇuᴉ puɐɥ-ʎʇdɯǝ ǝuo ɥʇᴉʍ sǝʌꞁoM pꞁᴉʍ ǝɯɐʇ ⸵suoʇǝꞁǝʞS ʎɐʍɐ sǝɹɐɔS",
"tooltip.anvilcraft.item.dragon_rod": "ǝᵷuɐɹ ǝꞁqɐʇsnɾpɐ ɥʇᴉʍ ɹǝɹnoʌǝp ʞɔoꞁq ǝꞁqɐʇɹoԀ",
"tooltip.anvilcraft.item.dragon_rod.shift": "ʎʇᴉꞁᴉqɐɹnp ǝɹoɯ sʇsoɔ ǝᵷuɐɹ ɹǝᵷɹɐꞁ 'ǝᵷuɐɹ ʇsnɾpɐ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ 'ǝuᴉɯ oʇ ʞɔᴉꞁɔ-ʇɟǝꞁ 'ɹǝɹnoʌǝp ʞɔoꞁq ǝꞁqɐʇɹoԀ",
"tooltip.anvilcraft.item.drain": "ǝʌoqɐ ɯoɹɟ ᵷuᴉʍɐɹp puɐ pɹɐʍuʍop ᵷuᴉʇʇndʇno 'ʎꞁꞁɐɔᴉʇɹǝʌ pᴉnꞁɟ sɹǝɟsuɐɹ⟘",
@@ -2149,10 +2150,12 @@
"tooltip.anvilcraft.item.ember_glass": "ʇɔǝꞁꞁoɔ uo pǝɹᴉnbǝɹ sꞁooʇ oN 'ɟooɹd ɹǝɥʇᴉM 'ssɐꞁᵷ ꞁɐʇǝW ɹǝqɯƎ",
"tooltip.anvilcraft.item.ember_grindstone": "ɟooɹd ɹǝɥʇᴉM 'sʞooq oʇuo sʇuǝɯʇuɐɥɔuǝ sʇɔɐɹʇxƎ",
"tooltip.anvilcraft.item.ember_metal_block": "ɟooɹd ɹǝɥʇᴉM 'suoǝ ɹoɟ ǝɹᴉɟ uᴉ pǝɹǝdɯǝʇ ǝʇᴉɹǝɥʇǝN ʇuɐʇsᴉsǝɹ-ʇɐǝɥ ɟo ʞɔoꞁq ǝᵷɹɐꞁ Ɐ",
+ "tooltip.anvilcraft.item.ember_metal_heavy_halberd": "sǝpoɯ ɥɔʇᴉʍs oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.item.ember_metal_ingot": "suoǝ ɹoɟ ǝɹᴉɟ uᴉ pǝɹǝdɯǝʇ ǝʇᴉɹǝɥʇǝN ʇuɐʇsᴉsǝɹ-ʇɐǝɥ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.ember_metal_nugget": "suoǝ ɹoɟ ǝɹᴉɟ uᴉ pǝɹǝdɯǝʇ ǝʇᴉɹǝɥʇǝN ʇuɐʇsᴉsǝɹ-ʇɐǝɥ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
+ "tooltip.anvilcraft.item.ember_metal_resonator": "ʞɔᴉꞁɔ‑ʇɥᵷᴉɹ ᵷuᴉpꞁoɥ uǝɥʍ sʞɔoꞁq ʇsoɯ ”ǝuᴉɯ‑ǝɔuɐuosǝɹ“ uɐɔ puɐ sꞁooʇ ꞁꞁɐ sʇɹoddns ǝpoɯ oʇnⱯ ˙sǝpoɯ ǝᵷuɐɥɔ oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.item.ember_smithing_table": "ɟooɹd ɹǝɥʇᴉM 'ᵷuᴉɥʇᴉɯs uoᴉʇɐuᴉqɯoɔ ǝuo-uᴉ-ꞁꞁⱯ",
- "tooltip.anvilcraft.item.emerald_amulet": "ǝᵷɐꞁꞁᴉɅ ǝɥʇ ɟo oɹǝH sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.emerald_amulet": "ɹǝɹɐǝʍ ǝɥʇ oʇ ǝꞁᴉʇsoɥ ǝɯoɔǝq ɹǝʌǝu sɯǝꞁo⅁ uoɹI ⸵sʇunoɔsᴉp ɹǝɟɟo sɹǝᵷɐꞁꞁᴉɅ",
"tooltip.anvilcraft.item.enchanted_gold_block": "sɟɟnqǝp pꞁoᵷ pǝsɹnɔ sꞁǝɔuɐɔ pꞁoᵷ pǝʇuɐɥɔuǝ ᵷuᴉʎɹɹɐƆ",
"tooltip.anvilcraft.item.enchanted_gold_ingot": "sǝɯᴉʇ ɹnoɟ ʇᴉ ɥʇᴉʍ ɹǝʇɹɐq suᴉꞁᵷᴉԀ\nsɟɟnqǝp pꞁoᵷ pǝsɹnɔ sꞁǝɔuɐɔ pꞁoᵷ pǝʇuɐɥɔuǝ ᵷuᴉʎɹɹɐƆ",
"tooltip.anvilcraft.item.enchanted_gold_nugget": "sɟɟnqǝp pꞁoᵷ pǝsɹnɔ sꞁǝɔuɐɔ pꞁoᵷ pǝʇuɐɥɔuǝ ᵷuᴉʎɹɹɐƆ",
@@ -2164,7 +2167,7 @@
"tooltip.anvilcraft.item.exp_gem": "ǝpᴉsuᴉ pǝuᴉɐʇuoɔ ԀXƎ ǝɥʇ ʇɔɐɹʇxǝ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉᴚ",
"tooltip.anvilcraft.item.exposed_copper_pressure_plate": "ǝʇɐꞁd ɹǝddoɔ ɐ osꞁɐ 'ǝɯᴉʇ ᵷuᴉssǝɹd ɥʇᴉʍ sǝsɐǝɹɔuᴉ ꞁɐuᵷᴉs ǝuoʇspǝᴚ",
"tooltip.anvilcraft.item.fe_collector": "ɹǝʍod ǝʇɐɹǝuǝᵷ oʇ ƎℲ ᵷuᴉʇɔǝꞁꞁoƆ",
- "tooltip.anvilcraft.item.feather_amulet": "ǝᵷɐɯɐp ꞁꞁɐɟ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.feather_amulet": "ᵷuᴉꞁꞁɐℲ ʍoꞁS sǝʌoɯǝɹ ʇɟᴉɥS ᵷuᴉpꞁoɥ ⸵ᵷuᴉꞁꞁɐℲ ʍoꞁS puɐ ǝᵷɐɯɐp ꞁꞁɐɟ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.ferrite_core_magnet_block": "sǝᵷɹɐɥɔ ǝʇɐɹǝuǝᵷ oʇ sʞɔoꞁq ɹǝddoɔ ʇuǝɔɐɾpɐ sǝsnɐɔ 'uoʇsᴉd ǝɥʇ ʎq pǝꞁꞁnd puɐ pǝɥsnd uǝɥʍ 'ʍoꞁǝq ꞁᴉʌuɐ ǝɥʇ ᵷuᴉʇɔɐɹʇʇⱯ",
"tooltip.anvilcraft.item.filter": "ʇoꞁs ɹǝʇꞁᴉɟ ʎuɐ uᴉ ǝꞁqɐsn 'uoᴉʇɐɹnᵷᴉɟuoɔ uo pǝsɐq sɯǝʇᴉ sǝɥɔʇɐW",
"tooltip.anvilcraft.item.fish_tank": "sᴉsǝɥʇuʎs ꞁᴉʌuɐ ɹoɟ pǝsn 'sʞooꞁ ʇᴉ uɐɥʇ ɹǝᴉpɹnʇs sᴉ ʇI",
@@ -2181,8 +2184,10 @@
"tooltip.anvilcraft.item.frost_glass": "ʇɔǝꞁꞁoɔ uo pǝɹᴉnbǝɹ sꞁooʇ oN 'ɟooɹd uoᴉsoꞁdxƎ 'ssɐꞁᵷ ꞁɐʇǝW ʇsoɹℲ",
"tooltip.anvilcraft.item.frost_grindstone": "ɟooɹd uoᴉsoꞁdxƎ 'sʇuǝɯʇuɐɥɔuǝ ꞁɐnpᴉʌᴉpuᴉ sǝʌoɯǝɹ ʎꞁǝʌᴉʇɔǝꞁǝS",
"tooltip.anvilcraft.item.frost_metal_block": "ɟooɹd uoᴉsoꞁdxƎ 'suoǝ ɹoɟ pꞁoɔ ǝɯǝɹʇxǝ uᴉ pǝɹǝdɯǝʇ ꞁǝǝʇS ꞁɐʎoᴚ ʇuɐʇsᴉsǝɹ-pꞁoɔ ɟo ʞɔoꞁq ǝᵷɹɐꞁ Ɐ",
+ "tooltip.anvilcraft.item.frost_metal_heavy_halberd": "sǝpoɯ ɥɔʇᴉʍs oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.item.frost_metal_ingot": "suoǝ ɹoɟ pꞁoɔ ǝɯǝɹʇxǝ uᴉ pǝɹǝdɯǝʇ ꞁǝǝʇS ꞁɐʎoᴚ ʇuɐʇsᴉsǝɹ-pꞁoɔ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.frost_metal_nugget": "suoǝ ɹoɟ pꞁoɔ ǝɯǝɹʇxǝ uᴉ pǝɹǝdɯǝʇ ꞁǝǝʇS ꞁɐʎoᴚ ʇuɐʇsᴉsǝɹ-pꞁoɔ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
+ "tooltip.anvilcraft.item.frost_metal_resonator": "ʞɔᴉꞁɔ‑ʇɥᵷᴉɹ ᵷuᴉpꞁoɥ uǝɥʍ sʞɔoꞁq ʇsoɯ ”ǝuᴉɯ‑ǝɔuɐuosǝɹ“ uɐɔ puɐ sꞁooʇ ꞁꞁɐ sʇɹoddns ǝpoɯ oʇnⱯ ˙sǝpoɯ ǝᵷuɐɥɔ oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.item.frost_smithing_table": "ɟooɹd uoᴉsoꞁdxƎ 'sǝʇɐꞁdɯǝʇ ᵷuᴉɥʇᴉɯs uoᴉʇɐɯɹoɟǝᗡ puɐ uoᴉʇɐʇnɯɹǝԀ ɥʇᴉʍ sʞɹoM",
"tooltip.anvilcraft.item.gem_amulet": "sʇǝꞁnɯⱯ ɯǝ⅁ ɹnoɟ ꞁꞁɐ ɟo sʇɔǝɟɟǝ sǝuᴉqɯoƆ",
"tooltip.anvilcraft.item.geode": "ʇᴉ ᵷuᴉsn uǝɥʍ ǝpoǝ⅁ ʇsʎɥʇǝɯⱯ ᵷuᴉpunoɹɹns ǝɥʇ puᴉℲ",
@@ -2238,6 +2243,7 @@
"tooltip.anvilcraft.item.lead_nugget": "ꞁɐʇǝɯ ʎʌɐǝɥ 'ǝsuǝp ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.lead_pressure_plate": "ǝʇɐꞁd pɐǝꞁ ɐ osꞁɐ 'ǝʌoqɐ sǝᴉɔǝds qoɯ ɟo ɹǝqɯnu ǝɥʇ uo pǝsɐq ꞁɐuᵷᴉs ǝuoʇspǝɹ ʇndʇnO",
"tooltip.anvilcraft.item.lens": "sɹǝsɐꞁ ʇuɐɥɔuǝ oʇ ssɐꞁᵷ ꞁɐᴉɔǝds ǝs∩",
+ "tooltip.anvilcraft.item.lens.shift": "pǝᵷuɐɥɔun uᴉɐɯǝɹ puɐ ɯɹoɟ pǝʇꞁǝɯs ou ǝʌɐɥ ǝuoʇS pᴉoɅ puɐ ǝɹO pɹɐɥS ǝɹoƆ ⸵ʎꞁʇɔǝɹᴉp sʇꞁnsǝɹ pǝʇꞁǝɯs sdoɹp :(ʍoꞁꞁǝʎ) ɹǝqɯƎ\nԀXƎ oʇ ʇɹǝʌuoɔ osꞁɐ ǝuoʇS pᴉoɅ puɐ ǝɹO pɹɐɥS ǝɹoƆ ⸵ʞɔoꞁq pǝuᴉɯ ɹǝd ǝɔuɐɥ% c0⥝ 'sǝɹo ɟo pɐǝʇsuᴉ sɯǝ⅁ ǝɔuǝᴉɹǝdxƎ sdoɹp :(ǝnꞁq ʇɥᵷᴉꞁ) ʇsoɹℲ\nǝuoʇS pᴉoɅ puɐ ǝɹO pɹɐɥS ǝɹoƆ ᵷuᴉpnꞁɔuᴉ 'sꞁɐᴉɹǝʇɐɯ ʍɐɹ ɟo pɐǝʇsuᴉ sʞɔoꞁq ǝɹo ʍɐɹ sdoɹp :(uɐʎɔ) ꞁɐʎoᴚ",
"tooltip.anvilcraft.item.levitation_powder": "ssǝussǝꞁʇɥᵷᴉǝʍ ʇɥᵷᴉꞁS",
"tooltip.anvilcraft.item.light_blue_cement_bucket": "ʇuǝɯǝƆ ʎɐɹ⅁ ᵷuᴉǝʎp ʎq pǝɔnpoɹԀ",
"tooltip.anvilcraft.item.light_gray_cement_bucket": "ʇuǝɯǝƆ ʎɐɹ⅁ ᵷuᴉǝʎp ʎq pǝɔnpoɹԀ",
@@ -2259,7 +2265,7 @@
"tooltip.anvilcraft.item.menger_sponge": "pᴉnbᴉꞁ ǝʇᴉuᴉɟuᴉ sqɹosqⱯ",
"tooltip.anvilcraft.item.multiphase_matter": "suoᴉʇᴉpuoɔ ꞁɐᴉɔǝds ɹǝpun ǝꞁqɐɥɔʇᴉʍs 'ʎꞁsnoǝuɐʇꞁnɯᴉs sǝsɐɥd ǝꞁqɐʇs ǝꞁdᴉʇꞁnɯ uᴉ sʇsᴉxǝ ʇɐɥʇ ɹǝʇʇɐW",
"tooltip.anvilcraft.item.multitool": "sǝpoɯ ꞁooʇ ɥɔʇᴉʍs oʇ [ʇꞁⱯ] ssǝɹԀ",
- "tooltip.anvilcraft.item.nature_amulet": "sʇɔǝɟɟǝ ʇǝꞁnɯⱯ ɹǝɥʇɐǝℲ puɐ 'ᵷoᗡ 'ʇɐƆ 'ǝɔuǝꞁᴉS sǝuᴉqɯoƆ",
+ "tooltip.anvilcraft.item.nature_amulet": "sʇɔǝɟɟǝ ʇǝꞁnɯⱯ oꞁꞁᴉpɐɯɹⱯ puɐ 'ᵷoᗡ 'ʇɐƆ 'ǝɔuǝꞁᴉS sǝuᴉqɯoƆ",
"tooltip.anvilcraft.item.negative_matter": "ɹǝʇʇɐɯ ʎʇᴉʌɐɹᵷ ᴉʇuɐ sᴉ ʇᴉ 'ɹǝʇʇɐɯᴉʇuɐ ʇou sᴉ ɹǝʇʇɐɯ ǝʌᴉʇɐᵷǝN",
"tooltip.anvilcraft.item.negative_matter_block": "ɹǝʇʇɐɯ ʎʇᴉʌɐɹᵷ ᴉʇuɐ sᴉ ʇᴉ 'ɹǝʇʇɐɯᴉʇuɐ ʇou sᴉ ɹǝʇʇɐɯ ǝʌᴉʇɐᵷǝN",
"tooltip.anvilcraft.item.negative_matter_nugget": "ɹǝʇʇɐɯ ʎʇᴉʌɐɹᵷ ᴉʇuɐ sᴉ ʇᴉ 'ɹǝʇʇɐɯᴉʇuɐ ʇou sᴉ ɹǝʇʇɐɯ ǝʌᴉʇɐᵷǝN",
@@ -2285,10 +2291,15 @@
"tooltip.anvilcraft.item.plywood": "ǝɔuɐɥɔ ɐ ʇoN ¿pooʍ pᴉꞁoS",
"tooltip.anvilcraft.item.plywood_slab": "ǝɔuɐɥɔ ɐ ʇoN ¿pooʍ pᴉꞁoS",
"tooltip.anvilcraft.item.plywood_stairs": "ǝɔuɐɥɔ ɐ ʇoN ¿pooʍ pᴉꞁoS",
+ "tooltip.anvilcraft.item.pockets_leggings": "sᵷuᴉᵷᵷǝꞁ ᵷuᴉʌoɯǝɹ ǝɹoɟǝq sʇǝʞɔod ʎʇdɯƎ\npuɐɥɟɟo ɹnoʎ ɥʇᴉʍ dɐʍs oʇ ʎǝʞ ʇǝʞɔod ǝɥʇ ǝs∩\nsʇoꞁs ʇǝʞɔod 9",
"tooltip.anvilcraft.item.polished_heavy_iron_block": "ɟooɹd uoᴉsoꞁdxƎ 'ʞɔoꞁq ǝʌᴉʇɐɹoɔǝp uoɹI ʎʌɐǝH",
"tooltip.anvilcraft.item.polished_heavy_iron_slab": "ɟooɹd uoᴉsoꞁdxƎ 'ʞɔoꞁq ǝʌᴉʇɐɹoɔǝp uoɹI ʎʌɐǝH",
"tooltip.anvilcraft.item.polished_heavy_iron_stairs": "ɟooɹd uoᴉsoꞁdxƎ 'ʞɔoꞁq ǝʌᴉʇɐɹoɔǝp uoɹI ʎʌɐǝH",
- "tooltip.anvilcraft.item.power_converter": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
+ "tooltip.anvilcraft.item.power_converter_big": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
+ "tooltip.anvilcraft.item.power_converter_extremely_big": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
+ "tooltip.anvilcraft.item.power_converter_middle": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
+ "tooltip.anvilcraft.item.power_converter_small": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
+ "tooltip.anvilcraft.item.power_converter_super_big": "Mʞ %d sǝɯnsuoɔ 'ƎℲ oʇuᴉ ɹǝʍod ʇɹǝʌuoƆ",
"tooltip.anvilcraft.item.powered_sliding_rail": "pǝɹǝʍod uǝɥʍ sʞɔoꞁq ɹo 'sǝᴉʇᴉʇuǝ 'sɯǝʇᴉ sǝʇɐɹǝꞁǝɔɔⱯ",
"tooltip.anvilcraft.item.propel_piston": "ɹǝʍod ɹǝsɐꞀ ɹo ɹoʇᴉɔɐdɐƆ sǝɹᴉnbǝɹ 'ɯɹoʍ uoʇsᴉd pǝʇɐɹᵷǝʇuI",
"tooltip.anvilcraft.item.pulse_generator": "uoᴉʇɐɹnp puɐ ʎɐꞁǝp ǝsꞁnd sǝzᴉɯoʇsnƆ",
@@ -2316,11 +2327,11 @@
"tooltip.anvilcraft.item.royal_steel_ingot": "ɔᴉᵷɐɯ ɯǝᵷ ɥʇᴉʍ pǝsnɟuᴉ uoɹᴉ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.royal_steel_nugget": "ɔᴉᵷɐɯ ɯǝᵷ ɥʇᴉʍ pǝsnɟuᴉ uoɹᴉ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.ruby": "ǝɹᴉɟ ɟo ɹǝʍod ǝɥʇ ᵷuᴉuᴉɐʇuoƆ",
- "tooltip.anvilcraft.item.ruby_amulet": "ǝɔuɐʇsᴉsǝᴚ ǝɹᴉℲ sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.ruby_amulet": "ǝɹᴉɟ uo ǝꞁᴉɥʍ II ɥʇᵷuǝɹʇS ⸵I ɥʇᵷuǝɹʇS puɐ ǝɔuɐʇsᴉsǝᴚ ǝɹᴉℲ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.ruby_laser": "pǝɹǝʍod uǝɥʍ ɯɐǝq ɹǝsɐꞁ ɐ sʇᴉɯƎ",
"tooltip.anvilcraft.item.ruby_prism": "sɯɐǝq ɹǝsɐꞁ sǝᵷɹǝʌuoɔ ɹo sʇɔǝꞁɟǝᗡ",
"tooltip.anvilcraft.item.sapphire": "ʇsoɹɟ ɟo ɹǝʍod ǝɥʇ ᵷuᴉuᴉɐʇuoƆ",
- "tooltip.anvilcraft.item.sapphire_amulet": "ɹǝʍoԀ ʇᴉnpuoƆ sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.sapphire_amulet": "ʇǝɯꞁǝH ʇᴉnsǝɔɐdS ɟooɹdɹǝɥʇɐǝM ɹo ʇǝɯꞁǝH ᵷuᴉɥʇɐǝɹᗺ ɐ ɥʇᴉʍ ɹo ɹǝʇɐʍ uᴉ I ǝɔuɐʇsᴉsǝᴚ ⸵ɹǝʍoԀ ʇᴉnpuoƆ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.seeds_pack": "doɹɔ ɹo pǝǝs ɯopuɐɹ ɐ uᴉɐʇqo oʇ uǝdO",
"tooltip.anvilcraft.item.shulker_container": "ǝʇɐɹƆ ǝᵷɹɐꞀ ɐ ɯoɹɟ pǝpɐɹᵷdn ɹǝuᴉɐʇuoɔ ᵷuᴉpꞁoɟ-ǝɔɐds Ɐ",
"tooltip.anvilcraft.item.shulker_container.shift": "uoᴉʇɐʇS ǝᵷɐɹoʇS uoᴉsuǝɯᴉpɹǝdʎH oʇ ǝʇɐpdn uɐƆ\n(sǝɯᴉʇ ߈ oʇ dn) ǝdʎʇ ɹǝd ǝɔɐds ǝɥʇ puɐ ʇᴉɯᴉꞁ ǝdʎʇ ǝɥʇ ɥʇoq sǝꞁqnop ǝuo ɥɔɐƎ\nʎʇᴉɔɐdɐɔ puɐdxǝ oʇ ꞁᴉʌuɐ uɐ ɥʇᴉʍ ǝʞᴉɹʇs puɐ doʇ uo sɹossǝɹdɯoɔɹǝʌO ǝɔɐdS doɹᗡ\nǝpᴉsuᴉ pǝɹoʇs sɯǝʇᴉ sʇᴉ ɥʇᴉʍ ɹǝuᴉɐʇuoɔ ǝɥʇ sdoɹp ʇᴉ ᵷuᴉʞɐǝɹᗺ\nʇꞁnɐɟǝp ʎq sɯǝʇᴉ ɟo ʇᴉun 9Ɛϛϛ9 ɥʇᴉʍ ǝdʎʇ ɥɔɐǝ 'ʇꞁnɐɟǝp ʎq sɯǝʇᴉ ɟo sǝdʎʇ ߈ᘔ0⥝ uᴉɐʇuoɔ uɐƆ",
@@ -2328,7 +2339,7 @@
"tooltip.anvilcraft.item.shulker_terminal.shift": "ʇsɐꞁ ʇɐ sʞɔoꞁq ߈9 uᴉɥʇᴉʍ ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS ʇsǝɹɐǝu 'sǝxoᗺ ɹǝʞꞁnɥS ꞁꞁɐ uǝɥʇ 'ʇsɹᴉɟ ʇɐ ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS ʎɹoʇuǝʌuI\n:ʎʇᴉɹoᴉɹd uᴉ ǝᵷɐɹoʇs ǝʞᴉꞁ-ɹǝʞꞁnɥS oʇ sʞuᴉꞁ ʎꞁꞁɐɔᴉʇɐɯoʇnⱯ",
"tooltip.anvilcraft.item.sifting_table": "sǝdᴉɔǝɹ ᵷuᴉʇɟᴉs ɯɹoɟɹǝԀ",
"tooltip.anvilcraft.item.sifting_table.shift": "ɹǝɯɯɐH ꞁᴉʌuⱯ ɹo 'ɹoopdɐɹ⟘ uoɹI 'ᵷuᴉpꞁoɟɟɐɔS 'ǝuoʇspuᴉɹ⅁ ɐ ᵷuᴉsn ǝpoɯ ᵷuᴉpuodsǝɹɹoɔ ǝɥʇ oʇ pǝʇɹǝʌuoɔ ǝq uɐƆ",
- "tooltip.anvilcraft.item.silence_amulet": "ɹǝɹɐǝʍ ǝɥʇ sǝɔuǝꞁᴉS",
+ "tooltip.anvilcraft.item.silence_amulet": "ssǝuʞɹɐᗡ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹᵷ puɐ ɹǝɹɐǝʍ ǝɥʇ sǝɔuǝꞁᴉS",
"tooltip.anvilcraft.item.silver_block": "ꞁɐʇǝɯ ǝʌᴉʇɔǝꞁɟǝɹ ʎꞁɥᵷᴉɥ ɟo ʞɔoꞁq ǝᵷɹɐꞁ Ɐ",
"tooltip.anvilcraft.item.silver_ingot": "ꞁɐʇǝɯ ǝʌᴉʇɔǝꞁɟǝɹ ʎꞁɥᵷᴉɥ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.silver_nugget": "ꞁɐʇǝɯ ǝʌᴉʇɔǝꞁɟǝɹ ʎꞁɥᵷᴉɥ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
@@ -2392,7 +2403,7 @@
"tooltip.anvilcraft.item.titanium_nugget": "ꞁɐʇǝɯ ʇɥᵷᴉǝʍʇɥᵷᴉꞁ 'ᵷuoɹʇs ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.titanium_pressure_plate": "ǝʇɐꞁd ɯnᴉuɐʇᴉʇ ɐ osꞁɐ 'ǝʌoqɐ sɯǝʇᴉ ǝɥʇ ɟo ʎʇᴉꞁᴉqɐɹnp ʇsǝɥᵷᴉɥ ǝɥʇ uo pǝsɐq ꞁɐuᵷᴉs ǝuoʇspǝɹ ɐ ʇndʇnO",
"tooltip.anvilcraft.item.topaz": "ᵷuᴉuʇɥᵷᴉꞁ ɟo ɹǝʍod ǝɥʇ ᵷuᴉuᴉɐʇuoƆ",
- "tooltip.anvilcraft.item.topaz_amulet": "ǝᵷɐɯɐp ᵷuᴉuʇɥᵷᴉꞁ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
+ "tooltip.anvilcraft.item.topaz_amulet": "I ǝʇsɐH puɐ ǝᵷɐɯɐp ᵷuᴉuʇɥᵷᴉꞁ oʇ ʎʇᴉunɯɯᴉ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.totem_of_rage": "ǝʇnuᴉɯ ⥝ ɹǝʇɟɐ ǝꞁqɐʇᴉʌǝuᴉ sᴉ ɥʇɐǝp 'ǝᵷɐɯɐp ꞁɐʇɐɟ uo ʞɹǝsɹǝq puɐ ʎʇᴉꞁᴉqɐɹǝuꞁnʌuᴉ sʇuɐɹ⅁",
"tooltip.anvilcraft.item.totem_of_recovery": "ʇuᴉod ɥʇɐǝp oʇ uɹnʇǝɹ oʇ ꞁɹɐǝԀ ꞁꞁɐɔǝᴚ ɐ sʇuɐɹᵷ 'ɥʇɐǝp uo uʍɐds oʇ sʇɹodǝꞁǝ⟘",
"tooltip.anvilcraft.item.trading_station": "sɹǝᵷɐꞁꞁᴉʌ puɐ sɹǝʎɐꞁd ɹoɟ ɯɹoɟʇɐꞁd ᵷuᴉpɐɹ⟘",
@@ -2404,6 +2415,8 @@
"tooltip.anvilcraft.item.transcendence_dragon_rod": "ǝᵷuɐɹ ǝꞁqɐʇsnɾpɐ ɥʇᴉʍ ɹǝɹnoʌǝp ʞɔoꞁq ǝꞁqɐʇɹoԀ",
"tooltip.anvilcraft.item.transcendence_dragon_rod.shift": "ʎʇᴉꞁᴉqɐɹnp ǝɹoɯ sʇsoɔ ǝᵷuɐɹ ɹǝᵷɹɐꞁ 'ǝᵷuɐɹ ʇsnɾpɐ oʇ ʞɔᴉꞁɔ-ʇɥᵷᴉɹ 'ǝuᴉɯ oʇ ʞɔᴉꞁɔ-ʇɟǝꞁ 'ɹǝɹnoʌǝp ʞɔoꞁq ǝꞁqɐʇɹoԀ",
"tooltip.anvilcraft.item.transcendence_grindstone": "spoɥʇǝɯ uoᴉʇɔnɹʇsǝp ʇsoɯ oʇ ǝunɯɯI\nsʇuǝɯʇuɐɥɔuǝ ǝꞁdᴉʇꞁnɯ sɹǝɟsuɐɹʇ ɹo sǝʌoɯǝɹ ʎꞁǝʌᴉʇɔǝꞁǝs 'sʇsoɔ ɹᴉɐdǝɹ puɐ sǝsɹnɔ sǝʌoɯǝᴚ",
+ "tooltip.anvilcraft.item.transcendence_heavy_halberd": "sǝpoɯ ɥɔʇᴉʍs oʇ [%s] ssǝɹԀ",
+ "tooltip.anvilcraft.item.transcendence_resonator": "ʞɔᴉꞁɔ‑ʇɥᵷᴉɹ ᵷuᴉpꞁoɥ uǝɥʍ sʞɔoꞁq ʇsoɯ ”ǝuᴉɯ‑ǝɔuɐuosǝɹ“ uɐɔ puɐ sꞁooʇ ꞁꞁɐ sʇɹoddns ǝpoɯ oʇnⱯ ˙sǝpoɯ ǝᵷuɐɥɔ oʇ [%s] ssǝɹԀ",
"tooltip.anvilcraft.item.transcendence_smithing_table": "ɟooɹd uoᵷɐɹᗡ ɹǝpuƎ puɐ ɹǝɥʇᴉM 'uoᴉsoꞁdxƎ\npǝɹᴉnbǝɹ sǝʇɐꞁdɯǝʇ ou ɥʇᴉʍ suoᴉʇɐɹǝdo ᵷuᴉɥʇᴉɯs ꞁꞁɐ sɯɹoɟɹǝԀ",
"tooltip.anvilcraft.item.transcendium_block": "uoᴉʇɔnɹʇsǝp ɟo sɯɹoɟ ʇsoɯ oʇ ǝunɯɯᴉ 'ɔᴉᵷɐɯ ʎq pǝuᴉɐʇsns ɹǝʇʇɐɯ uoᴉʇɔɐɹǝʇuᴉ-ᵷuoɹʇs ɟo ʞɔoꞁq ǝᵷɹɐꞁ Ɐ",
"tooltip.anvilcraft.item.transcendium_ingot": "uoᴉʇɔnɹʇsǝp ɟo sɯɹoɟ ʇsoɯ oʇ ǝunɯɯᴉ 'ɔᴉᵷɐɯ ʎq pǝuᴉɐʇsns ɹǝʇʇɐɯ uoᴉʇɔɐɹǝʇuᴉ-ᵷuoɹʇs ɟo ǝɔǝᴉd Ɐ",
@@ -2424,6 +2437,10 @@
"tooltip.anvilcraft.item.void_matter": "pᴉoʌ ǝɥʇ ǝpᴉsʇno sʎɐɔǝp 'pᴉoʌ ǝɥʇ ɯoɹɟ pǝuᴉɯ 'sᵷuᴉɥʇ ꞁꞁɐ sǝʇɐǝɹɔ ʇɐɥʇ ǝɔuɐʇsqns ꞁɐᴉpɹoɯᴉɹd ǝɥ⟘",
"tooltip.anvilcraft.item.void_matter_block": "˙˙˙pᴉoʌ ɟo ʞunɥɔ Ɐ",
"tooltip.anvilcraft.item.weathered_copper_pressure_plate": "ǝʇɐꞁd ɹǝddoɔ ɐ osꞁɐ 'ǝɯᴉʇ ᵷuᴉssǝɹd ɥʇᴉʍ sǝsɐǝɹɔuᴉ ꞁɐuᵷᴉs ǝuoʇspǝᴚ",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_boots": "pᴉoʌ ǝɥʇ oʇuᴉ ᵷuᴉꞁꞁɐɟ sʇuǝʌǝɹd ⸵pᴉoʌ ǝɥʇ ʇdǝɔxǝ ǝᵷɐɯɐp ꞁɐʇuǝɯuoɹᴉʌuǝ oʇ ǝunɯɯᴉ :ʇᴉns ꞁꞁnℲ\nɹǝʇsɐɟ puǝɔsǝp oʇ ʞɐǝus pꞁoɥ 'ǝᵷɹǝɯqns oʇ ʞɐǝus ⸵sǝɔɐɟɹns pᴉnꞁɟ ꞁꞁᴉʇs uo ʞꞁɐM\nʇɥᵷᴉǝɥ sʞɔoꞁq ߈ oʇ dn 'dɯnɾ ɐ ǝᵷɹɐɥɔ oʇ ʞɐǝus pꞁoH\nǝᵷɐɯɐp ꞁꞁɐɟ oʇ ǝunɯɯI",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_chestplate": "pᴉoʌ ǝɥʇ oʇuᴉ ᵷuᴉꞁꞁɐɟ sʇuǝʌǝɹd ⸵pᴉoʌ ǝɥʇ ʇdǝɔxǝ ǝᵷɐɯɐp ꞁɐʇuǝɯuoɹᴉʌuǝ oʇ ǝunɯɯᴉ :ʇᴉns ꞁꞁnℲ\nsɹoʇᴉɔɐdɐɔ ɹo spᴉɹᵷ ɯoɹɟ ǝꞁqɐǝᵷɹɐɥɔǝᴚ\npuoɔǝs ɹǝd ƎℲʞ 00⥝ sǝs∩",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_helmet": "pᴉoʌ ǝɥʇ oʇuᴉ ᵷuᴉꞁꞁɐɟ sʇuǝʌǝɹd ⸵pᴉoʌ ǝɥʇ ʇdǝɔxǝ ǝᵷɐɯɐp ꞁɐʇuǝɯuoɹᴉʌuǝ oʇ ǝunɯɯᴉ :ʇᴉns ꞁꞁnℲ\n%s\nspᴉnꞁɟ ꞁꞁɐ uᴉ uoᴉsᴉʌ ɹɐǝꞁƆ\nsǝᴉʇꞁɐuǝd ᵷuᴉuᴉɯ ɹǝʇɐʍɹǝpun sǝʌoɯǝᴚ\nɯnnɔɐʌ uᴉ puɐ ɹǝʇɐʍɹǝpun uǝᵷʎxo sǝᴉꞁddnS",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_leggings": "pᴉoʌ ǝɥʇ oʇuᴉ ᵷuᴉꞁꞁɐɟ sʇuǝʌǝɹd ⸵pᴉoʌ ǝɥʇ ʇdǝɔxǝ ǝᵷɐɯɐp ꞁɐʇuǝɯuoɹᴉʌuǝ oʇ ǝunɯɯᴉ :ʇᴉns ꞁꞁnℲ\nsᵷuᴉᵷᵷǝꞁ ᵷuᴉʌoɯǝɹ ǝɹoɟǝq sʇǝʞɔod ʎʇdɯƎ\npuɐɥɟɟo ɹnoʎ ɥʇᴉʍ dɐʍs oʇ ʎǝʞ ʇǝʞɔod ǝɥʇ ǝs∩\nsʇoꞁs ʇǝʞɔod ᘔ⥝",
"tooltip.anvilcraft.item.white_cement_bucket": "ʇuǝɯǝƆ ʎɐɹ⅁ ᵷuᴉǝʎp ʎq pǝɔnpoɹԀ",
"tooltip.anvilcraft.item.white_chocolate_block": "ʇsooᗺ dɯnՐ uᴉɐᵷ oʇ ʇᴉ uo dǝʇs 'ɹǝʇʇnq ɐoɔoɔ ꞁɐǝɹ ʎq ǝpɐW",
"tooltip.anvilcraft.item.white_chocolate_slab": "ʇsooᗺ dɯnՐ uᴉɐᵷ oʇ ʇᴉ uo dǝʇs 'ɹǝʇʇnq ɐoɔoɔ ꞁɐǝɹ ʎq ǝpɐW",
@@ -2435,9 +2452,11 @@
"tooltip.anvilcraft.item.zinc_ingot": "ꞁɐʇǝɯ ʇɥᵷᴉǝʍʇɥᵷᴉꞁ ɟo ǝɔǝᴉd Ɐ",
"tooltip.anvilcraft.item.zinc_nugget": "ꞁɐʇǝɯ ʇɥᵷᴉǝʍʇɥᵷᴉꞁ ɟo ǝɔǝᴉd ꞁꞁɐɯs Ɐ",
"tooltip.anvilcraft.item.zinc_pressure_plate": "ǝʇɐꞁd ɔuᴉz ɐ osꞁɐ 'ǝʌoqɐ sqoɯ ǝɥʇ ɟo ɥʇꞁɐǝɥ ɟo ǝᵷɐʇuǝɔɹǝd ʇsǝɥᵷᴉɥ ǝɥʇ uo pǝsɐq ꞁɐuᵷᴉs ǝuoʇspǝɹ ɐ ʇndʇnO",
+ "tooltip.anvilcraft.item_count": "%sx %s",
"tooltip.anvilcraft.jade.item_detector": "%d :ǝᵷuɐᴚ uoᴉʇɔǝʇǝᗡ",
"tooltip.anvilcraft.jade.power_information": "%s :pᴉɹ⅁ ɹǝʍoԀ",
"tooltip.anvilcraft.jade.ruby_prism.power": "%d :ꞁǝʌǝꞁ ɹǝsɐꞀ",
+ "tooltip.anvilcraft.key": "[%s]",
"tooltip.anvilcraft.large_cauldron.burning": "ᵷuᴉuɹnq ",
"tooltip.anvilcraft.large_cauldron.fluids": ":spᴉnꞁℲ",
"tooltip.anvilcraft.large_cauldron.inputs": ":sɯǝʇI ʇnduI",
@@ -2492,8 +2511,7 @@
"tooltip.anvilcraft.redstone.output_to_redstone": "%d :ǝuoʇspǝᴚ oʇ ʇndʇnO uɐƆ ",
"tooltip.anvilcraft.redstone.power": "%d :ɹǝʍoԀ ",
"tooltip.anvilcraft.redstone.title": ":sʇɐʇS ǝuoʇspǝᴚ",
- "tooltip.anvilcraft.resonator.desc": "sꞁooʇ ꞁꞁɐ sʇɹoddns ǝpoɯ oʇnⱯ ˙sǝpoɯ ɥɔʇᴉʍs oʇ [%s] ssǝɹԀ",
- "tooltip.anvilcraft.resonator.mining_desc": "ʞɔᴉꞁɔ‑ʇɥᵷᴉɹ ᵷuᴉpꞁoɥ uǝɥʍ sʞɔoꞁq ʇsoɯ ”ǝuᴉɯ‑ǝɔuɐuosǝɹ“ uɐɔ puɐ sꞁooʇ ꞁꞁɐ sʇɹoddns ǝpoɯ oʇnⱯ ˙sǝpoɯ ǝᵷuɐɥɔ oʇ [%s] ssǝɹԀ",
+ "tooltip.anvilcraft.resin_block.mob": "%s -",
"tooltip.anvilcraft.shulker_container.0": "oʇuᴉ sɹossǝɹdɯoɔɹǝʌO ǝɔɐdS ǝɹoɯ ʇɔǝɾuI",
"tooltip.anvilcraft.shulker_container.1": "ʎʇᴉɔɐdɐɔ sʇᴉ ǝsɐǝɹɔuᴉ oʇ ɹǝuᴉɐʇuoƆ ɹǝʞꞁnɥS",
"tooltip.anvilcraft.shulker_container.2": "ǝꞁqᴉsɹǝʌǝɹɹᴉ sᴉ ssǝɔoɹd sᴉɥ⟘",
@@ -2523,6 +2541,7 @@
"tooltip.anvilcraft.storage.types.value.infinite": "∞ / %s ",
"tooltip.anvilcraft.storage_port.item": ":ɯǝʇI",
"tooltip.anvilcraft.thought": "oɟuᴉ ǝɹoɯ ɹoɟ [%s] ssǝɹԀ",
+ "tooltip.anvilcraft.thought.progress": "[%s%s]",
"tooltip.anvilcraft.waiting": "ᵷuᴉɔuʎs ɹoɟ ᵷuᴉʇᴉɐM",
"tooltip.anvilcraft.wip_block.jade.recipe": "%s :ǝdᴉɔǝᴚ ꞁɐɹnpǝɔoɹԀ",
"tooltip.anvilcraft.wip_block.jade.step_count": "%d :pǝʇnɔǝxƎ sdǝʇS",
diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json
index eafac0f4e4..9ef0cf2dca 100644
--- a/src/generated/resources/assets/anvilcraft/lang/en_us.json
+++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json
@@ -1072,6 +1072,7 @@
"item.anvilcraft.anvil_amulet": "Anvil Amulet",
"item.anvilcraft.anvil_hammer": "Anvil Hammer",
"item.anvilcraft.anvil_railgun": "Anvil Railgun",
+ "item.anvilcraft.armadillo_amulet": "Armadillo Amulet",
"item.anvilcraft.beef_mushroom_stew": "Beef Mushroom Stew",
"item.anvilcraft.black_cement_bucket": "Black Cement Bucket",
"item.anvilcraft.blue_cement_bucket": "Blue Cement Bucket",
@@ -1256,7 +1257,7 @@
"item.anvilcraft.stellar_ring_component": "Stellar Ring Component",
"item.anvilcraft.structure_disk": "Structure Disk",
"item.anvilcraft.structure_disk.fit_placer": "✓ Can be placed by Smart Block Placer",
- "item.anvilcraft.structure_disk.size": "Size: %s",
+ "item.anvilcraft.structure_disk.size": "Size: %s x %s x %s",
"item.anvilcraft.structure_disk.structure": "Structure: %s",
"item.anvilcraft.structure_disk.too_large_for_placer": "✗ Too large for Smart Block Placer (max 5x5x5)",
"item.anvilcraft.structure_tool": "Structure Tool",
@@ -1807,6 +1808,7 @@
"screen.anvilcraft.storage.category.mode.unlimited": "Unlimited",
"screen.anvilcraft.storage.category.name": "Name: %s",
"screen.anvilcraft.storage.category.setting.title": "Category Setting",
+ "screen.anvilcraft.storage.category.setting.tooltip": "Left click to configure, right click to reset filters",
"screen.anvilcraft.storage.category.tooltip": "Left click to move to alternates, right click to pin to top",
"screen.anvilcraft.storage.count": "Count: %s",
"screen.anvilcraft.storage.crafting.auto_fill": "Auto Refill: %s",
@@ -1850,6 +1852,7 @@
"screen.anvilcraft.structure_scanner.marker": "Display item (JEI drag supported; right-click to clear)",
"screen.anvilcraft.structure_scanner.name": "Structure name",
"screen.anvilcraft.structure_scanner.no_files": "No matching files",
+ "screen.anvilcraft.structure_scanner.open_folder": "Open structure folder",
"screen.anvilcraft.structure_scanner.ready": "Structure scan ready",
"screen.anvilcraft.structure_scanner.scan": "Click to scan structure",
"screen.anvilcraft.structure_scanner.scanning": "Scanning",
@@ -1939,21 +1942,16 @@
"tooltip.anvilcraft.charger.jade.working_progress": "Charging Progress: %s",
"tooltip.anvilcraft.collector.jade.cooldown": "Collect Cooldown: %s",
"tooltip.anvilcraft.collector.jade.range": "Collect Range: %s",
+ "tooltip.anvilcraft.comrade_amulet.player": "· %s",
"tooltip.anvilcraft.control_valve.jade.filter": "Filter: %s",
"tooltip.anvilcraft.control_valve.jade.rate": "Max Rate: %s",
+ "tooltip.anvilcraft.count": " x%s",
"tooltip.anvilcraft.crab_trap.jade.fishing": "Fishing Attempts: %s",
"tooltip.anvilcraft.crab_trap.state_name": "Crab Trap State",
"tooltip.anvilcraft.creative_crate.item": "Item:",
"tooltip.anvilcraft.discharger.jade.energy": "%1$s / %2$s",
"tooltip.anvilcraft.discharger.jade.time": "%1$s / %2$s",
"tooltip.anvilcraft.discharger.jade.working_progress": "Discharging Progress: %s",
- "tooltip.anvilcraft.equipment.breathing": "Supplies oxygen underwater and in vacuum\nRemoves underwater mining penalties",
- "tooltip.anvilcraft.equipment.buffer_boots": "Immune to fall damage\nHold sneak to charge a jump, up to 4 blocks height",
- "tooltip.anvilcraft.equipment.clear_vision": "Clear vision in all fluids",
- "tooltip.anvilcraft.equipment.fluid_walking": "Walk on still fluid surfaces; sneak to submerge, hold sneak to descend faster",
- "tooltip.anvilcraft.equipment.full_suit": "Full suit: immune to environmental damage except the void; prevents falling into the void",
- "tooltip.anvilcraft.equipment.pockets": "%s pocket slots\nUse the pocket key to swap with your offhand\nEmpty pockets before removing leggings",
- "tooltip.anvilcraft.equipment.recharge": "Uses 100 kFE per second\nRechargeable from grids or capacitors",
"tooltip.anvilcraft.fe_collector.energy": " %d/%d kFE",
"tooltip.anvilcraft.fe_collector.low_energy": "Insufficient FE",
"tooltip.anvilcraft.fe_collector.title": "Forge Energy",
@@ -1963,6 +1961,7 @@
"tooltip.anvilcraft.fluid_tank.capacity.value": " %s / %s",
"tooltip.anvilcraft.fluid_tank.capacity.value.infinity": " %s / ∞",
"tooltip.anvilcraft.fluid_tank.fluid": "Fluid:",
+ "tooltip.anvilcraft.fluid_tank.fluid.value": " %s %s",
"tooltip.anvilcraft.fluid_tank.more": "and %s more...",
"tooltip.anvilcraft.grid_information.consumer_stats": "Power Consumer Stats:",
"tooltip.anvilcraft.grid_information.input_power": " Power Consumption: %d",
@@ -1985,7 +1984,6 @@
"tooltip.anvilcraft.heat.tier.redhot": "RedHot",
"tooltip.anvilcraft.heat.title": "Heat Stats:",
"tooltip.anvilcraft.heat_collector.not_work": "Heat Collector is not working",
- "tooltip.anvilcraft.heavy_halberd.desc": "Press [%s] to switch modes",
"tooltip.anvilcraft.heliostats.adjacent_heliostats": "Adjacent heliostats detected.",
"tooltip.anvilcraft.heliostats.no_rotation_angle": " No possible direction angle to irradiate the target",
"tooltip.anvilcraft.heliostats.no_sun": " No sunlight",
@@ -1994,8 +1992,9 @@
"tooltip.anvilcraft.heliostats.too_far": " The illumination pos is too far",
"tooltip.anvilcraft.heliostats.unknown": " Unknown reason",
"tooltip.anvilcraft.heliostats.unspecified_irradiation_block": " Unspecified irradiation block",
+ "tooltip.anvilcraft.indented": " %s",
"tooltip.anvilcraft.infinity": "Infinity",
- "tooltip.anvilcraft.item.abnormal_amulet": "Prevents damage from carrying Uranium, Plutonium, Floating Powder, Cursed Gold items",
+ "tooltip.anvilcraft.item.abnormal_amulet": "Prevents harmful effects from food and from carrying Uranium, Plutonium, Floating Powder, or Cursed Gold items",
"tooltip.anvilcraft.item.acceleration_ring": "Creates acceleration field for anvils, projectiles, or players with Anvil Hammer",
"tooltip.anvilcraft.item.activator_sliding_rail": "Activates blocks sliding over it",
"tooltip.anvilcraft.item.active_silencer": "Eliminate selected nearby sounds",
@@ -2004,10 +2003,11 @@
"tooltip.anvilcraft.item.amulet_box.fullness": "Fullness: %1$d / %2$d",
"tooltip.anvilcraft.item.amulet_box.shift": "Stores multiple active amulets or totems\nRight click to store the Totems of Undying on your inventory, and shift-right-click to retrieve the totems;\nWhen holding, consume the totems in the box when needed, and after consuming the totem, you may receive a secret gift",
"tooltip.anvilcraft.item.ancient_sea_reef": "A chunk of sea reef – looks like it's hiding some treasure",
- "tooltip.anvilcraft.item.anvil_amulet": "Grants immunity to anvil damage",
+ "tooltip.anvilcraft.item.anvil_amulet": "Grants immunity to anvil damage, knockback, Levitation, and celestial gravity from the Celestial Forging Anvil",
"tooltip.anvilcraft.item.anvil_hammer": "It's a hammer, an anvil, a wrench, goggles, and a mace",
"tooltip.anvilcraft.item.anvil_railgun": "Hold right-click to consumes power to charge up and launch a high-speed anvil",
"tooltip.anvilcraft.item.anvil_railgun.shift": "Hold right-click to load an anvil from your offhand into the railgun\nThen hold right-click again to charge up, and release to launch the anvil\nLonger charging results in higher speed and damage\nEnchanting the Anvil Railgun can boost damage, reduce charge time, or change its firing behavior",
+ "tooltip.anvilcraft.item.armadillo_amulet": "Scares away Spiders; grants Resistance II while holding Shift",
"tooltip.anvilcraft.item.auto_enchanting_table": "Automatically enchant items with experience fluid, primers, or liquid enchantment",
"tooltip.anvilcraft.item.auto_enchanting_table.shift": "Automatically enchants items with power from the grid\nRandom mode: consumes experience fluid, enchants based on nearby bookshelf level (16 kW)\nPrimer mode: uses a primer to list selectable enchantments, consumes experience fluid (64 kW)\nLiquid enchantment mode: consumes enchanted liquid enchantment to apply a chosen enchantment up to level 15 (64 kW plus 64 kW per existing enchantment)\nRedstone signal pauses the machine",
"tooltip.anvilcraft.item.batch_crafter": "Received a redstone signal and crafted all internal items at once, with a power consumption of 4 kW",
@@ -2030,20 +2030,21 @@
"tooltip.anvilcraft.item.brass_ingot": "A piece of corrosion-resistant copper-zinc alloy",
"tooltip.anvilcraft.item.brass_nugget": "A small piece of corrosion-resistant copper-zinc alloy",
"tooltip.anvilcraft.item.brass_pressure_plate": "Output a redstone signal based on the inventory's filling degree of player above, also a brass plate",
+ "tooltip.anvilcraft.item.breathing_helmet": "Supplies oxygen underwater and in vacuum\nRemoves underwater mining penalties",
"tooltip.anvilcraft.item.bronze_block": "A large block of durable copper-tin alloy",
"tooltip.anvilcraft.item.bronze_ingot": "A piece of durable copper-tin alloy",
"tooltip.anvilcraft.item.bronze_nugget": "A small piece of durable copper-tin alloy",
"tooltip.anvilcraft.item.bronze_pressure_plate": "Output a redstone signal based on player satiety above, also a bronze plate",
"tooltip.anvilcraft.item.brown_cement_bucket": "Produced by dyeing Gray Cement",
- "tooltip.anvilcraft.item.building_rod": "Place blocks and blueprints in bulk\nGrants crab claw reach while carried\n",
- "tooltip.anvilcraft.item.building_rod.shift": "Hold in either hand alongside blocks, a filter, a fluid bucket or a structure disk\nHold use and drag to select a box of blocks, then release to place them\nCarry a book for a missing-material list , hold Shift to places the available part of a blueprint\nCtrl+Z undoes the last placement\nBuckets fill areas and waterlog blocks, 2 B per fill\nConsume 100 FE per block, recharged from capacitors\n",
+ "tooltip.anvilcraft.item.buffer_boots": "Immune to fall damage\nHold sneak to charge a jump, up to 4 blocks height",
+ "tooltip.anvilcraft.item.building_rod": "Place blocks in bulk and build blueprints quickly; increases reach while carried",
"tooltip.anvilcraft.item.burning_heater": "Consume fuel to heat the block above\nEach crafting consumes 240 seconds of burn time",
"tooltip.anvilcraft.item.cake_base_block": "A block of cake base, use a shovel as a spoon to eat it",
"tooltip.anvilcraft.item.cake_block": "A block of cream cake, use a shovel as a spoon to eat it",
"tooltip.anvilcraft.item.canned_food": "Stackable instant food",
"tooltip.anvilcraft.item.capacitor": "8 MFE stored",
"tooltip.anvilcraft.item.capacitor_empty": "8 MFE capacity",
- "tooltip.anvilcraft.item.cat_amulet": "Scares away Creepers and Phantoms",
+ "tooltip.anvilcraft.item.cat_amulet": "Scares away Creepers and Phantoms; tame wild Cats with one empty-hand interaction",
"tooltip.anvilcraft.item.celestial_forging_anvil": "Forge celestial bodies, build megastructures",
"tooltip.anvilcraft.item.celestial_forging_anvil.shift": "Place an anvil to determine celestial parameters\nOnce a suitable celestial body is found, lock it to build a megastructure\nUnlocking the celestial body will destroy the megastructure\nDifferent megastructures serve different purposes — check the megastructure button for details",
"tooltip.anvilcraft.item.celestial_forging_anvil_amplifier": "Amplifies the Celestial Forging Anvil so it can support larger celestial bodies and megastructures",
@@ -2133,7 +2134,7 @@
"tooltip.anvilcraft.item.discharger": "Discharges capacitors, supports manual or automated input",
"tooltip.anvilcraft.item.disk.clear": "Shift + Right click to clear data stored",
"tooltip.anvilcraft.item.disk.store": "Right click block to copy its setting",
- "tooltip.anvilcraft.item.dog_amulet": "Scares away Skeletons",
+ "tooltip.anvilcraft.item.dog_amulet": "Scares away Skeletons; tame wild Wolves with one empty-hand interaction",
"tooltip.anvilcraft.item.dragon_rod": "Portable block devourer with adjustable range",
"tooltip.anvilcraft.item.dragon_rod.shift": "Portable block devourer, left-click to mine, right-click to adjust range, larger range costs more durability",
"tooltip.anvilcraft.item.drain": "Transfers fluid vertically, outputting downward and drawing from above",
@@ -2149,10 +2150,12 @@
"tooltip.anvilcraft.item.ember_glass": "Ember Metal glass, Wither proof, No tools required on collect",
"tooltip.anvilcraft.item.ember_grindstone": "Extracts enchantments onto books, Wither proof",
"tooltip.anvilcraft.item.ember_metal_block": "A large block of heat-resistant Netherite tempered in fire for eons, Wither proof",
+ "tooltip.anvilcraft.item.ember_metal_heavy_halberd": "Press [%s] to switch modes",
"tooltip.anvilcraft.item.ember_metal_ingot": "A piece of heat-resistant Netherite tempered in fire for eons",
"tooltip.anvilcraft.item.ember_metal_nugget": "A small piece of heat-resistant Netherite tempered in fire for eons",
+ "tooltip.anvilcraft.item.ember_metal_resonator": "Press [%s] to change modes. Auto mode supports all tools and can “resonance‑mine” most blocks when holding right‑click",
"tooltip.anvilcraft.item.ember_smithing_table": "All-in-one combination smithing, Wither proof",
- "tooltip.anvilcraft.item.emerald_amulet": "Grants Hero of the Village",
+ "tooltip.anvilcraft.item.emerald_amulet": "Villagers offer discounts; Iron Golems never become hostile to the wearer",
"tooltip.anvilcraft.item.enchanted_gold_block": "Carrying enchanted gold cancels cursed gold debuffs",
"tooltip.anvilcraft.item.enchanted_gold_ingot": "Carrying enchanted gold cancels cursed gold debuffs\nPiglins barter with it four times",
"tooltip.anvilcraft.item.enchanted_gold_nugget": "Carrying enchanted gold cancels cursed gold debuffs",
@@ -2164,7 +2167,7 @@
"tooltip.anvilcraft.item.exp_gem": "Right-click to extract the EXP contained inside",
"tooltip.anvilcraft.item.exposed_copper_pressure_plate": "Redstone signal increases with pressing time, also a copper plate",
"tooltip.anvilcraft.item.fe_collector": "Collecting FE to generate power",
- "tooltip.anvilcraft.item.feather_amulet": "Grants immunity to fall damage",
+ "tooltip.anvilcraft.item.feather_amulet": "Grants immunity to fall damage and Slow Falling; holding Shift removes Slow Falling",
"tooltip.anvilcraft.item.ferrite_core_magnet_block": "Attracting the anvil below, when pushed and pulled by the piston, causes adjacent copper blocks to generate charges",
"tooltip.anvilcraft.item.filter": "Matches items based on configuration, usable in any filter slot",
"tooltip.anvilcraft.item.fish_tank": "It is sturdier than it looks, used for anvil synthesis",
@@ -2181,8 +2184,10 @@
"tooltip.anvilcraft.item.frost_glass": "Frost Metal glass, Explosion proof, No tools required on collect",
"tooltip.anvilcraft.item.frost_grindstone": "Selectively removes individual enchantments, Explosion proof",
"tooltip.anvilcraft.item.frost_metal_block": "A large block of cold-resistant Royal Steel tempered in extreme cold for eons, Explosion proof",
+ "tooltip.anvilcraft.item.frost_metal_heavy_halberd": "Press [%s] to switch modes",
"tooltip.anvilcraft.item.frost_metal_ingot": "A piece of cold-resistant Royal Steel tempered in extreme cold for eons",
"tooltip.anvilcraft.item.frost_metal_nugget": "A small piece of cold-resistant Royal Steel tempered in extreme cold for eons",
+ "tooltip.anvilcraft.item.frost_metal_resonator": "Press [%s] to change modes. Auto mode supports all tools and can “resonance‑mine” most blocks when holding right‑click",
"tooltip.anvilcraft.item.frost_smithing_table": "Works with Permutation and Deformation smithing templates, Explosion proof",
"tooltip.anvilcraft.item.gem_amulet": "Combines effects of all four Gem Amulets",
"tooltip.anvilcraft.item.geode": "Find the surrounding Amethyst Geode when using it",
@@ -2238,6 +2243,7 @@
"tooltip.anvilcraft.item.lead_nugget": "A small piece of dense, heavy metal",
"tooltip.anvilcraft.item.lead_pressure_plate": "Output redstone signal based on the number of mob species above, also a lead plate",
"tooltip.anvilcraft.item.lens": "Use special glass to enchant lasers",
+ "tooltip.anvilcraft.item.lens.shift": "Royal (cyan): drops raw ore blocks instead of raw materials, including Core Shard Ore and Void Stone\nFrost (light blue): drops Experience Gems instead of ores, 10% chance per mined block; Core Shard Ore and Void Stone also convert to EXP\nEmber (yellow): drops smelted results directly; Core Shard Ore and Void Stone have no smelted form and remain unchanged",
"tooltip.anvilcraft.item.levitation_powder": "Slight weightlessness",
"tooltip.anvilcraft.item.light_blue_cement_bucket": "Produced by dyeing Gray Cement",
"tooltip.anvilcraft.item.light_gray_cement_bucket": "Produced by dyeing Gray Cement",
@@ -2259,7 +2265,7 @@
"tooltip.anvilcraft.item.menger_sponge": "Absorbs infinite liquid",
"tooltip.anvilcraft.item.multiphase_matter": "Matter that exists in multiple stable phases simultaneously, switchable under special conditions",
"tooltip.anvilcraft.item.multitool": "Press [Alt] to switch tool modes",
- "tooltip.anvilcraft.item.nature_amulet": "Combines Silence, Cat, Dog, and Feather Amulet effects",
+ "tooltip.anvilcraft.item.nature_amulet": "Combines Silence, Cat, Dog, and Armadillo Amulet effects",
"tooltip.anvilcraft.item.negative_matter": "Negative matter is not antimatter, it is anti gravity matter",
"tooltip.anvilcraft.item.negative_matter_block": "Negative matter is not antimatter, it is anti gravity matter",
"tooltip.anvilcraft.item.negative_matter_nugget": "Negative matter is not antimatter, it is anti gravity matter",
@@ -2285,10 +2291,15 @@
"tooltip.anvilcraft.item.plywood": "Solid wood? Not a chance",
"tooltip.anvilcraft.item.plywood_slab": "Solid wood? Not a chance",
"tooltip.anvilcraft.item.plywood_stairs": "Solid wood? Not a chance",
+ "tooltip.anvilcraft.item.pockets_leggings": "6 pocket slots\nUse the pocket key to swap with your offhand\nEmpty pockets before removing leggings",
"tooltip.anvilcraft.item.polished_heavy_iron_block": "Heavy Iron decorative block, Explosion proof",
"tooltip.anvilcraft.item.polished_heavy_iron_slab": "Heavy Iron decorative block, Explosion proof",
"tooltip.anvilcraft.item.polished_heavy_iron_stairs": "Heavy Iron decorative block, Explosion proof",
- "tooltip.anvilcraft.item.power_converter": "Convert power into FE, consumes %d kW",
+ "tooltip.anvilcraft.item.power_converter_big": "Convert power into FE, consumes %d kW",
+ "tooltip.anvilcraft.item.power_converter_extremely_big": "Convert power into FE, consumes %d kW",
+ "tooltip.anvilcraft.item.power_converter_middle": "Convert power into FE, consumes %d kW",
+ "tooltip.anvilcraft.item.power_converter_small": "Convert power into FE, consumes %d kW",
+ "tooltip.anvilcraft.item.power_converter_super_big": "Convert power into FE, consumes %d kW",
"tooltip.anvilcraft.item.powered_sliding_rail": "Accelerates items, entities, or blocks when powered",
"tooltip.anvilcraft.item.propel_piston": "Integrated piston worm, requires Capacitor or Laser power",
"tooltip.anvilcraft.item.pulse_generator": "Customizes pulse delay and duration",
@@ -2316,11 +2327,11 @@
"tooltip.anvilcraft.item.royal_steel_ingot": "A piece of iron infused with gem magic",
"tooltip.anvilcraft.item.royal_steel_nugget": "A small piece of iron infused with gem magic",
"tooltip.anvilcraft.item.ruby": "Containing the power of fire",
- "tooltip.anvilcraft.item.ruby_amulet": "Grants Fire Resistance",
+ "tooltip.anvilcraft.item.ruby_amulet": "Grants Fire Resistance and Strength I; Strength II while on fire",
"tooltip.anvilcraft.item.ruby_laser": "Emits a laser beam when powered",
"tooltip.anvilcraft.item.ruby_prism": "Deflects or converges laser beams",
"tooltip.anvilcraft.item.sapphire": "Containing the power of frost",
- "tooltip.anvilcraft.item.sapphire_amulet": "Grants Conduit Power",
+ "tooltip.anvilcraft.item.sapphire_amulet": "Grants Conduit Power; Resistance I in water or with a Breathing Helmet or Weatherproof Spacesuit Helmet",
"tooltip.anvilcraft.item.seeds_pack": "Open to obtain a random seed or crop",
"tooltip.anvilcraft.item.shulker_container": "A space-folding container upgraded from a Large Crate",
"tooltip.anvilcraft.item.shulker_container.shift": "Can contain 1024 types of items by default, each type with 65536 unit of items by default\nBreaking it drops the container with its items stored inside\nDrop Space Overcompressors on top and strike with an anvil to expand capacity\nEach one doubles both the type limit and the space per type (up to 4 times)\nCan update to Hyperdimension Storage Station",
@@ -2328,7 +2339,7 @@
"tooltip.anvilcraft.item.shulker_terminal.shift": "Automatically links to Shulker-like storage in priority:\nInventory Shulker Container at first, then all Shulker Boxes, nearest Shulker Container within 64 blocks at last",
"tooltip.anvilcraft.item.sifting_table": "Perform sifting recipes",
"tooltip.anvilcraft.item.sifting_table.shift": "Can be converted to the corresponding mode using a Grindstone, Scaffolding, Iron Trapdoor, or Anvil Hammer",
- "tooltip.anvilcraft.item.silence_amulet": "Silences the wearer",
+ "tooltip.anvilcraft.item.silence_amulet": "Silences the wearer and grants immunity to Darkness",
"tooltip.anvilcraft.item.silver_block": "A large block of highly reflective metal",
"tooltip.anvilcraft.item.silver_ingot": "A piece of highly reflective metal",
"tooltip.anvilcraft.item.silver_nugget": "A small piece of highly reflective metal",
@@ -2392,7 +2403,7 @@
"tooltip.anvilcraft.item.titanium_nugget": "A small piece of strong, lightweight metal",
"tooltip.anvilcraft.item.titanium_pressure_plate": "Output a redstone signal based on the highest durability of the items above, also a titanium plate",
"tooltip.anvilcraft.item.topaz": "Containing the power of lightning",
- "tooltip.anvilcraft.item.topaz_amulet": "Grants immunity to lightning damage",
+ "tooltip.anvilcraft.item.topaz_amulet": "Grants immunity to lightning damage and Haste I",
"tooltip.anvilcraft.item.totem_of_rage": "Grants invulnerability and berserk on fatal damage, death is inevitable after 1 minute",
"tooltip.anvilcraft.item.totem_of_recovery": "Teleports to spawn on death, grants a Recall Pearl to return to death point",
"tooltip.anvilcraft.item.trading_station": "Trading platform for players and villagers",
@@ -2404,6 +2415,8 @@
"tooltip.anvilcraft.item.transcendence_dragon_rod": "Portable block devourer with adjustable range",
"tooltip.anvilcraft.item.transcendence_dragon_rod.shift": "Portable block devourer, left-click to mine, right-click to adjust range, larger range costs more durability",
"tooltip.anvilcraft.item.transcendence_grindstone": "Removes curses and repair costs, selectively removes or transfers multiple enchantments\nImmune to most destruction methods",
+ "tooltip.anvilcraft.item.transcendence_heavy_halberd": "Press [%s] to switch modes",
+ "tooltip.anvilcraft.item.transcendence_resonator": "Press [%s] to change modes. Auto mode supports all tools and can “resonance‑mine” most blocks when holding right‑click",
"tooltip.anvilcraft.item.transcendence_smithing_table": "Performs all smithing operations with no templates required\nExplosion, Wither and Ender Dragon proof",
"tooltip.anvilcraft.item.transcendium_block": "A large block of strong-interaction matter sustained by magic, immune to most forms of destruction",
"tooltip.anvilcraft.item.transcendium_ingot": "A piece of strong-interaction matter sustained by magic, immune to most forms of destruction",
@@ -2424,6 +2437,10 @@
"tooltip.anvilcraft.item.void_matter": "The primordial substance that creates all things, mined from the void, decays outside the void",
"tooltip.anvilcraft.item.void_matter_block": "A chunk of void...",
"tooltip.anvilcraft.item.weathered_copper_pressure_plate": "Redstone signal increases with pressing time, also a copper plate",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_boots": "Immune to fall damage\nHold sneak to charge a jump, up to 4 blocks height\nWalk on still fluid surfaces; sneak to submerge, hold sneak to descend faster\nFull suit: immune to environmental damage except the void; prevents falling into the void",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_chestplate": "Uses 100 kFE per second\nRechargeable from grids or capacitors\nFull suit: immune to environmental damage except the void; prevents falling into the void",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_helmet": "Supplies oxygen underwater and in vacuum\nRemoves underwater mining penalties\nClear vision in all fluids\n%s\nFull suit: immune to environmental damage except the void; prevents falling into the void",
+ "tooltip.anvilcraft.item.weatherproof_spacesuit_leggings": "12 pocket slots\nUse the pocket key to swap with your offhand\nEmpty pockets before removing leggings\nFull suit: immune to environmental damage except the void; prevents falling into the void",
"tooltip.anvilcraft.item.white_cement_bucket": "Produced by dyeing Gray Cement",
"tooltip.anvilcraft.item.white_chocolate_block": "Made by real cocoa butter, step on it to gain Jump Boost",
"tooltip.anvilcraft.item.white_chocolate_slab": "Made by real cocoa butter, step on it to gain Jump Boost",
@@ -2435,9 +2452,11 @@
"tooltip.anvilcraft.item.zinc_ingot": "A piece of lightweight metal",
"tooltip.anvilcraft.item.zinc_nugget": "A small piece of lightweight metal",
"tooltip.anvilcraft.item.zinc_pressure_plate": "Output a redstone signal based on the highest percentage of health of the mobs above, also a zinc plate",
+ "tooltip.anvilcraft.item_count": "%s x%s",
"tooltip.anvilcraft.jade.item_detector": "Detection Range: %d",
"tooltip.anvilcraft.jade.power_information": "Power Grid: %s",
"tooltip.anvilcraft.jade.ruby_prism.power": "Laser level: %d",
+ "tooltip.anvilcraft.key": "[%s]",
"tooltip.anvilcraft.large_cauldron.burning": " burning",
"tooltip.anvilcraft.large_cauldron.fluids": "Fluids:",
"tooltip.anvilcraft.large_cauldron.inputs": "Input Items:",
@@ -2492,8 +2511,7 @@
"tooltip.anvilcraft.redstone.output_to_redstone": " Can Output to Redstone: %d",
"tooltip.anvilcraft.redstone.power": " Power: %d",
"tooltip.anvilcraft.redstone.title": "Redstone Stats:",
- "tooltip.anvilcraft.resonator.desc": "Press [%s] to switch modes. Auto mode supports all tools",
- "tooltip.anvilcraft.resonator.mining_desc": "Press [%s] to change modes. Auto mode supports all tools and can “resonance‑mine” most blocks when holding right‑click",
+ "tooltip.anvilcraft.resin_block.mob": "- %s",
"tooltip.anvilcraft.shulker_container.0": "Inject more Space Overcompressors into",
"tooltip.anvilcraft.shulker_container.1": "Shulker Container to increase its capacity",
"tooltip.anvilcraft.shulker_container.2": "This process is irreversible",
@@ -2523,6 +2541,7 @@
"tooltip.anvilcraft.storage.types.value.infinite": " %s / ∞",
"tooltip.anvilcraft.storage_port.item": "Item:",
"tooltip.anvilcraft.thought": "Press [%s] for more info",
+ "tooltip.anvilcraft.thought.progress": "[%s%s]",
"tooltip.anvilcraft.waiting": "Waiting for syncing",
"tooltip.anvilcraft.wip_block.jade.recipe": "Procedural Recipe: %s",
"tooltip.anvilcraft.wip_block.jade.step_count": "Steps Executed: %d",
diff --git a/src/generated/resources/assets/anvilcraft/models/item/armadillo_amulet.json b/src/generated/resources/assets/anvilcraft/models/item/armadillo_amulet.json
new file mode 100644
index 0000000000..05249c4a78
--- /dev/null
+++ b/src/generated/resources/assets/anvilcraft/models/item/armadillo_amulet.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:item/generated",
+ "textures": {
+ "layer0": "anvilcraft:item/armadillo_amulet"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/jewel_crafting/armadillo_amulet.json b/src/generated/resources/data/anvilcraft/advancement/recipes/jewel_crafting/armadillo_amulet.json
new file mode 100644
index 0000000000..48a3f394cc
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/jewel_crafting/armadillo_amulet.json
@@ -0,0 +1,21 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:jewel_crafting/armadillo_amulet"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:jewel_crafting/armadillo_amulet"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/misc/multiphase_transcendium.json b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/multiphase_transcendium.json
index df0bc2139c..ed2649355f 100644
--- a/src/generated/resources/data/anvilcraft/advancement/recipes/misc/multiphase_transcendium.json
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/multiphase_transcendium.json
@@ -17,11 +17,11 @@
},
"trigger": "minecraft:recipe_unlocked"
},
- "has_transcendium_ingot": {
+ "has_transcendium_nugget": {
"conditions": {
"items": [
{
- "items": "anvilcraft:transcendium_ingot"
+ "items": "anvilcraft:transcendium_nugget"
}
]
},
@@ -32,7 +32,7 @@
[
"has_the_recipe",
"has_multiphase_matter",
- "has_transcendium_ingot"
+ "has_transcendium_nugget"
]
],
"rewards": {
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_0.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_0.json
new file mode 100644
index 0000000000..6432123312
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_0.json
@@ -0,0 +1,21 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/ember_metal_block_0"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/ember_metal_block_0"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_1.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_1.json
new file mode 100644
index 0000000000..57c9483e81
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_1.json
@@ -0,0 +1,21 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/ember_metal_block_1"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/ember_metal_block_1"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_2.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_2.json
new file mode 100644
index 0000000000..ae2f3df982
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_2.json
@@ -0,0 +1,21 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/ember_metal_block_2"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/ember_metal_block_2"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_3.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_3.json
new file mode 100644
index 0000000000..b2fbb8cea6
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/ember_metal_block_3.json
@@ -0,0 +1,21 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/ember_metal_block_3"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/ember_metal_block_3"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_0.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_0.json
new file mode 100644
index 0000000000..fe501aee45
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_0.json
@@ -0,0 +1,32 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_royal_steel_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:royal_steel_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/frost_metal_block_0"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe",
+ "has_royal_steel_block"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/frost_metal_block_0"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_1.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_1.json
new file mode 100644
index 0000000000..a61fbfaa13
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_1.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_iron_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "minecraft:iron_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_royal_steel_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:royal_steel_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/frost_metal_block_1"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe",
+ "has_royal_steel_block",
+ "has_iron_block"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/frost_metal_block_1"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_2.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_2.json
new file mode 100644
index 0000000000..7e85dd2356
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_2.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_royal_steel_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:royal_steel_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_silver_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:silver_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/frost_metal_block_2"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe",
+ "has_royal_steel_block",
+ "has_silver_block"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/frost_metal_block_2"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_3.json b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_3.json
new file mode 100644
index 0000000000..ea00320429
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/advancement/recipes/time_warp/frost_metal_block_3.json
@@ -0,0 +1,43 @@
+{
+ "parent": "minecraft:recipes/root",
+ "criteria": {
+ "has_royal_steel_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:royal_steel_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ },
+ "has_the_recipe": {
+ "conditions": {
+ "recipe": "anvilcraft:time_warp/frost_metal_block_3"
+ },
+ "trigger": "minecraft:recipe_unlocked"
+ },
+ "has_titanium_block": {
+ "conditions": {
+ "items": [
+ {
+ "items": "anvilcraft:titanium_block"
+ }
+ ]
+ },
+ "trigger": "minecraft:inventory_changed"
+ }
+ },
+ "requirements": [
+ [
+ "has_the_recipe",
+ "has_royal_steel_block",
+ "has_titanium_block"
+ ]
+ ],
+ "rewards": {
+ "recipes": [
+ "anvilcraft:time_warp/frost_metal_block_3"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/anvilcraft/amulet_definition/armadillo.json b/src/generated/resources/data/anvilcraft/anvilcraft/amulet_definition/armadillo.json
new file mode 100644
index 0000000000..aacfadc9c5
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/anvilcraft/amulet_definition/armadillo.json
@@ -0,0 +1,12 @@
+{
+ "type": "anvilcraft:normal",
+ "amulet": {
+ "count": 1,
+ "id": "anvilcraft:armadillo_amulet"
+ },
+ "obtains": {
+ "source_entity": {
+ "type": "#anvilcraft:amulet_valid/armadillo"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/four_to_one_smithing/nature_amulet.json b/src/generated/resources/data/anvilcraft/recipe/four_to_one_smithing/nature_amulet.json
index 15fab4d9da..58a66f9560 100644
--- a/src/generated/resources/data/anvilcraft/recipe/four_to_one_smithing/nature_amulet.json
+++ b/src/generated/resources/data/anvilcraft/recipe/four_to_one_smithing/nature_amulet.json
@@ -5,7 +5,7 @@
"items": "anvilcraft:silence_amulet"
},
{
- "items": "anvilcraft:feather_amulet"
+ "items": "anvilcraft:armadillo_amulet"
},
{
"items": "anvilcraft:cat_amulet"
diff --git a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json
index 49b54366e0..15cdee68ff 100644
--- a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json
+++ b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor.json
@@ -6,7 +6,7 @@
"type": "anvillib_recipe:has_item_ingredient",
"item": {
"count": 2,
- "items": "#c:plates/iron"
+ "items": "minecraft:copper_block"
},
"offset": [
0.0,
diff --git a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor_empty.json b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor_empty.json
index cfb4a2508f..4c53de1912 100644
--- a/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor_empty.json
+++ b/src/generated/resources/data/anvilcraft/recipe/item_compress/supercapacitor_empty.json
@@ -3,7 +3,7 @@
"ingredients": [
{
"count": 2,
- "items": "#c:plates/iron"
+ "items": "minecraft:copper_block"
},
{
"items": "anvilcraft:resin_block",
diff --git a/src/generated/resources/data/anvilcraft/recipe/jewel_crafting/armadillo_amulet.json b/src/generated/resources/data/anvilcraft/recipe/jewel_crafting/armadillo_amulet.json
new file mode 100644
index 0000000000..cb21e5f5b0
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/jewel_crafting/armadillo_amulet.json
@@ -0,0 +1,72 @@
+{
+ "type": "anvilcraft:jewel_crafting",
+ "ingredients": [
+ {
+ "item": "anvilcraft:silver_ingot"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:spider_eye"
+ },
+ {
+ "item": "minecraft:armadillo_scute"
+ },
+ {
+ "item": "minecraft:armadillo_scute"
+ },
+ {
+ "item": "minecraft:armadillo_scute"
+ },
+ {
+ "item": "minecraft:armadillo_scute"
+ }
+ ],
+ "result": {
+ "count": 1,
+ "id": "anvilcraft:armadillo_amulet"
+ }
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/multiphase_transcendium.json b/src/generated/resources/data/anvilcraft/recipe/multiphase_transcendium.json
index 971a51251c..56ece74077 100644
--- a/src/generated/resources/data/anvilcraft/recipe/multiphase_transcendium.json
+++ b/src/generated/resources/data/anvilcraft/recipe/multiphase_transcendium.json
@@ -1,13 +1,18 @@
{
- "type": "minecraft:crafting_shapeless",
+ "type": "minecraft:crafting_shaped",
"category": "misc",
- "ingredients": [
- {
+ "key": {
+ "M": {
"item": "anvilcraft:multiphase_matter"
},
- {
- "item": "anvilcraft:transcendium_ingot"
+ "N": {
+ "item": "anvilcraft:transcendium_nugget"
}
+ },
+ "pattern": [
+ "NNN",
+ "NMN",
+ "NNN"
],
"result": {
"count": 1,
diff --git a/src/generated/resources/data/anvilcraft/recipe/permutation/nature_amulets.json b/src/generated/resources/data/anvilcraft/recipe/permutation/nature_amulets.json
index 84bd42d112..57ce972a6a 100644
--- a/src/generated/resources/data/anvilcraft/recipe/permutation/nature_amulets.json
+++ b/src/generated/resources/data/anvilcraft/recipe/permutation/nature_amulets.json
@@ -3,7 +3,7 @@
"input": {
"items": [
"anvilcraft:silence_amulet",
- "anvilcraft:feather_amulet",
+ "anvilcraft:armadillo_amulet",
"anvilcraft:cat_amulet",
"anvilcraft:dog_amulet"
]
@@ -25,7 +25,7 @@
"items": "minecraft:cake"
}
},
- "result": "anvilcraft:feather_amulet"
+ "result": "anvilcraft:armadillo_amulet"
},
{
"material": {
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_0.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_0.json
new file mode 100644
index 0000000000..725a8a60f7
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_0.json
@@ -0,0 +1,18 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "anvilcraft:oil",
+ "ignited": true,
+ "ingredients": [
+ {
+ "count": 9,
+ "items": "#anvilcraft:netherite_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 3,
+ "id": "anvilcraft:ember_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_1.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_1.json
new file mode 100644
index 0000000000..6698e6b7ee
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_1.json
@@ -0,0 +1,21 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "anvilcraft:oil",
+ "ignited": true,
+ "ingredients": [
+ {
+ "count": 9,
+ "items": "#anvilcraft:netherite_block"
+ },
+ {
+ "items": "anvilcraft:earth_core_shard_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 4,
+ "id": "anvilcraft:ember_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_2.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_2.json
new file mode 100644
index 0000000000..9383eab8be
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_2.json
@@ -0,0 +1,22 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "anvilcraft:oil",
+ "ignited": true,
+ "ingredients": [
+ {
+ "count": 9,
+ "items": "#anvilcraft:netherite_block"
+ },
+ {
+ "count": 2,
+ "items": "anvilcraft:earth_core_shard_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 5,
+ "id": "anvilcraft:ember_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_3.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_3.json
new file mode 100644
index 0000000000..545a08a7c7
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/ember_metal_block_3.json
@@ -0,0 +1,22 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "anvilcraft:oil",
+ "ignited": true,
+ "ingredients": [
+ {
+ "count": 9,
+ "items": "#anvilcraft:netherite_block"
+ },
+ {
+ "count": 3,
+ "items": "anvilcraft:earth_core_shard_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 6,
+ "id": "anvilcraft:ember_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_0.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_0.json
new file mode 100644
index 0000000000..618139701e
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_0.json
@@ -0,0 +1,16 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "minecraft:powder_snow",
+ "ingredients": [
+ {
+ "count": 3,
+ "items": "anvilcraft:royal_steel_block"
+ }
+ ],
+ "results": [
+ {
+ "id": "anvilcraft:frost_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_1.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_1.json
new file mode 100644
index 0000000000..f314ccf72b
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_1.json
@@ -0,0 +1,20 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "minecraft:powder_snow",
+ "ingredients": [
+ {
+ "count": 3,
+ "items": "anvilcraft:royal_steel_block"
+ },
+ {
+ "items": "minecraft:iron_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 2,
+ "id": "anvilcraft:frost_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_2.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_2.json
new file mode 100644
index 0000000000..397f177443
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_2.json
@@ -0,0 +1,20 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "minecraft:powder_snow",
+ "ingredients": [
+ {
+ "count": 3,
+ "items": "anvilcraft:royal_steel_block"
+ },
+ {
+ "items": "anvilcraft:silver_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 4,
+ "id": "anvilcraft:frost_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_3.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_3.json
new file mode 100644
index 0000000000..2cb4df929c
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_block_3.json
@@ -0,0 +1,20 @@
+{
+ "type": "anvilcraft:time_warp",
+ "consume": 9000,
+ "fluid": "minecraft:powder_snow",
+ "ingredients": [
+ {
+ "count": 3,
+ "items": "anvilcraft:royal_steel_block"
+ },
+ {
+ "items": "anvilcraft:titanium_block"
+ }
+ ],
+ "results": [
+ {
+ "count": 8,
+ "id": "anvilcraft:frost_metal_block"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_2.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_2.json
index 7fe85dc402..88dab5fce9 100644
--- a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_2.json
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_2.json
@@ -13,7 +13,7 @@
],
"results": [
{
- "count": 3,
+ "count": 4,
"id": "anvilcraft:frost_metal_ingot"
}
]
diff --git a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_3.json b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_3.json
index b2e7cae82c..2de5fbb1d8 100644
--- a/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_3.json
+++ b/src/generated/resources/data/anvilcraft/recipe/time_warp/frost_metal_ingot_3.json
@@ -13,7 +13,7 @@
],
"results": [
{
- "count": 4,
+ "count": 8,
"id": "anvilcraft:frost_metal_ingot"
}
]
diff --git a/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid.json b/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid.json
index 0131169bef..a0d92f44a0 100644
--- a/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid.json
+++ b/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid.json
@@ -12,6 +12,10 @@
"id": "#anvilcraft:amulet_valid/anvil",
"required": false
},
+ {
+ "id": "#anvilcraft:amulet_valid/armadillo",
+ "required": false
+ },
{
"id": "#anvilcraft:amulet_valid/cat",
"required": false
diff --git a/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid/armadillo.json b/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid/armadillo.json
new file mode 100644
index 0000000000..16a677d7eb
--- /dev/null
+++ b/src/generated/resources/data/anvilcraft/tags/entity_type/amulet_valid/armadillo.json
@@ -0,0 +1,6 @@
+{
+ "values": [
+ "minecraft:spider",
+ "minecraft:cave_spider"
+ ]
+}
\ No newline at end of file
diff --git a/src/generated/resources/data/anvilcraft/tags/item/amulet.json b/src/generated/resources/data/anvilcraft/tags/item/amulet.json
index 606eb6630d..16b279a7b5 100644
--- a/src/generated/resources/data/anvilcraft/tags/item/amulet.json
+++ b/src/generated/resources/data/anvilcraft/tags/item/amulet.json
@@ -7,6 +7,7 @@
"anvilcraft:anvil_amulet",
"anvilcraft:comrade_amulet",
"anvilcraft:feather_amulet",
+ "anvilcraft:armadillo_amulet",
"anvilcraft:cat_amulet",
"anvilcraft:dog_amulet",
"anvilcraft:silence_amulet",
diff --git a/src/main/java/dev/dubhe/anvilcraft/api/pointer/BlockPointer.java b/src/main/java/dev/dubhe/anvilcraft/api/pointer/BlockPointer.java
index cdfc717490..892e9a32ab 100644
--- a/src/main/java/dev/dubhe/anvilcraft/api/pointer/BlockPointer.java
+++ b/src/main/java/dev/dubhe/anvilcraft/api/pointer/BlockPointer.java
@@ -151,6 +151,13 @@ private boolean moveToPos(ServerLevel level, BlockPos pos, BlockState targetStat
targetPart.state()
)
: clearWaterlogged(sourcePart.state());
+ if (applyBlueprintRules && partTargetState.hasProperty(BlockStateProperties.POWERED)) {
+ // 保留原供电状态,使放置后的邻居回调能识别蓝图搬运前后的信号变化。
+ partTargetState = partTargetState.setValue(
+ BlockStateProperties.POWERED,
+ sourcePart.state().getValue(BlockStateProperties.POWERED)
+ );
+ }
// 副部件(门/植物的上半、床尾等)的存活取决于同批搬运的主部件,此处跳过存活校验
if (!BlockPlacementUtil.isSecondaryMultiblockPart(partTargetState)
&& !partTargetState.canSurvive(level, targetPart.pos())) {
@@ -214,6 +221,13 @@ private boolean moveToPos(ServerLevel level, BlockPos pos, BlockState targetStat
level.updateNeighbourForOutputSignal(part.targetPos(), part.targetState().getBlock());
}
}
+ // 邻居更新不会通知搬运的方块自身;全部部件和方块实体就位后重新检查目标位置的信号。
+ for (MovingPart part : movingParts) {
+ for (Direction direction : Direction.values()) {
+ BlockPos neighborPos = part.targetPos().relative(direction);
+ level.neighborChanged(part.targetPos(), level.getBlockState(neighborPos).getBlock(), neighborPos);
+ }
+ }
if (BlockPlacementUtil.getPresentMultiblockParts(level, pos, targetState).size() != targetParts.size()) {
// 目标方块被自身 onPlace 转化为实体(如 TNT 被红石激活引爆)时源方块已被消耗,
// 与取物模式一致视为放置成功,不再回滚源方块,避免刷方块
diff --git a/src/main/java/dev/dubhe/anvilcraft/api/thought/Thinkable.java b/src/main/java/dev/dubhe/anvilcraft/api/thought/Thinkable.java
index a6311788ab..63759ac516 100644
--- a/src/main/java/dev/dubhe/anvilcraft/api/thought/Thinkable.java
+++ b/src/main/java/dev/dubhe/anvilcraft/api/thought/Thinkable.java
@@ -1,43 +1,9 @@
package dev.dubhe.anvilcraft.api.thought;
-import net.minecraft.ChatFormatting;
-import net.minecraft.client.Minecraft;
-import net.minecraft.network.chat.Component;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
-import net.neoforged.neoforgespi.Environment;
-
-import java.util.List;
public interface Thinkable {
- @OnlyIn(Dist.CLIENT)
- default void appendHoverText(List tooltipComponents) {
- if (!Environment.get().getDist().isClient()) {
- return;
- }
- long lastThoughtTime = ThoughtManager.getLastThoughtTime();
- if (lastThoughtTime <= 0) {
- tooltipComponents.add(
- Component.translatable(
- "tooltip.anvilcraft.thought",
- Component.keybind("key.anvilcraft.thought")
- ).withStyle(ChatFormatting.GRAY)
- );
- return;
- }
- Minecraft minecraft = Minecraft.getInstance();
- long curTime = minecraft.gui.getGuiTicks();
- long deltaTime = curTime - lastThoughtTime;
- final int maxPlaceholderCount = 20;
- final double maxSeconds = ThoughtManager.getMAX_SECONDS();
- int placeholderCount = (int) Math.floor(Math.min(deltaTime, 20 * maxSeconds) / (20 * maxSeconds) * maxPlaceholderCount);
- int blankCount = maxPlaceholderCount - placeholderCount;
- StringBuilder builder = new StringBuilder("[");
- builder.append("||".repeat(Math.max(0, placeholderCount)));
- builder.append(" ".repeat(Math.max(0, blankCount)));
- tooltipComponents.add(Component.literal(builder.append("]").toString()).withStyle(ChatFormatting.GRAY));
- }
-
@OnlyIn(Dist.CLIENT)
default void onThought() {
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/FluidTankItemTooltip.java b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/FluidTankItemTooltip.java
index 3babb74df2..fc2c2bf2df 100644
--- a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/FluidTankItemTooltip.java
+++ b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/FluidTankItemTooltip.java
@@ -1,22 +1,13 @@
package dev.dubhe.anvilcraft.api.tooltip;
import dev.dubhe.anvilcraft.inventory.tooltip.FluidTankTooltip;
-import dev.dubhe.anvilcraft.util.UnitUtil;
-import net.minecraft.ChatFormatting;
-import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
-import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.tooltip.TooltipComponent;
-import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
-import net.neoforged.neoforge.fluids.FluidStack;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
import java.util.Optional;
public final class FluidTankItemTooltip {
@@ -28,17 +19,6 @@ public final class FluidTankItemTooltip {
private FluidTankItemTooltip() {
}
- public static void appendFixedTank(
- ItemStack stack,
- Item.TooltipContext context,
- List tooltip,
- int capacity
- ) {
- CompoundTag tankTag = getTankTag(stack);
- List fluids = readSingleFluid(tankTag, Objects.requireNonNull(context.registries()), capacity);
- append(tooltip, fluids, totalAmount(fluids), capacity);
- }
-
/** 单流体储罐的 tooltip 数据(携带 Tank NBT,客户端解析渲染为 图标+文字)。 */
public static Optional singleFluidTooltipImage(
ItemStack stack, int baseCapacity, int enhancedCapacity
@@ -76,54 +56,4 @@ private static CompoundTag getBlockEntityData(ItemStack stack) {
return data.copyTag();
}
- private static List readSingleFluid(
- CompoundTag tankTag,
- HolderLookup.Provider registries,
- int maxAmount
- ) {
- if (!tankTag.contains(TAG_FLUID, Tag.TAG_COMPOUND)) return new ArrayList<>();
- FluidStack fluid = FluidStack.parseOptional(registries, tankTag.getCompound(TAG_FLUID));
- if (fluid.isEmpty()) return new ArrayList<>();
- fluid.setAmount(Math.min(fluid.getAmount(), maxAmount));
- return new ArrayList<>(List.of(new TooltipFluid(fluid, false)));
- }
-
- private static long totalAmount(List fluids) {
- long amount = 0;
- for (TooltipFluid fluid : fluids) {
- amount += fluid.fluid().getAmount();
- }
- return amount;
- }
-
- private static void append(
- List tooltip,
- List fluids,
- long amount,
- int capacity
- ) {
- if (!fluids.isEmpty()) {
- tooltip.add(Component.translatable("tooltip.anvilcraft.fluid_tank.fluid").withStyle(ChatFormatting.BLUE));
- for (TooltipFluid stored : fluids) {
- String fluidAmount = stored.infinite()
- ? UnitUtil.INFINITE_POWER
- : UnitUtil.fluidUnit(stored.fluid().getAmount(), false);
- tooltip.add(Component.literal(" ")
- .append(stored.fluid().getHoverName())
- .append(Component.literal(" " + fluidAmount))
- .withStyle(ChatFormatting.GRAY));
- }
- }
-
- tooltip.add(Component.translatable("tooltip.anvilcraft.fluid_tank.capacity").withStyle(ChatFormatting.BLUE));
- Component capacityLine = Component.translatable(
- "tooltip.anvilcraft.fluid_tank.capacity.value",
- UnitUtil.fluidUnit(amount, false),
- UnitUtil.fluidUnit(capacity, false)
- );
- tooltip.add(capacityLine.copy().withStyle(ChatFormatting.GRAY));
- }
-
- private record TooltipFluid(FluidStack fluid, boolean infinite) {
- }
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
index 3d73d33440..5610b93f53 100644
--- a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
+++ b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
@@ -17,8 +17,7 @@
import dev.dubhe.anvilcraft.init.item.ModItemTags;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.inventory.tooltip.StorageTooltip;
-import dev.dubhe.anvilcraft.item.amulet.AmuletBoxItem;
-import dev.dubhe.anvilcraft.item.property.component.BoxContents;
+import dev.dubhe.anvilcraft.item.EquipmentArmorItem;
import dev.dubhe.anvilcraft.item.property.component.StorageRef;
import dev.dubhe.anvilcraft.rpc.StorageServerStub;
import dev.dubhe.anvilcraft.util.TooltipUtil;
@@ -47,24 +46,55 @@ public class ItemTooltipManager {
private static final Map- NORMAL = Maps.newHashMap();
private static final Map
- SHIFT = Maps.newHashMap();
+ private static final Map
- NORMAL_ARGUMENTS = Maps.newHashMap();
private static final Map STORAGE_USAGE = new ConcurrentHashMap<>();
private static final Map STORAGE_USAGE_TIMES = new ConcurrentHashMap<>();
private static final Set STORAGE_USAGE_PENDING = ConcurrentHashMap.newKeySet();
private static final long STORAGE_USAGE_REFRESH_INTERVAL = 2000L;
static {
- NORMAL.put(ModItems.BUILDING_ROD.get(), """
- Place blocks and blueprints in bulk
- Grants crab claw reach while carried
- """);
- SHIFT.put(ModItems.BUILDING_ROD.get(), """
- Hold in either hand alongside blocks, a filter, a fluid bucket or a structure disk
- Hold use and drag to select a box of blocks, then release to place them
- Carry a book for a missing-material list , hold Shift to places the available part of a blueprint
- Ctrl+Z undoes the last placement
- Buckets fill areas and waterlog blocks, 2 B per fill
- Consume 100 FE per block, recharged from capacitors
- """);
+ final String breathing = "Supplies oxygen underwater and in vacuum\nRemoves underwater mining penalties";
+ final String pockets = "%s pocket slots\nUse the pocket key to swap with your offhand\nEmpty pockets before removing leggings";
+ final String boots = "Immune to fall damage\nHold sneak to charge a jump, up to 4 blocks height";
+ final String fullSuit = "\nFull suit: immune to environmental damage except the void; prevents falling into the void";
+ NORMAL.put(ModItems.BREATHING_HELMET.get(), breathing);
+ NORMAL.put(ModItems.POCKETS_LEGGINGS.get(), pockets.formatted(6));
+ NORMAL.put(ModItems.BUFFER_BOOTS.get(), boots);
+ NORMAL.put(ModItems.WEATHERPROOF_SPACESUIT_HELMET.get(), breathing + "\nClear vision in all fluids\n%s" + fullSuit);
+ NORMAL_ARGUMENTS.put(ModItems.WEATHERPROOF_SPACESUIT_HELMET.get(),
+ new Object[]{Component.translatable("effect.minecraft.night_vision")});
+ NORMAL.put(ModItems.WEATHERPROOF_SPACESUIT_CHESTPLATE.get(),
+ "Uses 100 kFE per second\nRechargeable from grids or capacitors" + fullSuit);
+ NORMAL.put(ModItems.WEATHERPROOF_SPACESUIT_LEGGINGS.get(), pockets.formatted(12) + fullSuit);
+ NORMAL.put(ModItems.WEATHERPROOF_SPACESUIT_BOOTS.get(), boots
+ + "\nWalk on still fluid surfaces; sneak to submerge, hold sneak to descend faster" + fullSuit);
+ for (Item item : List.of(ModItems.FROST_METAL_HEAVY_HALBERD.get(), ModItems.EMBER_METAL_HEAVY_HALBERD.get(),
+ ModItems.TRANSCENDENCE_HEAVY_HALBERD.get())) {
+ NORMAL.put(item, "Press [%s] to switch modes");
+ NORMAL_ARGUMENTS.put(item, new Object[]{Component.keybind("key.anvilcraft.switch_tool_mode")});
+ }
+ for (Item item : List.of(ModItems.FROST_METAL_RESONATOR.get(), ModItems.EMBER_METAL_RESONATOR.get(),
+ ModItems.TRANSCENDENCE_RESONATOR.get())) {
+ NORMAL.put(item, "Press [%s] to change modes. Auto mode supports all tools"
+ + " and can “resonance‑mine” most blocks when holding right‑click");
+ NORMAL_ARGUMENTS.put(item, new Object[]{Component.keybind("key.anvilcraft.switch_tool_mode")});
+ }
+ NORMAL.put(ModBlocks.POWER_CONVERTER_SMALL.asItem(), "Convert power into FE, consumes %d kW");
+ NORMAL_ARGUMENTS.put(ModBlocks.POWER_CONVERTER_SMALL.asItem(), new Object[]{PowerConverterSmallBlock.INPUT_TIME});
+ NORMAL.put(ModBlocks.POWER_CONVERTER_MIDDLE.asItem(), "Convert power into FE, consumes %d kW");
+ NORMAL_ARGUMENTS.put(ModBlocks.POWER_CONVERTER_MIDDLE.asItem(), new Object[]{PowerConverterMiddleBlock.INPUT_TIME});
+ NORMAL.put(ModBlocks.POWER_CONVERTER_BIG.asItem(), "Convert power into FE, consumes %d kW");
+ NORMAL_ARGUMENTS.put(ModBlocks.POWER_CONVERTER_BIG.asItem(), new Object[]{PowerConverterBigBlock.INPUT_TIME});
+ NORMAL.put(ModBlocks.POWER_CONVERTER_SUPER_BIG.asItem(), "Convert power into FE, consumes %d kW");
+ NORMAL_ARGUMENTS.put(ModBlocks.POWER_CONVERTER_SUPER_BIG.asItem(), new Object[]{PowerConverterSuperBigBlock.INPUT_TIME});
+ NORMAL.put(ModBlocks.POWER_CONVERTER_EXTREMELY_BIG.asItem(), "Convert power into FE, consumes %d kW");
+ NORMAL_ARGUMENTS.put(ModBlocks.POWER_CONVERTER_EXTREMELY_BIG.asItem(), new Object[]{PowerConverterExtremelyBigBlock.INPUT_TIME});
+ SHIFT.put(ModBlocks.LENS.asItem(), """
+ Royal (cyan): drops raw ore blocks instead of raw materials, including Core Shard Ore and Void Stone
+ Frost (light blue): drops Experience Gems instead of ores, 10% chance per mined block; Core Shard Ore and Void Stone also convert to EXP
+ Ember (yellow): drops smelted results directly; Core Shard Ore and Void Stone have no smelted form and remain unchanged""");
+ NORMAL.put(ModItems.BUILDING_ROD.get(),
+ "Place blocks in bulk and build blueprints quickly; increases reach while carried");
NORMAL.put(ModItems.MAGNET.get(), "Attract surrounding items when use");
NORMAL.put(ModItems.GEODE.get(), "Find the surrounding Amethyst Geode when using it");
NORMAL.put(ModItems.ANVIL_HAMMER.get(), "It's a hammer, an anvil, a wrench, goggles, and a mace");
@@ -321,20 +351,27 @@ public class ItemTooltipManager {
NORMAL.put(ModBlocks.PROPEL_PISTON.asItem(), "Integrated piston worm, requires Capacitor or Laser power");
NORMAL.put(ModBlocks.PULSE_GENERATOR.asItem(), "Customizes pulse delay and duration");
NORMAL.put(ModBlocks.ADVANCED_COMPARATOR.asItem(), "Supports Hysteresis and Window comparison modes");
- NORMAL.put(ModItems.EMERALD_AMULET.get(), "Grants Hero of the Village");
- NORMAL.put(ModItems.TOPAZ_AMULET.get(), "Grants immunity to lightning damage");
- NORMAL.put(ModItems.RUBY_AMULET.get(), "Grants Fire Resistance");
- NORMAL.put(ModItems.SAPPHIRE_AMULET.get(), "Grants Conduit Power");
- NORMAL.put(ModItems.ANVIL_AMULET.get(), "Grants immunity to anvil damage");
- NORMAL.put(ModItems.FEATHER_AMULET.get(), "Grants immunity to fall damage");
- NORMAL.put(ModItems.CAT_AMULET.get(), "Scares away Creepers and Phantoms");
- NORMAL.put(ModItems.DOG_AMULET.get(), "Scares away Skeletons");
- NORMAL.put(ModItems.SILENCE_AMULET.get(), "Silences the wearer");
+ NORMAL.put(ModItems.EMERALD_AMULET.get(), "Villagers offer discounts; Iron Golems never become hostile to the wearer");
+ NORMAL.put(ModItems.TOPAZ_AMULET.get(), "Grants immunity to lightning damage and Haste I");
+ NORMAL.put(ModItems.RUBY_AMULET.get(), "Grants Fire Resistance and Strength I; Strength II while on fire");
+ NORMAL.put(
+ ModItems.SAPPHIRE_AMULET.get(),
+ "Grants Conduit Power; Resistance I in water or with a Breathing Helmet or Weatherproof Spacesuit Helmet"
+ );
+ NORMAL.put(
+ ModItems.ANVIL_AMULET.get(),
+ "Grants immunity to anvil damage, knockback, Levitation, and celestial gravity from the Celestial Forging Anvil"
+ );
+ NORMAL.put(ModItems.FEATHER_AMULET.get(), "Grants immunity to fall damage and Slow Falling; holding Shift removes Slow Falling");
+ NORMAL.put(ModItems.ARMADILLO_AMULET.get(), "Scares away Spiders; grants Resistance II while holding Shift");
+ NORMAL.put(ModItems.CAT_AMULET.get(), "Scares away Creepers and Phantoms; tame wild Cats with one empty-hand interaction");
+ NORMAL.put(ModItems.DOG_AMULET.get(), "Scares away Skeletons; tame wild Wolves with one empty-hand interaction");
+ NORMAL.put(ModItems.SILENCE_AMULET.get(), "Silences the wearer and grants immunity to Darkness");
NORMAL.put(
ModItems.ABNORMAL_AMULET.get(),
- "Prevents damage from carrying Uranium, Plutonium, Floating Powder, Cursed Gold items"
+ "Prevents harmful effects from food and from carrying Uranium, Plutonium, Floating Powder, or Cursed Gold items"
);
- NORMAL.put(ModItems.NATURE_AMULET.get(), "Combines Silence, Cat, Dog, and Feather Amulet effects");
+ NORMAL.put(ModItems.NATURE_AMULET.get(), "Combines Silence, Cat, Dog, and Armadillo Amulet effects");
NORMAL.put(ModItems.GEM_AMULET.get(), "Combines effects of all four Gem Amulets");
NORMAL.put(ModItems.CAPACITOR.asItem(), "8 MFE stored");
NORMAL.put(ModItems.CAPACITOR_EMPTY.asItem(), "8 MFE capacity");
@@ -987,15 +1024,9 @@ public static void addTooltip(ItemStack stack, List tooltip) {
if (stack.has(ModComponents.FIRE_REFORGING)) {
propertyTooltip("fire_reforging", tooltip, ChatFormatting.GOLD);
}
- if (SHIFT.containsKey(item) || item == ModBlocks.LENS.asItem()) {
+ if (SHIFT.containsKey(item)) {
if (Screen.hasShiftDown()) {
- if (item == ModBlocks.LENS.asItem()) {
- tooltip.add(1, Component.literal("Ember (yellow): drops smelted results directly; Core Shard Ore and Void Stone have no smelted form and remain unchanged").withColor(0xFFAA00));
- tooltip.add(1, Component.literal("Frost (light blue): drops Experience Gems instead of ores, 10% chance per mined block; Core Shard Ore and Void Stone also convert to EXP").withColor(0xB4F0F6));
- tooltip.add(1, Component.literal("Royal (cyan): drops raw ore blocks instead of raw materials, including Core Shard Ore and Void Stone").withColor(0x00FFBF));
- } else {
- addShiftTooltip(tooltip, item);
- }
+ addShiftTooltip(tooltip, item);
} else {
if (NORMAL.containsKey(item)) {
addNormalTooltip(tooltip, item);
@@ -1005,7 +1036,8 @@ public static void addTooltip(ItemStack stack, List tooltip) {
1 + anvilCraftLines,
Component.translatable(
"tooltip.anvilcraft.press_key",
- Component.literal("[Shift]").withStyle(ChatFormatting.WHITE)
+ Component.translatable("tooltip.anvilcraft.key", Component.translatable("key.keyboard.left.shift"))
+ .withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.DARK_GRAY)
);
}
@@ -1017,41 +1049,6 @@ public static void addTooltip(ItemStack stack, List tooltip) {
UnitUtil.energyUnit(stack.getOrDefault(ModComponents.STORED_ENERGY, 0), Screen.hasShiftDown()))
.withStyle(ChatFormatting.GRAY));
}
- if (stack.is(ModBlocks.POWER_CONVERTER_SMALL.asItem())) {
- tooltip.add(
- 1,
- Component.translatable("tooltip.anvilcraft.item.power_converter", PowerConverterSmallBlock.INPUT_TIME)
- .withStyle(ChatFormatting.GRAY)
- );
- }
- if (stack.is(ModBlocks.POWER_CONVERTER_MIDDLE.asItem())) {
- tooltip.add(
- 1,
- Component.translatable("tooltip.anvilcraft.item.power_converter", PowerConverterMiddleBlock.INPUT_TIME)
- .withStyle(ChatFormatting.GRAY)
- );
- }
- if (stack.is(ModBlocks.POWER_CONVERTER_BIG.asItem())) {
- tooltip.add(
- 1,
- Component.translatable("tooltip.anvilcraft.item.power_converter", PowerConverterBigBlock.INPUT_TIME)
- .withStyle(ChatFormatting.GRAY)
- );
- }
- if (stack.is(ModBlocks.POWER_CONVERTER_SUPER_BIG.asItem())) {
- tooltip.add(
- 1,
- Component.translatable("tooltip.anvilcraft.item.power_converter", PowerConverterSuperBigBlock.INPUT_TIME)
- .withStyle(ChatFormatting.GRAY)
- );
- }
- if (stack.is(ModBlocks.POWER_CONVERTER_EXTREMELY_BIG.asItem())) {
- tooltip.add(
- 1,
- Component.translatable("tooltip.anvilcraft.item.power_converter", PowerConverterExtremelyBigBlock.INPUT_TIME)
- .withStyle(ChatFormatting.GRAY)
- );
- }
if (stack.is(ModItemTags.REINFORCED_CONCRETE)) {
tooltip.add(
1,
@@ -1059,13 +1056,6 @@ public static void addTooltip(ItemStack stack, List tooltip) {
.withStyle(ChatFormatting.GRAY)
);
}
- if (stack.is(ModItems.AMULET_BOX.asItem())) {
- BoxContents contents = stack.getOrDefault(ModComponents.BOX_CONTENTS, BoxContents.EMPTY);
- tooltip.add(Component.empty());
- tooltip.add(Component.translatable(
- "tooltip.anvilcraft.item.amulet_box.fullness", contents.usage(), AmuletBoxItem.CAPACITY
- ).withStyle(ChatFormatting.GRAY));
- }
}
public static Optional getStorageTooltip(ItemStack stack) {
@@ -1108,8 +1098,8 @@ private static void requestStorageUsage(UUID storageId) {
/**
* 添加翻译后的tooltip,自动将 \n 拆分为多行
*/
- private static void addTranslatedTooltip(List tooltip, String key) {
- List lines = TooltipUtil.translatedLines(key, ChatFormatting.GRAY);
+ private static void addTranslatedTooltip(List tooltip, String key, Object... args) {
+ List lines = TooltipUtil.translatedLines(key, ChatFormatting.GRAY, args);
// 倒序插到下标 1,使多行在 tooltip 开头保持原有顺序
for (int i = lines.size() - 1; i >= 0; i--) {
tooltip.add(1, lines.get(i));
@@ -1117,7 +1107,12 @@ private static void addTranslatedTooltip(List tooltip, String key) {
}
private static void addNormalTooltip(List tooltip, Item item) {
- addTranslatedTooltip(tooltip, getTranslationKey(item));
+ final int previousSize = tooltip.size();
+ addTranslatedTooltip(tooltip, getTranslationKey(item), NORMAL_ARGUMENTS.getOrDefault(item, new Object[0]));
+ if (item instanceof EquipmentArmorItem armor && armor.isWeatherproof() && tooltip.size() > previousSize) {
+ int lastLine = tooltip.size() - previousSize;
+ tooltip.set(lastLine, tooltip.get(lastLine).copy().withStyle(ChatFormatting.AQUA));
+ }
}
private static void addShiftTooltip(List tooltip, Item item) {
@@ -1129,7 +1124,14 @@ private static void addShiftTooltip(List tooltip, Item item) {
).withStyle(ChatFormatting.GRAY));
return;
}
+ final int previousSize = tooltip.size();
addTranslatedTooltip(tooltip, getTranslationKeyShift(item));
+ if (item == ModBlocks.LENS.asItem()) {
+ int[] colors = {0x00FFBF, 0xB4F0F6, 0xFFAA00};
+ for (int i = 0; i < Math.min(colors.length, tooltip.size() - previousSize); i++) {
+ tooltip.set(i + 1, tooltip.get(i + 1).copy().withColor(colors[i]));
+ }
+ }
}
public static String getTranslationKey(Item item) {
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/AncientSeaReefBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/AncientSeaReefBlock.java
new file mode 100644
index 0000000000..06f2a4a45c
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/block/AncientSeaReefBlock.java
@@ -0,0 +1,48 @@
+package dev.dubhe.anvilcraft.block;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.world.item.context.BlockPlaceContext;
+import net.minecraft.world.level.LevelAccessor;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.SimpleWaterloggedBlock;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.StateDefinition;
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+import net.minecraft.world.level.block.state.properties.BooleanProperty;
+import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.level.material.Fluids;
+
+public class AncientSeaReefBlock extends Block implements SimpleWaterloggedBlock {
+ public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
+
+ public AncientSeaReefBlock(Properties properties) {
+ super(properties);
+ this.registerDefaultState(this.defaultBlockState().setValue(WATERLOGGED, false));
+ }
+
+ @Override
+ protected void createBlockStateDefinition(StateDefinition.Builder builder) {
+ builder.add(WATERLOGGED);
+ }
+
+ @Override
+ public BlockState getStateForPlacement(BlockPlaceContext context) {
+ return this.defaultBlockState().setValue(WATERLOGGED, context.getLevel().getFluidState(context.getClickedPos()).is(Fluids.WATER));
+ }
+
+ @Override
+ protected FluidState getFluidState(BlockState state) {
+ return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
+ }
+
+ @Override
+ protected BlockState updateShape(
+ BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos
+ ) {
+ if (state.getValue(WATERLOGGED)) {
+ level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
+ }
+ return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/SmartBlockPlacerBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/SmartBlockPlacerBlockEntity.java
index cc4e5ab20f..3d49ac59aa 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/entity/SmartBlockPlacerBlockEntity.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/SmartBlockPlacerBlockEntity.java
@@ -632,6 +632,10 @@ public BlockPos getBlueprintPosition(int storageIndex) {
return this.getBlueprintLayout().getPosition(storageIndex);
}
+ public BlockPos getBlueprintPosition(int storageIndex, Direction targetFacing, boolean upsideDown) {
+ return this.getBlueprintLayout().getPosition(storageIndex, targetFacing, upsideDown);
+ }
+
public BlockState getBlueprintStateForPlacement(int storageIndex) {
return this.getBlueprintLayout().getState(storageIndex);
}
@@ -657,6 +661,7 @@ private BlockPlacementUtil.BlueprintLayout getBlueprintLayout() {
this.getBlockPos(),
this.getFacing().getOpposite(),
scannerFacing,
+ diskData == null || diskData.autoRotate(),
this.isUpsideDown(),
POSITION_GRID_SIZE,
POSITION_DISTANCE,
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/StoragePortBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/StoragePortBlockEntity.java
index c8c78de9d9..a70acb5648 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/entity/StoragePortBlockEntity.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/StoragePortBlockEntity.java
@@ -12,6 +12,7 @@
import dev.dubhe.anvilcraft.block.entity.storage.StorageBlockEntity;
import dev.dubhe.anvilcraft.block.item.StoragePortBlockItem;
import dev.dubhe.anvilcraft.config.AnvilCraftServerConfig;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
import dev.dubhe.anvilcraft.item.AnvilHammerItem;
import dev.dubhe.anvilcraft.saved.storage.Storages;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
@@ -80,6 +81,7 @@ public class StoragePortBlockEntity extends BlockEntity implements IItemHandlerH
private final ItemStackHandler buffer = new ItemStackHandler(StoragePortBlockEntity.BUFFER_SLOTS) {
@Override
public boolean isItemValid(int slot, ItemStack stack) {
+ if (stack.is(ModBlocks.SHULKER_CONTAINER.asItem())) return false;
ItemStack marked = StoragePortBlockEntity.this.markedItem;
return marked.isEmpty() || ItemStack.isSameItemSameComponents(marked, stack);
}
@@ -284,7 +286,7 @@ public boolean onLeftClick(Player player, List ports) {
public boolean onRightClick(Player player, InteractionHand hand, List ports) {
ItemStack stack = player.getItemInHand(hand);
// 铁砧锤:普通右键不调整任何状态(去标记需长按右键并滑动,见客户端手势)
- if (stack.getItem() instanceof AnvilHammerItem) {
+ if (stack.getItem() instanceof AnvilHammerItem || stack.is(ModBlocks.SHULKER_CONTAINER.asItem())) {
return false;
}
boolean doubleClick = this.isDoubleClick(player);
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/item/ChuteBlockItem.java b/src/main/java/dev/dubhe/anvilcraft/block/item/ChuteBlockItem.java
index 434a8c0763..759cae05a6 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/item/ChuteBlockItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/item/ChuteBlockItem.java
@@ -3,6 +3,7 @@
import dev.dubhe.anvilcraft.api.itemhandler.IItemHandlerHolder;
import dev.dubhe.anvilcraft.block.entity.CreativeCrateBlockEntity;
import dev.dubhe.anvilcraft.block.entity.StoragePortBlockEntity;
+import dev.dubhe.anvilcraft.block.entity.StoragePortConsolidatorBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@@ -27,7 +28,8 @@ public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) {
Level level = context.getLevel();
BlockPos pos = context.getClickedPos();
BlockEntity blockEntity = level.getBlockEntity(pos);
- if (blockEntity instanceof CreativeCrateBlockEntity || blockEntity instanceof StoragePortBlockEntity) {
+ if (blockEntity instanceof CreativeCrateBlockEntity || blockEntity instanceof StoragePortBlockEntity
+ || blockEntity instanceof StoragePortConsolidatorBlockEntity) {
if (isStorageInteraction(context)) return InteractionResult.PASS;
InteractionResult result = this.useOn(context);
return result == InteractionResult.PASS ? InteractionResult.FAIL : result;
@@ -41,7 +43,8 @@ public static boolean isStorageInteraction(UseOnContext context) {
if (context.isSecondaryUseActive()) return false;
BlockEntity blockEntity = context.getLevel().getBlockEntity(context.getClickedPos());
if (!(blockEntity instanceof CreativeCrateBlockEntity)
- && !(blockEntity instanceof StoragePortBlockEntity && context.getHand() == InteractionHand.MAIN_HAND)) {
+ && !((blockEntity instanceof StoragePortBlockEntity || blockEntity instanceof StoragePortConsolidatorBlockEntity)
+ && context.getHand() == InteractionHand.MAIN_HAND)) {
return false;
}
BlockPos pos = context.getClickedPos();
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/item/HasMobBlockItem.java b/src/main/java/dev/dubhe/anvilcraft/block/item/HasMobBlockItem.java
index 262e16990f..a60e9e8faf 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/item/HasMobBlockItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/item/HasMobBlockItem.java
@@ -81,7 +81,8 @@ public void appendHoverText(
Entity entity = HasMobBlockItem.getMobFromItem(level, stack);
if (entity == null) return;
tooltipComponents.add(
- Component.literal("- ").append(entity.getDisplayName()).withStyle(ChatFormatting.DARK_GRAY));
+ Component.translatable("tooltip.anvilcraft.resin_block.mob", entity.getDisplayName())
+ .withStyle(ChatFormatting.DARK_GRAY));
int resentment = savedEntity.isMonster() && entity instanceof LivingEntity livingEntity
? ResentmentUtil.getResentment(livingEntity)
: 0;
diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodObstructions.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodObstructions.java
new file mode 100644
index 0000000000..c3b81ee23a
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodObstructions.java
@@ -0,0 +1,43 @@
+package dev.dubhe.anvilcraft.building;
+
+import dev.dubhe.anvilcraft.network.BuildingRodObstructionPacket;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.shapes.BooleanOp;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.Shapes;
+import net.neoforged.neoforge.network.PacketDistributor;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public final class BuildingRodObstructions {
+ private BuildingRodObstructions() {
+ }
+
+ static Set find(Level level, List cells, CollisionContext context) {
+ Set blockers = new LinkedHashSet<>();
+ for (var cell : cells) {
+ var shape = cell.state().getCollisionShape(level, cell.pos(), context);
+ if (shape.isEmpty()) continue;
+ shape = shape.move(cell.pos().getX(), cell.pos().getY(), cell.pos().getZ());
+ for (Entity entity : level.getEntities((Entity) null, shape.bounds())) {
+ if (!entity.isRemoved() && entity.blocksBuilding
+ && Shapes.joinIsNotEmpty(shape, Shapes.create(entity.getBoundingBox()), BooleanOp.AND)) {
+ blockers.add(entity);
+ }
+ }
+ }
+ return blockers;
+ }
+
+ static boolean reject(ServerPlayer player, List cells) {
+ Set blockers = find(player.level(), cells, CollisionContext.of(player));
+ if (blockers.isEmpty()) return false;
+ PacketDistributor.sendToPlayer(player, new BuildingRodObstructionPacket(blockers.stream().map(Entity::getId).toList()));
+ BuildingRodService.message(player, "blocked");
+ return true;
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodService.java b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodService.java
index 0c47c418e4..2060076b19 100644
--- a/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodService.java
+++ b/src/main/java/dev/dubhe/anvilcraft/building/BuildingRodService.java
@@ -336,7 +336,14 @@ private static List
singleAttempt(BlockItem item, UseOnContext use) {
}
BlockState state = item instanceof LargeCakeBlockItem cake
? cake.getPlacementState(context) : item.getBlock().getStateForPlacement(context);
- if (state == null || !item.canPlace(context, state)) return List.of();
+ if (state == null) return List.of();
+ if (!item.canPlace(context, state)) {
+ if (use.getPlayer() instanceof ServerPlayer player && BuildingRodItem.isHeld(player)) {
+ BuildingRodObstructions.reject(player,
+ List.of(new Cell(context.getClickedPos(), state, new CompoundTag(), List.of())));
+ }
+ return List.of();
+ }
Group group = new Group();
addBoxCells(group, context.getClickedPos(), state);
for (Cell cell : group.cells) {
@@ -511,6 +518,7 @@ static boolean commit(ServerPlayer player, List groups, boolean partial,
message(player, "too_many");
return false;
}
+ if (BuildingRodObstructions.reject(player, groups.stream().flatMap(group -> group.cells.stream()).toList())) return false;
BuildingMaterials exactMaterials = new BuildingMaterials(player);
boolean allowMismatch = groups.stream().anyMatch(group -> exactMaterials.reserve(group, false) == null);
BuildingMaterials materials = new BuildingMaterials(player);
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintClientFiles.java b/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintClientFiles.java
index d33aca7c4c..0bc18a91b9 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintClientFiles.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintClientFiles.java
@@ -1,29 +1,23 @@
package dev.dubhe.anvilcraft.client.building;
-import dev.dubhe.anvilcraft.AnvilCraft;
import dev.dubhe.anvilcraft.building.ConstructionBlueprintException;
import dev.dubhe.anvilcraft.building.StructureSnapshotCodec;
import dev.dubhe.anvilcraft.client.gui.screen.StructureScannerScreen;
import dev.dubhe.anvilcraft.network.StructureScannerFilePacket;
+import dev.dubhe.anvilcraft.network.StructureScannerFilePacket.Action;
import dev.dubhe.anvilcraft.network.StructureScannerFileResultPacket;
+import dev.dubhe.anvilcraft.util.StructureBlueprintFiles;
import dev.dubhe.anvilcraft.util.StructureFileTransfer;
+import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.neoforged.neoforge.network.PacketDistributor;
import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
import java.util.Locale;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -31,152 +25,151 @@
public final class BlueprintClientFiles {
@Nullable private static UUID pending;
@Nullable private static StructureFileTransfer download;
- @Nullable private static Path exportTarget;
+ @Nullable private static UUID pendingList;
+ @Nullable private static StructureFileTransfer listDownload;
+ private static int listContainerId = -1;
private static long started;
private static int pendingContainerId = -1;
- private static String importFileName = "";
+ private static String fileName = "";
+ private static boolean exporting;
private BlueprintClientFiles() {
}
- public static Path directory() throws IOException {
- Path root = Minecraft.getInstance().gameDirectory.toPath().resolve("anvilcraft/structures").toAbsolutePath().normalize();
- Files.createDirectories(root);
- return root;
+ public static boolean canOpenDirectory() {
+ return Minecraft.getInstance().hasSingleplayerServer();
}
- public static List listFiles() {
- List files = new ArrayList<>();
- try (DirectoryStream entries = Files.newDirectoryStream(directory())) {
- for (Path entry : entries) {
- String name = entry.getFileName().toString();
- String lower = name.toLowerCase(Locale.ROOT);
- if (StructureFileTransfer.isSafeName(name) && (lower.endsWith(".nbt") || lower.endsWith(".litematic"))
- && Files.isRegularFile(entry, LinkOption.NOFOLLOW_LINKS) && Files.size(entry) <= StructureFileTransfer.MAX_BYTES) {
- files.add(name);
- }
+ public static void openDirectory() {
+ var server = Minecraft.getInstance().getSingleplayerServer();
+ if (server == null) return;
+ try {
+ Util.getPlatform().openFile(StructureBlueprintFiles.directory(server).toFile());
+ } catch (IOException exception) {
+ message("file_failed", exception.getMessage());
+ }
+ }
+
+ public static void requestFiles(int containerId) {
+ UUID id = UUID.randomUUID();
+ pendingList = id;
+ listContainerId = containerId;
+ listDownload = null;
+ PacketDistributor.sendToServer(new StructureScannerFilePacket(containerId, id, Action.LIST, ""));
+ }
+
+ private static void receiveFiles(StructureScannerFileResultPacket packet) {
+ try {
+ if (!packet.error().isEmpty()) throw new IOException(packet.error());
+ if (packet.offset() == 0) listDownload = new StructureFileTransfer(packet.id(), "", packet.total());
+ if (listDownload == null || !listDownload.append(packet.id(), "", packet.total(), packet.offset(), packet.bytes())) return;
+ var tag = NbtIo.readCompressed(new ByteArrayInputStream(listDownload.finish()), NbtAccounter.create(16L * 1024 * 1024));
+ if (Minecraft.getInstance().screen instanceof StructureScannerScreen screen
+ && screen.getMenu().containerId == listContainerId) {
+ screen.onFilesReceived(tag.getList("files", Tag.TAG_STRING).stream().map(Tag::getAsString).toList());
}
} catch (IOException exception) {
- AnvilCraft.LOGGER.warn("Cannot list scanner import files", exception);
+ message("file_failed", exception.getMessage());
}
- files.sort(Comparator.naturalOrder());
- return files;
+ pendingList = null;
+ listDownload = null;
+ listContainerId = -1;
}
public static boolean isValidExportName(String name) {
- String fileName = name.toLowerCase(Locale.ROOT).endsWith(".nbt") ? name : name + ".nbt";
- return !name.isBlank() && StructureFileTransfer.isSafeName(fileName);
+ String exported = name.toLowerCase(Locale.ROOT).endsWith(".nbt") ? name : name + ".nbt";
+ return !name.isBlank() && StructureFileTransfer.isSafeName(exported);
}
public static boolean isBusy() {
if (pending != null && System.nanoTime() - started > 60_000_000_000L) {
- clear();
+ clearTransfer();
message("file_failed", "Transfer timed out");
}
return pending != null;
}
- public static void upload(int containerId, String name) {
+ public static void requestImport(int containerId, String name) {
if (isBusy()) return;
- try {
- Path file = resolve(name);
- if (!Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS) || Files.size(file) > StructureFileTransfer.MAX_BYTES) {
- throw new IOException("Invalid structure file");
- }
- byte[] bytes;
- try (var input = Files.newInputStream(file, LinkOption.NOFOLLOW_LINKS)) {
- bytes = input.readNBytes(StructureFileTransfer.MAX_BYTES + 1);
- }
- if (bytes.length == 0 || bytes.length > StructureFileTransfer.MAX_BYTES) throw new IOException("Invalid file size");
- UUID id = begin(containerId);
- importFileName = name;
- for (int offset = 0; offset < bytes.length; offset += StructureFileTransfer.CHUNK_BYTES) {
- byte[] chunk = Arrays.copyOfRange(bytes, offset, Math.min(offset + StructureFileTransfer.CHUNK_BYTES, bytes.length));
- PacketDistributor.sendToServer(new StructureScannerFilePacket(containerId, id, name, bytes.length, offset, chunk));
- }
- } catch (IOException exception) {
- clear();
- message("file_failed", exception.getMessage());
- }
+ UUID id = begin(containerId, name, false);
+ PacketDistributor.sendToServer(new StructureScannerFilePacket(containerId, id, Action.IMPORT, name));
}
public static void requestExport(int containerId, String name) {
if (isBusy() || !isValidExportName(name)) return;
- try {
- String fileName = name.toLowerCase(Locale.ROOT).endsWith(".nbt") ? name : name + ".nbt";
- Path target = resolve(fileName);
- UUID id = begin(containerId);
- exportTarget = target;
- PacketDistributor.sendToServer(new StructureScannerFilePacket(containerId, id, fileName, 0, 0, new byte[0]));
- } catch (IOException exception) {
- clear();
- message("file_failed", exception.getMessage());
- }
+ String exported = name.toLowerCase(Locale.ROOT).endsWith(".nbt") ? name : name + ".nbt";
+ UUID id = begin(containerId, exported, true);
+ PacketDistributor.sendToServer(new StructureScannerFilePacket(containerId, id, Action.EXPORT, exported));
}
- private static UUID begin(int containerId) {
- clear();
+ private static UUID begin(int containerId, String name, boolean export) {
+ clearTransfer();
UUID id = UUID.randomUUID();
pending = id;
pendingContainerId = containerId;
+ fileName = name;
+ exporting = export;
started = System.nanoTime();
return id;
}
- private static Path resolve(String name) throws IOException {
- if (!StructureFileTransfer.isSafeName(name)) throw new IOException("Invalid file name");
- Path root = directory();
- Path file = root.resolve(name).normalize();
- if (!root.equals(file.getParent()) || Files.isSymbolicLink(file)) throw new IOException("Invalid file path");
- return file;
- }
-
public static void receive(StructureScannerFileResultPacket packet) {
+ if (packet.id().equals(pendingList)) {
+ receiveFiles(packet);
+ return;
+ }
if (!isBusy() || !packet.id().equals(pending)) return;
try {
if (!packet.error().isEmpty()) throw new IOException(packet.error());
- Path target = exportTarget;
- String name = target == null ? importFileName : target.getFileName().toString();
- if (packet.offset() == 0) download = new StructureFileTransfer(packet.id(), name, packet.total());
- if (download == null || !download.append(packet.id(), name, packet.total(), packet.offset(), packet.bytes())) return;
- if (target == null) {
- var minecraft = Minecraft.getInstance();
- if (minecraft.level != null && minecraft.screen instanceof StructureScannerScreen screen
- && screen.getMenu().containerId == pendingContainerId) {
- var tag = NbtIo.readCompressed(new ByteArrayInputStream(download.finish()), NbtAccounter.create(16L * 1024 * 1024));
- var snapshot = StructureSnapshotCodec.parse(tag, minecraft.level.registryAccess()).snapshot();
- screen.onImportComplete(name.substring(0, name.lastIndexOf('.')), snapshot);
- message("imported", "");
+ if (exporting) {
+ if (packet.total() != 0 || packet.offset() != 0 || packet.bytes().length != 0) {
+ throw new IOException("Invalid export result");
}
- clear();
+ message("exported", fileName);
+ if (Minecraft.getInstance().screen instanceof StructureScannerScreen screen
+ && screen.getMenu().containerId == pendingContainerId) requestFiles(pendingContainerId);
+ clearTransfer();
return;
}
- Path temporary = Files.createTempFile(target.getParent(), ".scanner-", ".tmp");
- try {
- Files.write(temporary, download.finish());
- if (!resolve(name).equals(target)) throw new IOException("Export directory changed");
- Files.move(temporary, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
- } finally {
- Files.deleteIfExists(temporary);
+ if (packet.offset() == 0) download = new StructureFileTransfer(packet.id(), fileName, packet.total());
+ if (download == null || !download.append(packet.id(), fileName, packet.total(), packet.offset(), packet.bytes())) return;
+ var minecraft = Minecraft.getInstance();
+ if (minecraft.level != null && minecraft.screen instanceof StructureScannerScreen screen
+ && screen.getMenu().containerId == pendingContainerId) {
+ var tag = NbtIo.readCompressed(new ByteArrayInputStream(download.finish()), NbtAccounter.create(16L * 1024 * 1024));
+ var snapshot = StructureSnapshotCodec.parse(tag, minecraft.level.registryAccess()).snapshot();
+ screen.onImportComplete(fileName.substring(0, fileName.lastIndexOf('.')), snapshot);
+ message("imported", "");
}
- message("exported", name);
- clear();
+ clearTransfer();
} catch (IOException | ConstructionBlueprintException | IllegalArgumentException exception) {
- clear();
+ clearTransfer();
message("file_failed", exception.getMessage());
}
}
public static void cancelImport(int containerId) {
- if (pendingContainerId == containerId && exportTarget == null) clear();
+ if (pendingContainerId == containerId && !exporting) clearTransfer();
+ if (listContainerId == containerId) {
+ pendingList = null;
+ listDownload = null;
+ listContainerId = -1;
+ }
}
public static void clear() {
+ clearTransfer();
+ pendingList = null;
+ listDownload = null;
+ listContainerId = -1;
+ }
+
+ private static void clearTransfer() {
pending = null;
- importFileName = "";
+ fileName = "";
pendingContainerId = -1;
download = null;
- exportTarget = null;
+ exporting = false;
}
private static void message(String key, @Nullable String detail) {
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintRenderView.java b/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintRenderView.java
index f4b0a88816..b62a759985 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintRenderView.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/building/BlueprintRenderView.java
@@ -9,7 +9,6 @@
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
-import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
@@ -35,7 +34,6 @@ final class BlueprintRenderView implements BlockAndTintGetter {
private final BlockPos tintPos;
private final Long2ObjectOpenHashMap blocks = new Long2ObjectOpenHashMap<>();
private final Long2ObjectOpenHashMap blockEntities = new Long2ObjectOpenHashMap<>();
- private boolean hideNonOccludingNeighbors = true;
BlueprintRenderView(ClientLevel level, BlockPos tintPos) {
this.level = level;
@@ -50,10 +48,6 @@ void put(BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity) {
}
}
- void hideNonOccludingNeighbors(boolean hide) {
- this.hideNonOccludingNeighbors = hide;
- }
-
BlockState realState(BlockPos pos) {
BlockState state = this.blocks.get(pos.asLong());
return state != null ? state : Blocks.AIR.defaultBlockState();
@@ -61,15 +55,7 @@ BlockState realState(BlockPos pos) {
@Override
public BlockState getBlockState(BlockPos pos) {
- BlockState state = this.realState(pos);
- // 流体连接高度读邻格 getBlockState().getFluidState();把熔岩/含水方块藏成空气会画出阶梯和内部水面。
- // 红石粉 canOcclude 为真但不是实心立方体,会切掉下方顶面;邻接查询只把 isSolidRender 立方体当真。
- if (this.hideNonOccludingNeighbors
- && state.getFluidState().isEmpty()
- && !state.isSolidRender(EmptyBlockGetter.INSTANCE, pos)) {
- return Blocks.AIR.defaultBlockState();
- }
- return state;
+ return this.realState(pos);
}
/** 把查询原点挪到 {@code origin},供 {@code renderLiquid} 在 (0,0,0) 写 0-1 顶点。 */
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodObstructionHighlight.java b/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodObstructionHighlight.java
new file mode 100644
index 0000000000..b118dbcff7
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodObstructionHighlight.java
@@ -0,0 +1,32 @@
+package dev.dubhe.anvilcraft.client.building;
+
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.level.Level;
+
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+public final class BuildingRodObstructionHighlight {
+ private static final Map EXPIRES_AT = new WeakHashMap<>();
+
+ private BuildingRodObstructionHighlight() {
+ }
+
+ public static void show(Level level, List ids) {
+ long now = System.nanoTime();
+ EXPIRES_AT.entrySet().removeIf(entry -> entry.getKey().level() != level || entry.getValue() <= now);
+ for (int id : ids) {
+ Entity entity = level.getEntity(id);
+ if (entity != null) EXPIRES_AT.put(entity, now + 3_000_000_000L);
+ }
+ }
+
+ public static boolean isHighlighted(Entity entity) {
+ Long expiresAt = EXPIRES_AT.get(entity);
+ if (expiresAt == null) return false;
+ if (System.nanoTime() < expiresAt) return true;
+ EXPIRES_AT.remove(entity);
+ return false;
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodRenderTypes.java b/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodRenderTypes.java
index fbad651ca2..487121b52f 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodRenderTypes.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/building/BuildingRodRenderTypes.java
@@ -1,6 +1,5 @@
package dev.dubhe.anvilcraft.client.building;
-import com.mojang.blaze3d.systems.RenderSystem;
import dev.dubhe.anvilcraft.client.support.RenderSupport;
import net.minecraft.client.renderer.RenderType;
@@ -15,19 +14,6 @@ private BuildingRodRenderTypes() {
static RenderType ghost(RenderType original) {
if (TYPES.size() >= 128) TYPES.clear();
- return TYPES.computeIfAbsent(original, type -> new DoubleSided(RenderSupport.useTranslucentIfPossible(type)));
- }
-
- private static final class DoubleSided extends RenderType {
- private DoubleSided(RenderType original) {
- super("anvilcraft:building_rod_ghost", original.format(), original.mode(), original.bufferSize(),
- original.affectsCrumbling(), original.sortOnUpload(), () -> {
- original.setupRenderState();
- RenderSystem.disableCull();
- }, () -> {
- RenderSystem.enableCull();
- original.clearRenderState();
- });
- }
+ return TYPES.computeIfAbsent(original, RenderSupport::useTranslucentIfPossible);
}
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/LargeBlockPlacePreviewEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/LargeBlockPlacePreviewEventListener.java
index 87c88681c2..4c0bef0aff 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/event/LargeBlockPlacePreviewEventListener.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/event/LargeBlockPlacePreviewEventListener.java
@@ -9,7 +9,6 @@
import dev.dubhe.anvilcraft.api.tooltip.TooltipRenderHelper;
import dev.dubhe.anvilcraft.block.cfa.CelestialForgingAnvilAmplifierBlock;
import dev.dubhe.anvilcraft.block.entity.CelestialForgingAnvilBlockEntity;
-import dev.dubhe.anvilcraft.block.item.ChuteBlockItem;
import dev.dubhe.anvilcraft.block.item.PlaceInWaterBlockItem;
import dev.dubhe.anvilcraft.block.multipart.AbstractMultiPartBlock;
import dev.dubhe.anvilcraft.block.state.DirectionCube232PartHalf;
@@ -152,6 +151,8 @@ private static void updatePreview() {
if (!multiPart && !blockItem.getBlock().defaultBlockState().is(ModBlockTags.PLACEMENT_PREVIEW)) {
return;
}
+ if (mc.hitResult instanceof BlockHitResult hit && hit.getType() == HitResult.Type.BLOCK
+ && !PlacementInteractions.allowsPlacement(new UseOnContext(player, hand, hit))) return;
UseOnContext useContext;
if (item.getItem() instanceof PlaceInWaterBlockItem) {
// 这类物品只在水面放置:useOn() 返回 PASS,实际落点由 use() 用流体射线
@@ -173,10 +174,6 @@ private static void updatePreview() {
useContext = new UseOnContext(mc.level, player, hand, item, hit);
} else {
useContext = new UseOnContext(player, hand, target);
- if (!PlacementInteractions.allowsPlacement(useContext)) return;
- if (blockItem instanceof ChuteBlockItem && ChuteBlockItem.isStorageInteraction(useContext)) {
- return;
- }
useContext = BlockPlacementPicking.forPlacement(useContext);
}
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/PocketEffectLayout.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/PocketEffectLayout.java
new file mode 100644
index 0000000000..c4f54c3a8d
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/PocketEffectLayout.java
@@ -0,0 +1,64 @@
+package dev.dubhe.anvilcraft.client.gui.component;
+
+import dev.dubhe.anvilcraft.inventory.PocketInventory;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
+import net.minecraft.client.gui.screens.inventory.CreativeModeInventoryScreen;
+import net.minecraft.client.gui.screens.inventory.InventoryScreen;
+import net.minecraft.client.renderer.Rect2i;
+import net.minecraft.world.effect.MobEffectInstance;
+import net.neoforged.neoforge.client.ClientHooks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class PocketEffectLayout {
+ private PocketEffectLayout() {
+ }
+
+ public static boolean isEnabled(AbstractContainerScreen> screen) {
+ var player = Minecraft.getInstance().player;
+ return player != null && PocketInventory.capacity(player) > 0
+ && (screen instanceof InventoryScreen
+ || screen instanceof CreativeModeInventoryScreen creative && creative.isInventoryOpen());
+ }
+
+ public static List visibleEffects() {
+ var player = Minecraft.getInstance().player;
+ return player == null ? List.of() : player.getActiveEffects().stream()
+ .filter(ClientHooks::shouldRenderEffect).sorted().toList();
+ }
+
+ public static List areas(AbstractContainerScreen> screen, int count) {
+ return areas(screen.width, screen.getGuiLeft(), screen.getGuiTop(), screen.getXSize(),
+ screen instanceof CreativeModeInventoryScreen, count);
+ }
+
+ public static List areas(int screenWidth, int left, int top, int imageWidth, boolean creative, int count) {
+ if (count == 0) return List.of();
+ int bottom = Math.max(18, top - (creative ? 28 : 0));
+ int size = Math.min(32, bottom - 2);
+ List areas = new ArrayList<>();
+ if (creative) {
+ int halfWidth = (imageWidth - 48 - 36) / 2;
+ int firstCount = (count + 1) / 2;
+ size = Math.min(size, Math.max(16, halfWidth / firstCount));
+ int posY = Math.max(0, bottom - size - 2);
+ addRow(areas, left + 24, posY, halfWidth, size, firstCount);
+ addRow(areas, left + imageWidth / 2 + 18, posY, halfWidth, size, count - firstCount);
+ } else {
+ int available = Math.max(size, screenWidth - 4);
+ int width = Math.min(available, count * (size + 1) - 1);
+ int start = Math.clamp(left + (imageWidth - width) / 2, 2, Math.max(2, screenWidth - width - 2));
+ addRow(areas, start, Math.max(0, bottom - size - 2), width, size, count);
+ }
+ return areas;
+ }
+
+ private static void addRow(List areas, int left, int top, int width, int size, int count) {
+ if (count == 0) return;
+ int spacing = count == 1 ? 0 : Math.min(size + 1, (width - size) / (count - 1));
+ int start = left + (width - size - spacing * (count - 1)) / 2;
+ for (int index = 0; index < count; index++) areas.add(new Rect2i(start + index * spacing, top, size, size));
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/StructureScannerButtonState.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/StructureScannerButtonState.java
index eaf9205da4..06af9e6b95 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/StructureScannerButtonState.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/StructureScannerButtonState.java
@@ -1,12 +1,12 @@
package dev.dubhe.anvilcraft.client.gui.component;
-/** 成型舱式三段/五段按钮状态;只有有效的松开操作才提交动作。 */
+/** 成型舱式三段/五段按钮及六段文件按钮状态;只有有效的松开操作才提交动作。 */
public final class StructureScannerButtonState {
private final int frames;
private int pressedInput = -1;
public StructureScannerButtonState(int frames) {
- if (frames != 3 && frames != 5) throw new IllegalArgumentException("Expected a three- or five-frame button");
+ if (frames != 3 && frames != 5 && frames != 6) throw new IllegalArgumentException("Expected three, five or six frames");
this.frames = frames;
}
@@ -35,6 +35,11 @@ public void cancel() {
}
public int frame(boolean enabled, boolean hovered, boolean selected) {
+ if (this.frames == 6) {
+ if (!enabled) this.cancel();
+ int frame = !enabled ? 0 : this.pressedInput != -1 && (hovered || this.keyboardPressed()) ? 2 : hovered ? 1 : 0;
+ return (selected ? 3 : 0) + frame;
+ }
if (!enabled) {
this.cancel();
return this.frames == 5 && selected ? 3 : 0;
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/category/CategoryList.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/category/CategoryList.java
index 2fb166c570..317f924358 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/category/CategoryList.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/category/CategoryList.java
@@ -7,6 +7,7 @@
import dev.dubhe.anvilcraft.client.gui.screen.StorageScreen;
import dev.dubhe.anvilcraft.saved.setting.PlayerSetting;
import dev.dubhe.anvilcraft.saved.storage.category.store.CategoryEntry;
+import dev.dubhe.anvilcraft.saved.storage.category.store.CategoryMode;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
@@ -60,18 +61,31 @@ public CategoryList(int x, int y, ButtonInfo info, PlayerSetting setting, Button
this.info = info;
this.categoryButtons = new ArrayList<>();
this.categoryOnPress = categoryOnPress;
- this.settingButton = new TexturedButton(
+ this.settingButton = this.createSettingButton(x, 0, openSetting);
+ this.rebuild(info, setting);
+ }
+
+ private TexturedButton createSettingButton(int x, int y, Button.OnPress openSetting) {
+ return new TexturedButton(
x,
- 0,
- info.width(),
- info.height(),
+ y,
+ this.info.width(),
+ this.info.height(),
this.info.setting(),
20,
- info.width(),
- info.height() * 2,
- openSetting
+ this.info.width(),
+ this.info.height() * 2,
+ openSetting,
+ Component.translatable("screen.anvilcraft.storage.category.setting.tooltip"),
+ button -> this.resetFilterModes()
);
- this.rebuild(info, setting);
+ }
+
+ private void resetFilterModes() {
+ for (CategoryButton button : this.categoryButtons) {
+ button.setMode(button.entry().changeMode(CategoryMode.UNLIMITED));
+ }
+ this.categoryOnPress.onPress(this.settingButton);
}
protected static int calculateWidth(ButtonInfo info) {
@@ -84,15 +98,9 @@ protected static int calculateHeight(ButtonInfo info) {
public void rebuild(ButtonInfo info, PlayerSetting setting) {
this.info = info;
- this.settingButton = new TexturedButton(
+ this.settingButton = this.createSettingButton(
this.settingButton.getX(),
this.settingButton.getY(),
- this.info.width(),
- this.info.height(),
- this.info.setting(),
- 20,
- this.info.width(),
- this.info.height() * 2,
this.settingButton.getOnPress()
);
this.width = CategoryList.calculateWidth(info);
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java
index 1355b72928..bbf9983e8c 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/SmartBlockPlacerScreen.java
@@ -1455,12 +1455,9 @@ private LevelLike buildPreviewLevelLike(SmartBlockPlacerBlockEntity blockEntity,
Direction.NORTH,
upsideDown
);
- int row = position / SmartBlockPlacerBlockEntity.POSITION_GRID_SIZE;
- int column = position % SmartBlockPlacerBlockEntity.POSITION_GRID_SIZE;
- previewLevelLike.setBlockState(
- new BlockPos(column, layer, row),
- blueprintState
- );
+ BlockPos previewPos = blockEntity.getBlueprintPosition(index, Direction.NORTH, upsideDown)
+ .subtract(blockEntity.getBlockPos()).offset(placerX, placerY, placerZ);
+ previewLevelLike.setBlockState(previewPos, blueprintState);
}
}
return previewLevelLike;
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageMenu.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageMenu.java
index 6b7ad84b46..cc88829b53 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageMenu.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StorageMenu.java
@@ -2,8 +2,6 @@
import lombok.Getter;
import net.minecraft.core.BlockPos;
-import net.minecraft.world.SimpleContainer;
-import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ClickType;
@@ -40,43 +38,29 @@ public class StorageMenu extends AbstractContainerMenu {
@Getter
private final BlockPos sourcePos;
- private StorageMenu(AbstractContainerMenu inventoryMenu, Inventory inventory, BlockPos sourcePos) {
+ private StorageMenu(AbstractContainerMenu inventoryMenu, BlockPos sourcePos) {
super(null, 0);
this.inventoryMenu = inventoryMenu;
this.sourcePos = sourcePos;
- // 槽位布局与 InventoryMenu 完全一致(46 槽),保证服务端广播
- // (containerId == 0 的槽位/内容包)按同一 index 写回正确的背包槽。
- // 0 合成结果、1~4 合成格:服务端是独立容器(不在地图背包),用空容器承接广播
- SimpleContainer hidden = new SimpleContainer(1);
- for (int i = 0; i < 5; i++) {
- this.addSlot(new Slot(hidden, 0, HIDDEN_X, HIDDEN_Y));
- }
- // 5~8 盔甲(Inventory 36~39):与 InventoryMenu 一致
- for (int i = 0; i < 4; i++) {
- this.addSlot(new Slot(inventory, 36 + i, HIDDEN_X, HIDDEN_Y));
- }
- // 9~35 主物品栏:3 行,与 StorageScreen 渲染位置一致
- for (int row = 0; row < 3; row++) {
- for (int column = 0; column < 9; column++) {
- this.addSlot(new Slot(
- inventory,
- 9 + row * 9 + column,
- PLAYER_INVENTORY_X + 18 * column,
- PLAYER_INVENTORY_Y + 18 * row
- ));
+ // 复用原菜单的槽位映射,确保盔甲和附加口袋的同步索引保持一致。
+ for (int index = 0; index < inventoryMenu.slots.size(); index++) {
+ Slot source = inventoryMenu.getSlot(index);
+ int slotX = HIDDEN_X;
+ int slotY = HIDDEN_Y;
+ if (index >= 9 && index < 36) {
+ slotX = PLAYER_INVENTORY_X + 18 * ((index - 9) % 9);
+ slotY = PLAYER_INVENTORY_Y + 18 * ((index - 9) / 9);
+ } else if (index >= 36 && index < 45) {
+ slotX = PLAYER_INVENTORY_X + 18 * (index - 36);
+ slotY = PLAYER_INVENTORY_Y + HOTBAR_Y;
}
+ this.addSlot(new Slot(source.container, source.getSlotIndex(), slotX, slotY));
}
- // 36~44 快捷栏(0~8)
- for (int i = 0; i < 9; i++) {
- this.addSlot(new Slot(inventory, i, PLAYER_INVENTORY_X + 18 * i, PLAYER_INVENTORY_Y + HOTBAR_Y));
- }
- // 45 副手:隐藏
- this.addSlot(new Slot(inventory, 40, HIDDEN_X, HIDDEN_Y));
}
/** 创建包裹玩家 {@code inventoryMenu} 的仓储菜单。 */
public static StorageMenu create(Player player, BlockPos sourcePos) {
- return new StorageMenu(player.inventoryMenu, player.getInventory(), sourcePos);
+ return new StorageMenu(player.inventoryMenu, sourcePos);
}
@Override
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StructureScannerScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StructureScannerScreen.java
index 1b9c41782c..83781c47b5 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StructureScannerScreen.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/StructureScannerScreen.java
@@ -93,6 +93,7 @@ public class StructureScannerScreen extends AbstractContainerScreen this.folderMode,
button -> {
+ this.updateNameInputEditable();
this.importDropdown = false;
- BlueprintClientFiles.upload(this.menu.containerId, this.importInput.getValue());
+ if (this.folderMode) {
+ BlueprintClientFiles.openDirectory();
+ } else if (button.active) {
+ BlueprintClientFiles.requestImport(this.menu.containerId, this.importInput.getValue());
+ }
}, Component.translatable("screen.anvilcraft.structure_scanner.import")
));
this.exportButton = this.addRenderableWidget(new ScannerButton(
- this.leftPos + 115, this.topPos + 38, 16, scannerTexture("export"),
- button -> BlueprintClientFiles.requestExport(this.menu.containerId, this.exportInput.getValue()),
+ this.leftPos + 115, this.topPos + 38, 16, scannerTexture("export"), 6, () -> this.folderMode,
+ button -> {
+ this.updateNameInputEditable();
+ if (this.folderMode) {
+ BlueprintClientFiles.openDirectory();
+ } else if (button.active) {
+ BlueprintClientFiles.requestExport(this.menu.containerId, this.exportInput.getValue());
+ }
+ },
Component.translatable("screen.anvilcraft.structure_scanner.export")
));
this.refreshFiles();
@@ -445,7 +459,11 @@ public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float par
}
private void refreshFiles() {
- this.importFiles = BlueprintClientFiles.listFiles();
+ BlueprintClientFiles.requestFiles(this.menu.containerId);
+ }
+
+ public void onFilesReceived(List files) {
+ this.importFiles = List.copyOf(files);
this.filterFiles();
}
@@ -789,8 +807,16 @@ private void updateNameInputEditable() {
if (!visible && this.nameInput.isFocused()) this.clearInputFocus();
this.confirmButton.active = visible && this.cachedHasDisk && this.menu.getSlot(1).getItem().isEmpty()
&& !BlueprintClientFiles.isBusy();
- this.importButton.active = this.importFiles.contains(this.importInput.getValue()) && !BlueprintClientFiles.isBusy();
- this.exportButton.active = BlueprintClientFiles.isValidExportName(this.exportInput.getValue())
+ boolean openFolder = hasShiftDown() && BlueprintClientFiles.canOpenDirectory();
+ if (this.folderMode != openFolder) {
+ this.folderMode = openFolder;
+ this.importButton.setTooltip(Tooltip.create(Component.translatable(
+ "screen.anvilcraft.structure_scanner." + (openFolder ? "open_folder" : "import"))));
+ this.exportButton.setTooltip(Tooltip.create(Component.translatable(
+ "screen.anvilcraft.structure_scanner." + (openFolder ? "open_folder" : "export"))));
+ }
+ this.importButton.active = openFolder || this.importFiles.contains(this.importInput.getValue()) && !BlueprintClientFiles.isBusy();
+ this.exportButton.active = openFolder || BlueprintClientFiles.isValidExportName(this.exportInput.getValue())
&& (imported || this.cachedIsScanComplete || this.menu.getSlot(0).hasItem() || this.menu.getSlot(1).hasItem())
&& !BlueprintClientFiles.isBusy();
}
@@ -1219,6 +1245,7 @@ private void renderScannerBorder(GuiGraphics guiGraphics, int posX, int posY, Di
*/
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ this.updateNameInputEditable();
if (this.importDropdown && this.isHovering(6, 35, 106,
Math.max(1, Math.min(FILE_ROWS, this.filteredFiles.size())) * FILE_ROW_HEIGHT, mouseX, mouseY)) {
int row = (int) (mouseY - this.topPos - 35) / FILE_ROW_HEIGHT + this.fileOffset;
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/StructureDiskPreviewSupport.java b/src/main/java/dev/dubhe/anvilcraft/client/support/StructureDiskPreviewSupport.java
index 05066a9838..ad7ef4f935 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/support/StructureDiskPreviewSupport.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/support/StructureDiskPreviewSupport.java
@@ -164,9 +164,10 @@ public static void renderPreviewAt(GuiGraphics guiGraphics, ItemStack diskStack,
guiGraphics.renderOutline(previewX - 2, previewY - 2, previewSize + 4, previewSize + 4, 0x505000ff);
// 渲染3D预览
+ StructureDiskData diskData = diskStack.get(ModComponents.STRUCTURE_DISK_DATA);
RenderSupport.renderLevelLike(
cache.levelLike, guiGraphics, previewX + previewSize / 2, previewY + previewSize / 2, 60.0f, // 缩放因子
- 2.0f // 旋转速度
+ diskData == null || diskData.autoRotate() ? 2.0f : 0.0f
);
// 恢复Z轴层级
diff --git a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftServerConfig.java b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftServerConfig.java
index d4c520bb32..6ed6ce35b0 100644
--- a/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftServerConfig.java
+++ b/src/main/java/dev/dubhe/anvilcraft/config/AnvilCraftServerConfig.java
@@ -111,7 +111,7 @@ public class AnvilCraftServerConfig {
@Comment("The maximum number of logs that can be cut per level of Felling enchantment")
@BoundedDiscrete(max = 24, min = 2)
- public int fellingBlockPerLevel = 2;
+ public int fellingBlockPerLevel = 4;
@Comment("Should show anvil levitate animation")
public boolean displayAnvilAnimation = true;
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/CategoryLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/CategoryLang.java
index b5b47ce9ad..7331f9a857 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/lang/CategoryLang.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/CategoryLang.java
@@ -46,6 +46,7 @@ public static void init(RegistrumLangProvider provider) {
provider.add("screen.anvilcraft.storage.category.alternate.removable", "Left click to list, right click to delete this category");
provider.add("screen.anvilcraft.storage.category.alternate.unremovable", "Left click to list");
provider.add("screen.anvilcraft.storage.category.setting.title", "Category Setting");
+ provider.add("screen.anvilcraft.storage.category.setting.tooltip", "Left click to configure, right click to reset filters");
provider.add("screen.anvilcraft.balance_mode.smart", "Smart");
provider.add("screen.anvilcraft.balance_mode.restock", "Restock only");
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ItemTooltipLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ItemTooltipLang.java
index ede542a769..6085120dd4 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ItemTooltipLang.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ItemTooltipLang.java
@@ -6,30 +6,11 @@
public class ItemTooltipLang {
@SuppressWarnings("checkstyle:LineLength")
public static void init(RegistrumLangProvider provider) {
- provider.add("tooltip.anvilcraft.equipment.breathing", """
- Supplies oxygen underwater and in vacuum
- Removes underwater mining penalties"""
- );
- provider.add("tooltip.anvilcraft.equipment.clear_vision", "Clear vision in all fluids");
- provider.add("tooltip.anvilcraft.equipment.recharge", """
- Uses 100 kFE per second
- Rechargeable from grids or capacitors"""
- );
- provider.add("tooltip.anvilcraft.equipment.pockets", """
- %s pocket slots
- Use the pocket key to swap with your offhand
- Empty pockets before removing leggings""");
- provider.add("tooltip.anvilcraft.equipment.buffer_boots", """
- Immune to fall damage
- Hold sneak to charge a jump, up to 4 blocks height""");
- provider.add("tooltip.anvilcraft.equipment.fluid_walking", "Walk on still fluid surfaces; sneak to submerge, hold sneak to descend faster");
- provider.add("tooltip.anvilcraft.equipment.full_suit", "Full suit: immune to environmental damage except the void; prevents falling into the void");
ItemTooltipManager.getNormalMap().forEach(
(item, s) -> provider.add(ItemTooltipManager.getTranslationKey(item), s));
ItemTooltipManager.getShiftMap().forEach(
(item, s) -> provider.add(ItemTooltipManager.getTranslationKeyShift(item), s));
provider.add("tooltip.anvilcraft.item.reinforced_concrete", "Creeper proof");
- provider.add("tooltip.anvilcraft.item.power_converter", "Convert power into FE, consumes %d kW");
provider.add("tooltip.anvilcraft.item.recipe.processing.chance", "%1$s%% Chance");
provider.add("tooltip.anvilcraft.item.resin_block.resentment", "Resentment: %s%%");
@@ -72,18 +53,20 @@ public static void init(RegistrumLangProvider provider) {
- provider.add("tooltip.anvilcraft.resonator.desc", "Press [%s] to switch modes. Auto mode supports all tools");
- provider.add(
- "tooltip.anvilcraft.resonator.mining_desc",
- "Press [%s] to change modes. Auto mode supports all tools and can “resonance‑mine” most blocks when holding right‑click"
- );
- provider.add("tooltip.anvilcraft.heavy_halberd.desc", "Press [%s] to switch modes");
-
provider.add("tooltip.anvilcraft.thought", "Press [%s] for more info");
+ provider.add("tooltip.anvilcraft.item_count", "%s x%s");
+ provider.add("tooltip.anvilcraft.count", " x%s");
+ provider.add("tooltip.anvilcraft.indented", " %s");
+ provider.add("tooltip.anvilcraft.fluid_tank.fluid.value", " %s %s");
+ provider.add("tooltip.anvilcraft.comrade_amulet.player", "· %s");
+ provider.add("tooltip.anvilcraft.resin_block.mob", "- %s");
+ provider.add("tooltip.anvilcraft.thought.progress", "[%s%s]");
+ provider.add("tooltip.anvilcraft.key", "[%s]");
+
// Structure Disk
provider.add("item.anvilcraft.structure_disk.structure", "Structure: %s");
- provider.add("item.anvilcraft.structure_disk.size", "Size: %s");
+ provider.add("item.anvilcraft.structure_disk.size", "Size: %s x %s x %s");
provider.add("item.anvilcraft.structure_disk.fit_placer", "✓ Can be placed by Smart Block Placer");
provider.add("item.anvilcraft.structure_disk.too_large_for_placer", "✗ Too large for Smart Block Placer (max 5x5x5)");
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
index 983c8951ca..e9b416c303 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
@@ -319,6 +319,7 @@ public static void init(RegistrumLangProvider provider) {
provider.add("screen.anvilcraft.structure_scanner.export_file", "Export file");
provider.add("screen.anvilcraft.structure_scanner.import", "Import structure");
provider.add("screen.anvilcraft.structure_scanner.export", "Export structure");
+ provider.add("screen.anvilcraft.structure_scanner.open_folder", "Open structure folder");
provider.add("screen.anvilcraft.structure_scanner.name", "Structure name");
provider.add("screen.anvilcraft.structure_scanner.confirm", "Record structure to disk");
provider.add("screen.anvilcraft.structure_scanner.auto_rotate_on", "Auto rotation: on");
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ItemCompressRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ItemCompressRecipeLoader.java
index 3d9baa25b6..83c2f3ff14 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/ItemCompressRecipeLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/ItemCompressRecipeLoader.java
@@ -64,7 +64,7 @@ public static void init(RegistrumRecipeProvider provider) {
.save(provider);
ItemCompressRecipe.builder()
- .requires(ModItemTags.IRON_PLATES, 2)
+ .requires(Items.COPPER_BLOCK, 2)
.requires(
ItemIngredientPredicate
.of(ModBlocks.RESIN_BLOCK.asItem())
@@ -91,7 +91,7 @@ public static void init(RegistrumRecipeProvider provider) {
ExtendInWorldRecipeBuilder.extendCompatible(ModRecipeTriggers.ON_ANVIL_FALL_ON)
.hasItemIngredient(builder -> builder
- .of(ModItemTags.IRON_PLATES)
+ .of(Items.COPPER_BLOCK)
.count(2)
.offset(0.0, -0.375, 0.0)
.range(0.75, 0.75, 0.75)
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/MultipleToOneSmithingRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/MultipleToOneSmithingRecipeLoader.java
index e649c76025..575d32bab2 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/MultipleToOneSmithingRecipeLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/MultipleToOneSmithingRecipeLoader.java
@@ -141,7 +141,7 @@ public static void four(RegistrumRecipeProvider provider) {
FourToOneSmithingRecipe.builder()
.material(ModBlocks.FROST_METAL_BLOCK)
.input(ModItems.SILENCE_AMULET)
- .input(ModItems.FEATHER_AMULET)
+ .input(ModItems.ARMADILLO_AMULET)
.input(ModItems.CAT_AMULET)
.input(ModItems.DOG_AMULET)
.result(ModItems.NATURE_AMULET)
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/PermutationRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/PermutationRecipeLoader.java
index 6f87df4ec2..8ea9dea742 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/PermutationRecipeLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/PermutationRecipeLoader.java
@@ -76,11 +76,11 @@ public static void init(RegistrumRecipeProvider provider) {
.save(provider, "gem_amulets");
PermutationRecipe.builder()
- .input(ModItems.SILENCE_AMULET, ModItems.FEATHER_AMULET, ModItems.CAT_AMULET, ModItems.DOG_AMULET)
+ .input(ModItems.SILENCE_AMULET, ModItems.ARMADILLO_AMULET, ModItems.CAT_AMULET, ModItems.DOG_AMULET)
.options(
CustomFrostMaterialPredicate.of(PermutationRecipeLoader.count(Items.CAKE, 1)),
ModItems.SILENCE_AMULET,
- ModItems.FEATHER_AMULET,
+ ModItems.ARMADILLO_AMULET,
ModItems.CAT_AMULET,
ModItems.DOG_AMULET
)
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java
index 8fce33284d..9cb4f181cb 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/RegistrumItemRecipeLoader.java
@@ -1375,16 +1375,19 @@ public static void resonatorCore(DataGenContext- ctx, R
}
public static void multiphaseTranscendium(DataGenContext
- ctx, RegistrumRecipeProvider provider) {
- ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ctx.get())
- .requires(ModItems.MULTIPHASE_MATTER)
- .requires(ModItems.TRANSCENDIUM_INGOT)
+ ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.get())
+ .pattern("NNN")
+ .pattern("NMN")
+ .pattern("NNN")
+ .define('M', ModItems.MULTIPHASE_MATTER)
+ .define('N', ModItems.TRANSCENDIUM_NUGGET)
.unlockedBy(
AnvilCraftDatagen.hasItem(ModItems.MULTIPHASE_MATTER),
AnvilCraftDatagen.has(ModItems.MULTIPHASE_MATTER)
)
.unlockedBy(
- AnvilCraftDatagen.hasItem(ModItems.TRANSCENDIUM_INGOT),
- AnvilCraftDatagen.has(ModItems.TRANSCENDIUM_INGOT)
+ AnvilCraftDatagen.hasItem(ModItems.TRANSCENDIUM_NUGGET),
+ AnvilCraftDatagen.has(ModItems.TRANSCENDIUM_NUGGET)
)
.save(provider);
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java
index 54a604cfaa..e20c4a261e 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/recipe/TimeWarpRecipeLoader.java
@@ -121,6 +121,41 @@ public static void init(RegistrumRecipeProvider provider) {
.consume(1000)
.save(provider, AnvilCraft.of("time_warp/ember_metal_ingot_3"));
+ TimeWarpRecipe.builder()
+ .requires(ModItemTags.NETHERITE_BLOCK, 9)
+ .result(ModBlocks.EMBER_METAL_BLOCK, 3)
+ .fluid(ModBlocks.OIL_CAULDRON.get())
+ .ignite()
+ .consume(9000)
+ .save(provider, AnvilCraft.of("time_warp/ember_metal_block_0"));
+
+ TimeWarpRecipe.builder()
+ .requires(ModItemTags.NETHERITE_BLOCK, 9)
+ .requires(ModBlocks.EARTH_CORE_SHARD_BLOCK, 1)
+ .result(ModBlocks.EMBER_METAL_BLOCK, 4)
+ .fluid(ModBlocks.OIL_CAULDRON.get())
+ .ignite()
+ .consume(9000)
+ .save(provider, AnvilCraft.of("time_warp/ember_metal_block_1"));
+
+ TimeWarpRecipe.builder()
+ .requires(ModItemTags.NETHERITE_BLOCK, 9)
+ .requires(ModBlocks.EARTH_CORE_SHARD_BLOCK, 2)
+ .result(ModBlocks.EMBER_METAL_BLOCK, 5)
+ .fluid(ModBlocks.OIL_CAULDRON.get())
+ .ignite()
+ .consume(9000)
+ .save(provider, AnvilCraft.of("time_warp/ember_metal_block_2"));
+
+ TimeWarpRecipe.builder()
+ .requires(ModItemTags.NETHERITE_BLOCK, 9)
+ .requires(ModBlocks.EARTH_CORE_SHARD_BLOCK, 3)
+ .result(ModBlocks.EMBER_METAL_BLOCK, 6)
+ .fluid(ModBlocks.OIL_CAULDRON.get())
+ .ignite()
+ .consume(9000)
+ .save(provider, AnvilCraft.of("time_warp/ember_metal_block_3"));
+
TimeWarpRecipe.builder()
.requires(Items.SKELETON_SKULL)
.requires(Items.COAL, 4)
@@ -157,7 +192,7 @@ public static void init(RegistrumRecipeProvider provider) {
.requires(ModItems.SILVER_INGOT)
.fluid(Blocks.POWDER_SNOW_CAULDRON)
.consume(1000)
- .result(ModItems.FROST_METAL_INGOT, 3)
+ .result(ModItems.FROST_METAL_INGOT, 4)
.unlockedBy(AnvilCraftDatagen.hasItem(ModItems.ROYAL_STEEL_INGOT), AnvilCraftDatagen.has(ModItems.ROYAL_STEEL_INGOT))
.unlockedBy(AnvilCraftDatagen.hasItem(ModItems.SILVER_INGOT), AnvilCraftDatagen.has(ModItems.SILVER_INGOT))
.save(provider, "frost_metal_ingot_2");
@@ -167,11 +202,49 @@ public static void init(RegistrumRecipeProvider provider) {
.requires(ModItems.TITANIUM_INGOT)
.fluid(Blocks.POWDER_SNOW_CAULDRON)
.consume(1000)
- .result(ModItems.FROST_METAL_INGOT, 4)
+ .result(ModItems.FROST_METAL_INGOT, 8)
.unlockedBy(AnvilCraftDatagen.hasItem(ModItems.ROYAL_STEEL_INGOT), AnvilCraftDatagen.has(ModItems.ROYAL_STEEL_INGOT))
.unlockedBy(AnvilCraftDatagen.hasItem(ModItems.TITANIUM_INGOT), AnvilCraftDatagen.has(ModItems.TITANIUM_INGOT))
.save(provider, "frost_metal_ingot_3");
+ TimeWarpRecipe.builder()
+ .requires(ModBlocks.ROYAL_STEEL_BLOCK, 3)
+ .fluid(Blocks.POWDER_SNOW_CAULDRON)
+ .consume(9000)
+ .result(ModBlocks.FROST_METAL_BLOCK, 1)
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.ROYAL_STEEL_BLOCK), AnvilCraftDatagen.has(ModBlocks.ROYAL_STEEL_BLOCK))
+ .save(provider, "frost_metal_block_0");
+
+ TimeWarpRecipe.builder()
+ .requires(ModBlocks.ROYAL_STEEL_BLOCK, 3)
+ .requires(Items.IRON_BLOCK)
+ .fluid(Blocks.POWDER_SNOW_CAULDRON)
+ .consume(9000)
+ .result(ModBlocks.FROST_METAL_BLOCK, 2)
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.ROYAL_STEEL_BLOCK), AnvilCraftDatagen.has(ModBlocks.ROYAL_STEEL_BLOCK))
+ .unlockedBy(AnvilCraftDatagen.hasItem(Items.IRON_BLOCK), AnvilCraftDatagen.has(Items.IRON_BLOCK))
+ .save(provider, "frost_metal_block_1");
+
+ TimeWarpRecipe.builder()
+ .requires(ModBlocks.ROYAL_STEEL_BLOCK, 3)
+ .requires(ModBlocks.SILVER_BLOCK)
+ .fluid(Blocks.POWDER_SNOW_CAULDRON)
+ .consume(9000)
+ .result(ModBlocks.FROST_METAL_BLOCK, 4)
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.ROYAL_STEEL_BLOCK), AnvilCraftDatagen.has(ModBlocks.ROYAL_STEEL_BLOCK))
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.SILVER_BLOCK), AnvilCraftDatagen.has(ModBlocks.SILVER_BLOCK))
+ .save(provider, "frost_metal_block_2");
+
+ TimeWarpRecipe.builder()
+ .requires(ModBlocks.ROYAL_STEEL_BLOCK, 3)
+ .requires(ModBlocks.TITANIUM_BLOCK)
+ .fluid(Blocks.POWDER_SNOW_CAULDRON)
+ .consume(9000)
+ .result(ModBlocks.FROST_METAL_BLOCK, 8)
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.ROYAL_STEEL_BLOCK), AnvilCraftDatagen.has(ModBlocks.ROYAL_STEEL_BLOCK))
+ .unlockedBy(AnvilCraftDatagen.hasItem(ModBlocks.TITANIUM_BLOCK), AnvilCraftDatagen.has(ModBlocks.TITANIUM_BLOCK))
+ .save(provider, "frost_metal_block_3");
+
TimeWarpRecipe.builder()
.requires(ItemTags.FLOWERS)
.result(Items.WITHER_ROSE, 0.2f)
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/tags/EntityTypeTagLoader.java b/src/main/java/dev/dubhe/anvilcraft/data/tags/EntityTypeTagLoader.java
index 8c7be93489..15ece73568 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/tags/EntityTypeTagLoader.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/tags/EntityTypeTagLoader.java
@@ -23,6 +23,7 @@ public static void init(RegistrumTagsProvider> provider) {
.addOptionalTag(ModEntityTypeTags.EMERALD_AMULET_VALID)
.addOptionalTag(ModEntityTypeTags.SAPPHIRE_AMULET_VALID)
.addOptionalTag(ModEntityTypeTags.ANVIL_AMULET_VALID)
+ .addOptionalTag(ModEntityTypeTags.ARMADILLO_AMULET_VALID)
.addOptionalTag(ModEntityTypeTags.CAT_AMULET_VALID)
.addOptionalTag(ModEntityTypeTags.DOG_AMULET_VALID)
.addOptionalTag(ModEntityTypeTags.SILENCE_AMULET_VALID);
@@ -39,6 +40,10 @@ public static void init(RegistrumTagsProvider> provider) {
.add(findResourceKey(EntityType.FALLING_BLOCK))
.add(ModEntities.FALLING_GIANT_ANVIL.getKey());
+ provider.addTag(ModEntityTypeTags.ARMADILLO_AMULET_VALID)
+ .add(findResourceKey(EntityType.SPIDER))
+ .add(findResourceKey(EntityType.CAVE_SPIDER));
+
provider.addTag(ModEntityTypeTags.CAT_AMULET_VALID)
.add(findResourceKey(EntityType.CREEPER))
.add(findResourceKey(EntityType.PHANTOM));
diff --git a/src/main/java/dev/dubhe/anvilcraft/event/LivingEntityEventListener.java b/src/main/java/dev/dubhe/anvilcraft/event/LivingEntityEventListener.java
index 7102435862..9932655aff 100644
--- a/src/main/java/dev/dubhe/anvilcraft/event/LivingEntityEventListener.java
+++ b/src/main/java/dev/dubhe/anvilcraft/event/LivingEntityEventListener.java
@@ -26,6 +26,7 @@
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.Giant;
import net.minecraft.world.entity.monster.Phantom;
+import net.minecraft.world.entity.monster.Spider;
import net.minecraft.world.entity.monster.ZombifiedPiglin;
import net.minecraft.world.entity.monster.warden.Warden;
import net.minecraft.world.entity.npc.AbstractVillager;
@@ -56,7 +57,9 @@ public static void onChangeTarget(LivingChangeTargetEvent event) {
if (!(event.getNewAboutToBeSetTarget() instanceof Player player)) return;
AmuletManager manager = AmuletManager.get(player.registryAccess());
if (
- entity.getType().is(EntityTypeTags.SKELETONS) && manager.hasAmuletInInventory(player, ModAmulets.DOG.getKey())
+ entity instanceof Spider && manager.hasAmuletInInventory(player, ModAmulets.ARMADILLO.getKey())
+ || entity instanceof IronGolem && manager.hasAmuletInInventory(player, ModAmulets.EMERALD.getKey())
+ || entity.getType().is(EntityTypeTags.SKELETONS) && manager.hasAmuletInInventory(player, ModAmulets.DOG.getKey())
|| (entity instanceof Creeper || entity instanceof Phantom) && manager.hasAmuletInInventory(player, ModAmulets.CAT.getKey())
) {
event.setCanceled(true);
@@ -71,9 +74,12 @@ public static void onTick(EntityTickEvent.Post event) {
if (!(entity.getTarget() instanceof Player player)) return;
AmuletManager manager = AmuletManager.get(player.registryAccess());
if (
- entity.getType().is(EntityTypeTags.SKELETONS) && manager.hasAmuletInInventory(player, ModAmulets.DOG.getKey())
+ entity instanceof Spider && manager.hasAmuletInInventory(player, ModAmulets.ARMADILLO.getKey())
+ || entity instanceof IronGolem && manager.hasAmuletInInventory(player, ModAmulets.EMERALD.getKey())
+ || entity.getType().is(EntityTypeTags.SKELETONS) && manager.hasAmuletInInventory(player, ModAmulets.DOG.getKey())
|| (entity instanceof Creeper || entity instanceof Phantom) && manager.hasAmuletInInventory(player, ModAmulets.CAT.getKey())
) {
+ if (entity instanceof IronGolem golem) golem.stopBeingAngry();
entity.setTarget(null);
}
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
index 2225b5f89d..a03f9a57cd 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
@@ -9,6 +9,7 @@
import dev.dubhe.anvilcraft.block.ActiveSilencerBlock;
import dev.dubhe.anvilcraft.block.AdvancedComparatorBlock;
import dev.dubhe.anvilcraft.block.AmberBlock;
+import dev.dubhe.anvilcraft.block.AncientSeaReefBlock;
import dev.dubhe.anvilcraft.block.ArrowBlock;
import dev.dubhe.anvilcraft.block.AutoEnchantingTableBlock;
import dev.dubhe.anvilcraft.block.BerryCakeBlock;
@@ -1070,7 +1071,8 @@ public class ModBlocks {
)
.blockstate(DataGenUtil::noExtraModelOrState)
.item(InfiniteCollectorBlockItem::new)
- .properties(properties -> properties.fireResistant().rarity(Rarity.EPIC))
+ .properties(properties -> properties.fireResistant().rarity(Rarity.EPIC)
+ .component(ModComponents.ETERNAL, Eternal.INSTANCE))
.tag(ModItemTags.EXPLOSION_PROOF)
.build()
.recipe(RegistrumBlockRecipeLoader::infiniteCollector)
@@ -2066,7 +2068,8 @@ public class ModBlocks {
.loot(SimpleMultiPartBlock::loot)
.tag((BlockTags.MINEABLE_WITH_PICKAXE), BlockTags.WITHER_IMMUNE, BlockTags.DRAGON_IMMUNE, ModBlockTags.COLLISION_IMMUNE)
.item(CelestialForgingAnvilBlockItem::new)
- .properties(properties -> properties.stacksTo(1).rarity(Rarity.EPIC))
+ .properties(properties -> properties.stacksTo(1).rarity(Rarity.EPIC)
+ .component(ModComponents.ETERNAL, Eternal.INSTANCE))
.tag(ItemTags.HEAD_ARMOR_ENCHANTABLE, ItemTags.EQUIPPABLE_ENCHANTABLE, ItemTags.VANISHING_ENCHANTABLE)
.model((ctx, provider) -> provider.withExistingParent(ctx.getName(), AnvilCraft.of("block/celestial_forging_anvil"))
.transforms()
@@ -2093,7 +2096,8 @@ public class ModBlocks {
.pushReaction(PushReaction.BLOCK)
.emissiveRendering(ModBlocks::always))
.item(CelestialForgingAnvilAmplifierBlockItem::new)
- .properties(properties -> properties.stacksTo(16).rarity(Rarity.EPIC))
+ .properties(properties -> properties.stacksTo(16).rarity(Rarity.EPIC)
+ .component(ModComponents.ETERNAL, Eternal.INSTANCE))
.build()
.blockstate(DataGenUtil::noExtraModelOrState)
.tag((BlockTags.MINEABLE_WITH_PICKAXE), BlockTags.WITHER_IMMUNE, BlockTags.DRAGON_IMMUNE)
@@ -2181,7 +2185,8 @@ public class ModBlocks {
.tag(BlockTags.MINEABLE_WITH_PICKAXE, BlockTags.WITHER_IMMUNE, BlockTags.DRAGON_IMMUNE, ModBlockTags.COLLISION_IMMUNE)
.item(CelestialForgingAnvilPortalBlockItem::new)
.model((ctx, provider) -> provider.withExistingParent(ctx.getName(), AnvilCraft.of("block/celestial_forging_anvil_gate")))
- .properties(properties -> properties.rarity(Rarity.EPIC))
+ .properties(properties -> properties.rarity(Rarity.EPIC)
+ .component(ModComponents.ETERNAL, Eternal.INSTANCE))
.build()
.recipe(RegistrumBlockRecipeLoader::celestialForgingAnvilPortal)
.register();
@@ -4788,8 +4793,8 @@ private static void confinementChamberLoot(RegistrumBlockLootTables tables, Conf
.loot((tables, block) -> tables.add(block, tables.createSlabItemTable(block)))
.register();
- public static final BlockEntry ANCIENT_SEA_REEF = REGISTRUM
- .block("ancient_sea_reef", Block::new)
+ public static final BlockEntry ANCIENT_SEA_REEF = REGISTRUM
+ .block("ancient_sea_reef", AncientSeaReefBlock::new)
.initialProperties(() -> Blocks.STONE)
.properties(properties -> properties
.noOcclusion()
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/entity/ModEntityTypeTags.java b/src/main/java/dev/dubhe/anvilcraft/init/entity/ModEntityTypeTags.java
index 17a9ce40b9..91e017e6e1 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/entity/ModEntityTypeTags.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/entity/ModEntityTypeTags.java
@@ -11,6 +11,7 @@ public class ModEntityTypeTags {
public static final TagKey> EMERALD_AMULET_VALID = bind("amulet_valid/emerald");
public static final TagKey> SAPPHIRE_AMULET_VALID = bind("amulet_valid/sapphire");
public static final TagKey> ANVIL_AMULET_VALID = bind("amulet_valid/anvil");
+ public static final TagKey> ARMADILLO_AMULET_VALID = bind("amulet_valid/armadillo");
public static final TagKey> CAT_AMULET_VALID = bind("amulet_valid/cat");
public static final TagKey> DOG_AMULET_VALID = bind("amulet_valid/dog");
public static final TagKey> SILENCE_AMULET_VALID = bind("amulet_valid/silence");
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmuletDefinitions.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmuletDefinitions.java
index 9e186ec6f1..20446fda43 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmuletDefinitions.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmuletDefinitions.java
@@ -26,6 +26,7 @@ public class ModAmuletDefinitions {
public static final ResourceKey ANVIL = ModAmuletDefinitions.key("anvil");
public static final ResourceKey COMRADE = ModAmuletDefinitions.key("comrade");
public static final ResourceKey FEATHER = ModAmuletDefinitions.key("feather");
+ public static final ResourceKey ARMADILLO = ModAmuletDefinitions.key("armadillo");
public static final ResourceKey CAT = ModAmuletDefinitions.key("cat");
public static final ResourceKey DOG = ModAmuletDefinitions.key("dog");
public static final ResourceKey SILENCE = ModAmuletDefinitions.key("silence");
@@ -84,6 +85,12 @@ public static void bootstrap(BootstrapContext ctx) {
.obtain(ModDamageTypeTags.FEATHER_AMULET_VALID)
.build()
);
+ ctx.register(
+ ModAmuletDefinitions.ARMADILLO,
+ AmuletDefinition.builder(ModItems.ARMADILLO_AMULET)
+ .obtainEntity(ModEntityTypeTags.ARMADILLO_AMULET_VALID)
+ .build()
+ );
ctx.register(
ModAmuletDefinitions.CAT,
AmuletDefinition.builder(ModItems.CAT_AMULET)
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmulets.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmulets.java
index 5d683097f9..39f68938db 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmulets.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModAmulets.java
@@ -58,6 +58,7 @@ public class ModAmulets {
.immune(ModDamageTypeTags.FEATHER_AMULET_VALID)
.build()
);
+ public static final DeferredHolder ARMADILLO = REGISTER.register("armadillo", DoNothingAmulet::new);
public static final DeferredHolder CAT = REGISTER.register("cat", DoNothingAmulet::new);
public static final DeferredHolder DOG = REGISTER.register("dog", DoNothingAmulet::new);
public static final DeferredHolder SILENCE = REGISTER.register("silence", DoNothingAmulet::new);
@@ -74,7 +75,7 @@ public class ModAmulets {
public static final DeferredHolder NATURE = REGISTER.register(
"nature",
() -> new WrappedOthersAmulet(List.of(
- ModAmulets.FEATHER.getKey(),
+ ModAmulets.ARMADILLO.getKey(),
ModAmulets.CAT.getKey(),
ModAmulets.DOG.getKey(),
ModAmulets.SILENCE.getKey()
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
index 3700b18787..acc0548190 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
@@ -91,6 +91,7 @@
import dev.dubhe.anvilcraft.item.abnormal.RadiationItem;
import dev.dubhe.anvilcraft.item.abnormal.SuperHeavyItem;
import dev.dubhe.anvilcraft.item.amulet.AmuletBoxItem;
+import dev.dubhe.anvilcraft.item.amulet.ComradeAmuletItem;
import dev.dubhe.anvilcraft.item.property.component.Eternal;
import dev.dubhe.anvilcraft.item.property.component.Merciless;
import dev.dubhe.anvilcraft.item.property.component.Multiphase;
@@ -749,6 +750,7 @@ private static ItemEntry extends Item> createBigAmuletItem(String type, Resour
);
public static final ItemEntry extends Item> COMRADE_AMULET = createAmuletItem(
"comrade",
+ ComradeAmuletItem::new,
ModAmulets.COMRADE.getKey(),
IAmulet.SMALL_AMULET_WEIGHT,
builder -> builder.requires(Items.NAME_TAG, 4)
@@ -759,6 +761,12 @@ private static ItemEntry extends Item> createBigAmuletItem(String type, Resour
IAmulet.SMALL_AMULET_WEIGHT,
builder -> builder.requires(Items.FEATHER, 16).requires(Items.PHANTOM_MEMBRANE, 4)
);
+ public static final ItemEntry extends Item> ARMADILLO_AMULET = createAmuletItem(
+ "armadillo",
+ ModAmulets.ARMADILLO.getKey(),
+ IAmulet.SMALL_AMULET_WEIGHT,
+ builder -> builder.requires(Items.SPIDER_EYE, 16).requires(Items.ARMADILLO_SCUTE, 4)
+ );
public static final ItemEntry extends Item> CAT_AMULET = createAmuletItem(
"cat",
ModAmulets.CAT.getKey(),
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java
index 22cfa3fd0e..ba847e6493 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ItemsSections.java
@@ -137,6 +137,7 @@ public void accept() {
content.accept(ModItems.ANVIL_AMULET);
content.accept(ModItems.COMRADE_AMULET);
content.accept(ModItems.FEATHER_AMULET);
+ content.accept(ModItems.ARMADILLO_AMULET);
content.accept(ModItems.CAT_AMULET);
content.accept(ModItems.DOG_AMULET);
content.accept(ModItems.SILENCE_AMULET);
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ToolsAndUtilities.java b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ToolsAndUtilities.java
index 9f5b5af17e..800605b481 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ToolsAndUtilities.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/tabs/ToolsAndUtilities.java
@@ -104,6 +104,7 @@ public void accept() {
this.plain(ModItems.ANVIL_AMULET);
this.plain(ModItems.COMRADE_AMULET);
this.plain(ModItems.FEATHER_AMULET);
+ this.plain(ModItems.ARMADILLO_AMULET);
this.plain(ModItems.CAT_AMULET);
this.plain(ModItems.DOG_AMULET);
this.plain(ModItems.SILENCE_AMULET);
diff --git a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/ItemCompressCategory.java b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/ItemCompressCategory.java
index 4edb82e63d..7fd6293290 100644
--- a/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/ItemCompressCategory.java
+++ b/src/main/java/dev/dubhe/anvilcraft/integration/jei/category/anvil/ItemCompressCategory.java
@@ -7,7 +7,6 @@
import dev.dubhe.anvilcraft.init.block.ModBlocks;
import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.init.item.ModItemSubPredicates;
-import dev.dubhe.anvilcraft.init.item.ModItemTags;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.init.recipe.ModRecipeTypes;
import dev.dubhe.anvilcraft.integration.jei.AnvilCraftJeiPlugin;
@@ -142,7 +141,7 @@ public static void registerRecipes(IRecipeRegistration registration) {
private static ItemCompressRecipe specialSupercapacitorRecipe() {
return ItemCompressRecipe.builder()
- .requires(ModItemTags.IRON_PLATES, 2)
+ .requires(Blocks.COPPER_BLOCK, 2)
.requires(ItemIngredientPredicate.Builder.item().of(resinWithCreeper(true)).build())
// 数据配方中充能超电容是 in_world 配方(铁砧落下 50% 概率爆炸 / 50% 产出),
// 该 JEI 分类无法直接收集,这里以概率结果表达
diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/StructureScannerMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/StructureScannerMenu.java
index 607be6875c..f8a804a401 100644
--- a/src/main/java/dev/dubhe/anvilcraft/inventory/StructureScannerMenu.java
+++ b/src/main/java/dev/dubhe/anvilcraft/inventory/StructureScannerMenu.java
@@ -5,8 +5,6 @@
import dev.dubhe.anvilcraft.init.block.ModBlocks;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.inventory.component.StructureDiskOnlySlot;
-import dev.dubhe.anvilcraft.network.StructureScannerFilePacket;
-import dev.dubhe.anvilcraft.util.StructureFileTransfer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
@@ -19,7 +17,6 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
-import java.io.IOException;
import java.util.Objects;
import javax.annotation.Nullable;
@@ -27,7 +24,6 @@ public class StructureScannerMenu extends AbstractContainerMenu {
private final StructureScannerBlockEntity blockEntity;
private final Level level;
- @Nullable private StructureFileTransfer upload;
@Nullable private ImportedStructure importedStructure;
public record ImportedStructure(String name, StructureSnapshot snapshot) {
@@ -44,7 +40,6 @@ public void setImportedStructure(String name, StructureSnapshot snapshot) {
public void clearImportedStructure() {
this.importedStructure = null;
- this.upload = null;
}
@Override
@@ -53,25 +48,6 @@ public void removed(Player player) {
this.clearImportedStructure();
}
- @Nullable
- public byte[] acceptUpload(StructureScannerFilePacket packet) throws IOException {
- if (packet.offset() == 0) {
- this.upload = new StructureFileTransfer(packet.id(), packet.name(), packet.total());
- }
- try {
- if (this.upload == null) {
- throw new IOException("No structure upload is in progress");
- }
- if (!this.upload.append(packet.id(), packet.name(), packet.total(), packet.offset(), packet.bytes())) return null;
- byte[] result = this.upload.finish();
- this.upload = null;
- return result;
- } catch (IOException exception) {
- this.upload = null;
- throw exception;
- }
- }
-
@SuppressWarnings("resource")
public StructureScannerMenu(
@Nullable MenuType> menuType, int containerId, Inventory inventory, FriendlyByteBuf extraData) {
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/AmuletAbilities.java b/src/main/java/dev/dubhe/anvilcraft/item/AmuletAbilities.java
new file mode 100644
index 0000000000..d6a628368d
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/item/AmuletAbilities.java
@@ -0,0 +1,164 @@
+package dev.dubhe.anvilcraft.item;
+
+import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.api.amulet.AmuletManager;
+import dev.dubhe.anvilcraft.init.item.ModAmulets;
+import dev.dubhe.anvilcraft.init.item.ModComponents;
+import dev.dubhe.anvilcraft.item.property.component.amulet.IAmulet;
+import net.minecraft.core.Holder;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.InteractionResult;
+import net.minecraft.world.effect.MobEffect;
+import net.minecraft.world.effect.MobEffectCategory;
+import net.minecraft.world.effect.MobEffectInstance;
+import net.minecraft.world.effect.MobEffects;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.TamableAnimal;
+import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.animal.Cat;
+import net.minecraft.world.entity.animal.Wolf;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.phys.Vec3;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.neoforge.event.entity.living.LivingKnockBackEvent;
+import net.neoforged.neoforge.event.entity.living.MobEffectEvent;
+import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
+import net.neoforged.neoforge.event.level.ExplosionKnockbackEvent;
+import net.neoforged.neoforge.event.tick.PlayerTickEvent;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Supplier;
+import javax.annotation.Nullable;
+
+@EventBusSubscriber(modid = AnvilCraft.MOD_ID)
+public final class AmuletAbilities {
+ private static final ResourceLocation ANVIL_KNOCKBACK_RESISTANCE = AnvilCraft.of("anvil_amulet_knockback_resistance");
+ private static final ThreadLocal FOOD_CONSUMER = new ThreadLocal<>();
+
+ private AmuletAbilities() {
+ }
+
+ @SubscribeEvent
+ public static void onTick(PlayerTickEvent.Post event) {
+ if (!(event.getEntity() instanceof ServerPlayer player)) return;
+ AmuletManager manager = AmuletManager.get(player.registryAccess());
+ Set> active = new HashSet<>();
+ for (ItemStack stack : manager.getAmuletsFromInventory(player)) {
+ ResourceKey key = stack.get(ModComponents.AMULET);
+ IAmulet amulet = manager.getAmulet(stack);
+ if (key == null || amulet == null) continue;
+ active.add(key);
+ active.addAll(amulet.canActLike());
+ }
+ if (active.contains(ModAmulets.TOPAZ.getKey())) refreshEffect(player, MobEffects.DIG_SPEED, 0);
+ if (active.contains(ModAmulets.RUBY.getKey())) refreshEffect(player, MobEffects.DAMAGE_BOOST, player.isOnFire() ? 1 : 0);
+ if (active.contains(ModAmulets.SAPPHIRE.getKey()) && (player.isInWater() || EquipmentAbilities.canBreathe(player))) {
+ refreshEffect(player, MobEffects.DAMAGE_RESISTANCE, 0);
+ }
+ if (active.contains(ModAmulets.ARMADILLO.getKey()) && player.isShiftKeyDown()) {
+ refreshEffect(player, MobEffects.DAMAGE_RESISTANCE, 1);
+ }
+ if (active.contains(ModAmulets.FEATHER.getKey())) {
+ if (player.isShiftKeyDown()) {
+ player.removeEffect(MobEffects.SLOW_FALLING);
+ } else {
+ refreshEffect(player, MobEffects.SLOW_FALLING, 0);
+ }
+ }
+ if (active.contains(ModAmulets.SILENCE.getKey())) player.removeEffect(MobEffects.DARKNESS);
+ boolean anvil = active.contains(ModAmulets.ANVIL.getKey());
+ if (anvil) player.removeEffect(MobEffects.LEVITATION);
+ AttributeInstance resistance = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
+ if (resistance != null) {
+ if (anvil && !resistance.hasModifier(ANVIL_KNOCKBACK_RESISTANCE)) {
+ resistance.addTransientModifier(new AttributeModifier(
+ ANVIL_KNOCKBACK_RESISTANCE, 1, AttributeModifier.Operation.ADD_VALUE));
+ } else if (!anvil) {
+ resistance.removeModifier(ANVIL_KNOCKBACK_RESISTANCE);
+ }
+ }
+ }
+
+ private static void refreshEffect(Player player, Holder effect, int amplifier) {
+ player.addEffect(new MobEffectInstance(effect, 2, amplifier, false, false, true));
+ }
+
+ @SubscribeEvent
+ public static void onEffect(MobEffectEvent.Applicable event) {
+ if (!(event.getEntity() instanceof Player player)) return;
+ Holder effect = event.getEffectInstance().getEffect();
+ if (effect.is(MobEffects.DARKNESS) && has(player, ModAmulets.SILENCE.getKey())
+ || effect.is(MobEffects.LEVITATION) && has(player, ModAmulets.ANVIL.getKey())
+ || effect.is(MobEffects.SLOW_FALLING) && player.isShiftKeyDown() && has(player, ModAmulets.FEATHER.getKey())
+ || FOOD_CONSUMER.get() == player && effect.value().getCategory() == MobEffectCategory.HARMFUL) {
+ event.setResult(MobEffectEvent.Applicable.Result.DO_NOT_APPLY);
+ }
+ }
+
+ public static ItemStack consumeFood(LivingEntity consumer, Supplier action) {
+ if (!(consumer instanceof Player player) || !has(player, ModAmulets.ABNORMAL.getKey())) return action.get();
+ LivingEntity previous = FOOD_CONSUMER.get();
+ FOOD_CONSUMER.set(consumer);
+ try {
+ return action.get();
+ } finally {
+ if (previous == null) {
+ FOOD_CONSUMER.remove();
+ } else {
+ FOOD_CONSUMER.set(previous);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public static void onKnockback(LivingKnockBackEvent event) {
+ if (event.getEntity() instanceof Player player && has(player, ModAmulets.ANVIL.getKey())) event.setCanceled(true);
+ }
+
+ @SubscribeEvent
+ public static void onExplosionKnockback(ExplosionKnockbackEvent event) {
+ if (event.getAffectedEntity() instanceof Player player && has(player, ModAmulets.ANVIL.getKey())) {
+ event.setKnockbackVelocity(Vec3.ZERO);
+ }
+ }
+
+ @SubscribeEvent
+ public static void onInteract(PlayerInteractEvent.EntityInteract event) {
+ if (!event.getItemStack().isEmpty() || !(event.getTarget() instanceof TamableAnimal animal) || animal.isTame()) return;
+ ResourceKey amulet;
+ if (animal instanceof Cat) {
+ amulet = ModAmulets.CAT.getKey();
+ } else if (animal instanceof Wolf) {
+ amulet = ModAmulets.DOG.getKey();
+ } else {
+ return;
+ }
+ Player player = event.getEntity();
+ if (!has(player, amulet)) return;
+ if (!event.getLevel().isClientSide()) {
+ if (animal instanceof Wolf wolf) wolf.stopBeingAngry();
+ animal.tame(player);
+ animal.getNavigation().stop();
+ animal.setTarget(null);
+ animal.setOrderedToSit(true);
+ event.getLevel().broadcastEntityEvent(animal, (byte) 7);
+ }
+ event.setCancellationResult(InteractionResult.sidedSuccess(event.getLevel().isClientSide()));
+ event.setCanceled(true);
+ }
+
+ public static boolean isGolemProtected(@Nullable LivingEntity entity) {
+ return entity instanceof Player player && has(player, ModAmulets.EMERALD.getKey());
+ }
+
+ private static boolean has(Player player, ResourceKey amulet) {
+ return AmuletManager.get(player.registryAccess()).hasAmuletInInventory(player, amulet);
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/CannedFoodItem.java b/src/main/java/dev/dubhe/anvilcraft/item/CannedFoodItem.java
index 144e786acf..d03e165ee1 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/CannedFoodItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/CannedFoodItem.java
@@ -73,7 +73,7 @@ public void appendHoverText(ItemStack stack, TooltipContext context, List use(
return super.use(level, player, usedHand);
}
- private static Component tooltip(String suffix) {
- return Component.translatable(TOOLTIP_PREFIX + suffix)
- .withStyle(ChatFormatting.GRAY);
- }
-
private static Component message(String suffix) {
return Component.translatable(MESSAGE_PREFIX + suffix);
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
index 69bd775684..0c6bf60459 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
@@ -9,6 +9,8 @@
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageTypes;
+import net.minecraft.world.effect.MobEffectInstance;
+import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
@@ -213,6 +215,11 @@ public static boolean canStandOnFluid(LivingEntity entity, FluidState fluid) {
&& !player.isShiftKeyDown() && !SUBMERGING.getOrDefault(player, false) && fluid.isSource();
}
+ public static boolean shouldSinkInFluid(Player player) {
+ return player.isShiftKeyDown() && !player.isSpectator() && !player.getAbilities().flying
+ && player.getItemBySlot(EquipmentSlot.FEET).is(ModItems.WEATHERPROOF_SPACESUIT_BOOTS);
+ }
+
public static boolean isVoidProtected(Player player) {
return !player.isSpectator() && !player.getAbilities().flying && hasFullSuit(player);
}
@@ -243,7 +250,7 @@ public static void beforeTick(PlayerTickEvent.Pre event) {
else if (!player.isInFluidType() && player.level().getFluidState(player.blockPosition().below()).isEmpty()) {
SUBMERGING.remove(player);
}
- if (player.isShiftKeyDown() && player.isInFluidType() && !player.getAbilities().flying) {
+ if (shouldSinkInFluid(player) && player.isInFluidType()) {
player.setDeltaMovement(player.getDeltaMovement().add(0, -0.08, 0));
}
} else {
@@ -264,6 +271,12 @@ else if (!player.isInFluidType() && player.level().getFluidState(player.blockPos
public static void afterTick(PlayerTickEvent.Post event) {
if (!(event.getEntity() instanceof ServerPlayer player)) return;
PocketInventory.get(player).tick(player);
+ if (player.getItemBySlot(EquipmentSlot.HEAD).is(ModItems.WEATHERPROOF_SPACESUIT_HELMET)) {
+ MobEffectInstance nightVision = player.getEffect(MobEffects.NIGHT_VISION);
+ if (nightVision == null || nightVision.endsWithin(200)) {
+ player.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 200, 0, false, false, true));
+ }
+ }
AttributeInstance resistance = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
if (resistance == null) return;
boolean flying = !IonocraftBackpackItem.getByPlayer(player).isEmpty() && player.getAbilities().flying;
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
index a39cc1b169..62d8b84fd5 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
@@ -2,10 +2,7 @@
import dev.dubhe.anvilcraft.AnvilCraft;
import dev.dubhe.anvilcraft.init.item.ModItems;
-import dev.dubhe.anvilcraft.util.TooltipUtil;
import lombok.Getter;
-import net.minecraft.ChatFormatting;
-import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
@@ -17,11 +14,8 @@
import net.minecraft.world.item.ArmorMaterials;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
-import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.ItemAttributeModifiers;
-import java.util.List;
-
public class EquipmentArmorItem extends ArmorItem {
@Getter
private final boolean weatherproof;
@@ -34,37 +28,6 @@ public EquipmentArmorItem(Properties properties, Type type, boolean weatherproof
this.texture = AnvilCraft.of("textures/entity/equipment/" + texture + ".png");
}
- @Override
- public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag flag) {
- super.appendHoverText(stack, context, tooltip, flag);
- switch (this.type) {
- case HELMET -> {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.breathing", ChatFormatting.GRAY));
- if (this.weatherproof) {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.clear_vision", ChatFormatting.GRAY));
- }
- }
- case CHESTPLATE -> {
- if (this.weatherproof) {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.recharge", ChatFormatting.GRAY));
- }
- }
- case LEGGINGS -> tooltip.addAll(TooltipUtil.translatedLines(
- "tooltip.anvilcraft.equipment.pockets", ChatFormatting.GRAY, this.weatherproof ? 12 : 6));
- case BOOTS -> {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.buffer_boots", ChatFormatting.GRAY));
- if (this.weatherproof) {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.fluid_walking", ChatFormatting.GRAY));
- }
- }
- default -> {
- }
- }
- if (this.weatherproof) {
- tooltip.addAll(TooltipUtil.translatedLines("tooltip.anvilcraft.equipment.full_suit", ChatFormatting.AQUA));
- }
- }
-
@Override
public int getDefense() {
return super.getDefense() + (this.weatherproof ? (this.type == Type.HELMET || this.type == Type.BOOTS ? 2 : 3) : 0);
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/FluidTankMinecartItem.java b/src/main/java/dev/dubhe/anvilcraft/item/FluidTankMinecartItem.java
index 7a5272075f..18a48d1aa3 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/FluidTankMinecartItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/FluidTankMinecartItem.java
@@ -1,10 +1,12 @@
package dev.dubhe.anvilcraft.item;
-import dev.dubhe.anvilcraft.api.tooltip.FluidTankItemTooltip;
import dev.dubhe.anvilcraft.entity.FluidTankMinecartEntity;
import dev.dubhe.anvilcraft.init.entity.ModEntities;
+import dev.dubhe.anvilcraft.util.UnitUtil;
+import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
+import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.dispenser.BlockSource;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
@@ -26,6 +28,7 @@
import net.minecraft.world.level.block.state.properties.RailShape;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.Vec3;
+import net.neoforged.neoforge.fluids.FluidStack;
import java.util.List;
@@ -46,12 +49,21 @@ public void appendHoverText(
TooltipFlag tooltipFlag
) {
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
- FluidTankItemTooltip.appendFixedTank(
- stack,
- context,
- tooltipComponents,
- FluidTankMinecartEntity.CAPACITY
- );
+ CustomData data = stack.getOrDefault(DataComponents.BLOCK_ENTITY_DATA, CustomData.EMPTY);
+ CompoundTag tank = data.copyTag().getCompound("Tank");
+ HolderLookup.Provider registries = context.registries();
+ FluidStack fluid = registries == null ? FluidStack.EMPTY
+ : FluidStack.parseOptional(registries, tank.getCompound("Fluid"));
+ int amount = fluid.isEmpty() ? 0 : Math.min(fluid.getAmount(), FluidTankMinecartEntity.CAPACITY);
+ if (!fluid.isEmpty()) {
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.fluid_tank.fluid").withStyle(ChatFormatting.BLUE));
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.fluid_tank.fluid.value",
+ fluid.getHoverName(), UnitUtil.fluidUnit(amount, false)).withStyle(ChatFormatting.GRAY));
+ }
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.fluid_tank.capacity").withStyle(ChatFormatting.BLUE));
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.fluid_tank.capacity.value",
+ UnitUtil.fluidUnit(amount, false), UnitUtil.fluidUnit(FluidTankMinecartEntity.CAPACITY, false))
+ .withStyle(ChatFormatting.GRAY));
}
private static FluidTankMinecartEntity createMinecart(
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/GuideBookItem.java b/src/main/java/dev/dubhe/anvilcraft/item/GuideBookItem.java
index 1c710c7ffc..a73859dd6a 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/GuideBookItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/GuideBookItem.java
@@ -1,9 +1,12 @@
package dev.dubhe.anvilcraft.item;
import dev.dubhe.anvilcraft.api.thought.Thinkable;
+import dev.dubhe.anvilcraft.api.thought.ThoughtManager;
import dev.dubhe.anvilcraft.integration.IntegrationUtil;
import dev.dubhe.anvilcraft.network.OpenIntegrationScreenPacket;
import dev.dubhe.anvilcraft.util.ModEventUtil;
+import net.minecraft.ChatFormatting;
+import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
@@ -20,6 +23,7 @@
import net.minecraft.world.level.Level;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
+import net.neoforged.neoforgespi.Environment;
import java.util.List;
@@ -51,7 +55,28 @@ public InteractionResultHolder use(Level level, Player player, Intera
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) {
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
- this.appendHoverText(tooltipComponents);
+ if (!Environment.get().getDist().isClient()) {
+ return;
+ }
+ long lastThoughtTime = ThoughtManager.getLastThoughtTime();
+ if (lastThoughtTime <= 0) {
+ tooltipComponents.add(
+ Component.translatable(
+ "tooltip.anvilcraft.thought",
+ Component.keybind("key.anvilcraft.thought")
+ ).withStyle(ChatFormatting.GRAY)
+ );
+ return;
+ }
+ Minecraft minecraft = Minecraft.getInstance();
+ long curTime = minecraft.gui.getGuiTicks();
+ long deltaTime = curTime - lastThoughtTime;
+ final int maxPlaceholderCount = 20;
+ final double maxSeconds = ThoughtManager.getMAX_SECONDS();
+ int placeholderCount = (int) Math.floor(Math.min(deltaTime, 20 * maxSeconds) / (20 * maxSeconds) * maxPlaceholderCount);
+ int blankCount = maxPlaceholderCount - placeholderCount;
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.thought.progress",
+ "||".repeat(Math.max(0, placeholderCount)), " ".repeat(Math.max(0, blankCount))).withStyle(ChatFormatting.GRAY));
}
@Override
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/HeavyHalberdItem.java b/src/main/java/dev/dubhe/anvilcraft/item/HeavyHalberdItem.java
index d8e56102f8..15fdab1306 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/HeavyHalberdItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/HeavyHalberdItem.java
@@ -6,7 +6,6 @@
import dev.dubhe.anvilcraft.init.enchantment.ModEnchantmentTags;
import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.item.property.component.Merciless;
-import net.minecraft.ChatFormatting;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.core.BlockPos;
@@ -17,7 +16,6 @@
import net.minecraft.core.HolderSet;
import net.minecraft.core.Position;
import net.minecraft.core.component.DataComponents;
-import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
@@ -53,7 +51,6 @@
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.Tier;
import net.minecraft.world.item.TieredItem;
-import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.ItemAttributeModifiers;
@@ -72,7 +69,6 @@
import org.jetbrains.annotations.Range;
import java.util.ArrayList;
-import java.util.List;
import java.util.function.Consumer;
import javax.annotation.Nullable;
@@ -234,17 +230,6 @@ && getMode(stack) == TRIDENT_MODE
);
}
- @Override
- public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) {
- super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
- tooltipComponents.add(
- Component.translatable(
- "tooltip.anvilcraft.heavy_halberd.desc",
- Component.keybind("key.anvilcraft.switch_tool_mode")
- ).withStyle(ChatFormatting.GRAY)
- );
- }
-
@Override
public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) {
super.inventoryTick(stack, level, entity, slotId, isSelected);
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java b/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
index 18e5432d69..3727fc5cc4 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
@@ -43,9 +43,6 @@ public class IonocraftBackpackItem extends EquipmentArmorItem implements IInvent
/** 追踪玩家背包飞行状态,用于在状态变化时同步到其他客户端 */
private static final Map FLYING_TRACKER = new WeakHashMap<>();
- /** 追踪上一 tick 是否装备着背包,用于在装备状态变化时重同步物品栏槽位 */
- private static final Map EQUIPPED_TRACKER = new WeakHashMap<>();
-
public IonocraftBackpackItem(Properties properties) {
this(properties, false);
}
@@ -187,14 +184,6 @@ public static void playerTick(ServerPlayer player) {
ItemStack backpack = getByPlayer(player);
boolean equipped = !backpack.isEmpty();
- // 装备状态变化时整表重同步物品栏。背包的穿戴只走 setItemSlot / onEquipItem,
- // 不经过 inventoryMenu 的槽位变更广播,客户端本端 Inventory 会残留上一次的槽位内容,
- // 表现为护腿等槽位出现幻影物品(仅客户端渲染,任意槽位点击后即被服务端校正回传覆盖)。
- Boolean prevEquipped = EQUIPPED_TRACKER.put(player, equipped);
- if (prevEquipped != null && prevEquipped != equipped) {
- player.inventoryMenu.sendAllDataToRemote();
- }
-
boolean nowFlying = equipped
&& player.getAbilities().flying
&& !player.isCreative()
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/ResonatorItem.java b/src/main/java/dev/dubhe/anvilcraft/item/ResonatorItem.java
index a5c00d043d..f46178a35b 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/ResonatorItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/ResonatorItem.java
@@ -8,7 +8,6 @@
import dev.dubhe.anvilcraft.init.item.ModItemTags;
import dev.dubhe.anvilcraft.network.ResonanceMiningEffectPacket;
import dev.dubhe.anvilcraft.network.WeaponChargeProgressPacket;
-import net.minecraft.ChatFormatting;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
@@ -17,7 +16,6 @@
import net.minecraft.core.HolderOwner;
import net.minecraft.core.HolderSet;
import net.minecraft.core.component.DataComponents;
-import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -44,7 +42,6 @@
import net.minecraft.world.item.SwordItem;
import net.minecraft.world.item.Tier;
import net.minecraft.world.item.TieredItem;
-import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.ItemAttributeModifiers;
@@ -119,17 +116,6 @@ public boolean applyForgeHandTransform(
});
}
- @Override
- public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) {
- super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
- tooltipComponents.add(
- Component.translatable(
- "tooltip.anvilcraft.resonator.mining_desc",
- Component.keybind("key.anvilcraft.switch_tool_mode")
- ).withStyle(ChatFormatting.GRAY)
- );
- }
-
public static ItemAttributeModifiers createAttributes(Tier tier, float attackDamage, float attackSpeed) {
return ItemAttributeModifiers.builder()
.add(
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/SpectralSlingshotItem.java b/src/main/java/dev/dubhe/anvilcraft/item/SpectralSlingshotItem.java
index b1d0edd3ea..580b5ddba5 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/SpectralSlingshotItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/SpectralSlingshotItem.java
@@ -438,19 +438,17 @@ public void appendHoverText(ItemStack stack, Item.TooltipContext context, List list = Lists.newArrayList();
Items.FIREWORK_ROCKET.appendHoverText(itemstack, context, list, tooltipFlag);
if (!list.isEmpty()) {
- list.replaceAll(sibling -> Component.literal(" ").append(sibling).withStyle(ChatFormatting.GRAY));
+ list.replaceAll(sibling -> Component.translatable("tooltip.anvilcraft.indented", sibling)
+ .withStyle(ChatFormatting.GRAY));
tooltipComponents.addAll(list);
}
}
tooltipComponents.add(
- Component
- .literal(" ")
- .append(
- canTakeOutAmmo(stack)
- ? Component.translatable("item.anvilcraft.spectral_slingshot.unload_return")
- : Component.translatable("item.anvilcraft.spectral_slingshot.unload_vanish")
- )
- .withStyle(ChatFormatting.GRAY)
+ Component.translatable("tooltip.anvilcraft.indented",
+ canTakeOutAmmo(stack)
+ ? Component.translatable("item.anvilcraft.spectral_slingshot.unload_return")
+ : Component.translatable("item.anvilcraft.spectral_slingshot.unload_vanish")
+ ).withStyle(ChatFormatting.GRAY)
);
}
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/StructureDiskItem.java b/src/main/java/dev/dubhe/anvilcraft/item/StructureDiskItem.java
index 6bd26f6b89..b08853df04 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/StructureDiskItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/StructureDiskItem.java
@@ -43,8 +43,7 @@ public void appendHoverText(ItemStack stack, Item.TooltipContext context, List tooltip, TooltipFlag flag) {
+ super.appendHoverText(stack, context, tooltip, flag);
+ BoxContents contents = stack.getOrDefault(ModComponents.BOX_CONTENTS, BoxContents.EMPTY);
+ tooltip.add(Component.empty());
+ tooltip.add(Component.translatable("tooltip.anvilcraft.item.amulet_box.fullness", contents.usage(), CAPACITY)
+ .withStyle(ChatFormatting.GRAY));
+ }
+
@Override
protected boolean storesContentsLocally() {
return true;
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/amulet/ComradeAmuletItem.java b/src/main/java/dev/dubhe/anvilcraft/item/amulet/ComradeAmuletItem.java
new file mode 100644
index 0000000000..894e2143f1
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/item/amulet/ComradeAmuletItem.java
@@ -0,0 +1,47 @@
+package dev.dubhe.anvilcraft.item.amulet;
+
+import com.mojang.authlib.GameProfile;
+import dev.dubhe.anvilcraft.init.item.ModComponents;
+import dev.dubhe.anvilcraft.item.property.component.Comrades;
+import dev.dubhe.anvilcraft.util.Util;
+import net.minecraft.ChatFormatting;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.TooltipFlag;
+import net.minecraft.world.level.Level;
+
+import java.util.List;
+import java.util.UUID;
+
+public class ComradeAmuletItem extends Item {
+ public ComradeAmuletItem(Properties properties) {
+ super(properties);
+ }
+
+ @Override
+ public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag flag) {
+ super.appendHoverText(stack, context, tooltipComponents, flag);
+ Comrades comrades = stack.get(ModComponents.COMRADES);
+ if (comrades == null) return;
+ tooltipComponents.add(Component.translatable("item.anvilcraft.comrade_amulet.tooltip").withStyle(ChatFormatting.GRAY));
+ for (UUID id : comrades.players()) {
+ Level level = context.level();
+ Component entry;
+ if (level != null) {
+ Player player = level.getPlayerByUUID(id);
+ if (player == null) {
+ entry = Component.literal(
+ Util.findProfileCache(level).get(id).map(GameProfile::getName).orElse(id.toString())
+ );
+ } else {
+ entry = player.getDisplayName();
+ }
+ } else {
+ entry = Component.literal(id.toString());
+ }
+ tooltipComponents.add(Component.translatable("tooltip.anvilcraft.comrade_amulet.player", entry));
+ }
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Comrades.java b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Comrades.java
index 2a20c16c5a..ee0a4c1750 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Comrades.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Comrades.java
@@ -1,28 +1,19 @@
package dev.dubhe.anvilcraft.item.property.component;
import com.google.common.collect.ImmutableList;
-import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
-import dev.dubhe.anvilcraft.util.Util;
import io.netty.buffer.ByteBuf;
-import net.minecraft.ChatFormatting;
import net.minecraft.core.UUIDUtil;
-import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.item.Item;
-import net.minecraft.world.item.TooltipFlag;
-import net.minecraft.world.item.component.TooltipProvider;
-import net.minecraft.world.level.Level;
import java.util.List;
import java.util.UUID;
-import java.util.function.Consumer;
/// 战友护符已签署的玩家
-public record Comrades(List players) implements TooltipProvider {
+public record Comrades(List players) {
public static final Comrades EMPTY = new Comrades(List.of());
public static final Codec CODEC = RecordCodecBuilder.create(ins -> ins.group(
UUIDUtil.CODEC
@@ -51,25 +42,4 @@ public boolean contains(UUID id) {
return this.players.contains(id);
}
- @Override
- public void addToTooltip(Item.TooltipContext ctx, Consumer builder, TooltipFlag flag) {
- builder.accept(Component.translatable("item.anvilcraft.comrade_amulet.tooltip").withStyle(ChatFormatting.GRAY));
- for (UUID id : this.players) {
- Level level = ctx.level();
- Component entry;
- if (level != null) {
- Player player = level.getPlayerByUUID(id);
- if (player == null) {
- entry = Component.literal(
- Util.findProfileCache(level).get(id).map(GameProfile::getName).orElse(id.toString())
- );
- } else {
- entry = player.getDisplayName();
- }
- } else {
- entry = Component.literal(id.toString());
- }
- builder.accept(Component.literal("· ").append(entry));
- }
- }
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Ferocious.java b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Ferocious.java
index b2a55c53b8..a0de9ae9c6 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Ferocious.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Ferocious.java
@@ -6,6 +6,8 @@
import dev.anvilcraft.lib.v2.util.InventoryUtil;
import dev.dubhe.anvilcraft.AnvilCraft;
import dev.dubhe.anvilcraft.init.item.ModComponents;
+import dev.dubhe.anvilcraft.item.HeavyHalberdItem;
+import dev.dubhe.anvilcraft.item.ResonatorItem;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
@@ -53,14 +55,14 @@ private static void tick(ItemStack stack) {
// 修改属性修饰符
ItemAttributeModifiers.Builder builder = ItemAttributeModifiers.builder();
- if (attackDamage != 0) {
+ if (attackDamage != 0 && !(stack.getItem() instanceof ResonatorItem)) {
builder.add(
Attributes.ATTACK_DAMAGE,
new AttributeModifier(FEROCIOUS_ID, attackDamage, AttributeModifier.Operation.ADD_VALUE),
EquipmentSlotGroup.MAINHAND
);
}
- if (miningEfficiency != 0) {
+ if (miningEfficiency != 0 && !(stack.getItem() instanceof HeavyHalberdItem)) {
builder.add(
Attributes.MINING_EFFICIENCY,
new AttributeModifier(FEROCIOUS_ID, miningEfficiency, AttributeModifier.Operation.ADD_VALUE),
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Merciless.java b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Merciless.java
index 47b7037ba7..59beaeef21 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/property/component/Merciless.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/property/component/Merciless.java
@@ -6,6 +6,8 @@
import dev.anvilcraft.lib.v2.util.InventoryUtil;
import dev.dubhe.anvilcraft.AnvilCraft;
import dev.dubhe.anvilcraft.init.item.ModComponents;
+import dev.dubhe.anvilcraft.item.HeavyHalberdItem;
+import dev.dubhe.anvilcraft.item.ResonatorItem;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
@@ -103,14 +105,14 @@ public static void absorbEnchantments(ItemStack stack) {
// 修改属性修饰符
ItemAttributeModifiers.Builder builder = ItemAttributeModifiers.builder();
- if (attackDamage != 0) {
+ if (attackDamage != 0 && !(stack.getItem() instanceof ResonatorItem)) {
builder.add(
Attributes.ATTACK_DAMAGE,
new AttributeModifier(MERCILESS_ID, attackDamage, AttributeModifier.Operation.ADD_VALUE),
EquipmentSlotGroup.MAINHAND
);
}
- if (miningEfficiency != 0) {
+ if (miningEfficiency != 0 && !(stack.getItem() instanceof HeavyHalberdItem)) {
builder.add(
Attributes.MINING_EFFICIENCY,
new AttributeModifier(MERCILESS_ID, miningEfficiency, AttributeModifier.Operation.ADD_VALUE),
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/weapon/AnvilRailgunItem.java b/src/main/java/dev/dubhe/anvilcraft/item/weapon/AnvilRailgunItem.java
index 5cff0f0f4a..9d22e8a17e 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/weapon/AnvilRailgunItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/weapon/AnvilRailgunItem.java
@@ -238,7 +238,7 @@ public void appendHoverText(ItemStack stack, Item.TooltipContext context, List {
+ @Shadow
+ private static CreativeModeTab selectedTab;
+ @Shadow
+ @Final
+ private static SimpleContainer CONTAINER;
+
+ protected CreativeModeInventoryScreenMixin(
+ CreativeModeInventoryScreen.ItemPickerMenu menu,
+ Inventory inventory,
+ Component title
+ ) {
+ super(menu, inventory, title);
+ }
+
@Inject(method = "slotClicked", at = @At("HEAD"), cancellable = true)
private void anvilcraft$blockTerminalSlotClick(@Nullable Slot slot, int slotId, int mouseButton, ClickType type, CallbackInfo ci) {
if (
@@ -30,4 +54,19 @@ abstract class CreativeModeInventoryScreenMixin {
ci.cancel();
}
}
+
+ @Inject(method = "renderLabels", at = @At("TAIL"))
+ private void anvilcraft$renderVariantIndicators(GuiGraphics graphics, int mouseX, int mouseY, CallbackInfo ci) {
+ if (selectedTab.getType() != CreativeModeTab.Type.CATEGORY) return;
+ graphics.pose().pushPose();
+ graphics.pose().translate(0.0F, 0.0F, 200.0F);
+ for (Slot slot : this.menu.slots) {
+ if (slot.container != CONTAINER || !slot.isActive()
+ || !CreativeVariantPickerRegistry.isCreativePickerEnabled(slot.getItem())) {
+ continue;
+ }
+ graphics.drawString(this.font, "+", slot.x + 10, slot.y + 1, 0xFFFFFFFF, true);
+ }
+ graphics.pose().popPose();
+ }
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/CreativePocketPacketMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/CreativePocketPacketMixin.java
index ddb4c9fd0e..ab6c2377c2 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/CreativePocketPacketMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/CreativePocketPacketMixin.java
@@ -1,15 +1,15 @@
package dev.dubhe.anvilcraft.mixin;
+import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.dubhe.anvilcraft.inventory.PocketInventory;
+import dev.dubhe.anvilcraft.inventory.PocketSlot;
import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.ModifyConstant;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerGamePacketListenerImpl.class)
@@ -17,17 +17,25 @@ abstract class CreativePocketPacketMixin {
@Shadow
public ServerPlayer player;
- @ModifyConstant(method = "handleSetCreativeModeSlot", constant = @Constant(intValue = 45))
- private int anvilcraft$allowPocketSlots(int original) {
- return original + PocketInventory.capacity(this.player);
+ @ModifyExpressionValue(method = "handleSetCreativeModeSlot", at = @At(value = "CONSTANT", args = "intValue=45"))
+ private int anvilcraft$allowPocketSlots(int original, ServerboundSetCreativeModeSlotPacket packet) {
+ int slot = packet.slotNum();
+ if (slot >= 0 && slot < this.player.inventoryMenu.slots.size()
+ && this.player.inventoryMenu.getSlot(slot) instanceof PocketSlot pocket && pocket.isActive()) {
+ return Math.max(original, slot);
+ }
+ return original;
}
@Inject(method = "handleSetCreativeModeSlot", at = @At(value = "INVOKE", target =
"Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;"
+ "Lnet/minecraft/network/PacketListener;Lnet/minecraft/server/level/ServerLevel;)V",
shift = At.Shift.AFTER), cancellable = true)
- private void anvilcraft$lockLeggings(ServerboundSetCreativeModeSlotPacket packet, CallbackInfo ci) {
- if (packet.slotNum() == 7 && PocketInventory.isLocked(this.player)) {
+ private void anvilcraft$validateSlot(ServerboundSetCreativeModeSlotPacket packet, CallbackInfo ci) {
+ int slot = packet.slotNum();
+ if (slot >= this.player.inventoryMenu.slots.size()
+ || slot >= 0 && this.player.inventoryMenu.getSlot(slot) instanceof PocketSlot pocket && !pocket.isActive()
+ || slot == 7 && PocketInventory.isLocked(this.player)) {
this.player.inventoryMenu.sendAllDataToRemote();
ci.cancel();
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/EntityMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/EntityMixin.java
index 56c805f71b..a2d7d89bfc 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/EntityMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/EntityMixin.java
@@ -42,6 +42,11 @@
@Mixin(Entity.class)
public abstract class EntityMixin implements IEntityExtension {
+ @ModifyVariable(method = {"onInsideBubbleColumn", "onAboveBubbleCol"}, at = @At("HEAD"), argsOnly = true)
+ private boolean anvilcraft$sinkInBubbleColumn(boolean downwards) {
+ return downwards || (Object) this instanceof Player player && EquipmentAbilities.shouldSinkInFluid(player);
+ }
+
@Inject(method = "setAirSupply", at = @At("HEAD"), cancellable = true)
private void anvilcraft$preventBreathingWithoutOxygen(int airSupply, CallbackInfo ci) {
if ((Object) this instanceof LivingEntity living && EquipmentAbilities.canBreathe(living)
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
index aa3a3ed792..b67e9ad805 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
@@ -4,10 +4,13 @@
import dev.dubhe.anvilcraft.client.init.ModRenderTargets;
import dev.dubhe.anvilcraft.client.init.ModShaders;
import dev.dubhe.anvilcraft.client.renderer.mun.MunSurfaceRenderer;
+import dev.dubhe.anvilcraft.init.item.ModItems;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceProvider;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -19,6 +22,11 @@
@Mixin(GameRenderer.class)
abstract class GameRendererMixin {
+ @ModifyReturnValue(method = "getNightVisionScale", at = @At("RETURN"))
+ private static float anvilcraft$steadyHelmetNightVision(float original, LivingEntity entity, float partialTick) {
+ return entity.getItemBySlot(EquipmentSlot.HEAD).is(ModItems.WEATHERPROOF_SPACESUIT_HELMET) ? 1 : original;
+ }
+
@ModifyReturnValue(method = "getRendertypeSolidShader", at = @At("RETURN"))
private static @Nullable ShaderInstance anvilcraft$munSolid(@Nullable ShaderInstance original) {
return MunSurfaceRenderer.terrain(original, 0);
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/ItemStackMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/ItemStackMixin.java
index 607b167d2d..5b9baf78df 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/ItemStackMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/ItemStackMixin.java
@@ -8,6 +8,7 @@
import dev.dubhe.anvilcraft.block.item.ChuteBlockItem;
import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.init.item.ModItems;
+import dev.dubhe.anvilcraft.item.AmuletAbilities;
import dev.dubhe.anvilcraft.item.BuildingRodItem;
import dev.dubhe.anvilcraft.item.HeavyHalberdItem;
import dev.dubhe.anvilcraft.item.MultitoolItem;
@@ -23,6 +24,7 @@
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.InteractionResultHolder;
+import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
@@ -44,6 +46,13 @@
@Mixin(ItemStack.class)
public abstract class ItemStackMixin implements DataComponentHolder {
+ @WrapMethod(method = "finishUsingItem")
+ private ItemStack anvilcraft$protectFoodEffects(Level level, LivingEntity consumer, Operation original) {
+ ItemStack stack = Util.cast(this);
+ if (stack.getFoodProperties(consumer) == null) return original.call(level, consumer);
+ return AmuletAbilities.consumeFood(consumer, () -> original.call(level, consumer));
+ }
+
@WrapMethod(method = "useOn")
private InteractionResult anvilcraft$animateOffhandRodPlacement(UseOnContext context, Operation original) {
ItemStack stack = context.getItemInHand();
@@ -153,7 +162,6 @@ private void addMercilessToTooltip(
tooltipFlag
);
this.addToTooltip(ModComponents.STORAGE, tooltipContext, list::add, tooltipFlag);
- this.addToTooltip(ModComponents.COMRADES, tooltipContext, list::add, tooltipFlag);
}
@Inject(
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/JeiPocketEffectAreaMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiPocketEffectAreaMixin.java
new file mode 100644
index 0000000000..fad980adcd
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/JeiPocketEffectAreaMixin.java
@@ -0,0 +1,23 @@
+package dev.dubhe.anvilcraft.mixin;
+
+import dev.dubhe.anvilcraft.client.gui.component.PocketEffectLayout;
+import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen;
+import net.minecraft.client.renderer.Rect2i;
+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 java.util.List;
+
+@Mixin(targets = "mezz.jei.library.plugins.vanilla.gui.InventoryEffectRendererGuiHandler", remap = false)
+abstract class JeiPocketEffectAreaMixin {
+ @Inject(method = "getGuiExtraAreas(Lnet/minecraft/client/gui/screens/inventory/EffectRenderingInventoryScreen;)Ljava/util/List;",
+ at = @At("HEAD"), cancellable = true)
+ private void anvilcraft$effectsAbovePockets(EffectRenderingInventoryScreen> screen,
+ CallbackInfoReturnable
> cir) {
+ if (PocketEffectLayout.isEnabled(screen)) {
+ cir.setReturnValue(PocketEffectLayout.areas(screen, PocketEffectLayout.visibleEffects().size()));
+ }
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LivingEntityMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LivingEntityMixin.java
index 99184a12ef..ef417bc5a2 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/LivingEntityMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LivingEntityMixin.java
@@ -19,6 +19,7 @@
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.init.loot.ModLootTables;
import dev.dubhe.anvilcraft.inventory.PocketInventory;
+import dev.dubhe.anvilcraft.item.AmuletAbilities;
import dev.dubhe.anvilcraft.item.EquipmentAbilities;
import dev.dubhe.anvilcraft.item.property.component.BoxContents;
import dev.dubhe.anvilcraft.util.AtmosphereManager;
@@ -35,6 +36,7 @@
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.entity.animal.IronGolem;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
@@ -46,7 +48,6 @@
import net.minecraft.world.level.storage.loot.LootTable;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.EffectCure;
-import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -64,6 +65,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import javax.annotation.Nullable;
@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity {
@@ -76,6 +78,11 @@ public abstract class LivingEntityMixin extends Entity {
return original;
}
+ @Inject(method = "setLastHurtByMob", at = @At("HEAD"), cancellable = true)
+ private void anvilcraft$ignoreProtectedAttacker(@Nullable LivingEntity attacker, CallbackInfo ci) {
+ if ((Object) this instanceof IronGolem && AmuletAbilities.isGolemProtected(attacker)) ci.cancel();
+ }
+
@Unique
private boolean anvilcraft$raged = false;
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java
index ac701c365e..f27a4b80b6 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java
@@ -6,6 +6,7 @@
import dev.dubhe.anvilcraft.block.cfa.CelestialForgingAnvilBlock;
import dev.dubhe.anvilcraft.block.cfa.item.CelestialForgingAnvilBlockItem;
import dev.dubhe.anvilcraft.block.entity.CelestialForgingAnvilBlockEntity;
+import dev.dubhe.anvilcraft.client.building.BuildingRodObstructionHighlight;
import dev.dubhe.anvilcraft.client.rpc.StorageTerminalClientStub;
import dev.dubhe.anvilcraft.client.rpc.TerminalJeiStorageCache;
import dev.dubhe.anvilcraft.entity.FluidTankMinecartEntity;
@@ -28,6 +29,7 @@
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
import java.util.UUID;
@@ -46,6 +48,11 @@ public MinecraftClientMixin(String name) {
super(name);
}
+ @Inject(method = "shouldEntityAppearGlowing", at = @At("HEAD"), cancellable = true)
+ private void anvilcraft$highlightBuildingObstruction(Entity entity, CallbackInfoReturnable cir) {
+ if (BuildingRodObstructionHighlight.isHighlighted(entity)) cir.setReturnValue(true);
+ }
+
@WrapOperation(
method = "pickBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;"
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/NearestAttackableTargetGoalMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/NearestAttackableTargetGoalMixin.java
index 803b48edf1..3183398460 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/NearestAttackableTargetGoalMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/NearestAttackableTargetGoalMixin.java
@@ -13,6 +13,7 @@
import net.minecraft.world.entity.ai.targeting.TargetingConditions;
import net.minecraft.world.entity.monster.AbstractSkeleton;
import net.minecraft.world.entity.monster.Creeper;
+import net.minecraft.world.entity.monster.Spider;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
@@ -46,6 +47,7 @@ public NearestAttackableTargetGoalMixin(Mob mob, boolean mustSee) {
private TargetingConditions addAmuletScare(TargetingConditions conditions) {
ResourceKey amulet;
switch (this.mob.getClass()) {
+ case Class> clazz when Spider.class.isAssignableFrom(clazz) -> amulet = ModAmulets.ARMADILLO.getKey();
case Class> clazz when clazz.isAssignableFrom(AbstractSkeleton.class) -> amulet = ModAmulets.DOG.getKey();
case Class> clazz when clazz.isAssignableFrom(Creeper.class) -> amulet = ModAmulets.CAT.getKey();
default -> {
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/PocketEffectScreenMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/PocketEffectScreenMixin.java
new file mode 100644
index 0000000000..0ee6df4e83
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/PocketEffectScreenMixin.java
@@ -0,0 +1,68 @@
+package dev.dubhe.anvilcraft.mixin;
+
+import dev.dubhe.anvilcraft.client.gui.component.PocketEffectLayout;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
+import net.minecraft.client.gui.screens.inventory.EffectRenderingInventoryScreen;
+import net.minecraft.client.renderer.Rect2i;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.effect.MobEffectInstance;
+import net.minecraft.world.effect.MobEffectUtil;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.neoforged.neoforge.client.ClientHooks;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+import java.util.Optional;
+
+@Mixin(EffectRenderingInventoryScreen.class)
+abstract class PocketEffectScreenMixin extends AbstractContainerScreen {
+ protected PocketEffectScreenMixin(T menu, Inventory inventory, Component title) {
+ super(menu, inventory, title);
+ }
+
+ @Shadow
+ protected abstract void renderBackgrounds(GuiGraphics graphics, int renderX, int offset,
+ Iterable effects, boolean large);
+
+ @Shadow
+ protected abstract void renderIcons(GuiGraphics graphics, int renderX, int offset,
+ Iterable effects, boolean large);
+
+ @Shadow
+ protected abstract Component getEffectName(MobEffectInstance effect);
+
+ @Inject(method = "renderEffects", at = @At("HEAD"), cancellable = true)
+ private void anvilcraft$effectsAbovePockets(GuiGraphics graphics, int mouseX, int mouseY, CallbackInfo ci) {
+ if (!PocketEffectLayout.isEnabled(this) || this.minecraft == null || this.minecraft.level == null) return;
+ ci.cancel();
+ List effects = PocketEffectLayout.visibleEffects();
+ List areas = PocketEffectLayout.areas(this, effects.size());
+ for (int index = 0; index < effects.size(); index++) {
+ Rect2i area = areas.get(index);
+ graphics.pose().pushPose();
+ graphics.pose().translate(area.getX(), area.getY(), 0);
+ float scale = area.getWidth() / 32.0F;
+ graphics.pose().scale(scale, scale, 1);
+ graphics.pose().translate(0, -this.topPos, 0);
+ List entry = List.of(effects.get(index));
+ this.renderBackgrounds(graphics, 0, 0, entry, false);
+ this.renderIcons(graphics, 0, 0, entry, false);
+ graphics.pose().popPose();
+ }
+ for (int index = effects.size() - 1; index >= 0; index--) {
+ if (!areas.get(index).contains(mouseX, mouseY)) continue;
+ MobEffectInstance effect = effects.get(index);
+ List tooltip = List.of(this.getEffectName(effect),
+ MobEffectUtil.formatDuration(effect, 1, this.minecraft.level.tickRateManager().tickrate()));
+ tooltip = ClientHooks.getEffectTooltip((EffectRenderingInventoryScreen>) (Object) this, effect, tooltip);
+ graphics.renderTooltip(this.font, tooltip, Optional.empty(), mouseX, mouseY);
+ break;
+ }
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/SpiderMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/SpiderMixin.java
new file mode 100644
index 0000000000..336e054860
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/SpiderMixin.java
@@ -0,0 +1,28 @@
+package dev.dubhe.anvilcraft.mixin;
+
+import dev.dubhe.anvilcraft.api.amulet.AmuletManager;
+import dev.dubhe.anvilcraft.init.item.ModAmulets;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
+import net.minecraft.world.entity.monster.Monster;
+import net.minecraft.world.entity.monster.Spider;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+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.CallbackInfo;
+
+@Mixin(Spider.class)
+public abstract class SpiderMixin extends Monster {
+ protected SpiderMixin(EntityType extends Monster> entityType, Level level) {
+ super(entityType, level);
+ }
+
+ @Inject(method = "registerGoals", at = @At("TAIL"))
+ private void anvilcraft$avoidArmadilloAmulet(CallbackInfo ci) {
+ this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 6.0F, 1.0, 1.2,
+ entity -> entity instanceof Player player
+ && AmuletManager.get(player.registryAccess()).hasAmuletInInventory(player, ModAmulets.ARMADILLO.getKey())));
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/network/BuildingRodObstructionPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/BuildingRodObstructionPacket.java
new file mode 100644
index 0000000000..199f5d2f7e
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/network/BuildingRodObstructionPacket.java
@@ -0,0 +1,28 @@
+package dev.dubhe.anvilcraft.network;
+
+import dev.anvilcraft.lib.v2.network.packet.IClientboundPacket;
+import dev.anvilcraft.lib.v2.network.packet.IPacket;
+import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.client.building.BuildingRodObstructionHighlight;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.ByteBufCodecs;
+import net.minecraft.network.codec.StreamCodec;
+import net.minecraft.world.entity.player.Player;
+
+import java.util.List;
+
+public record BuildingRodObstructionPacket(List entities) implements IClientboundPacket {
+ public static final Type TYPE = IPacket.type(AnvilCraft.of("building_rod_obstruction"));
+ public static final StreamCodec STREAM_CODEC = StreamCodec.composite(
+ ByteBufCodecs.VAR_INT.apply(ByteBufCodecs.list()), BuildingRodObstructionPacket::entities, BuildingRodObstructionPacket::new);
+
+ @Override
+ public Type type() {
+ return TYPE;
+ }
+
+ @Override
+ public void handleOnClient(Player player) {
+ BuildingRodObstructionHighlight.show(player.level(), this.entities);
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/network/StructureScannerFilePacket.java b/src/main/java/dev/dubhe/anvilcraft/network/StructureScannerFilePacket.java
index 1c8dddfa38..34ad19a733 100644
--- a/src/main/java/dev/dubhe/anvilcraft/network/StructureScannerFilePacket.java
+++ b/src/main/java/dev/dubhe/anvilcraft/network/StructureScannerFilePacket.java
@@ -4,7 +4,6 @@
import dev.anvilcraft.lib.v2.network.packet.IServerboundPacket;
import dev.dubhe.anvilcraft.AnvilCraft;
import dev.dubhe.anvilcraft.inventory.StructureScannerMenu;
-import dev.dubhe.anvilcraft.util.StructureFileTransfer;
import dev.dubhe.anvilcraft.util.StructureScannerFiles;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
@@ -13,20 +12,21 @@
import java.util.UUID;
-public record StructureScannerFilePacket(int containerId, UUID id, String name, int total, int offset, byte[] bytes)
+public record StructureScannerFilePacket(int containerId, UUID id, Action action, String name)
implements IServerboundPacket {
+ public enum Action {
+ LIST, IMPORT, EXPORT
+ }
+
public static final Type TYPE = IPacket.type(AnvilCraft.of("structure_scanner_file"));
public static final StreamCodec STREAM_CODEC = StreamCodec.of(
(buffer, packet) -> {
buffer.writeVarInt(packet.containerId);
buffer.writeUUID(packet.id);
+ buffer.writeEnum(packet.action);
buffer.writeUtf(packet.name, 128);
- buffer.writeVarInt(packet.total);
- buffer.writeVarInt(packet.offset);
- buffer.writeByteArray(packet.bytes);
},
- buffer -> new StructureScannerFilePacket(buffer.readVarInt(), buffer.readUUID(), buffer.readUtf(128),
- buffer.readVarInt(), buffer.readVarInt(), buffer.readByteArray(StructureFileTransfer.CHUNK_BYTES))
+ buffer -> new StructureScannerFilePacket(buffer.readVarInt(), buffer.readUUID(), buffer.readEnum(Action.class), buffer.readUtf(128))
);
@Override
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementPicking.java b/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementPicking.java
index 01db741e92..5035b63a9c 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementPicking.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementPicking.java
@@ -33,8 +33,11 @@ public static UseOnContext forPlacement(UseOnContext context) {
Vec3 start = player.getEyePosition();
// 使用本次点击携带的位置恢复射线,避免服务端依赖另一帧的视角包。
Vec3 direction = context.getClickLocation().subtract(start);
+ double range = player.blockInteractionRange();
+ // Sable 等模组的命中点可能在子空间存储坐标中,不能与玩家的世界坐标混合重算射线。
+ if (!(direction.lengthSqr() <= range * range + HIT_EPSILON)) return context;
if (direction.lengthSqr() < 1.0E-10) direction = player.getViewVector(1);
- Vec3 end = start.add(direction.normalize().scale(player.blockInteractionRange()));
+ Vec3 end = start.add(direction.normalize().scale(range));
BlockHitResult hit = context.getLevel().clip(new ClipContext(
start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player));
// 已在原形状表面的点击保留调用方给出的面和 inside 标记,包括只调整朝向的放置操作。
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementUtil.java b/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementUtil.java
index 0a5188a93d..295b4583c1 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementUtil.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/BlockPlacementUtil.java
@@ -396,6 +396,7 @@ public record BlueprintLayout(
BlockPos placerPos,
Direction targetFacing,
Direction scannerFacing,
+ boolean autoRotate,
boolean upsideDown,
int gridSize,
int distance,
@@ -413,19 +414,31 @@ public BlockPos getPosition(int storageIndex) {
return this.getPosition(storageIndex, this.targetFacing, this.upsideDown);
}
- private BlockPos getPosition(int storageIndex, Direction targetFacing, boolean upsideDown) {
+ public BlockPos getPosition(int storageIndex, Direction targetFacing, boolean upsideDown) {
int positionsPerLayer = this.gridSize * this.gridSize;
int layer = storageIndex / positionsPerLayer;
int position = storageIndex % positionsPerLayer;
int row = position / this.gridSize;
int column = position % this.gridSize;
- Direction right = targetFacing.getClockWise();
+ Direction layoutFacing = this.getLayoutFacing(targetFacing);
+ Direction right = layoutFacing.getClockWise();
int gridRadius = this.gridSize / 2;
int verticalOffset = upsideDown ? layer - this.gridSize + 1 : layer;
return this.placerPos.relative(targetFacing, this.distance)
.above(verticalOffset)
.relative(right, column - gridRadius)
- .relative(targetFacing, gridRadius - row);
+ .relative(layoutFacing, gridRadius - row);
+ }
+
+ private Direction getLayoutFacing(Direction targetFacing) {
+ if (this.autoRotate) {
+ return targetFacing;
+ }
+ return Direction.from2DDataValue(Math.floorMod(
+ this.scannerFacing.getOpposite().get2DDataValue()
+ + targetFacing.get2DDataValue() - this.targetFacing.get2DDataValue(),
+ 4
+ ));
}
public BlockState getState(int storageIndex) {
@@ -436,7 +449,7 @@ public BlockState getState(int storageIndex) {
this.states[storageIndex],
this.level,
this.getPosition(storageIndex),
- this.targetFacing,
+ this.getLayoutFacing(this.targetFacing),
this.scannerFacing,
this.upsideDown
);
@@ -450,7 +463,7 @@ public BlockState getState(int storageIndex, Direction targetFacing, boolean ups
this.states[storageIndex],
this.level,
this.getPosition(storageIndex, targetFacing, upsideDown),
- targetFacing,
+ this.getLayoutFacing(targetFacing),
this.scannerFacing,
upsideDown
);
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/GravityManager.java b/src/main/java/dev/dubhe/anvilcraft/util/GravityManager.java
index 5f56578da6..c6b99d2cfb 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/GravityManager.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/GravityManager.java
@@ -1,6 +1,7 @@
package dev.dubhe.anvilcraft.util;
import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.api.amulet.AmuletManager;
import dev.dubhe.anvilcraft.api.entity.IAnvilCraftEntityExtension;
import dev.dubhe.anvilcraft.api.injection.entity.IEntityExtension;
import dev.dubhe.anvilcraft.block.BlackHoleBlock;
@@ -11,6 +12,7 @@
import dev.dubhe.anvilcraft.entity.StandableFallingBlockEntity;
import dev.dubhe.anvilcraft.entity.StandableLevitatingBlockEntity;
import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.init.item.ModAmulets;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.network.GravitySourcesSyncPacket;
import net.minecraft.core.BlockPos;
@@ -156,6 +158,11 @@ public static GravityType getFallingBlockGravityType(Block block) {
: GravityType.NORMAL;
}
+ private static boolean ignoresCelestialGravity(Entity entity) {
+ return entity instanceof Player player && (player.isShiftKeyDown()
+ || AmuletManager.get(player.registryAccess()).hasAmuletInInventory(player, ModAmulets.ANVIL.getKey()));
+ }
+
public static Vec3 getGravityVector(Entity entity) {
return getGravityVector(entity, GravitySourceManager.getEntityG(entity));
}
@@ -165,7 +172,7 @@ public static Vec3 getGravityVector(Entity entity, double baseGravity) {
entity.level(),
entity.getBoundingBox().getCenter(),
Math.abs(baseGravity),
- entity instanceof Player player && player.isShiftKeyDown()
+ ignoresCelestialGravity(entity)
);
if (entity instanceof IAnvilCraftEntityExtension extension) {
Vec3 additional = extension.anvilcraft$getAdditionalGravity(Math.abs(baseGravity));
@@ -683,7 +690,7 @@ private static SweptGravity calculateSweptGravity(Entity entity, Vec3 movement)
Vec3 movementImpulse = Vec3.ZERO;
Vec3 velocityImpulse = Vec3.ZERO;
- boolean ignoreCelestialGravity = entity instanceof Player player && player.isShiftKeyDown();
+ boolean ignoreCelestialGravity = ignoresCelestialGravity(entity);
for (GravitySource source : index.sourcesAlong(start, end)) {
if (ignoreCelestialGravity && source.type().bodyRadius() > 0) continue;
if (start.distanceToSqr(source.center()) <= source.type().radiusSqr()
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/PlacementInteractions.java b/src/main/java/dev/dubhe/anvilcraft/util/PlacementInteractions.java
index 02b7b62b31..4c962780c1 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/PlacementInteractions.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/PlacementInteractions.java
@@ -1,5 +1,7 @@
package dev.dubhe.anvilcraft.util;
+import dev.dubhe.anvilcraft.block.entity.StoragePortBlockEntity;
+import dev.dubhe.anvilcraft.block.entity.StoragePortConsolidatorBlockEntity;
import dev.dubhe.anvilcraft.block.entity.storage.StorageBlockEntity;
import dev.dubhe.anvilcraft.block.item.ChuteBlockItem;
import dev.dubhe.anvilcraft.block.multipart.AbstractMultiPartBlock;
@@ -38,6 +40,10 @@ public static boolean allowsPlacement(UseOnContext context) {
BlockPos menuPos = block instanceof AbstractMultiPartBlock> multipart
? multipart.getMainPartPos(context.getClickedPos(), state) : context.getClickedPos();
var entity = context.getLevel().getBlockEntity(menuPos);
+ if (entity instanceof StoragePortBlockEntity || entity instanceof StoragePortConsolidatorBlockEntity) {
+ if (context.getItemInHand().getItem() instanceof ChuteBlockItem) return !ChuteBlockItem.isStorageInteraction(context);
+ return context.getHand() != InteractionHand.MAIN_HAND;
+ }
if (state.getMenuProvider(context.getLevel(), menuPos) != null
|| entity instanceof MenuProvider || entity instanceof StorageBlockEntity) return false;
return !(block instanceof ButtonBlock || block instanceof LeverBlock || block instanceof BedBlock || block instanceof BellBlock
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/StructureBlueprintFiles.java b/src/main/java/dev/dubhe/anvilcraft/util/StructureBlueprintFiles.java
new file mode 100644
index 0000000000..81a414fb31
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/util/StructureBlueprintFiles.java
@@ -0,0 +1,80 @@
+package dev.dubhe.anvilcraft.util;
+
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.level.storage.LevelResource;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+public final class StructureBlueprintFiles {
+ private StructureBlueprintFiles() {
+ }
+
+ public static Path directory(MinecraftServer server) throws IOException {
+ Path root = server.getWorldPath(LevelResource.ROOT).resolve("anvilcraft/blueprints").toAbsolutePath().normalize();
+ Files.createDirectories(root);
+ return root;
+ }
+
+ public static List list(MinecraftServer server) throws IOException {
+ List files = new ArrayList<>();
+ try (DirectoryStream entries = Files.newDirectoryStream(directory(server))) {
+ for (Path entry : entries) {
+ String name = entry.getFileName().toString();
+ if (isImportName(name) && Files.isRegularFile(entry, LinkOption.NOFOLLOW_LINKS)
+ && Files.size(entry) <= StructureFileTransfer.MAX_BYTES) files.add(name);
+ }
+ }
+ files.sort(Comparator.naturalOrder());
+ return files;
+ }
+
+ private static boolean isImportName(String name) {
+ String lower = name.toLowerCase(Locale.ROOT);
+ return StructureFileTransfer.isSafeName(name) && (lower.endsWith(".nbt") || lower.endsWith(".litematic"));
+ }
+
+ public static byte[] read(MinecraftServer server, String name) throws IOException {
+ if (!isImportName(name)) throw new IOException("Invalid blueprint file name");
+ Path file = resolve(server, name);
+ if (!Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS) || Files.size(file) > StructureFileTransfer.MAX_BYTES) {
+ throw new IOException("Invalid blueprint file");
+ }
+ byte[] bytes;
+ try (var input = Files.newInputStream(file, LinkOption.NOFOLLOW_LINKS)) {
+ bytes = input.readNBytes(StructureFileTransfer.MAX_BYTES + 1);
+ }
+ if (bytes.length == 0 || bytes.length > StructureFileTransfer.MAX_BYTES) throw new IOException("Invalid file size");
+ return bytes;
+ }
+
+ public static void write(MinecraftServer server, String name, byte[] bytes) throws IOException {
+ if (!name.toLowerCase(Locale.ROOT).endsWith(".nbt")) throw new IOException("Invalid export file name");
+ if (bytes.length == 0 || bytes.length > StructureFileTransfer.MAX_BYTES) throw new IOException("Invalid file size");
+ Path target = resolve(server, name);
+ Path temporary = Files.createTempFile(target.getParent(), ".scanner-", ".tmp");
+ try {
+ Files.write(temporary, bytes);
+ if (!resolve(server, name).equals(target)) throw new IOException("Export directory changed");
+ Files.move(temporary, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
+ } finally {
+ Files.deleteIfExists(temporary);
+ }
+ }
+
+ private static Path resolve(MinecraftServer server, String name) throws IOException {
+ if (!StructureFileTransfer.isSafeName(name)) throw new IOException("Invalid file name");
+ Path root = directory(server);
+ Path file = root.resolve(name).normalize();
+ if (!root.equals(file.getParent()) || Files.isSymbolicLink(file)) throw new IOException("Invalid file path");
+ return file;
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/StructureLoadUtil.java b/src/main/java/dev/dubhe/anvilcraft/util/StructureLoadUtil.java
index 60ca2f0b89..b2c369daad 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/StructureLoadUtil.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/StructureLoadUtil.java
@@ -26,7 +26,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -304,8 +303,6 @@ private static Path getStructureDirectory(Level level) {
// 客户端回退方案:使用 DistExecutor 安全地访问客户端代码
return StructureLoadUtil.getClientStructureDirectory();
-
- // 最后的备选方案:使用当前工作目录(确保永远不返回 null)
}
/**
@@ -324,12 +321,7 @@ private static Path getClientStructureDirectory() {
if (integratedServer != null) {
Path worldDir = integratedServer.getWorldPath(LevelResource.ROOT);
result.set(worldDir.toAbsolutePath().normalize().resolve("anvilcraft").resolve("structures"));
- return;
}
-
- // 如果是纯客户端(多人游戏),结构文件应该不存在,返回一个安全的路径
- Path gameDir = minecraft.gameDirectory.toPath();
- result.set(gameDir.resolve("anvilcraft").resolve("structures"));
}
} catch (Exception e) {
LOGGER.debug("Client-side structure directory fallback failed: {}", e.getMessage());
@@ -342,8 +334,7 @@ private static Path getClientStructureDirectory() {
return clientPath;
}
- // 最后的备选方案:使用当前工作目录
- return Paths.get(".").toAbsolutePath().normalize().resolve("anvilcraft").resolve("structures");
+ throw new IllegalStateException("Structure files require a server world");
}
/**
diff --git a/src/main/java/dev/dubhe/anvilcraft/util/StructureScannerFiles.java b/src/main/java/dev/dubhe/anvilcraft/util/StructureScannerFiles.java
index 82284a453e..b018e3c1be 100644
--- a/src/main/java/dev/dubhe/anvilcraft/util/StructureScannerFiles.java
+++ b/src/main/java/dev/dubhe/anvilcraft/util/StructureScannerFiles.java
@@ -18,9 +18,11 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
+import net.minecraft.nbt.StringTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.item.ItemStack;
@@ -45,21 +47,25 @@ private StructureScannerFiles() {
public static void handle(ServerPlayer player, StructureScannerMenu menu, StructureScannerFilePacket packet) {
try {
- if (!StructureFileTransfer.isSafeName(packet.name())) throw new IOException("Invalid file name");
- if (packet.total() == 0) {
- if (packet.offset() != 0 || packet.bytes().length != 0) throw new IOException("Invalid export request");
- export(player, menu, packet);
- return;
+ switch (packet.action()) {
+ case LIST -> {
+ ListTag files = new ListTag();
+ StructureBlueprintFiles.list(player.server).forEach(name -> files.add(StringTag.valueOf(name)));
+ CompoundTag tag = new CompoundTag();
+ tag.put("files", files);
+ sendFile(player, packet.id(), compress(tag));
+ }
+ case IMPORT -> {
+ byte[] preview = importBlueprint(player, menu, packet.name());
+ menu.broadcastChanges();
+ sendFile(player, packet.id(), preview);
+ }
+ case EXPORT -> {
+ exportBlueprint(player, menu, packet.name());
+ PacketDistributor.sendToPlayer(player, new StructureScannerFileResultPacket(packet.id(), "", 0, 0, new byte[0]));
+ }
+ default -> throw new IOException("Unsupported blueprint action");
}
- byte[] bytes = menu.acceptUpload(packet);
- if (bytes == null) return;
- CompoundTag tag = bytes.length >= 2 && (bytes[0] & 255) == 31 && (bytes[1] & 255) == 139
- ? NbtIo.readCompressed(new ByteArrayInputStream(bytes), NbtAccounter.create(16L * 1024 * 1024))
- : NbtIo.read(new DataInputStream(new ByteArrayInputStream(bytes)), NbtAccounter.create(16L * 1024 * 1024));
- if (tag == null) throw new IOException("Empty structure file");
- byte[] preview = stageImport(player, menu, tag, packet.name());
- menu.broadcastChanges();
- sendFile(player, packet.id(), preview);
} catch (IOException | ConstructionBlueprintException | IllegalArgumentException exception) {
AnvilCraft.LOGGER.warn("Structure scanner file operation failed: {}", packet.name(), exception);
String message = exception.getMessage();
@@ -69,6 +75,16 @@ public static void handle(ServerPlayer player, StructureScannerMenu menu, Struct
}
}
+ public static byte[] importBlueprint(ServerPlayer player, StructureScannerMenu menu, String name)
+ throws IOException, ConstructionBlueprintException {
+ byte[] bytes = StructureBlueprintFiles.read(player.server, name);
+ CompoundTag tag = bytes.length >= 2 && (bytes[0] & 255) == 31 && (bytes[1] & 255) == 139
+ ? NbtIo.readCompressed(new ByteArrayInputStream(bytes), NbtAccounter.create(16L * 1024 * 1024))
+ : NbtIo.read(new DataInputStream(new ByteArrayInputStream(bytes)), NbtAccounter.create(16L * 1024 * 1024));
+ if (tag == null) throw new IOException("Empty structure file");
+ return stageImport(player, menu, tag, name);
+ }
+
public static StructureSnapshot parseImport(ServerPlayer player, CompoundTag tag, String fileName)
throws IOException, ConstructionBlueprintException {
if (!StructureFileTransfer.isSafeName(fileName)) throw new IOException("Invalid file name");
@@ -134,9 +150,9 @@ private static void sendFile(ServerPlayer player, UUID id, byte[] bytes) {
}
}
- private static void export(ServerPlayer player, StructureScannerMenu menu, StructureScannerFilePacket packet)
+ public static void exportBlueprint(ServerPlayer player, StructureScannerMenu menu, String name)
throws IOException, ConstructionBlueprintException {
- sendFile(player, packet.id(), compress(exportStructure(player, menu)));
+ StructureBlueprintFiles.write(player.server, name, compress(exportStructure(player, menu)));
}
public static CompoundTag exportStructure(ServerPlayer player, StructureScannerMenu menu)
diff --git a/src/main/java/dev/dubhe/anvilcraft/worldgen/OverworldLikeGenerationBootstrap.java b/src/main/java/dev/dubhe/anvilcraft/worldgen/OverworldLikeGenerationBootstrap.java
index 38c5670c83..b6053343e8 100644
--- a/src/main/java/dev/dubhe/anvilcraft/worldgen/OverworldLikeGenerationBootstrap.java
+++ b/src/main/java/dev/dubhe/anvilcraft/worldgen/OverworldLikeGenerationBootstrap.java
@@ -207,7 +207,8 @@ private static void configureOverworldLikeBorder(MinecraftServer server, ServerL
private static void unlinkOverworldLikeBorder(MinecraftServer server, ServerLevel level) {
WorldBorder overworldBorder = server.overworld().getWorldBorder();
- for (BorderChangeListener listener : ((WorldBorderAccessor) overworldBorder).invokeGetListeners()) {
+ List listeners = List.copyOf(((WorldBorderAccessor) overworldBorder).invokeGetListeners());
+ for (BorderChangeListener listener : listeners) {
if (listener instanceof DelegateBorderChangeListenerAccessor delegate
&& delegate.getWorldBorder() == level.getWorldBorder()) {
overworldBorder.removeListener(listener);
diff --git a/src/main/resources/anvilcraft.mixins.json b/src/main/resources/anvilcraft.mixins.json
index 0fffdb3a18..f0ac827bb0 100644
--- a/src/main/resources/anvilcraft.mixins.json
+++ b/src/main/resources/anvilcraft.mixins.json
@@ -73,6 +73,7 @@
"ServerPlayerContainerListenerMixin",
"ServerPlayerMixin",
"ServicesMixin",
+ "SpiderMixin",
"StriderMixin",
"TntBlockMixin",
"UseOnContextMixin",
@@ -132,12 +133,14 @@
"ItemInHandRendererMixin",
"ItemOverridesMixin",
"JeiBasicRecipeTransferHandlerMixin",
+ "JeiPocketEffectAreaMixin",
"LevelRendererMixin",
"LocalPlayerMixin",
"MinecraftClientMixin",
"MouseHandlerMixin",
"OverworldSkyMixin",
"ParticleEngineMixin",
+ "PocketEffectScreenMixin",
"PocketInventoryScreenMixin",
"PocketRecipeBookMixin",
"PostChainAccessor",
diff --git a/src/main/resources/assets/anvilcraft/ageratum/en_us/001_feature/001_spawner.md b/src/main/resources/assets/anvilcraft/ageratum/en_us/001_feature/001_spawner.md
index 320dcd25a9..ea545c7719 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/en_us/001_feature/001_spawner.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/en_us/001_feature/001_spawner.md
@@ -1,10 +1,10 @@
---
navigation:
- title: "Forced Spawner Activation"
+ title: "Immediate Spawn Attempt"
icon: "minecraft:spawner"
---
-# Forced Spawner Activation
+# Immediate Spawn Attempt
diff --git a/src/main/resources/assets/anvilcraft/ageratum/en_us/005_tool/001_amethyst_tools.md b/src/main/resources/assets/anvilcraft/ageratum/en_us/005_tool/001_amethyst_tools.md
index dd8cad3341..6cc32d8da7 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/en_us/005_tool/001_amethyst_tools.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/en_us/005_tool/001_amethyst_tools.md
@@ -30,7 +30,7 @@ items:
| Tool | Enchantment (Crafting Level / Max Level) | Enchantment Function |
|---------|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| Pickaxe | Fortune III / III | Increases ore drops (helps obtain more iron ore) |
-| Axe | Timber I / III | When chopping, each level breaks 2 additional connected logs |
+| Axe | Timber I / III | When chopping, each level breaks 4 additional connected logs |
| Shovel | Efficiency III / V | Increases breaking speed |
| Hoe | Harvest I / III | Right-click mature crops to harvest and replant them, also breaks weeds within range; breaking leaves with left-click also breaks nearby leaves; each level harvests an additional ring |
| Sword | Beheading I / III | Each level increases the chance of certain mobs dropping heads; very high chance for players and the Ender Dragon |
diff --git a/src/main/resources/assets/anvilcraft/ageratum/en_us/008_recipe/210_spawner.md b/src/main/resources/assets/anvilcraft/ageratum/en_us/008_recipe/210_spawner.md
index 7d3e400853..3cfb9f27aa 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/en_us/008_recipe/210_spawner.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/en_us/008_recipe/210_spawner.md
@@ -41,5 +41,5 @@ If you do not have a means of moving , plan the l
-Recommended to use in conjunction with [Forced Mob Spawning](../001_feature/001_spawner.md)
+Recommended to use in conjunction with [Immediate Spawn Attempt](../001_feature/001_spawner.md)
diff --git a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/001_feature/001_spawner.md b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/001_feature/001_spawner.md
index 544ebc369d..44ddb4c5c8 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/001_feature/001_spawner.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/001_feature/001_spawner.md
@@ -1,10 +1,10 @@
---
navigation:
- title: "强制刷怪笼工作"
+ title: "立即尝试刷怪"
icon: "minecraft:spawner"
---
-# 强制刷怪笼工作
+# 立即尝试刷怪
diff --git a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/005_tool/001_amethyst_tools.md b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/005_tool/001_amethyst_tools.md
index 14dea8fffb..d46568a76e 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/005_tool/001_amethyst_tools.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/005_tool/001_amethyst_tools.md
@@ -30,7 +30,7 @@ items:
| 工具 | 附魔(合成赋予的等级 / 等级上限) | 附魔功能 |
|----|--------------------|------------------------------------|
| 镐 | 时运 III / III | 增加矿物掉落(帮助获得更多铁矿) |
-| 斧 | 伐木 I / III | 伐木时,每级额外破坏 2 个相连的原木 |
+| 斧 | 伐木 I / III | 伐木时,每级额外破坏 4 个相连的原木 |
| 锹 | 效率 III / V | 提高破坏效率 |
| 锄 | 收割 I / III | 右键成熟庄稼可收获并补种,也可破坏范围内的杂草;左键破坏树叶可范围破坏树叶;每级额外收割一圈范围 |
| 剑 | 斩首 I / III | 每级提高生物掉落头颅的概率,对玩家和末影龙概率极高 |
diff --git a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/008_recipe/210_spawner.md b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/008_recipe/210_spawner.md
index 5c6c0c106f..8ea480bd87 100644
--- a/src/main/resources/assets/anvilcraft/ageratum/zh_cn/008_recipe/210_spawner.md
+++ b/src/main/resources/assets/anvilcraft/ageratum/zh_cn/008_recipe/210_spawner.md
@@ -41,5 +41,5 @@ items:
-推荐配合[强制刷怪](../001_feature/001_spawner.md)使用
+推荐配合[立即尝试刷怪](../001_feature/001_spawner.md)使用
diff --git a/src/main/resources/assets/anvilcraft/textures/gui/machine/structure_scanner/export.png b/src/main/resources/assets/anvilcraft/textures/gui/machine/structure_scanner/export.png
index bc5942581ad336ed6654ae2de54ab33e551ff91b..9972c6f514aba76f10fe4222d7840900b05164fc 100644
GIT binary patch
delta 972
zcmV;-12g=h0@VkQ7k^*~1^@s66w&ga000AuNklx7>0-5%BPPXWPW}=02`mh<1rC{NCL@5#KuR=d^RQm5J@nCjrfS!vyCo*
z`s0pv*|2ewsA_SYD3c9-PaVzaRH2~2o;Qt
z3`rI6(w3qXFB1zD>^^=|svuOr4gB~vBQf3&w#%!ITLIW{44WJfD!_;_Io2)HQ}04A
zU>ex?VvgB~07McQ->W4izgq?*4`wRnU}
zGH`(%doRkqGq+0tF3BJWOpbQR_`sv4K#mJwR7I#@YW$^C0WWQ7+qp{?gO=Kx*RmLd
zDO71Doa=D>|W$|11OXP_KQq31jwRnU}4sZdQ
z#L_jCD!qW5Fa_1(`KlJj!y5dZ^TO;GdGe?$0cV4SK=~k4fYI};CbQqC67+NqNDR0!
zT)>^vr$izkdVK3jxv_6u0AkL~j8q_VHglx{6MusuF*3D#!=rNjX@39}#Z{yNSwh{_
zzfB}YT~L0V+T1@rBT@t>=Ek2Z$a%lLrvm3cixibdT1x@0>A1kU^{sOH
z!qHNITPMKYf9cK(lv3raKn}V8*m+^3^R6@^{+A!yln?x8oOscm^a4H{{4q$1;R5cA
zdVc|Z0g<>LAm-f6NCh%yGgm4Q_X6xpt+5}(Ripx0LfsYj0_?ir|J4sR?>SMb7tk}{
z+EVx;u3o!EcAmUe3UKQL*!wS^7pk8b!mq&IzY6#zf&d21qOqCqjDI1J16+VFjBmdY
z_+yY1!v);A+)$)&bHT>L7b`c?Ma=!B??tIV(dL3!gR~3KBxn|80M~R}fF`kY4TZgb
u6)=sT00030|5+FcGynhq21!IgR09C4y!Fh6f>Fc(0000S2EuJyvqoZNPi_K*#3~vrYM<)Lc?c)
z;$;XRtWyYjA0`BQHz7uECWOEySZhUwfL&48fm;!zNDxC4AKFe(yce!SUB4Gv-xnx0
zLjh%-Liuz(gnAyDV&)M-p`M3*3}H+1A0D_BnMa5tj~JTx(B}z?@4}U+>pu$r7Es|Q
azyN1T90R0KrwvE|0000TH77k^*~1^@s66w&ga000AbNklSZd6&yYh)CeR+u50KzMI|0&D*Pq$kKX%fOK5ER0+w0#eWH4EG~YR5Q75)^5pR&
z2kbD0O%5;tCLz9j{v=CFOAgp!EVLU6grp)n5*QnOFHgI>q8xn0@&OYd2#Ioi>7MN8
z#Nfd7F%uvNk$|c)WT2Y*;K21k6YvhkJ$v_wYMQ`YRRte*9xDp?C#b3kdEcr6lvsp@
z0+gtlK2f!Jgn#RWYze1MpOkAiu9pJbj6l6UDt&!}YXUilSOrX~zzIx_k4hErz*^Cc
zmx;Lwj;t^;-$!zKr~3NZ4RoBiN=0n5P7$NjvafG4ar
zMglXFeNumUKgz>LEFUldf{-YevVkXK17dLC`j`n2gnvXpRT(l+O?`0S`k)DT2jh;W
z!}c08S5?7>oyUp-{t2pTLf*Hk03{Zop#UYSrcYEY9^rZ+Tf&jntI~GiUMaxM2+U0M
z%G7Y@nm`UBRsoYLa00VaucZojV6D03sH_LI`n$KX9=Ih`X(r^jC2#{Yi4ZiA&4$A1
zkKIYmfq#F_O1zPq4P|*>(%q5elPXO@Rb2~HEgs=U0yK$@YbaHE0XZzArdqsS)#A8&
zo&V1F!ooLs*4Z0@Z-bRU`M_0x(f^_@3qNNg^!E%)2)H3kz?^gEMFJq&esHVYZrkO6
zm~j&$7D$|HxnhCo5s?sy+QYF4x%GU=fr`Q^Vt;`oA#)8K5($wB%JREl{;7G9A{a3>
zR<9uCHN3|Hm%oY>Atz-5ttXmg+m@zMfa`TkVAt*@>9}&H6yRnAIQwth_X4F<`Bor@
z+<)wQVZ7&ocqITBV(L#g4l
zMR-^p=zIr26<1&b1{@kNKAF(~=oEXxp>w1sSOrAzL%n%uOA(%1XHc)NrKaC4!p9Oo
z_zvK&UxCbh$O1X`Ap`P09E2m1Aoj7~*oPnL%|q`g!h7os>h*7>e_K%LC%^!m;2a|f
T+ECa40000
Date: Sat, 19 Sep 2026 22:31:57 +0800
Subject: [PATCH 2/3] change
---
.../assets/anvilcraft/lang/en_ud.json | 6 ++
.../assets/anvilcraft/lang/en_us.json | 6 ++
.../api/tooltip/ItemTooltipManager.java | 8 +++
.../block/entity/BaseChuteBlockEntity.java | 6 +-
.../event/WheelLifecycleEventListener.java | 54 ++++++++++++++++++
.../anvilcraft/data/lang/ScreenLang.java | 4 ++
.../data/lang/ToolPropertyLang.java | 2 +
.../anvilcraft/init/item/ModComponents.java | 10 ++++
.../dubhe/anvilcraft/init/item/ModItems.java | 3 +
.../anvilcraft/item/EquipmentAbilities.java | 55 +++++++++++++++----
.../anvilcraft/item/EquipmentArmorItem.java | 12 ++++
.../item/IonocraftBackpackItem.java | 4 ++
.../anvilcraft/mixin/GameRendererMixin.java | 5 +-
.../network/SwitchEquipmentAbilityPacket.java | 36 ++++++++++++
14 files changed, 196 insertions(+), 15 deletions(-)
create mode 100644 src/main/java/dev/dubhe/anvilcraft/network/SwitchEquipmentAbilityPacket.java
diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json
index 6d88db5b52..2c78d05a42 100644
--- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json
+++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json
@@ -1696,6 +1696,10 @@
"screen.anvilcraft.ember_smithing.resonator_core.missing_tools": "sꞁooʇ ꞁɐʇǝɯ ʇsoɹℲ ɹo ɹǝqɯƎ ǝɯɐs ppⱯ",
"screen.anvilcraft.ember_smithing.tooltip.missing_template": "ǝʇɐꞁdɯǝ⟘ ǝuO oʇ ǝꞁdᴉʇꞁnW ppⱯ",
"screen.anvilcraft.ember_smithing.two.missing": "ɯnᴉpuǝɔsuɐɹ⟘ ǝsɐɥdᴉʇꞁnW ppⱯ",
+ "screen.anvilcraft.equipment.charged_jump.off": "dɯnՐ pǝᵷɹɐɥƆ ǝꞁqɐsᴉᗡ",
+ "screen.anvilcraft.equipment.charged_jump.on": "dɯnՐ pǝᵷɹɐɥƆ ǝꞁqɐuƎ",
+ "screen.anvilcraft.equipment.night_vision.off": "uoᴉsᴉɅ ʇɥᵷᴉN ǝꞁqɐsᴉᗡ",
+ "screen.anvilcraft.equipment.night_vision.on": "uoᴉsᴉɅ ʇɥᵷᴉN ǝꞁqɐuƎ",
"screen.anvilcraft.exp_collector.tooltip": "ᗺɯ%s/%s :dxƎ",
"screen.anvilcraft.filter.allow_list": "ʇsᴉꞀ ʍoꞁꞁⱯ",
"screen.anvilcraft.filter.deny_list": "ʇsᴉꞀ ʎuǝᗡ",
@@ -2473,6 +2477,7 @@
"tooltip.anvilcraft.propel_piston.remaining_energy": "%s :ʎᵷɹǝuƎ ᵷuᴉuᴉɐɯǝᴚ ",
"tooltip.anvilcraft.propel_piston.remaining_push": "ǝɯᴉʇ-ʞɔoꞁq %s :ɥsnԀ ᵷuᴉuᴉɐɯǝᴚ ",
"tooltip.anvilcraft.propel_piston.state": " :ǝʇɐʇS uoʇsᴉԀ ꞁǝdoɹԀ",
+ "tooltip.anvilcraft.property.charged_jump": "%2$s :[%1$s pꞁoH] dɯnՐ pǝᵷɹɐɥƆ",
"tooltip.anvilcraft.property.eternal": "pᴉoʌ ǝɥʇ puɐ ǝɯᴉʇ ǝɥʇ uǝʌǝ 'snʇɔɐɔ 'ǝpoꞁdxǝ 'ǝɹᴉɟ ǝunɯɯᴉ 'ǝꞁqɐʞɐǝɹqun :ꞁɐuɹǝʇƎ",
"tooltip.anvilcraft.property.ferocious": "sʇuǝɯʇuɐɥɔuǝ ꞁꞁɐ ɟo ꞁǝʌǝꞁ ǝɥʇ uo pǝsɐq ʎɔuǝᴉɔᴉɟɟǝ ᵷuᴉuᴉɯ puɐ ǝᵷɐɯɐp ʞɔɐʇʇɐ ǝɔuɐɥuǝ :snoᴉɔoɹǝℲ",
"tooltip.anvilcraft.property.fire_reforging": "ɐʌɐꞁ ɹo ǝɹᴉɟ uᴉ ᵷuᴉpuǝɯ :ᵷuᴉᵷɹoɟǝᴚ",
@@ -2486,6 +2491,7 @@
"tooltip.anvilcraft.property.multiphase.suffix.1": "β-",
"tooltip.anvilcraft.property.multiphase.suffix.2": "γ-",
"tooltip.anvilcraft.property.multiphase.suffix.3": "δ-",
+ "tooltip.anvilcraft.property.night_vision": "%2$s :[%1$s pꞁoH] uoᴉsᴉɅ ʇɥᵷᴉN",
"tooltip.anvilcraft.property.protect_containers": "%1$s :[⟘ꞀⱯ] uoᴉʇɔǝʇoɹԀ ɹǝuᴉɐʇuoƆ",
"tooltip.anvilcraft.property.protect_containers.off": "ɟɟO",
"tooltip.anvilcraft.property.protect_containers.on": "uO",
diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json
index 9ef0cf2dca..233146b372 100644
--- a/src/generated/resources/assets/anvilcraft/lang/en_us.json
+++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json
@@ -1696,6 +1696,10 @@
"screen.anvilcraft.ember_smithing.resonator_core.missing_tools": "Add same Ember or Frost metal tools",
"screen.anvilcraft.ember_smithing.tooltip.missing_template": "Add Multiple to One Template",
"screen.anvilcraft.ember_smithing.two.missing": "Add Multiphase Transcendium",
+ "screen.anvilcraft.equipment.charged_jump.off": "Disable Charged Jump",
+ "screen.anvilcraft.equipment.charged_jump.on": "Enable Charged Jump",
+ "screen.anvilcraft.equipment.night_vision.off": "Disable Night Vision",
+ "screen.anvilcraft.equipment.night_vision.on": "Enable Night Vision",
"screen.anvilcraft.exp_collector.tooltip": "Exp: %s/%smB",
"screen.anvilcraft.filter.allow_list": "Allow List",
"screen.anvilcraft.filter.deny_list": "Deny List",
@@ -2473,6 +2477,7 @@
"tooltip.anvilcraft.propel_piston.remaining_energy": " Remaining Energy: %s",
"tooltip.anvilcraft.propel_piston.remaining_push": " Remaining Push: %s block-time",
"tooltip.anvilcraft.propel_piston.state": "Propel Piston State: ",
+ "tooltip.anvilcraft.property.charged_jump": "Charged Jump [Hold %1$s]: %2$s",
"tooltip.anvilcraft.property.eternal": "Eternal: unbreakable, immune fire, explode, cactus, even the time and the void",
"tooltip.anvilcraft.property.ferocious": "Ferocious: enhance attack damage and mining efficiency based on the level of all enchantments",
"tooltip.anvilcraft.property.fire_reforging": "Reforging: mending in fire or lava",
@@ -2486,6 +2491,7 @@
"tooltip.anvilcraft.property.multiphase.suffix.1": "-β",
"tooltip.anvilcraft.property.multiphase.suffix.2": "-γ",
"tooltip.anvilcraft.property.multiphase.suffix.3": "-δ",
+ "tooltip.anvilcraft.property.night_vision": "Night Vision [Hold %1$s]: %2$s",
"tooltip.anvilcraft.property.protect_containers": "Container Protection [ALT]: %1$s",
"tooltip.anvilcraft.property.protect_containers.off": "Off",
"tooltip.anvilcraft.property.protect_containers.on": "On",
diff --git a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
index 5610b93f53..90dc3aad2c 100644
--- a/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
+++ b/src/main/java/dev/dubhe/anvilcraft/api/tooltip/ItemTooltipManager.java
@@ -979,6 +979,14 @@ public static void addTooltip(ItemStack stack, List tooltip) {
ModKeyMappings.SWITCH_PHASE.get().getKey().getDisplayName()
);
}
+ var equipmentAbility = EquipmentArmorItem.abilityComponent(stack);
+ if (equipmentAbility != null) {
+ boolean enabled = stack.getOrDefault(equipmentAbility, true);
+ propertyTooltip(equipmentAbility == ModComponents.NIGHT_VISION_ENABLED ? "night_vision" : "charged_jump",
+ tooltip, 0xDD91FA, ModKeyMappings.SWITCH_TOOL_MODE.get().getTranslatedKeyMessage(),
+ Component.translatable(enabled ? "options.on" : "options.off")
+ .withStyle(enabled ? ChatFormatting.GREEN : ChatFormatting.RED));
+ }
if (stack.has(ModComponents.DEVOUR_PROTECT_CONTAINERS)) {
boolean protectContainers = stack.getOrDefault(ModComponents.DEVOUR_PROTECT_CONTAINERS, false);
propertyTooltip(
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseChuteBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseChuteBlockEntity.java
index ac6a7e8ea7..8137b03378 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseChuteBlockEntity.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseChuteBlockEntity.java
@@ -233,8 +233,10 @@ public void tick() {
getInputDirection().getOpposite(),
level
);
- if (source != null) {
- resetCD |= ItemHandlerUtil.importFromTarget(getItemHandler(), 64, stack -> true, source);
+ boolean imported = source != null
+ && ItemHandlerUtil.importFromTarget(getItemHandler(), 64, stack -> true, source);
+ if (imported) {
+ resetCD = true;
} else {
List itemEntities = Objects.requireNonNull(getLevel())
.getEntitiesOfClass(
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/event/WheelLifecycleEventListener.java b/src/main/java/dev/dubhe/anvilcraft/client/event/WheelLifecycleEventListener.java
index df94487ace..313c8ffd66 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/event/WheelLifecycleEventListener.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/event/WheelLifecycleEventListener.java
@@ -10,12 +10,14 @@
import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.item.DragonRodItem;
+import dev.dubhe.anvilcraft.item.EquipmentArmorItem;
import dev.dubhe.anvilcraft.item.HeavyHalberdItem;
import dev.dubhe.anvilcraft.item.MultitoolItem;
import dev.dubhe.anvilcraft.item.ResonatorItem;
import dev.dubhe.anvilcraft.item.property.component.Multiphase;
import dev.dubhe.anvilcraft.item.property.component.TerminalBinding;
import dev.dubhe.anvilcraft.network.SwitchDragonRodProtectContainersPacket;
+import dev.dubhe.anvilcraft.network.SwitchEquipmentAbilityPacket;
import dev.dubhe.anvilcraft.network.SwitchHeavyHalberdModePacket;
import dev.dubhe.anvilcraft.network.SwitchMultitoolModePacket;
import dev.dubhe.anvilcraft.network.SwitchResonateModePacket;
@@ -29,7 +31,9 @@
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
+import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.level.block.Blocks;
import net.neoforged.api.distmarker.Dist;
@@ -69,6 +73,10 @@ public class WheelLifecycleEventListener {
private static boolean dragonRodKeyWasDown = false;
private static @Nullable Optional dragonRodWheelCache = null;
+ private static long equipmentKeyTime = -1L;
+ private static boolean equipmentKeyWasDown = false;
+ private static @Nullable WheelMenuModel equipmentWheelCache;
+
private static long balanceKeyTime = -1L;
private static boolean balanceKeyWasDown = false;
private static @Nullable Optional balanceWheelCache = null;
@@ -89,6 +97,7 @@ public static void onClientTick(ClientTickEvent.Post event) {
WheelLifecycleEventListener.openMultitoolWheel(gameTime);
WheelLifecycleEventListener.openDragonRodWheel(gameTime);
WheelLifecycleEventListener.openBalanceWheel(gameTime);
+ WheelLifecycleEventListener.openEquipmentWheel(gameTime);
}
private static void openMultiphaseWheel(long gameTime) {
@@ -219,6 +228,23 @@ private static void openDragonRodWheel(long gameTime) {
}
}
+ private static void openEquipmentWheel(long gameTime) {
+ Minecraft client = Minecraft.getInstance();
+ if (equipmentKeyTime < 0 || gameTime - equipmentKeyTime <= 4 || client.player == null
+ || client.screen != null && !equipmentKeyWasDown) return;
+ if (equipmentWheelCache == null) {
+ for (InteractionHand hand : InteractionHand.values()) {
+ ItemStack stack = client.player.getItemInHand(hand);
+ if (EquipmentArmorItem.abilityComponent(stack) == null) continue;
+ equipmentWheelCache = getEquipmentWheel(hand, stack);
+ break;
+ }
+ }
+ if (equipmentWheelCache == null) return;
+ CONTROLLER.onHoldKeyPressed(equipmentWheelCache);
+ equipmentKeyWasDown = true;
+ }
+
private static void openBalanceWheel(long gameTime) {
if (
WheelLifecycleEventListener.balanceKeyTime > 0
@@ -524,6 +550,21 @@ private static WheelMenuModel getMultitoolWheel(InteractionHand hand, ItemStack
.build();
}
+ private static WheelMenuModel getEquipmentWheel(InteractionHand hand, ItemStack holding) {
+ EquipmentSlot slot = ((EquipmentArmorItem) holding.getItem()).getEquipmentSlot();
+ String ability = slot == EquipmentSlot.HEAD ? "night_vision" : "charged_jump";
+ return WheelMenuBuilder.create()
+ .selectionEffect(WheelSelectionEffect.ANNULAR_SECTOR)
+ .slotsPerPage(2)
+ .action("enable", Component.translatable("screen.anvilcraft.equipment." + ability + ".on"),
+ (graphics, pose, width, height) -> renderWheelItem(graphics, holding),
+ ctx -> PacketDistributor.sendToServer(new SwitchEquipmentAbilityPacket(hand, slot, true)))
+ .action("disable", Component.translatable("screen.anvilcraft.equipment." + ability + ".off"),
+ (graphics, pose, width, height) -> renderWheelItem(graphics, new ItemStack(Items.BARRIER)),
+ ctx -> PacketDistributor.sendToServer(new SwitchEquipmentAbilityPacket(hand, slot, false)))
+ .build();
+ }
+
private static WheelMenuModel getDragonRodWheel(InteractionHand hand, ItemStack holding) {
return WheelMenuBuilder.create()
.selectionEffect(WheelSelectionEffect.ANNULAR_SECTOR)
@@ -633,6 +674,7 @@ public static void onKeyInput(InputEvent.Key event) {
WheelLifecycleEventListener.processMultitoolPress(client, event.getAction());
WheelLifecycleEventListener.processDragonRodPress(client, event.getAction());
WheelLifecycleEventListener.processBalancePress(client, event.getAction());
+ WheelLifecycleEventListener.processEquipmentPress(client, event.getAction());
}
}
@@ -649,6 +691,7 @@ public static void onKeyInput(InputEvent.MouseButton.Post event) {
WheelLifecycleEventListener.processMultitoolPress(client, event.getAction());
WheelLifecycleEventListener.processDragonRodPress(client, event.getAction());
WheelLifecycleEventListener.processBalancePress(client, event.getAction());
+ WheelLifecycleEventListener.processEquipmentPress(client, event.getAction());
}
}
@@ -790,6 +833,17 @@ private static void processDragonRodPress(Minecraft client, int action) {
}
}
+ private static void processEquipmentPress(Minecraft client, int action) {
+ if (action == GLFW.GLFW_RELEASE) {
+ if (equipmentKeyWasDown) CONTROLLER.onHoldKeyReleased();
+ equipmentKeyWasDown = false;
+ equipmentKeyTime = -1L;
+ equipmentWheelCache = null;
+ } else if (action == GLFW.GLFW_PRESS && client.level != null && client.screen == null && !equipmentKeyWasDown) {
+ equipmentKeyTime = client.level.getGameTime();
+ }
+ }
+
private static void processBalancePress(Minecraft client, int action) {
if (client.level == null) return;
if (action == GLFW.GLFW_RELEASE) {
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
index e9b416c303..2384bad5de 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java
@@ -254,6 +254,10 @@ public static void init(RegistrumLangProvider provider) {
provider.add("screen.anvilcraft.resonator.hoe", "Hoe");
provider.add("screen.anvilcraft.resonator.pickaxe", "Pickaxe");
+ provider.add("screen.anvilcraft.equipment.night_vision.on", "Enable Night Vision");
+ provider.add("screen.anvilcraft.equipment.night_vision.off", "Disable Night Vision");
+ provider.add("screen.anvilcraft.equipment.charged_jump.on", "Enable Charged Jump");
+ provider.add("screen.anvilcraft.equipment.charged_jump.off", "Disable Charged Jump");
provider.add("screen.anvilcraft.dragon_rod.protect_containers", "Protect Containers");
provider.add("screen.anvilcraft.dragon_rod.devour_containers", "Devour Containers");
diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ToolPropertyLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ToolPropertyLang.java
index bafc2cc4b0..3132800472 100644
--- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ToolPropertyLang.java
+++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ToolPropertyLang.java
@@ -15,6 +15,8 @@ public static void init(RegistrumLangProvider provider) {
provider.add("tooltip.anvilcraft.property.multiphase.suffix.1", "-β");
provider.add("tooltip.anvilcraft.property.multiphase.suffix.2", "-γ");
provider.add("tooltip.anvilcraft.property.multiphase.suffix.3", "-δ");
+ provider.add("tooltip.anvilcraft.property.night_vision", "Night Vision [Hold %1$s]: %2$s");
+ provider.add("tooltip.anvilcraft.property.charged_jump", "Charged Jump [Hold %1$s]: %2$s");
provider.add("tooltip.anvilcraft.property.protect_containers", "Container Protection [ALT]: %1$s");
provider.add("tooltip.anvilcraft.property.protect_containers.on", "On");
provider.add("tooltip.anvilcraft.property.protect_containers.off", "Off");
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java
index d03d18051e..f9cdbeba0c 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModComponents.java
@@ -121,6 +121,16 @@ public class ModComponents {
b -> b.persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL)
);
+ public static final DataComponentType NIGHT_VISION_ENABLED = register(
+ "night_vision_enabled",
+ b -> b.persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL)
+ );
+
+ public static final DataComponentType CHARGED_JUMP_ENABLED = register(
+ "charged_jump_enabled",
+ b -> b.persistent(Codec.BOOL).networkSynchronized(ByteBufCodecs.BOOL)
+ );
+
public static final DataComponentType BOX_CONTENTS = register(
"box_contents",
b -> b.persistent(BoxContents.CODEC).networkSynchronized(BoxContents.STREAM_CODEC)
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
index acc0548190..17c4598dce 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/item/ModItems.java
@@ -550,12 +550,14 @@ public class ModItems {
public static final ItemEntry BUFFER_BOOTS = REGISTRUM
.item("buffer_boots", p -> new EquipmentArmorItem(p, ArmorItem.Type.BOOTS, false, "spacesuit"))
+ .properties(p -> p.component(ModComponents.CHARGED_JUMP_ENABLED, true))
.tag(ItemTags.FOOT_ARMOR, ItemTags.FOOT_ARMOR_ENCHANTABLE, ItemTags.DURABILITY_ENCHANTABLE)
.recipe(RegistrumItemRecipeLoader::bufferArmor)
.register();
public static final ItemEntry WEATHERPROOF_SPACESUIT_HELMET = REGISTRUM
.item("weatherproof_spacesuit_helmet", p -> new EquipmentArmorItem(p, ArmorItem.Type.HELMET, true, "weatherproof_spacesuit"))
+ .properties(p -> p.component(ModComponents.NIGHT_VISION_ENABLED, true))
.properties(Item.Properties::fireResistant)
.tag(ItemTags.HEAD_ARMOR, ItemTags.HEAD_ARMOR_ENCHANTABLE, ItemTags.DURABILITY_ENCHANTABLE)
.register();
@@ -582,6 +584,7 @@ public class ModItems {
public static final ItemEntry WEATHERPROOF_SPACESUIT_BOOTS = REGISTRUM
.item("weatherproof_spacesuit_boots", p -> new EquipmentArmorItem(p, ArmorItem.Type.BOOTS, true, "weatherproof_spacesuit"))
+ .properties(p -> p.component(ModComponents.CHARGED_JUMP_ENABLED, true))
.properties(Item.Properties::fireResistant)
.tag(ItemTags.FOOT_ARMOR, ItemTags.FOOT_ARMOR_ENCHANTABLE, ItemTags.DURABILITY_ENCHANTABLE)
.register();
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
index 0c6bf60459..cdf697a31c 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentAbilities.java
@@ -1,6 +1,7 @@
package dev.dubhe.anvilcraft.item;
import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.init.item.ModItems;
import dev.dubhe.anvilcraft.inventory.PocketInventory;
import dev.dubhe.anvilcraft.util.AtmosphereManager;
@@ -27,6 +28,7 @@
import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.event.entity.living.LivingBreatheEvent;
import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent;
+import net.neoforged.neoforge.event.entity.living.MobEffectEvent;
import net.neoforged.neoforge.event.tick.PlayerTickEvent;
import java.util.Map;
@@ -47,6 +49,7 @@ public final class EquipmentAbilities {
private static final Map CHARGE_RELEASE = new WeakHashMap<>();
/** 停止蓄力那一刻锁定的蓄力值,衰减以此为基础线性下降。 */
private static final Map CHARGE_RELEASE_START = new WeakHashMap<>();
+ private static final Map HELMET_NIGHT_VISION = new WeakHashMap<>();
private static final Map SUBMERGING = new WeakHashMap<>();
private EquipmentAbilities() {
@@ -62,6 +65,15 @@ public static boolean hasBufferBoots(LivingEntity entity) {
return boots.is(ModItems.BUFFER_BOOTS) || boots.is(ModItems.WEATHERPROOF_SPACESUIT_BOOTS);
}
+ public static boolean hasNightVision(LivingEntity entity) {
+ ItemStack helmet = entity.getItemBySlot(EquipmentSlot.HEAD);
+ return helmet.is(ModItems.WEATHERPROOF_SPACESUIT_HELMET) && helmet.getOrDefault(ModComponents.NIGHT_VISION_ENABLED, true);
+ }
+
+ public static boolean canChargeJump(LivingEntity entity) {
+ return hasBufferBoots(entity) && entity.getItemBySlot(EquipmentSlot.FEET).getOrDefault(ModComponents.CHARGED_JUMP_ENABLED, true);
+ }
+
public static boolean hasFullSuit(LivingEntity entity) {
return entity.getItemBySlot(EquipmentSlot.HEAD).is(ModItems.WEATHERPROOF_SPACESUIT_HELMET)
&& entity.getItemBySlot(EquipmentSlot.CHEST).is(ModItems.WEATHERPROOF_SPACESUIT_CHESTPLATE)
@@ -99,7 +111,7 @@ public static void damage(LivingIncomingDamageEvent event) {
* 因此构成一段输入缓冲窗口。 |
*/
public static int chargeTicks(Player player) {
- return CHARGE.getOrDefault(player, 0);
+ return canChargeJump(player) ? CHARGE.getOrDefault(player, 0) : 0;
}
/**
@@ -131,7 +143,7 @@ public static boolean isChargeHeld(Player player) {
* @return 强化后的跳跃速度;无蓄力时原样返回 {@code normal}
*/
public static float consumeChargedJump(Player player, float normal) {
- float progress = hasBufferBoots(player) ? chargeProgress(player) : 0;
+ float progress = canChargeJump(player) ? chargeProgress(player) : 0;
clearCharge(player);
if (progress <= 0) return normal;
double gravity = Math.max(0.001, player.getAttributeValue(Attributes.GRAVITY));
@@ -243,8 +255,7 @@ private static boolean hasChargeSupport(Player player) {
@SubscribeEvent
public static void beforeTick(PlayerTickEvent.Pre event) {
Player player = event.getEntity();
- boolean boots = hasBufferBoots(player);
- tickCharge(player, boots);
+ tickCharge(player, canChargeJump(player));
if (player.getItemBySlot(EquipmentSlot.FEET).is(ModItems.WEATHERPROOF_SPACESUIT_BOOTS)) {
if (player.isShiftKeyDown()) SUBMERGING.put(player, true);
else if (!player.isInFluidType() && player.level().getFluidState(player.blockPosition().below()).isEmpty()) {
@@ -267,16 +278,40 @@ else if (!player.isInFluidType() && player.level().getFluidState(player.blockPos
}
}
+ @SubscribeEvent
+ public static void preserveExternalNightVision(MobEffectEvent.Added event) {
+ if (event.getEntity() instanceof Player player && event.getEffectInstance().is(MobEffects.NIGHT_VISION)
+ && event.getEffectInstance() != HELMET_NIGHT_VISION.get(player)) {
+ HELMET_NIGHT_VISION.remove(player);
+ }
+ }
+
+ private static void updateNightVision(ServerPlayer player) {
+ MobEffectInstance current = player.getEffect(MobEffects.NIGHT_VISION);
+ MobEffectInstance provided = HELMET_NIGHT_VISION.get(player);
+ if (!hasNightVision(player)) {
+ if (current != null && current == provided) player.removeEffect(MobEffects.NIGHT_VISION);
+ HELMET_NIGHT_VISION.remove(player);
+ return;
+ }
+ if (current != null && current != provided) {
+ HELMET_NIGHT_VISION.remove(player);
+ return;
+ }
+ if (current != null && !current.endsWithin(200)) return;
+ MobEffectInstance refreshed = new MobEffectInstance(MobEffects.NIGHT_VISION, 200, 0, false, false, true);
+ HELMET_NIGHT_VISION.put(player, refreshed);
+ player.addEffect(refreshed);
+ current = player.getEffect(MobEffects.NIGHT_VISION);
+ if (current == null) HELMET_NIGHT_VISION.remove(player);
+ else HELMET_NIGHT_VISION.put(player, current);
+ }
+
@SubscribeEvent
public static void afterTick(PlayerTickEvent.Post event) {
if (!(event.getEntity() instanceof ServerPlayer player)) return;
PocketInventory.get(player).tick(player);
- if (player.getItemBySlot(EquipmentSlot.HEAD).is(ModItems.WEATHERPROOF_SPACESUIT_HELMET)) {
- MobEffectInstance nightVision = player.getEffect(MobEffects.NIGHT_VISION);
- if (nightVision == null || nightVision.endsWithin(200)) {
- player.addEffect(new MobEffectInstance(MobEffects.NIGHT_VISION, 200, 0, false, false, true));
- }
- }
+ updateNightVision(player);
AttributeInstance resistance = player.getAttribute(Attributes.KNOCKBACK_RESISTANCE);
if (resistance == null) return;
boolean flying = !IonocraftBackpackItem.getByPlayer(player).isEmpty() && player.getAbilities().flying;
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
index 62d8b84fd5..409e7e3dd4 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/EquipmentArmorItem.java
@@ -1,8 +1,10 @@
package dev.dubhe.anvilcraft.item;
import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.init.item.ModComponents;
import dev.dubhe.anvilcraft.init.item.ModItems;
import lombok.Getter;
+import net.minecraft.core.component.DataComponentType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
@@ -16,6 +18,8 @@
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ItemAttributeModifiers;
+import javax.annotation.Nullable;
+
public class EquipmentArmorItem extends ArmorItem {
@Getter
private final boolean weatherproof;
@@ -28,6 +32,14 @@ public EquipmentArmorItem(Properties properties, Type type, boolean weatherproof
this.texture = AnvilCraft.of("textures/entity/equipment/" + texture + ".png");
}
+ public static @Nullable DataComponentType abilityComponent(ItemStack stack) {
+ if (stack.is(ModItems.WEATHERPROOF_SPACESUIT_HELMET)) return ModComponents.NIGHT_VISION_ENABLED;
+ if (stack.is(ModItems.BUFFER_BOOTS) || stack.is(ModItems.WEATHERPROOF_SPACESUIT_BOOTS)) {
+ return ModComponents.CHARGED_JUMP_ENABLED;
+ }
+ return null;
+ }
+
@Override
public int getDefense() {
return super.getDefense() + (this.weatherproof ? (this.type == Type.HELMET || this.type == Type.BOOTS ? 2 : 3) : 0);
diff --git a/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java b/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
index 3727fc5cc4..3096ccb9cd 100644
--- a/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
+++ b/src/main/java/dev/dubhe/anvilcraft/item/IonocraftBackpackItem.java
@@ -6,6 +6,7 @@
import dev.dubhe.anvilcraft.api.power.PowerGrid;
import dev.dubhe.anvilcraft.init.ModDataAttachments;
import dev.dubhe.anvilcraft.network.IonocraftBackpackFlyingPacket;
+import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
@@ -20,6 +21,7 @@
import net.neoforged.neoforge.common.NeoForgeMod;
import net.neoforged.neoforge.network.PacketDistributor;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -157,6 +159,8 @@ public static void refreshFlight(ServerPlayer player) {
if (player.getAbilities().mayfly != mayFly || player.getAbilities().flying != flying) {
player.getAbilities().mayfly = mayFly;
player.getAbilities().flying = flying;
+ // 先同步飞行属性,避免客户端在关闭飞行后仍凭旧属性重新起飞。
+ player.connection.send(new ClientboundUpdateAttributesPacket(player.getId(), List.of(flight)));
player.onUpdateAbilities();
}
}
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
index b67e9ad805..079b4177f4 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/GameRendererMixin.java
@@ -4,12 +4,11 @@
import dev.dubhe.anvilcraft.client.init.ModRenderTargets;
import dev.dubhe.anvilcraft.client.init.ModShaders;
import dev.dubhe.anvilcraft.client.renderer.mun.MunSurfaceRenderer;
-import dev.dubhe.anvilcraft.init.item.ModItems;
+import dev.dubhe.anvilcraft.item.EquipmentAbilities;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceProvider;
-import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@@ -24,7 +23,7 @@ abstract class GameRendererMixin {
@ModifyReturnValue(method = "getNightVisionScale", at = @At("RETURN"))
private static float anvilcraft$steadyHelmetNightVision(float original, LivingEntity entity, float partialTick) {
- return entity.getItemBySlot(EquipmentSlot.HEAD).is(ModItems.WEATHERPROOF_SPACESUIT_HELMET) ? 1 : original;
+ return EquipmentAbilities.hasNightVision(entity) ? 1 : original;
}
@ModifyReturnValue(method = "getRendertypeSolidShader", at = @At("RETURN"))
diff --git a/src/main/java/dev/dubhe/anvilcraft/network/SwitchEquipmentAbilityPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/SwitchEquipmentAbilityPacket.java
new file mode 100644
index 0000000000..c47e2a8daf
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/network/SwitchEquipmentAbilityPacket.java
@@ -0,0 +1,36 @@
+package dev.dubhe.anvilcraft.network;
+
+import dev.anvilcraft.lib.v2.codec.StreamCodecUtil;
+import dev.anvilcraft.lib.v2.network.packet.IServerboundPacket;
+import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.item.EquipmentArmorItem;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.ByteBufCodecs;
+import net.minecraft.network.codec.StreamCodec;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.ItemStack;
+
+public record SwitchEquipmentAbilityPacket(InteractionHand hand, EquipmentSlot slot, boolean enabled) implements IServerboundPacket {
+ public static final Type TYPE = new Type<>(AnvilCraft.of("switch_equipment_ability"));
+ public static final StreamCodec STREAM_CODEC = StreamCodec.composite(
+ StreamCodecUtil.enumStreamCodec(InteractionHand.class), SwitchEquipmentAbilityPacket::hand,
+ StreamCodecUtil.enumStreamCodec(EquipmentSlot.class), SwitchEquipmentAbilityPacket::slot,
+ ByteBufCodecs.BOOL, SwitchEquipmentAbilityPacket::enabled,
+ SwitchEquipmentAbilityPacket::new
+ );
+
+ @Override
+ public Type type() {
+ return TYPE;
+ }
+
+ @Override
+ public void handleOnServer(Player player) {
+ ItemStack stack = player.getItemInHand(this.hand);
+ if (!(stack.getItem() instanceof EquipmentArmorItem armor) || armor.getEquipmentSlot() != this.slot) return;
+ var component = EquipmentArmorItem.abilityComponent(stack);
+ if (component != null) stack.set(component, this.enabled);
+ }
+}
From 6b1b6364d24e5dfd29d23381b3aa7f1756d6809d Mon Sep 17 00:00:00 2001
From: Fff
Date: Sat, 19 Sep 2026 23:11:06 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A5=9E=E7=A7=98?=
=?UTF-8?q?=E6=95=88=E6=9E=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../entity/ProcessingTableBlockEntity.java | 2 +-
.../block/item/SmartBlockPlacerBlockItem.java | 63 +++++++++
.../anvilcraft/client/AnvilCraftClient.java | 2 +
.../blockentity/SmartBlockPlacerRenderer.java | 61 ++++++++-
.../item/SmartBlockPlacerItemRenderer.java | 126 ++++++++++++++++++
.../WrappingItemCustomRenderedModels.java | 1 +
.../anvilcraft/init/block/ModBlocks.java | 6 +-
.../mixin/ItemInHandRendererMixin.java | 17 +++
.../models/block/burning_heater.json | 2 +-
9 files changed, 275 insertions(+), 5 deletions(-)
create mode 100644 src/main/java/dev/dubhe/anvilcraft/block/item/SmartBlockPlacerBlockItem.java
create mode 100644 src/main/java/dev/dubhe/anvilcraft/client/renderer/item/SmartBlockPlacerItemRenderer.java
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/ProcessingTableBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/ProcessingTableBlockEntity.java
index 981845a983..87d8c316f4 100644
--- a/src/main/java/dev/dubhe/anvilcraft/block/entity/ProcessingTableBlockEntity.java
+++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/ProcessingTableBlockEntity.java
@@ -108,7 +108,7 @@ public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
- return ProcessingTableBlockEntity.this.input.extractItem(slot, amount, simulate);
+ return ItemStack.EMPTY;
}
};
diff --git a/src/main/java/dev/dubhe/anvilcraft/block/item/SmartBlockPlacerBlockItem.java b/src/main/java/dev/dubhe/anvilcraft/block/item/SmartBlockPlacerBlockItem.java
new file mode 100644
index 0000000000..09af7945cf
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/block/item/SmartBlockPlacerBlockItem.java
@@ -0,0 +1,63 @@
+package dev.dubhe.anvilcraft.block.item;
+
+import dev.dubhe.anvilcraft.AnvilCraft;
+import net.minecraft.core.Holder;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.stats.Stats;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.InteractionResultHolder;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.EquipmentSlotGroup;
+import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+import net.minecraft.world.entity.ai.attributes.Attributes;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.item.ArmorItem;
+import net.minecraft.world.item.ArmorMaterials;
+import net.minecraft.world.item.BlockItem;
+import net.minecraft.world.item.Equipable;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.component.ItemAttributeModifiers;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Block;
+
+public class SmartBlockPlacerBlockItem extends BlockItem implements Equipable {
+ private final ItemAttributeModifiers armorModifiers;
+
+ public SmartBlockPlacerBlockItem(Block block, Properties properties) {
+ super(block, properties);
+ this.armorModifiers = ItemAttributeModifiers.builder()
+ .add(Attributes.ARMOR, new AttributeModifier(
+ AnvilCraft.of("smart_block_placer_helmet"),
+ ArmorMaterials.IRON.value().getDefense(ArmorItem.Type.HELMET),
+ AttributeModifier.Operation.ADD_VALUE
+ ), EquipmentSlotGroup.HEAD)
+ .build();
+ }
+
+ @Override
+ public EquipmentSlot getEquipmentSlot() {
+ return EquipmentSlot.HEAD;
+ }
+
+ @Override
+ public Holder getEquipSound() {
+ return ArmorMaterials.IRON.value().equipSound();
+ }
+
+ @Override
+ public ItemAttributeModifiers getDefaultAttributeModifiers(ItemStack stack) {
+ return this.armorModifiers;
+ }
+
+ @Override
+ public InteractionResultHolder use(Level level, Player player, InteractionHand hand) {
+ ItemStack stack = player.getItemInHand(hand);
+ if (!player.canUseSlot(EquipmentSlot.HEAD) || !player.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+ return InteractionResultHolder.fail(stack);
+ }
+ player.setItemSlot(EquipmentSlot.HEAD, stack.copyWithCount(1));
+ stack.consume(1, player);
+ if (!level.isClientSide()) player.awardStat(Stats.ITEM_USED.get(this));
+ return InteractionResultHolder.sidedSuccess(stack, level.isClientSide());
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/AnvilCraftClient.java b/src/main/java/dev/dubhe/anvilcraft/client/AnvilCraftClient.java
index 7a10c16a8b..917f27d7d2 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/AnvilCraftClient.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/AnvilCraftClient.java
@@ -17,6 +17,7 @@
import dev.dubhe.anvilcraft.client.renderer.item.CelestialForgingAnvilItemRenderer;
import dev.dubhe.anvilcraft.client.renderer.item.ItemSlotClipping;
import dev.dubhe.anvilcraft.client.renderer.item.RuinsBlockItemRenderer;
+import dev.dubhe.anvilcraft.client.renderer.item.SmartBlockPlacerItemRenderer;
import dev.dubhe.anvilcraft.client.renderer.item.decoration.TerminalInsertionDecoration;
import dev.dubhe.anvilcraft.client.renderer.item.decoration.WeatherproofChestplateDecoration;
import dev.dubhe.anvilcraft.client.selection.ModelBlockSelection;
@@ -118,6 +119,7 @@ public static void registerClientExtensions(RegisterClientExtensionsEvent e) {
ModFluids.onRegisterFluidType(e);
e.registerItem(new RuinsBlockItemRenderer.ItemExtensions(), ModBlocks.RUINS_BLOCK.asItem());
e.registerItem(new CelestialForgingAnvilItemRenderer.ItemExtensions(), ModBlocks.CELESTIAL_FORGING_ANVIL.asItem());
+ e.registerItem(new SmartBlockPlacerItemRenderer.ItemExtensions(), ModBlocks.SMART_BLOCK_PLACER.asItem());
ItemExtensionImpl itemExtensionInstance = new ItemExtensionImpl();
e.registerItem(itemExtensionInstance, ModItems.IONOCRAFT_BACKPACK, ModItems.BREATHING_HELMET,
ModItems.POCKETS_LEGGINGS, ModItems.BUFFER_BOOTS, ModItems.WEATHERPROOF_SPACESUIT_HELMET,
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/SmartBlockPlacerRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/SmartBlockPlacerRenderer.java
index 82619589a4..f5b81f672e 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/SmartBlockPlacerRenderer.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/blockentity/SmartBlockPlacerRenderer.java
@@ -17,6 +17,7 @@
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
+import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
@@ -27,6 +28,7 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundSource;
+import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemDisplayContext;
@@ -47,6 +49,7 @@
import net.neoforged.neoforge.client.RenderTypeHelper;
import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions;
import net.neoforged.neoforge.client.model.data.ModelData;
+import org.joml.Vector3f;
import java.util.List;
import javax.annotation.Nullable;
@@ -77,6 +80,7 @@ public class SmartBlockPlacerRenderer implements BlockEntityRenderer this.renderModel(modelPose, buffer, model, packedLight, packedOverlay),
+ () -> {}
+ );
+ pose.popPose();
+ }
+
private void renderArm(
SmartBlockPlacerBlockEntity entity,
@Nullable Level level,
@@ -354,7 +377,7 @@ private void renderArm(
poseStack,
upsideDown,
state,
- (model, pose) -> this.renderModel(pose, buffer, model, packedLight, packedOverlay),
+ (model, pose) -> this.renderWorldModel(pose, buffer, model, packedLight, packedOverlay, level),
() -> this.renderHeldContent(poseStack, buffer, entity.getCurrentHeldBlock(), level, partialTick, packedLight, packedOverlay)
);
}
@@ -531,6 +554,42 @@ private BlockPos getNextTargetPosition(
return null;
}
+ private void renderWorldModel(
+ PoseStack poseStack, MultiBufferSource buffer, ModelResourceLocation model,
+ int packedLight, int packedOverlay, @Nullable Level level
+ ) {
+ if (level == null) {
+ this.renderModel(poseStack, buffer, model, packedLight, packedOverlay);
+ return;
+ }
+ VertexConsumer vertices = buffer.getBuffer(RenderType.cutout());
+ BakedModel bakedModel = this.modelManager.getModel(model);
+ for (Direction cull : Direction.values()) {
+ this.random.setSeed(42L);
+ this.renderWorldQuads(
+ poseStack.last(), vertices, bakedModel.getQuads(null, cull, this.random), packedLight, packedOverlay, level
+ );
+ }
+ this.random.setSeed(42L);
+ this.renderWorldQuads(
+ poseStack.last(), vertices, bakedModel.getQuads(null, null, this.random), packedLight, packedOverlay, level
+ );
+ }
+
+ private void renderWorldQuads(
+ PoseStack.Pose pose, VertexConsumer consumer, List quads,
+ int packedLight, int packedOverlay, Level level
+ ) {
+ Vector3f normal = new Vector3f();
+ for (BakedQuad quad : quads) {
+ Direction local = quad.getDirection();
+ normal.set(local.getStepX(), local.getStepY(), local.getStepZ()).mul(pose.normal());
+ Direction worldDirection = Direction.getNearest(normal.x(), normal.y(), normal.z());
+ float shade = level.getShade(worldDirection, quad.isShade());
+ consumer.putBulkData(pose, quad, shade, shade, shade, 1.0f, packedLight, packedOverlay);
+ }
+ }
+
@SuppressWarnings("deprecation")
private void renderModel(
PoseStack poseStack, MultiBufferSource buffer, ModelResourceLocation model, int packedLight, int packedOverlay) {
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/SmartBlockPlacerItemRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/SmartBlockPlacerItemRenderer.java
new file mode 100644
index 0000000000..3db3ebd250
--- /dev/null
+++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/SmartBlockPlacerItemRenderer.java
@@ -0,0 +1,126 @@
+package dev.dubhe.anvilcraft.client.renderer.item;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+import dev.dubhe.anvilcraft.AnvilCraft;
+import dev.dubhe.anvilcraft.block.SmartBlockPlacerBlock;
+import dev.dubhe.anvilcraft.block.entity.SmartBlockPlacerBlockEntity;
+import dev.dubhe.anvilcraft.client.renderer.blockentity.SmartBlockPlacerRenderer;
+import dev.dubhe.anvilcraft.init.ModDataAttachments;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.BlockEntityWithoutLevelRenderer;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.entity.ItemRenderer;
+import net.minecraft.client.resources.model.BakedModel;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.entity.EquipmentSlot;
+import net.minecraft.world.entity.LivingEntity;
+import net.minecraft.world.item.ItemDisplayContext;
+import net.minecraft.world.item.ItemStack;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+import net.neoforged.neoforge.client.event.ClientTickEvent;
+import net.neoforged.neoforge.client.extensions.common.IClientItemExtensions;
+import net.neoforged.neoforge.client.model.data.ModelData;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import javax.annotation.Nullable;
+
+@EventBusSubscriber(modid = AnvilCraft.MOD_ID, value = Dist.CLIENT)
+public class SmartBlockPlacerItemRenderer extends BlockEntityWithoutLevelRenderer {
+ private static final Map ANIMATIONS = new WeakHashMap<>();
+ private static @Nullable LivingEntity currentWearer;
+
+ private final SmartBlockPlacerBlockEntity preview = new SmartBlockPlacerBlockEntity(
+ BlockPos.ZERO, ModBlocks.SMART_BLOCK_PLACER.getDefaultState()
+ );
+
+ private SmartBlockPlacerItemRenderer() {
+ super(Minecraft.getInstance().getBlockEntityRenderDispatcher(), Minecraft.getInstance().getEntityModels());
+ }
+
+ public static void withWearer(LivingEntity wearer, Runnable render) {
+ LivingEntity previousWearer = currentWearer;
+ currentWearer = wearer;
+ try {
+ render.run();
+ } finally {
+ currentWearer = previousWearer;
+ }
+ }
+
+ @SubscribeEvent
+ public static void onClientTick(ClientTickEvent.Post event) {
+ Minecraft minecraft = Minecraft.getInstance();
+ if (minecraft.level == null) {
+ ANIMATIONS.clear();
+ return;
+ }
+ ANIMATIONS.keySet().removeIf(wearer -> wearer.level() != minecraft.level || wearer.isRemoved()
+ || !wearer.getItemBySlot(EquipmentSlot.HEAD).is(ModBlocks.SMART_BLOCK_PLACER.asItem()));
+ if (minecraft.isPaused()) return;
+ for (var player : minecraft.level.players()) {
+ if (!player.getItemBySlot(EquipmentSlot.HEAD).is(ModBlocks.SMART_BLOCK_PLACER.asItem())) continue;
+ ANIMATIONS.computeIfAbsent(player, wearer -> new DanceAnimation()).tick(hasPower(player));
+ }
+ }
+
+ private static boolean hasPower(LivingEntity wearer) {
+ return wearer.getData(ModDataAttachments.IN_POWER_GRID) && !wearer.getData(ModDataAttachments.POWER_GRID_OVERLOADED);
+ }
+
+ @Override
+ public void renderByItem(
+ ItemStack stack, ItemDisplayContext context, PoseStack pose, MultiBufferSource buffer, int light, int overlay
+ ) {
+ Minecraft minecraft = Minecraft.getInstance();
+ if (context != ItemDisplayContext.HEAD) {
+ ItemRenderer renderer = minecraft.getItemRenderer();
+ BakedModel model = renderer.getItemModelShaper().getItemModel(stack);
+ FilterItemRenderer.renderModel(renderer, stack, pose, buffer, light, overlay, model);
+ return;
+ }
+ LivingEntity wearer = currentWearer;
+ boolean powered = wearer != null && hasPower(wearer);
+ pose.pushPose();
+ pose.translate(0, 0.9, 0);
+ minecraft.getBlockRenderer().renderSingleBlock(
+ this.preview.getBlockState().setValue(SmartBlockPlacerBlock.OVERLOAD, !powered),
+ pose, buffer, light, overlay, ModelData.EMPTY, null
+ );
+ var renderer = minecraft.getBlockEntityRenderDispatcher().getRenderer(this.preview);
+ if (renderer instanceof SmartBlockPlacerRenderer placerRenderer) {
+ DanceAnimation animation = wearer == null ? null : ANIMATIONS.get(wearer);
+ float partialTick = minecraft.isPaused() ? 1 : minecraft.getTimer().getGameTimeDeltaPartialTick(false);
+ double renderTick = animation == null ? 0 : animation.getRenderTime(partialTick, powered);
+ placerRenderer.renderDancingArm(renderTick, pose, buffer, light, overlay);
+ }
+ pose.popPose();
+ }
+
+ private static class DanceAnimation {
+ private long ticks;
+ private boolean playing;
+
+ void tick(boolean powered) {
+ this.playing = powered;
+ if (powered) this.ticks++;
+ }
+
+ double getRenderTime(float partialTick, boolean powered) {
+ return this.ticks - (this.playing && powered ? 1 - partialTick : 0);
+ }
+ }
+
+ public static class ItemExtensions implements IClientItemExtensions {
+ private @Nullable SmartBlockPlacerItemRenderer renderer;
+
+ @Override
+ public BlockEntityWithoutLevelRenderer getCustomRenderer() {
+ if (this.renderer == null) this.renderer = new SmartBlockPlacerItemRenderer();
+ return this.renderer;
+ }
+ }
+}
diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/WrappingItemCustomRenderedModels.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/WrappingItemCustomRenderedModels.java
index 11264e442d..3c8b5edaf8 100644
--- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/WrappingItemCustomRenderedModels.java
+++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/item/WrappingItemCustomRenderedModels.java
@@ -32,6 +32,7 @@ public static void onModelBake(ModelEvent.ModifyBakingResult event) {
swapModels(modelRegistry, ModelResourceLocation.inventory(FLUID_TANK));
swapModels(modelRegistry, ModelResourceLocation.inventory(LARGE_FLUID_TANK));
swapModels(modelRegistry, ModelResourceLocation.inventory(AnvilCraft.of("celestial_forging_anvil")));
+ swapModels(modelRegistry, ModelResourceLocation.inventory(AnvilCraft.of("smart_block_placer")));
swapModels(modelRegistry, ModelResourceLocation.inventory(CREATIVE_FLUID_TANK));
swapModels(modelRegistry, ModelResourceLocation.inventory(CREATIVE_CRATE));
swapModels(modelRegistry, ModelResourceLocation.inventory(STORAGE_PORT));
diff --git a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
index a03f9a57cd..4338a1d7a9 100644
--- a/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
+++ b/src/main/java/dev/dubhe/anvilcraft/init/block/ModBlocks.java
@@ -238,6 +238,7 @@
import dev.dubhe.anvilcraft.block.item.RuinsBlockItem;
import dev.dubhe.anvilcraft.block.item.ShulkerContainerBlockItem;
import dev.dubhe.anvilcraft.block.item.SimpleMultiPartBlockItem;
+import dev.dubhe.anvilcraft.block.item.SmartBlockPlacerBlockItem;
import dev.dubhe.anvilcraft.block.item.StorageFluidPortBlockItem;
import dev.dubhe.anvilcraft.block.item.StoragePortBlockItem;
import dev.dubhe.anvilcraft.block.item.SuperHeavyBlockItem;
@@ -941,7 +942,7 @@ public class ModBlocks {
.lang("Burning Heater")
.initialProperties(ModBlocks.MAGNET_BLOCK)
.properties(properties -> properties.isValidSpawn(Blocks::never)
- .lightLevel(state -> 15))
+ .lightLevel(state -> state.getValue(BurningHeaterBlock.LEVEL) > 0 ? 15 : 0))
.blockstate(DataGenUtil::noExtraModelOrState)
.item()
.build()
@@ -1380,7 +1381,8 @@ public class ModBlocks {
.build();
});
})
- .simpleItem()
+ .item(SmartBlockPlacerBlockItem::new)
+ .build()
.tag(BlockTags.MINEABLE_WITH_PICKAXE)
.recipe(RegistrumBlockRecipeLoader::smartBlockPlacer)
.register();
diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/ItemInHandRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/ItemInHandRendererMixin.java
index 65346caabd..7f03d71665 100644
--- a/src/main/java/dev/dubhe/anvilcraft/mixin/ItemInHandRendererMixin.java
+++ b/src/main/java/dev/dubhe/anvilcraft/mixin/ItemInHandRendererMixin.java
@@ -1,12 +1,15 @@
package dev.dubhe.anvilcraft.mixin;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.blaze3d.vertex.PoseStack;
import dev.dubhe.anvilcraft.client.building.BuildingRodItemRenderer;
import dev.dubhe.anvilcraft.client.event.BigRedButtonInputListener;
import dev.dubhe.anvilcraft.client.renderer.item.ItemInHandRendererManager;
+import dev.dubhe.anvilcraft.client.renderer.item.SmartBlockPlacerItemRenderer;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
import dev.dubhe.anvilcraft.init.item.ModItems;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.AbstractClientPlayer;
@@ -50,6 +53,20 @@ public abstract void renderItem(
@Unique
private ItemInHandRendererManager anvilcraft$manager = null;
+ @WrapMethod(method = "renderItem")
+ private void anvilcraft$renderSmartPlacerHelmet(
+ LivingEntity entity, ItemStack stack, ItemDisplayContext context, boolean leftHand,
+ PoseStack pose, MultiBufferSource buffers, int light, Operation original
+ ) {
+ if (context == ItemDisplayContext.HEAD && stack.is(ModBlocks.SMART_BLOCK_PLACER.asItem())) {
+ SmartBlockPlacerItemRenderer.withWearer(
+ entity, () -> original.call(entity, stack, context, leftHand, pose, buffers, light)
+ );
+ } else {
+ original.call(entity, stack, context, leftHand, pose, buffers, light);
+ }
+ }
+
@ModifyExpressionValue(method = "renderHandsWithItems", at = @At(value = "FIELD",
target = "Lnet/minecraft/client/renderer/ItemInHandRenderer$HandRenderSelection;renderOffHand:Z"))
private boolean anvilcraft$showOffhandRod(boolean original, float partialTick, PoseStack pose,
diff --git a/src/main/resources/assets/anvilcraft/models/block/burning_heater.json b/src/main/resources/assets/anvilcraft/models/block/burning_heater.json
index 39c81064a4..bf5f593f97 100644
--- a/src/main/resources/assets/anvilcraft/models/block/burning_heater.json
+++ b/src/main/resources/assets/anvilcraft/models/block/burning_heater.json
@@ -1,7 +1,7 @@
{
"credit": "Made by XeKr with Blockbench",
"parent": "minecraft:block/block",
- "ambientocclusion": false,
+ "ambientocclusion": true,
"textures": {
"bottom": "anvilcraft:block/burning_heater_bottom",
"particle": "anvilcraft:block/burning_heater_side",