From c58f5fea40ae97db437817c6bc38cffe29afa8d6 Mon Sep 17 00:00:00 2001 From: wenhe <1046926120@qq.com> Date: Fri, 11 Sep 2026 15:32:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B2=B9=E9=94=85=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script_NeteaseModousIY5aSF/modMain.py | 2 + .../xiangyemod/youguo/YouguoData.py | 175 ++++++++++++ .../xiangyemod/youguo/__init__.py | 1 + .../youguo/service/YouguoService.py | 229 ++++++++++++++++ .../youguo/service/YouguoTimerService.py | 249 ++++++++++++++++++ .../xiangyemod/youguo/service/__init__.py | 1 + .../netease_blocks/oil_frieddumplings_1.json | 2 +- .../netease_blocks/oilchickennuggets_0.json | 2 +- .../netease_blocks/oilchickennuggets_1.json | 2 +- .../netease_blocks/oilfrenchfries_0.json | 2 +- .../netease_blocks/oilfrenchfries_1.json | 2 +- .../netease_blocks/oilpotfish_0.json | 2 +- .../netease_blocks/oilpotfish_1.json | 2 +- .../netease_blocks/oilpotporkchop_0.json | 2 +- .../netease_blocks/oilpotporkchop_1.json | 2 +- .../netease_blocks/oiltempura_1.json | 2 +- tuilun/全部功能做了什么.txt | 26 ++ tuilun/全部功能没做什么.txt | 7 +- 18 files changed, 697 insertions(+), 13 deletions(-) create mode 100644 behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/YouguoData.py create mode 100644 behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/__init__.py create mode 100644 behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoService.py create mode 100644 behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoTimerService.py create mode 100644 behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/__init__.py diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/modMain.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/modMain.py index c911459..d7d0f94 100644 --- a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/modMain.py +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/modMain.py @@ -20,6 +20,8 @@ from xiangyemod.shicai.service.ShicaiService import ShicaiService from xiangyemod.shousi.service.ShousiService import ShousiService from xiangyemod.mianshi.service.MianshiService import MianshiService from xiangyemod.mianshi.service.MianshiTimerService import MianshiTimerService +from xiangyemod.youguo.service.YouguoService import YouguoService +from xiangyemod.youguo.service.YouguoTimerService import YouguoTimerService if not serverApi.IsInServer(): pass diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/YouguoData.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/YouguoData.py new file mode 100644 index 0000000..d8b19f5 --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/YouguoData.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +油锅(xy_youguo)转化规则数据 +================================================================================ +统一管理"油锅"分类下的转化规则常量。只被 +YouguoService / YouguoTimerService 读取, FeatureDispatcher 通过 +方块名路由到对应服务。 + +【实现范围(对应 油锅转化关系.txt 一~三)】 + 一、油锅基础: 倒油(油罐->玻璃罐)/取油(玻璃罐->油罐) + 二、炸物 : 满油锅+裹粉半成品(右键) -> 炸物(生) -> 计时 -> 熟 + 三、出锅 : 熟炸物+盘子(右键) -> 掉落盘装成品(掉落物形式) + +【计时(见 YouguoTimerService)】 + 炸物(生)->60秒->炸物(半熟)->30秒->炸物(熟); 天妇罗/炸饺子 生->60秒->熟 + 计时判定: 炸物方块正下方须是 点燃的炉灶(stove_2) 才开始计时, + 计时数据在炸物方块本身(炸物方块 tick 判定下方); 炉灶移走/熄灭则暂停 + +【不实现】: 各盘装成品 右键吃 (吃的操作后续由总食用功能实现) +================================================================================ +""" + +# ============================================================================ +# 一、方块/物品(常量) +# ============================================================================ +OILPOT_0 = "rtdd:oilpot_0" # 油锅(空) +OILPOT_1 = "rtdd:oilpot_1" # 油锅(满油) +OILCAN = "rtdd:oilcan" # 油罐(厨具) +MEDICINE_TANK = "rtdd:medicinetank" # 玻璃罐(厨具) +PLATE = "rtdd:plate_0" # 盘子(厨具) +LIT_STOVE = "rtdd:stove_2" # 点燃的炉灶(厨具) + +# 裹粉半成品(物品, 乡野食材) +BREADED_CHICKEN = "rtdd:oil_chickennuggets" # 裹粉鸡块 +BREADED_PORK = "rtdd:oilpork" # 裹粉猪肉 +BREADED_FRIES = "rtdd:oilfrenchfries" # 裹粉薯条 +BREADED_FISH = "rtdd:oilfish_0" # 裹粉鱼 +BREADED_TEMPURA = "rtdd:oiltempura" # 裹粉天妇罗 +DUMPLINGS = "rtdd:dumplings_4" # 生饺子(面食.甜食) + +# 炸物方块(生/半熟/熟) +CHICKEN_0 = "rtdd:oilchickennuggets_0" # 炸鸡块(生) +CHICKEN_1 = "rtdd:oilchickennuggets_1" # 炸鸡块(半熟) +CHICKEN_2 = "rtdd:oilchickennuggets_2" # 炸鸡块(熟) +PORK_0 = "rtdd:oilpotporkchop_0" # 炸猪排(生) +PORK_1 = "rtdd:oilpotporkchop_1" # 炸猪排(半熟) +PORK_2 = "rtdd:oilpotporkchop_2" # 炸猪排(熟) +FRIES_0 = "rtdd:oilfrenchfries_0" # 炸薯条(生) +FRIES_1 = "rtdd:oilfrenchfries_1" # 炸薯条(半熟) +FRIES_2 = "rtdd:oilfrenchfries_2" # 炸薯条(熟) +FISH_0 = "rtdd:oilpotfish_0" # 炸鱼块(生) +FISH_1 = "rtdd:oilpotfish_1" # 炸鱼块(半熟) +FISH_2 = "rtdd:oilpotfish_2" # 炸鱼块(熟) +TEMPURA_1 = "rtdd:oiltempura_1" # 炸天妇罗(生) +TEMPURA_2 = "rtdd:oiltempura_2" # 炸天妇罗(熟) +DUMPLING_1 = "rtdd:oil_frieddumplings_1" # 炸饺子(生, 油锅+饺子合体) +DUMPLING_2 = "rtdd:oil_frieddumplings_2" # 炸饺子(熟) + +# 盘装成品(掉落物形式, 归 youguo_finished) +PLATE_CHICKEN = "rtdd:plate_38" # 盘装鸡块 +PLATE_PORK = "rtdd:plate_36" # 炸猪排(盘装) +PLATE_FRIES = "rtdd:plate_37" # 炸薯条(盘装) +PLATE_FISH = "rtdd:plate_39" # 盘装炸鱼 +PLATE_TEMPURA = "rtdd:plate_49" # 天妇罗炸虾 +PLATE_DUMPLINGS = "rtdd:plate_51" # 炸饺子(盘装) + +# ============================================================================ +# 二、右键规则 (方块, 手持物品) -> 规则 +# target=替换方块; consume=消耗手持; give=给背包; startTimer=转换后启动计时; +# action=take_out(出锅: 清方块+掉落盘装成品) +# ============================================================================ +SWITCH_RULES = [ + # ---- 一、油锅基础 ---- + {"block": OILPOT_0, "item": OILCAN, "target": OILPOT_1, + "consume": 1, "give": MEDICINE_TANK, + "tip": "§a油罐倒入油锅, 油罐变回玻璃罐"}, + {"block": OILPOT_1, "item": MEDICINE_TANK, "target": OILPOT_0, + "consume": 1, "give": OILCAN, + "tip": "§a从油锅取油, 装回油罐"}, + + # ---- 二、放料(满油锅 + 裹粉半成品 -> 炸物(生), 开始计时炸制) ---- + # 油锅下须是 点燃的炉灶(stove_2) 才允许放料(否则不炸制) + {"block": OILPOT_1, "item": BREADED_CHICKEN, "target": CHICKEN_0, + "consume": 1, "startTimer": CHICKEN_0, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入裹粉鸡块, 开始炸制(60秒半熟, 再30秒熟)"}, + {"block": OILPOT_1, "item": BREADED_PORK, "target": PORK_0, + "consume": 1, "startTimer": PORK_0, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入裹粉猪肉, 开始炸制(60秒半熟, 再30秒熟)"}, + {"block": OILPOT_1, "item": BREADED_FRIES, "target": FRIES_0, + "consume": 3, "startTimer": FRIES_0, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入裹粉薯条x3, 开始炸制(60秒半熟, 再30秒熟)"}, + {"block": OILPOT_1, "item": BREADED_FISH, "target": FISH_0, + "consume": 1, "startTimer": FISH_0, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入裹粉鱼, 开始炸制(60秒半熟, 再30秒熟)"}, + {"block": OILPOT_1, "item": BREADED_TEMPURA, "target": TEMPURA_1, + "consume": 4, "startTimer": TEMPURA_1, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入裹粉天妇罗x4, 开始炸制(60秒)"}, + {"block": OILPOT_1, "item": DUMPLINGS, "target": DUMPLING_1, + "consume": 3, "startTimer": DUMPLING_1, + "needBelow": LIT_STOVE, "belowTip": "§c油锅需要放在点燃的炉灶上", + "tip": "§a放入生饺子x3, 开始炸制(60秒), 计时/取出都在炸饺子上"}, + + # ---- 三、出锅(熟炸物 + 盘子 -> 掉落盘装成品) ---- + {"block": CHICKEN_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_CHICKEN, "consume": 1, "tip": "§a取出盘装鸡块"}, + {"block": PORK_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_PORK, "consume": 1, "tip": "§a取出炸猪排(盘装)"}, + {"block": FRIES_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_FRIES, "consume": 1, "tip": "§a取出炸薯条(盘装)"}, + {"block": FISH_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_FISH, "consume": 1, "tip": "§a取出盘装炸鱼"}, + {"block": TEMPURA_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_TEMPURA, "consume": 1, "tip": "§a取出天妇罗炸虾"}, + {"block": DUMPLING_2, "item": PLATE, "action": "take_out", + "dropItem": PLATE_DUMPLINGS, "consume": 1, "tip": "§a取出炸饺子(盘装)"}, +] + +# ============================================================================ +# 三、计时规则(供 YouguoTimerService) +# below: 炸物方块正下方须是 点燃的炉灶(stove_2) 才计时 +# ============================================================================ +TIMER_RULES = [ + # 炸鸡块: 生->半熟60秒, 半熟->熟30秒 + {"source": CHICKEN_0, "target": CHICKEN_1, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸鸡块半熟了, 继续炸(30秒)"}, + {"source": CHICKEN_1, "target": CHICKEN_2, "time": 30, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸鸡块炸好了, 手持盘子右键取出"}, + # 炸猪排 + {"source": PORK_0, "target": PORK_1, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸猪排半熟了, 继续炸(30秒)"}, + {"source": PORK_1, "target": PORK_2, "time": 30, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸猪排炸好了, 手持盘子右键取出"}, + # 炸薯条 + {"source": FRIES_0, "target": FRIES_1, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸薯条半熟了, 继续炸(30秒)"}, + {"source": FRIES_1, "target": FRIES_2, "time": 30, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸薯条炸好了, 手持盘子右键取出"}, + # 炸鱼块 + {"source": FISH_0, "target": FISH_1, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸鱼块半熟了, 继续炸(30秒)"}, + {"source": FISH_1, "target": FISH_2, "time": 30, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸鱼块炸好了, 手持盘子右键取出"}, + # 炸天妇罗(单级: 生->熟60秒) + {"source": TEMPURA_1, "target": TEMPURA_2, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e天妇罗炸好了, 手持盘子右键取出"}, + # 炸饺子(单级: 生->熟60秒, 油锅+饺子合体) + {"source": DUMPLING_1, "target": DUMPLING_2, "time": 60, + "below": [LIT_STOVE], "autoStart": True, "doneTip": "§e炸饺子炸好了, 手持盘子右键取出"}, +] + +# ============================================================================ +# 四、中文名映射(提示用) +# ============================================================================ +NAME_MAP = { + OILPOT_0: "油锅(空)", OILPOT_1: "油锅(满油)", OILCAN: "油罐", + MEDICINE_TANK: "玻璃罐", PLATE: "盘子", LIT_STOVE: "点燃的炉灶", + BREADED_CHICKEN: "裹粉鸡块", BREADED_PORK: "裹粉猪肉", + BREADED_FRIES: "裹粉薯条", BREADED_FISH: "裹粉鱼", + BREADED_TEMPURA: "裹粉天妇罗", DUMPLINGS: "生饺子", + CHICKEN_0: "炸鸡块(生)", CHICKEN_1: "炸鸡块(半熟)", CHICKEN_2: "炸鸡块(熟)", + PORK_0: "炸猪排(生)", PORK_1: "炸猪排(半熟)", PORK_2: "炸猪排(熟)", + FRIES_0: "炸薯条(生)", FRIES_1: "炸薯条(半熟)", FRIES_2: "炸薯条(熟)", + FISH_0: "炸鱼块(生)", FISH_1: "炸鱼块(半熟)", FISH_2: "炸鱼块(熟)", + TEMPURA_1: "炸天妇罗(生)", TEMPURA_2: "炸天妇罗(熟)", + DUMPLING_1: "炸饺子(生)", DUMPLING_2: "炸饺子(熟)", + PLATE_CHICKEN: "盘装鸡块", PLATE_PORK: "炸猪排(盘装)", + PLATE_FRIES: "炸薯条(盘装)", PLATE_FISH: "盘装炸鱼", + PLATE_TEMPURA: "天妇罗炸虾", PLATE_DUMPLINGS: "炸饺子(盘装)", +} diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoService.py new file mode 100644 index 0000000..1b3075e --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoService.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +油锅·右键转换引擎(服务端) +================================================================================ +处理"油锅"(xy_youguo) 分类下的右键转换: + 倒油(油罐->玻璃罐) / 取油(玻璃罐->油罐) / 放料(满油锅+裹粉->炸物生) / + 出锅(熟炸物+盘子->掉落盘装成品) + +【事件监听在哪?】 + 本类【不】监听任何事件! + 所有事件由 FeatureDispatcher 统一监听, 按 blockOwner 路由到本类 handleBlockUse。 + +【本类怎么做】 + handleBlockUse <- 入口: 分发器转发右键方块事件 + └─ handleSwitch <- 通用转换(倒油/取油/放料/出锅) + +【计时在?】 + 放料后的炸制计时在 YouguoTimerService, 本类只负责"开始计时"(startTimer 字段调 + FeatureDispatcher.startTimerFor 转发, 记录玩家以便完成/暂停提示送达)。 +================================================================================ +""" +import time +import mod.server.extraServerApi as serverApi + +# ---- 网易API基础对象 ---- +ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类 +comp = serverApi.GetEngineCompFactory() # 组件工厂 + +# ---- 继承 BlueFox 框架的系统基类(提供 CreateEngineItemEntity 等方法) ---- +from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem +from ....BlueFoxTools.ToolsConfig import LevelId + +# ---- 只导入油锅自己的数据库 ---- +from ..YouguoData import SWITCH_RULES + +# ---- 统一分发器 ---- +from ...service.FeatureDispatcher import FeatureDispatcher + + +class YouguoService(BlueFoxServerSystem): + """ + 油锅·右键转换引擎 + """ + + def __init__(self, namespace, systemName): + super(YouguoService, self).__init__(namespace, systemName) + print "===== YouguoService init =====" + + # ---- (方块, 手持物品) -> 规则: O(1) 查找 ---- + self.recipes = {} + self.featureBlocks = set() + for r in SWITCH_RULES: + self.recipes[(r["block"], r.get("item"))] = r + self.featureBlocks.add(r["block"]) + + # ---- 右键冷却表 ---- + self.cooldown = {} + + # ---- 注册到统一分发器 ---- + FeatureDispatcher.register(self) + + # ============================================================================ + # 一、给 FeatureDispatcher 的接口 + # ============================================================================ + + def getFeatureBlocks(self): + """返回本功能管辖的所有右键方块名集合""" + return self.featureBlocks + + def getItemUseOnRules(self): + """手持物品右键方块表面不需要单独路由(放料/出锅都是右键方块本身), 返回空""" + return {} + + def handleBlockUse(self, args): + """由 FeatureDispatcher 转发的右键方块入口""" + 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.recipes.get((blockName, itemDict.get("newItemName") if itemDict else None)) + if not recipe: + return False + if not self.checkCooldown(key, 0.4): + return True + self.handleSwitch(recipe, playerId, pos, dim, itemDict) + return True + + # ============================================================================ + # 二、通用转换执行 + # ============================================================================ + + def handleSwitch(self, recipe, playerId, pos, dim, itemDict): + # 需要下方方块(放料: 油锅下须是点燃的炉灶才计时炸制): 不满足则提示并拦截 + needBelow = recipe.get("needBelow") + if needBelow and not self._belowIs(dim, pos, needBelow): + comp.CreateMsg(playerId).NotifyOneMessage( + playerId, recipe.get("belowTip", "§c下方方块不符合")) + return + + # 消耗物品(数量不足则拦截, 不转换) + consume = recipe.get("consume", 0) + if consume > 1 and itemDict is not None and (itemDict.get("count") or 0) < consume: + comp.CreateMsg(playerId).NotifyOneMessage( + playerId, "§c数量不足, 需要 %d 个" % consume) + return + if consume > 0: + self.consumeItem(playerId, itemDict, consume) + + # 出锅: 熟炸物消失 + 掉落盘装成品(掉落物形式) + action = recipe.get("action") + if action == "take_out": + self.clearBlock(pos, dim) + self.dropItem(dim, pos, recipe["dropItem"]) + # 替换方块(倒油/取油/放料) + if recipe.get("target"): + self.replaceBlock(pos, dim, recipe["target"], playerId) + + # 放料后启动计时(记录发起玩家, 炸好/暂停提示能送达) + if recipe.get("startTimer"): + FeatureDispatcher.startTimerFor(recipe["startTimer"], dim, pos, playerId) + + # 给背包物品(倒油给玻璃罐 / 取油给油罐) + if recipe.get("give"): + self.giveItem(playerId, recipe["give"]) + + # 提示 + if recipe.get("tip"): + comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["tip"]) + + # ============================================================================ + # 三、工具方法:判断正下方是否指定方块(点燃炉灶) + # ============================================================================ + + def _belowIs(self, dim, pos, blockName): + try: + b = comp.CreateBlockInfo(LevelId).GetBlockNew((pos[0], pos[1] - 1, pos[2]), dim) + return bool(b) and b.get("name") == blockName + except Exception: + return False + + # ============================================================================ + # 四、工具方法:获取玩家主手物品 + # ============================================================================ + + def getCarriedItem(self, playerId): + itemComp = comp.CreateItem(playerId) + return itemComp.GetPlayerItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0, True) + + # ============================================================================ + # 五、工具方法:给玩家物品 + # ============================================================================ + + def giveItem(self, playerId, itemName, count=1, aux=0): + try: + itemDict = {"itemName": itemName, "count": count, "auxValue": aux} + comp.CreateItem(playerId).SpawnItemToPlayerInv(itemDict, playerId) + except Exception: + pass + + # ============================================================================ + # 六、工具方法:消耗物品 + # ============================================================================ + + 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), + "userData": itemDict.get("userData", {})}) + + # ============================================================================ + # 七、工具方法:以掉落物形式生成物品 + # ============================================================================ + + def dropItem(self, dim, pos, itemName, count=1, aux=0): + try: + itemDict = {"itemName": itemName, "count": count, "auxValue": aux} + self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2])) + except Exception: + pass + + # ============================================================================ + # 八、工具方法:替换方块 / 清空方块(清空时 aux 强制 0, 兼容定向变体) + # ============================================================================ + + def replaceBlock(self, pos, dim, newBlockName, playerId=None): + creator = playerId if playerId is not None else LevelId + aux = 0 + 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 clearBlock(self, pos, dim): + comp.CreateBlockInfo(LevelId).SetBlockNew( + pos, blockDict={"name": "minecraft:air", "aux": 0}, + oldBlockHandling=0, dimensionId=dim) + + # ============================================================================ + # 九、工具方法:冷却检查 + # ============================================================================ + + def checkCooldown(self, key, cd=0.4): + now = time.time() + last = self.cooldown.get(key, 0) + if now - last < cd: + return False + self.cooldown[key] = now + return True diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoTimerService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoTimerService.py new file mode 100644 index 0000000..ad80dd9 --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/YouguoTimerService.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +油锅·计时炸制引擎(服务端) +================================================================================ +只处理"油锅"(xy_youguo) 分类下的【计时自动转换】: + 炸物(生) -> 60秒 -> 炸物(半熟) -> 30秒 -> 炸物(熟) + 天妇罗/炸饺子: 生 -> 60秒 -> 熟 + +【计时判定(用户指定, 与蒸笼一致)】 + - 炸物方块【正下方】必须是 点燃的炉灶(stove_2) 才开始计时 + - 计时数据仍在【炸物方块本身】上(炸物方块 tick 时判定下方炉灶) + - 炉灶被移走/熄灭 -> 暂停计时(数据保留, 炉灶回来继续) + 提示发起玩家 + +【事件监听在哪?】 + 本类【不】监听任何事件! + ServerBlockEntityTickEvent 由 FeatureDispatcher 统一监听, 再按 timerOwner + 路由到本类的 handleBlockEntityTick。计时用绝对时间戳写方块实体数据, + 退出重进继续计时。 +================================================================================ +""" +import time +import mod.server.extraServerApi as serverApi + +# ---- 网易API基础对象 ---- +ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类 +comp = serverApi.GetEngineCompFactory() # 组件工厂 + +# ---- 继承 BlueFox 框架的系统基类 ---- +from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem +from ....BlueFoxTools.ToolsConfig import LevelId + +# ---- 方块实体数据组件(计时持久化: 开始时间戳+时长写进方块) ---- +blockEntityDataComp = comp.CreateBlockEntityData(LevelId) + +# ---- 只导入油锅自己的数据库 ---- +from ..YouguoData import TIMER_RULES + +# ---- 统一分发器 ---- +from ...service.FeatureDispatcher import FeatureDispatcher + + +class YouguoTimerService(BlueFoxServerSystem): + """ + 油锅·计时炸制引擎 + """ + + def __init__(self, namespace, systemName): + super(YouguoTimerService, self).__init__(namespace, systemName) + print "===== YouguoTimerService init =====" + + # ---- 计时规则: source -> [规则...] ---- + self.timerMap = {} + for t in TIMER_RULES: + self.timerMap.setdefault(t["source"], []).append(t) + + # ---- 注册到分发器的计时路由表 ---- + FeatureDispatcher.registerTimer(self) + + # ============================================================================ + # 一、给 FeatureDispatcher 的接口 + # ============================================================================ + + def getTimerBlocks(self): + """返回本功能的所有"计时源方块"集合(tick 路由用)""" + return set(self.timerMap.keys()) + + def handleBlockEntityTick(self, args): + """由 FeatureDispatcher 转发的计时方块 tick 入口""" + dim = args.get("dimension") + pos = (args.get("posX"), args.get("posY"), args.get("posZ")) + blockName = args.get("blockName", "") + self._tickProcess(dim, pos, blockName) + + # ============================================================================ + # 二、启动计时(右键放料后由 YouguoService 通过 startTimerFor 转发) + # ============================================================================ + + def startTimer(self, dim, pos, blockName, playerId=None): + rules = self.timerMap.get(blockName) + if not rules: + return False + rule = self._pickRule(rules, dim, pos) + if not rule: + return False + try: + data = blockEntityDataComp.GetBlockEntityData(dim, pos) + if data and data["rtdd_tid"] == blockName: + return False + except Exception: + pass + self._writeTimerData(dim, pos, rule, playerId) + return True + + # ============================================================================ + # 三、tick 检查与到点转换 + # ============================================================================ + + def _pickRule(self, rules, dim, pos): + """按"下方方块"匹配规则(点燃炉灶), 返回命中的规则; 无命中返回 None""" + belowPos = (pos[0], pos[1] - 1, pos[2]) + try: + b = comp.CreateBlockInfo(LevelId).GetBlockNew(belowPos, dim) + belowName = b.get("name") if b else "" + except Exception: + belowName = "" + for rule in rules: + if belowName in rule.get("below", [belowName]): + return rule + return None + + def _tickProcess(self, dim, pos, blockName): + try: + rules = self.timerMap.get(blockName) + if not rules: + return + + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + + try: + tid = blockData["rtdd_tid"] + except Exception: + tid = "" + if tid != blockName: + # autoStart: 计时源方块无计时数据且下方点燃炉灶时自动启动 + if any(r.get("autoStart", True) for r in rules): + rule = self._pickRule(rules, dim, pos) + if rule: + self._writeTimerData(dim, pos, rule, None) + return + + # 计时中: 下方点燃炉灶被移走/熄灭 -> 暂停 + 提示 + rule = self._pickRule(rules, dim, pos) + if rule is None: + self._handleBelowBroken(dim, pos, blockName, blockData, rules) + return + + tstart = blockData["rtdd_tstart"] + tdur = blockData["rtdd_tdur"] + + if time.time() - tstart >= tdur: + self._finishTimer(dim, pos, rule, blockData) + except Exception: + pass + + def _handleBelowBroken(self, dim, pos, blockName, data, rules): + """下方点燃炉灶被移走/熄灭: 暂停计时(数据保留, 炉灶回来继续) + 提示发起玩家""" + self._notifyIfOnline(data.get("rtdd_tp"), "§c油锅下的炉灶移走了, 暂停炸制") + + def _finishTimer(self, dim, pos, rule, data): + try: + cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim) + if cur and cur.get("name") != rule["source"]: + self._clearTimerData(dim, pos) + return + # 到点转换 + self.replaceBlock(pos, dim, rule["target"], None) + self._clearTimerData(dim, pos) + tip = rule.get("doneTip", "") + if tip: + self._notifyIfOnline(data.get("rtdd_tp"), tip) + if not data.get("rtdd_tp"): + self._notifyNearby(dim, pos, tip) + except Exception: + pass + + # ============================================================================ + # 四、计时数据读写(存在方块实体数据里,重启不丢) + # ============================================================================ + + def _writeTimerData(self, dim, pos, recipe, playerId=None): + try: + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + now = time.time() + blockData["rtdd_tid"] = recipe["source"] + blockData["rtdd_tstart"] = now + blockData["rtdd_tdur"] = recipe.get("time", 60) + blockData["rtdd_tp"] = playerId if playerId else "" + except Exception: + pass + + def _clearTimerData(self, dim, pos): + try: + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + blockData["rtdd_tid"] = "" + blockData["rtdd_tstart"] = 0 + blockData["rtdd_tdur"] = 0 + blockData["rtdd_tp"] = "" + except Exception: + pass + + # ============================================================================ + # 五、提示工具 + # ============================================================================ + + def _notifyIfOnline(self, playerId, tip): + if not tip or not playerId: + return + try: + ids = comp.CreateGame(LevelId).GetAllPlayerIds() + if playerId in ids: + comp.CreateMsg(playerId).NotifyOneMessage(playerId, tip) + except Exception: + pass + + def _notifyNearby(self, dim, pos, tip, radius=6): + """完成提示兜底: 发起玩家不在/未知时, 提示该方块附近(三维距离)的在线玩家""" + if not tip: + return + try: + r2 = radius * radius + for pid in comp.CreateGame(LevelId).GetAllPlayerIds(): + try: + if comp.CreateDimension(pid).GetEntityDimensionId() != dim: + continue + fp = comp.CreatePos(pid).GetFootPos() + if fp is None: + continue + dx = fp[0] - pos[0] + dy = fp[1] - pos[1] + dz = fp[2] - pos[2] + if dx * dx + dy * dy + dz * dz <= r2: + comp.CreateMsg(pid).NotifyOneMessage(pid, tip) + except Exception: + pass + except Exception: + pass + + # ============================================================================ + # 六、工具方法:替换方块 + # ============================================================================ + + def replaceBlock(self, pos, dim, newBlockName, playerId=None): + creator = playerId if playerId is not None else LevelId + aux = 0 + 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) diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/youguo/service/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oil_frieddumplings_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oil_frieddumplings_1.json index bf86ca6..498ca1b 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oil_frieddumplings_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oil_frieddumplings_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_0.json b/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_0.json index bd85d29..0cf6c95 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_0.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_0.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_1.json index b9af221..d4d26fe 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilchickennuggets_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_0.json b/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_0.json index ccf991d..7e85a5f 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_0.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_0.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_1.json index bd3bdee..ccf07e7 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilfrenchfries_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_0.json b/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_0.json index 3d906c5..114f54c 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_0.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_0.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_1.json index d273162..9237e53 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilpotfish_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_0.json b/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_0.json index 1a8cea9..8f2afc6 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_0.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_0.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_1.json index 11cc8ad..dff90ce 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oilpotporkchop_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/behavior_pack_v8jz0zSR/netease_blocks/oiltempura_1.json b/behavior_pack_v8jz0zSR/netease_blocks/oiltempura_1.json index 8a827a7..bef022e 100644 --- a/behavior_pack_v8jz0zSR/netease_blocks/oiltempura_1.json +++ b/behavior_pack_v8jz0zSR/netease_blocks/oiltempura_1.json @@ -48,7 +48,7 @@ "netease:block_entity": { "client_tick": false, "movable": false, - "tick": false + "tick": true } } } diff --git a/tuilun/全部功能做了什么.txt b/tuilun/全部功能做了什么.txt index 5844b40..6b223a2 100644 --- a/tuilun/全部功能做了什么.txt +++ b/tuilun/全部功能做了什么.txt @@ -647,3 +647,29 @@ FurnitureData+FurnitureService 实现, 注册到 FeatureDispatcher) 【留后】吃(熟肉包子/馍/煎饼卷/熟竹筒饭/蛋糕小块 右键吃)、巧克力蛋糕系列 见 全部功能没做什么.txt 面食节 +######################################## +【油锅】(xy_youguo) 2026-09-11 +######################################## +实现于 xiangyemod/youguo (YouguoData + YouguoService + YouguoTimerService, 注册到 FeatureDispatcher) +(10个计时炸物方块 netease:block_entity tick 已改 true) + +一、油锅基础 +- 倒油: 手持油罐右键空油锅 -> 满油锅 + 油罐变回玻璃罐 [已实现] +- 取油: 手持玻璃罐右键满油锅 -> 空油锅 + 获得油罐 [已实现] +- 放料: 手持裹粉半成品/生饺子右键满油锅(油锅下须点燃炉灶 stove_2, 否则提示不放料) -> 炸物(生) [已实现] + 消耗: 裹粉鸡块1 / 裹粉猪肉1 / 裹粉薯条3 / 裹粉鱼1 / 裹粉天妇罗4 / 生饺子3 [已实现] + +二、炸制计时(炸物方块正下方是点燃炉灶才计时, 计时数据在炸物方块本身; 炉灶移走暂停+提示) +- 炸鸡块/炸猪排/炸薯条/炸鱼块: 生 -> 60秒 -> 半熟 -> 30秒 -> 熟 [已实现] +- 炸天妇罗/炸饺子: 生 -> 60秒 -> 熟(单级); 炸饺子=油锅+饺子合体, 计时/取出均在炸饺子上 [已实现] +- 计时完成提示(半熟/熟)发给发起玩家, 不在则提示附近6格 [已实现] + +三、出锅(手持盘子右键熟炸物, 消耗1盘子, 盘装成品以掉落物形式出现, 熟炸物方块消失) +- 炸鸡块熟 -> 盘装鸡块(plate_38); 炸猪排熟 -> plate_36; 炸薯条熟 -> plate_37; + 炸鱼块熟 -> plate_39; 炸天妇罗熟 -> 天妇罗炸虾(plate_49); 炸饺子熟 -> plate_51 [已实现] + +【提示/体验】倒油/取油/放料/出锅均有提示(含消耗数量与计时时长); 数量不足/油锅下无点燃炉灶 +有提示并拦截; 右键冷却0.4秒; 手持方块物品右键油锅类功能方块取消默认放置(全局) [已实现] +【留后】各盘装成品 右键吃(总食用功能后续实现)、天妇罗炸虾递减吃(整份->半份 plate_49a)、 +盘子用后变脏 + diff --git a/tuilun/全部功能没做什么.txt b/tuilun/全部功能没做什么.txt index f13e806..c9a0181 100644 --- a/tuilun/全部功能没做什么.txt +++ b/tuilun/全部功能没做什么.txt @@ -153,9 +153,10 @@ - 未做原因: 属于食物食用链路, 留到具体菜品/总食用功能实现 (厨具转化关系.txt 五、盘子) 二、油罐⇄油锅/锅铲上油 (oilcan -> 空油锅/炒锅) -- 涉及: 油罐取油耗尽变回玻璃罐(18)、油罐直接上油空炒锅→满油油锅(18a)、 - 锅铲<->带油锅铲(19/19a)、带油锅铲上油炒锅(20) -- 未做原因: 依赖油锅/炒锅(具体菜品)分类, 留到炒锅/油锅功能实现时做 (厨具转化关系.txt 六、七) +- 油锅部分 已实现(2026-09-11): 油罐倒油 空油锅->满油锅 + 油罐变玻璃罐; 玻璃罐取油 -> 空油锅 + 油罐 + (油锅转化关系.txt 一) +- 仍未做(依赖炒锅分类): 油罐直接上油空炒锅->满油油锅(18a)、锅铲<->带油锅铲(19/19a)、 + 带油锅铲上油炒锅(20) (厨具转化关系.txt 六、七; 留到炒锅功能实现时做) 三、蒸笼-蒸熟 (steamercover + 生食 -> 熟) - 涉及: 蒸饺子/蒸包子/蒸竹筒饭/蒸牛奶鸡蛋