This commit is contained in:
wenhe committed 2026-07-20 16:20:06 +08:00
1 parent 7ef25d4e9d
commit d873dd4901
2 files changed
+237 -33

No files matched your search

@@ -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)
@@ -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 *