烧烤 动物养殖 分类 功能实现1
This commit is contained in:
1 parent
98335837dd
commit
eac9409571
16 files changed
+1493
-5
No files matched your search
@@ -1,6 +1,9 @@
|
||||
from BlueFoxTools.ModLoader import *
|
||||
from xiangyemod.service.CookingService import CookingService
|
||||
from xiangyemod.service.BlockSwitchService import BlockSwitchService
|
||||
from xiangyemod.service.FeatureDispatcher import FeatureDispatcher
|
||||
from xiangyemod.dongwuyangzhi.service.AnimalService import AnimalService
|
||||
from xiangyemod.shaokao.service.BarbecueService import BarbecueService
|
||||
|
||||
if not serverApi.IsInServer():
|
||||
pass
|
||||
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
动物养殖方块转换规则数据
|
||||
================================================================================
|
||||
所有"动物养殖"分类下"右键方块→消耗物品→替换方块"的转换规则都在这里。
|
||||
只被 AnimalService 读取,FeatureDispatcher 通过方块名集合路由到 AnimalService,
|
||||
不会去查其他功能(烧烤/做菜/工具切换)的数据库。
|
||||
|
||||
与 BlockSwitchData.py 的关系:
|
||||
BlockSwitchData.py = 通用工具方块切换(箭桶、挂肉钩、水槽、书柜等)
|
||||
AnimalData.py = 动物养殖专属(鸡窝、饲料盆、饲料槽、粪便等)
|
||||
|
||||
字段说明(与 BlockSwitchData 基本一致,新增 giveCount/dropItem):
|
||||
source: 被右键的方块ID(必填)
|
||||
item: 玩家手里必须拿的物品,None=空手也行
|
||||
target: 替换成什么方块,"minecraft:air"=消失
|
||||
consume: 消耗物品数量,0=不消耗(默认1)
|
||||
giveItem: 额外给玩家什么物品,None=不给
|
||||
giveCount: giveItem 的数量,默认1
|
||||
dropItem: 转换时以掉落物形式生成的物品列表,优先于 giveItem
|
||||
格式: [{"name": "xxx", "count": N}, ...]
|
||||
做成掉落物而不是直接塞背包,符合"取下/获得"的体验
|
||||
type: "switch"=右键切换, "timer"=计时自动转换
|
||||
time: 计时型转换需要多少秒(到时间自动替换方块)
|
||||
================================================================================
|
||||
"""
|
||||
|
||||
ANIMAL_SWITCHES = [
|
||||
|
||||
# ============================================================
|
||||
# 一、鸡窝(产蛋) —— 低级机制:手持鸡蛋右键放置递增
|
||||
# 对应方块:chickencoop_0(0蛋) ~ chickencoop_5(5蛋)
|
||||
# ============================================================
|
||||
# 【放蛋1】鸡窝_1(0蛋) → 鸡窝_2(1蛋)
|
||||
{
|
||||
"source": "rtdd:chickencoop_0",
|
||||
"item": "minecraft:egg",
|
||||
"target": "rtdd:chickencoop_1",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【放蛋2】鸡窝_2(1蛋) → 鸡窝_3(2蛋)
|
||||
{
|
||||
"source": "rtdd:chickencoop_1",
|
||||
"item": "minecraft:egg",
|
||||
"target": "rtdd:chickencoop_2",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【放蛋3】鸡窝_3(2蛋) → 鸡窝_4(3蛋)
|
||||
{
|
||||
"source": "rtdd:chickencoop_2",
|
||||
"item": "minecraft:egg",
|
||||
"target": "rtdd:chickencoop_3",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【放蛋4】鸡窝_4(3蛋) → 鸡窝_5(4蛋)
|
||||
{
|
||||
"source": "rtdd:chickencoop_3",
|
||||
"item": "minecraft:egg",
|
||||
"target": "rtdd:chickencoop_4",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【放蛋5】鸡窝_5(4蛋) → 鸡窝_6(5蛋)
|
||||
{
|
||||
"source": "rtdd:chickencoop_4",
|
||||
"item": "minecraft:egg",
|
||||
"target": "rtdd:chickencoop_5",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 二、鸡窝(取蛋) —— 空手右键取走鸡蛋,以掉落物形式生成
|
||||
# 数量转换遵循"递增→递减"双向规则:放蛋1→2→3→4→5,取蛋5→4→3→2→1
|
||||
# 取蛋后一律回到0蛋形态(chickencoop_0)
|
||||
# ============================================================
|
||||
# 【取5蛋】鸡窝_6(5蛋) → 鸡窝_1(0蛋),掉落5个鸡蛋
|
||||
{
|
||||
"source": "rtdd:chickencoop_5",
|
||||
"item": None,
|
||||
"target": "rtdd:chickencoop_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "minecraft:egg", "count": 5}],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【取4蛋】鸡窝_5(4蛋) → 鸡窝_1(0蛋),掉落4个鸡蛋
|
||||
{
|
||||
"source": "rtdd:chickencoop_4",
|
||||
"item": None,
|
||||
"target": "rtdd:chickencoop_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "minecraft:egg", "count": 4}],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【取3蛋】鸡窝_4(3蛋) → 鸡窝_1(0蛋),掉落3个鸡蛋
|
||||
{
|
||||
"source": "rtdd:chickencoop_3",
|
||||
"item": None,
|
||||
"target": "rtdd:chickencoop_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "minecraft:egg", "count": 3}],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【取2蛋】鸡窝_3(2蛋) → 鸡窝_1(0蛋),掉落2个鸡蛋
|
||||
{
|
||||
"source": "rtdd:chickencoop_2",
|
||||
"item": None,
|
||||
"target": "rtdd:chickencoop_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "minecraft:egg", "count": 2}],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【取1蛋】鸡窝_2(1蛋) → 鸡窝_1(0蛋),掉落1个鸡蛋
|
||||
{
|
||||
"source": "rtdd:chickencoop_1",
|
||||
"item": None,
|
||||
"target": "rtdd:chickencoop_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "minecraft:egg", "count": 1}],
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 三、鸡饲料盆 —— 手持种子右键填充,计时变空
|
||||
# 对应方块:chickenmanger_0(空)、chickenmanger_1(满)
|
||||
# ============================================================
|
||||
# 【加饲料】鸡饲料盆_1(空) → 鸡饲料盆_2(满),消耗16个种子
|
||||
{
|
||||
"source": "rtdd:chickenmanger_0",
|
||||
"item": "minecraft:wheat_seeds",
|
||||
"target": "rtdd:chickenmanger_1",
|
||||
"consume": 16,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时变空】鸡饲料盆_2(满) → 鸡饲料盆_1(空),计时60秒
|
||||
# 这是一个自动转换:玩家填充后启动计时,到时间自动变空
|
||||
{
|
||||
"source": "rtdd:chickenmanger_1",
|
||||
"item": None,
|
||||
"target": "rtdd:chickenmanger_0",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 四、饲料槽-小麦 —— 手持小麦右键填充(消耗32个),计时逐级减少
|
||||
# 对应方块:manger_0(空)、manger_3(满小麦)、manger_2(半量小麦)、manger_1(少量小麦)
|
||||
# ============================================================
|
||||
# 【加小麦】饲料槽_1(空) → 饲料槽_2(满小麦),消耗32个小麦
|
||||
{
|
||||
"source": "rtdd:manger_0",
|
||||
"item": "minecraft:wheat",
|
||||
"target": "rtdd:manger_3",
|
||||
"consume": 32,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时减少1】饲料槽_2(满小麦) → 饲料槽_3(半量小麦),计时60秒
|
||||
{
|
||||
"source": "rtdd:manger_3",
|
||||
"item": None,
|
||||
"target": "rtdd:manger_2",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时减少2】饲料槽_3(半量小麦) → 饲料槽_4(少量小麦),计时60秒
|
||||
{
|
||||
"source": "rtdd:manger_2",
|
||||
"item": None,
|
||||
"target": "rtdd:manger_1",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时减少3】饲料槽_4(少量小麦) → 饲料槽_1(空),计时60秒
|
||||
{
|
||||
"source": "rtdd:manger_1",
|
||||
"item": None,
|
||||
"target": "rtdd:manger_0",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 五、饲料槽-水 —— 手持水桶右键填充,水桶变空桶
|
||||
# 对应方块:manger_0(空)、mangerwater(水槽)
|
||||
# ============================================================
|
||||
# 【加水】饲料槽_1(空) → 饲料槽_6(水槽),消耗水桶,获得空桶
|
||||
{
|
||||
"source": "rtdd:manger_0",
|
||||
"item": "minecraft:water_bucket",
|
||||
"target": "rtdd:mangerwater",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": "minecraft:bucket",
|
||||
"giveCount": 1,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 六、饲料槽-胡萝卜 —— 手持胡萝卜右键填充(消耗32个),计时逐级减少
|
||||
# 对应方块:manger_0(空)、pigmanger_1(满胡萝卜)、pigmanger_2(半量胡萝卜)
|
||||
# ============================================================
|
||||
# 【加胡萝卜】饲料槽_1(空) → 饲料槽_7(满胡萝卜),消耗32个胡萝卜
|
||||
{
|
||||
"source": "rtdd:manger_0",
|
||||
"item": "minecraft:carrot",
|
||||
"target": "rtdd:pigmanger_1",
|
||||
"consume": 32,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时减少1】饲料槽_7(满胡萝卜) → 饲料槽_8(半量胡萝卜),计时60秒
|
||||
{
|
||||
"source": "rtdd:pigmanger_1",
|
||||
"item": None,
|
||||
"target": "rtdd:pigmanger_2",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【计时减少2】饲料槽_8(半量胡萝卜) → 饲料槽_1(空),计时60秒
|
||||
{
|
||||
"source": "rtdd:pigmanger_2",
|
||||
"item": None,
|
||||
"target": "rtdd:manger_0",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 七、粪便清理 —— 空手右键逐级清理,最后消失
|
||||
# 对应方块:feces_3(一坨)、feces_2(半坨)、feces_1(仅痕迹)
|
||||
# 【食用部分后续在总食用功能中实现,本次不实现】
|
||||
# ============================================================
|
||||
# 【清理1】粪便(一坨) → 粪便(半坨)
|
||||
{
|
||||
"source": "rtdd:feces_3",
|
||||
"item": None,
|
||||
"target": "rtdd:feces_2",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【清理2】粪便(半坨) → 粪便(仅痕迹)
|
||||
{
|
||||
"source": "rtdd:feces_2",
|
||||
"item": None,
|
||||
"target": "rtdd:feces_1",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【清理3】粪便(仅痕迹) → 消失(空气)
|
||||
{
|
||||
"source": "rtdd:feces_1",
|
||||
"item": None,
|
||||
"target": "minecraft:air",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
]
|
||||
Whitespace-only changes.
+20
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
动物养殖客户端(预留)
|
||||
=====================
|
||||
目前动物养殖功能的服务端逻辑已由 AnimalService + FeatureDispatcher 处理,
|
||||
客户端暂不需要额外的 UI 工作。
|
||||
如果以后需要饲料余量提示、产蛋进度条等 UI,可以在这里扩展。
|
||||
"""
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
ClientSystem = clientApi.GetClientSystemCls()
|
||||
|
||||
|
||||
class AnimalClient(ClientSystem):
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
print "===== AnimalClient init ====="
|
||||
|
||||
def Destroy(self):
|
||||
print "===== AnimalClient Destroy ====="
|
||||
Whitespace-only changes.
+332
@@ -0,0 +1,332 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
动物养殖引擎(服务端)
|
||||
================================================================================
|
||||
只处理"动物养殖"(xy_animal) 分类下的方块转换:鸡窝、饲料盆、饲料槽、粪便。
|
||||
|
||||
【与 BlockSwitchService / CookingService 的区别】
|
||||
那些系统各自监听 ServerBlockUseEvent,右键时会互相跑一遍拦截。
|
||||
本系统(方案A)改为"统一分发器"架构:
|
||||
- FeatureDispatcher 是唯一监听 ServerBlockUseEvent 的系统
|
||||
- 本类通过 FeatureDispatcher.register(self) 把管辖的方块名注册进去
|
||||
- 玩家右键方块时,FeatureDispatcher 用 O(1) 的 dict 直接找到本实例
|
||||
- 本类只查自己的 AnimalData(ANIMAL_SWITCHES),不碰其他功能的数据库
|
||||
→ 每个右键事件只查一次数据库,功能损耗最小
|
||||
|
||||
【本类不做的事】
|
||||
- 不自己监听 ServerBlockUseEvent(由 FeatureDispatcher 统一转发 handleBlockUse)
|
||||
- 不处理其他分类的方块(数据只来自 AnimalData)
|
||||
|
||||
【转换类型】
|
||||
- type="switch" : 右键直接转换(放蛋/取蛋/填充饲料/清理粪便/加水)
|
||||
- type="timer" : 计时自动转换(饲料盆/饲料槽随时间变空),
|
||||
由本类用 AddTimer 实现,不依赖方块实体 tick
|
||||
================================================================================
|
||||
"""
|
||||
import time
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
# ---- 网易API基础对象 ----
|
||||
ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类
|
||||
comp = serverApi.GetEngineCompFactory() # 组件工厂
|
||||
|
||||
# ---- 继承 BlueFox 框架的系统基类(提供 AddTimer / CreateEngineItemEntity 等方法) ----
|
||||
from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem
|
||||
from ....BlueFoxTools.ToolsConfig import LevelId
|
||||
|
||||
# ---- 只导入动物养殖自己的数据库 ----
|
||||
from ..AnimalData import ANIMAL_SWITCHES
|
||||
|
||||
# ---- 统一分发器(本类把自己注册进去,让分发器把右键事件转发过来) ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
|
||||
class AnimalService(BlueFoxServerSystem):
|
||||
"""
|
||||
动物养殖引擎
|
||||
|
||||
【核心数据结构】
|
||||
animalRecipes = {
|
||||
(方块名, 物品名): 规则dict, # O(1) 查找
|
||||
}
|
||||
animalBlockSet = {方块名, ...} # O(1) 拦截/注册用
|
||||
animalTimers = {方块名: 规则dict} # 计时型规则的起始方块 → 规则
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
"""
|
||||
初始化:构建自己的转换规则字典,并注册到统一分发器
|
||||
"""
|
||||
super(AnimalService, self).__init__(namespace, systemName)
|
||||
print "===== AnimalService init ====="
|
||||
|
||||
# ---- 构建动物养殖规则字典(O(1) 查找) ----
|
||||
self.animalRecipes = {}
|
||||
self.animalBlockSet = set()
|
||||
self.animalTimers = {}
|
||||
for sw in ANIMAL_SWITCHES:
|
||||
key = (sw["source"], sw.get("item"))
|
||||
self.animalRecipes[key] = sw
|
||||
self.animalBlockSet.add(sw["source"])
|
||||
# 收集计时型规则的"起始方块",方便转换后自动启动计时
|
||||
if sw.get("type") == "timer":
|
||||
self.animalTimers[sw["source"]] = sw
|
||||
|
||||
# ---- 计时去重表:key=(dim,x,y,z) -> 上次启动计时的时间戳 ----
|
||||
self._timerSchedule = {}
|
||||
|
||||
# ---- 右键冷却表:防止连点瞬间触发多次 ----
|
||||
self.cooldown = {}
|
||||
|
||||
# ---- 注册到统一分发器(把管辖方块交给 FeatureDispatcher 路由) ----
|
||||
FeatureDispatcher.register(self)
|
||||
|
||||
# ============================================================================
|
||||
# 给 FeatureDispatcher 的接口
|
||||
# ============================================================================
|
||||
|
||||
def getFeatureBlocks(self):
|
||||
"""
|
||||
返回本功能管辖的所有方块名集合。
|
||||
FeatureDispatcher 靠它构建 方块名→本实例 的 O(1) 路由表。
|
||||
"""
|
||||
return self.animalBlockSet
|
||||
|
||||
def getItemUseOnRules(self):
|
||||
"""
|
||||
返回本功能的"手持物品右键方块表面"规则(当前动物养殖没有,返回空)。
|
||||
接口统一预留,方便以后扩展(如手持鸡蛋右键地面直接生蛋等)。
|
||||
"""
|
||||
return {}
|
||||
|
||||
def handleBlockUse(self, args):
|
||||
"""
|
||||
由 FeatureDispatcher 转发的右键方块入口。
|
||||
只有 blockName 在本功能的方块集合里才会被调用。
|
||||
|
||||
返回 True = 本功能处理了这个右键(分发器会取消默认行为)
|
||||
返回 False = 当前组合没有匹配规则(分发器不拦截,交还默认行为)
|
||||
"""
|
||||
blockName = args.get("blockName")
|
||||
playerId = args.get("playerId")
|
||||
x, y, z = args.get("x"), args.get("y"), args.get("z")
|
||||
pos = (x, y, z)
|
||||
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
|
||||
key = (dim, x, y, z)
|
||||
|
||||
# ---- 拿玩家主手物品 ----
|
||||
itemDict = self.getCarriedItem(playerId)
|
||||
|
||||
# ---- 查动物养殖规则(O(1)) ----
|
||||
recipe = self.findRecipe(blockName, itemDict)
|
||||
if not recipe:
|
||||
return False
|
||||
|
||||
# ---- 冷却检查(防止连点,规则可自定义冷却时间 switchCd) ----
|
||||
if not self.checkCooldown(key, recipe.get("switchCd", 0.5)):
|
||||
return True # 冷却中,虽然不执行但也算"处理了",避免默认行为
|
||||
|
||||
# ---- 计时型规则:启动/续计时,不立即转换 ----
|
||||
if recipe.get("type") == "timer":
|
||||
self.startTimer(key, recipe, dim, playerId)
|
||||
return True
|
||||
|
||||
# ---- 切换型规则:执行具体转换 ----
|
||||
self.handleSwitch(recipe, playerId, pos, dim, itemDict, key, blockName)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# 规则匹配(O(1) 字典查找)
|
||||
# ============================================================================
|
||||
|
||||
def findRecipe(self, blockName, itemDict):
|
||||
"""
|
||||
在动物养殖字典中查找匹配规则:
|
||||
1. 先精确匹配 (方块名, 物品名)
|
||||
2. 没找到再匹配 (方块名, None) —— 空手也能触发的规则
|
||||
"""
|
||||
itemName = itemDict.get("newItemName") if itemDict else None
|
||||
recipe = self.animalRecipes.get((blockName, itemName))
|
||||
if recipe:
|
||||
return recipe
|
||||
recipe = self.animalRecipes.get((blockName, None))
|
||||
if recipe:
|
||||
return recipe
|
||||
return None
|
||||
|
||||
# ============================================================================
|
||||
# 切换型规则处理
|
||||
# ============================================================================
|
||||
|
||||
def handleSwitch(self, recipe, playerId, pos, dim, itemDict, key, blockName):
|
||||
"""
|
||||
处理"右键直接转换"类规则。
|
||||
|
||||
正向(item 不为 None):放蛋/加饲料/加水 → 消耗物品 → 替换方块 → 给奖励 → 续计时
|
||||
逆向(item 为 None) :取蛋/清理粪便/取回 → 掉落物品 → 替换方块
|
||||
"""
|
||||
# ---- 正向操作:往方块里放东西 ----
|
||||
if recipe.get("item") is not None:
|
||||
# 消耗物品
|
||||
if recipe.get("consume", 1) > 0:
|
||||
self.consumeItem(playerId, itemDict, recipe.get("consume", 1))
|
||||
|
||||
# 替换方块
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
# 给玩家物品(如加水给空桶)——直接塞背包
|
||||
if recipe.get("giveItem"):
|
||||
self.giveItem(playerId, recipe["giveItem"], recipe.get("giveCount", 1))
|
||||
|
||||
# ★ 如果新方块是某个计时链的起始方块 → 自动启动计时(如刚装满的饲料盆)
|
||||
if recipe.get("target") in self.animalTimers:
|
||||
nextTimer = self.animalTimers[recipe["target"]]
|
||||
self.startTimer(key, nextTimer, dim, playerId)
|
||||
|
||||
return
|
||||
|
||||
# ---- 逆向操作:取东西/清理 ----
|
||||
# 掉落物形式生成(取蛋、取下物品等),优先于 giveItem
|
||||
if recipe.get("dropItem"):
|
||||
self.dropItems(dim, pos, recipe["dropItem"])
|
||||
elif recipe.get("giveItem"):
|
||||
self.giveItem(playerId, recipe["giveItem"], recipe.get("giveCount", 1))
|
||||
|
||||
# 替换方块(可能是空气=消失)
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
# ============================================================================
|
||||
# 计时型规则处理(AddTimer 一次性定时,不依赖方块实体tick)
|
||||
# ============================================================================
|
||||
|
||||
def startTimer(self, key, recipe, dim, playerId=None):
|
||||
"""
|
||||
为计时型规则启动一次性定时器。
|
||||
1 秒内的重复调用会被去重(防止多次右键重复计时)。
|
||||
到时间后由 _timerDone 检查方块状态并执行转换。
|
||||
"""
|
||||
now = time.time()
|
||||
last = self._timerSchedule.get(key, 0)
|
||||
if now - last < 1.0:
|
||||
return
|
||||
self._timerSchedule[key] = now
|
||||
|
||||
t = recipe.get("time", 60)
|
||||
# 用系统自带 AddTimer 一次性定时
|
||||
self.AddTimer(t, self._timerDone, key, recipe, dim)
|
||||
|
||||
def _timerDone(self, key, recipe, dim):
|
||||
"""
|
||||
定时器到点回调:
|
||||
检查当前方块是否仍是计时源方块(防止玩家中途改变了方块),
|
||||
是则替换为下一状态;若新状态仍是计时源则继续下一级计时。
|
||||
"""
|
||||
pos = (key[1], key[2], key[3])
|
||||
try:
|
||||
cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
if not cur or cur.get("name") != recipe["source"]:
|
||||
return # 方块已变,放弃本次转换
|
||||
# 替换方块
|
||||
self.replaceBlock(pos, dim, recipe["target"], None)
|
||||
# 若新方块仍是计时源 → 继续下一级计时(如 满→半→少→空)
|
||||
nextRecipe = self.animalTimers.get(recipe["target"])
|
||||
if nextRecipe:
|
||||
self.startTimer(key, nextRecipe, dim)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:获取玩家主手物品
|
||||
# ============================================================================
|
||||
|
||||
def getCarriedItem(self, playerId):
|
||||
itemComp = comp.CreateItem(playerId)
|
||||
return itemComp.GetPlayerItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0)
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:消耗物品
|
||||
# ============================================================================
|
||||
|
||||
def consumeItem(self, playerId, itemDict, amount=1):
|
||||
if itemDict is None:
|
||||
return
|
||||
count = itemDict.get("count", 0)
|
||||
if count <= amount:
|
||||
comp.CreateItem(playerId).SetEntityItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": "minecraft:air", "count": 0, "auxValue": 0})
|
||||
else:
|
||||
itemDict["count"] = count - amount
|
||||
comp.CreateItem(playerId).SetEntityItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": itemDict["newItemName"],
|
||||
"count": itemDict["count"],
|
||||
"auxValue": itemDict.get("newAuxValue", 0)})
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:替换方块
|
||||
# ============================================================================
|
||||
|
||||
def replaceBlock(self, pos, dim, newBlockName, playerId, aux=None):
|
||||
"""
|
||||
替换指定位置的方块。
|
||||
newBlockName="minecraft:air" 表示清除该方块。
|
||||
playerId 可为 None(计时回调时没有玩家),用 LevelId 创建组件。
|
||||
"""
|
||||
creator = playerId if playerId is not None else LevelId
|
||||
if aux is None:
|
||||
try:
|
||||
oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim)
|
||||
aux = oldBlock.get("aux", 0)
|
||||
except Exception:
|
||||
aux = 0
|
||||
comp.CreateBlockInfo(creator).SetBlockNew(
|
||||
pos,
|
||||
blockDict={"name": newBlockName, "aux": aux},
|
||||
oldBlockHandling=0,
|
||||
dimensionId=dim)
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:给玩家物品(塞背包)
|
||||
# ============================================================================
|
||||
|
||||
def giveItem(self, playerId, itemName, count=1, auxValue=0):
|
||||
comp.CreateItem(playerId).SpawnItemToPlayerInv(
|
||||
{"itemName": itemName, "count": count, "auxValue": auxValue}, playerId)
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:以掉落物形式生成物品(取蛋/取下物品等,符合"获得"体验)
|
||||
# ============================================================================
|
||||
|
||||
def dropItems(self, dim, pos, dropList):
|
||||
"""
|
||||
dropList 格式:[{"name": "minecraft:egg", "count": 5}, ...]
|
||||
在方块上方生成掉落物实体,让它自然掉到地上。
|
||||
"""
|
||||
for d in dropList:
|
||||
itemDict = {
|
||||
"itemName": d["name"],
|
||||
"count": d.get("count", 1),
|
||||
"auxValue": d.get("aux", 0),
|
||||
}
|
||||
self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2]))
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法:冷却检查
|
||||
# ============================================================================
|
||||
|
||||
def checkCooldown(self, key, cd=0.5):
|
||||
"""
|
||||
右键冷却:同一方块 cd 秒内只响应一次,防止连点刷物品。
|
||||
放蛋等需要快速连点的规则可在数据里用 switchCd 调小冷却。
|
||||
"""
|
||||
now = time.time()
|
||||
last = self.cooldown.get(key, 0)
|
||||
if now - last < cd:
|
||||
return False
|
||||
self.cooldown[key] = now
|
||||
return True
|
||||
Whitespace-only changes.
+185
@@ -0,0 +1,185 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
统一事件分发器(服务端)—— 方案A核心
|
||||
================================================================================
|
||||
【为什么需要它】
|
||||
以前每个功能(做菜/工具切换)都各自监听 ServerBlockUseEvent,
|
||||
玩家每次右键,所有系统都会被触发一遍、各自跑一次数据库拦截判断,
|
||||
功能越多损耗越大。
|
||||
|
||||
【它做什么】
|
||||
FeatureDispatcher 是"唯一"监听右键相关事件的服务端系统:
|
||||
- ServerBlockUseEvent 玩家右键方块
|
||||
- ServerItemUseOnEvent 手持物品右键方块表面(如放置生鸡肉)
|
||||
- ServerItemTryUseEvent 拦截喝药水/吃食物等默认行为
|
||||
|
||||
各功能服务在初始化时通过 FeatureDispatcher.register(self) 上报:
|
||||
- 自己管辖的方块名集合 → 构建 方块名→服务 的 O(1) 路由表
|
||||
- 自己处理的手持物品 → 构建 物品名→服务 的 O(1) 路由表
|
||||
|
||||
事件进来后,分发器先用 blockName / itemName 在路由表里 O(1) 定位到
|
||||
唯一的那个功能服务,再调用它的 handleBlockUse / handleItemUseOn。
|
||||
→ 每个右键事件只命中一个数据库,其余功能的数据库根本不会被查询。
|
||||
|
||||
【如何新增一个功能】
|
||||
1. 新建 xxx/xxxData.py 写转换规则
|
||||
2. 新建 xxx/service/XxxService.py(继承 BlueFoxServerSystem):
|
||||
- __init__ 里构建自己的规则字典
|
||||
- 实现 getFeatureBlocks() / getItemUseOnRules()
|
||||
- 实现 handleBlockUse(args) / handleItemUseOn(args)
|
||||
- 调用 FeatureDispatcher.register(self)
|
||||
3. 在 modMain.py import 这个服务类即可
|
||||
================================================================================
|
||||
"""
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
# ---- 网易API基础对象 ----
|
||||
ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类
|
||||
comp = serverApi.GetEngineCompFactory() # 组件工厂
|
||||
|
||||
# ---- 继承 BlueFox 框架的系统基类 ----
|
||||
from ...BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem
|
||||
|
||||
|
||||
class FeatureDispatcher(BlueFoxServerSystem):
|
||||
"""
|
||||
统一事件分发器
|
||||
|
||||
【类级注册表(所有功能服务共享同一份路由表)】
|
||||
blockOwner = {方块名: 功能服务实例}
|
||||
blockSet = {方块名} # 全部功能方块集合
|
||||
itemUseOnOwner = {物品名: 功能服务实例}
|
||||
itemUseOnSet = {物品名} # 全部"手持物品右键方块表面"集合
|
||||
"""
|
||||
|
||||
# ---- 类级共享路由表(用类变量,任何实例都能读写) ----
|
||||
blockOwner = {}
|
||||
blockSet = set()
|
||||
itemUseOnOwner = {}
|
||||
itemUseOnSet = set()
|
||||
|
||||
@classmethod
|
||||
def register(cls, service):
|
||||
"""
|
||||
功能服务初始化时调用,把管辖的方块和物品注册进路由表。
|
||||
同一方块/物品只会归属最后一个注册的服务(一般不会冲突)。
|
||||
"""
|
||||
# 右键方块路由:方块名 → 服务
|
||||
for block in service.getFeatureBlocks():
|
||||
cls.blockOwner[block] = service
|
||||
cls.blockSet.add(block)
|
||||
|
||||
# 手持物品右键方块表面路由:物品名 → 服务
|
||||
for itemName in service.getItemUseOnRules():
|
||||
cls.itemUseOnOwner[itemName] = service
|
||||
cls.itemUseOnSet.add(itemName)
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
"""
|
||||
初始化:注册三个事件监听。
|
||||
注意:本类自身不注册任何功能方块,只负责把事件分发给已注册的服务。
|
||||
"""
|
||||
super(FeatureDispatcher, self).__init__(namespace, systemName)
|
||||
print "===== FeatureDispatcher init ====="
|
||||
self._cancelItemUse = {} # 将要取消下一次物品使用的玩家ID集合(防止喝药水等)
|
||||
self.ListenEvent()
|
||||
|
||||
def Destroy(self):
|
||||
print "===== FeatureDispatcher Destroy ====="
|
||||
self.UnListenEvent()
|
||||
|
||||
# ============================================================================
|
||||
# 事件注册与注销
|
||||
# ============================================================================
|
||||
|
||||
def ListenEvent(self):
|
||||
"""
|
||||
注册三个事件:
|
||||
1. ServerBlockUseEvent —— 玩家右键方块(所有功能的右键转换入口)
|
||||
2. ServerItemUseOnEvent —— 手持物品右键方块表面(如放置生鸡肉)
|
||||
3. ServerItemTryUseEvent —— 玩家尝试使用物品(拦截喝药水/吃食物等默认行为)
|
||||
"""
|
||||
self.ListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerBlockUseEvent", self, self.OnBlockUse)
|
||||
self.ListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerItemUseOnEvent", self, self.OnItemUseOn)
|
||||
self.ListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerItemTryUseEvent", self, self.OnItemTryUse)
|
||||
|
||||
def UnListenEvent(self):
|
||||
self.UnListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerBlockUseEvent", self, self.OnBlockUse)
|
||||
self.UnListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerItemUseOnEvent", self, self.OnItemUseOn)
|
||||
self.UnListenForEvent(serverApi.GetEngineNamespace(),
|
||||
serverApi.GetEngineSystemName(),
|
||||
"ServerItemTryUseEvent", self, self.OnItemTryUse)
|
||||
|
||||
# ============================================================================
|
||||
# 主入口1:玩家右键方块
|
||||
# ============================================================================
|
||||
|
||||
def OnBlockUse(self, args):
|
||||
"""
|
||||
玩家右键方块时触发。
|
||||
用被右键的方块名 O(1) 定位到唯一的功能服务,转发给它的 handleBlockUse。
|
||||
|
||||
服务返回 True = 处理成功 → 取消默认行为(如防止喝药水),并标记拦截下一次物品使用
|
||||
服务返回 False = 当前组合无匹配规则 → 交还默认行为
|
||||
"""
|
||||
blockName = args.get("blockName")
|
||||
service = self.blockOwner.get(blockName)
|
||||
if not service:
|
||||
# 这个方块不属于任何已注册的功能 → 交给原版/其他模组处理
|
||||
return
|
||||
|
||||
handled = service.handleBlockUse(args)
|
||||
if handled:
|
||||
args['cancel'] = True
|
||||
playerId = args.get("playerId")
|
||||
self._cancelItemUse[playerId] = True
|
||||
|
||||
# ============================================================================
|
||||
# 主入口2:手持物品右键方块表面
|
||||
# ============================================================================
|
||||
|
||||
def OnItemUseOn(self, args):
|
||||
"""
|
||||
手持物品右键某个方块表面时触发(如手持原版生鸡肉右键地面)。
|
||||
用手持物品名 O(1) 定位到唯一的功能服务,转发给它的 handleItemUseOn。
|
||||
|
||||
注意:取消"使用物品于方块"的字段是 ret(不是 cancel),
|
||||
官方要求双端设置才能真正取消默认行为。本分发器已在服务端设 ret=True,
|
||||
服务端不会执行默认放置/使用逻辑;客户端如需完美取消动画,
|
||||
可再增加一个监听 ClientItemUseOnEvent 的客户端配对系统。
|
||||
"""
|
||||
itemName = args.get("itemName")
|
||||
service = self.itemUseOnOwner.get(itemName)
|
||||
if not service:
|
||||
return
|
||||
|
||||
handled = service.handleItemUseOn(args)
|
||||
if handled:
|
||||
args['ret'] = True
|
||||
playerId = args.get("playerId")
|
||||
self._cancelItemUse[playerId] = True
|
||||
|
||||
# ============================================================================
|
||||
# 主入口3:拦截物品使用(防止喝药水/吃食物等默认行为)
|
||||
# ============================================================================
|
||||
|
||||
def OnItemTryUse(self, args):
|
||||
"""
|
||||
玩家尝试使用物品(喝药水、吃食物等)时触发。
|
||||
如果该玩家刚被标记为"取消下一次物品使用"(_cancelItemUse),
|
||||
就取消这次默认行为,避免右键转换方块时误喝药水。
|
||||
"""
|
||||
playerId = args.get("playerId")
|
||||
if self._cancelItemUse.pop(playerId, False):
|
||||
args['cancel'] = True
|
||||
@@ -0,0 +1,289 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
烧烤方块转换规则数据
|
||||
================================================================================
|
||||
所有"烧烤"(xy_barbecue) 分类下"右键方块→消耗物品→替换方块"的转换规则都在这里。
|
||||
只被 BarbecueService 读取,FeatureDispatcher 通过方块名集合路由到 BarbecueService,
|
||||
不会去查其他功能(动物养殖/做菜/工具切换)的数据库。
|
||||
|
||||
【烤法区分】
|
||||
1) 烤串(牛肉串/羊肉串/烤蔬菜串/烤香菇串)
|
||||
→ 放在"烧烤架"上烤,右键 2 次转换,不计时
|
||||
→ 牛肉串/羊肉串: 生=_1, 半熟=_0, 熟=_3(注意顺序与蔬菜串不同!)
|
||||
→ 烤蔬菜串/烤香菇串: 生=_0, 半熟=_1, 熟=_3
|
||||
2) 烤鸡/烤乳猪/烤鱼
|
||||
→ "上支杆"烤法: 支杆=b_34, 手持生食材右键支杆上杆
|
||||
→ 支杆下方放"篝火(原版营火 campfire)", 计时自动转换
|
||||
→ 烤熟后空手右键"取下" → 以掉落物形式获得整只成品
|
||||
|
||||
【食用部分说明】
|
||||
烤熟成品(整只/整头)的"吃东西逐级分食"转换(如 整只→5/6→…→1/6)
|
||||
由后续统一的"总食用功能"实现,本次不实现、不收录。
|
||||
|
||||
字段说明(与 AnimalData 一致):
|
||||
source: 被右键的方块ID(必填)
|
||||
item: 玩家手里必须拿的物品,None=空手也行
|
||||
target: 替换成什么方块,"minecraft:air"=消失
|
||||
consume: 消耗物品数量,0=不消耗(默认1)
|
||||
giveItem: 额外给玩家什么物品,None=不给
|
||||
giveCount: giveItem 的数量,默认1
|
||||
dropItem: 转换时以掉落物形式生成的物品列表,优先于 giveItem
|
||||
格式: [{"name": "xxx", "count": N}, ...]
|
||||
type: "switch"=右键切换, "timer"=计时自动转换
|
||||
time: 计时型转换需要多少秒(到时间自动替换方块)
|
||||
below: 计时型转换要求下方方块(如 ["minecraft:campfire"])
|
||||
================================================================================
|
||||
"""
|
||||
|
||||
BARBECUE_SWITCHES = [
|
||||
|
||||
# ============================================================
|
||||
# 一、烧烤架(点火/灭火)
|
||||
# ============================================================
|
||||
# 【点火-打火石】烧烤架_1(无火) → 烧烤架_2(点燃),打火石不消耗
|
||||
{
|
||||
"source": "rtdd:barbecuegrill_0",
|
||||
"item": "minecraft:flint_and_steel",
|
||||
"target": "rtdd:barbecuegrill_1",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【点火-烈焰弹】烧烤架_1(无火) → 烧烤架_2(点燃),烈焰弹消耗1个
|
||||
{
|
||||
"source": "rtdd:barbecuegrill_0",
|
||||
"item": "minecraft:fire_charge",
|
||||
"target": "rtdd:barbecuegrill_1",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【灭火】烧烤架_2(点燃) → 烧烤架_1(无火),消耗水桶,获得空桶
|
||||
{
|
||||
"source": "rtdd:barbecuegrill_1",
|
||||
"item": "minecraft:water_bucket",
|
||||
"target": "rtdd:barbecuegrill_0",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": "minecraft:bucket",
|
||||
"giveCount": 1,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 二、烤串(放烧烤架上,右键 2 次转换,不计时)
|
||||
# 【重要】牛肉串/羊肉串的 生=_1 半熟=_0;蔬菜串/香菇串的 生=_0 半熟=_1
|
||||
# ============================================================
|
||||
# 牛肉串: 生(_1) → 半熟(_0) → 熟(_3)
|
||||
# 放在"已点燃的烧烤架"上方才能右键烤制(下方必须是 barbecuegrill_1)
|
||||
{
|
||||
"source": "rtdd:beefkebabs_1",
|
||||
"item": None,
|
||||
"target": "rtdd:beefkebabs_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
{
|
||||
"source": "rtdd:beefkebabs_0",
|
||||
"item": None,
|
||||
"target": "rtdd:beefkebabs_3",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 羊肉串: 生(_1) → 半熟(_0) → 熟(_3)
|
||||
{
|
||||
"source": "rtdd:lambkebabs_1",
|
||||
"item": None,
|
||||
"target": "rtdd:lambkebabs_0",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
{
|
||||
"source": "rtdd:lambkebabs_0",
|
||||
"item": None,
|
||||
"target": "rtdd:lambkebabs_3",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 烤蔬菜串: 生(_0) → 半熟(_1) → 熟(_3)
|
||||
{
|
||||
"source": "rtdd:roasted_vegetables_0",
|
||||
"item": None,
|
||||
"target": "rtdd:roasted_vegetables_1",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
{
|
||||
"source": "rtdd:roasted_vegetables_1",
|
||||
"item": None,
|
||||
"target": "rtdd:roasted_vegetables_3",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 烤香菇串: 生(_0) → 半熟(_1) → 熟(_3)
|
||||
{
|
||||
"source": "rtdd:roastedmushrooms_0",
|
||||
"item": None,
|
||||
"target": "rtdd:roastedmushrooms_1",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
{
|
||||
"source": "rtdd:roastedmushrooms_1",
|
||||
"item": None,
|
||||
"target": "rtdd:roastedmushrooms_3",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"switchCd": 0.15,
|
||||
"below": ["rtdd:barbecuegrill_1"],
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 三、上杆(手持生食材右键 支杆b_34)
|
||||
# ============================================================
|
||||
# 【上烤鸡】支杆 + 生鸡肉(放置后方块物品) → 烤鸡(上烤架_生)
|
||||
{
|
||||
"source": "rtdd:b_34",
|
||||
"item": "rtdd:roastchickenplace_0",
|
||||
"target": "rtdd:roastchicken_0",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【上烤乳猪】支杆 + 死猪 → 烤乳猪(生)
|
||||
{
|
||||
"source": "rtdd:b_34",
|
||||
"item": "rtdd:dead_pig",
|
||||
"target": "rtdd:roastpig_0",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【上烤鳕鱼】支杆 + 鳕鱼 → 烤鳕鱼(生)
|
||||
{
|
||||
"source": "rtdd:b_34",
|
||||
"item": "minecraft:cod",
|
||||
"target": "rtdd:grilledcod_0",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【上烤鲑鱼】支杆 + 鲑鱼 → 烤鲑鱼(生)
|
||||
{
|
||||
"source": "rtdd:b_34",
|
||||
"item": "minecraft:salmon",
|
||||
"target": "rtdd:grilled_fish",
|
||||
"consume": 1,
|
||||
"type": "switch",
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 四、计时烤制(上杆后,支杆下方篝火,计时自动转换)
|
||||
# ============================================================
|
||||
# 【烤熟】烤鸡(上烤架_生) → 烤鸡(上烤架_熟),下方需篝火,约60秒
|
||||
{
|
||||
"source": "rtdd:roastchicken_0",
|
||||
"item": None,
|
||||
"target": "rtdd:roastchicken_1",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"below": ["minecraft:campfire"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【烤熟】烤乳猪(生) → 烤乳猪(熟),下方需篝火,约60秒
|
||||
{
|
||||
"source": "rtdd:roastpig_0",
|
||||
"item": None,
|
||||
"target": "rtdd:roastpig_1",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"below": ["minecraft:campfire"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【烤熟】烤鳕鱼(生) → 烤鳕鱼(熟),下方需篝火,约60秒
|
||||
{
|
||||
"source": "rtdd:grilledcod_0",
|
||||
"item": None,
|
||||
"target": "rtdd:grilledcod_1",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"below": ["minecraft:campfire"],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【烤熟】烤鲑鱼(生) → 烤鲑鱼(熟),下方需篝火,约60秒
|
||||
{
|
||||
"source": "rtdd:grilled_fish",
|
||||
"item": None,
|
||||
"target": "rtdd:grilled_salmon",
|
||||
"consume": 0,
|
||||
"type": "timer",
|
||||
"time": 60,
|
||||
"below": ["minecraft:campfire"],
|
||||
"giveItem": None,
|
||||
},
|
||||
|
||||
# ============================================================
|
||||
# 五、取下(空手右键烤熟方块,支杆恢复,成品以掉落物获得)
|
||||
# ============================================================
|
||||
# 【取下烤鸡】烤鸡(上烤架_熟) → 支杆 + 掉落 烤鸡(整只)
|
||||
{
|
||||
"source": "rtdd:roastchicken_1",
|
||||
"item": None,
|
||||
"target": "rtdd:b_34",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "rtdd:roast_chickenplace_1", "count": 1}],
|
||||
"giveItem": None,
|
||||
},
|
||||
# 【取下烤乳猪】烤乳猪(熟) → 支杆 + 掉落 烤乳猪(整只)
|
||||
{
|
||||
"source": "rtdd:roastpig_1",
|
||||
"item": None,
|
||||
"target": "rtdd:b_34",
|
||||
"consume": 0,
|
||||
"type": "switch",
|
||||
"dropItem": [{"name": "rtdd:roastpignew_1", "count": 1}],
|
||||
"giveItem": None,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 六、手持物品右键"方块表面"生成规则(ServerItemUseOnEvent)
|
||||
# ============================================================
|
||||
# 目前只有一条:手持"原版生鸡肉"右键任意方块表面 → 在其表面生成
|
||||
# "生鸡肉(原版生鸡肉右键放置后)"(roastchickenplace_0) 方块,消耗1个。
|
||||
# 该规则由 FeatureDispatcher 转发 ServerItemUseOnEvent 到 BarbecueService。
|
||||
ITEM_USE_ON_RULES = [
|
||||
{
|
||||
"item": "minecraft:chicken", # 手持的原版生鸡肉
|
||||
"target": "rtdd:roastchickenplace_0", # 生成的目标方块
|
||||
"consume": 1, # 消耗1个生鸡肉
|
||||
},
|
||||
]
|
||||
Whitespace-only changes.
+20
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
烧烤客户端(预留)
|
||||
=================
|
||||
目前烧烤功能的服务端逻辑已由 BarbecueService + FeatureDispatcher 处理,
|
||||
客户端暂不需要额外的 UI 工作。
|
||||
如果以后需要烤制进度提示、烤串状态展示等 UI,可以在这里扩展。
|
||||
"""
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
ClientSystem = clientApi.GetClientSystemCls()
|
||||
|
||||
|
||||
class BarbecueClient(ClientSystem):
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
print "===== BarbecueClient init ====="
|
||||
|
||||
def Destroy(self):
|
||||
print "===== BarbecueClient Destroy ====="
|
||||
Whitespace-only changes.
+341
@@ -0,0 +1,341 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
烧烤引擎(服务端)
|
||||
================================================================================
|
||||
只处理"烧烤"(xy_barbecue) 分类下的方块转换:烧烤架、烤串、烤鸡、烤乳猪、烤鱼。
|
||||
|
||||
【与 BlockSwitchService / CookingService 的区别】
|
||||
本系统(方案A)采用"统一分发器"架构:
|
||||
- FeatureDispatcher 是唯一监听 ServerBlockUseEvent 的系统
|
||||
- 本类通过 FeatureDispatcher.register(self) 把管辖方块注册进去
|
||||
- 玩家右键方块时,FeatureDispatcher 用 O(1) dict 直接找到本实例
|
||||
- 本类只查自己的 BarbecueData(BARBECUE_SWITCHES),不碰其他功能数据库
|
||||
|
||||
【本类额外处理的规则】
|
||||
- 手持"原版生鸡肉"右键方块表面(ServerItemUseOnEvent) → 生成"生鸡肉(放置后)"方块
|
||||
该规则通过 FeatureDispatcher 的 itemUseOn 路由转发到本类 handleItemUseOn
|
||||
|
||||
【转换类型】
|
||||
- type="switch" : 右键直接转换(点火/灭火/烤串2次/上杆/取下)
|
||||
- type="timer" : 计时自动转换(上杆后下方篝火烤熟),
|
||||
由本类用 AddTimer 实现,启动前检查下方是否为篝火
|
||||
================================================================================
|
||||
"""
|
||||
import time
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
# ---- 网易API基础对象 ----
|
||||
ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类
|
||||
comp = serverApi.GetEngineCompFactory() # 组件工厂
|
||||
|
||||
# ---- 继承 BlueFox 框架的系统基类(提供 AddTimer / CreateEngineItemEntity 等方法) ----
|
||||
from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem
|
||||
from ....BlueFoxTools.ToolsConfig import LevelId
|
||||
|
||||
# ---- 只导入烧烤自己的数据库 ----
|
||||
from ..BarbecueData import BARBECUE_SWITCHES, ITEM_USE_ON_RULES
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
# ---- 方块表面方向 → 相邻坐标偏移(0下 1上 2北 3南 4西 5东) ----
|
||||
FACE_OFFSET = {
|
||||
0: (0, -1, 0),
|
||||
1: (0, 1, 0),
|
||||
2: (0, 0, -1),
|
||||
3: (0, 0, 1),
|
||||
4: (-1, 0, 0),
|
||||
5: (1, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class BarbecueService(BlueFoxServerSystem):
|
||||
"""
|
||||
烧烤引擎
|
||||
|
||||
【核心数据结构】
|
||||
barbecueRecipes = {
|
||||
(方块名, 物品名): 规则dict, # O(1) 查找
|
||||
}
|
||||
barbecueBlockSet = {方块名, ...} # O(1) 拦截/注册用
|
||||
barbecueTimers = {方块名: 规则dict} # 计时型起始方块 → 规则
|
||||
itemUseOnRecipes = {物品名: 规则dict} # 手持物品右键方块表面 → 生成规则
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
super(BarbecueService, self).__init__(namespace, systemName)
|
||||
print "===== BarbecueService init ====="
|
||||
|
||||
# ---- 构建烧烤规则字典(O(1) 查找) ----
|
||||
self.barbecueRecipes = {}
|
||||
self.barbecueBlockSet = set()
|
||||
self.barbecueTimers = {}
|
||||
for sw in BARBECUE_SWITCHES:
|
||||
key = (sw["source"], sw.get("item"))
|
||||
self.barbecueRecipes[key] = sw
|
||||
self.barbecueBlockSet.add(sw["source"])
|
||||
if sw.get("type") == "timer":
|
||||
self.barbecueTimers[sw["source"]] = sw
|
||||
|
||||
# ---- 手持物品右键方块表面规则:物品名 → 规则 ----
|
||||
self.itemUseOnRecipes = {}
|
||||
for rule in ITEM_USE_ON_RULES:
|
||||
self.itemUseOnRecipes[rule["item"]] = rule
|
||||
|
||||
# ---- 计时去重表 + 右键冷却表 ----
|
||||
self._timerSchedule = {}
|
||||
self.cooldown = {}
|
||||
|
||||
# ---- 注册到统一分发器 ----
|
||||
FeatureDispatcher.register(self)
|
||||
|
||||
# ============================================================================
|
||||
# 给 FeatureDispatcher 的接口
|
||||
# ============================================================================
|
||||
|
||||
def getFeatureBlocks(self):
|
||||
"""返回本功能管辖的所有方块名集合(右键路由用)"""
|
||||
return self.barbecueBlockSet
|
||||
|
||||
def getItemUseOnRules(self):
|
||||
"""返回手持物品右键方块表面的规则:{物品名: 规则dict}(供分发器路由)"""
|
||||
return self.itemUseOnRecipes
|
||||
|
||||
def handleBlockUse(self, args):
|
||||
"""
|
||||
由 FeatureDispatcher 转发的右键方块入口。
|
||||
只有 blockName 在本功能方块集合里才会被调用。
|
||||
返回 True=已处理,False=当前组合无匹配规则。
|
||||
"""
|
||||
blockName = args.get("blockName")
|
||||
playerId = args.get("playerId")
|
||||
x, y, z = args.get("x"), args.get("y"), args.get("z")
|
||||
pos = (x, y, z)
|
||||
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
|
||||
key = (dim, x, y, z)
|
||||
|
||||
itemDict = self.getCarriedItem(playerId)
|
||||
|
||||
recipe = self.findRecipe(blockName, itemDict)
|
||||
if not recipe:
|
||||
return False
|
||||
|
||||
# 冷却检查(烤串"右键2次"用 switchCd 调小冷却以便快速连点)
|
||||
if not self.checkCooldown(key, recipe.get("switchCd", 0.5)):
|
||||
return True
|
||||
|
||||
# ---- 下方方块检查(timer 需篝火,烤串需点燃的烧烤架) ----
|
||||
if not self.checkBelow(recipe, dim, pos):
|
||||
# 下方不是要求的方法 → 提示玩家,不执行(但也算处理,取消默认行为)
|
||||
if recipe.get("type") == "timer":
|
||||
tip = "§c支杆下方需要放一个篝火(营火)才能烤制!"
|
||||
else:
|
||||
tip = "§c烤串需要放在点燃的烧烤架上才能烤制!"
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, tip)
|
||||
return True
|
||||
|
||||
# 计时型:启动计时
|
||||
if recipe.get("type") == "timer":
|
||||
self.startTimer(key, recipe, dim)
|
||||
return True
|
||||
|
||||
# 切换型
|
||||
self.handleSwitch(recipe, playerId, pos, dim, itemDict, key, blockName)
|
||||
return True
|
||||
|
||||
def handleItemUseOn(self, args):
|
||||
"""
|
||||
由 FeatureDispatcher 转发的"手持物品右键方块表面"事件。
|
||||
当前用于:手持原版生鸡肉右键任意方块表面 → 在其表面生成生鸡肉(放置后)方块。
|
||||
返回 True=已处理,False=当前组合无匹配规则。
|
||||
"""
|
||||
itemName = args.get("itemName")
|
||||
playerId = args.get("playerId")
|
||||
rule = self.itemUseOnRecipes.get(itemName)
|
||||
if not rule:
|
||||
return False
|
||||
|
||||
x, y, z = args.get("x"), args.get("y"), args.get("z")
|
||||
face = args.get("face", 1)
|
||||
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
|
||||
|
||||
# 计算被点方块表面相邻的目标格
|
||||
off = FACE_OFFSET.get(face, (0, 1, 0))
|
||||
targetPos = (x + off[0], y + off[1], z + off[2])
|
||||
|
||||
# 目标格必须为空(可放置)
|
||||
try:
|
||||
cur = comp.CreateBlockInfo(LevelId).GetBlockNew(targetPos, dim)
|
||||
if cur and cur.get("name") != "minecraft:air":
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# 消耗 1 个生鸡肉
|
||||
itemDict = self.getCarriedItem(playerId)
|
||||
if not itemDict:
|
||||
return False
|
||||
self.consumeItem(playerId, itemDict, rule.get("consume", 1))
|
||||
|
||||
# 生成生鸡肉(放置后)方块
|
||||
self.replaceBlock(targetPos, dim, rule["target"], playerId)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# 规则匹配(O(1))
|
||||
# ============================================================================
|
||||
|
||||
def findRecipe(self, blockName, itemDict):
|
||||
itemName = itemDict.get("newItemName") if itemDict else None
|
||||
recipe = self.barbecueRecipes.get((blockName, itemName))
|
||||
if recipe:
|
||||
return recipe
|
||||
recipe = self.barbecueRecipes.get((blockName, None))
|
||||
if recipe:
|
||||
return recipe
|
||||
return None
|
||||
|
||||
# ============================================================================
|
||||
# 切换型规则处理
|
||||
# ============================================================================
|
||||
|
||||
def handleSwitch(self, recipe, playerId, pos, dim, itemDict, key, blockName):
|
||||
"""
|
||||
正向(item 非 None):点火/上杆 → 消耗 → 替换 → 给奖励 → 续计时
|
||||
逆向(item 为 None):烤串2次/取下 → 掉落 → 替换
|
||||
"""
|
||||
# ---- 正向 ----
|
||||
if recipe.get("item") is not None:
|
||||
if recipe.get("consume", 1) > 0:
|
||||
self.consumeItem(playerId, itemDict, recipe.get("consume", 1))
|
||||
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
if recipe.get("giveItem"):
|
||||
self.giveItem(playerId, recipe["giveItem"], recipe.get("giveCount", 1))
|
||||
|
||||
# 上杆后若新方块是计时源 → 尝试启动计时(下方需有篝火)
|
||||
if recipe.get("target") in self.barbecueTimers:
|
||||
nextTimer = self.barbecueTimers[recipe["target"]]
|
||||
if self.checkBelow(nextTimer, dim, pos):
|
||||
self.startTimer(key, nextTimer, dim)
|
||||
|
||||
return
|
||||
|
||||
# ---- 逆向 ----
|
||||
if recipe.get("dropItem"):
|
||||
self.dropItems(dim, pos, recipe["dropItem"])
|
||||
elif recipe.get("giveItem"):
|
||||
self.giveItem(playerId, recipe["giveItem"], recipe.get("giveCount", 1))
|
||||
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
# ============================================================================
|
||||
# 计时型规则处理(AddTimer 一次性定时,启动前检查下方篝火)
|
||||
# ============================================================================
|
||||
|
||||
def checkBelow(self, recipe, dim, pos):
|
||||
"""
|
||||
检查方块下方是否为 recipe["below"] 中指定的方块。
|
||||
没写 below 字段 → 视为满足。
|
||||
"""
|
||||
below = recipe.get("below")
|
||||
if not below:
|
||||
return True
|
||||
belowPos = (pos[0], pos[1] - 1, pos[2])
|
||||
try:
|
||||
b = comp.CreateBlockInfo(LevelId).GetBlockNew(belowPos, dim)
|
||||
return b.get("name") in below
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def startTimer(self, key, recipe, dim):
|
||||
"""启动一次性定时器,1秒内去重,到点由 _timerDone 执行转换"""
|
||||
now = time.time()
|
||||
last = self._timerSchedule.get(key, 0)
|
||||
if now - last < 1.0:
|
||||
return
|
||||
self._timerSchedule[key] = now
|
||||
self.AddTimer(recipe.get("time", 60), self._timerDone, key, recipe, dim)
|
||||
|
||||
def _timerDone(self, key, recipe, dim):
|
||||
"""计时到点:确认方块仍是计时源,且下方仍有篝火,才执行转换"""
|
||||
pos = (key[1], key[2], key[3])
|
||||
try:
|
||||
cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
if not cur or cur.get("name") != recipe["source"]:
|
||||
return
|
||||
if not self.checkBelow(recipe, dim, pos):
|
||||
return # 篝火被拿走了,暂停烤制
|
||||
self.replaceBlock(pos, dim, recipe["target"], None)
|
||||
# 若新方块仍是计时源 → 继续下一级(当前烧烤无多级计时,预留)
|
||||
nextRecipe = self.barbecueTimers.get(recipe["target"])
|
||||
if nextRecipe:
|
||||
self.startTimer(key, nextRecipe, dim)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ============================================================================
|
||||
# 工具方法
|
||||
# ============================================================================
|
||||
|
||||
def getCarriedItem(self, playerId):
|
||||
itemComp = comp.CreateItem(playerId)
|
||||
return itemComp.GetPlayerItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0)
|
||||
|
||||
def consumeItem(self, playerId, itemDict, amount=1):
|
||||
if itemDict is None:
|
||||
return
|
||||
count = itemDict.get("count", 0)
|
||||
if count <= amount:
|
||||
comp.CreateItem(playerId).SetEntityItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": "minecraft:air", "count": 0, "auxValue": 0})
|
||||
else:
|
||||
itemDict["count"] = count - amount
|
||||
comp.CreateItem(playerId).SetEntityItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": itemDict["newItemName"],
|
||||
"count": itemDict["count"],
|
||||
"auxValue": itemDict.get("newAuxValue", 0)})
|
||||
|
||||
def replaceBlock(self, pos, dim, newBlockName, playerId, aux=None):
|
||||
creator = playerId if playerId is not None else LevelId
|
||||
if aux is None:
|
||||
try:
|
||||
oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim)
|
||||
aux = oldBlock.get("aux", 0)
|
||||
except Exception:
|
||||
aux = 0
|
||||
comp.CreateBlockInfo(creator).SetBlockNew(
|
||||
pos,
|
||||
blockDict={"name": newBlockName, "aux": aux},
|
||||
oldBlockHandling=0,
|
||||
dimensionId=dim)
|
||||
|
||||
def giveItem(self, playerId, itemName, count=1, auxValue=0):
|
||||
comp.CreateItem(playerId).SpawnItemToPlayerInv(
|
||||
{"itemName": itemName, "count": count, "auxValue": auxValue}, playerId)
|
||||
|
||||
def dropItems(self, dim, pos, dropList):
|
||||
"""以掉落物形式生成物品(取下烤鸡/烤乳猪等)"""
|
||||
for d in dropList:
|
||||
itemDict = {
|
||||
"itemName": d["name"],
|
||||
"count": d.get("count", 1),
|
||||
"auxValue": d.get("aux", 0),
|
||||
}
|
||||
self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2]))
|
||||
|
||||
def checkCooldown(self, key, cd=0.5):
|
||||
"""右键冷却:同一方块 cd 秒内只响应一次,防止连点刷物品"""
|
||||
now = time.time()
|
||||
last = self.cooldown.get(key, 0)
|
||||
if now - last < cd:
|
||||
return False
|
||||
self.cooldown[key] = now
|
||||
return True
|
||||
Whitespace-only changes.
+5
-4
@@ -47,7 +47,7 @@ todo : 手持种子 消耗16个种子
|
||||
|
||||
8. 鸡饲料盆_2(满) -> 鸡饲料盆_1(空)
|
||||
转换条件:[鸡进食后计时变空]【机制待确认】
|
||||
todo : 计时变换 先三分钟
|
||||
todo : 计时变换 先一分钟
|
||||
|
||||
========================================
|
||||
三、饲料槽(牛羊等)
|
||||
@@ -58,7 +58,7 @@ todo : 手持小麦 消耗32个小麦
|
||||
|
||||
10. 饲料槽_2(满小麦) -> 饲料槽_3(半量小麦) -> 饲料槽_4(少量小麦) -> 饲料槽_1(空)
|
||||
转换条件:[动物进食后计时逐级减少]【机制待确认】
|
||||
todo : 计时变换 先三分钟 逐级都是 计时变化
|
||||
todo : 计时变换 先一分钟 逐级都是 计时变化
|
||||
|
||||
11. 饲料槽_1(空) -> 饲料槽_6(水槽)
|
||||
转换条件:[手持 水桶 右键 进行转换,消耗水桶]【操作方式待确认】
|
||||
@@ -70,14 +70,15 @@ todo : 手持胡萝卜 消耗32个胡萝卜
|
||||
|
||||
13. 饲料槽_7(满胡萝卜) -> 饲料槽_8(半量胡萝卜) -> 饲料槽_1(空)
|
||||
转换条件:[动物进食后计时逐级减少]【机制待确认】
|
||||
todo : 计时变换 先三分钟 逐级都是 计时变化
|
||||
todo : 计时变换 先一分钟 逐级都是 计时变化
|
||||
|
||||
========================================
|
||||
四、粪便
|
||||
|
||||
14. 粪便(一坨) -> 粪便(半坨) -> 粪便(仅痕迹) -> 无
|
||||
转换条件:[右键 进行转换,取走/清理粪便]
|
||||
机制 : 右键取走一个等级,逐级清理至消失(暂定,可先不管)
|
||||
todo 这玩意也可食用
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
|
||||
========================================
|
||||
五、其他静态方块
|
||||
|
||||
+6
-1
@@ -40,7 +40,8 @@
|
||||
转换条件:[放到烧烤架上,手持右键 2 次进行转换]
|
||||
机制: 放在烧烤架上,右键第1次:生->半熟,右键第2次:半熟->熟(不计时)
|
||||
|
||||
todo 熟的烤串也都可以直接右键食用
|
||||
todo 熟的烤串也都可以直接右键食用
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
========================================
|
||||
三、烤鸡(上支杆 + 篝火烤,非烧烤架)
|
||||
========================================
|
||||
@@ -63,6 +64,7 @@ todo 熟的烤串也都可以直接右键食用
|
||||
12. 烤鸡(整只) -> 烤鸡(3/4只) -> 烤鸡(半只) -> 烤鸡(1/4只) -> (吃完消失)
|
||||
转换条件:[吃东西(右键食用)转换,每吃一份降一级]
|
||||
机制: 右键食用,吃一份: 整只->3/4->半->1/4,最后一份吃完方块消失
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
|
||||
========================================
|
||||
四、烤乳猪(上支杆 + 篝火烤,非烧烤架)
|
||||
@@ -82,6 +84,7 @@ todo 熟的烤串也都可以直接右键食用
|
||||
16. 烤乳猪(整只/6份) -> 烤乳猪(5/6只) -> 烤乳猪(4/6只) -> 烤乳猪(3/6只) -> 烤乳猪(2/6只) -> 烤乳猪(1/6只) -> (吃完消失)
|
||||
转换条件:[吃东西(右键食用)转换,每吃一份,剩余份数递减]
|
||||
机制: 右键食用,共6份,吃一份剩余递减1/6: 整只->5/6->4/6->3/6->2/6->1/6,最后一份吃完方块消失
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
|
||||
========================================
|
||||
五、烤鱼(上支杆 + 篝火烤,非烧烤架)
|
||||
@@ -94,6 +97,7 @@ todo 熟的烤串也都可以直接右键食用
|
||||
转换条件:[支杆下方篝火,计时自动转换]
|
||||
机制: 支杆下方篝火计时约60秒烤熟
|
||||
todo 烤鳕鱼(熟)可以直接右键食用
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
19. 鲑鱼 -> 烤鲑鱼(生)
|
||||
转换条件:[手持 鲑鱼 右键 支杆(b_34) 进行转换,消耗1条]
|
||||
机制: 手持鲑鱼右键支杆,上杆待烤,变为烤鲑鱼(生)
|
||||
@@ -102,6 +106,7 @@ todo 烤鳕鱼(熟)可以直接右键食用
|
||||
转换条件:[支杆下方篝火,计时自动转换]
|
||||
机制: 支杆下方篝火计时约60秒烤熟
|
||||
todo 烤鲑鱼(熟)可以直接右键食用
|
||||
【食用部分后续在总食用功能中实现,本次不实现】
|
||||
========================================
|
||||
六、方块归属说明(均有转换,无静态成品)
|
||||
========================================
|
||||
|
||||
Reference in new issue
Block a user