diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 01995f4..ff1f4dc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,7 +28,7 @@ jobs:
uses: christian-draeger/read-properties@1.1.1
with:
path: gradle.properties
- properties: 'mod_id mod_name java_version upload'
+ properties: 'mod_id mod_name java_version modrinth_id curseforge_id upload'
- name: Setup Java ${{ steps.properties.outputs.java_version }}
uses: actions/setup-java@v3.6.0
@@ -71,4 +71,4 @@ jobs:
uses: Anvil-Dev/dedicated-server-launch-test@1.21.1-neoforge
with:
mod: build/libs/${{ steps.properties.outputs.mod_id }}-neoforge-${{ steps.version.outputs.version }}.jar
- extra-mods: patchouli:1.21-87-neoforge anvilcraft:1.21.1-1.5.0+pre-release.8 guideme:21.1.15
+ extra-mods: anvilcraft:1.21.1-1.5.0+hotfix.1560 guideme:21.1.15
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 236df83..be52db7 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -62,4 +62,4 @@ jobs:
uses: Anvil-Dev/dedicated-server-launch-test@1.21.1-neoforge
with:
mod: build/libs/${{ steps.properties.outputs.mod_id }}-neoforge-${{ steps.version.outputs.version }}.jar
- extra-mods: patchouli:1.21-87-neoforge anvilcraft:1.21.1-1.5.0+pre-release.8 guideme:21.1.15
+ extra-mods: anvilcraft:1.21.1-1.5.0+hotfix.1560 guideme:21.1.15
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8e86a76
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# AnvilCraft-GuideMe
+
+[](https://github.com/Anvil-Dev/AnvilCraft-GuideME/actions/workflows/ci.yml)
+
+## [AnvilCraft](https://github.com/Anvil-Dev/AnvilCraft)附属 by [Anvil-Dev](https://github.com/Anvil-Dev)
+
+> 欢迎来到模组AnvilCraft-GuideMe的页面!模组是为铁砧工艺本体提供指南的附属,添加了本体内的游戏指南和额外的功能,主要功能有:
+
+- ModInfo: 提供modid以查看当前当前模组加载情况,可以添加url跳转至外部链接
+- NeoColor: 可以提供十六进制颜色值以灵活自定义文本颜色
+- ItemEntity: 方便在场景中显示物品实体(由于技术原因,物品实体不会转)
+- Key: 提供某个功能的id以显示这个功能绑定的按键
+- WIP: 咕咕咕
+
+部分文本抄自[AnvilCraft-Patchouli](https://github.com/Anvil-Dev/AnvilCraft-Patchouli)和[Xekr's TODO](https://github.com/Anvil-Dev/AnvilCraft/issues?q=is%3Aissue%20state%3Aopen%20author%3AXeKr%20label%3A%22%F0%9F%93%8B%EF%B8%8F%20TODO%22)
+
+## 使用许可
+
+[LICENSE 文件(LGPL-3.0)](./LICENSE)
+
+## 使用方法
+
+下载对应版本[GuideME](https://github.com/AppliedEnergistics/GuideME)、[AnvilCraft](https://github.com/Anvil-Dev/AnvilCraft)和此 `mod` 放入 `mods` 文件夹,启动游戏即可
+
+## 主要维护者
+
+[@theabab2333](https://github.com/theabab23333)
+[@Gugle](https://github.com/Gu-ZT)
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5b6b97e..3fd695b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,12 +2,11 @@
minecraft = "1.21.1"
neoForge = "21.1.152"
registrate = "MC1.21-1.3.0+62"
-anvillib = "1.4.0+build.160"
-anvilcraft = "1.5.0+pre-release.24"
+anvillib = "1.4.0+build.172"
+anvilcraft = "1.5.0+hotfix.1562"
curios = "9.0.15+1.21.1"
jei = "19.21.0.247"
jade = "15.3.4+neoforge"
-patchouli = "1.21-87-NEOFORGE"
modDevGradle = "2.0.78"
lombok = "8.7.1"
guideme = "21.1.9"
@@ -22,7 +21,6 @@ jeiCommonApi = { group = "mezz.jei", name = "jei-1.21.1-common-api", version.ref
jeiForgeApi = { group = "mezz.jei", name = "jei-1.21.1-neoforge-api", version.ref = "jei" }
jeiForgeImpl = { group = "mezz.jei", name = "jei-1.21.1-neoforge", version.ref = "jei" }
jade = { group = "maven.modrinth", name = "jade", version.ref = "jade" }
-patchouli = { group = "vazkii.patchouli", name = "Patchouli", version.ref = "patchouli" }
guideme = { group = "org.appliedenergistics", name = "guideme", version.ref = "guideme" }
ae2 = { group = "org.appliedenergistics", name = "appliedenergistics2", version.ref = "ae2" }
diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle
index 04f64a1..06eadcb 100644
--- a/gradle/scripts/dependencies.gradle
+++ b/gradle/scripts/dependencies.gradle
@@ -21,9 +21,6 @@ dependencies {
// Jade
implementation(libs.jade)
- // Patchouli
- implementation(libs.patchouli)
-
// GuideME
compileOnly("org.appliedenergistics:guideme:${libs.versions.guideme.get()}:api")
runtimeOnly(libs.guideme)
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a441313..37f853b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/guidebook/ac_assets/sturcture/laser_miner.snbt b/guidebook/ac_assets/sturcture/laser_miner.snbt
new file mode 100644
index 0000000..d14b301
--- /dev/null
+++ b/guidebook/ac_assets/sturcture/laser_miner.snbt
@@ -0,0 +1,30 @@
+{
+ DataVersion: 3955,
+ size: [3, 7, 3],
+ data: [
+ {pos: [0, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 0, 0], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 0, 1], state: "anvilcraft:mineral_fountain", nbt: {id: "anvilcraft:mineral_fountain", tickCount: -1}},
+ {pos: [1, 0, 2], state: "anvilcraft:raw_silver_block"},
+ {pos: [2, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 1, 1], state: "anvilcraft:deepslate_silver_ore"},
+ {pos: [0, 5, 1], state: "anvilcraft:ruby_laser{facing:east,overload:false,switch:on}", nbt: {id: "anvilcraft:ruby_laser"}},
+ {pos: [1, 5, 0], state: "anvilcraft:ruby_laser{facing:south,overload:false,switch:on}", nbt: {id: "anvilcraft:ruby_laser"}},
+ {pos: [1, 5, 1], state: "anvilcraft:ruby_prism{facing:down}", nbt: {id: "anvilcraft:ruby_prism"}},
+ {pos: [1, 5, 2], state: "anvilcraft:ruby_laser{facing:north,overload:false,switch:on}", nbt: {id: "anvilcraft:ruby_laser"}},
+ {pos: [2, 5, 1], state: "anvilcraft:ruby_laser{facing:west,overload:false,switch:on}", nbt: {id: "anvilcraft:ruby_laser"}},
+ {pos: [1, 6, 1], state: "minecraft:barrel{facing:east,open:false}", nbt: {Items: [{Slot: 0b, count: 3, id: "anvilcraft:raw_silver"}], id: "minecraft:barrel"}}
+ ],
+ entities: [],
+ palette: [
+ "anvilcraft:raw_silver_block",
+ "anvilcraft:deepslate_silver_ore",
+ "anvilcraft:mineral_fountain",
+ "anvilcraft:ruby_laser{facing:east,overload:false,switch:on}",
+ "anvilcraft:ruby_laser{facing:south,overload:false,switch:on}",
+ "anvilcraft:ruby_prism{facing:down}",
+ "anvilcraft:ruby_laser{facing:north,overload:false,switch:on}",
+ "anvilcraft:ruby_laser{facing:west,overload:false,switch:on}",
+ "minecraft:barrel{facing:east,open:false}"
+ ]
+}
\ No newline at end of file
diff --git a/guidebook/ac_assets/sturcture/mineral_fountain.snbt b/guidebook/ac_assets/sturcture/mineral_fountain.snbt
new file mode 100644
index 0000000..b5a3e6f
--- /dev/null
+++ b/guidebook/ac_assets/sturcture/mineral_fountain.snbt
@@ -0,0 +1,27 @@
+{
+ DataVersion: 3955,
+ size: [7, 2, 3],
+ data: [
+ {pos: [0, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 0, 0], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 0, 1], state: "anvilcraft:mineral_fountain"},
+ {pos: [1, 0, 2], state: "anvilcraft:raw_silver_block"},
+ {pos: [2, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [3, 0, 1], state: "anvilcraft:arrow{facing:east}"},
+ {pos: [4, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [5, 0, 0], state: "anvilcraft:raw_silver_block"},
+ {pos: [5, 0, 1], state: "anvilcraft:mineral_fountain", nbt: {id: "anvilcraft:mineral_fountain", tickCount: -1}},
+ {pos: [5, 0, 2], state: "anvilcraft:raw_silver_block"},
+ {pos: [6, 0, 1], state: "anvilcraft:raw_silver_block"},
+ {pos: [1, 1, 1], state: "minecraft:deepslate{axis:y}"},
+ {pos: [5, 1, 1], state: "anvilcraft:deepslate_silver_ore"}
+ ],
+ entities: [],
+ palette: [
+ "anvilcraft:raw_silver_block",
+ "minecraft:deepslate{axis:y}",
+ "anvilcraft:deepslate_silver_ore",
+ "anvilcraft:arrow{facing:east}",
+ "anvilcraft:mineral_fountain"
+ ]
+}
\ No newline at end of file
diff --git a/guidebook/basic/basic_block_processing.md b/guidebook/basic/basic_block_processing.md
index d08f200..701e909 100644
--- a/guidebook/basic/basic_block_processing.md
+++ b/guidebook/basic/basic_block_processing.md
@@ -94,6 +94,7 @@ navigation:
+
使方块变为掉落物。
diff --git a/guidebook/basic/basic_minerals.md b/guidebook/basic/basic_minerals.md
index 116abe9..faed604 100644
--- a/guidebook/basic/basic_minerals.md
+++ b/guidebook/basic/basic_minerals.md
@@ -9,97 +9,81 @@ item_ids:
---
# 基本矿石
+#### 不同于大多数mod,正常情况下本模组的矿石不会自然生成,只能通过矿物涌泉得到。
-不同于大多数mod,正常情况下本模组的矿石不会自然生成,只能通过矿物涌泉得到。
-为了在前期获得本模组的金属,你需要使用类似于“无中生有”的方式——“[物品过筛](basic_item_processing.mdesh)”。
-以此法获得的金属粒及其来源在后文列出。
+## 物品过筛
-
+### 为了在前期获得本模组的金属,你需要使用类似于“无中生有”的方式—— **物品过筛**。
-*铅粒*通过筛选*火山灰*概率获得。
+##### **铅粒**、**锡粒**、**锌粒**和**银粒**都可通过过筛**火山灰**概率获得。
-
+
+
+
+
+
+
+
-*锡粒*通过筛选*火山灰*概率获得。
+##### **钨粒** 通过筛选 **下界尘** 概率获得。
-
+
-*锌粒*通过筛选*火山灰*概率获得。
+##### **钛粒** 通过筛选**末地尘** 概率获得。
+
-
-
-*银粒*通过筛选*火山灰*概率获得。
-
-
-
-*钨粒*通过筛选*下界尘*概率获得。
-
-
-
-*钛粒*通过筛选*末地尘*概率获得。
+---
-在获得一定量的金属后,你可以通过[时移](../smithing_corrupted_beacon.md#time_warp)将金属块转化为粗矿,并用粗矿块配合*矿脉涌泉*再生矿物。这要求你现阶段有能力制造*腐化信标*和*冲击桩*
+## 矿物涌泉
+在获得一定量的金属后,你可以通过 **时移** 将金属块转化为粗矿,
+并用粗矿块配合 **矿脉涌泉** 再生矿物。
+这要求你现阶段有能力制造 **腐化信标** 和 **冲击桩**
-## 矿物涌泉产矿
+### 矿物涌泉产矿
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-这种结构下的*矿物涌泉*可以生成矿石
+*这种结构下的 **矿物涌泉** 可以生成矿石*
-*矿物涌泉*每秒检测四个面相邻方块,如果它们都是某一种粗矿块,则将*深板岩*转化为对应的深层矿。
-在主世界生成时,有1%概率生成*地核碎片矿石*,有1%概率生成*虚空石*。
-在下界生成时,有20%概率生成*地核碎片矿石*。
-在末地生成时,有20%概率生成*虚空石*。
-注意:宝石不能以这种方式再生,仅接受粗矿块。
-注意:矿物涌泉仅在y=-54及以下位置工作
+**矿物涌泉** 每秒检测四个面相邻方块,如果它们都是某一种粗矿块,则将 **深板岩** 转化为对应的深层矿。
+在主世界生成时,有1%概率生成 **地核碎片矿石**,有1%概率生成 **虚空石** 。
+在下界生成时,有20%概率生成 **地核碎片矿石** 。
+在末地生成时,有20%概率生成 **虚空石** 。
-
-
+注意:宝石不能以这种方式再生,仅接受粗矿块。
+注意:矿物涌泉仅在y=-54及以下位置工作
-*地核碎片矿石*只能通过*矿物涌泉产矿*伴生
-受到*时运*与*精准采集*影响
+**地核碎片矿石** 只能通过 **矿物涌泉产矿** 伴生
+受到 **时运** 与 **精准采集** 影响
需要钻石镐及以上等级镐挖掘
-挖掘获得*地核碎片*
-
-
-
+挖掘获得 **地核碎片**
-*地核碎片*及其块状物、矿石防火,不受*熔岩*和*火焰*损害
-可参与[余烬金属锭](../smithing_tier_2_materials.md#ember_metal)的制作
+
-
-
-
-*虚空石*只能通过*矿物涌泉产矿*伴生
-受到*时运*与*精准采集*影响
-需要钻石镐及以上等级镐挖掘
-挖掘获得*虚空物质*
+**地核碎片** 及其块状物、矿石防火,不受 **熔岩** 和 **火焰** 损害
+可参与[余烬金属锭]的制作
-
-
+
-*虚空物质*及其块状物、矿石防虚空,在*虚空*中会上升
+**虚空石** 只能通过 **矿物涌泉产矿** 伴生
+受到 **时运** 与 **精准采集** 影响
+需要钻石镐及以上等级镐挖掘
+挖掘获得 **虚空物质**
+**虚空物质** 及其块状物、矿石防虚空,在 **虚空** 中会上升
-
-*虚空物质块*防虚空
-可用于制作[虚空能收集器](../power_system/power_advanced_void_energy_collection.md#void_energy_collector)
-自身具有*虚空衰变*的特性
+
+
+
+
+
+
-
+**虚空物质块** 防虚空
+可用于制作[虚空能收集器]
+自身具有 **虚空衰变** 的特性
## 虚空衰变
@@ -113,18 +97,8 @@ item_ids:
## 激光采矿系统
-
-
-
-
-
-
-
-
-
-
-
-
+
+
这种结构可以自动提取粗矿并存放至上方容器内,更多结构可以参考*5.5激光采矿机*
diff --git a/guidebook/basic/basic_more_device.md b/guidebook/basic/basic_more_device.md
index 380875d..3c32262 100644
--- a/guidebook/basic/basic_more_device.md
+++ b/guidebook/basic/basic_more_device.md
@@ -16,11 +16,11 @@ item_ids:
本页面的设备可以帮助你在初期实现自动化。
-*手持磁铁*右键使用将附近物品吸引到脚下。
+**手持磁铁** 右键使用将附近物品吸引到脚下。
-*蟹笼*放置在水面,可以自动产出鱼获,四面紧邻的方块至少三个为水源或含水方块时正常工作。右键或铁砧砸之使其吐出产物。不同生物群系产出略有不同,但都会产出*蟹钳*。
+**蟹笼** 放置在水面,可以自动产出鱼获,四面紧邻的方块至少三个为水源或含水方块时正常工作。右键或铁砧砸之使其吐出产物。不同生物群系产出略有不同,但都会产出*蟹钳*。
@@ -32,11 +32,11 @@ item_ids:
-*方块吞噬器*有红石信号或被铁砧砸时破坏前方方块,红石激活范围3x3,铁砧激活根据高度1和2分别为5x5和7x7,掉落物会尝试进入吞噬器后方的方块、实体内,无法进入则原地掉落。世界基质方块如石头、下界岩等只有极少概率掉落。
+**方块吞噬器** 有红石信号或被铁砧砸时破坏前方方块,红石激活范围3x3,铁砧激活根据高度1和2分别为5x5和7x7,掉落物会尝试进入吞噬器后方的方块、实体内,无法进入则原地掉落。世界基质方块如石头、下界岩等只有极少概率掉落。
-*溜槽*是一种特殊的漏斗,有9格容量,一次性输送一组物品,可以将物品丢出至世界上。打开gui可以查看库存、改变输出方向和设置过滤。
+**溜槽** 是一种特殊的漏斗,有9格容量,一次性输送一组物品,可以将物品丢出至世界上。打开gui可以查看库存、改变输出方向和设置过滤。
diff --git a/guidebook/dev.md b/guidebook/dev.md
new file mode 100644
index 0000000..6578be9
--- /dev/null
+++ b/guidebook/dev.md
@@ -0,0 +1,40 @@
+---
+navigation:
+ title: "开发者指南"
+ icon: "anvilcraft:structure_tool"
+ position: 9999999
+---
+
+# 开发者指南
+
+ **假如你有兴趣开发本附属或用此附属开发自己的附属又或者出于其他目的**
+ **以为更多玩家提供便利,而找不到合适学习的地方,不妨看看这里**
+ ~~abab~~添加了一些不太常用和常用的功能
+ 添加的功能在下面写着
+ 如果不知道怎么下手,可以先去学MarkDown怎么写(~~抄也能抄会?~~)
+
+## 本模组目前状态:
+
+ - [y] 绝大部分配方的兼容
+ - [n] guide内容重写和新写
+ - [?] 有用没用的功能添加
+
+### 其他:
+
+ - [y] 在开发环境中[实时预览](https://guideme.appliedenergistics.org/live-preview)
+ - [n] 从帕秋莉转换 ~~(已放弃 出各种莫名其妙的bug)~~
+ - [?] 英文翻译 ~~(等中文补全再说吧 咕咕咕)~~
+
+## 模组加载情况:
+
+
+
+
+
+
+## 添加的功能:
+
+ - ModInfo: **提供modid以查看当前当前模组加载情况** (可以添加url跳转至外部链接)
+ - NeoColor: **提供十六进制颜色值以灵活自定义文本颜色** ~~("我吐了怎么guideme本体只能填那几个注册好的颜色啊")~~
+ - ItemEntity: **方便在场景中显示物品实体**~~(由于技术原因,物品实体不会转)~~
+ - Key: **提供某个功能的id以显示这个功能绑定的按键**
\ No newline at end of file
diff --git a/guidebook/index.md b/guidebook/index.md
index c8648f4..cbe22f7 100644
--- a/guidebook/index.md
+++ b/guidebook/index.md
@@ -4,12 +4,12 @@ navigation:
icon: "anvilcraft_guideme:guideme_book"
position: 0
---
-
+
# 介绍
-## 欢迎来到模组《铁砧工艺》的页面!模组是以铁砧为核心的原版生存拓展,主要内容有:
+## 欢迎来到模组《铁砧工艺》的页面!模组是以铁砧为核心的原版生存拓展
* 磁铁:将铁砧吸到空中,红石充能后释放
* 粉碎:将岩石粉碎为粉末
@@ -20,4 +20,4 @@ navigation:
* 膨发:将材料加水膨发
* 晶化:将材料加细雪晶化
* 压合:将两个方块压成一个
-* 方块破坏:破坏切石机上的方块
+* 方块破坏:破坏切石机上的方块
\ No newline at end of file
diff --git a/guidebook/item-block-machines.md b/guidebook/item-block-machines.md
new file mode 100644
index 0000000..9c127e8
--- /dev/null
+++ b/guidebook/item-block-machines.md
@@ -0,0 +1,25 @@
+---
+navigation:
+ title: "铁砧工艺本体-物品/方块/机器"
+ icon: "anvilcraft:magnet"
+---
+
+# 物品、方块和机器
+
+这是模组中其他页面可以链接的内容列表,以及它们的功能描述
+
+## 杂项 原料和方块
+
+
+
+## 电网方块
+
+
+
+## 机器
+
+
+
+## 工具
+
+
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/block/crab_trap.md b/guidebook/items-blocks-machines/block/crab_trap.md
new file mode 100644
index 0000000..e69de29
diff --git a/guidebook/items-blocks-machines/block/spectral_anvil.md b/guidebook/items-blocks-machines/block/spectral_anvil.md
new file mode 100644
index 0000000..e69de29
diff --git a/guidebook/items-blocks-machines/item/amethyst_tools.md b/guidebook/items-blocks-machines/item/amethyst_tools.md
new file mode 100644
index 0000000..05d7b55
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/amethyst_tools.md
@@ -0,0 +1,47 @@
+---
+navigation:
+ title: "紫水晶工具"
+ icon: "anvilcraft:amethyst_pickaxe"
+ position: 4
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:amethyst_pickaxe
+ - anvilcraft:amethyst_axe
+ - anvilcraft:amethyst_shovel
+ - anvilcraft:amethyst_hoe
+ - anvilcraft:amethyst_sword
+---
+
+# 紫水晶工具
+
+
+
+
+
+
+
+
+
+## 紫水晶工具
+
+- **便宜还好用的工具**
+- 紫水晶工具自带附魔
+ 1. 镐拥有时运III,可以帮助你获得更多铁矿,但是它挖掘等级低于铁镐
+ 2. 斧拥有伐木I,这是本模组新增的附魔,每级可以额外破坏2个相连的原木,上限为III
+ 3. 锹拥有效率III,可以帮助你更快整地
+ 4. 锄拥有收割I,这是本模组新增的附魔,右键成熟的庄稼使用收获庄稼并补种,每级可以额外收割一圈范围,上限为III
+ 5. 剑拥有斩首I,这是本模组新增的附魔,每级可以额外增加一些生物掉落头颅的概率,对于玩家和末影龙这个概率非常高,上限为III
+
+### 合成
+
+- 紫水晶工具可以被合成
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/amulet_box.md b/guidebook/items-blocks-machines/item/amulet_box.md
new file mode 100644
index 0000000..0b4b6bb
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/amulet_box.md
@@ -0,0 +1,32 @@
+---
+navigation:
+ title: "护符盒"
+ icon: "anvilcraft:amulet_box"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:amulet_box
+---
+
+# 护符盒
+
+
+
+
+
+## 护符盒
+
+- 护符盒是一种类似收纳袋的物品,可以容纳不死图腾或护符
+- 手持护符盒右击收纳物品栏中的不死图腾,潜行右击则会取出所有的不死图腾
+- 副手手持含有不死图腾的护符盒等价于持有不死图腾
+- 如果存活于特定致命伤害,有20%的概率获得相应护符。若失败,额外增加10%
+---
+- 每个不死图腾占用护符盒的 1 个槽位
+- 每个基础护符占用2*3即 6 个槽位
+- 在物品栏中将光标指向护符盒可以查看其占用情况
+
+## 获取
+
+- 可以通过和大师级珠宝匠村民交易获得[珠宝匠](jeweler.md)
diff --git a/guidebook/items-blocks-machines/item/anvil_hammer.md b/guidebook/items-blocks-machines/item/anvil_hammer.md
new file mode 100644
index 0000000..044e104
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/anvil_hammer.md
@@ -0,0 +1,51 @@
+---
+navigation:
+ title: "铁砧锤"
+ icon: "anvilcraft:anvil_hammer"
+ position: 9
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:anvil_hammer
+ - anvilcraft:royal_anvil_hammer
+ - anvilcraft:ember_anvil_hammer
+ - anvilcraft:transcendence_anvil_hammer
+---
+
+# 铁砧锤
+
+
+
+
+
+
+
+
+## 铁砧锤
+
+ - *铁砧锤*是本模组的扳手
+ 1. 右键可以调整某些方块的方向,蹲下右键可以拆除某些方块
+ 2. 左键方块对方块执行铁砧砸到方块的操作
+ 3. 重锤,从高处落下攻击生物对生物追加如同铁砧下落增加的伤害
+ 4. 护目镜,戴在头上可以查看电网范围和电网负载信息
+ 5. 头槌,戴在头上时鞘翅飞行撞击生物造成伤害
+ - *注意,左键敲击方块、左键攻击生物以及头槌撞击生物会消耗铁砧锤的耐久,其他操作不会*
+
+## 合成
+
+铁砧锤的合成
+
+此外,你还能合成皇家钢、余烬金属版本和超限金属版本的铁砧锤,这些铁砧锤有着对应金属工具的属性
+
+
+
+
+
+
+
+### 相关
+
+- [皇家金属工具](royal_steel_tools.md)
+- [余烬金属工具](ember_metal_tools.md)
+- [超限金属工具](transcendence_tools.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/crab_claw.md b/guidebook/items-blocks-machines/item/crab_claw.md
new file mode 100644
index 0000000..d2102a9
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/crab_claw.md
@@ -0,0 +1,26 @@
+---
+navigation:
+ title: "蟹钳"
+ icon: "anvilcraft:crab_claw"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:crab_claw
+---
+
+# 蟹钳
+
+
+
+
+
+## 蟹钳
+
+- ~~拳头硬了.png~~
+- 主手或副手手持时增加3格触及距离
+
+## 获取
+
+请看[蟹笼](../block/crab_trap.md)
diff --git a/guidebook/items-blocks-machines/item/dragon_rod.md b/guidebook/items-blocks-machines/item/dragon_rod.md
new file mode 100644
index 0000000..707aed1
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/dragon_rod.md
@@ -0,0 +1,65 @@
+---
+navigation:
+ title: "龙杖"
+ icon: "anvilcraft:dragon_rod"
+ position: 10
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:dragon_rod
+ - anvilcraft:royal_dragon_rod
+ - anvilcraft:ember_dragon_rod
+ - anvilcraft:transcendence_dragon_rod
+---
+
+# 龙杖
+
+
+
+
+
+
+
+
+## 龙杖
+
+- **龙杖** 本质上是为了将“放下**方块吞噬器**→**铁砧锤**敲击→收回方块吞噬器”的流程简化至一个工具内
+- 所有龙杖的功能是相同的,只是耐久不同和属性不同
+
+## 合成
+
+龙杖可以在工作台中合成
+
+
+
+此外,你还能合成皇家钢、余烬金属版本和超限金属版本的龙杖,这些龙杖有着对应金属工具的属性
+
+
+
+
+
+
+
+
+
+
+## 使用
+
+- 龙杖的操作十分简单
+ 1. 左键破坏一定范围内的方块
+ 2. 右键切换范围大小,有3x3、5x5、7x7、9x9四个范围
+ 3. 当手持龙杖准星指向方块时会显示范围框。
+- 3x3范围不消耗耐久,往后依次消耗1、2、4点耐久。
+- 当龙杖耐久消耗殆尽时不会完全损坏,而是失去所有功能,类似于 **鞘翅**
+
+## 破坏时
+
+龙杖遵循方块吞噬器的规则,当挖掘世界基底方块(**石头**、**下界岩**、**末地石**)时,只有5%的概率掉落。但是它无法连锁顶部的可下落方块
+龙杖在挖掘一次后会有一段冷却时间,默认为1秒。这段冷却时长只受*急迫*效果和*挖掘疲劳*效果影响,每级急迫会减少4tick,每级挖掘疲劳会增加1秒
+
+### 相关
+
+- [皇家金属工具](royal_steel_tools.md)
+- [余烬金属工具](ember_metal_tools.md)
+- [超限金属工具](transcendence_tools.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/ember_metal_tools.md b/guidebook/items-blocks-machines/item/ember_metal_tools.md
new file mode 100644
index 0000000..f7d5c12
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/ember_metal_tools.md
@@ -0,0 +1,65 @@
+---
+navigation:
+ title: "余烬金属工具"
+ icon: "anvilcraft:ember_metal_pickaxe"
+ position: 6
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:ember_metal_pickaxe
+ - anvilcraft:ember_metal_axe
+ - anvilcraft:ember_metal_shovel
+ - anvilcraft:ember_metal_hoe
+ - anvilcraft:ember_metal_sword
+---
+
+# 余烬金属工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 余烬金属工具
+
+ !重铸!
+- 默认数值
+ 1. 耐久以及挖掘等级与下界合金工具相同
+ 2. 所有余烬金属工具武器均拥有特殊属性: [重铸](../properties/properties.md#重铸)
+ 3. 基础挖掘速度 10
+ 4. 造成伤害的基础值为:剑9,斧11,镐7,锹7.5,锄2
+ 6. 不怕火焰和熔岩
+
+## 合成方式:
+
+需要余烬锻造模板和余烬金属锭(块)以及被锻造的工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 相关:
+
+- [铁砧锤](anvil_hammer.md)
+- [龙杖](dragon_rod.md)
+- [皇家钢工具](royal_steel_tools.md)
+- [共振器](resonator.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/frost_metal_tools.md b/guidebook/items-blocks-machines/item/frost_metal_tools.md
new file mode 100644
index 0000000..aa836b1
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/frost_metal_tools.md
@@ -0,0 +1,60 @@
+---
+navigation:
+ title: "浮霜金属工具"
+ icon: "anvilcraft:frost_metal_pickaxe"
+ position: 7
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:frost_metal_pickaxe
+ - anvilcraft:frost_metal_axe
+ - anvilcraft:frost_metal_shovel
+ - anvilcraft:frost_metal_hoe
+ - anvilcraft:frost_metal_sword
+---
+
+# 浮霜金属工具
+
+
+
+
+
+
+
+
+
+
+
+## 浮霜金属工具
+
+ ~~**数值怪还是机制怪?**~~
+- 默认数值
+ 1. 耐久以及挖掘等级与下界合金工具相同
+ 2. 所有浮霜金属工具武器均拥有特殊属性:[无情](../properties/properties.md#无情)
+ 3. 基础挖掘速度与金质工具(均为12)
+ 4. 造成伤害的基础值为:剑12,斧15,镐9,锹10,锄4
+ 5. 攻击速度与下界合金相同
+
+## 合成方式:
+
+需要浮霜金属锻造模板和浮霜金属锭(块)以及被锻造的工具
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 相关:
+
+- [龙杖](dragon_rod.md)
+- [皇家钢工具](royal_steel_tools.md)
+- [共振器](resonator.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/geode.md b/guidebook/items-blocks-machines/item/geode.md
new file mode 100644
index 0000000..69b0cf1
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/geode.md
@@ -0,0 +1,38 @@
+---
+navigation:
+ title: "晶洞"
+ icon: "anvilcraft:geode"
+ position: 3
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:geode
+---
+
+# 晶洞
+
+
+
+## 晶洞
+
+### 获取方式:
+
+1. 挖掘紫水晶母岩(时运无法生效)
+2. 与珠宝商交易
+3. 开局的奖励箱(需要在创建世界时开启奖励箱选项)
+
+### 功能:
+
+1. 使用时能够找到附近的紫水晶洞(地物)
+2. 参与合成
+
+### 参与合成:
+
+- 在晶洞在物品冲压时,一定出4个紫水晶碎片,概率出黄玉、蓝宝石和红宝石
+- 对晶洞进行时移,可以合成出紫水晶母岩
+
+
+
+
+
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/guide_book.md b/guidebook/items-blocks-machines/item/guide_book.md
new file mode 100644
index 0000000..b700d53
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/guide_book.md
@@ -0,0 +1,36 @@
+---
+navigation:
+ title: "铁砧工艺指南"
+ icon: "anvilcraft:guide_book"
+ position: 1
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:guide_book
+---
+
+# 铁砧工艺指南
+
+
+
+## 铁砧工艺指南
+
+### 获取方式:
+
+1. 玩家第一次进入游戏可以获得一本 **铁砧工艺指南**
+2. 玩家可以通过 **书** 和 **铁砧** 为原料的无序合成来合成 **铁砧工艺指南**
+
+
+
+### 功能:
+
+1. 将鼠标 **悬浮** 在指南上并按住 **W** 即可打开铁砧工艺的"集成",这时能查看铁砧工艺本体有哪些收录的附属以及与什么模组有联动/兼容,可以按需安装这些模组
+2. 在游戏内直接使用指南,能打开附属提供的指南(比如现在打开的就是AnvilCraft-GuideME提供的指南),你可以在里面查看大多数铁砧工艺在游戏内的信息
+3. 在部分物品上按住快捷键能直接打开指南,查看物品信息
+
+### 其他:
+
+1. 指南提供的信息可能跟不上本体作出的更改 需要及时更新(记得反馈)
+2. 指南的更新和完善需要你的帮助,如果你有决心、有能力,可以来为指南做贡献
+3. 部分信息指南无法给出,必要时请查阅互联网或询问**~~大佬~~**其他玩家
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/heavy_halberd.md b/guidebook/items-blocks-machines/item/heavy_halberd.md
new file mode 100644
index 0000000..37bb6ec
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/heavy_halberd.md
@@ -0,0 +1,50 @@
+---
+navigation:
+ title: "重戟"
+ icon: "anvilcraft:ember_metal_heavy_halberd"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:ember_metal_heavy_halberd
+ - anvilcraft:frost_metal_heavy_halberd
+ - anvilcraft:transcendence_heavy_halberd
+---
+
+# 重戟
+
+
+
+
+
+
+
+## 重戟
+
+- **重戟** 融合了剑、斧、重锤和三叉戟的特性,是一个强力的武器
+- **重戟核心** 是合成重戟的核心材料
+- 重戟可以作为合成它所需的四件武器中的任意一件使用
+ 1. 它能兼容剑、重锤和三叉戟的所有附魔
+ 2. 它的攻击伤害值与斧相同
+ 3. 攻击速度与剑相同
+ 4. 还具有它们的挖掘特性
+ 5. 从高处落下可以触发重锤的猛击
+ 6. 长按右键也可以像三叉戟一样投掷(伤害根据速度计算,与箭类似)
+ 7. 当它拥有忠诚魔咒时,掷入虚空会回到玩家处
+ 8. 重戟不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../properties/properties.md#无情)
+ 也会失效
+
+## 合成
+
+
+
+
+
+
+
+### 相关
+
+- [皇家金属工具](royal_steel_tools.md)
+- [余烬金属工具](ember_metal_tools.md)
+- [超限金属工具](transcendence_tools.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/ionocraft.md b/guidebook/items-blocks-machines/item/ionocraft.md
new file mode 100644
index 0000000..8b53d5a
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/ionocraft.md
@@ -0,0 +1,35 @@
+---
+navigation:
+ title: "飘升机"
+ icon: "anvilcraft:ionocraft"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:ionocraft
+---
+
+# 飘升机
+
+
+
+
+
+## 飘升机
+
+- ~~飞起来~~
+- 右键放置在地面上生成一个飘升机实体(类似船实体,打掉掉飘升机物品)
+- 电网范围中给电网增加16kw负荷,会快速上升
+- 不在电网范围内会缓慢下降
+- 实体可以踩在上面
+
+## 合成
+
+
+
+
+
+### 相关
+
+- [飘升机背包](ionocraft_backpack.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/ionocraft_backpack.md b/guidebook/items-blocks-machines/item/ionocraft_backpack.md
new file mode 100644
index 0000000..2ee9ec0
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/ionocraft_backpack.md
@@ -0,0 +1,37 @@
+---
+navigation:
+ title: "飘升机背包"
+ icon: "anvilcraft:ionocraft_backpack"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:ionocraft_backpack
+---
+
+# 飘升机背包
+
+
+
+
+
+## 飘升机背包
+
+- ~~小小鸟 飞飞飞~~
+- 可以穿在胸甲栏位
+- 飞行行为不会消耗耐久
+- 装备着时,拥有创造飞行能力
+- 穿戴飘升机背包时不免疫摔落伤害
+- 在电网中对电网增加64kw的负载,自身每秒补充10秒的飞行时间值
+- 在电网外,玩家身上有电容器时,飞行时间小于600s自动将一个电容器转化为空电容器,增加600s飞行时间
+
+## 合成
+
+
+
+
+
+### 相关
+
+- [飘升机](ionocraft.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/jewelcrafting_table.md b/guidebook/items-blocks-machines/item/jewelcrafting_table.md
new file mode 100644
index 0000000..e69de29
diff --git a/guidebook/items-blocks-machines/item/jeweler.md b/guidebook/items-blocks-machines/item/jeweler.md
new file mode 100644
index 0000000..7a8327e
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/jeweler.md
@@ -0,0 +1,33 @@
+---
+navigation:
+ title: "珠宝匠"
+ icon: "anvilcraft:amulet_box"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:amulet_box
+---
+
+# 珠宝匠
+
+
+
+
+
+## 珠宝匠
+
+珠宝匠是村民的一种,可以与之交易一些铁砧工艺相关物品,包括重要的锻造模板
+
+## 转职
+
+- 将珠宝加工台作为工作方块的村民将转职为珠宝匠[珠宝加工台](jewelcrafting_table.md)
+
+## 交易列表
+
+| 等级 | 购买的物品 | 出售的物品 |
+|----|----------------------------------------------|--------------------------------------------|
+| 新手 | 4个 | 1个 |
+| 新手 | 1个 | 1个 |
+| 学徒 | 8个 | 1个 |
diff --git a/guidebook/items-blocks-machines/item/magnet.md b/guidebook/items-blocks-machines/item/magnet.md
new file mode 100644
index 0000000..bfa0654
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/magnet.md
@@ -0,0 +1,29 @@
+---
+navigation:
+ title: "手持磁铁"
+ icon: "anvilcraft:magnet"
+ position: 2
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+---
+
+# 手持磁铁
+
+
+
+## 手持磁铁
+
+### 获取方式:
+
+- 仅能通过合成获取
+
+
+
+### 功能:
+
+- 手持磁铁在使用能将附近物品吸引到脚下,会消耗耐久
+
+### 附魔:
+
+- 能够被附魔上 经验修补、耐久和消失诅咒
diff --git a/guidebook/items-blocks-machines/item/multitool.md b/guidebook/items-blocks-machines/item/multitool.md
new file mode 100644
index 0000000..8adbc48
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/multitool.md
@@ -0,0 +1,35 @@
+---
+navigation:
+ title: "多用途工具"
+ icon: "anvilcraft:multitool"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:multitool
+---
+
+# 多用途工具
+
+
+
+
+
+## 多用途工具
+
+- ~~小子,是瑞士军刀~~
+- **多相物质** 是合成多用途工具的核心材料
+- 多用途工具可以作为剪刀、打火石、刷子、望远镜、手持磁铁、钓鱼竿、胡萝卜钓竿、诡异菌钓竿八件工具中的任意一件使用
+ 1. 可以加速挖掘需要这些工具挖掘的方块
+ 2. ~~扎手呢!~~ 全开模式右键对自己造成1伤害,无其他反应
+
+## 合成
+
+
+
+
+
+### 相关
+
+- [手持磁铁](magnet.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/recovery_pearl.md b/guidebook/items-blocks-machines/item/recovery_pearl.md
new file mode 100644
index 0000000..aa75a53
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/recovery_pearl.md
@@ -0,0 +1,33 @@
+---
+navigation:
+ title: "回溯珍珠"
+ icon: "anvilcraft:recovery_pearl"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:recovery_pearl
+---
+
+# 回溯珍珠
+
+
+
+
+
+## 回溯珍珠
+
+- 右键使用将玩家传送到上个死亡点
+- 玩家距离上个死亡点距离小于12格时,右键使用将玩家传送回重生点
+- 使用后消耗,对玩家造成4点摔落伤害
+
+## 获取
+
+
+
+
+
+## 相关
+
+[回溯图腾](totem_of_recovery.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/resonator.md b/guidebook/items-blocks-machines/item/resonator.md
new file mode 100644
index 0000000..081d076
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/resonator.md
@@ -0,0 +1,49 @@
+---
+navigation:
+ title: "共振器"
+ icon: "anvilcraft:ember_metal_resonator"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:ember_metal_resonator
+ - anvilcraft:frost_metal_resonator
+ - anvilcraft:transcendence_resonator
+---
+
+# 共振器
+
+
+
+
+
+
+
+## 共振器
+
+- **共振器** 融合了镐、斧、锄、锹和剑与剪刀的挖掘能力的特性,是一个强力的工具
+- **共振器核心** 是合成共振器的核心材料
+- 共振器可以作为镐、斧、锄、锹、剑和剪刀六件工具中的任意一件使用
+ 1. 可以加速挖掘需要这些工具挖掘的方块
+ 2. 对于没有指定挖掘工具的方块,不额外提速,但使挖掘惩罚无效
+ 3. 按住左键时,当挖掘某方块达到了原版免去挖掘冷却的程度后,在按住左键的第一次挖掘后强制给一个挖掘冷却(防误触)
+ 4. 不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../properties/properties.md#无情)也会失效
+ 5. 基础攻击伤害和速度与对应的斧相同,耐久、基础挖掘等级与对应的镐相同
+ 6. 按住键(可配置按键)呼出轮盘,轮盘上有五个选项
+ - 选择Auto时即加速破坏任意方块
+ - 选择对应的工具类型时,只加速破坏对应的方块
+
+## 合成
+
+
+
+
+
+
+
+### 相关
+
+- [皇家金属工具](royal_steel_tools.md)
+- [余烬金属工具](ember_metal_tools.md)
+- [超限金属工具](transcendence_tools.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/royal_steel_tools.md b/guidebook/items-blocks-machines/item/royal_steel_tools.md
new file mode 100644
index 0000000..a85c35f
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/royal_steel_tools.md
@@ -0,0 +1,54 @@
+---
+navigation:
+ title: "皇家钢工具"
+ icon: "anvilcraft:royal_steel_pickaxe"
+ position: 5
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:royal_steel_pickaxe
+ - anvilcraft:royal_steel_axe
+ - anvilcraft:royal_steel_shovel
+ - anvilcraft:royal_steel_hoe
+ - anvilcraft:royal_steel_sword
+---
+
+# 皇家钢工具
+
+
+
+
+
+
+
+
+
+
+
+## 皇家钢工具
+
+ ~~**我的天哪!是黄瓜钢大人!**~~
+ - 相比[紫水晶工具](../item/amethyst_tools.md),皇家钢工具本身不自带附魔,继承被锻造的工具的附魔
+ - **钻石品质**
+
+## 合成方式:
+
+需要皇家钢锻造模板和皇家钢锭(块)以及被锻造的工具
+
+
+
+
+
+
+
+
+
+
+
+## 相关
+
+- [铁砧锤](anvil_hammer.md)
+- [龙杖](dragon_rod.md)
+- [余烬金属工具](../item/ember_metal_tools.md)
+- [浮霜金属工具](../item/frost_metal_tools.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/spectral_slingshot.md b/guidebook/items-blocks-machines/item/spectral_slingshot.md
new file mode 100644
index 0000000..4288db7
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/spectral_slingshot.md
@@ -0,0 +1,45 @@
+---
+navigation:
+ title: "幻灵弹弓"
+ icon: "anvilcraft:spectral_slingshot"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:spectral_slingshot
+---
+
+# 幻灵弹弓
+
+
+
+
+
+## 幻灵弹弓
+
+- ~~影分身?~~
+- 装填:装填只可装填入一个物品,但射击不会消耗物品,射出幻影
+ - 副手持物品,主手持幻灵弹弓来装填
+- 待机:与原版弩类似,鼠标指上去可以看到说明中有弹药信息
+- 射击:点按右键射出一弹药,长按右键连续射击,每次射击之间冷却为2秒
+- 伤害:基础伤害为该物品的近战伤害的50%(原武器仅增伤附魔生效,无情时增伤附魔不生效,然后可以通过幻灵弹弓的力量附魔增伤)
+- 取出和替换弹药:另一只手为空时shift右键取出弹药,另一只手为与弹药不同的物品时shift右键替换弹药
+- 附魔:
+ - 快速装填影响装填速度,与原版弩一致,同时降低冷却,每级降低0.25秒,最多降1秒
+ - 多重射击时每次射击射出多个弹药
+ - 穿透使得弹射物可以穿过实体
+ - 无限使得装填装填时不消耗手中的物品,但取出或替换弹药时也不会从中获得物品
+ - 力量增加弹射物伤害(与原版箭矢不同,每级只增加10%伤害)
+ - 火矢使弹射物燃烧
+ - 冲击造成击退
+
+## 合成
+
+
+
+
+
+### 相关
+
+- [幻灵铁砧](../block/spectral_anvil.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/totem_of_rage.md b/guidebook/items-blocks-machines/item/totem_of_rage.md
new file mode 100644
index 0000000..ed8ac14
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/totem_of_rage.md
@@ -0,0 +1,30 @@
+---
+navigation:
+ title: "狂暴图腾"
+ icon: "anvilcraft:totem_of_rage"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:totem_of_rage
+---
+
+# 狂暴图腾
+
+
+
+
+
+## 狂暴图腾
+
+- 与不死图腾类似,玩家受到致命伤害后触发
+- 消耗时先清除玩家已有的所有效果,恢复玩家所有血量和饱食度,再给玩家1分钟的以下效果
+ - 力量5,迅捷3,急迫3
+ - 狂暴(rage):持续时间内不再能获得任何效果,且该效果无法被牛奶等一般方式移除,只能通过指令移除或等待时间结束
+ - 无敌(invulnerable):免疫所有正常的伤害,除了kill指令(如同创造模式)
+- 一分钟的持续时间结束后玩家死亡,此次死亡无法被任何图腾免疫,对创造模式和旁观者模式玩家特判,不会死亡
+
+## 合成
+
+
diff --git a/guidebook/items-blocks-machines/item/totem_of_recovery.md b/guidebook/items-blocks-machines/item/totem_of_recovery.md
new file mode 100644
index 0000000..13baa23
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/totem_of_recovery.md
@@ -0,0 +1,32 @@
+---
+navigation:
+ title: "回溯图腾"
+ icon: "anvilcraft:totem_of_recovery"
+ position: 11
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+item_ids:
+ - anvilcraft:totem_of_recovery
+---
+
+# 回溯图腾
+
+
+
+
+
+## 回溯图腾
+
+- 与不死图腾类似,玩家受到致命伤害后触发(但对虚空伤害也生效),消耗时给于玩家一个回溯珍珠物品
+- 消耗时,与不死图腾给于玩家相同的药水效果,同时将玩家传送回当前重生点,同时刷新玩家的死亡位置为传送前的位置,此时使用回溯珍珠可以回到传送前的位置
+
+## 获取
+
+
+
+
+
+## 相关
+
+[回溯珍珠](recovery_pearl.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/item/transcendence_tools.md b/guidebook/items-blocks-machines/item/transcendence_tools.md
new file mode 100644
index 0000000..728468d
--- /dev/null
+++ b/guidebook/items-blocks-machines/item/transcendence_tools.md
@@ -0,0 +1,42 @@
+---
+navigation:
+ title: "超限工具"
+ icon: "anvilcraft:transcendence_anvil_hammer"
+ position: 8
+ parent: anvilcraft_guideme:item-block-machines.md
+categories:
+ - tools
+---
+
+# 超限工具
+
+
+
+
+
+
+
+
+## 超限工具
+
+ 那么强?!
+拥有特殊属性: [永恒](../properties/properties.md#永恒) 和 [强运](../properties/properties.md#强运)
+
+## 合成
+
+
+
+
+
+
+
+
+
+
+
+
+## 相关
+
+- [铁砧锤](anvil_hammer.md)
+- [龙杖](dragon_rod.md)
+- [共振器](resonator.md)
\ No newline at end of file
diff --git a/guidebook/items-blocks-machines/properties/properties.md b/guidebook/items-blocks-machines/properties/properties.md
new file mode 100644
index 0000000..e2b2b11
--- /dev/null
+++ b/guidebook/items-blocks-machines/properties/properties.md
@@ -0,0 +1,63 @@
+---
+navigation:
+ title: "属性"
+ icon: "minecraft:enchanted_book"
+ position: 10000000
+ parent: anvilcraft_guideme:item-block-machines.md
+---
+
+# 属性
+
+本页面展示了目前所有可用的特殊工具属性,以及它们的效果
+
+## 属性:
+
+### 重铸:
+
+[余烬金属工具](../item/ember_metal_tools.md)和多项物质工具均拥有重铸
+工具能在高温中恢复耐久
+
+| 方块(流体) | 恢复的耐久/gt |
+|--------|----------|
+| 火 | 2 |
+| 灵魂火 | 5 |
+| 熔岩 | 10 |
+| 熔岩炼药锅 | 10 |
+
+---
+
+### 无情
+
+[浮霜金属工具](../item/frost_metal_tools.md)均拥有无情
+拥有 **无情** 的武器工具,除了*耐久*、*经验修补*、*激流*、*忠诚*之外的附魔将会全部失效,将其转换为攻击伤害和挖掘速度
+ - 伤害数值:2*√n + n/3
+ - 挖掘速度: n
+ - (n为附魔等级加和)
+
+---
+
+### 多相
+
+多相物质工具均拥有多相
+拥有此属性的工具将会拥有两个“相位”
+每个相位会存储它们自己的名称和附魔,不会冲突
+可以通过按下
+
+---
+
+### 永恒
+
+[超限工具](../item/transcendence_tools.md)均拥有永恒
+拥有 **永恒** 属性的工具将:
+ 1. 不再消耗耐久,耐久时刻保持为满(同时加上原版不可破坏的属性,作为双保险)
+ 2. 免疫火焰、爆炸、仙人掌伤害,不会坠入虚空,在y小于Ymin+5的区域缓慢上浮
+ 3. 不会随着时间消失
+
+---
+
+### 强运
+
+[超限工具](../item/transcendence_tools.md)均拥有强运
+拥有 **强运** 属性的工具对于白名单内的附魔,
+附魔触发时,有25%概率再触发一次,有5%概率再触发两次(一次掉三个头、一次钓出三条鱼是可能的)
+目前支持的附魔: 时运、抢夺、斩首、荆棘、海之眷顾
diff --git a/guidebook/process/game_process_1.md b/guidebook/process/game_process_1.md
deleted file mode 100644
index 0891626..0000000
--- a/guidebook/process/game_process_1.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-navigation:
- title: "流程1-开局"
- icon: "anvilcraft:geode"
- position: 1
- parent: anvilcraft_guideme:process_index.md
-item_ids:
- - anvilcraft:amethyst_pickaxe
- - anvilcraft:amethyst_axe
- - anvilcraft:amethyst_shovel
- - anvilcraft:amethyst_hoe
- - anvilcraft:amethyst_sword
----
-
-# 流程1-开局
-
-本模组非常耗铁,除了建造原版铁傀儡农场之外,本模组提供了一些方法让你快速获得基础矿物,这要从*紫水晶*说起。*晶洞*是你开局的好伙伴,如果你开启了奖励箱,那么你在奖励箱中能找到一些,如果没有奖励箱,挖掘*紫水晶母岩*也会掉落晶洞,虽然晶洞的其中一个作用是帮助你寻找紫水晶洞穴。
-
-
-
-右键使用定位附近的紫水晶洞穴。在*冲压平台*上被铁砧砸碎可以得到紫水晶碎片和以下三种宝石之一:*黄玉*、*红宝石*、*蓝宝石*。在后续游戏流程中还可以使用时移操作将其变回*紫水晶母岩*。珠宝匠村民也会交易晶洞。
-
-
-
-使用*紫水晶碎片*制作一些工具,它们比铁工具还耐用,并且做出来就有附魔,可以帮助你在游戏初期快速发展。
-
-*紫水晶镐*拥有时运III,可以帮助你获得更多铁矿,但是它挖掘等级低于铁镐。
-
-
-
-*紫水晶斧*拥有伐木I,这是本模组新增的附魔,每级可以额外破坏2个相连的原木,上限为III。
-
-
-
-*紫水晶锹*拥有效率III,可以帮助你更快整地。
-
-
-
-*紫水晶锄*拥有收割I,这是本模组新增的附魔,右键成熟的庄稼使用收获庄稼并补种,每级可以额外收割一圈范围,上限为III。
-
-
-
-*紫水晶剑*拥有斩首I,这是本模组新增的附魔,每级可以额外增加一些生物掉落头颅的概率,对于玩家和末影龙这个概率非常高,上限为III。
-
-
-
diff --git a/guidebook/smithing_system/smithing_advanced_tools.md b/guidebook/smithing_system/smithing_advanced_tools.md
index d3bba82..a3f242e 100644
--- a/guidebook/smithing_system/smithing_advanced_tools.md
+++ b/guidebook/smithing_system/smithing_advanced_tools.md
@@ -37,7 +37,8 @@ item_ids:
{"type":"patchouli:smithing","recipe":"anvilcraft:smithing/royal_dragon_rod","recipe2":"anvilcraft:smithing/ember_dragon_rod"}
```
-龙杖的操作十分简单。左键破坏一定范围内的方块,右键切换范围大小。有3x3、5x5、7x7、9x9四个范围,当手持龙杖准星指向方块时会显示范围框。3x3范围不消耗耐久,往后依次消耗1、2、4点耐久。当龙杖耐久消耗殆尽时不会完全损坏,而是失去所有功能,类似于*鞘翅*。
+龙杖的操作十分简单。左键破坏一定范围内的方块,右键切换范围大小。有3x3、5x5、7x7、9x9四个范围,当手持龙杖准星指向方块时会显示范围框。3x3范围不消耗耐久,往后依次消耗1、2、4点耐久。当龙杖耐久消耗殆尽时不会完全损坏,而是失去所有功能,类似于
+*鞘翅*。
龙杖遵循方块吞噬器的规则,当挖掘世界基底方块(*石头*、*下界岩*、*末地石*)时,只有5%的概率掉落。但是它无法连锁顶部的可下落方块。
龙杖在挖掘一次后会有一段冷却时间,默认为1秒。这段冷却时长只受*急迫*效果和*挖掘疲劳*效果影响,每级急迫会减少4tick,每级挖掘疲劳会增加1秒。
@@ -58,7 +59,8 @@ item_ids:
重戟可以作为合成它所需的四件武器中的任意一件使用。它能兼容剑、重锤和三叉戟的所有附魔。它的攻击伤害值与斧相同,攻击速度与剑相同,还具有它们的挖掘特性。从高处落下可以触发重锤的猛击。长按右键也可以像三叉戟一样投掷,但是投掷伤害根据速度计算,与箭类似。当它拥有忠诚魔咒时,掷入虚空会回到玩家处。
-重戟同样不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../advanced_features/advanced_tool_properties.md#merciless)也会失效。
+重戟同样不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../advanced_features/advanced_tool_properties.md#merciless)
+也会失效。
@@ -78,5 +80,6 @@ item_ids:
手持共振器按住[]可以打开一个轮盘选择界面,包含共振器的五个模式,将鼠标拖到对应的位置就可以选中该模式并切换。
-共振器同样不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../advanced_features/advanced_tool_properties.md#merciless)也会失效。
+共振器同样不会完全损坏,类似于鞘翅,它会失去所有增益、功能等效果,攻击伤害变为0,附魔大部分失效,浮霜带来的[无情](../advanced_features/advanced_tool_properties.md#merciless)
+也会失效。
diff --git a/img.png b/img.png
new file mode 100644
index 0000000..cb63cfb
Binary files /dev/null and b/img.png differ
diff --git a/src/generated/resources/assets/anvilcraft_guideme/lang/en_ud.json b/src/generated/resources/assets/anvilcraft_guideme/lang/en_ud.json
index bc32573..81b60fd 100644
--- a/src/generated/resources/assets/anvilcraft_guideme/lang/en_ud.json
+++ b/src/generated/resources/assets/anvilcraft_guideme/lang/en_ud.json
@@ -1,3 +1,4 @@
{
- "item.anvilcraft_guideme.guideme_book": "ʞooᗺ ǝɯǝpın⅁"
+ "gui.ac_guideme.loaded": "¡pǝpɐoꞀ sı %s",
+ "gui.ac_guideme.unloaded": "¡pǝpɐoꞀ ʇ,usı %s"
}
\ No newline at end of file
diff --git a/src/generated/resources/assets/anvilcraft_guideme/lang/en_us.json b/src/generated/resources/assets/anvilcraft_guideme/lang/en_us.json
index 5c9727d..b4b1970 100644
--- a/src/generated/resources/assets/anvilcraft_guideme/lang/en_us.json
+++ b/src/generated/resources/assets/anvilcraft_guideme/lang/en_us.json
@@ -1,3 +1,4 @@
{
- "item.anvilcraft_guideme.guideme_book": "Guideme Book"
+ "gui.ac_guideme.loaded": "%s is Loaded!",
+ "gui.ac_guideme.unloaded": "%s isn't Loaded!"
}
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/AnvilCraftGuideME.java b/src/main/java/dev/anvilcraft/guideme/AnvilCraftGuideME.java
index 97fd86d..d1c9627 100644
--- a/src/main/java/dev/anvilcraft/guideme/AnvilCraftGuideME.java
+++ b/src/main/java/dev/anvilcraft/guideme/AnvilCraftGuideME.java
@@ -3,16 +3,18 @@
import com.mojang.logging.LogUtils;
import com.tterrag.registrate.Registrate;
import dev.anvilcraft.guideme.data.ModDatagen;
-import dev.anvilcraft.guideme.element.ItemEntityShapeCompiler;
-import dev.anvilcraft.guideme.init.item.AddonItems;
+import dev.anvilcraft.guideme.guide.compiler.tag.KeyMapTagCompiler;
+import dev.anvilcraft.guideme.guide.compiler.tag.ModInfoTagCompiler;
+import dev.anvilcraft.guideme.guide.compiler.tag.NeoColorTagCompiler;
+import dev.anvilcraft.guideme.guide.compiler.tag.ItemEntityShapeCompiler;
import guideme.Guide;
+import guideme.compiler.TagCompiler;
import guideme.scene.element.SceneElementTagCompiler;
import lombok.Getter;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
-import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@Mod(AnvilCraftGuideME.MOD_ID)
@@ -20,22 +22,27 @@ public class AnvilCraftGuideME {
public static final String MOD_ID = "anvilcraft_guideme";
public static final Logger LOGGER = LogUtils.getLogger();
public static final Registrate REGISTRATE = Registrate.create(MOD_ID);
+ public static final ResourceLocation GID = AnvilCraftGuideME.of("guideme");
@Getter
private static Guide guideme;
- public static final ResourceLocation GID = AnvilCraftGuideME.of("guideme");
- public AnvilCraftGuideME(@NotNull IEventBus modEventBus, @NotNull ModContainer modContainer) {
- AddonItems.register();
+ public AnvilCraftGuideME(IEventBus modEventBus, ModContainer modContainer) {
ModDatagen.init();
+ this.guide();
+ }
+ private void guide() {
guideme = Guide.builder(GID)
.folder("ac_guidebook")
.extension(SceneElementTagCompiler.EXTENSION_POINT, new ItemEntityShapeCompiler())
+ .extension(TagCompiler.EXTENSION_POINT, new NeoColorTagCompiler())
+ .extension(TagCompiler.EXTENSION_POINT, new ModInfoTagCompiler())
+ .extension(TagCompiler.EXTENSION_POINT, new KeyMapTagCompiler())
.build();
}
- public static @NotNull ResourceLocation of(String path) {
+ public static ResourceLocation of(String path) {
return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
}
}
diff --git a/src/main/java/dev/anvilcraft/guideme/data/ModDatagen.java b/src/main/java/dev/anvilcraft/guideme/data/ModDatagen.java
index 9c387c3..4849062 100644
--- a/src/main/java/dev/anvilcraft/guideme/data/ModDatagen.java
+++ b/src/main/java/dev/anvilcraft/guideme/data/ModDatagen.java
@@ -12,7 +12,8 @@
@EventBusSubscriber(modid = AnvilCraftGuideME.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class ModDatagen {
@SubscribeEvent
- public static void gatherData(GatherDataEvent event) {}
+ public static void gatherData(GatherDataEvent event) {
+ }
/**
* 初始化生成器
diff --git a/src/main/java/dev/anvilcraft/guideme/data/lang/LangHandler.java b/src/main/java/dev/anvilcraft/guideme/data/lang/LangHandler.java
index a301747..c349d62 100644
--- a/src/main/java/dev/anvilcraft/guideme/data/lang/LangHandler.java
+++ b/src/main/java/dev/anvilcraft/guideme/data/lang/LangHandler.java
@@ -7,9 +7,10 @@ public class LangHandler {
/**
* 语言文件初始化
*
- * @param provider 提供器
+ * @param p 提供器
*/
- public static void init(RegistrateLangProvider provider) {
-
+ public static void init(RegistrateLangProvider p) {
+ p.add("gui.ac_guideme.loaded", "%s is Loaded!");
+ p.add("gui.ac_guideme.unloaded", "%s isn't Loaded!");
}
}
diff --git a/src/main/java/dev/anvilcraft/guideme/event/AddonGuideEventListener.java b/src/main/java/dev/anvilcraft/guideme/event/AddonGuideEventListener.java
new file mode 100644
index 0000000..a5a17a9
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/event/AddonGuideEventListener.java
@@ -0,0 +1,30 @@
+package dev.anvilcraft.guideme.event;
+
+import dev.anvilcraft.guideme.AnvilCraftGuideME;
+import dev.dubhe.anvilcraft.api.event.CheckIntegrationLoadedEvent;
+import dev.dubhe.anvilcraft.api.event.GuideBookEvent;
+import guideme.GuidesCommon;
+import net.neoforged.bus.api.SubscribeEvent;
+import net.neoforged.fml.common.EventBusSubscriber;
+
+import static dev.anvilcraft.guideme.AnvilCraftGuideME.GID;
+
+@EventBusSubscriber(modid = AnvilCraftGuideME.MOD_ID)
+public class AddonGuideEventListener {
+ @SubscribeEvent
+ public static void onHasGuide(GuideBookEvent.HasGuideBookEvent event) {
+ event.hasGuideBook();
+ }
+
+ @SubscribeEvent
+ public static void onOpenGuide(GuideBookEvent.OpenGuideBookEvent event) {
+ GuidesCommon.openGuide(event.getPlayer(), GID);
+ }
+
+ @SubscribeEvent
+ public static void onHasGuide(CheckIntegrationLoadedEvent event) {
+ if (event.getId().equals("guideme")) {
+ event.setLoaded();
+ }
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/element/package-info.java b/src/main/java/dev/anvilcraft/guideme/event/package-info.java
similarity index 80%
rename from src/main/java/dev/anvilcraft/guideme/element/package-info.java
rename to src/main/java/dev/anvilcraft/guideme/event/package-info.java
index d632225..f8350fc 100644
--- a/src/main/java/dev/anvilcraft/guideme/element/package-info.java
+++ b/src/main/java/dev/anvilcraft/guideme/event/package-info.java
@@ -1,6 +1,6 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
-package dev.anvilcraft.guideme.element;
+package dev.anvilcraft.guideme.event;
import net.minecraft.MethodsReturnNonnullByDefault;
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/RecipeTypeContributions.java b/src/main/java/dev/anvilcraft/guideme/guide/RecipeTypeContributions.java
new file mode 100644
index 0000000..7070172
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/RecipeTypeContributions.java
@@ -0,0 +1,401 @@
+package dev.anvilcraft.guideme.guide;
+
+import dev.anvilcraft.guideme.guide.recipe.LytBaseMultipleToOneSmithingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytBlockCompressRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytBlockCrushRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytBlockSmearRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytBoilingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytBulgingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytChargerChargingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytCollisionRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytCookingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytItemCompressRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytItemCrushRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytItemInjectRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytJewelCraftingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytMassInjectRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytMeshRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytMobTransformRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytMobTransformWithItemRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytNeutronIrradiationRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytSqueezingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytStampingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytSuperHeatingRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytTimeWarpRecipe;
+import dev.anvilcraft.guideme.guide.recipe.LytUnpackRecipe;
+import dev.dubhe.anvilcraft.init.item.ModItems;
+import dev.dubhe.anvilcraft.init.reicpe.ModRecipeTypes;
+import dev.dubhe.anvilcraft.recipe.ChargerChargingRecipe;
+import dev.dubhe.anvilcraft.recipe.JewelCraftingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.MassInjectRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.collision.AnvilCollisionCraftRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockCompressRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockCrushRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockSmearRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BoilingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BulgingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.CookingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemCompressRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemCrushRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemInjectRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.MeshRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.NeutronIrradiationRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.SqueezingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.StampingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.SuperHeatingRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.TimeWarpRecipe;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.UnpackRecipe;
+import dev.dubhe.anvilcraft.recipe.multiple.BaseMultipleToOneSmithingRecipe;
+import dev.dubhe.anvilcraft.recipe.transform.MobTransformRecipe;
+import dev.dubhe.anvilcraft.recipe.transform.MobTransformWithItemRecipe;
+import guideme.compiler.tags.RecipeTypeMappingSupplier;
+import guideme.document.block.recipes.LytStandardRecipeBox;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.crafting.RecipeHolder;
+
+public class RecipeTypeContributions implements RecipeTypeMappingSupplier {
+
+ // TODO: BeaconConversion | MultiBlock | EndPortalConversion | Transcendium | MobTransform
+
+ @Override
+ public void collect(RecipeTypeMappings mappings) {
+ mappings.add(ModRecipeTypes.BLOCK_COMPRESS_TYPE.get(), RecipeTypeContributions::blockCompress);
+ mappings.add(ModRecipeTypes.BLOCK_CRUSH_TYPE.get(), RecipeTypeContributions::blockCrush);
+ mappings.add(ModRecipeTypes.BLOCK_SMEAR_TYPE.get(), RecipeTypeContributions::blockSmear);
+ mappings.add(ModRecipeTypes.BOILING_TYPE.get(), RecipeTypeContributions::boiling);
+ mappings.add(ModRecipeTypes.BULGING_TYPE.get(), RecipeTypeContributions::bulging);
+ mappings.add(ModRecipeTypes.COOKING_TYPE.get(), RecipeTypeContributions::cooking);
+ mappings.add(ModRecipeTypes.ITEM_COMPRESS_TYPE.get(), RecipeTypeContributions::itemCompress);
+ mappings.add(ModRecipeTypes.ITEM_CRUSH_TYPE.get(), RecipeTypeContributions::itemCrush);
+ mappings.add(ModRecipeTypes.ITEM_INJECT_TYPE.get(), RecipeTypeContributions::itemInject);
+ mappings.add(ModRecipeTypes.MASS_INJECT_TYPE.get(), RecipeTypeContributions::massInject);
+ mappings.add(ModRecipeTypes.MESH_TYPE.get(), RecipeTypeContributions::mesh);
+ mappings.add(ModRecipeTypes.NEUTRON_IRRADIATION.get(), RecipeTypeContributions::neutronIrradiation);
+ mappings.add(ModRecipeTypes.SQUEEZING_TYPE.get(), RecipeTypeContributions::squeezing);
+ mappings.add(ModRecipeTypes.STAMPING_TYPE.get(), RecipeTypeContributions::stamping);
+ mappings.add(ModRecipeTypes.SUPER_HEATING_TYPE.get(), RecipeTypeContributions::superHeating);
+ mappings.add(ModRecipeTypes.TIME_WARP_TYPE.get(), RecipeTypeContributions::timeWarp);
+ mappings.add(ModRecipeTypes.UNPACK_TYPE.get(), RecipeTypeContributions::unpack);
+ mappings.add(ModRecipeTypes.ANVIL_COLLISION_CRAFT.get(), RecipeTypeContributions::collision);
+ mappings.add(ModRecipeTypes.CHARGER_CHARGING_TYPE.get(), RecipeTypeContributions::chargerCharging);
+ mappings.add(ModRecipeTypes.JEWEL_CRAFTING_TYPE.get(), RecipeTypeContributions::jewelCrafting);
+ mappings.add(ModRecipeTypes.MULTIPLE_TO_ONE_SMITHING_TYPE.get(), RecipeTypeContributions::multiple);
+ }
+
+ private static LytStandardRecipeBox blockCompress(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBlockCompressRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getFirstResultBlock()
+ .state()
+ .getBlock()
+ .asItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox blockCrush(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBlockCrushRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getFirstResultBlock()
+ .state()
+ .getBlock()
+ .asItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox blockSmear(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBlockSmearRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getFirstResultBlock()
+ .state()
+ .getBlock()
+ .asItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox boiling(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBoilingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox bulging(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBulgingRecipe(holder.value()))
+ .build(holder);
+ }
+ private static LytStandardRecipeBox cooking(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytCookingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox itemCompress(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytItemCompressRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox itemCrush(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytItemCrushRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox itemInject(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytItemInjectRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getFirstResultBlock()
+ .state()
+ .getBlock()
+ .asItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox massInject(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytMassInjectRecipe(holder.value()))
+ .title(
+ ModItems.NEUTRONIUM_INGOT.asItem().getDescription().getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox mesh(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytMeshRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox neutronIrradiation(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytNeutronIrradiationRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox squeezing(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytSqueezingRecipe(holder.value()))
+ .build(holder);
+ }
+ private static LytStandardRecipeBox stamping(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytStampingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox superHeating(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytSuperHeatingRecipe(holder.value()))
+ .build(holder);
+ }
+ private static LytStandardRecipeBox timeWarp(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytTimeWarpRecipe(holder.value()))
+ .build(holder);
+ }
+ private static LytStandardRecipeBox unpack(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytUnpackRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResultItems()
+ .getFirst()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox collision(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytCollisionRecipe(holder.value()))
+ .build(holder);
+ }
+ private static LytStandardRecipeBox chargerCharging(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytChargerChargingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResult()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox jewelCrafting(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytJewelCraftingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResult()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox multiple(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytBaseMultipleToOneSmithingRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .getResult()
+ .getResult()
+ .getItem()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+
+ private static LytStandardRecipeBox mobTransform(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytMobTransformRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .input()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+ private static LytStandardRecipeBox mobTransformWithItem(RecipeHolder holder) {
+ return LytStandardRecipeBox
+ .builder()
+ .icon(Items.ANVIL)
+ .customBody(new LytMobTransformWithItemRecipe(holder.value()))
+ .title(
+ holder
+ .value()
+ .input()
+ .getDescription()
+ .getString()
+ )
+ .build(holder);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/annotation/package-info.java b/src/main/java/dev/anvilcraft/guideme/guide/annotation/package-info.java
new file mode 100644
index 0000000..0c97bd3
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/annotation/package-info.java
@@ -0,0 +1,7 @@
+@MethodsReturnNonnullByDefault
+@ParametersAreNonnullByDefault
+package dev.anvilcraft.guideme.guide.annotation;
+
+import net.minecraft.MethodsReturnNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/element/ItemEntityShapeCompiler.java b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ItemEntityShapeCompiler.java
similarity index 79%
rename from src/main/java/dev/anvilcraft/guideme/element/ItemEntityShapeCompiler.java
rename to src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ItemEntityShapeCompiler.java
index 71869f3..95a2433 100644
--- a/src/main/java/dev/anvilcraft/guideme/element/ItemEntityShapeCompiler.java
+++ b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ItemEntityShapeCompiler.java
@@ -1,4 +1,4 @@
-package dev.anvilcraft.guideme.element;
+package dev.anvilcraft.guideme.guide.compiler.tag;
import guideme.compiler.PageCompiler;
import guideme.compiler.tags.MdxAttrs;
@@ -12,6 +12,10 @@
import java.util.Set;
+/**
+ * 物品实体编译器,实现SceneElementTagCompiler接口
+ * 用于处理场景中的物品实体标签,负责编译ItemEntity标签
+ */
public class ItemEntityShapeCompiler implements SceneElementTagCompiler {
public static final String TAG_NAME = "ItemEntity";
@@ -21,12 +25,7 @@ public Set getTagNames() {
}
@Override
- public void compile(
- GuidebookScene scene,
- PageCompiler compiler,
- LytErrorSink errorSink,
- MdxJsxElementFields el
- ) {
+ public void compile(GuidebookScene scene, PageCompiler compiler, LytErrorSink errorSink, MdxJsxElementFields el) {
ItemStack itemStack = MdxAttrs.getRequiredItemStack(compiler, errorSink, el);
if (itemStack == null) return;
int count = MdxAttrs.getInt(compiler, errorSink, el, "count", 1);
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/KeyMapTagCompiler.java b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/KeyMapTagCompiler.java
new file mode 100644
index 0000000..a73309c
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/KeyMapTagCompiler.java
@@ -0,0 +1,39 @@
+package dev.anvilcraft.guideme.guide.compiler.tag;
+
+import dev.anvilcraft.guideme.guide.compiler.util.ColorUtil;
+import guideme.compiler.PageCompiler;
+import guideme.compiler.tags.FlowTagCompiler;
+import guideme.compiler.tags.MdxAttrs;
+import guideme.document.flow.LytFlowParent;
+import guideme.document.flow.LytFlowSpan;
+import guideme.libs.mdast.mdx.model.MdxJsxElementFields;
+import guideme.style.TextStyle;
+import net.minecraft.client.KeyMapping;
+import net.minecraft.network.chat.Component;
+
+import java.util.Set;
+
+public class KeyMapTagCompiler extends FlowTagCompiler {
+ @Override
+ public Set getTagNames() {
+ return Set.of("Key");
+ }
+
+ @Override
+ protected void compile(PageCompiler compiler, LytFlowParent parent, MdxJsxElementFields el) {
+ String string = MdxAttrs.getString(compiler, parent, el, "id", null);
+ if (string == null) {
+ parent.appendError(compiler, "Missing 'id', attribute", el);
+ return;
+ }
+
+ Component component = KeyMapping.createNameSupplier(string).get();
+ Component component1 = Component.translatable(string);
+ String name = component1.getString() + " [" + component.getString() + "] ";
+ LytFlowSpan span = new LytFlowSpan();
+ span.appendText(name);
+ span.setHoverStyle(TextStyle.builder().bold(true).color(new ColorUtil("97d9e1")).build());
+ span.setStyle(TextStyle.builder().color(new ColorUtil("d9afd9")).build());
+ parent.append(span);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ModInfoTagCompiler.java b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ModInfoTagCompiler.java
new file mode 100644
index 0000000..b85c5bb
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/ModInfoTagCompiler.java
@@ -0,0 +1,81 @@
+package dev.anvilcraft.guideme.guide.compiler.tag;
+
+import dev.anvilcraft.guideme.guide.compiler.util.ColorUtil;
+import dev.anvilcraft.guideme.util.IntegrationUtil;
+import guideme.PageAnchor;
+import guideme.compiler.LinkParser;
+import guideme.compiler.PageCompiler;
+import guideme.compiler.tags.FlowTagCompiler;
+import guideme.compiler.tags.MdxAttrs;
+import guideme.document.flow.LytFlowLink;
+import guideme.document.flow.LytFlowParent;
+import guideme.libs.mdast.mdx.model.MdxJsxElementFields;
+import guideme.style.TextStyle;
+import net.minecraft.network.chat.Component;
+
+import java.net.URI;
+import java.util.Set;
+
+/**
+ * ModInfo标签编译器,继承自FlowTagCompiler
+ * 用于处理ModInfo元素,负责编译ModInfo标签并显示模组信息
+ */
+public class ModInfoTagCompiler extends FlowTagCompiler implements LinkParser.Visitor {
+ @Override
+ public Set getTagNames() {
+ return Set.of("ModInfo");
+ }
+
+ @Override
+ protected void compile(PageCompiler compiler, LytFlowParent parent, MdxJsxElementFields el) {
+ String id = MdxAttrs.getString(compiler, parent, el, "id", null);
+ if (id == null) {
+ parent.appendError(compiler, "Missing 'id' attribute", el);
+ return;
+ }
+ LytFlowLink link = new LytFlowLink();
+ String modName = IntegrationUtil.getName(id);
+ String modVersion = IntegrationUtil.getVersion(id);
+ String url = el.getAttributeString("url", null);
+ String nameAndVersion;
+ if (modName != null && modVersion != null) {
+ nameAndVersion = modName + " " + modVersion;
+ link.appendText(Component.translatable("gui.ac_guideme.loaded", nameAndVersion).getString());
+ link.setStyle(TextStyle.builder().color(new ColorUtil("98fb98")).build());
+ link.setHoverStyle(TextStyle.builder().bold(true).color(new ColorUtil("00ff00")).build());
+ } else if (modName != null) {
+ nameAndVersion = modName;
+ link.appendText(Component.translatable("gui.ac_guideme.loaded", nameAndVersion).getString());
+ link.setStyle(TextStyle.builder().color(new ColorUtil("98fb98")).build());
+ link.setHoverStyle(TextStyle.builder().bold(true).color(new ColorUtil("00ff00")).build());
+ } else {
+ nameAndVersion = id;
+ link.appendText(Component.translatable("gui.ac_guideme.unloaded", nameAndVersion).getString());
+ link.setStyle(TextStyle.builder().color(new ColorUtil("dc143c")).build());
+ link.setHoverStyle(TextStyle.builder().bold(true).color(new ColorUtil("ff0000")).build());
+ }
+
+ if (url != null) {
+ LinkParser.parseLink(
+ compiler, url, new LinkParser.Visitor() {
+ @Override
+ public void handlePage(PageAnchor page) {
+ link.setPageLink(page);
+ }
+
+ @Override
+ public void handleExternal(URI uri) {
+ link.setExternalUrl(uri);
+ }
+
+ @Override
+ public void handleError(String error) {
+ parent.appendError(compiler, error, el);
+ }
+ }
+ );
+ }
+
+ parent.append(link);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/NeoColorTagCompiler.java b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/NeoColorTagCompiler.java
new file mode 100644
index 0000000..263eb68
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/compiler/tag/NeoColorTagCompiler.java
@@ -0,0 +1,47 @@
+package dev.anvilcraft.guideme.guide.compiler.tag;
+
+import dev.anvilcraft.guideme.guide.compiler.util.ColorUtil;
+import guideme.color.ColorValue;
+import guideme.compiler.PageCompiler;
+import guideme.compiler.tags.FlowTagCompiler;
+import guideme.compiler.tags.MdxAttrs;
+import guideme.document.flow.LytFlowParent;
+import guideme.document.flow.LytFlowSpan;
+import guideme.libs.mdast.mdx.model.MdxJsxElementFields;
+import guideme.style.TextStyle;
+
+import java.util.Set;
+
+/**
+ * NeoColor标签编译器,继承自FlowTagCompiler
+ * 用于处理NeoColor标签,将指定颜色应用到文本内容上
+ */
+public class NeoColorTagCompiler extends FlowTagCompiler {
+ @Override
+ public Set getTagNames() {
+ return Set.of("NeoColor");
+ }
+
+ @Override
+ protected void compile(PageCompiler compiler, LytFlowParent parent, MdxJsxElementFields el) {
+ String string = MdxAttrs.getString(compiler, parent, el, "id", null);
+ if (string == null) {
+ parent.appendError(compiler, "Missing 'id' attribute", el);
+ return;
+ }
+
+ ColorValue firstColor;
+
+ try {
+ firstColor = new ColorUtil(string);
+ } catch (IllegalArgumentException e) {
+ parent.appendError(compiler, "Unknown color: '" + string + "'", el);
+ return;
+ }
+
+ LytFlowSpan span = new LytFlowSpan();
+ span.setStyle(TextStyle.builder().color(firstColor).build());
+ parent.append(span);
+ compiler.compileFlowContext(el.children(), span);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/compiler/util/ColorUtil.java b/src/main/java/dev/anvilcraft/guideme/guide/compiler/util/ColorUtil.java
new file mode 100644
index 0000000..746deb9
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/compiler/util/ColorUtil.java
@@ -0,0 +1,81 @@
+package dev.anvilcraft.guideme.guide.compiler.util;
+
+import guideme.color.ColorValue;
+import guideme.color.LightDarkMode;
+import net.minecraft.util.FastColor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// 大部分代码来自 guideme.color.Colors : https://github.com/AppliedEnergistics/GuideME : LGPL-3.0 https://github.com/AppliedEnergistics/GuideME/blob/main/LICENSE.md
+public class ColorUtil implements ColorValue {
+ private static final Logger LOG = LoggerFactory.getLogger(ColorUtil.class);
+
+ private final int color;
+
+ public ColorUtil(String color) {
+ this.color = hexToRgb(color);
+ }
+
+ public static int hexToRgb(String hexColor) {
+ if (!hexColor.isEmpty()) {
+ int start = 0;
+ if (hexColor.charAt(0) == '#') {
+ start++;
+ }
+
+ int remainingChars = hexColor.length() - start;
+
+ if (remainingChars == 3 || remainingChars == 4) {
+ int r = fromHexChar(hexColor.charAt(start));
+ int g = fromHexChar(hexColor.charAt(start + 1));
+ int b = fromHexChar(hexColor.charAt(start + 2));
+ int a = 15;
+ if (remainingChars == 4) {
+ a = fromHexChar(hexColor.charAt(start + 3));
+ }
+ if (r != -1 && g != -1 && b != -1 && a != -1) {
+ return argb(a << 4 | a, r << 4 | r, g << 4 | g, b << 4 | b);
+ }
+ } else if (remainingChars == 6 || remainingChars == 8) {
+ int rHi = fromHexChar(hexColor.charAt(start));
+ int rLo = fromHexChar(hexColor.charAt(start + 1));
+ int gHi = fromHexChar(hexColor.charAt(start + 2));
+ int gLo = fromHexChar(hexColor.charAt(start + 3));
+ int bHi = fromHexChar(hexColor.charAt(start + 4));
+ int bLo = fromHexChar(hexColor.charAt(start + 5));
+ int aHi = 15, aLo = 15;
+ if (remainingChars == 8) {
+ aHi = fromHexChar(hexColor.charAt(start + 6));
+ aLo = fromHexChar(hexColor.charAt(start + 7));
+ }
+ if (rHi != -1 && rLo != -1 && gHi != -1 && gLo != -1 && bHi != -1 && bLo != -1 && aHi != -1 && aLo != -1) {
+ return argb(aHi << 4 | aLo, rHi << 4 | rLo, gHi << 4 | gLo, bHi << 4 | bLo);
+ }
+ }
+ }
+
+ LOG.error("Tried to parse an invalid hexadecimal color string: '{}'", hexColor);
+ return 0;
+ }
+
+ private static int fromHexChar(int ch) {
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 0xa + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 0xa + (ch - 'A');
+ } else {
+ return -1;
+ }
+ }
+
+ public static int argb(int a, int r, int g, int b) {
+ return FastColor.ARGB32.color(a, r, g, b);
+ }
+
+ @Override
+ public int resolve(LightDarkMode lightDarkMode) {
+ return color;
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/init/item/package-info.java b/src/main/java/dev/anvilcraft/guideme/guide/package-info.java
similarity index 80%
rename from src/main/java/dev/anvilcraft/guideme/init/item/package-info.java
rename to src/main/java/dev/anvilcraft/guideme/guide/package-info.java
index 907a697..db178d2 100644
--- a/src/main/java/dev/anvilcraft/guideme/init/item/package-info.java
+++ b/src/main/java/dev/anvilcraft/guideme/guide/package-info.java
@@ -1,6 +1,6 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
-package dev.anvilcraft.guideme.init.item;
+package dev.anvilcraft.guideme.guide;
import net.minecraft.MethodsReturnNonnullByDefault;
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBaseMultipleToOneSmithingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBaseMultipleToOneSmithingRecipe.java
new file mode 100644
index 0000000..a38da5c
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBaseMultipleToOneSmithingRecipe.java
@@ -0,0 +1,75 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytSimpleItemSlot;
+import dev.anvilcraft.guideme.util.TextureConstants;
+import dev.dubhe.anvilcraft.recipe.multiple.BaseMultipleToOneSmithingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.item.crafting.Ingredient;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytBaseMultipleToOneSmithingRecipe extends LytVBox {
+ private static final int[] INPUT_X = {
+ 76,
+ 76,
+ 58,
+ 94,
+ 94,
+ 58,
+ 94,
+ 58
+ };
+ private static final int[] INPUT_Y = {
+ -10,
+ 26,
+ 8,
+ 8,
+ -10,
+ -10,
+ 26,
+ 26
+ };
+
+ private final LytSimpleItemSlot templateSlot;
+ private final LytSimpleItemSlot materialSlot;
+ private final LytSimpleItemSlot outputSlot;
+ private final List slots = new ArrayList<>();
+
+ public LytBaseMultipleToOneSmithingRecipe(BaseMultipleToOneSmithingRecipe recipe) {
+ append(templateSlot = new LytSimpleItemSlot(Ingredient.of(recipe.getTemplate().getItems())));
+ append(materialSlot = new LytSimpleItemSlot(Ingredient.of(recipe.getMaterial().getItems())));
+ append(outputSlot = new LytSimpleItemSlot(recipe.getResult().getResult()));
+ for (int i = 0; i < Math.min(8, recipe.getInputs().size()); i++) {
+ LytSimpleItemSlot slot;
+ append(slot = new LytSimpleItemSlot(Ingredient.of(recipe.getInputs().get(i).getItems())));
+ slots.add(slot);
+ }
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ templateSlot.render(context);
+ materialSlot.render(context);
+ outputSlot.render(context);
+ for (LytSimpleItemSlot slot : slots) {
+ slot.render(context);
+ }
+ context.fillIcon(new LytRect(bounds.x() - 3, bounds.y() - 14, 176, 64), TextureConstants.MULTIPLE_TO_ONE_SMITHING);
+ super.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ templateSlot.layout(context, x + 5, y + 21, availableWidth);
+ materialSlot.layout(context, x + 77, y + 9, availableWidth);
+ outputSlot.layout(context, x + 148, y + 21, availableWidth);
+ for (int i = 0; i < Math.min(8, slots.size()); i++) {
+ slots.get(i).layout(context, x + INPUT_X[i] + 1, y + INPUT_Y[i] + 1, availableWidth);
+ }
+ return new LytRect(x, y, 170, 47);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCompressRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCompressRecipe.java
new file mode 100644
index 0000000..87b8008
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCompressRecipe.java
@@ -0,0 +1,36 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockCompressRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+public class LytBlockCompressRecipe extends LytVBox {
+ private final LytBlockSlot inputBlocks;
+ private final LytBlockSlot outputBlocks;
+
+ public LytBlockCompressRecipe(BlockCompressRecipe recipe) {
+ append(inputBlocks = new LytBlockSlot(recipe.getInputBlocks()));
+ append(outputBlocks = new LytBlockSlot(BlockStateUtil.ChanceBlockStatesTransToBlockStatePredicates(recipe.getResultBlocks())));
+ inputBlocks.setAnvilAnimation(true);
+ inputBlocks.setHasAnvil(true);
+ outputBlocks.setHasAnvil(true);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputBlocks.render(context);
+ outputBlocks.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputBlocks.layout(context, x + 20, y - 2, availableWidth);
+ outputBlocks.layout(context, x + 50, y + 15, availableWidth);
+ int size = Math.max(inputBlocks.blockStatePredicates.size(), outputBlocks.blockStatePredicates.size());
+ return new LytRect(x, y, 90, size * 24);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCrushRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCrushRecipe.java
new file mode 100644
index 0000000..5a566bd
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockCrushRecipe.java
@@ -0,0 +1,36 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockCrushRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+public class LytBlockCrushRecipe extends LytVBox {
+ private final LytBlockSlot inputBlocks;
+ private final LytBlockSlot outputBlocks;
+
+
+ public LytBlockCrushRecipe(BlockCrushRecipe recipe) {
+ append(inputBlocks = new LytBlockSlot(recipe.getInputBlocks()));
+ append(outputBlocks = new LytBlockSlot(BlockStateUtil.ChanceBlockStatesTransToBlockStatePredicates(recipe.getResultBlocks())));
+ inputBlocks.setAnvilAnimation(true);
+ inputBlocks.setHasAnvil(true);
+ outputBlocks.setHasAnvil(true);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputBlocks.render(context);
+ outputBlocks.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputBlocks.layout(context, x + 20, y + 10, availableWidth);
+ outputBlocks.layout(context, x + 50, y + 10, availableWidth);
+ return new LytRect(x, y, 90, 42);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockSmearRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockSmearRecipe.java
new file mode 100644
index 0000000..f4ab95b
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBlockSmearRecipe.java
@@ -0,0 +1,35 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BlockSmearRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+public class LytBlockSmearRecipe extends LytVBox {
+ private final LytBlockSlot inputBlocks;
+ private final LytBlockSlot outputBlocks;
+
+ public LytBlockSmearRecipe(BlockSmearRecipe recipe) {
+ append(inputBlocks = new LytBlockSlot(recipe.getInputBlocks()));
+ append(outputBlocks = new LytBlockSlot(BlockStateUtil.ChanceBlockStatesTransToBlockStatePredicates(recipe.getResultBlocks())));
+ inputBlocks.setAnvilAnimation(true);
+ inputBlocks.setHasAnvil(true);
+ outputBlocks.setHasAnvil(true);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputBlocks.render(context);
+ outputBlocks.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputBlocks.layout(context, x + 20, y - 2, availableWidth);
+ outputBlocks.layout(context, x + 87, y + 15, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBoilingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBoilingRecipe.java
new file mode 100644
index 0000000..4860187
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBoilingRecipe.java
@@ -0,0 +1,50 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BoilingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.CampfireBlock;
+import net.minecraft.world.level.block.LayeredCauldronBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytBoilingRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+
+ public LytBoilingRecipe(BoilingRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.WATER_CAULDRON).with(LayeredCauldronBlock.LEVEL, 3).build());
+ work.add(BlockStatePredicate.builder().of(Blocks.CAMPFIRE).with(CampfireBlock.LIT, true).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBulgingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBulgingRecipe.java
new file mode 100644
index 0000000..426d05f
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytBulgingRecipe.java
@@ -0,0 +1,58 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.BulgingRecipe;
+import dev.dubhe.anvilcraft.util.CauldronUtil;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+
+public class LytBulgingRecipe extends LytVBox {
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ private final LytBlockSlot inputBlockSlot;
+ private final LytBlockSlot outputBlockSlot;
+
+ public LytBulgingRecipe(BulgingRecipe recipe) {
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+
+ BlockState state;
+ if (recipe.isFromWater()) {
+ state = CauldronUtil.fullState(Blocks.WATER_CAULDRON);
+ } else if (recipe.isProduceFluid()) {
+ state = Blocks.CAULDRON.defaultBlockState();
+ } else {
+ state = recipe.getHasCauldron().getTransformCauldron().defaultBlockState();
+ }
+ append(inputBlockSlot = new LytBlockSlot(state));
+ append(outputBlockSlot = new LytBlockSlot(BlockStateUtil.getCauldron(recipe.getHasCauldron())));
+ inputBlockSlot.setHasAnvil(true);
+ inputBlockSlot.setAnvilAnimation(true);
+ outputBlockSlot.setRender(!recipe.getResultItems().isEmpty());
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ inputBlockSlot.render(context);
+ outputBlockSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ inputBlockSlot.layout(context, x + 70, y + 10, availableWidth);
+ outputBlockSlot.layout(context, x + 100, y + 10, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytChargerChargingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytChargerChargingRecipe.java
new file mode 100644
index 0000000..fa99d0c
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytChargerChargingRecipe.java
@@ -0,0 +1,73 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.dubhe.anvilcraft.block.ChargerBlock;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.ChargerChargingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytSlot;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.network.chat.Component;
+
+public class LytChargerChargingRecipe extends LytVBox {
+ private final LytBlockSlot workBlock;
+ private final LytSlot inputItemSlot;
+ private final LytSlot outputItemSlot;
+ private final int power;
+ private final int time;
+
+ private static final String KEY_CATEGORY = "gui.anvilcraft.category.charger_charging";
+ private static final String KEY_POWER_CONSUME = KEY_CATEGORY + ".power_consume";
+ private static final String KEY_POWER_PRODUCE = KEY_CATEGORY + ".power_produce";
+ private static final String KEY_TIME = KEY_CATEGORY + ".time";
+
+ public LytChargerChargingRecipe(ChargerChargingRecipe recipe) {
+ this.power = recipe.getPower();
+ this.time = recipe.getTime();
+ if (power < 0) {
+ append(workBlock = new LytBlockSlot(ModBlocks.CHARGER.getDefaultState().trySetValue(ChargerBlock.OVERLOAD, false)));
+ } else {
+ append(workBlock = new LytBlockSlot(ModBlocks.DISCHARGER.getDefaultState().trySetValue(ChargerBlock.OVERLOAD, false)));
+ }
+
+ append(inputItemSlot = new LytSlot(recipe.getIngredient()));
+ append(outputItemSlot = new LytSlot(recipe.getResult()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlock.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+
+ GuiGraphics guiGraphics = context.guiGraphics();
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable(power < 0 ? KEY_POWER_CONSUME : KEY_POWER_PRODUCE, Math.abs(power)),
+ bounds.x(),
+ bounds.y() + 40,
+ 0xFF000000,
+ false
+ );
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable(KEY_TIME, 0.05 * time),
+ bounds.x(),
+ bounds.y() + 53,
+ 0xFF000000,
+ false
+ );
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlock.layout(context, x + 70, y + 15, availableWidth);
+ inputItemSlot.layout(context, x + 30, y + 10, availableWidth);
+ outputItemSlot.layout(context, x + 105, y + 10, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCollisionRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCollisionRecipe.java
new file mode 100644
index 0000000..1ad79c5
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCollisionRecipe.java
@@ -0,0 +1,84 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytSimpleItemSlot;
+import dev.anvilcraft.guideme.util.BlockTransformUtil;
+import dev.anvilcraft.guideme.util.TextureConstants;
+import dev.dubhe.anvilcraft.recipe.anvil.collision.AnvilCollisionCraftRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytSlot;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.Ingredient;
+
+public class LytCollisionRecipe extends LytVBox {
+ private final LytSlot inputAnvilSlot;
+ private final LytSimpleItemSlot hitBlockSlot;
+ private final LytOutputItemSlot outputItemSlot;
+ private final LytOutputItemSlot transOutputBlockSlot;
+ private final LytInputItemSlot transInputBlockSlot;
+ private final AnvilCollisionCraftRecipe recipe;
+
+ public LytCollisionRecipe(AnvilCollisionCraftRecipe recipe) {
+ this.recipe = recipe;
+ append(transInputBlockSlot = new LytInputItemSlot(BlockTransformUtil.getItemIngredientPredicate(recipe.transformBlocks())));
+ append(transOutputBlockSlot = new LytOutputItemSlot(BlockTransformUtil.getChanceItemStacks(recipe.transformBlocks())));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.outputItems()));
+ append(inputAnvilSlot = new LytSlot(Ingredient.of(recipe.anvil()
+ .getBlocks()
+ .stream()
+ .map(blockHolder -> new ItemStack(blockHolder.value())))));
+ append(hitBlockSlot = new LytSimpleItemSlot(Ingredient.of(recipe.hitBlock()
+ .getBlocks()
+ .stream()
+ .map(blockHolder -> new ItemStack(blockHolder.value())))));
+ hitBlockSlot.setItemSize(32);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputAnvilSlot.render(context);
+ hitBlockSlot.render(context);
+ outputItemSlot.render(context);
+ transInputBlockSlot.render(context);
+ transOutputBlockSlot.render(context);
+ context.fillIcon(new LytRect(bounds.x() + 40, bounds.y() - 5, 64, 64), TextureConstants.EXPLOSION);
+ if (!transInputBlockSlot.getMergedIngredients().isEmpty() && !transOutputBlockSlot.getResultItems().isEmpty()) {
+ context.fillIcon(new LytRect(bounds.x() + 124, bounds.y() + 16, 14, 22), TextureConstants.CONVERSION);
+ }
+
+ GuiGraphics guiGraphics = context.guiGraphics();
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable("gui.anvilcraft.category.anvil_collision.consume", recipe.consume()),
+ bounds.x(),
+ bounds.y() + 48,
+ 0xFF000000,
+ false
+ );
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable("gui.anvilcraft.category.anvil_collision.speed", recipe.speed()),
+ bounds.x(),
+ bounds.y() + 58,
+ 0xFF000000,
+ false
+ );
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputAnvilSlot.layout(context, x + 10, y + 15, availableWidth);
+ hitBlockSlot.layout(context, x + 51, y + 11, availableWidth);
+ outputItemSlot.layout(context, x + 90, y, availableWidth);
+ transInputBlockSlot.layout(context, x + 90, y + 25, availableWidth);
+ transOutputBlockSlot.layout(context, x + 90, y - 20, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCookingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCookingRecipe.java
new file mode 100644
index 0000000..e1746b5
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytCookingRecipe.java
@@ -0,0 +1,48 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.CookingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.CampfireBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytCookingRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytCookingRecipe(CookingRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.CAULDRON).build());
+ work.add(BlockStatePredicate.builder().of(Blocks.CAMPFIRE).with(CampfireBlock.LIT, true).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCompressRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCompressRecipe.java
new file mode 100644
index 0000000..7fca8b3
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCompressRecipe.java
@@ -0,0 +1,46 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemCompressRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytItemCompressRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytItemCompressRecipe(ItemCompressRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.CAULDRON).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 15, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCrushRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCrushRecipe.java
new file mode 100644
index 0000000..ea12c75
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemCrushRecipe.java
@@ -0,0 +1,46 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemCrushRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytItemCrushRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytItemCrushRecipe(ItemCrushRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(ModBlocks.CRUSHING_TABLE).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 15, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemInjectRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemInjectRecipe.java
new file mode 100644
index 0000000..a217a3c
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytItemInjectRecipe.java
@@ -0,0 +1,38 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.ItemInjectRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+public class LytItemInjectRecipe extends LytVBox {
+ private final LytBlockSlot inputBlockSlot;
+ private final LytBlockSlot outputBlockSlot;
+ private final LytInputItemSlot inputItemSlot;
+
+ public LytItemInjectRecipe(ItemInjectRecipe recipe) {
+ append(inputBlockSlot = new LytBlockSlot(recipe.getInputBlocks()));
+ append(outputBlockSlot = new LytBlockSlot(recipe.getFirstResultBlock().state()));
+ inputBlockSlot.setAnvilAnimation(true);
+ inputBlockSlot.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputBlockSlot.render(context);
+ outputBlockSlot.render(context);
+ inputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputBlockSlot.layout(context, x + 70, y + 15, availableWidth);
+ outputBlockSlot.layout(context, x + 105, y + 15, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytJewelCraftingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytJewelCraftingRecipe.java
new file mode 100644
index 0000000..ce4f0e6
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytJewelCraftingRecipe.java
@@ -0,0 +1,48 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytSimpleItemSlot;
+import dev.anvilcraft.guideme.util.TextureConstants;
+import dev.dubhe.anvilcraft.recipe.JewelCraftingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytJewelCraftingRecipe extends LytVBox {
+ private final LytSimpleItemSlot copySlot;
+ private final LytSimpleItemSlot resultSlot;
+ private final List slots = new ArrayList<>();
+
+ public LytJewelCraftingRecipe(JewelCraftingRecipe recipe) {
+ append(copySlot = new LytSimpleItemSlot(recipe.result.copy()));
+ append(resultSlot = new LytSimpleItemSlot(recipe.result.copy()));
+ for (var entry : recipe.mergedIngredients) {
+ LytSimpleItemSlot slot;
+ append(slot = new LytSimpleItemSlot(entry.getKey()));
+ slots.add(slot);
+ }
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ copySlot.render(context);
+ resultSlot.render(context);
+ for (LytSimpleItemSlot slot : slots) {
+ slot.render(context);
+ }
+ context.fillIcon(new LytRect(bounds.x() - 5, bounds.y() - 15, 176, 77), TextureConstants.STONE);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ copySlot.layout(context, x + 74, y + 4, availableWidth);
+ resultSlot.layout(context, x + 128, y + 36, availableWidth);
+ for (int i = 0; i < slots.size(); i++) {
+ slots.get(i).layout(context, x + i * 18 + 21, y + 36, availableWidth);
+ }
+ return new LytRect(x, y, 166, 57);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMassInjectRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMassInjectRecipe.java
new file mode 100644
index 0000000..1f3ca80
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMassInjectRecipe.java
@@ -0,0 +1,65 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.init.item.ModItems;
+import dev.dubhe.anvilcraft.recipe.anvil.MassInjectRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytSlot;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.network.chat.Component;
+
+import static dev.dubhe.anvilcraft.block.entity.SpaceOvercompressorBlockEntity.NEUTRONIUM_INGOT_MASS;
+
+public class LytMassInjectRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytSlot inputItemSlot;
+ private final LytSlot outputItemSlot;
+ private final MassInjectRecipe recipe;
+
+ public LytMassInjectRecipe(MassInjectRecipe recipe) {
+ this.recipe = recipe;
+ append(workBlocks = new LytBlockSlot(ModBlocks.SPACE_OVERCOMPRESSOR.getDefaultState()));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytSlot(recipe.getIngredient()));
+ append(outputItemSlot = new LytSlot(ModItems.NEUTRONIUM_INGOT.asStack()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+
+ GuiGraphics guiGraphics = context.guiGraphics();
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable("gui.anvilcraft.category.mass_inject.mass_value", recipe.displayMassValue()),
+ bounds.x(),
+ bounds.y() + 5,
+ 0xFF000000,
+ false
+ );
+ guiGraphics.drawString(
+ Minecraft.getInstance().font,
+ Component.translatable("gui.anvilcraft.category.mass_inject.items_needed", Math.ceilDiv(NEUTRONIUM_INGOT_MASS, recipe.getMass())),
+ bounds.x(),
+ bounds.y() + 50,
+ 0xFF000000,
+ false
+ );
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 15, availableWidth);
+ inputItemSlot.layout(context, x + 35, y + 15, availableWidth);
+ outputItemSlot.layout(context, x + 102, y + 15, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMeshRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMeshRecipe.java
new file mode 100644
index 0000000..199d1f6
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMeshRecipe.java
@@ -0,0 +1,46 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.MeshRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytMeshRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytMeshRecipe(MeshRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.SCAFFOLDING).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 10, availableWidth);
+ inputItemSlot.layout(context, x + 15, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformRecipe.java
new file mode 100644
index 0000000..2614cbb
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformRecipe.java
@@ -0,0 +1,10 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.dubhe.anvilcraft.recipe.transform.MobTransformRecipe;
+import guideme.document.block.LytVBox;
+
+// WIP
+public class LytMobTransformRecipe extends LytVBox {
+ public LytMobTransformRecipe(MobTransformRecipe recipe) {
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformWithItemRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformWithItemRecipe.java
new file mode 100644
index 0000000..c0c291f
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytMobTransformWithItemRecipe.java
@@ -0,0 +1,10 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.dubhe.anvilcraft.recipe.transform.MobTransformWithItemRecipe;
+import guideme.document.block.LytVBox;
+
+// WIP
+public class LytMobTransformWithItemRecipe extends LytVBox {
+ public LytMobTransformWithItemRecipe(MobTransformWithItemRecipe recipe) {
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytNeutronIrradiationRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytNeutronIrradiationRecipe.java
new file mode 100644
index 0000000..f3354da
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytNeutronIrradiationRecipe.java
@@ -0,0 +1,48 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.NeutronIrradiationRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytNeutronIrradiationRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytNeutronIrradiationRecipe(NeutronIrradiationRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.CAULDRON).build());
+ work.add(BlockStatePredicate.builder().of(ModBlocks.NEUTRON_IRRADIATOR).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSqueezingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSqueezingRecipe.java
new file mode 100644
index 0000000..50f27b0
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSqueezingRecipe.java
@@ -0,0 +1,48 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.anvilcraft.lib.recipe.component.ChanceBlockState;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.SqueezingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytSqueezingRecipe extends LytVBox {
+ private final LytBlockSlot inputBlocks;
+ private final LytBlockSlot outputBlocks;
+
+ public LytSqueezingRecipe(SqueezingRecipe recipe) {
+ List input = new ArrayList<>(recipe.getInputBlocks());
+ List output = new ArrayList<>(recipe.getResultBlocks());
+
+ input.add(BlockStatePredicate.builder().of(Blocks.CAULDRON).build());
+ output.add(ChanceBlockState.of(() -> BlockStateUtil.getCauldron(recipe.getHasCauldron()).getBlock()));
+ append(inputBlocks = new LytBlockSlot(input));
+ append(outputBlocks = new LytBlockSlot(BlockStateUtil.ChanceBlockStatesTransToBlockStatePredicates(output)));
+
+ inputBlocks.setAnvilAnimation(true);
+ inputBlocks.setHasAnvil(true);
+
+ outputBlocks.setHasAnvil(true);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputBlocks.render(context);
+ outputBlocks.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputBlocks.layout(context, x + 40, y, availableWidth);
+ outputBlocks.layout(context, x + 100, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytStampingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytStampingRecipe.java
new file mode 100644
index 0000000..de3840b
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytStampingRecipe.java
@@ -0,0 +1,46 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.StampingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytStampingRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytStampingRecipe(StampingRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(ModBlocks.STAMPING_PLATFORM).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 10, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSuperHeatingRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSuperHeatingRecipe.java
new file mode 100644
index 0000000..c48c1ea
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytSuperHeatingRecipe.java
@@ -0,0 +1,57 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.block.HeaterBlock;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.SuperHeatingRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytSuperHeatingRecipe extends LytVBox {
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ private final LytBlockSlot workBlocks;
+ private final LytBlockSlot outputBlockSlot;
+
+ public LytSuperHeatingRecipe(SuperHeatingRecipe recipe) {
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.CAULDRON).build());
+ work.add(BlockStatePredicate.builder().of(ModBlocks.HEATER).with(HeaterBlock.OVERLOAD, false).build());
+ append(workBlocks = new LytBlockSlot(work));
+ append(outputBlockSlot = new LytBlockSlot(BlockStateUtil.getCauldron(recipe.getHasCauldron())));
+ workBlocks.setHasAnvil(true);
+ workBlocks.setAnvilAnimation(true);
+ outputBlockSlot.setRender(!recipe.getResultItems().isEmpty());
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ workBlocks.render(context);
+ outputBlockSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ workBlocks.layout(context, x + 70, y, availableWidth);
+ outputBlockSlot.layout(context, x + 105, y + 15, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytTimeWarpRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytTimeWarpRecipe.java
new file mode 100644
index 0000000..613f56d
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytTimeWarpRecipe.java
@@ -0,0 +1,54 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.guideme.util.BlockStateUtil;
+import dev.dubhe.anvilcraft.block.CorruptedBeaconBlock;
+import dev.dubhe.anvilcraft.init.block.ModBlocks;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.TimeWarpRecipe;
+import dev.dubhe.anvilcraft.util.CauldronUtil;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+
+public class LytTimeWarpRecipe extends LytVBox {
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ private final LytBlockSlot workBlocks;
+ private final LytBlockSlot outputBlockSlot;
+
+ public LytTimeWarpRecipe(TimeWarpRecipe recipe) {
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+
+ append(workBlocks = new LytBlockSlot(
+ CauldronUtil.fullState(recipe.getHasCauldron().getFluidCauldron()),
+ ModBlocks.CORRUPTED_BEACON.getDefaultState().trySetValue(CorruptedBeaconBlock.LIT, false)
+ )
+ );
+ append(outputBlockSlot = new LytBlockSlot(BlockStateUtil.getCauldron(recipe.getHasCauldron())));
+ workBlocks.setHasAnvil(true);
+ workBlocks.setAnvilAnimation(true);
+ outputBlockSlot.setRender(!recipe.getResultItems().isEmpty());
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ workBlocks.render(context);
+ outputBlockSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 80, y, availableWidth);
+ workBlocks.layout(context, x + 70, y, availableWidth);
+ outputBlockSlot.layout(context, x + 105, y + 15, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytUnpackRecipe.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytUnpackRecipe.java
new file mode 100644
index 0000000..379d173
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/LytUnpackRecipe.java
@@ -0,0 +1,48 @@
+package dev.anvilcraft.guideme.guide.recipe;
+
+import dev.anvilcraft.guideme.guide.slot.LytBlockSlot;
+import dev.anvilcraft.guideme.guide.slot.LytInputItemSlot;
+import dev.anvilcraft.guideme.guide.slot.LytOutputItemSlot;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.wrap.UnpackRecipe;
+import guideme.document.LytRect;
+import guideme.document.block.LytVBox;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.TrapDoorBlock;
+import net.minecraft.world.level.block.state.properties.Half;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LytUnpackRecipe extends LytVBox {
+ private final LytBlockSlot workBlocks;
+ private final LytInputItemSlot inputItemSlot;
+ private final LytOutputItemSlot outputItemSlot;
+
+ public LytUnpackRecipe(UnpackRecipe recipe) {
+ List work = new ArrayList<>();
+ work.add(BlockStatePredicate.builder().of(Blocks.IRON_TRAPDOOR).with(TrapDoorBlock.HALF, Half.TOP).build());
+ append(workBlocks = new LytBlockSlot(work));
+ workBlocks.setAnvilAnimation(true);
+ workBlocks.setHasAnvil(true);
+ append(inputItemSlot = new LytInputItemSlot(recipe.getInputItems()));
+ append(outputItemSlot = new LytOutputItemSlot(recipe.getResultItems()));
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ workBlocks.render(context);
+ inputItemSlot.render(context);
+ outputItemSlot.render(context);
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ workBlocks.layout(context, x + 70, y + 10, availableWidth);
+ inputItemSlot.layout(context, x, y, availableWidth);
+ outputItemSlot.layout(context, x + 87, y, availableWidth);
+ return new LytRect(x, y, 162, 64);
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/recipe/package-info.java b/src/main/java/dev/anvilcraft/guideme/guide/recipe/package-info.java
new file mode 100644
index 0000000..ad49d1f
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/recipe/package-info.java
@@ -0,0 +1,7 @@
+@MethodsReturnNonnullByDefault
+@ParametersAreNonnullByDefault
+package dev.anvilcraft.guideme.guide.recipe;
+
+import net.minecraft.MethodsReturnNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/slot/LytBlockSlot.java b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytBlockSlot.java
new file mode 100644
index 0000000..5d6d7e5
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytBlockSlot.java
@@ -0,0 +1,136 @@
+package dev.anvilcraft.guideme.guide.slot;
+
+import dev.anvilcraft.guideme.util.GuideMERenderUtil;
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import guideme.document.LytRect;
+import guideme.document.block.LytBox;
+import guideme.document.interaction.GuideTooltip;
+import guideme.document.interaction.InteractiveElement;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+public class LytBlockSlot extends LytBox implements InteractiveElement {
+ private static final int WIDTH = 16;
+ private static final int HEIGHT = 16;
+
+ public final List blockStatePredicates;
+
+ @Setter
+ @Getter
+ private boolean anvilAnimation;
+ @Setter
+ @Getter
+ private boolean hasAnvil;
+ @Setter
+ @Getter
+ private boolean render;
+
+ public LytBlockSlot(List blockStatePredicates) {
+ this.blockStatePredicates = Objects.requireNonNullElseGet(blockStatePredicates, ArrayList::new);
+ }
+
+ public LytBlockSlot(BlockState blockState) {
+ BlockStatePredicate.Builder builder = BlockStatePredicate.builder();
+
+ for (Map.Entry, Comparable>> entry : blockState.getValues().entrySet()) {
+ Property> property = entry.getKey();
+ Comparable> value = entry.getValue();
+ builder.with(property, value.toString());
+ }
+
+ BlockStatePredicate predicate = builder.of(blockState.getBlock()).build();
+ this.blockStatePredicates = List.of(predicate);
+ }
+
+ public LytBlockSlot(List blockStatePredicates, BlockState blockState) {
+ BlockStatePredicate.Builder builder = BlockStatePredicate.builder();
+ for (Map.Entry, Comparable>> entry : blockState.getValues().entrySet()) {
+ Property> property = entry.getKey();
+ Comparable> value = entry.getValue();
+ builder.with(property, value.toString());
+ }
+ BlockStatePredicate predicate = builder.of(blockState.getBlock()).build();
+
+ List list = new ArrayList<>(blockStatePredicates);
+ list.add(predicate);
+ this.blockStatePredicates = list;
+ }
+
+ public LytBlockSlot(BlockState state1, BlockState state2) {
+ BlockStatePredicate.Builder builder1 = BlockStatePredicate.builder();
+ BlockStatePredicate.Builder builder2 = BlockStatePredicate.builder();
+ for (Map.Entry, Comparable>> entry : state1.getValues().entrySet()) {
+ Property> property = entry.getKey();
+ Comparable> value = entry.getValue();
+ builder1.with(property, value.toString());
+ }
+
+ for (Map.Entry, Comparable>> entry : state2.getValues().entrySet()) {
+ Property> property = entry.getKey();
+ Comparable> value = entry.getValue();
+ builder2.with(property, value.toString());
+ }
+
+ BlockStatePredicate predicate1 = builder1.of(state1.getBlock()).build();
+ BlockStatePredicate predicate2 = builder2.of(state2.getBlock()).build();
+ List list = new ArrayList<>();
+ list.add(predicate1);
+ list.add(predicate2);
+ this.blockStatePredicates = list;
+ }
+
+ @Override
+ protected LytRect computeBoxLayout(LayoutContext context, int x, int y, int availableWidth) {
+ int size = blockStatePredicates.size();
+ return new LytRect(x, y, WIDTH, size * HEIGHT);
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ int x = getSafeX();
+ int y = getSafeY();
+ if (blockStatePredicates.isEmpty()) return;
+ if (anvilAnimation && hasAnvil) {
+ GuideMERenderUtil.renderedBlockStatesAndAnvilAnimation(context.guiGraphics(), blockStatePredicates, x, y);
+ } else if (hasAnvil) {
+ GuideMERenderUtil.renderedBlockStatesAndAnvil(context.guiGraphics(), blockStatePredicates, x, y);
+ } else if (!render) {
+ GuideMERenderUtil.renderedBlock(context.guiGraphics(), blockStatePredicates, x, y);
+ }
+ }
+
+ @Override
+ public Optional getTooltip(float mouseX, float mouseY) {
+ // TODO: 这块的tooltip不会写 有没有好人帮我写下(发好人卡ing)
+ return Optional.empty();
+ }
+
+ public int getSafeX() {
+ return bounds.width() / 2 + bounds.x();
+ }
+
+ public int getSafeY() {
+ return bounds.height() / 2 + bounds.y();
+ }
+
+ @Override
+ protected void onLayoutMoved(int deltaX, int deltaY) {
+
+ }
+
+ @Override
+ public void renderBatch(RenderContext context, MultiBufferSource buffers) {
+
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/slot/LytInputItemSlot.java b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytInputItemSlot.java
new file mode 100644
index 0000000..b89ed24
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytInputItemSlot.java
@@ -0,0 +1,138 @@
+package dev.anvilcraft.guideme.guide.slot;
+
+import dev.anvilcraft.lib.recipe.component.ItemIngredientPredicate;
+import guideme.document.LytRect;
+import guideme.document.block.LytBlock;
+import guideme.document.interaction.GuideTooltip;
+import guideme.document.interaction.InteractiveElement;
+import guideme.document.interaction.ItemTooltip;
+import guideme.layout.LayoutContext;
+import guideme.render.GuiAssets;
+import guideme.render.GuiSprite;
+import guideme.render.RenderContext;
+import lombok.Getter;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+public class LytInputItemSlot extends LytBlock implements InteractiveElement {
+ private static final int ITEM_SIZE = 16;
+ private static final int SLOT_SIZE = 18;
+ private static final int CYCLE_TIME = 2000;
+
+ @Getter
+ private final List mergedIngredients;
+
+ public LytInputItemSlot(List mergedIngredients) {
+ this.mergedIngredients = mergedIngredients;
+ }
+
+ @Override
+ protected LytRect computeLayout(LayoutContext context, int x, int y, int availableWidth) {
+ int size = mergedIngredients.size();
+ if (size <= 1) {
+ return new LytRect(x + 32, y + 16, SLOT_SIZE, SLOT_SIZE);
+ } else if (size <= 4) {
+ return new LytRect(x + 24, y + 12, SLOT_SIZE * 2, SLOT_SIZE * 2);
+ } else if (size <= 6) {
+ return new LytRect(x + 8, y + 8, SLOT_SIZE * 3, SLOT_SIZE * 2);
+ } else {
+ return new LytRect(x + 8, y + 4, SLOT_SIZE * 3, SLOT_SIZE * 3);
+ }
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ GuiSprite texture = GuiAssets.SLOT;
+ var x = bounds.x();
+ var y = bounds.y();
+ int size = mergedIngredients.size();
+ if (size == 1) {
+ ItemIngredientPredicate ingredient = mergedIngredients.getFirst();
+ LytRect lytRect = new LytRect(bounds.x(), bounds.y(), SLOT_SIZE, SLOT_SIZE);
+ context.renderItem(ingredient.getItems()[0], x + 1, y + 1, ITEM_SIZE, ITEM_SIZE);
+ context.fillIcon(lytRect, texture);
+ } else if (size <= 4) {
+ for (int i = 0; i < size; i++) {
+ int row = i / 2;
+ int col = i % 2;
+ getItemStack(context, texture, i, row, col);
+ }
+ } else if (size <= 6) {
+ for (int i = 0; i < size; i++) {
+ int row = i / 3;
+ int col = i % 3;
+ getItemStack(context, texture, i, row, col);
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ if (i > 9) break;
+ int row = i / 3;
+ int col = i % 3;
+ getItemStack(context, texture, i, row, col);
+ }
+ }
+ }
+
+ private void getItemStack(RenderContext context, GuiSprite texture, int i, int row, int col) {
+ ItemStack stack = getDisplayedStack(mergedIngredients.get(i).getItems());
+ LytRect lytRect = new LytRect(bounds.x() + SLOT_SIZE * col, bounds.y() + SLOT_SIZE * row, SLOT_SIZE, SLOT_SIZE);
+ context.fillIcon(lytRect, texture);
+ context.renderItem(stack, lytRect.x() + 1, lytRect.y() + 1, ITEM_SIZE, ITEM_SIZE);
+ }
+
+ @Override
+ public Optional getTooltip(float mouseX, float mouseY) {
+ int size = mergedIngredients.size();
+ if (size == 0) return Optional.empty();
+
+ if (size == 1) {
+ return Optional.of(new ItemTooltip(getDisplayedStack(mergedIngredients.getFirst().getItems())));
+ } else {
+ int cols;
+ if (size <= 4) {
+ cols = 2;
+ } else {
+ cols = 3;
+ }
+
+ int localMouseX = (int) (mouseX - bounds.x());
+ int localMouseY = (int) (mouseY - bounds.y());
+
+ if (localMouseX < 0 || localMouseY < 0) {
+ return Optional.empty();
+ }
+
+ int col = localMouseX / 18;
+ int row = localMouseY / 18;
+
+ int index = row * cols + col;
+
+ if (index < size) {
+ return Optional.of(new ItemTooltip(getDisplayedStack(mergedIngredients.get(index).getItems())));
+ }
+ }
+ return InteractiveElement.super.getTooltip(mouseX, mouseY);
+ }
+
+ private ItemStack getDisplayedStack(ItemStack[] stacks) {
+ if (stacks.length == 0) {
+ return ItemStack.EMPTY;
+ }
+ var cycle = System.nanoTime() / TimeUnit.MILLISECONDS.toNanos(CYCLE_TIME);
+ return stacks[(int) (cycle % stacks.length)];
+ }
+
+ @Override
+ protected void onLayoutMoved(int deltaX, int deltaY) {
+
+ }
+
+ @Override
+ public void renderBatch(RenderContext context, MultiBufferSource buffers) {
+
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/slot/LytOutputItemSlot.java b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytOutputItemSlot.java
new file mode 100644
index 0000000..7811825
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytOutputItemSlot.java
@@ -0,0 +1,135 @@
+package dev.anvilcraft.guideme.guide.slot;
+
+import dev.anvilcraft.guideme.guide.tooltip.ChanceItemTooltip;
+import dev.anvilcraft.guideme.util.NumberUtil;
+import dev.anvilcraft.lib.recipe.component.ChanceItemStack;
+import guideme.document.LytRect;
+import guideme.document.block.LytBlock;
+import guideme.document.interaction.GuideTooltip;
+import guideme.document.interaction.InteractiveElement;
+import guideme.layout.LayoutContext;
+import guideme.render.GuiAssets;
+import guideme.render.GuiSprite;
+import guideme.render.RenderContext;
+import lombok.Getter;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.List;
+import java.util.Optional;
+
+public class LytOutputItemSlot extends LytBlock implements InteractiveElement {
+ private static final int ITEM_SIZE = 16;
+ private static final int SLOT_SIZE = 18;
+
+ @Getter
+ private final List resultItems;
+
+ public LytOutputItemSlot(List resultItems) {
+ this.resultItems = resultItems;
+ }
+
+ @Override
+ protected LytRect computeLayout(LayoutContext context, int x, int y, int availableWidth) {
+ int size = resultItems.size();
+ if (size <= 1) {
+ return new LytRect(x + 32, y + 16, SLOT_SIZE, SLOT_SIZE);
+ } else if (size <= 4) {
+ return new LytRect(x + 24, y + 12, SLOT_SIZE * 2, SLOT_SIZE * 2);
+ } else if (size <= 6) {
+ return new LytRect(x + 8, y + 8, SLOT_SIZE * 3, SLOT_SIZE * 2);
+ } else {
+ return new LytRect(x + 8, y + 4, SLOT_SIZE * 3, SLOT_SIZE * 3);
+ }
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ if (resultItems.isEmpty()) return;
+ GuiSprite texture = GuiAssets.SLOT;
+ var x = bounds.x();
+ var y = bounds.y();
+ int size = resultItems.size();
+ if (size == 1) {
+ ItemStack stack = resultItems.getFirst().stack().copy();
+ double count = NumberUtil.getMax(resultItems.getFirst().count());
+ stack.setCount((int) count);
+ LytRect lytRect = new LytRect(bounds.x(), bounds.y(), SLOT_SIZE, SLOT_SIZE);
+ context.renderItem(stack, x + 1, y + 1, ITEM_SIZE, ITEM_SIZE);
+ context.fillIcon(lytRect, texture);
+ } else if (size <= 4) {
+ for (int i = 0; i < size; i++) {
+ int row = i / 2;
+ int col = i % 2;
+ getItemStack(context, texture, i, row, col);
+ }
+ } else if (size <= 6) {
+ for (int i = 0; i < size; i++) {
+ int row = i / 3;
+ int col = i % 3;
+ getItemStack(context, texture, i, row, col);
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ if (i > 9) break;
+ int row = i / 3;
+ int col = i % 3;
+ getItemStack(context, texture, i, row, col);
+ }
+ }
+ }
+
+ private void getItemStack(RenderContext context, GuiSprite texture, int i, int row, int col) {
+ ItemStack stack = resultItems.get(i).stack().copy();
+ double count = NumberUtil.getMax(resultItems.get(i).count());
+ stack.setCount((int) count);
+ LytRect lytRect = new LytRect(bounds.x() + SLOT_SIZE * col, bounds.y() + SLOT_SIZE * row, SLOT_SIZE, SLOT_SIZE);
+ context.fillIcon(lytRect, texture);
+ context.renderItem(stack, lytRect.x() + 1, lytRect.y() + 1, ITEM_SIZE, ITEM_SIZE);
+ }
+
+ @Override
+ public Optional getTooltip(float mouseX, float mouseY) {
+ if (resultItems.isEmpty()) return Optional.empty();
+
+ int size = resultItems.size();
+
+ if (size == 1) {
+ return Optional.of(new ChanceItemTooltip(resultItems.getFirst()));
+ } else {
+ int cols;
+ if (size <= 4) {
+ cols = 2;
+ } else {
+ cols = 3;
+ }
+
+ int localMouseX = (int) (mouseX - bounds.x());
+ int localMouseY = (int) (mouseY - bounds.y());
+
+ if (localMouseX < 0 || localMouseY < 0) {
+ return Optional.empty();
+ }
+
+ int col = localMouseX / 18;
+ int row = localMouseY / 18;
+
+ int index = row * cols + col;
+
+ if (index < size) {
+ return Optional.of(new ChanceItemTooltip(resultItems.get(index)));
+ }
+ }
+ return InteractiveElement.super.getTooltip(mouseX, mouseY);
+ }
+
+ @Override
+ protected void onLayoutMoved(int deltaX, int deltaY) {
+
+ }
+
+ @Override
+ public void renderBatch(RenderContext context, MultiBufferSource buffers) {
+
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/slot/LytSimpleItemSlot.java b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytSimpleItemSlot.java
new file mode 100644
index 0000000..180c1f4
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/slot/LytSimpleItemSlot.java
@@ -0,0 +1,78 @@
+package dev.anvilcraft.guideme.guide.slot;
+
+import guideme.document.LytRect;
+import guideme.document.block.LytBlock;
+import guideme.document.interaction.GuideTooltip;
+import guideme.document.interaction.InteractiveElement;
+import guideme.document.interaction.ItemTooltip;
+import guideme.layout.LayoutContext;
+import guideme.render.RenderContext;
+import lombok.Getter;
+import lombok.Setter;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.Ingredient;
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+public class LytSimpleItemSlot extends LytBlock implements InteractiveElement {
+ private static final int CYCLE_TIME = 2000;
+
+ @Setter
+ @Getter
+ private boolean largeSlot;
+ @Setter
+ @Getter
+ private int itemSize = 16;
+
+ private final ItemStack[] stacks;
+
+ public LytSimpleItemSlot(Ingredient ingredient) {
+ this.stacks = ingredient.getItems();
+ }
+
+ public LytSimpleItemSlot(ItemStack stack) {
+ this.stacks = new ItemStack[]{stack};
+ }
+
+ @Override
+ protected LytRect computeLayout(LayoutContext context, int x, int y, int availableWidth) {
+ return new LytRect(x, y, itemSize, itemSize);
+ }
+
+ @Override
+ protected void onLayoutMoved(int deltaX, int deltaY) {
+ }
+
+ @Override
+ public void renderBatch(RenderContext context, MultiBufferSource buffers) {
+
+ }
+
+ @Override
+ public void render(RenderContext context) {
+ var stack = getDisplayedStack();
+ if (!stack.isEmpty()) {
+ context.renderItem(stack, bounds.x(), bounds.y(), 1, itemSize, itemSize);
+ }
+ }
+
+ @Override
+ public Optional getTooltip(float x, float y) {
+ var stack = getDisplayedStack();
+ if (stack.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.of(new ItemTooltip(stack));
+ }
+
+ private ItemStack getDisplayedStack() {
+ if (stacks.length == 0) {
+ return ItemStack.EMPTY;
+ }
+
+ var cycle = System.nanoTime() / TimeUnit.MILLISECONDS.toNanos(CYCLE_TIME);
+ return stacks[(int) (cycle % stacks.length)];
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/annotation/package-info.java b/src/main/java/dev/anvilcraft/guideme/guide/slot/package-info.java
similarity index 79%
rename from src/main/java/dev/anvilcraft/guideme/annotation/package-info.java
rename to src/main/java/dev/anvilcraft/guideme/guide/slot/package-info.java
index 7a5ab7c..627c491 100644
--- a/src/main/java/dev/anvilcraft/guideme/annotation/package-info.java
+++ b/src/main/java/dev/anvilcraft/guideme/guide/slot/package-info.java
@@ -1,6 +1,6 @@
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
-package dev.anvilcraft.guideme.annotation;
+package dev.anvilcraft.guideme.guide.slot;
import net.minecraft.MethodsReturnNonnullByDefault;
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/tooltip/ChanceItemTooltip.java b/src/main/java/dev/anvilcraft/guideme/guide/tooltip/ChanceItemTooltip.java
new file mode 100644
index 0000000..bc99c42
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/tooltip/ChanceItemTooltip.java
@@ -0,0 +1,93 @@
+package dev.anvilcraft.guideme.guide.tooltip;
+
+import dev.anvilcraft.lib.recipe.component.ChanceItemStack;
+import dev.anvilcraft.lib.util.NumberProviderUtil;
+import guideme.document.interaction.GuideTooltip;
+import guideme.siteexport.ResourceExporter;
+import net.minecraft.ChatFormatting;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator;
+import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
+import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
+import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import static dev.anvilcraft.guideme.util.NumberUtil.*;
+
+public class ChanceItemTooltip implements GuideTooltip {
+ private static final DecimalFormat FORMATTER = new DecimalFormat();
+
+ private final ChanceItemStack chanceItemStack;
+
+ public ChanceItemTooltip(ChanceItemStack chanceItemStack) {
+ this.chanceItemStack = chanceItemStack;
+ }
+
+ @Override
+ public ItemStack getIcon() {
+ return chanceItemStack.stack();
+ }
+
+ @Override
+ public List getLines() {
+ List list = new ArrayList<>(Screen.getTooltipFromItem(Minecraft.getInstance(), chanceItemStack.stack()));
+
+ NumberProvider provider = chanceItemStack.count();
+ int count = chanceItemStack.stack().getCount();
+
+ if (provider instanceof BinomialDistributionGenerator(NumberProvider n, NumberProvider p)) {
+ if (n instanceof ConstantValue(float value) && value == 1) {
+ String chance = FORMATTER.format(NumberProviderUtil.expected(p) * 100);
+ list.add(Component.translatable("gui.anvilcraft.category.chance", chance)
+ .withStyle(ChatFormatting.GRAY));
+ } else {
+ addAvgOutput(list, count * NumberProviderUtil.expected(provider));
+ }
+ addMinMax(list, 0, getMax(n));
+ } else if (provider.getClass() != ConstantValue.class) {
+ double val = count * NumberProviderUtil.expected(provider);
+ if (val != -1) {
+ addAvgOutput(list, val);
+ if (provider instanceof UniformGenerator) {
+ addMinMax(list, getMin(provider), getMax(provider));
+ }
+ }
+ }
+
+ return list
+ .stream()
+ .map(Component::getVisualOrderText)
+ .map(ClientTooltipComponent::create)
+ .toList();
+ }
+
+ @Override
+ public void exportResources(ResourceExporter exporter) {
+ exporter.referenceItem(chanceItemStack.stack());
+ }
+
+
+
+ private static void addAvgOutput(List tooltipLines, double avgValue) {
+ String avgOutput = FORMATTER.format(avgValue);
+ tooltipLines.add(Component.translatable("gui.anvilcraft.category.average_output", avgOutput)
+ .withStyle(ChatFormatting.GRAY));
+ }
+
+ private static void addMinMax(List tooltipLines, double min, double max) {
+ String minOutput = FORMATTER.format(min);
+ String maxOutput = FORMATTER.format(max);
+
+ tooltipLines.add(Component.translatable("gui.anvilcraft.category.min_output", minOutput)
+ .withStyle(ChatFormatting.GRAY));
+ tooltipLines.add(Component.translatable("gui.anvilcraft.category.max_output", maxOutput)
+ .withStyle(ChatFormatting.GRAY));
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/guide/tooltip/package-info.java b/src/main/java/dev/anvilcraft/guideme/guide/tooltip/package-info.java
new file mode 100644
index 0000000..90e34ef
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/guide/tooltip/package-info.java
@@ -0,0 +1,7 @@
+@MethodsReturnNonnullByDefault
+@ParametersAreNonnullByDefault
+package dev.anvilcraft.guideme.guide.tooltip;
+
+import net.minecraft.MethodsReturnNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/init/item/AddonItems.java b/src/main/java/dev/anvilcraft/guideme/init/item/AddonItems.java
deleted file mode 100644
index fec9bdc..0000000
--- a/src/main/java/dev/anvilcraft/guideme/init/item/AddonItems.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package dev.anvilcraft.guideme.init.item;
-
-import com.tterrag.registrate.util.entry.ItemEntry;
-import dev.dubhe.anvilcraft.util.DataGenUtil;
-import net.minecraft.core.component.DataComponents;
-
-import static dev.anvilcraft.guideme.AnvilCraftGuideME.REGISTRATE;
-
-public class AddonItems {
- public static final ItemEntry GUIDEME_BOOK = REGISTRATE
- .item("guideme_book", GuideMEBookItem::new)
- .model(DataGenUtil::noExtraModelOrState)
- .properties(properties -> properties.stacksTo(1).component(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, true))
- .register();
-
- public static void register() {
- }
-}
diff --git a/src/main/java/dev/anvilcraft/guideme/init/item/GuideMEBookItem.java b/src/main/java/dev/anvilcraft/guideme/init/item/GuideMEBookItem.java
deleted file mode 100644
index d9fddd5..0000000
--- a/src/main/java/dev/anvilcraft/guideme/init/item/GuideMEBookItem.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package dev.anvilcraft.guideme.init.item;
-
-import guideme.GuidesCommon;
-import net.minecraft.world.InteractionHand;
-import net.minecraft.world.InteractionResultHolder;
-import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.item.Item;
-import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.level.Level;
-
-import static dev.anvilcraft.guideme.AnvilCraftGuideME.GID;
-
-public class GuideMEBookItem extends Item {
-
- public GuideMEBookItem(Properties properties) {
- super(properties);
- }
-
- @Override
- public InteractionResultHolder use(Level world, Player player, InteractionHand hand) {
- if (world.isClientSide) {
- GuidesCommon.openGuide(player, GID);
- }
- return InteractionResultHolder.consume(player.getItemInHand(hand));
- }
-}
diff --git a/src/main/java/dev/anvilcraft/guideme/util/BlockStateUtil.java b/src/main/java/dev/anvilcraft/guideme/util/BlockStateUtil.java
new file mode 100644
index 0000000..5960a6d
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/BlockStateUtil.java
@@ -0,0 +1,37 @@
+package dev.anvilcraft.guideme.util;
+
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.anvilcraft.lib.recipe.component.ChanceBlockState;
+import dev.dubhe.anvilcraft.recipe.anvil.predicate.block.HasCauldron;
+import dev.dubhe.anvilcraft.recipe.component.HasCauldronSimple;
+import dev.dubhe.anvilcraft.util.CauldronUtil;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlockStateUtil {
+ public static List ChanceBlockStatesTransToBlockStatePredicates(
+ List chanceBlockStateList
+ ) {
+ List blockStatePredicateList = new ArrayList<>();
+ for (ChanceBlockState chanceBlockState : chanceBlockStateList) {
+ BlockStatePredicate blockStatePredicate = BlockStatePredicate.builder()
+ .of(chanceBlockState.state().getBlock())
+ .nbt(chanceBlockState.nbt())
+ .build();
+ blockStatePredicateList.add(blockStatePredicate);
+ }
+ return blockStatePredicateList;
+ }
+
+ public static BlockState getCauldron(HasCauldronSimple hasCauldron) {
+ boolean isProduceFluid = HasCauldron.isNotEmpty(hasCauldron.transform()) && hasCauldron.consume() < 0;
+ if (isProduceFluid) {
+ return Blocks.CAULDRON.defaultBlockState();
+ } else {
+ return CauldronUtil.fullState(HasCauldron.getDefaultCauldron(hasCauldron.transform()));
+ }
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/util/BlockTransformUtil.java b/src/main/java/dev/anvilcraft/guideme/util/BlockTransformUtil.java
new file mode 100644
index 0000000..2abbad3
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/BlockTransformUtil.java
@@ -0,0 +1,31 @@
+package dev.anvilcraft.guideme.util;
+
+import dev.anvilcraft.lib.recipe.component.ChanceItemStack;
+import dev.anvilcraft.lib.recipe.component.ItemIngredientPredicate;
+import dev.dubhe.anvilcraft.recipe.anvil.collision.BlockTransform;
+import net.minecraft.core.Holder;
+import net.minecraft.world.level.block.Block;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlockTransformUtil {
+ public static List getChanceItemStacks(List transformBlocks) {
+ List itemStacks = new ArrayList<>();
+ for (BlockTransform transform : transformBlocks) {
+ ChanceItemStack itemStack = ChanceItemStack.of(transform.outputBlock().state().getBlock(), transform.outputBlock().chance());
+ itemStacks.add(itemStack);
+ }
+ return itemStacks;
+ }
+
+ public static List getItemIngredientPredicate(List transformBlocks) {
+ List list = new ArrayList<>();
+ for (BlockTransform transform : transformBlocks) {
+ for (Holder block : transform.inputBlock().getBlocks()) {
+ list.add(ItemIngredientPredicate.of(block.value()).build());
+ }
+ }
+ return list;
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/util/GuideMERenderUtil.java b/src/main/java/dev/anvilcraft/guideme/util/GuideMERenderUtil.java
new file mode 100644
index 0000000..ae79f67
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/GuideMERenderUtil.java
@@ -0,0 +1,84 @@
+package dev.anvilcraft.guideme.util;
+
+import dev.anvilcraft.lib.recipe.component.BlockStatePredicate;
+import dev.dubhe.anvilcraft.client.support.RenderSupport;
+import net.minecraft.client.gui.GuiGraphics;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class GuideMERenderUtil {
+ private static float TIME = 0f;
+ private static long LAST_UPDATE = System.nanoTime();
+
+ public static float getAnvilAnimationOffset() {
+ long now = System.nanoTime();
+ float deltaTime = (now - LAST_UPDATE) / 1_000_000_000f;
+ TIME += deltaTime * 4.0f;
+ LAST_UPDATE = now;
+ float cycleTime = (TIME % 10);
+ if (cycleTime >= 2) return 0;
+ double progress = cycleTime / 2;
+ double smoothProgress = progress * progress * (3 - 2 * progress);
+ return (float) (Math.sin(smoothProgress * Math.PI) * 9);
+ }
+
+ public static int getDisplayPage(int size) {
+ var cycle = System.nanoTime() / TimeUnit.MILLISECONDS.toNanos(2000);
+ return ((int) (cycle % size));
+ }
+
+ public static void renderAnvil(GuiGraphics guiGraphics, int x, float y, int z) {
+ RenderSupport.renderBlock(guiGraphics, Blocks.ANVIL.defaultBlockState(), x, y, z, 12, RenderSupport.SINGLE_BLOCK);
+ }
+
+ public static void renderedBlock(GuiGraphics guiGraphics, List list, int x, int y) {
+ int z = 25;
+ List list1 = new ArrayList<>(list);
+ for (int i = list1.size() - 1; i >= 0; i--) {
+ List input = list1.get(i).constructStatesForRender();
+ if (input.isEmpty()) continue;
+ BlockState renderedState = input.get((int) ((System.currentTimeMillis() / 1000) % input.size()));
+ if (renderedState == null) continue;
+ RenderSupport.renderBlock(guiGraphics, renderedState, x, y + 10 * i, z - 10 * i, 12, RenderSupport.SINGLE_BLOCK);
+ }
+ }
+
+ public static void renderedBlockStatesAndAnvilAnimation(
+ GuiGraphics guiGraphics,
+ List list,
+ int startX,
+ int startY
+ ) {
+ int z = 25;
+ List list1 = new ArrayList<>(list);
+ list1.addFirst(BlockStatePredicate.builder().of(Blocks.ANVIL).build());
+ for (int i = list1.size() - 1; i >= 0; i--) {
+ if (i == 0) {
+ renderAnvil(guiGraphics, startX, startY - 9 + getAnvilAnimationOffset(), z);
+ } else {
+ List input = list1.get(i).constructStatesForRender();
+ if (input.isEmpty()) continue;
+ BlockState renderedState = input.get(getDisplayPage(input.size()));
+ if (renderedState == null) continue;
+ RenderSupport.renderBlock(guiGraphics, renderedState, startX, startY + 10 * i, z - 10 * i, 12, RenderSupport.SINGLE_BLOCK);
+ }
+ }
+ }
+
+ public static void renderedBlockStatesAndAnvil(GuiGraphics guiGraphics, List list, int startX, int startY) {
+ int z = 25;
+ List list1 = new ArrayList<>(list);
+ list1.addFirst(BlockStatePredicate.builder().of(Blocks.ANVIL).build());
+ for (int i = list1.size() - 1; i >= 0; i--) {
+ List input = list1.get(i).constructStatesForRender();
+ if (input.isEmpty()) continue;
+ BlockState renderedState = input.get((int) ((System.currentTimeMillis() / 1000) % input.size()));
+ if (renderedState == null) continue;
+ RenderSupport.renderBlock(guiGraphics, renderedState, startX, startY + 10 * i, z - 10 * i, 12, RenderSupport.SINGLE_BLOCK);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/util/IntegrationUtil.java b/src/main/java/dev/anvilcraft/guideme/util/IntegrationUtil.java
new file mode 100644
index 0000000..fbe3787
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/IntegrationUtil.java
@@ -0,0 +1,20 @@
+package dev.anvilcraft.guideme.util;
+
+import net.neoforged.fml.loading.LoadingModList;
+import net.neoforged.fml.loading.moddiscovery.ModFileInfo;
+
+import javax.annotation.Nullable;
+
+public class IntegrationUtil {
+ public static @Nullable String getVersion(String modId) {
+ ModFileInfo info = LoadingModList.get().getModFileById(modId);
+ if (info == null) return null;
+ return info.versionString();
+ }
+
+ public static @Nullable String getName(String modId) {
+ ModFileInfo info = LoadingModList.get().getModFileById(modId);
+ if (info == null) return null;
+ return info.moduleName();
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/util/NumberUtil.java b/src/main/java/dev/anvilcraft/guideme/util/NumberUtil.java
new file mode 100644
index 0000000..f5b6255
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/NumberUtil.java
@@ -0,0 +1,26 @@
+package dev.anvilcraft.guideme.util;
+
+import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator;
+import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
+import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
+import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
+
+public class NumberUtil {
+
+ public static double getMin(NumberProvider provider) {
+ return switch (provider) {
+ case ConstantValue value -> value.value();
+ case UniformGenerator uniform -> getMin(uniform.min());
+ default -> 0;
+ };
+ }
+
+ public static double getMax(NumberProvider provider) {
+ return switch (provider) {
+ case ConstantValue value -> value.value();
+ case UniformGenerator uniform -> getMax(uniform.max());
+ case BinomialDistributionGenerator binomial -> getMax(binomial.n());
+ default -> 0;
+ };
+ }
+}
diff --git a/src/main/java/dev/anvilcraft/guideme/util/TextureConstants.java b/src/main/java/dev/anvilcraft/guideme/util/TextureConstants.java
new file mode 100644
index 0000000..029694d
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/TextureConstants.java
@@ -0,0 +1,20 @@
+package dev.anvilcraft.guideme.util;
+
+import dev.anvilcraft.guideme.AnvilCraftGuideME;
+import guideme.render.GuiSprite;
+
+import static guideme.render.GuiAssets.sprite;
+
+public final class TextureConstants {
+ // Background
+ public static final GuiSprite HEAVY_IRON_1 = sprite(AnvilCraftGuideME.of("background/heavy_iron_1"));
+ public static final GuiSprite HEAVY_IRON_2 = sprite(AnvilCraftGuideME.of("background/heavy_iron_2"));
+ public static final GuiSprite HEAVY_IRON_3 = sprite(AnvilCraftGuideME.of("background/heavy_iron_3"));
+ public static final GuiSprite ROYAL = sprite(AnvilCraftGuideME.of("background/royal"));
+ public static final GuiSprite EMBER = sprite(AnvilCraftGuideME.of("background/ember"));
+ public static final GuiSprite MULTIPLE_TO_ONE_SMITHING = sprite(AnvilCraftGuideME.of("background/multiple_to_one_smithing_guideme"));
+ public static final GuiSprite STONE = sprite(AnvilCraftGuideME.of("background/stone"));
+
+ public static final GuiSprite CONVERSION = sprite(AnvilCraftGuideME.of("arrow/block_conversion"));
+ public static final GuiSprite EXPLOSION = sprite(AnvilCraftGuideME.of("explosion"));
+}
\ No newline at end of file
diff --git a/src/main/java/dev/anvilcraft/guideme/util/package-info.java b/src/main/java/dev/anvilcraft/guideme/util/package-info.java
new file mode 100644
index 0000000..9f8c110
--- /dev/null
+++ b/src/main/java/dev/anvilcraft/guideme/util/package-info.java
@@ -0,0 +1,7 @@
+@MethodsReturnNonnullByDefault
+@ParametersAreNonnullByDefault
+package dev.anvilcraft.guideme.util;
+
+import net.minecraft.MethodsReturnNonnullByDefault;
+
+import javax.annotation.ParametersAreNonnullByDefault;
\ No newline at end of file
diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg
index 2b7dc05..3acef67 100644
--- a/src/main/resources/META-INF/accesstransformer.cfg
+++ b/src/main/resources/META-INF/accesstransformer.cfg
@@ -123,3 +123,12 @@ public net.minecraft.world.level.block.FallingBlock falling(Lnet/minecraft/world
public net.minecraft.world.entity.projectile.AbstractArrow piercingIgnoreEntityIds # piercingIgnoreEntityIds
public net.minecraft.world.entity.projectile.AbstractArrow life # life
+
+public net.minecraft.world.item.crafting.SmithingTransformRecipe template
+public net.minecraft.world.item.crafting.SmithingTransformRecipe base
+public net.minecraft.world.item.crafting.SmithingTransformRecipe addition
+public net.minecraft.world.item.crafting.SmithingTransformRecipe result
+
+public net.minecraft.world.item.crafting.SmithingTrimRecipe template
+public net.minecraft.world.item.crafting.SmithingTrimRecipe base
+public net.minecraft.world.item.crafting.SmithingTrimRecipe addition
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/guideme.compiler.tags.RecipeTypeMappingSupplier b/src/main/resources/META-INF/services/guideme.compiler.tags.RecipeTypeMappingSupplier
new file mode 100644
index 0000000..fe73f58
--- /dev/null
+++ b/src/main/resources/META-INF/services/guideme.compiler.tags.RecipeTypeMappingSupplier
@@ -0,0 +1 @@
+dev.anvilcraft.guideme.guide.RecipeTypeContributions
\ No newline at end of file
diff --git a/src/main/resources/assets/anvilcraft_guideme/lang/zh_cn.json b/src/main/resources/assets/anvilcraft_guideme/lang/zh_cn.json
new file mode 100644
index 0000000..d03aa3c
--- /dev/null
+++ b/src/main/resources/assets/anvilcraft_guideme/lang/zh_cn.json
@@ -0,0 +1,4 @@
+{
+ "gui.ac_guideme.loaded": "%s 已加载!",
+ "gui.ac_guideme.unloaded": "%s 未加载!"
+}
\ No newline at end of file
diff --git a/src/main/resources/assets/anvilcraft_guideme/models/item/guideme_book.json b/src/main/resources/assets/anvilcraft_guideme/models/item/guideme_book.json
deleted file mode 100644
index 40ff976..0000000
--- a/src/main/resources/assets/anvilcraft_guideme/models/item/guideme_book.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "parent": "anvilcraft:item/guide_book"
-}
\ No newline at end of file
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/arrow/block_conversion.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/arrow/block_conversion.png
new file mode 100644
index 0000000..61856ee
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/arrow/block_conversion.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/echo.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/echo.png
new file mode 100644
index 0000000..1969ddc
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/echo.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/ember.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/ember.png
new file mode 100644
index 0000000..6526659
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/ember.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_1.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_1.png
new file mode 100644
index 0000000..5e65582
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_1.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_2.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_2.png
new file mode 100644
index 0000000..867af0b
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_2.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_3.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_3.png
new file mode 100644
index 0000000..7b7833e
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/heavy_iron_3.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/multiple_to_one_smithing_guideme.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/multiple_to_one_smithing_guideme.png
new file mode 100644
index 0000000..2010062
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/multiple_to_one_smithing_guideme.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/royal.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/royal.png
new file mode 100644
index 0000000..2332535
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/royal.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/shulker.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/shulker.png
new file mode 100644
index 0000000..01da9fa
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/shulker.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/stone.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/stone.png
new file mode 100644
index 0000000..f9c9b0d
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/stone.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/transcendence.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/transcendence.png
new file mode 100644
index 0000000..28fde8c
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/background/transcendence.png differ
diff --git a/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/explosion.png b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/explosion.png
new file mode 100644
index 0000000..9900bea
Binary files /dev/null and b/src/main/resources/assets/anvilcraft_guideme/textures/gui/sprites/explosion.png differ
diff --git a/src/main/resources/pack.png b/src/main/resources/pack.png
new file mode 100644
index 0000000..d1e9300
Binary files /dev/null and b/src/main/resources/pack.png differ
diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml
index df2d7c3..11158da 100644
--- a/src/main/templates/META-INF/neoforge.mods.toml
+++ b/src/main/templates/META-INF/neoforge.mods.toml
@@ -7,6 +7,7 @@ modId = "${mod_id}"
version = "${mod_version}"
displayName = "${mod_name}"
displayURL = "https://docs.anvilcraft.dev/"
+logoFile="pack.png"
authors = "${mod_authors}"
description = '''${mod_description}'''
@@ -30,7 +31,7 @@ side = "BOTH"
[[dependencies."${mod_id}"]]
modId = "anvilcraft"
type = "required"
-versionRange = "[1.4.11,)"
+versionRange = "[1.5.0,)"
ordering = "NONE"
side = "BOTH"