炒锅功能实现 3
This commit is contained in:
1 parent
2db14445c8
commit
192a5e466b
14 files changed
+1515
-36
No files matched your search
+383
@@ -0,0 +1,383 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
炒锅·右键转换引擎(服务端)
|
||||
================================================================================
|
||||
处理"炒锅"(xy_chaoguo) 分类下的右键转换:
|
||||
炒锅(空)<->(盖锅)、辣椒锅底、葱花锅底、放料(添油+食材 -> 菜生态)、
|
||||
翻炒(锅铲右键3次, 需下方点燃炉灶)、出锅(熟态+盘子 -> 掉落盘装成品)、
|
||||
空手出锅(煎葱油饼_熟/煎馍_熟/煎面饼_熟/煎酱香饼)、
|
||||
盖饭双向(手持成品菜右键米饭 <-> 手持米饭右键成品菜, itemUseOn)。
|
||||
|
||||
【事件监听在哪?】
|
||||
本类【不】监听任何事件!
|
||||
所有事件由 FeatureDispatcher 统一监听, 按 blockOwner / itemUseOnOwner
|
||||
路由到本类 handleBlockUse / handleItemUseOn。
|
||||
|
||||
【计时在?】
|
||||
煎蛋/煎葱油饼/煎馍/煎面饼 的计时在 ChaoguoTimerService,
|
||||
本类只负责"空手右键计时源方块"转发启动(FeatureDispatcher.startTimerFor)。
|
||||
|
||||
【门控数据(存方块实体)】
|
||||
rtdd_cf: 翻炒计数(满3次后按规则变方块; 煎酱香饼门控=3)
|
||||
rtdd_cg: 煎蛋门控(计时30秒后由计时服务置1, 之后才允许盘子出锅)
|
||||
================================================================================
|
||||
"""
|
||||
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)
|
||||
|
||||
# ---- 只导入炒锅自己的数据库 ----
|
||||
from ..ChaoguoData import (
|
||||
SWITCH_RULES, ITEM_USE_ON_RULES, TIMER_RULES,
|
||||
WOK,
|
||||
COOKED_EGG, COOKING_SCALLION_PANCAKE_0, COOKING_DOUGH_0, COOKING_DOUGHT_0,
|
||||
)
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
|
||||
class ChaoguoService(BlueFoxServerSystem):
|
||||
"""
|
||||
炒锅·右键转换引擎
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
super(ChaoguoService, self).__init__(namespace, systemName)
|
||||
print "===== ChaoguoService init ====="
|
||||
|
||||
# ---- (方块, 手持物品) -> 规则: O(1) 查找 (item=None 表示空手右键) ----
|
||||
self.recipes = {}
|
||||
self.featureBlocks = set()
|
||||
for r in SWITCH_RULES:
|
||||
self.recipes[(r["block"], r.get("item"))] = r
|
||||
self.featureBlocks.add(r["block"])
|
||||
|
||||
# ---- 手持物品右键方块表面 -> 规则: (物品, 表面方块) -> 规则 ----
|
||||
self.itemUseOnRecipes = {}
|
||||
for r in ITEM_USE_ON_RULES:
|
||||
self.itemUseOnRecipes[(r["item"], r["surface"])] = r
|
||||
|
||||
# ---- 空手右键"计时源方块"(煎蛋/煎葱油饼_生/煎馍_生/煎面饼_生): ----
|
||||
# 放在点燃炉灶上 -> 开始计时(提示包含总共多少秒与结果)
|
||||
self.timerSources = set()
|
||||
self.startTimerTips = {} # 空手右键启动计时 -> 提示
|
||||
self.timingTips = {} # 空手右键"已在计时中" -> 提示
|
||||
for src, r in {t["source"]: t for t in TIMER_RULES}.items():
|
||||
self.timerSources.add(src)
|
||||
self.featureBlocks.add(src) # 登记右键路由, 空手右键才分发到这里
|
||||
self.startTimerTips = {
|
||||
COOKED_EGG: "§a开始计时: 还需30秒, 计时结束 煎蛋煎熟, 手持盘子出锅",
|
||||
COOKING_SCALLION_PANCAKE_0: "§a开始计时: 还需60秒, 计时结束变成 煎葱油饼_熟, 空手出锅 葱油饼",
|
||||
COOKING_DOUGH_0: "§a开始计时: 还需60秒, 计时结束变成 煎馍_熟, 空手出锅 馍",
|
||||
COOKING_DOUGHT_0: "§a开始计时: 还需60秒, 计时结束变成 煎面饼_熟, 空手出锅 煎饼",
|
||||
}
|
||||
self.timingTips = {
|
||||
COOKED_EGG: "§e正在计时中: 还需30秒, 计时结束 煎蛋煎熟, 手持盘子出锅",
|
||||
COOKING_SCALLION_PANCAKE_0: "§e正在计时中: 还需60秒, 计时结束变成 煎葱油饼_熟, 空手出锅 葱油饼",
|
||||
COOKING_DOUGH_0: "§e正在计时中: 还需60秒, 计时结束变成 煎馍_熟, 空手出锅 馍",
|
||||
COOKING_DOUGHT_0: "§e正在计时中: 还需60秒, 计时结束变成 煎面饼_熟, 空手出锅 煎饼",
|
||||
}
|
||||
|
||||
# ---- 右键冷却表 ----
|
||||
self.cooldown = {}
|
||||
|
||||
# ---- 注册到统一分发器 ----
|
||||
FeatureDispatcher.register(self)
|
||||
|
||||
# ============================================================================
|
||||
# 一、给 FeatureDispatcher 的接口
|
||||
# ============================================================================
|
||||
|
||||
def getFeatureBlocks(self):
|
||||
"""返回本功能管辖的所有右键方块名集合"""
|
||||
return self.featureBlocks
|
||||
|
||||
def getItemUseOnRules(self):
|
||||
"""手持物品右键方块表面路由: 盖饭成品菜 + 米饭三形态(盖饭/蛋炒饭)"""
|
||||
return {r["item"]: True for r in ITEM_USE_ON_RULES}
|
||||
|
||||
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)
|
||||
itemName = itemDict.get("newItemName") if itemDict else None
|
||||
if itemName == "minecraft:air":
|
||||
itemName = None
|
||||
recipe = self.recipes.get((blockName, itemName))
|
||||
if not recipe:
|
||||
# 空手右键"计时源方块"(煎蛋/煎葱油饼_生/煎馍_生/煎面饼_生):
|
||||
# 下方是点燃炉灶 -> 开始计时
|
||||
if itemName is None and blockName in self.timerSources:
|
||||
if not self.checkCooldown(key, 0.4):
|
||||
return True
|
||||
self._handleStartTimer(blockName, playerId, pos, dim)
|
||||
return True
|
||||
return False
|
||||
# 翻炒用 1.5 秒冷却(防连点刷计数), 其余 0.4 秒
|
||||
cd = 1.5 if recipe.get("action") == "stir" else 0.4
|
||||
if not self.checkCooldown(key, cd):
|
||||
return True
|
||||
self.handleSwitch(recipe, playerId, pos, dim, itemDict)
|
||||
return True
|
||||
|
||||
def _handleStartTimer(self, blockName, playerId, pos, dim):
|
||||
"""空手右键计时源: 已在计时中 -> 提示总时长; 否则转发计时服务开始(下方需点燃炉灶)"""
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data and data["rtdd_tid"] == blockName:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, self.timingTips.get(blockName, "§e已经在计时中"))
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
started = FeatureDispatcher.startTimerFor(blockName, dim, pos, playerId)
|
||||
if started:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, self.startTimerTips.get(blockName, "§a已启动计时"))
|
||||
else:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§c炒锅需要放在点燃的炉灶上才能开始计时")
|
||||
|
||||
def handleItemUseOn(self, args):
|
||||
"""由 FeatureDispatcher 转发的"手持物品右键方块表面"入口(盖饭/蛋炒饭)。
|
||||
正向: 手持成品菜 右键 米饭 -> 米饭变盖饭; 反向: 手持米饭 右键 成品菜 -> 菜品变盖饭;
|
||||
蛋炒饭: 手持米饭 右键 炒锅_86 -> 炒锅_87。转换与取消放置同一事件完成"""
|
||||
itemName = args.get("itemName")
|
||||
playerId = args.get("playerId") or args.get("entityId")
|
||||
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)
|
||||
try:
|
||||
b = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
clickedName = b.get("name") if b else None
|
||||
except Exception:
|
||||
clickedName = None
|
||||
recipe = self.itemUseOnRecipes.get((itemName, clickedName))
|
||||
if not recipe:
|
||||
# 手持 成品菜/米饭 右键 本分类功能方块(炒锅态等) 无对应转换 ->
|
||||
# 直接取消默认放置, 避免"先放置方块再消失"的视觉问题
|
||||
# (米饭/成品菜右键非功能方块仍可正常放置/堆叠)
|
||||
if clickedName and clickedName in self.featureBlocks:
|
||||
return True
|
||||
return False
|
||||
if not self.checkCooldown(key, 0.4):
|
||||
return True
|
||||
itemDict = self.getCarriedItem(playerId)
|
||||
|
||||
# 消耗物品(数量不足则拦截, 不转换)
|
||||
consume = recipe.get("consume", 1)
|
||||
if consume > 1 and itemDict is not None and (itemDict.get("count") or 0) < consume:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§c数量不足, 需要 %d 个" % consume)
|
||||
return True
|
||||
if consume > 0:
|
||||
self.consumeItem(playerId, itemDict, consume)
|
||||
|
||||
# 替换被右键的表面方块(米饭变盖饭 / 菜品变盖饭 / 蛋炒饭变熟)
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
self._clearWokData(dim, pos)
|
||||
|
||||
if recipe.get("tip"):
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["tip"])
|
||||
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
|
||||
|
||||
# 出锅门控(煎蛋需煎熟 rtdd_cg=1; 煎酱香饼需翻炒3次 rtdd_cf=3)
|
||||
needFlag = recipe.get("needFlag")
|
||||
if needFlag:
|
||||
flagKey, flagVal, flagTip = needFlag[0], needFlag[1], needFlag[2]
|
||||
if self._readFlag(dim, pos, flagKey) < flagVal:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, flagTip)
|
||||
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 == "stir":
|
||||
self._handleStir(recipe, playerId, pos, dim)
|
||||
elif action == "take_out":
|
||||
# 出锅: 锅变回 炒锅(空) + 掉落盘装成品(掉落物形式)
|
||||
self.replaceBlock(pos, dim, WOK, playerId)
|
||||
self.dropItem(dim, pos, recipe["dropItem"])
|
||||
self._clearWokData(dim, pos)
|
||||
# 普通替换方块(盖锅/锅底/放料/加料)
|
||||
if recipe.get("target") and action != "take_out":
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
self._clearWokData(dim, pos)
|
||||
|
||||
# 提示
|
||||
if recipe.get("tip"):
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["tip"])
|
||||
|
||||
def _handleStir(self, recipe, playerId, pos, dim):
|
||||
"""翻炒: 锅铲右键计1次(rtdd_cf), 满 times 次后变 target;
|
||||
煎酱香饼无 target, 满3次后门控(空手出锅用)"""
|
||||
times = recipe.get("times", 3)
|
||||
cf = self._readFlag(dim, pos, "rtdd_cf") + 1
|
||||
if cf >= times:
|
||||
target = recipe.get("target")
|
||||
if target:
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
self._clearWokData(dim, pos) # 新方块重置翻炒计数
|
||||
else:
|
||||
self._writeFlag(dim, pos, "rtdd_cf", times) # 门控: 保持3
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, recipe.get("tip", "§a翻炒完成"))
|
||||
else:
|
||||
self._writeFlag(dim, pos, "rtdd_cf", cf)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(
|
||||
playerId, "§a翻炒中 (%d/%d)" % (cf, times))
|
||||
|
||||
# ============================================================================
|
||||
# 三、方块实体门控数据 读/写(rtdd_cf 翻炒计数 / rtdd_cg 煎蛋门控)
|
||||
# ============================================================================
|
||||
|
||||
def _readFlag(self, dim, pos, key):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
return 0
|
||||
v = data[key]
|
||||
return int(v) if v else 0
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _writeFlag(self, dim, pos, key, val):
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is not None:
|
||||
data[key] = int(val)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _clearWokData(self, dim, pos):
|
||||
"""替换方块后清掉上一态的计时/门控数据(新方块从0开始)"""
|
||||
try:
|
||||
data = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if data is None:
|
||||
return
|
||||
data["rtdd_tid"] = ""
|
||||
data["rtdd_tstart"] = 0
|
||||
data["rtdd_tdur"] = 0
|
||||
data["rtdd_tp"] = ""
|
||||
data["rtdd_cf"] = 0
|
||||
data["rtdd_cg"] = 0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ============================================================================
|
||||
# 四、工具方法,判断正下方是否指定方块(点燃炉灶)
|
||||
# ============================================================================
|
||||
|
||||
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 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
|
||||
|
||||
# ============================================================================
|
||||
# 八、工具方法,替换方块
|
||||
# ============================================================================
|
||||
|
||||
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 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
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
炒锅·计时引擎(服务端)
|
||||
================================================================================
|
||||
只处理"炒锅"(xy_chaoguo) 分类下的【计时自动转换】:
|
||||
煎蛋 : 30秒煎熟(方块不变, 门控标记 rtdd_cg=1, 之后才可盘子出锅)
|
||||
煎葱油饼_生 : 60秒 -> 煎葱油饼_熟
|
||||
煎馍_生 : 60秒 -> 煎馍_熟
|
||||
煎面饼_生 : 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 ..ChaoguoData import TIMER_RULES
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
|
||||
|
||||
class ChaoguoTimerService(BlueFoxServerSystem):
|
||||
"""
|
||||
炒锅·计时引擎
|
||||
"""
|
||||
|
||||
def __init__(self, namespace, systemName):
|
||||
super(ChaoguoTimerService, self).__init__(namespace, systemName)
|
||||
print "===== ChaoguoTimerService 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)
|
||||
|
||||
# ============================================================================
|
||||
# 二、启动计时(空手右键计时源方块后由 ChaoguoService 通过 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)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _handleBelowBroken(self, dim, pos, blockName, data, rules):
|
||||
"""下方点燃炉灶被移走/熄灭: 暂停计时(数据保留, 炉灶回来继续) + 提示发起玩家"""
|
||||
tp = ""
|
||||
try:
|
||||
tp = data["rtdd_tp"] or ""
|
||||
except Exception:
|
||||
tp = ""
|
||||
self._notifyIfOnline(tp, "§c炒锅下的炉灶移走了, 暂停计时")
|
||||
|
||||
def _finishTimer(self, dim, pos, rule):
|
||||
"""到点转换; 煎蛋为门控(mark): 方块不变, 标记 rtdd_cg=1"""
|
||||
try:
|
||||
cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
if cur and cur.get("name") != rule["source"]:
|
||||
self._clearTimerData(dim, pos)
|
||||
return
|
||||
mark = rule.get("mark")
|
||||
if mark:
|
||||
# 门控计时: 方块不变, 标记煎熟(rtdd_cg=1)
|
||||
blockData = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if blockData is not None:
|
||||
blockData[mark] = 1
|
||||
self._clearTimerData(dim, pos)
|
||||
return
|
||||
# 到点转换
|
||||
self.replaceBlock(pos, dim, rule["target"], None)
|
||||
self._clearTimerData(dim, pos)
|
||||
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 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)
|
||||
+1
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
Reference in new issue
Block a user