文档重写 厨具功能实现
This commit is contained in:
1 parent
f55386659b
commit
262b91ba08
28 files changed
+1039
-31
No files matched your search
@@ -12,6 +12,8 @@ from xiangyemod.yuanbanfangzhi.service.YuanbanService import YuanbanService
|
||||
from xiangyemod.jiaju.service.FurnitureService import FurnitureService
|
||||
from xiangyemod.yinliao.service.YinliaoService import YinliaoService
|
||||
from xiangyemod.yinliao.service.YinliaoTimerService import YinliaoTimerService
|
||||
from xiangyemod.chuju.service.ChujuService import ChujuService
|
||||
from xiangyemod.chuju.service.ChujuTimerService import ChujuTimerService
|
||||
|
||||
if not serverApi.IsInServer():
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
厨具(xy_kitchenware)转化规则数据
|
||||
================================================================================
|
||||
统一管理"厨具"(xy_kitchenware) 分类下的转化规则常量。只被
|
||||
ChujuService / ChujuTimerService 读取,FeatureDispatcher 通过方块名/物品名
|
||||
集合路由到对应服务。
|
||||
|
||||
【本次实现的五大功能】
|
||||
1) 炉灶 (stove + 木条 -> 加木条 -> 打火石/烈焰弹 -> 点燃) 纯右键
|
||||
2) 丸子机 (面团 -> 鱿鱼须 -> 任意配料 -> 计时半熟 -> 计时熟 -> 盘子取) 右键+计时
|
||||
3) 搅拌器 (肉/牛奶/蛋 分别搅打; 牛奶+蛋互通; 蛋+木碗=奶油; +盘子=蛋液) 纯右键
|
||||
4) 石磨 (黄豆 -> 研磨旋转4次 -> 玻璃罐装油罐) 右键+计数
|
||||
5) 蒸笼 (盖盖/掀盖 双向) 纯右键
|
||||
|
||||
【留后】(到具体菜品/分类时再实现, 本模块不处理)
|
||||
- 盘子 plate_0 -> 脏盘子 plate_0a
|
||||
- 油罐 oilcan -> 空油锅/炒锅上油 (18/18a)
|
||||
- 锅铲 <-> 带油锅铲 上油 (19/19a/20)
|
||||
- 蒸笼蒸熟 (13)
|
||||
- 锅铲/锅勺/菜刀/擀面杖的具体用法 (21~24)
|
||||
|
||||
【计时说明】
|
||||
丸子机 _3(加料完成) -> 30秒 -> _4(半熟) -> 30秒 -> _5(熟) 为两段连续计时,
|
||||
autoStart=True: 方块变成计时源时会自动写入开始时间戳并启动。
|
||||
计时方块需配置 netease:block_entity tick:true (meatballmaker_3/4 已改)。
|
||||
================================================================================
|
||||
"""
|
||||
|
||||
# ============================================================================
|
||||
# 一、炉灶 (stove 空 -> stove_1 加木条 -> stove_2 点燃)
|
||||
# ============================================================================
|
||||
STOVE_RULES = [
|
||||
{"source": "rtdd:stove", "item": "rtdd:b_15",
|
||||
"target": "rtdd:stove_1", "consume": 4, "tip": "§a炉灶加好了木条"},
|
||||
{"source": "rtdd:stove_1", "item": "minecraft:flint_and_steel",
|
||||
"target": "rtdd:stove_2", "tip": "§a炉灶点燃了"},
|
||||
{"source": "rtdd:stove_1", "item": "minecraft:fire_charge",
|
||||
"target": "rtdd:stove_2", "consume": 1, "tip": "§a炉灶点燃了"},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# 二、丸子机 (meatballmaker_0 空 ~ _5 熟, 章鱼小丸子)
|
||||
# _0+面团 -> _1 -> +鱿鱼须 -> _2 -> +任意配料 -> _3 -> 计时 -> _4 半熟
|
||||
# -> 计时 -> _5 熟 -> +盘子 -> 章鱼小丸子(plate_40), 还原空
|
||||
# ============================================================================
|
||||
MEATBALLMAKER_RULES = [
|
||||
{"source": "rtdd:meatballmaker_0", "item": "rtdd:dough_0",
|
||||
"target": "rtdd:meatballmaker_1", "consume": 1, "tip": "§a加入面团"},
|
||||
{"source": "rtdd:meatballmaker_1", "item": "rtdd:rawsquidwhiskersr",
|
||||
"target": "rtdd:meatballmaker_2", "consume": 4, "tip": "§a加入鱿鱼须"},
|
||||
{"source": "rtdd:meatballmaker_2", "item": "*any",
|
||||
"target": "rtdd:meatballmaker_3", "consume": 1, "tip": "§a加入配料,开始烹制丸子"},
|
||||
{"source": "rtdd:meatballmaker_5", "item": "rtdd:plate_0",
|
||||
"target": "rtdd:meatballmaker_0", "consume": 1,
|
||||
"dropItem": [{"name": "rtdd:plate_40", "count": 1}],
|
||||
"tip": "§a取出了章鱼小丸子"},
|
||||
]
|
||||
|
||||
# 丸子机计时: _3(加料完成) -> 30s -> _4(半熟) -> 30s -> _5(熟)
|
||||
MEATBALL_TIMERS = [
|
||||
{"source": "rtdd:meatballmaker_3", "target": "rtdd:meatballmaker_4",
|
||||
"time": 30, "autoStart": True, "doneTip": "§e丸子半熟了"},
|
||||
{"source": "rtdd:meatballmaker_4", "target": "rtdd:meatballmaker_5",
|
||||
"time": 30, "autoStart": True, "doneTip": "§e丸子熟了!"},
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# 三、搅拌器 (beater 空)
|
||||
# 肉: beater -> beatermeat_0 -> beatermeat_1 -> 空手取肉馅(记住放的是猪/牛)
|
||||
# 奶: beater -> beatermike_0/1/2 (每次1个牛奶桶, 返还空桶)
|
||||
# 蛋: beater -> beater_custard_0/1/2 (每次1个鸡蛋)
|
||||
# 互通: 牛奶_3(beatermike_2)+蛋 -> 鸡蛋牛奶(beater_eggsandmilk_0)
|
||||
# 蛋_3(beater_custard_2)+奶 -> 鸡蛋牛奶(beater_eggsandmilk_0)
|
||||
# 鸡蛋牛奶: _0 -> 空手搅打 -> _1 -> 空手搅打 -> _2 -> +盘子 -> 蛋液半成品(plate_34)
|
||||
# 奶油: 蛋_3(beater_custard_2)+木碗 -> 奶油(cream_0), 还原空
|
||||
# ============================================================================
|
||||
# 原版生肉 -> 对应肉馅方块(取出时按放入的肉决定)
|
||||
MEAT_ITEMS = {
|
||||
"minecraft:porkchop": "rtdd:rawmeatball_0", # 生猪排 -> 生猪肉馅
|
||||
"minecraft:beef": "rtdd:rawmeatball_1", # 生牛肉 -> 生牛肉馅
|
||||
}
|
||||
MILK_ITEM = "minecraft:milk_bucket" # 牛奶桶
|
||||
BUCKET_ITEM = "minecraft:bucket" # 空桶(牛奶桶用掉返还)
|
||||
EGG_ITEM = "minecraft:egg" # 鸡蛋
|
||||
BOWL_ITEM = "rtdd:bowlasoup" # 木碗(取奶油)
|
||||
PLATE_ITEM = "rtdd:plate_0" # 盘子(取蛋液半成品)
|
||||
CREAM_ITEM = "rtdd:cream_0" # 奶油(蛋完成态+木碗取出)
|
||||
EGG_CUSTARD_ITEM = "rtdd:plate_34" # 蛋液半成品(归面食.甜食/面团·胚料)
|
||||
|
||||
# 搅拌器全部方块态
|
||||
BEATER_BLOCKS = [
|
||||
"rtdd:beater",
|
||||
"rtdd:beatermeat_0", "rtdd:beatermeat_1",
|
||||
"rtdd:beatermike_0", "rtdd:beatermike_1", "rtdd:beatermike_2",
|
||||
"rtdd:beater_custard_0", "rtdd:beater_custard_1", "rtdd:beater_custard_2",
|
||||
"rtdd:beater_eggsandmilk_0", "rtdd:beater_eggsandmilk_1", "rtdd:beater_eggsandmilk_2",
|
||||
]
|
||||
|
||||
# ============================================================================
|
||||
# 四、石磨 (stonemill 空)
|
||||
# + 黄豆(16个) -> 加入黄豆 -> 空手右键研磨(每次旋转90度) -> 4次完成
|
||||
# -> + 玻璃罐 -> 油罐(oilcan), 还原空
|
||||
# 研磨进度存方块实体数据 rtdd_mill (0=已放豆, 1~3=旋转中, 4=完成)
|
||||
# ============================================================================
|
||||
SOYBEAN_ITEM = "rtdd:soybean" # 黄豆
|
||||
SOYBEAN_COUNT = 16 # 磨一次要16个黄豆
|
||||
STONEMILL_GRIND_COUNT = 4 # 右键4次完成研磨
|
||||
GLASS_TANK_ITEM = "rtdd:medicinetank" # 玻璃罐(取出油罐)
|
||||
OIL_CAN_ITEM = "rtdd:oilcan" # 油罐(石磨产物)
|
||||
|
||||
# ============================================================================
|
||||
# 五、蒸笼 (steamer 空 <-> steamercover 加盖) 双向
|
||||
# ============================================================================
|
||||
STEAMER_RULES = [
|
||||
{"source": "rtdd:steamer", "item": None,
|
||||
"target": "rtdd:steamercover", "tip": "§a蒸笼盖上了盖子"},
|
||||
{"source": "rtdd:steamercover", "item": None,
|
||||
"target": "rtdd:steamer", "tip": "§a蒸笼掀开了盖子"},
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
+554
@@ -0,0 +1,554 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
厨具·右键转换引擎(服务端)
|
||||
================================================================================
|
||||
处理"厨具"(xy_kitchenware) 分类下的右键转换:
|
||||
炉灶(加木条/点燃)、丸子机(放面团/放鱿鱼须/放配料/盘子取成品)、
|
||||
搅拌器(肉/牛奶/蛋/鸡蛋牛奶/奶油/蛋液)、石磨(加黄豆/研磨/玻璃罐取油)、
|
||||
蒸笼(盖盖/掀盖)。
|
||||
|
||||
【与计时转换解耦】
|
||||
丸子机"加料完成 -> 半熟 -> 熟"的计时拆到 ChujuTimerService,
|
||||
本类右键到计时源方块连锁后由计时服务自动启动(autoStart)。
|
||||
|
||||
【事件监听在哪?】
|
||||
本类【不】监听任何事件!
|
||||
所有事件由 FeatureDispatcher 统一监听, 按 blockOwner 路由到本类 handleBlockUse。
|
||||
|
||||
【本类怎么做(自上而下的方法系列)】
|
||||
handleBlockUse ← 入口:分发器转发右键方块事件
|
||||
├─ _handleBeater ← 搅拌器(多分支, 需记录放入的肉类型)
|
||||
├─ _handleStonemill ← 石磨(黄豆/研磨旋转/玻璃罐取油)
|
||||
└─ handleSwitch ← 炉灶/丸子机/蒸笼 的通用开关规则
|
||||
(通用工具) getCarriedItem / consumeItem / giveItem / replaceBlock /
|
||||
dropItems / checkCooldown
|
||||
================================================================================
|
||||
"""
|
||||
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
|
||||
|
||||
# ---- 方块实体数据组件(搅拌器肉类型/石磨研磨进度持久化) ----
|
||||
blockEntityDataComp = comp.CreateBlockEntityData(LevelId)
|
||||
# ---- 游戏组件(AddTimer 延迟写入 block entity 兜底) ----
|
||||
gameComp = comp.CreateGame(LevelId)
|
||||
|
||||
# ---- 只导入厨具自己的数据库 ----
|
||||
from ..ChujuData import (
|
||||
STOVE_RULES, MEATBALLMAKER_RULES,
|
||||
MEAT_ITEMS, MILK_ITEM, BUCKET_ITEM, EGG_ITEM,
|
||||
BOWL_ITEM, PLATE_ITEM, CREAM_ITEM, EGG_CUSTARD_ITEM, BEATER_BLOCKS,
|
||||
SOYBEAN_ITEM, SOYBEAN_COUNT, STONEMILL_GRIND_COUNT,
|
||||
GLASS_TANK_ITEM, OIL_CAN_ITEM, STEAMER_RULES,
|
||||
)
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
|
||||
class ChujuService(BlueFoxServerSystem):
|
||||
"""
|
||||
厨具·右键转换引擎
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
super(ChujuService, self).__init__(namespace, systemName)
|
||||
print "===== ChujuService init ====="
|
||||
|
||||
# ---- 通用开关规则字典 (炉灶/丸子机/蒸笼): (方块, 物品) -> 规则 ----
|
||||
self.switchRecipes = {}
|
||||
for r in STOVE_RULES + MEATBALLMAKER_RULES + STEAMER_RULES:
|
||||
self.switchRecipes[(r["source"], r.get("item"))] = r
|
||||
|
||||
# ---- 本功能管辖的所有右键方块 ----
|
||||
self.featureBlocks = set()
|
||||
for r in STOVE_RULES + MEATBALLMAKER_RULES + STEAMER_RULES:
|
||||
self.featureBlocks.add(r["source"])
|
||||
for b in BEATER_BLOCKS:
|
||||
self.featureBlocks.add(b)
|
||||
self.featureBlocks.add("rtdd:stonemill")
|
||||
|
||||
# ---- 搅拌器方块集合(专用 handler) ----
|
||||
self.beaterBlockSet = set(BEATER_BLOCKS)
|
||||
|
||||
# ---- 右键冷却表 ----
|
||||
self.cooldown = {}
|
||||
|
||||
# ---- 注册到统一分发器 ----
|
||||
FeatureDispatcher.register(self)
|
||||
|
||||
# ============================================================================
|
||||
# 一、给 FeatureDispatcher 的接口
|
||||
# ============================================================================
|
||||
|
||||
def getFeatureBlocks(self):
|
||||
"""返回本功能管辖的所有方块名集合(右键路由用)"""
|
||||
return self.featureBlocks
|
||||
|
||||
def getItemUseOnRules(self):
|
||||
"""手持物品右键方块表面不需要单独路由, 返回空"""
|
||||
return {}
|
||||
|
||||
def handleBlockUse(self, args):
|
||||
"""
|
||||
由 FeatureDispatcher 转发的右键方块入口。
|
||||
返回 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)
|
||||
itemName = itemDict.get("newItemName") if itemDict else None
|
||||
|
||||
# ---- 搅拌器: 专用 handler ----
|
||||
if blockName in self.beaterBlockSet:
|
||||
return self._handleBeater(blockName, playerId, pos, dim, itemDict, itemName, key)
|
||||
|
||||
# ---- 石磨: 专用 handler ----
|
||||
if blockName == "rtdd:stonemill":
|
||||
return self._handleStonemill(playerId, pos, dim, itemDict, itemName, key)
|
||||
|
||||
# ---- 炉灶/丸子机/蒸笼: 通用 switch 规则 ----
|
||||
recipe = self.findRecipe(blockName, itemDict)
|
||||
if not recipe:
|
||||
return False
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.handleSwitch(recipe, playerId, pos, dim, itemDict, key)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# 二、通用规则匹配(精确 + *any)
|
||||
# ============================================================================
|
||||
|
||||
def findRecipe(self, blockName, itemDict):
|
||||
"""精确匹配 (方块, 物品); 其次 *any(丸子机 _2 手持任意物品)"""
|
||||
itemName = itemDict.get("newItemName") if itemDict else None
|
||||
recipe = self.switchRecipes.get((blockName, itemName))
|
||||
if recipe:
|
||||
return recipe
|
||||
if itemName and itemName != "minecraft:air":
|
||||
return self.switchRecipes.get((blockName, "*any"))
|
||||
return None
|
||||
|
||||
# ============================================================================
|
||||
# 三、通用开关规则执行(炉灶/丸子机/蒸笼)
|
||||
# ============================================================================
|
||||
|
||||
def handleSwitch(self, recipe, playerId, pos, dim, itemDict, key):
|
||||
"""消耗物品 → 替换方块 → 掉落/给物品 → 提示"""
|
||||
# 数量检查(如炉灶加柴需4个木条, 丸子机加鱿鱼须需4个)
|
||||
consume = recipe.get("consume", 0)
|
||||
if consume > 1 and itemDict is not None:
|
||||
if (itemDict.get("count") or 0) < consume:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§c数量不足,需要 %d 个" % consume)
|
||||
return
|
||||
|
||||
# 消耗物品
|
||||
if recipe.get("item") is not None and consume > 0:
|
||||
self.consumeItem(playerId, itemDict, consume)
|
||||
|
||||
# 替换方块
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
# 掉落物
|
||||
if recipe.get("dropItem"):
|
||||
self.dropItems(dim, pos, recipe["dropItem"])
|
||||
|
||||
# 给背包物品
|
||||
if recipe.get("giveItem"):
|
||||
self.giveItem(playerId, recipe["giveItem"])
|
||||
|
||||
# 提示
|
||||
if recipe.get("tip"):
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["tip"])
|
||||
|
||||
# ============================================================================
|
||||
# 四、搅拌器(肉/牛奶/蛋/鸡蛋牛奶/奶油/蛋液)
|
||||
# ============================================================================
|
||||
|
||||
def _handleBeater(self, blockName, playerId, pos, dim, itemDict, itemName, key):
|
||||
# ---------- 空搅拌器:放入第一份食材 ----------
|
||||
if blockName == "rtdd:beater":
|
||||
if itemName in MEAT_ITEMS:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.replaceBlock(pos, dim, "rtdd:beatermeat_0", playerId)
|
||||
self._writeMeat(dim, pos, itemName)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a放入肉,再放一份搅打")
|
||||
return True
|
||||
if itemName == MILK_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.giveItem(playerId, BUCKET_ITEM)
|
||||
self.replaceBlock(pos, dim, "rtdd:beatermike_0", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a放入牛奶")
|
||||
return True
|
||||
if itemName == EGG_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.replaceBlock(pos, dim, "rtdd:beater_custard_0", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a放入鸡蛋")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---------- 肉态: 再放一份同种肉 -> 搅打完成 ----------
|
||||
if blockName == "rtdd:beatermeat_0":
|
||||
meat = self._readMeat(pos, dim)
|
||||
if itemName in MEAT_ITEMS and itemName == meat:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.replaceBlock(pos, dim, "rtdd:beatermeat_1", playerId)
|
||||
self._writeMeat(dim, pos, meat)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a搅打完成,空手取出肉馅")
|
||||
return True
|
||||
if itemName in MEAT_ITEMS and itemName != meat:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§c不能混入不同种类的肉")
|
||||
return True
|
||||
return False
|
||||
|
||||
if blockName == "rtdd:beatermeat_1":
|
||||
if not itemName or itemName == "minecraft:air":
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
meat = self._readMeat(pos, dim)
|
||||
meatball = MEAT_ITEMS.get(meat) if meat else None
|
||||
if meatball:
|
||||
self.dropItems(dim, pos, [{"name": meatball, "count": 1}])
|
||||
self.replaceBlock(pos, dim, "rtdd:beater", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a取出了肉馅")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---------- 牛奶态: 继续加牛奶 -> 牛奶_3 ----------
|
||||
if blockName in ("rtdd:beatermike_0", "rtdd:beatermike_1"):
|
||||
if itemName == MILK_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.giveItem(playerId, BUCKET_ITEM)
|
||||
target = "rtdd:beatermike_1" if blockName == "rtdd:beatermike_0" \
|
||||
else "rtdd:beatermike_2"
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a继续搅打牛奶")
|
||||
return True
|
||||
return False
|
||||
|
||||
# 牛奶完成态(牛奶_3) + 蛋 -> 鸡蛋牛奶
|
||||
if blockName == "rtdd:beatermike_2":
|
||||
if itemName == EGG_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.replaceBlock(pos, dim, "rtdd:beater_eggsandmilk_0", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a加入鸡蛋,空手搅打做鸡蛋牛奶")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---------- 蛋态: 继续加蛋 -> 蛋_3 ----------
|
||||
if blockName in ("rtdd:beater_custard_0", "rtdd:beater_custard_1"):
|
||||
if itemName == EGG_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
target = "rtdd:beater_custard_1" if blockName == "rtdd:beater_custard_0" \
|
||||
else "rtdd:beater_custard_2"
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
if target == "rtdd:beater_custard_2":
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a蛋搅打完成,可加木碗取奶油或加牛奶做鸡蛋牛奶")
|
||||
else:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a继续搅打鸡蛋")
|
||||
return True
|
||||
return False
|
||||
|
||||
# 蛋完成态(蛋_3): +木碗取奶油 / +牛奶做鸡蛋牛奶
|
||||
if blockName == "rtdd:beater_custard_2":
|
||||
if itemName == BOWL_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.dropItems(dim, pos, [{"name": CREAM_ITEM, "count": 1}])
|
||||
self.replaceBlock(pos, dim, "rtdd:beater", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a取出了奶油")
|
||||
return True
|
||||
if itemName == MILK_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.giveItem(playerId, BUCKET_ITEM)
|
||||
self.replaceBlock(pos, dim, "rtdd:beater_eggsandmilk_0", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a加入牛奶,空手搅打做鸡蛋牛奶")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---------- 鸡蛋牛奶态: 空手搅打推进 1/2 -> 3 ----------
|
||||
if blockName in ("rtdd:beater_eggsandmilk_0", "rtdd:beater_eggsandmilk_1"):
|
||||
if not itemName or itemName == "minecraft:air":
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
target = "rtdd:beater_eggsandmilk_1" \
|
||||
if blockName == "rtdd:beater_eggsandmilk_0" else "rtdd:beater_eggsandmilk_2"
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
if target == "rtdd:beater_eggsandmilk_2":
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a鸡蛋牛奶完成,可加盘子取蛋液")
|
||||
else:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a搅打中…")
|
||||
return True
|
||||
return False
|
||||
|
||||
# 鸡蛋牛奶完成态 + 盘子 -> 蛋液半成品
|
||||
if blockName == "rtdd:beater_eggsandmilk_2":
|
||||
if itemName == PLATE_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.dropItems(dim, pos, [{"name": EGG_CUSTARD_ITEM, "count": 1}])
|
||||
self.replaceBlock(pos, dim, "rtdd:beater", playerId)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a取出了蛋液半成品")
|
||||
return True
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
# ============================================================================
|
||||
# 五、石磨(加黄豆 -> 研磨旋转 -> 玻璃罐取油罐)
|
||||
# ============================================================================
|
||||
|
||||
def _handleStonemill(self, playerId, pos, dim, itemDict, itemName, key):
|
||||
prog = self._readMill(pos, dim) # None=空, 0~3=研磨中, 4=完成
|
||||
|
||||
# ---- 空石磨 + 黄豆(16) -> 加入黄豆 ----
|
||||
if prog is None:
|
||||
if itemName == SOYBEAN_ITEM:
|
||||
if (itemDict.get("count") or 0) < SOYBEAN_COUNT:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§c需要 %d 个黄豆" % SOYBEAN_COUNT)
|
||||
return True
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, SOYBEAN_COUNT)
|
||||
self._writeMill(dim, pos, 0)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a加入黄豆,空手右键研磨")
|
||||
return True
|
||||
return False
|
||||
|
||||
# ---- 研磨中: 空手右键旋转一次 ----
|
||||
if 0 <= prog < STONEMILL_GRIND_COUNT:
|
||||
if itemName and itemName != "minecraft:air":
|
||||
return False
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
newProg = prog + 1
|
||||
self._rotateMill(pos, dim)
|
||||
self._writeMill(dim, pos, newProg)
|
||||
if newProg >= STONEMILL_GRIND_COUNT:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a研磨完成,可加玻璃罐取油")
|
||||
else:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a研磨中…(%d/%d)" % (newProg, STONEMILL_GRIND_COUNT))
|
||||
return True
|
||||
|
||||
# ---- 完成: + 玻璃罐 -> 油罐 ----
|
||||
if prog >= STONEMILL_GRIND_COUNT:
|
||||
if itemName == GLASS_TANK_ITEM:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
self.dropItems(dim, pos, [{"name": OIL_CAN_ITEM, "count": 1}])
|
||||
self._clearMill(dim, pos)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a取出了油罐")
|
||||
return True
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
# ============================================================================
|
||||
# 六、搅拌器肉类型 读/写(字段 rtdd_meat)
|
||||
# ============================================================================
|
||||
|
||||
def _writeMeat(self, dim, pos, itemName):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
gameComp.AddTimer(0.1, self._writeMeatDelayed, dim, pos, itemName)
|
||||
return
|
||||
data["rtdd_meat"] = str(itemName)
|
||||
except Exception:
|
||||
gameComp.AddTimer(0.1, self._writeMeatDelayed, dim, pos, itemName)
|
||||
|
||||
def _writeMeatDelayed(self, dim, pos, itemName):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is not None:
|
||||
data["rtdd_meat"] = str(itemName)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _readMeat(self, pos, dim):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
return None
|
||||
return data["rtdd_meat"] or None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
# ============================================================================
|
||||
# 七、石磨研磨进度 读/写(字段 rtdd_mill)
|
||||
# ============================================================================
|
||||
|
||||
def _writeMill(self, dim, pos, prog):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
gameComp.AddTimer(0.1, self._writeMillDelayed, dim, pos, prog)
|
||||
return
|
||||
data["rtdd_mill"] = int(prog)
|
||||
except Exception:
|
||||
gameComp.AddTimer(0.1, self._writeMillDelayed, dim, pos, prog)
|
||||
|
||||
def _writeMillDelayed(self, dim, pos, prog):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is not None:
|
||||
data["rtdd_mill"] = int(prog)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _readMill(self, pos, dim):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
return None
|
||||
return data["rtdd_mill"]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _clearMill(self, dim, pos):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is not None:
|
||||
data["rtdd_mill"] = -1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _rotateMill(self, pos, dim):
|
||||
"""石磨旋转一次(aux + 1, 四方向回归)"""
|
||||
try:
|
||||
old = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
aux = old.get("aux", 0)
|
||||
comp.CreateBlockInfo(LevelId).SetBlockNew(
|
||||
pos, blockDict={"name": "rtdd:stonemill", "aux": (aux + 1) % 4},
|
||||
oldBlockHandling=0, dimensionId=dim)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ============================================================================
|
||||
# 八、工具方法:获取玩家主手物品
|
||||
# ============================================================================
|
||||
|
||||
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 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 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):
|
||||
now = time.time()
|
||||
last = self.cooldown.get(key, 0)
|
||||
if now - last < cd:
|
||||
return False
|
||||
self.cooldown[key] = now
|
||||
return True
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
厨具·计时转换引擎(服务端)
|
||||
================================================================================
|
||||
只处理"厨具"(xy_kitchenware) 分类下的【计时自动转换】:
|
||||
丸子机(加料完成 meatballmaker_3) -> 30秒 -> 半熟(meatballmaker_4)
|
||||
半熟(meatballmaker_4) -> 30秒 -> 熟(meatballmaker_5)
|
||||
|
||||
【与右键转换解耦】
|
||||
右键转换(放面团/放鱿鱼须/放配料/盘子取成品)在 ChujuService,本类只负责计时。
|
||||
丸子机为 autoStart=True:方块变成计时源时自动写入开始时间戳并启动,
|
||||
因此 _3 -> _4 -> _5 两段连续计时无需右键服务显式调用 startTimer。
|
||||
|
||||
【计时怎么做到"退出游戏继续计时"】
|
||||
不做倒计时,只记"开始时刻":
|
||||
- 启动时把 开始时间戳(rtdd_tstart)+总时长(rtdd_tdur) 写进方块实体数据
|
||||
- 每帧 tick 判断 当前时间-开始时刻 >= 总时长 -> 到点转换
|
||||
- 时间戳是绝对时间(time.time()), 重启/离线真实时间仍在流逝, 剩余时间自然准确
|
||||
|
||||
【事件监听在哪?】
|
||||
本类【不】监听任何事件!
|
||||
ServerBlockEntityTickEvent 由 FeatureDispatcher 统一监听,再按 timerOwner
|
||||
路由到本类的 handleBlockEntityTick。
|
||||
|
||||
【本类怎么做】
|
||||
handleBlockEntityTick ← 入口:分发器转发 tick 事件
|
||||
└─ _tickProcess ← 判断是否到点 / autoStart=True 自动补写开始时刻
|
||||
└─ _finishTimer ← 到点执行转换 + 完成提示
|
||||
================================================================================
|
||||
"""
|
||||
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 ..ChujuData import MEATBALL_TIMERS
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
|
||||
class ChujuTimerService(BlueFoxServerSystem):
|
||||
"""
|
||||
厨具·计时转换引擎
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
super(ChujuTimerService, self).__init__(namespace, systemName)
|
||||
print "===== ChujuTimerService init ====="
|
||||
|
||||
# ---- 收集计时型规则(source -> 规则) ----
|
||||
self.meatballTimers = {t["source"]: t for t in MEATBALL_TIMERS}
|
||||
|
||||
# ---- 注册到分发器的计时路由表 ----
|
||||
FeatureDispatcher.registerTimer(self)
|
||||
|
||||
# ============================================================================
|
||||
# 一、给 FeatureDispatcher 的接口
|
||||
# ============================================================================
|
||||
|
||||
def getTimerBlocks(self):
|
||||
"""返回本功能的所有"计时源方块"集合(tick 路由用)"""
|
||||
return set(self.meatballTimers.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)
|
||||
|
||||
# ============================================================================
|
||||
# 二、启动计时(本模块 autoStart, 但保留接口以便需要时外部触发)
|
||||
# ============================================================================
|
||||
|
||||
def startTimer(self, dim, pos, blockName, playerId=None):
|
||||
recipe = self.meatballTimers.get(blockName)
|
||||
if not recipe:
|
||||
return False
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data and data["rtdd_tid"] == blockName:
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
self._writeTimerData(dim, pos, recipe, playerId)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# 三、tick 检查与到点转换
|
||||
# ============================================================================
|
||||
|
||||
def _tickProcess(self, dim, pos, blockName):
|
||||
try:
|
||||
recipe = self.meatballTimers.get(blockName)
|
||||
if not recipe:
|
||||
return
|
||||
|
||||
blockData = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if blockData is None:
|
||||
return
|
||||
|
||||
try:
|
||||
tid = blockData["rtdd_tid"]
|
||||
except Exception:
|
||||
tid = ""
|
||||
if tid != blockName:
|
||||
# 丸子机为 autoStart=True: 计时源方块无计时数据时自动启动
|
||||
if recipe.get("autoStart", True):
|
||||
self._writeTimerData(dim, pos, recipe, None)
|
||||
return
|
||||
|
||||
tstart = blockData["rtdd_tstart"]
|
||||
tdur = blockData["rtdd_tdur"]
|
||||
|
||||
if time.time() - tstart >= tdur:
|
||||
self._finishTimer(dim, pos, recipe, blockData)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _finishTimer(self, dim, pos, recipe, data):
|
||||
try:
|
||||
cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
if cur and cur.get("name") != recipe["source"]:
|
||||
self._clearTimerData(dim, pos)
|
||||
return
|
||||
self.replaceBlock(pos, dim, recipe["target"], None)
|
||||
self._clearTimerData(dim, pos)
|
||||
if recipe.get("doneTip"):
|
||||
self._notifyIfOnline(data["rtdd_tp"], recipe["doneTip"])
|
||||
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", 30)
|
||||
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 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 -*-
|
||||
+4
-1
@@ -255,7 +255,10 @@ class AnimalService(BlueFoxServerSystem):
|
||||
playerId 可为 None(计时回调时没有玩家),用 LevelId 创建组件。
|
||||
"""
|
||||
creator = playerId if playerId is not None else LevelId
|
||||
if aux is None:
|
||||
if newBlockName == "minecraft:air":
|
||||
# 空气没有方向变体,aux 必须强制为 0,否则方向方块(如粪便)无法被正常清空
|
||||
aux = 0
|
||||
elif aux is None:
|
||||
try:
|
||||
oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim)
|
||||
aux = oldBlock.get("aux", 0)
|
||||
|
||||
Reference in new issue
Block a user