配方 替换方块ceshi1
This commit is contained in:
1 parent
d873dd4901
commit
f79bd3f0e7
6 files changed
+420
No files matched your search
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
配方数据文件 - 所有"右键方块→消耗物品→替换方块"的配方都在这里
|
||||
===============================================================
|
||||
每加一道新菜,只需要在这里加一条配方,不用写任何Python代码。
|
||||
|
||||
配方字典字段说明([] = 可选,{} = 特殊类型):
|
||||
{
|
||||
"source": str, # 被右键的方块ID(必填)
|
||||
"item": str|None, # 玩家手里必须拿的物品,None=空手也行
|
||||
[below]: str, # 方块下面必须是什么
|
||||
[needOil]: bool, # 物品必须有"油标签"
|
||||
[target]: str|None, # 替换成什么方块,None=不替换
|
||||
[consume]: int, # 消耗物品数量,0=不消耗(默认1)
|
||||
[giveItem]: str, # 额外给玩家什么物品
|
||||
[sound]: str, # 播放什么声音
|
||||
[soundPitch]: float, # 声音频率(默认1.0)
|
||||
[stirCount]: int, # 需要翻炒几次才变身
|
||||
[stirCd]: float, # 翻炒冷却时间(秒)
|
||||
[onDone]: str, # 完成动作:"finish"=标记熟了不替换
|
||||
[doneMsg]: str, # 完成后的提示消息
|
||||
[failMsg]: str, # 冷却中/失败时的提示
|
||||
[dropIfDone]: list, # 熟了后拆掉掉落什么
|
||||
[dropIfNot]: list, # 没熟拆掉掉落什么
|
||||
}
|
||||
"""
|
||||
|
||||
# ============================================================
|
||||
# 炒菜示例:胡萝卜牛肉(完整一条链)
|
||||
# ============================================================
|
||||
# 流程:空锅 → 加油 → 放牛肉 → 翻炒 → 熟了 → 拆掉拿熟牛肉
|
||||
RECIPES = [
|
||||
|
||||
# ===== 步骤1:空炒锅 + 有油的物品 → 带油煎锅 =====
|
||||
{
|
||||
"source": "rtdd:wok",
|
||||
"item": None,
|
||||
"below": "minecraft:campfire",
|
||||
"needOil": True,
|
||||
"target": "rtdd:cookinggarlic",
|
||||
"consume": 0,
|
||||
"sound": "random.fizz",
|
||||
"soundPitch": 2.4,
|
||||
},
|
||||
|
||||
# ===== 步骤2:带油煎锅 + 生牛肉 → 胡萝卜牛肉锅 =====
|
||||
{
|
||||
"source": "rtdd:cookinggarlic",
|
||||
"item": "minecraft:beef",
|
||||
"target": "rtdd:carrotandbeef",
|
||||
"consume": 1,
|
||||
"sound": "random.fizz",
|
||||
"soundPitch": 0.8,
|
||||
},
|
||||
|
||||
# ===== 步骤3:胡萝卜牛肉锅 + 木剑翻炒 → 第二阶段 =====
|
||||
{
|
||||
"source": "rtdd:carrotandbeef",
|
||||
"item": "minecraft:wooden_sword",
|
||||
"target": "rtdd:carrotandbeef_1",
|
||||
"consume": 0,
|
||||
"stirCount": 5,
|
||||
"stirCd": 1.2,
|
||||
"sound": "random.fizz",
|
||||
"soundPitch": 2.1,
|
||||
"failMsg": "这时候翻动会不好熟的!",
|
||||
},
|
||||
|
||||
# ===== 步骤4:第二阶段 + 木剑翻炒 → 标记熟了 =====
|
||||
{
|
||||
"source": "rtdd:carrotandbeef_1",
|
||||
"item": "minecraft:wooden_sword",
|
||||
"target": None,
|
||||
"consume": 0,
|
||||
"stirCount": 3,
|
||||
"stirCd": 0.8,
|
||||
"sound": "random.fizz",
|
||||
"soundPitch": 2.1,
|
||||
"onDone": "finish",
|
||||
"doneMsg": "出锅啦!拿盘子盛出来",
|
||||
"failMsg": "这时候翻动会不好熟的!",
|
||||
"dropIfDone": ["minecraft:cooked_beef", "rtdd:wok"],
|
||||
"dropIfNot": ["minecraft:beef", "rtdd:wok"],
|
||||
},
|
||||
]
|
||||
Whitespace-only changes.
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
烹饪客户端(预留)
|
||||
=================
|
||||
目前客户端不需要做额外的UI工作。
|
||||
如果以后需要进度条、翻炒提示等UI,可以在这里扩展。
|
||||
"""
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
ClientSystem = clientApi.GetClientSystemCls()
|
||||
|
||||
|
||||
class CookingClient(ClientSystem):
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
print "===== CookingClient init ====="
|
||||
|
||||
def Destroy(self):
|
||||
print "===== CookingClient Destroy ====="
|
||||
Whitespace-only changes.
@@ -0,0 +1,316 @@
|
||||
# -*- 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)
|
||||
Whitespace-only changes.
Reference in new issue
Block a user