This commit is contained in:
wenhe committed 2026-09-11 15:32:06 +08:00
1 parent 69503e18a7
commit c58f5fea40
18 files changed
+697 -13

No files matched your search

@@ -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
@@ -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: "炸饺子(盘装)",
}
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
@@ -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
@@ -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)
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
@@ -48,7 +48,7 @@
"netease:block_entity": {
"client_tick": false,
"movable": false,
"tick": false
"tick": true
}
}
}
+26
View File
@@ -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)、
盘子用后变脏
+4 -3
View File
@@ -153,9 +153,10 @@
- 未做原因: 属于食物食用链路, 留到具体菜品/总食用功能实现 (厨具转化关系.txt 五、盘子)
二、油罐⇄油锅/锅铲上油 (oilcan -> 空油锅/炒锅)
- 涉及: 油罐取油耗尽变回玻璃罐(18)、油罐直接上油空炒锅→满油油锅(18a)、
锅铲<->带油锅铲(19/19a)、带油锅铲上油炒锅(20)
- 未做原因: 依赖油锅/炒锅(具体菜品)分类, 留到炒锅/油锅功能实现时做 (厨具转化关系.txt 六、七)
- 油锅部分 已实现(2026-09-11): 油罐倒油 空油锅->满油锅 + 油罐变玻璃罐; 玻璃罐取油 -> 空油锅 + 油罐
(油锅转化关系.txt 一)
- 仍未做(依赖炒锅分类): 油罐直接上油空炒锅->满油油锅(18a)、锅铲<->带油锅铲(19/19a)、
带油锅铲上油炒锅(20) (厨具转化关系.txt 六、七; 留到炒锅功能实现时做)
三、蒸笼-蒸熟 (steamercover + 生食 -> 熟)
- 涉及: 蒸饺子/蒸包子/蒸竹筒饭/蒸牛奶鸡蛋