From d873dd4901a8612274f91ee3dea26df85ab167fb Mon Sep 17 00:00:00 2001 From: wenhe <1046926120@qq.com> Date: Mon, 20 Jul 2026 16:20:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script_NeteaseModousIY5aSF/Function.py | 269 +++++++++++++++--- .../System/Client/MainClient.py | 1 + 2 files changed, 237 insertions(+), 33 deletions(-) diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/Function.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/Function.py index 4e453d7..8543d0d 100644 --- a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/Function.py +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/Function.py @@ -1,153 +1,295 @@ # coding=utf-8 +""" +Function.py — 模组所有"自定义方块行为"的定义文件 +===================================================== +这个文件定义了游戏中每种烹饪方块被玩家右键点击后会做什么。 +每个方块都是一个"类"(class),类里写了它的各种行为。 + +整个文件的结构: + 1. 导入(import)—— 把需要用到的工具引进来 + 2. CookingStateEnum —— 烹饪状态的标记(煮/不煮) + 3. BaseBlock —— 所有方块的"爸爸",定义了公共行为 + 4. WoK —— 空炒锅 + 5. OilCan —— 油瓶 + 6. CookingWok —— 加了油的炒锅 + 7. CarrotAndBeef —— 胡萝卜牛肉(第一阶段:翻炒中) + 8. CarrotAndBeef1 —— 胡萝卜牛肉(第二阶段:快熟了) + 9. blockIndex —— 方块ID → 类的对照表(注册表) + 10. GetBaseBlock —— 根据坐标查找方块实例的工具函数 +""" import time -import ModUtil as util -from BlueFoxTools.ItemTools import CreateItemDict -from BlueFoxTools.SpecialFunciton.BlockBlock import BlockBlock -from BlueFoxTools.SystemTools import SystemTools -from BlueFoxTools.ToolsConfig import * +import ModUtil as util # 自己写的工具函数包(Normal和ServerUtil) +from BlueFoxTools.ItemTools import CreateItemDict # 快速创建一个物品数据包 +from BlueFoxTools.SpecialFunciton.BlockBlock import BlockBlock # 多格方块系统(解决大模型消失问题) +from BlueFoxTools.SystemTools import SystemTools # 系统工具箱(用来找到MainServer) +from BlueFoxTools.ToolsConfig import * # 全局配置和工具(LevelId, serverCompFactory等) class CookingStateEnum(object): - Default = -1 - Boil = 0 + """ + 烹饪状态标记 + 就像开关一样,告诉游戏当前方块是在"煮"还是"默认"状态 + """ + Default = -1 # 默认状态:没在煮 + Boil = 0 # 煮的状态 class BaseBlock(BlockBlock): - instances = {} - dropItems = [] + """ + ===== 所有烹饪方块的"爸爸"类 ===== + 所有具体的烹饪方块(炒锅、油瓶、牛肉锅...)都继承这个类。 + 这里定义了所有方块共有的东西: + - 翻炒次数(stirFryProperty) + - 煮的时间(boilProperty) + - 拆掉方块时掉落物品(Destroy) + - 方块变身(Translater) + - 存档数据(SaveData) + """ + + # ====== 类级别的变量(所有实例共享) ====== + instances = {} # 全局字典:记录世界上所有活着的BaseBlock实例 + # 键 = 坐标(如(10, 64, 5, 0)),值 = 方块实例本身 + dropItems = [] # 拆掉方块后会掉落的物品列表(子类会覆盖) def __init__(self, posDim, aux, posDims=None): + """ + 方块"出生"时调用(玩家放下方块时触发) + :param posDim: 位置坐标 (x, y, z, 维度) + :param aux: 方块朝向(0=南, 1=西, 2=北, 3=东) + :param posDims: 多格方块的所有坐标列表(如果不是多格方块就是None) + """ + # 第一步:先让"爸爸"(BlockBlock)完成它的初始化 + # 这一步会处理多格方块的放置、数据加载等 super(BaseBlock, self).__init__(posDim, aux, posDims) + # 第二步:从方块存档数据里读取"翻炒次数"和"煮的时间" + # 如果之前存过档,就能恢复;如果是新方块,会是None self.__stirFryProperty = self.dataComp['stirFry'] self.__boilProperty = self.dataComp['boil'] + # 第三步:把自己登记到全局实例字典里(方便后续查找) BaseBlock.instances[self.centerPosDim] = self - self.cookingState = CookingStateEnum.Default - self.isFinish = False + # 第四步:设置初始状态 + self.cookingState = CookingStateEnum.Default # 默认不在煮 + self.isFinish = False # 还没熟 + # 第五步:拿到MainServer的引用(后面掉落物品时需要用到) self.serverSystem = SystemTools.GetSystemByClsName('MainServer') @classmethod def Instances(cls): + """获取所有活着的BaseBlock实例的列表""" return cls.instances.values() def Tick(self): + """每游戏帧调用一次(目前为空,留给子类扩展)""" pass def BlockUse(self, playerId, itemDict): - """玩家尝试使用方块""" + """ + 玩家右键点击方块时触发 + :param playerId: 点击方块的玩家ID + :param itemDict: 玩家手里拿的物品信息 + 每个子类会重写这个方法,定义自己的右键行为 + """ + pass + # ====== 翻炒属性 ====== @property def stirFryProperty(self): # type:()->int """ - 翻炒属性,用于记录翻炒了多久 + 读取"翻炒了多少次" + 如果从来没翻过(None),就返回0 """ return self.__stirFryProperty if self.__stirFryProperty is not None else 0 @stirFryProperty.setter def stirFryProperty(self, value): + """设置"翻炒次数"(每翻一次就+1)""" self.__stirFryProperty = value + # ====== 煮的时间属性 ====== @property def boilProperty(self): # type:()->float """ - 煮了多久 - :return: + 读取"煮了多久"(单位:秒) + 如果从来没煮过(None),就返回0.0 """ return self.__boilProperty if self.__boilProperty is not None else 0.0 @boilProperty.setter def boilProperty(self, value): + """设置"煮的时间"(单位:秒)""" self.__boilProperty = value def SaveData(self): + """ + 把翻炒次数和煮的时间写入方块存档数据 + 这样关闭游戏再打开,数据不会丢失 + """ self.dataComp['stirFry'] = self.__stirFryProperty self.dataComp['boil'] = self.__boilProperty def Destroy(self, drop=0): + """ + 方块被拆掉时调用 + :param drop: 1=掉落物品,0=不掉落 + 流程: + 1. 从全局实例字典里把自己删掉 + 2. 调用父类的Destroy清除方块 + 3. 如果需要掉落,把dropItems列表里的物品一个一个丢出来 + """ + # 从实例字典里注销自己 del BaseBlock.instances[self.centerPosDim] + # 调用BlockBlock的Destroy(参数0=不让原版方块掉落,我们自己控制掉落) super(BaseBlock, self).Destroy(0) + # 如果需要掉落物品,就把dropItems里的东西一个个生成到世界上 if drop: for dropItem in self.dropItems: - self.serverSystem.CreateEngineItemEntity(dropItem, self.centerPosDim[3], self.centerPosDim[:3]) + self.serverSystem.CreateEngineItemEntity( + dropItem, self.centerPosDim[3], self.centerPosDim[:3] + ) def Translater(self, newBlock): - """转换方块为新的方块""" + """ + 让方块"变身"成另一个方块 + :param newBlock: 目标方块的ID,如 'rtdd:cookinggarlic' + 流程: + 1. 在blockIndex注册表里查这个ID对应的类 + 2. 如果找到了,先销毁当前方块,再在同一个位置创建新方块 + """ cls = blockIndex.get(newBlock) if cls: - self.Destroy() - cls.getInstance(self.centerPosDim) + self.Destroy() # 把自己拆了 + cls.getInstance(self.centerPosDim) # 在同一个位置创建新方块实例 + + +# ============================================================================ +# 下面是具体的方块类,每个类代表游戏里的一种方块 +# ============================================================================ class WoK(BaseBlock): + """ + ===== 空炒锅 ===== + 方块ID: rtdd:wok + 行为:玩家右键时,如果手里有油且锅下面是营火,锅就变成"带油的煎锅" + """ + # 告诉BlockBlock系统:这个方块只占1格,ID是rtdd:wok blockList = {(0, 0, 0): {'name': 'rtdd:wok', 'aux': 0}} def __init__(self, *args): super(WoK, self).__init__(*args) def BlockUse(self, playerId, itemDict): + """ + 玩家右键空炒锅时: + 条件1:手里拿的物品有油(GetItemOil检查标签) + 条件2:锅下面那一格是营火(IsBlockBelow检查) + 都满足 → 锅变身 + 油用掉 + 播放滋滋声 + """ if util.Normal.GetItemOil(itemDict) and util.Normal.IsBlockBelow( self.centerPosDim, 'minecraft:campfire' ): - self.Translater('rtdd:cookinggarlic') # 转化为带油的煎锅 + # 锅变身成"带油的煎锅"(cookinggarlic = cooking + garlic,意思是煎蒜/煎东西) + self.Translater('rtdd:cookinggarlic') + # 把油标签从物品上去掉(油用掉了) util.Normal.SetItemOil(itemDict, False) + # 把更新后的物品还给玩家手里 serverCompFactory.CreateItem(LevelId).SpawnItemToPlayerCarried(itemDict, playerId) + # 让附近所有玩家听到滋滋声 util.ServerUtil.PlaySoundToAllClient( self.centerPosDim, 'random.fizz', 2.4 ) -OID_CAN_MAX_SIZE = 16 # 油瓶最大油量 +OID_CAN_MAX_SIZE = 16 # 油瓶最大能装多少份油(注意:OID是拼写错误,应该是OIL) class OilCan(BaseBlock): + """ + ===== 油瓶 ===== + 方块ID: rtdd:oilcan + 行为:右键可以存油/取油,最多存16份 + """ blockList = {(0, 0, 0): {'name': 'rtdd:oilcan', 'aux': 0}} def __init__(self, *args): super(OilCan, self).__init__(*args) + # 从存档数据里读取当前油量 self.__oil = self.dataComp['oil'] + # 初始化时自动装满油 self.SetFill() def SetFill(self): + """把油瓶装满(16份)""" self.oil = OID_CAN_MAX_SIZE def BlockUse(self, playerId, itemDict): + """ + 玩家右键油瓶时: + 情况A:手里有油 → 往瓶里加油(if self.oil < 16 才加) + 情况B:手里没油 → 从瓶里取油(if self.oil > 0 才取) + 最后显示剩余油量提示 + """ if util.Normal.GetItemOil(itemDict): + # ===== 情况A:玩家手里有油,往瓶里存 ===== if self.oil < OID_CAN_MAX_SIZE: - self.oil += 1 + self.oil += 1 # 瓶里油量+1 + # 把物品上的油标签去掉 util.Normal.SetItemOil(itemDict, False) serverCompFactory.CreateItem(playerId).SpawnItemToPlayerCarried(itemDict, playerId) elif self.oil > 0: + # ===== 情况B:玩家手里没油,从瓶里取 ===== + # 给物品贴上"有油"标签 util.Normal.SetItemOil(itemDict, True) serverCompFactory.CreateItem(playerId).SpawnItemToPlayerCarried(itemDict, playerId) - self.oil -= 1 + self.oil -= 1 # 瓶里油量-1 + # 在玩家屏幕上显示剩余油量(如"剩余油量8/16") serverCompFactory.CreateGame(LevelId).SetOneTipMessage( playerId, '剩余油量{}/{}'.format(self.oil, OID_CAN_MAX_SIZE) ) @property def oil(self): + """读取油量,如果是None(新方块)就返回0""" return 0 if self.__oil is None else self.__oil @oil.setter def oil(self, value): + """设置油量""" self.__oil = value def SaveData(self): + """ + 存档:先让父类存翻炒/煮的数据,再额外存油量 + """ super(OilCan, self).SaveData() self.dataComp['oil'] = self.__oil class CookingWok(BaseBlock): + """ + ===== 带油的煎锅(准备炒菜状态) ===== + 方块ID: rtdd:cookinggarlic + 行为:玩家右键时,如果手里是牛肉,锅就变成"胡萝卜牛肉锅" + BUG:目前没有消耗玩家手里的牛肉!玩家可以无限放牛肉。 + """ blockList = {(0, 0, 0): {'name': 'rtdd:cookinggarlic', 'aux': 0}} + # 拆掉带油锅会掉落空锅 dropItems = [CreateItemDict('rtdd:wok')] def __init__(self, *args): super(CookingWok, self).__init__(*args) def BlockUse(self, playerId, itemDict): + """ + 玩家右键带油煎锅时: + 如果手里拿的是生牛肉(minecraft:beef) + → 锅变身成"胡萝卜牛肉锅"(carrotandbeef) + → 播放滋滋声 + """ if itemDict and itemDict.get('newItemName') == 'minecraft:beef': self.Translater('rtdd:carrotandbeef') util.ServerUtil.PlaySoundToAllClient( @@ -156,32 +298,67 @@ class CookingWok(BaseBlock): class CarrotAndBeef(BaseBlock): + """ + ===== 胡萝卜牛肉(第一阶段:翻炒中) ===== + 方块ID: rtdd:carrotandbeef + 行为:玩家用木剑右键 = 翻炒动作 + 每1.2秒才能翻一次 + 翻够5次以上 → 变身成第二阶段 + """ blockList = {(0, 0, 0): {'name': 'rtdd:carrotandbeef', 'aux': 0}} + # 拆掉会退锅和生牛肉 dropItems = [CreateItemDict('rtdd:wok'), CreateItemDict('minecraft:beef')] def __init__(self, *args): super(CarrotAndBeef, self).__init__(*args) + # 记录上次翻炒的时间,用来判断冷却 self.stirTime = time.time() def BlockUse(self, playerId, itemDict): - stirTime = time.time() - if stirTime - self.stirTime > 1.2: + """ + 玩家右键胡萝卜牛肉锅时: + 1. 先检查距离上次翻炒是否超过1.2秒(冷却时间) + 2. 如果没到冷却时间 → 提示"这时候翻动会不好熟的!" + 3. 如果到了冷却时间且手里是木剑 → 执行翻炒: + - 翻炒次数+1 + - 播放滋滋声 + - 播放挥剑动画 + - 切换锅的纹理(0→1→2→3循环,模拟翻炒效果) + - 如果翻够5次以上 → 变身成第二阶段(carrotandbeef_1) + """ + stirTime = time.time() # 当前时间 + if stirTime - self.stirTime > 1.2: # 冷却时间1.2秒 if itemDict and itemDict.get('newItemName') == 'minecraft:wooden_sword': + # 翻炒次数+1 self.stirFryProperty += 1 + # 播放滋滋声 util.ServerUtil.PlaySoundToAllClient( self.centerPosDim, 'random.fizz', 2.1 ) + # 让玩家挥剑(模拟翻炒动作) util.ServerUtil.PlayAttackToClient(playerId) + # 替换方块纹理(aux 在 0,1,2,3 之间循环,制造翻炒动画效果) self.ReplaceBlocks({(0, 0, 0): {'name': 'rtdd:carrotandbeef', 'aux': self.stirFryProperty % 4}}) + # 翻够5次以上 → 熟了!变身第二阶段 if self.stirFryProperty > 5: self.Translater('rtdd:carrotandbeef_1') + # 更新最后一次翻炒时间 self.stirTime = stirTime else: + # 冷却时间还没到,提示玩家 serverCompFactory.CreateGame(LevelId).SetOneTipMessage(playerId, '这时候翻动会不好熟的!') class CarrotAndBeef1(BaseBlock): + """ + ===== 胡萝卜牛肉(第二阶段:快熟了) ===== + 方块ID: rtdd:carrotandbeef_1 + 行为:继续用木剑翻炒,冷却时间缩短到0.8秒 + 翻够3次以上 → 熟了!提示"出锅啦!拿盘子盛出来" + 拆掉熟锅 → 掉落熟牛肉(不再是生牛肉!) + """ blockList = {(0, 0, 0): {'name': 'rtdd:carrotandbeef_1', 'aux': 0}} + # 没熟之前拆掉,退锅和生牛肉 dropItems = [CreateItemDict('rtdd:wok'), CreateItemDict('minecraft:beef')] def __init__(self, *args): @@ -189,8 +366,16 @@ class CarrotAndBeef1(BaseBlock): self.stirTime = time.time() def BlockUse(self, playerId, itemDict): + """ + 玩家右键第二阶段牛肉锅时: + 1. 冷却时间0.8秒(比第一阶段更快) + 2. 没到冷却 → 提示"这时候翻动会不好熟的!" + 3. 到了冷却且手里是木剑且还没熟 → 翻炒 + 4. 到了冷却且手里是木剑且已经熟了 → 提示"出锅啦!" + """ stirTime = time.time() if stirTime - self.stirTime > 0.8: + # 还没熟,继续翻炒 if itemDict and itemDict.get('newItemName') == 'minecraft:wooden_sword' and not self.isFinish: self.stirFryProperty += 1 util.ServerUtil.PlaySoundToAllClient( @@ -198,26 +383,44 @@ class CarrotAndBeef1(BaseBlock): ) util.ServerUtil.PlayAttackToClient(playerId) self.ReplaceBlocks({(0, 0, 0): {'name': 'rtdd:carrotandbeef_1', 'aux': self.stirFryProperty % 4}}) + # 翻够3次以上 → 熟了! self.isFinish = self.stirFryProperty > 3 + # 已经熟了,提示出锅 elif itemDict and itemDict.get('newItemName') == 'minecraft:wooden_sword' and self.isFinish: serverCompFactory.CreateGame(LevelId).SetOneTipMessage(playerId, '出锅啦!拿盘子盛出来') else: serverCompFactory.CreateGame(LevelId).SetOneTipMessage(playerId, '这时候翻动会不好熟的!') def Destroy(self, drop=0): - if self.isFinish: self.dropItems = [CreateItemDict('minecraft:cooked_beef'), CreateItemDict('rtdd:wok')] + """ + 拆掉方块时: + 如果已经熟了 → 掉落物改成"熟牛肉+空锅"(不再是生牛肉!) + 如果还没熟 → 掉落物还是"生牛肉+空锅"(用父类定义的dropItems) + """ + if self.isFinish: + self.dropItems = [CreateItemDict('minecraft:cooked_beef'), CreateItemDict('rtdd:wok')] super(CarrotAndBeef1, self).Destroy(drop) +# ============================================================================ +# blockIndex — 方块ID注册表 +# ============================================================================ +# 这个字典告诉游戏:"遇到这个方块ID时,用对应的类来处理它" +# 当MainServer捕获到玩家放方块/右键方块事件时,会先来这个表里查 blockIndex = { - 'rtdd:wok': WoK, - 'rtdd:cookinggarlic': CookingWok, - 'rtdd:carrotandbeef': CarrotAndBeef, - 'rtdd:carrotandbeef_1': CarrotAndBeef1, - 'rtdd:oilcan': OilCan + 'rtdd:wok': WoK, # 空炒锅 + 'rtdd:cookinggarlic': CookingWok, # 带油的煎锅 + 'rtdd:carrotandbeef': CarrotAndBeef, # 胡萝卜牛肉(第一阶段) + 'rtdd:carrotandbeef_1': CarrotAndBeef1,# 胡萝卜牛肉(第二阶段) + 'rtdd:oilcan': OilCan # 油瓶 } def GetBaseBlock(posDim): - # type:(Tuple)->BaseBlock - return BaseBlock.instances.get(posDim) + """ + 根据坐标查找方块实例 + :param posDim: (x, y, z, 维度) 坐标 + :return: 该位置的BaseBlock实例,如果没找到返回None + 被MainServer调用,用来判断玩家右键的是不是我们的烹饪方块 + """ + return BaseBlock.instances.get(posDim) \ No newline at end of file diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/System/Client/MainClient.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/System/Client/MainClient.py index 17600fb..accd075 100644 --- a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/System/Client/MainClient.py +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/System/Client/MainClient.py @@ -1,4 +1,5 @@ # coding=utf-8 +from ...BlueFoxTools.ModConfig import MOD_NAME from ...BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxClientSystem from ...BlueFoxTools.SystemTools import SystemTools from ...BlueFoxTools.ToolsConfig import *