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.shousi.service.ShousiService import ShousiService
from xiangyemod.mianshi.service.MianshiService import MianshiService from xiangyemod.mianshi.service.MianshiService import MianshiService
from xiangyemod.mianshi.service.MianshiTimerService import MianshiTimerService 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(): if not serverApi.IsInServer():
pass 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": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
@@ -48,7 +48,7 @@
"netease:block_entity": { "netease:block_entity": {
"client_tick": false, "client_tick": false,
"movable": false, "movable": false,
"tick": false "tick": true
} }
} }
} }
+26
View File
@@ -647,3 +647,29 @@ FurnitureData+FurnitureService 实现, 注册到 FeatureDispatcher)
【留后】吃(熟肉包子/馍/煎饼卷/熟竹筒饭/蛋糕小块 右键吃)、巧克力蛋糕系列 【留后】吃(熟肉包子/馍/煎饼卷/熟竹筒饭/蛋糕小块 右键吃)、巧克力蛋糕系列
见 全部功能没做什么.txt 面食节 见 全部功能没做什么.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 五、盘子) - 未做原因: 属于食物食用链路, 留到具体菜品/总食用功能实现 (厨具转化关系.txt 五、盘子)
二、油罐⇄油锅/锅铲上油 (oilcan -> 空油锅/炒锅) 二、油罐⇄油锅/锅铲上油 (oilcan -> 空油锅/炒锅)
- 涉及: 油罐取油耗尽变回玻璃罐(18)、油罐直接上油空炒锅→满油油锅(18a)、 - 油锅部分 已实现(2026-09-11): 油罐倒油 空油锅->满油锅 + 油罐变玻璃罐; 玻璃罐取油 -> 空油锅 + 油罐
锅铲<->带油锅铲(19/19a)、带油锅铲上油炒锅(20) (油锅转化关系.txt 一)
- 未做原因: 依赖油锅/炒锅(具体菜品)分类, 留到炒锅/油锅功能实现时做 (厨具转化关系.txt 六、七) - 仍未做(依赖炒锅分类): 油罐直接上油空炒锅->满油油锅(18a)、锅铲<->带油锅铲(19/19a)、
带油锅铲上油炒锅(20) (厨具转化关系.txt 六、七; 留到炒锅功能实现时做)
三、蒸笼-蒸熟 (steamercover + 生食 -> 熟) 三、蒸笼-蒸熟 (steamercover + 生食 -> 熟)
- 涉及: 蒸饺子/蒸包子/蒸竹筒饭/蒸牛奶鸡蛋 - 涉及: 蒸饺子/蒸包子/蒸竹筒饭/蒸牛奶鸡蛋