特殊公开接口
AuraAccess
展示架、容器等方块实体实现的灵气存取接口:按整单位交换某一种灵气(insert/extract 一次只处理一种,返回操作后没能移动的数量;simulate=true 只模拟,不修改状态),容量由 getCapacity(entity) 给出。
参数是 Holder<Aura> 而不是 resource:resource 只是一套数值系统(边界/图标/资源条),它不知道自己这个数是干什么用的;aura 才是"哪一种灵气"的身份,它引用一个 resource 作为自己被计量的单位(Aura.resource() = "我用哪个数计量")。所以凡是"哪一种灵气"的字段、参数与存储键都用 Holder<Aura>——存取接口、物品与方块存储、环境灵气池、item_aura.type 都是。反过来说,纯计数器没有灵气身份,因而不能被存入物品(它能进玩家池子,因为池子按值开键)。语义边界见 research/audit/resource-cultivation-split.md §7.3/§7.4。
ItemAuraAccess
可充能物品实现的存储接口,只回答三件事:能装哪些灵气(getCapacity)、装进去多少(insert)、取出多少(extract)。除灵气、增加/抽取和模拟参数外,通过 getCapacity(LivingEntity, ItemStack) 从物品动态计算容量,不能在 Java 中写死容量;装的是哪种灵气与装了多少由 mxt:spirit_storage 组件记在物品自己身上(一张「灵气 → 已存单位」的表,键是 Holder<Aura>,灵石与符箓载体共用),SpiritStoneItem 因此只把定义当容量来源,不会因为数据包改了 item_aura.type 就把已有存量改读成另一种灵气。缺组件对灵石读作"满"、对符箓读作"空",这条由物品回答而不是由组件回答——组件只是一份数据。
存取被问在任何地方:展示架读写一张符、热键栏读一件物品、item_aura 定义描述一个物品,用的都是这个接口。所以只实现它的物品是被存进去的,不会被"按住右键灌"——那个手势是物品额外选择加入的,见下。
UseItemAuraAccess
让物品按住右键灌注灵气的接口,继承 ItemAuraAccess:HoldBinding/HoldService 负责手势与姿势,SpiritChargeService 每 tick 从持有者自己的灵气池取出并写入 insert。实现它不代表要自己描述形状——灵石就实现了它,却把 pour 留空,于是走 item_aura 定义那条共享读法。它表达的是"这个物品可以被按住灌",不是"这个物品是特殊的"。不实现它的存储(比如只用来存东西的通用物品)永远不会被武装成手势。
拆成两个接口是因为"存储"与"被灌"不是一回事:存储能被问在任何地方,被灌只发生在一个人对着自己手里那一堆做手势的时候。三个默认方法对应手势的三个时刻:
pour(registries, stack)(手势开始前、之后每 tick)——这个容器是什么:按灵气分列的SpiritPour(每种灵气已存多少、上限多少,顺序即灌注顺序),数值按整堆给出、不再乘堆叠。默认返回空 = "我没什么特别的",于是回落到item_aura定义那条共享读法(灵石走这条)。只有容量取决于这一堆上写了什么的物品(符箓载体)才覆写它。两侧都会问(客户端靠它算手势长度),所以实现只能读传进来的Provider,且同一个 stack 必须答得一样。速率与代价不在这里,那是手势的(定义路线按两个速度反向使用,自述存储按 1 单位/tick、1:1)。canPourInto(holder, stack)(每 tick,付灵气之前)——这一 tick 值不值得灌。手势的顺序是"先扣灵气、再insert、最后onCharged",所以只要有"插进去也没意义"的情况就会白花灵气;这个方法让物品在付钱前否掉。默认true(只进不出的容器没有二话可说),只有会因被灌满而自焚发动的物品覆写它——符箓覆写成TalismanService.canFireFrom,即"这个持有者的冷却窗口还开着吗"。注意它不是"要不要自动发动"那条:那由载体自己的模式决定(mxt:talisman组件的mode,fire灌满即发动/store只积累),跟灌注闸门不是一回事。onCharged(source, stack)(一次真实移动之后)——"我被灌了",由物品自己决定是不是满了、要不要动手。默认什么都不做。
写入者负责汇报:任何往存储里写入灵气的一方(长按灌注、AuraAccess 方块实体等)在真实写入之后调用 onCharged(SpiritSource, stack)(simulate 不算),由物品自己判断"这是不是满了"以及随之而来的行为(符箓在这里发动并消耗一件本体)。之所以由写入者汇报、而不是让物品在自己的 add 里判断,是因为只有写入者知道这东西在哪、谁付的账:展示架上的一张符,是被站在别处的人(或一枚灵爆)填满的。SpiritSource(level, position, actor, consumedByHand) 同时带着位置与行为者——行为者出账、被记录并为能力作答;位置是这次激发的地点,既以 block_x/block_y/block_z 进公式,也作为原点交给位置类行为;consumedByHand 说明这次是不是"手上的消耗"(展示架、机器等摆着的存储为 false)(见 灌注与激发)。也正因为汇报是"选择加入"的:写入方遇到只实现存储的物品时,本就没有什么可汇报的。
TooltipAppender
物品模块通过 NeoForge TooltipAppender 注册 Tooltip。每个模块使用独立 Appender,资源、货币、品质和灵气存储显示互不耦合。
Cost
技能、阵法和其他行为的消耗抽象,提供面向 Player 的检查和实际消耗方法。新增 Cost 类型应使用固有注册表分派,而不是在 JSON 中写 Java 类名。
HotbarEntry
纯客户端条目接口,提供名称、可选图标、强调色和 onPress、onPressTick(Player)、onRelease 回调,以及 cooldown(Player) / canPress(Player) 两个冷却钩子;render(...) 可以整体覆写。