diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/CookingService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/CookingService.py index b331ee9..30127ce 100644 --- a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/CookingService.py +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/CookingService.py @@ -24,6 +24,22 @@ comp = serverApi.GetEngineCompFactory() from ..RecipeData import RECIPES +def _getUserDataField(itemDict, *keys): + """ + 读取物品 dict 里的 userData 嵌套字段(纯本地操作,不调API)。 + 例如 _getUserDataField(itemDict, 'rtdd', 'oil') → 返回 itemDict['userData']['rtdd']['oil'] + 如果中间任何一层的 key 不存在,自动创建空 dict。 + """ + itemDict['userData'] = itemDict.get('userData', {}) + if itemDict.get('userData') is None: + itemDict['userData'] = {} + node = itemDict['userData'] + for key in keys: + node[key] = node.get(key, {}) + node = node[key] + return node + + class CookingService(ServerSystem): """ 通用烹饪引擎 @@ -51,7 +67,6 @@ class CookingService(ServerSystem): self.UnListenEvent() def ListenEvent(self): - """注册事件监听:玩家右键方块、玩家拆方块""" self.ListenForEvent(serverApi.GetEngineNamespace(), serverApi.GetEngineSystemName(), "ServerBlockUseEvent", self, self.OnBlockUse) @@ -81,34 +96,25 @@ class CookingService(ServerSystem): 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 @@ -117,14 +123,11 @@ class CookingService(ServerSystem): # ==================== 配方执行(普通替换) ==================== def _executeRecipe(self, recipe, playerId, pos, dim, itemDict): - """ - 执行一条普通配方:检查条件 → 消耗物品 → 替换方块 → 给物品 → 播声音 - """ # 1. 检查附加条件 if not self._checkConditions(recipe, playerId, pos, dim, itemDict): return - # 2. 消耗物品(扣除数量、去掉油标签) + # 2. 消耗物品 if recipe.get("consume", 1) > 0: self._consumeItem(playerId, itemDict, recipe.get("consume", 1)) if recipe.get("needOil"): @@ -138,25 +141,17 @@ class CookingService(ServerSystem): if recipe.get("giveItem"): self._giveItem(playerId, recipe["giveItem"]) - # 5. 播放声音 - if recipe.get("sound"): - self._playSound(playerId, recipe["sound"], recipe.get("soundPitch", 1.0)) + # 5. 播放声音(需要配合 BlueFox 框架的客户端 PlaySound 事件,此处暂不实现) # ==================== 翻炒处理 ==================== 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", [])}) + data = self.stirData.get(key) or { + "count": 0, "lastTime": 0.0, "finished": False, + "dropIfDone": recipe.get("dropIfDone", []), + "dropIfNot": recipe.get("dropIfNot", []) + } # 冷却检查 if now - data["lastTime"] < recipe.get("stirCd", 1.0): @@ -169,33 +164,21 @@ class CookingService(ServerSystem): 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) @@ -218,10 +201,15 @@ class CookingService(ServerSystem): def _getCarriedItem(self, playerId): """获取玩家主手拿的物品""" itemComp = comp.CreateItem(playerId) - return itemComp.GetPlayerItem(serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0) + return itemComp.GetPlayerItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0) def _consumeItem(self, playerId, itemDict, amount=1): - """消耗玩家手里的物品(减少数量)""" + """ + 消耗玩家手里的物品(减少数量)。 + 注意:SetEntityItem 接收的 key 是 itemName/auxValue/count, + 而 GetPlayerItem 返回的 key 是 newItemName/newAuxValue/count。 + """ count = itemDict.get("count", 0) if count <= amount: comp.CreateItem(playerId).SetInvItemNum( @@ -230,11 +218,12 @@ class CookingService(ServerSystem): itemDict["count"] = count - amount comp.CreateItem(playerId).SetEntityItem( serverApi.GetMinecraftEnum().ItemPosType.CARRIED, - {"itemName": itemDict["newItemName"], "count": itemDict["count"], - "auxValue": itemDict.get("auxValue", 0)}) + {"itemName": itemDict["newItemName"], + "count": itemDict["count"], + "auxValue": itemDict.get("newAuxValue", 0)}) def _replaceBlock(self, pos, dim, newBlockName, playerId): - """替换方块(一行搞定!)""" + """替换方块""" comp.CreateBlockInfo(playerId).SetBlockNew( pos, {"name": newBlockName}, 0, dim) @@ -243,45 +232,27 @@ class CookingService(ServerSystem): 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 - - # ==================== 油标签操作 ==================== + # ==================== 油标签操作(纯本地 dict 操作,不调 API) ==================== def _getItemOil(self, itemDict): """读取物品上的油标签""" try: - itemComp = comp.CreateItem(serverApi.GetLevelId()) - userData = itemComp.GetItemUserData(itemDict, "rtdd") - return userData.get("oil", False) if userData else False + return _getUserDataField(itemDict, 'rtdd').get('oil', 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}}) + _getUserDataField(itemDict, 'rtdd')['oil'] = False + # 更新玩家手里的物品(userData 跟着 itemDict 一起写回去) + itemComp = comp.CreateItem(playerId) + itemComp.SetEntityItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, + {"itemName": itemDict["newItemName"], + "count": itemDict.get("count", 1), + "auxValue": itemDict.get("newAuxValue", 0), + "userData": itemDict.get("userData", {})}) except: pass @@ -293,7 +264,6 @@ class CookingService(ServerSystem): 如果有翻炒数据且标记了熟了 → 掉落熟的东西 如果没熟 → 掉落生的东西 """ - 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() @@ -303,7 +273,6 @@ class CookingService(ServerSystem): if not data: return - # 根据是否熟了决定掉落什么 if data.get("finished"): dropItems = data.get("dropIfDone", []) else: @@ -312,5 +281,4 @@ class CookingService(ServerSystem): for itemName in dropItems: self._giveItem(playerId, itemName) - # 清理翻炒数据 self.stirData.pop(key, None) \ No newline at end of file