Files
rtdd/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/CookingService.py
T
2026-07-21 12:07:40 +08:00

316 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
通用烹饪引擎(服务端)
=====================
读取 RecipeData.py 里的配方,自动处理所有"右键方块→消耗物品→替换方块"的逻辑。
设计思路:
- 不再为每个方块写一个类,而是写一个通用引擎
- 所有配方存在 RecipeData.py,引擎读取配方来执行
- 加新菜 = 在 RecipeData.py 加一条配方,这里不用改一行代码
用法:
在 modMain.py 里注册这个系统即可:
serverApi.RegisterSystem("rtdd", "CookingService",
"Script_NeteaseModousIY5aSF.ceshi.service.CookingService.CookingService")
"""
import time
import mod.server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls()
comp = serverApi.GetEngineCompFactory()
# 从配方数据文件导入所有配方
from ..RecipeData import RECIPES
class CookingService(ServerSystem):
"""
通用烹饪引擎
内部数据:
stirData = {
(dim, x, y, z): {
"count": int, # 已经翻炒了多少次
"lastTime": float, # 上次翻炒的时间戳
"finished": bool, # 是否已经熟了
"dropIfDone": list, # 熟了掉落什么
"dropIfNot": list, # 没熟掉落什么
}
}
"""
def __init__(self, namespace, systemName):
super(CookingService, self).__init__(namespace, systemName)
print "===== CookingService init ====="
self.stirData = {} # 翻炒数据字典
self.ListenEvent()
def Destroy(self):
print "===== CookingService Destroy ====="
self.UnListenEvent()
def ListenEvent(self):
"""注册事件监听:玩家右键方块、玩家拆方块"""
self.ListenForEvent(serverApi.GetEngineNamespace(),
serverApi.GetEngineSystemName(),
"ServerBlockUseEvent", self, self.OnBlockUse)
self.ListenForEvent(serverApi.GetEngineNamespace(),
serverApi.GetEngineSystemName(),
"ServerPlayerTryDestroyBlockEvent", self, self.OnDestroy)
def UnListenEvent(self):
self.UnListenForEvent(serverApi.GetEngineNamespace(),
serverApi.GetEngineSystemName(),
"ServerBlockUseEvent", self, self.OnBlockUse)
self.UnListenForEvent(serverApi.GetEngineNamespace(),
serverApi.GetEngineSystemName(),
"ServerPlayerTryDestroyBlockEvent", self, self.OnDestroy)
# ==================== 主入口:玩家右键方块 ====================
def OnBlockUse(self, args):
"""
玩家右键方块时触发。
流程:查配方 → 验条件 → 处理翻炒 → 消耗物品 → 替换方块 → 给物品 → 播声音
"""
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._findRecipe(blockName, itemDict)
if not recipe:
return
# ===== 处理需要翻炒的配方 =====
if recipe.get("stirCount"):
self._handleStir(key, recipe, playerId, pos, dim, itemDict)
return
# ===== 处理普通配方(直接替换) =====
self._executeRecipe(recipe, playerId, pos, dim, itemDict)
# ==================== 配方匹配 ====================
def _findRecipe(self, blockName, itemDict):
"""
在 RECIPES 列表里找匹配的配方。
匹配条件:source 对上 + item 对上(或 item 为 None)
"""
itemName = itemDict.get("newItemName") if itemDict else None
for recipe in RECIPES:
if recipe["source"] != blockName:
continue
# item 为 None 表示不限制物品类型
if recipe["item"] is not None and recipe["item"] != itemName:
continue
return recipe
return None
# ==================== 配方执行(普通替换) ====================
def _executeRecipe(self, recipe, playerId, pos, dim, itemDict):
"""
执行一条普通配方:检查条件 → 消耗物品 → 替换方块 → 给物品 → 播声音
"""
# 1. 检查附加条件
if not self._checkConditions(recipe, playerId, pos, dim, itemDict):
return
# 2. 消耗物品(扣除数量、去掉油标签)
if recipe.get("consume", 1) > 0:
self._consumeItem(playerId, itemDict, recipe.get("consume", 1))
if recipe.get("needOil"):
self._removeOil(itemDict, playerId)
# 3. 替换方块
if recipe.get("target"):
self._replaceBlock(pos, dim, recipe["target"], playerId)
# 4. 给物品
if recipe.get("giveItem"):
self._giveItem(playerId, recipe["giveItem"])
# 5. 播放声音
if recipe.get("sound"):
self._playSound(playerId, recipe["sound"], recipe.get("soundPitch", 1.0))
# ==================== 翻炒处理 ====================
def _handleStir(self, key, recipe, playerId, pos, dim, itemDict):
"""
处理需要翻炒的配方。
翻炒逻辑:
1. 检查冷却时间
2. 没到冷却 → 提示"这时候翻动会不好熟的!"
3. 到了冷却 → 翻炒次数+1 → 播声音+挥剑动画
4. 翻够次数 → 执行替换或标记熟了
"""
now = time.time()
data = self.stirData.get(key, {"count": 0, "lastTime": 0, "finished": False,
"dropIfDone": recipe.get("dropIfDone", []),
"dropIfNot": recipe.get("dropIfNot", [])})
# 冷却检查
if now - data["lastTime"] < recipe.get("stirCd", 1.0):
if recipe.get("failMsg"):
comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["failMsg"])
return
# 翻炒次数+1
data["count"] += 1
data["lastTime"] = now
self.stirData[key] = data
# 播放声音和挥剑动画
if recipe.get("sound"):
self._playSound(playerId, recipe["sound"], recipe.get("soundPitch", 1.0))
self._playAttack(playerId)
# 判断是否翻够了
if data["count"] >= recipe["stirCount"]:
if recipe.get("onDone") == "finish":
# 标记熟了,不替换方块
data["finished"] = True
self.stirData[key] = data
if recipe.get("doneMsg"):
comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["doneMsg"])
elif recipe.get("target"):
# 替换方块
self._replaceBlock(pos, dim, recipe["target"], playerId)
# 清理翻炒数据
self.stirData.pop(key, None)
# ==================== 条件检查 ====================
def _checkConditions(self, recipe, playerId, pos, dim, itemDict):
"""
检查配方的附加条件。
返回 True = 通过,False = 不通过。
"""
# 检查下面是否有指定方块(如营火)
if recipe.get("below"):
belowPos = (pos[0], pos[1] - 1, pos[2])
blockComp = comp.CreateBlockInfo(playerId)
try:
belowBlock = blockComp.GetBlockNew(belowPos, dim)
if belowBlock.get("name") != recipe["below"]:
return False
except:
return False
# 检查物品是否有油标签
if recipe.get("needOil") and itemDict:
if not self._getItemOil(itemDict):
return False
return True
# ==================== 工具方法 ====================
def _getCarriedItem(self, playerId):
"""获取玩家主手拿的物品"""
itemComp = comp.CreateItem(playerId)
return itemComp.GetPlayerItem(serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0)
def _consumeItem(self, playerId, itemDict, amount=1):
"""消耗玩家手里的物品(减少数量)"""
count = itemDict.get("count", 0)
if count <= amount:
comp.CreateItem(playerId).SetInvItemNum(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0)
else:
itemDict["count"] = count - amount
comp.CreateItem(playerId).SetEntityItem(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
{"itemName": itemDict["newItemName"], "count": itemDict["count"],
"auxValue": itemDict.get("auxValue", 0)})
def _replaceBlock(self, pos, dim, newBlockName, playerId):
"""替换方块(一行搞定!)"""
comp.CreateBlockInfo(playerId).SetBlockNew(
pos, {"name": newBlockName}, 0, dim)
def _giveItem(self, playerId, itemName, count=1):
"""给玩家背包里塞一个物品"""
comp.CreateItem(playerId).SpawnItemToPlayerInv(
{"itemName": itemName, "count": count, "auxValue": 0}, playerId)
def _playSound(self, playerId, soundName, pitch=1.0):
"""在玩家位置播放声音"""
pos = comp.CreatePlayer(playerId).GetPos()
comp.CreateGame(playerId).PlaySound(
soundName, pos, pitch, 1.0, False)
def _playAttack(self, playerId):
"""让玩家播放攻击动画(挥剑)"""
# 通过引擎的 Attack 组件触发动画
try:
comp.CreatePlayer(playerId).Attack()
except:
pass
# ==================== 油标签操作 ====================
def _getItemOil(self, itemDict):
"""读取物品上的油标签"""
try:
itemComp = comp.CreateItem(serverApi.GetLevelId())
userData = itemComp.GetItemUserData(itemDict, "rtdd")
return userData.get("oil", False) if userData else False
except:
return False
def _removeOil(self, itemDict, playerId):
"""去掉物品上的油标签"""
try:
itemComp = comp.CreateItem(serverApi.GetLevelId())
userData = itemComp.GetItemUserData(itemDict, "rtdd")
if userData:
userData["oil"] = False
itemComp.SetItemUserData(itemDict, "rtdd", userData)
# 更新玩家手里的物品
comp.CreateItem(playerId).SetEntityItem(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
{"itemName": itemDict["newItemName"], "count": itemDict.get("count", 1),
"auxValue": itemDict.get("auxValue", 0),
"userData": {"rtdd": userData}})
except:
pass
# ==================== 拆方块处理 ====================
def OnDestroy(self, args):
"""
玩家拆方块时触发。
如果有翻炒数据且标记了熟了 → 掉落熟的东西
如果没熟 → 掉落生的东西
"""
blockName = args.get("blockName")
playerId = args.get("playerId")
x, y, z = args.get("x"), args.get("y"), args.get("z")
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
key = (dim, x, y, z)
data = self.stirData.get(key)
if not data:
return
# 根据是否熟了决定掉落什么
if data.get("finished"):
dropItems = data.get("dropIfDone", [])
else:
dropItems = data.get("dropIfNot", [])
for itemName in dropItems:
self._giveItem(playerId, itemName)
# 清理翻炒数据
self.stirData.pop(key, None)