diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/ShoubanData.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/ShoubanData.py new file mode 100644 index 0000000..96c4b2a --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/ShoubanData.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +手办分类转换规则数据 +================================================================================ +只处理"手办"(xy_shouban) 分类下的转换: + 1) 盲盒开盒: 空手右键 盲盒(未开, jewelrybox_0) → 盲盒(开过, jewelrybox_1) + + 随机开出 1 个手办方块(掉落物形式) + 2) 猫/狐狸手办循环: 空手右键 逐级切换 + cat_0 → cat_1 → cat_2 → cat_3 → cat_0, fox 同理 +其余赞助者手办(figurines ~ figurines_5)为静态手办, 无转换。 + +【盲盒开出范围】 + 只开"转换链第一个"的猫/狐狸手办(cat_0 / fox_0) + 赞助者手办, 共 8 种均等概率。 + cat_1~3、fox_1~3 无法从盲盒开出, 只能通过循环转换逐级获得。 +================================================================================ +""" + +# ============================================================ +# 一、盲盒 +# ============================================================ +BLIND_BOX_SOURCE = "rtdd:jewelrybox_0" # 盲盒(未开) +BLIND_BOX_TARGET = "rtdd:jewelrybox_1" # 盲盒(开过) + +# 盲盒开出内容: 只开链首的猫/狐狸 + 赞助者手办(均等概率) +BLIND_BOX_POOL = [ + "rtdd:cat_0", + "rtdd:fox_0", + "rtdd:figurines", + "rtdd:figurines_1", + "rtdd:figurines_2", + "rtdd:figurines_3", + "rtdd:figurines_4", + "rtdd:figurines_5", +] + +# 开出内容的显示名(用于开盒提示) +FIGURINE_NAMES = { + "rtdd:cat_0": "猫手办", + "rtdd:fox_0": "狐狸手办", + "rtdd:figurines": "赞助者手办_1", + "rtdd:figurines_1": "赞助者手办_2", + "rtdd:figurines_2": "赞助者手办_3", + "rtdd:figurines_3": "赞助者手办_4", + "rtdd:figurines_4": "赞助者手办_5", + "rtdd:figurines_5": "赞助者手办_6", +} + +# ============================================================ +# 二、猫/狐狸手办循环转换链(空手右键逐级切换, 末尾回到起点) +# ============================================================ +CYCLE_CHAINS = { + "rtdd:cat_0": "rtdd:cat_1", + "rtdd:cat_1": "rtdd:cat_2", + "rtdd:cat_2": "rtdd:cat_3", + "rtdd:cat_3": "rtdd:cat_0", + "rtdd:fox_0": "rtdd:fox_1", + "rtdd:fox_1": "rtdd:fox_2", + "rtdd:fox_2": "rtdd:fox_3", + "rtdd:fox_3": "rtdd:fox_0", +} diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/service/ShoubanService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/service/ShoubanService.py new file mode 100644 index 0000000..907699d --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/service/ShoubanService.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +手办·右键转换引擎(服务端) +================================================================================ +只处理"手办"(xy_shouban) 分类下的【右键切换型】转换: + 盲盒开盒、猫/狐狸手办循环切换。 +全部为即时右键转换, 无计时, 因此只有一个服务类, 不需要拆分 TimerService。 + +【事件监听在哪?】 + 本类【不】监听任何事件! + 所有事件由 FeatureDispatcher(xiangyemod/service/FeatureDispatcher.py)统一监听: + - ServerBlockUseEvent → OnBlockUse → 按 blockOwner 路由到本类 handleBlockUse + 本类只实现被分发器调用的接口方法, 不关心事件来源。 + +【本类怎么做(自上而下的方法系列)】 + handleBlockUse ← 入口:分发器转发右键方块事件 + ├─ 空手确认 ← 盲盒/循环都要求空手 + ├─ checkCooldown← 连点冷却(防刷手办) + ├─ openBlindBox ← 盲盒: 换为"开过"盒 + 随机掉落 1 个手办 + 提示 + └─ replaceBlock ← 猫/狐狸: 逐级切到链上下一个形态 +================================================================================ +""" +import time +import random +import mod.server.extraServerApi as serverApi + +# ---- 网易API基础对象 ---- +ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类 +comp = serverApi.GetEngineCompFactory() # 组件工厂 + +# ---- 继承 BlueFox 框架的系统基类(提供 CreateEngineItemEntity 等方法) ---- +from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem +from ....BlueFoxTools.ToolsConfig import LevelId + +# ---- 只导入手办自己的数据库 ---- +from ..ShoubanData import ( + BLIND_BOX_SOURCE, BLIND_BOX_TARGET, + BLIND_BOX_POOL, FIGURINE_NAMES, CYCLE_CHAINS, +) + +# ---- 统一分发器(本类把自己注册进去, 让分发器把右键事件转发过来) ---- +from ...service.FeatureDispatcher import FeatureDispatcher + + +class ShoubanService(BlueFoxServerSystem): + """ + 手办·右键转换引擎 + + 【核心数据结构】 + shoubanBlockSet = {方块名, ...} # 管辖方块(盲盒 + 猫/狐狸链上所有方块) + cooldown = {key: 时间戳} # 连点冷却表 + """ + + def __init__(self, namespace, systemName): + """ + 初始化:构建管辖方块集合, 并注册到统一分发器。 + """ + super(ShoubanService, self).__init__(namespace, systemName) + print "===== ShoubanService init =====" + + # ---- 管辖方块: 盲盒(未开) + 猫/狐狸循环链上所有方块 ---- + self.shoubanBlockSet = set([BLIND_BOX_SOURCE]) | set(CYCLE_CHAINS.keys()) + + # ---- 右键冷却表 ---- + self.cooldown = {} + + # ---- 注册到统一分发器 ---- + FeatureDispatcher.register(self) + + # ============================================================================ + # 一、给 FeatureDispatcher 的接口 + # ============================================================================ + + def getFeatureBlocks(self): + """返回本功能管辖的所有方块名集合(右键路由用)""" + return self.shoubanBlockSet + + def getItemUseOnRules(self): + """返回"手持物品右键方块表面"的规则(手办分类没有, 返回空)""" + return {} + + def handleBlockUse(self, args): + """ + 由 FeatureDispatcher 转发的右键方块入口。 + 只有 blockName 在本功能的方块集合里才会被调用。 + + 盲盒开盒 / 手办循环均要求【空手】右键: + - 盲盒: 空手右键 → 换为"开过"盒 + 随机掉落 1 个手办 + - 猫/狐狸: 空手右键 → 逐级切到链上下一个形态 + + 返回 True = 本功能处理了这个右键(分发器取消默认行为) + 返回 False = 当前组合没有匹配规则(分发器不拦截) + """ + 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) + itemName = itemDict.get("newItemName") if itemDict else None + if itemName and itemName != "minecraft:air": + return False + + # ---- 连点冷却 ---- + if not self.checkCooldown(key, 0.5): + return True + + # ---- 盲盒开盒 ---- + if blockName == BLIND_BOX_SOURCE: + self.openBlindBox(playerId, pos, dim) + return True + + # ---- 猫/狐狸手办循环切换 ---- + if blockName in CYCLE_CHAINS: + self.replaceBlock(pos, dim, CYCLE_CHAINS[blockName], playerId) + return True + + return False + + # ============================================================================ + # 二、盲盒开盒 + # ============================================================================ + + def openBlindBox(self, playerId, pos, dim): + """ + 盲盒开盒:换为"开过"盒, 随机掉落 1 个手办方块, 提示开出内容。 + """ + # 随机开出一个手办 + fig = random.choice(BLIND_BOX_POOL) + # 盒子变为"开过" + self.replaceBlock(pos, dim, BLIND_BOX_TARGET, playerId) + # 掉落手办 + self.dropItems(dim, pos, [{"name": fig, "count": 1}]) + # 提示开出内容 + name = FIGURINE_NAMES.get(fig, "手办") + comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a盲盒开出了" + name + "!") + + # ============================================================================ + # 三、工具方法:获取玩家主手物品 + # ============================================================================ + + def getCarriedItem(self, playerId): + itemComp = comp.CreateItem(playerId) + return itemComp.GetPlayerItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0) + + # ============================================================================ + # 四、工具方法:替换方块 + # ============================================================================ + + def replaceBlock(self, pos, dim, newBlockName, playerId=None): + """ + 替换指定位置的方块。 + playerId 可为 None, 用 LevelId 创建组件。 + """ + creator = playerId if playerId is not None else LevelId + aux = 0 + try: + oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim) + aux = oldBlock.get("aux", 0) + except Exception: + aux = 0 + comp.CreateBlockInfo(creator).SetBlockNew( + pos, + blockDict={"name": newBlockName, "aux": aux}, + oldBlockHandling=0, + dimensionId=dim) + + # ============================================================================ + # 五、工具方法:以掉落物形式生成物品(开出/取下等) + # ============================================================================ + + def dropItems(self, dim, pos, dropList): + """ + dropList 格式:[{"name": "xxx", "count": N}, ...] + 在方块上方生成掉落物实体, 让它自然掉到地上。 + """ + for d in dropList: + itemDict = { + "itemName": d["name"], + "count": d.get("count", 1), + "auxValue": d.get("aux", 0), + } + self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2])) + + # ============================================================================ + # 六、工具方法:冷却检查 + # ============================================================================ + + def checkCooldown(self, key, cd=0.5): + """ + 右键冷却:同一方块 cd 秒内只响应一次, 防止连点刷手办/刷提示。 + """ + now = time.time() + last = self.cooldown.get(key, 0) + if now - last < cd: + return False + self.cooldown[key] = now + return True diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/service/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/shouban/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/DeadmobData.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/DeadmobData.py new file mode 100644 index 0000000..8e0f672 --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/DeadmobData.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +死亡生物方块转换规则数据 +================================================================================ +所有"死亡生物"(xy_deadmob) 分类下"右键方块→消耗物品→替换方块/掉落物品"的转换规则 +都在这里。只被 DeadmobService / DeadmobTimerService 读取,FeatureDispatcher 通过 +方块名集合路由到对应服务,不会去查其他功能的数据库。 + +【流程】 + 1) 挂肉钩挂放: 手持 死牛/死猪/死羊/原版鲑鱼/原版鳕鱼 右键 空钩(meathook) + → 挂上对应尸体(消耗1个)。鱼可挂两条。 + 2) 取下退回: 切肉开始前, 空手右键 整只/整头 → 空钩 + 掉落回尸体(不可逆前可退) + 3) 挂肉钩切肉: 手持 菜刀(cleaver) 右键 逐级切割(不可逆), 以掉落物形式获得对应部位肉 + - 切肉每刀掉落数量: 常规肉 2~3 个; 四肢固定 4 个; 牛头骨固定 1 个 + - 牛: 去头去皮掉牛头骨1+原版牛肉4~5 → 去四肢掉牛腿肉4 → 去肉掉牛肋2~3+牛里脊2~3+原版牛肉4~5 → 空钩掉碎肉2~3+原版牛肉4~5 + - 猪: 去皮掉五花肉2~3+原版生猪排4~5 → 去四肢掉猪蹄4 → 去肉掉猪肋排2~3+原版生猪排4~5 → 空钩掉碎肉2~3+原版生猪排4~5 + - 羊: 去皮掉羊小排2~3+原版生羊肉4~5 → 去四肢掉羊腿4 → 去肉掉羊里脊2~3+原版生羊肉4~5 → 空钩掉碎肉2~3+原版生羊肉4~5 + 4) 鱼晾干(计时): 鲑鱼/鳕鱼挂上后自动计时约300秒晾干(挂肉钩自动开始, autoStart=True) + - 鲑鱼: 挂一条→干一条; 挂两条→干两条 + - 鳕鱼: 挂两条→干两条 + 5) 取鱼干: 空手右键 干鱼钩 → 以掉落物形式获得 鱼干(thedriedfish) + 6) 死鱼/死鱿鱼切块(手持菜刀, 掉落物): + - 死鲑鱼: 共四刀, 每刀掉1份生鱼片(sashimi), 共4份 → 方块消失 + - 死鳕鱼: 共三刀, 每刀掉1份生鱼片(sashimi), 共3份 → 方块消失 + - 死鱿鱼: 一刀切须, 掉5~6个鱿鱼须(rawsquidwhiskersr) → 变为 死鱿鱼(切掉鱿鱼须) + +【尸体生成机制】(玩家杀死对应生物生成尸体) 已在txt标注"暂时不搞", 本次不实现。 + +字段说明(与 AnimalData/BarbecueData 一致): + source: 被右键的方块ID(必填) + item: 玩家手里必须拿的物品, None=空手 + target: 替换成什么方块, "minecraft:air"=消失 + consume: 消耗物品数量, 0=不消耗(默认1) + dropItem: 转换时以掉落物形式生成的物品列表(优先于 giveItem) + 格式: [{"name": "xxx", "count": N}, ...] + count 支持范围写法 (min,max), 服务端随机取整(如 (2,3)=2~3) + type: "switch"=右键切换, "timer"=计时自动转换 + time: 计时型转换需要多少秒 + tip: 转换成功时给玩家的提示(绿色§a开头) + doneTip: 计时到点完成时给启动者的提示 + autoStart: 放置时是否自动开始计时, 默认True(鱼挂上自动晾干) +================================================================================ +""" + +DEADMOB_SWITCHES = [ + + # ============================================================ + # 一、挂肉钩 - 挂放尸体(手持尸体右键空钩, 消耗1个) + # ============================================================ + { + "source": "rtdd:meathook", + "item": "rtdd:dead_cow", + "target": "rtdd:cow_0", + "consume": 1, + "type": "switch", + "tip": "§a已把整只牛挂上肉钩", + "giveItem": None, + }, + { + "source": "rtdd:meathook", + "item": "rtdd:dead_pig", + "target": "rtdd:pig_0", + "consume": 1, + "type": "switch", + "tip": "§a已把整只猪挂上肉钩", + "giveItem": None, + }, + { + "source": "rtdd:meathook", + "item": "rtdd:dead_sheep", + "target": "rtdd:sheep", + "consume": 1, + "type": "switch", + "tip": "§a已把整只羊挂上肉钩", + "giveItem": None, + }, + # 挂鲑鱼(原版salmon): 空钩→一条→两条 + { + "source": "rtdd:meathook", + "item": "minecraft:salmon", + "target": "rtdd:hang_meat_hooks_1", + "consume": 1, + "type": "switch", + "tip": "§a已挂上一条鲑鱼,开始晾晒", + "giveItem": None, + }, + { + "source": "rtdd:hang_meat_hooks_1", + "item": "minecraft:salmon", + "target": "rtdd:hang_meat_hooks_2", + "consume": 1, + "type": "switch", + "tip": "§a已再挂上一条鲑鱼", + "giveItem": None, + }, + # 挂鳕鱼(原版cod): 空钩→一条→两条 + { + "source": "rtdd:meathook", + "item": "minecraft:cod", + "target": "rtdd:meathookcod_1", + "consume": 1, + "type": "switch", + "tip": "§a已挂上一条鳕鱼,开始晾晒", + "giveItem": None, + }, + { + "source": "rtdd:meathookcod_1", + "item": "minecraft:cod", + "target": "rtdd:meathookcod_2", + "consume": 1, + "type": "switch", + "tip": "§a已再挂上一条鳕鱼", + "giveItem": None, + }, + + # ============================================================ + # 二、取下退回(未切割前, 空手右键 整只/整头 → 空钩 + 掉落回尸体) + # ============================================================ + { + "source": "rtdd:cow_0", + "item": None, + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:dead_cow", "count": 1}], + "tip": "§a取下了整只牛(尚未切割)", + "giveItem": None, + }, + { + "source": "rtdd:pig_0", + "item": None, + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:dead_pig", "count": 1}], + "tip": "§a取下了整只猪(尚未切割)", + "giveItem": None, + }, + { + "source": "rtdd:sheep", + "item": None, + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:dead_sheep", "count": 1}], + "tip": "§a取下了整只羊(尚未切割)", + "giveItem": None, + }, + + # ============================================================ + # 三、挂肉钩切肉(手持 菜刀cleaver, 掉落物, 数量随机 2~3 / 四肢固定4) + # ============================================================ + # ---- 牛(不可逆) ---- + { + "source": "rtdd:cow_0", + "item": "rtdd:cleaver", + "target": "rtdd:cow_1", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:cow_skull", "count": 1}, + {"name": "minecraft:beef", "count": (4, 5)}, + ], + "tip": "§a砍下了牛头", + "giveItem": None, + }, + { + "source": "rtdd:cow_1", + "item": "rtdd:cleaver", + "target": "rtdd:cow_2", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:legcow", "count": 4}], + "tip": "§a卸下了四肢牛腿肉", + "giveItem": None, + }, + { + "source": "rtdd:cow_2", + "item": "rtdd:cleaver", + "target": "rtdd:cow_3", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:costillavacaes", "count": (2, 3)}, + {"name": "rtdd:beef_tenderloin", "count": (2, 3)}, + {"name": "minecraft:beef", "count": (4, 5)}, + ], + "tip": "§a剔出了牛肋和牛里脊", + "giveItem": None, + }, + { + "source": "rtdd:cow_3", + "item": "rtdd:cleaver", + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:crushedmeat", "count": (2, 3)}, + {"name": "minecraft:beef", "count": (4, 5)}, + ], + "tip": "§a刮下碎肉,肉钩已清空", + "giveItem": None, + }, + # ---- 猪(不可逆) ---- + { + "source": "rtdd:pig_0", + "item": "rtdd:cleaver", + "target": "rtdd:pig_1", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:porkloins", "count": (2, 3)}, + {"name": "minecraft:porkchop", "count": (4, 5)}, + ], + "tip": "§a已给猪去皮", + "giveItem": None, + }, + { + "source": "rtdd:pig_1", + "item": "rtdd:cleaver", + "target": "rtdd:pig_2", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:ham", "count": 4}], + "tip": "§a卸下了四肢猪蹄", + "giveItem": None, + }, + { + "source": "rtdd:pig_2", + "item": "rtdd:cleaver", + "target": "rtdd:pig_3", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:porkrib", "count": (2, 3)}, + {"name": "minecraft:porkchop", "count": (4, 5)}, + ], + "tip": "§a剔出了猪肋排", + "giveItem": None, + }, + { + "source": "rtdd:pig_3", + "item": "rtdd:cleaver", + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:crushedmeat", "count": (2, 3)}, + {"name": "minecraft:porkchop", "count": (4, 5)}, + ], + "tip": "§a刮下碎肉,肉钩已清空", + "giveItem": None, + }, + # ---- 羊(不可逆) ---- + { + "source": "rtdd:sheep", + "item": "rtdd:cleaver", + "target": "rtdd:sheep_1", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:sheepracks", "count": (2, 3)}, + {"name": "minecraft:mutton", "count": (4, 5)}, + ], + "tip": "§a已给羊去皮", + "giveItem": None, + }, + { + "source": "rtdd:sheep_1", + "item": "rtdd:cleaver", + "target": "rtdd:sheep_2", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:legsleep", "count": 4}], + "tip": "§a卸下了四肢羊腿", + "giveItem": None, + }, + { + "source": "rtdd:sheep_2", + "item": "rtdd:cleaver", + "target": "rtdd:sheep_3", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:sheeploins", "count": (2, 3)}, + {"name": "minecraft:mutton", "count": (4, 5)}, + ], + "tip": "§a剔出了羊里脊", + "giveItem": None, + }, + { + "source": "rtdd:sheep_3", + "item": "rtdd:cleaver", + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [ + {"name": "rtdd:crushedmeat", "count": (2, 3)}, + {"name": "minecraft:mutton", "count": (4, 5)}, + ], + "tip": "§a刮下碎肉,肉钩已清空", + "giveItem": None, + }, + + # ============================================================ + # 四、取鱼干(空手右键 干鱼钩 → 掉落 鱼干) + # ============================================================ + { + "source": "rtdd:hang_meat_hooks_3", + "item": None, + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:thedriedfish", "count": 1}], + "tip": "§a取下了1份鱼干", + "giveItem": None, + }, + { + "source": "rtdd:hang_meat_hooks_4", + "item": None, + "target": "rtdd:hang_meat_hooks_3", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:thedriedfish", "count": 1}], + "tip": "§a取下了1份鱼干,还剩一条", + "giveItem": None, + }, + { + "source": "rtdd:meathookcoddry", + "item": None, + "target": "rtdd:meathook", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:thedriedfish", "count": 1}], + "tip": "§a取下了1份鱼干", + "giveItem": None, + }, + + # ============================================================ + # 五、死鱼/死鱿鱼切块(手持 菜刀cleaver, 掉落物) + # ============================================================ + # 死鲑鱼: 四刀, 每刀1份生鱼片 + { + "source": "rtdd:killfish_0", + "item": "rtdd:cleaver", + "target": "rtdd:killfish_1", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切下1份生鱼片", + "giveItem": None, + }, + { + "source": "rtdd:killfish_1", + "item": "rtdd:cleaver", + "target": "rtdd:killfish_2", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切下1份生鱼片", + "giveItem": None, + }, + { + "source": "rtdd:killfish_2", + "item": "rtdd:cleaver", + "target": "rtdd:killfish_3", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切下1份生鱼片", + "giveItem": None, + }, + { + "source": "rtdd:killfish_3", + "item": "rtdd:cleaver", + "target": "minecraft:air", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切完最后一份生鱼片", + "giveItem": None, + }, + # 死鳕鱼: 三刀, 每刀1份生鱼片 + { + "source": "rtdd:killcod_0", + "item": "rtdd:cleaver", + "target": "rtdd:killcod_1", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切下1份生鱼片", + "giveItem": None, + }, + { + "source": "rtdd:killcod_1", + "item": "rtdd:cleaver", + "target": "rtdd:killcod_2", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切下1份生鱼片", + "giveItem": None, + }, + { + "source": "rtdd:killcod_2", + "item": "rtdd:cleaver", + "target": "minecraft:air", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:sashimi", "count": 1}], + "tip": "§a切完最后一份生鱼片", + "giveItem": None, + }, + # 死鱿鱼: 一刀切须 + { + "source": "rtdd:deadsquida", + "item": "rtdd:cleaver", + "target": "rtdd:dead_squid_1", + "consume": 0, + "type": "switch", + "dropItem": [{"name": "rtdd:rawsquidwhiskersr", "count": (5, 6)}], + "tip": "§a切下了鱿鱼须", + "giveItem": None, + }, + + # ============================================================ + # 六、鱼晾干(计时自动转换, 挂上即自动开始 autoStart=True) + # ============================================================ + { + "source": "rtdd:hang_meat_hooks_1", + "item": None, + "target": "rtdd:hang_meat_hooks_3", + "consume": 0, + "type": "timer", + "time": 300, + "doneTip": "§e鲑鱼晾干了!", + "giveItem": None, + }, + { + "source": "rtdd:hang_meat_hooks_2", + "item": None, + "target": "rtdd:hang_meat_hooks_4", + "consume": 0, + "type": "timer", + "time": 300, + "doneTip": "§e鲑鱼晾干了!", + "giveItem": None, + }, + { + "source": "rtdd:meathookcod_2", + "item": None, + "target": "rtdd:meathookcoddry", + "consume": 0, + "type": "timer", + "time": 300, + "doneTip": "§e鳕鱼晾干了!", + "giveItem": None, + }, +] diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobService.py new file mode 100644 index 0000000..b9d1eb8 --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobService.py @@ -0,0 +1,270 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +死亡生物·右键转换引擎(服务端) +================================================================================ +只处理"死亡生物"(xy_deadmob) 分类下的【右键切换型】转换: + 挂肉钩挂放尸体、取下退回、菜刀切肉(牛/猪/羊)、取鱼干、死鱼/死鱿鱼切块。 + +【与计时转换解耦】 + 同分类的"鱼晾干"(计时自动转换)已拆到独立类:DeadmobTimerService。 + 本类只负责右键事件,两者通过 FeatureDispatcher 协作: + - 挂上鱼后(变成计时源方块)由 DeadmobTimerService 的 tick 自动开始晾干 + (autoStart=True,无需右键激活,因此本类不需要转发 startTimer) + - 计时到点转换完全由 DeadmobTimerService 负责 + +【事件监听在哪?】 + 本类【不】监听任何事件! + 所有事件由 FeatureDispatcher(xiangyemod/service/FeatureDispatcher.py)统一监听: + - ServerBlockUseEvent → OnBlockUse → 按 blockOwner 路由到本类 handleBlockUse + 本类只实现被分发器调用的接口方法,不关心事件来源。 + +【本类怎么做(自上而下的方法系列)】 + handleBlockUse ← 入口:分发器转发右键方块事件 + ├─ findRecipe ← 匹配 (方块名, 物品名) 规则;空手规则需确认真正空手 + ├─ checkCooldown ← 连点冷却(防刷提示) + └─ handleSwitch ← 切换型规则具体执行(挂放/取回/切肉/取鱼干/切块) + ├─ consumeItem ← 消耗物品(挂放尸体消耗1个) + ├─ replaceBlock ← 替换方块 + └─ dropItems ← 掉落物形式给物品(切肉/取回/取鱼干),支持随机数量 +================================================================================ +""" +import time +import random +import mod.server.extraServerApi as serverApi + +# ---- 网易API基础对象 ---- +ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类 +comp = serverApi.GetEngineCompFactory() # 组件工厂 + +# ---- 继承 BlueFox 框架的系统基类(提供 CreateEngineItemEntity 等方法) ---- +from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem +from ....BlueFoxTools.ToolsConfig import LevelId + +# ---- 只导入死亡生物自己的数据库 ---- +from ..DeadmobData import DEADMOB_SWITCHES + +# ---- 统一分发器 ---- +from ...service.FeatureDispatcher import FeatureDispatcher + + +class DeadmobService(BlueFoxServerSystem): + """ + 死亡生物·右键转换引擎 + + 【核心数据结构】 + deadmobRecipes = {(方块名, 物品名): 规则dict} # O(1) 查找,只收 switch 规则 + deadmobBlockSet = {方块名, ...} # O(1) 注册用(所有右键源方块) + cooldown = {key: 时间戳} # 连点冷却表 + """ + + def __init__(self, namespace, systemName): + """ + 初始化:构建规则字典,并注册到统一分发器(让分发器把右键事件转发过来)。 + """ + super(DeadmobService, self).__init__(namespace, systemName) + print "===== DeadmobService init =====" + + # ---- 构建右键规则字典(O(1) 查找),只收 switch 规则 ---- + # 计时规则(鱼晾干)由 DeadmobTimerService 处理,本类不接管 + self.deadmobRecipes = {} + self.deadmobBlockSet = set() + for sw in DEADMOB_SWITCHES: + if sw.get("type") == "timer": + continue + key = (sw["source"], sw.get("item")) + self.deadmobRecipes[key] = sw + self.deadmobBlockSet.add(sw["source"]) + + # ---- 右键冷却表 ---- + self.cooldown = {} + + # ---- 注册到统一分发器 ---- + FeatureDispatcher.register(self) + + # ============================================================================ + # 一、给 FeatureDispatcher 的接口 + # ============================================================================ + + def getFeatureBlocks(self): + """返回本功能管辖的所有方块名集合(右键路由用)""" + return self.deadmobBlockSet + + def getItemUseOnRules(self): + """ + 返回"手持物品右键方块表面"的规则(当前返回空)。 + 挂鱼走 ServerBlockUseEvent(右键方块本身)即可,不需要 ItemUseOn: + 日志验证手持原版鲑鱼/鳕鱼右键肉钩会正常触发 ServerBlockUseEvent 并转换, + 若同时注册 ItemUseOn 会重复转换(多消耗一条鱼)且 ServerItemUseOnEvent 的 + 玩家键是 entityId 不是 playerId,易出错,故不再使用该路径。 + """ + return {} + + def handleBlockUse(self, args): + """ + 由 FeatureDispatcher 转发的右键方块入口。 + 只有 blockName 在本功能的方块集合里才会被调用。 + + 返回 True = 本功能处理了这个右键(分发器会取消默认行为) + 返回 False = 当前组合没有匹配规则(分发器不拦截,交还默认行为) + """ + 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) + print "[Deadmob-Use]", blockName, "hand:", (itemDict.get("newItemName") if itemDict else None), "hit:", (recipe.get("source") if recipe else "NONE") + if not recipe: + return False + + # 冷却检查(防止连点刷掉落物/刷提示) + if not self.checkCooldown(key, recipe.get("switchCd", 0.5)): + return True + + # 切换型规则:就地执行 + self.handleSwitch(recipe, playerId, pos, dim, itemDict) + return True + + # ============================================================================ + # 二、规则匹配(O(1) 字典查找) + # ============================================================================ + + def findRecipe(self, blockName, itemDict): + """ + 在死亡生物字典中查找匹配规则: + 1. 先精确匹配 (方块名, 物品名) —— 手持菜刀切肉、手持尸体挂放、手持原版鱼挂放 + 2. 没找到再匹配 (方块名, None) —— 空手触发的规则(取下退回/取鱼干) + 注意:同一个源方块可能同时有"手持菜刀"和"空手"两类规则(如 cow_0 切肉/取回), + 因此 fallback 到空手规则前必须确认玩家【确实空手】,避免手持其他物品误触发。 + """ + itemName = itemDict.get("newItemName") if itemDict else None + recipe = self.deadmobRecipes.get((blockName, itemName)) + if recipe: + return recipe + # 空手规则:只有确认空手(无物品或手持空气)才 fallback + if not itemName or itemName == "minecraft:air": + return self.deadmobRecipes.get((blockName, None)) + return None + + # ============================================================================ + # 三、切换型规则处理 + # ============================================================================ + + def handleSwitch(self, recipe, playerId, pos, dim, itemDict): + """ + 处理"右键直接转换"类规则(统一流程:掉落物 → 消耗 → 替换 → 提示)。 + - 切肉(手持菜刀, consume=0): 掉落物 + 替换, 不消耗 + - 挂放(手持尸体/原版鱼, consume=1): 无掉落物, 消耗 + 替换 + - 取回/取鱼干(空手, consume=0): 掉落物 + 替换 + """ + # ---- 掉落物优先(切肉/取回/取鱼干都生成掉落物) ---- + if recipe.get("dropItem"): + self.dropItems(dim, pos, recipe["dropItem"]) + + # ---- 消耗物品(挂放尸体/挂鱼) ---- + if recipe.get("item") is not None and recipe.get("consume", 1) > 0: + self.consumeItem(playerId, itemDict, recipe.get("consume", 1)) + + # ---- 替换方块 ---- + if recipe.get("target"): + self.replaceBlock(pos, dim, recipe["target"], playerId) + + # ---- 转换成功提示 ---- + if recipe.get("tip"): + comp.CreateMsg(playerId).NotifyOneMessage(playerId, recipe["tip"]) + + # ============================================================================ + # 四、工具方法:获取玩家主手物品 + # ============================================================================ + + def getCarriedItem(self, playerId): + itemComp = comp.CreateItem(playerId) + return itemComp.GetPlayerItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0) + + # ============================================================================ + # 五、工具方法:消耗物品 + # ============================================================================ + + def consumeItem(self, playerId, itemDict, amount=1): + if itemDict is None: + return + count = itemDict.get("count", 0) + if count <= amount: + comp.CreateItem(playerId).SetEntityItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, + {"itemName": "minecraft:air", "count": 0, "auxValue": 0}) + else: + itemDict["count"] = count - amount + comp.CreateItem(playerId).SetEntityItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, + {"itemName": itemDict["newItemName"], + "count": itemDict["count"], + "auxValue": itemDict.get("newAuxValue", 0)}) + + # ============================================================================ + # 六、工具方法:替换方块 + # ============================================================================ + + def replaceBlock(self, pos, dim, newBlockName, playerId=None): + """ + 替换指定位置的方块。 + newBlockName="minecraft:air" 表示清除该方块。 + playerId 可为 None,用 LevelId 创建组件。 + """ + creator = playerId if playerId is not None else LevelId + # 清空方块时必须用 aux=0:空气没有朝向变体, + # 若继承死鱼(face_directional)的朝向 aux,SetBlockNew 无法把方块清掉 + aux = 0 + if newBlockName != "minecraft:air": + try: + oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim) + aux = oldBlock.get("aux", 0) + except Exception: + aux = 0 + comp.CreateBlockInfo(creator).SetBlockNew( + pos, + blockDict={"name": newBlockName, "aux": aux}, + oldBlockHandling=0, + dimensionId=dim) + + # ============================================================================ + # 七、工具方法:以掉落物形式生成物品(切肉/取下/取鱼干) + # ============================================================================ + + def dropItems(self, dim, pos, dropList): + """ + dropList 格式:[{"name": "xxx", "count": N 或 (min,max)}, ...] + count 为 tuple/list 时表示范围,随机取整(如 (4,5)=4~5、(2,3)=2~3)。 + 在方块上方生成掉落物实体,让它自然掉到地上。 + """ + for d in dropList: + cnt = d.get("count", 1) + if isinstance(cnt, (tuple, list)): + cnt = random.randint(cnt[0], cnt[1]) + itemDict = { + "itemName": d["name"], + "count": cnt, + "auxValue": d.get("aux", 0), + } + self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2])) + + # ============================================================================ + # 八、工具方法:冷却检查 + # ============================================================================ + + def checkCooldown(self, key, cd=0.5): + """ + 右键冷却:同一方块 cd 秒内只响应一次,防止连点刷掉落物/刷提示。 + """ + now = time.time() + last = self.cooldown.get(key, 0) + if now - last < cd: + return False + self.cooldown[key] = now + return True diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobTimerService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobTimerService.py new file mode 100644 index 0000000..cc7f963 --- /dev/null +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/DeadmobTimerService.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +""" +================================================================================ +死亡生物·计时转换引擎(服务端) +================================================================================ +只处理"死亡生物"(xy_deadmob) 分类下的【计时自动转换】: + 鲑鱼/鳕鱼挂上肉钩后自动晾干(约300秒)→ 变为干鱼钩。 + +【与右键转换解耦】 + 同分类的"右键切换"(挂放/切肉/取回/取鱼干/死鱼切块)在 DeadmobService。 + 本类只负责计时,两者通过 FeatureDispatcher 协作: + - 挂上鱼后,方块变成计时源(如 hang_meat_hooks_1),它的 tick 由 FeatureDispatcher + 按 timerOwner 路由到本类的 handleBlockEntityTick + - autoStart=True:挂上即自动开始晾干,无需右键激活,因此 DeadmobService + 不需要转发 startTimer(本类 startTimer 仅为接口完整性保留,不会被调用) + +【晾干规则】 + - autoStart=True:计时源方块放置/替换后,第一次 tick 自动补写计时数据 + - 晾干不需要任何环境条件(无 below 检查) + - 完成后清除计时数据,若启动者在线则发完成提示(自动晾干通常无启动者) + +【计时怎么做到"退出游戏继续计时"】 + 不做倒计时,只记"开始时刻": + - 首次 tick 把 开始时间戳(rtdd_tstart)+总时长(rtdd_tdur) 写进方块实体数据 + - 每帧 tick 判断 当前时间-开始时刻 >= 总时长 → 到点转换 + - 时间戳是绝对时间(time.time()),服务器重启/玩家离线真实时间仍在流逝, + 回来后 tick 直接算差值,剩余时间自然准确 + +【事件监听在哪?】 + 本类【不】监听任何事件! + ServerBlockEntityTickEvent 由 FeatureDispatcher 统一监听,再按 timerOwner + 路由到本类的 handleBlockEntityTick。 + +【本类怎么做(自上而下的方法系列)】 + handleBlockEntityTick ← 入口:分发器转发 tick 事件 + └─ _tickProcess ← 判断是否到点 / autoStart=True 自动补写计时 + └─ _finishTimer ← 到点执行转换(确认方块仍是计时源,否则清数据) + └─ _notifyIfOnline ← 完成提示(启动者在线才发) + _writeTimerData ← 写 开始时刻+时长 到方块实体 + _clearTimerData ← 清理方块上的计时数据 +================================================================================ +""" +import time +import mod.server.extraServerApi as serverApi + +# ---- 网易API基础对象 ---- +ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类 +comp = serverApi.GetEngineCompFactory() # 组件工厂 + +# ---- 继承 BlueFox 框架的系统基类 ---- +from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem +from ....BlueFoxTools.ToolsConfig import LevelId + +# ---- 方块实体数据组件(计时持久化:把开始时间戳+时长写进方块,重启后继续计时) ---- +blockEntityDataComp = comp.CreateBlockEntityData(LevelId) + +# ---- 只导入死亡生物自己的数据库 ---- +from ..DeadmobData import DEADMOB_SWITCHES + +# ---- 统一分发器(本类把自己注册进计时路由表,让分发器把 tick 事件转发过来) ---- +from ...service.FeatureDispatcher import FeatureDispatcher + + +class DeadmobTimerService(BlueFoxServerSystem): + """ + 死亡生物·计时转换引擎(鱼晾干) + + 【核心数据结构】 + deadmobTimers = {计时源方块名: 规则dict} # 只收 type=="timer" 的规则 + """ + + def __init__(self, namespace, systemName): + """ + 初始化:收集计时型规则,并注册到统一分发器的计时路由表。 + """ + super(DeadmobTimerService, self).__init__(namespace, systemName) + print "===== DeadmobTimerService init =====" + + # ---- 只收集计时型规则(source → 规则) ---- + self.deadmobTimers = {} + for sw in DEADMOB_SWITCHES: + if sw.get("type") == "timer": + self.deadmobTimers[sw["source"]] = sw + + # ---- 注册到分发器的计时路由表(tick 事件按方块名路由到这里) ---- + FeatureDispatcher.registerTimer(self) + + # ============================================================================ + # 一、给 FeatureDispatcher 的接口 + # ============================================================================ + + def getTimerBlocks(self): + """ + 返回本功能的所有"计时源方块"集合。 + FeatureDispatcher 靠它构建 方块名→本实例 的计时路由表(timerOwner)。 + """ + return set(self.deadmobTimers.keys()) + + def handleBlockEntityTick(self, args): + """ + 由 FeatureDispatcher 转发的计时方块 tick 入口。 + 只有 blockName 在计时路由表里才会被调用。 + """ + dim = args.get("dimension") + pos = (args.get("posX"), args.get("posY"), args.get("posZ")) + blockName = args.get("blockName", "") + self._tickProcess(dim, pos, blockName) + + def startTimer(self, dim, pos, blockName, playerId=None): + """ + 接口完整性保留。鱼晾干为 autoStart=True(挂上自动开始), + 本功能右键服务不会转发 startTimer,此方法通常不会被调用。 + """ + recipe = self.deadmobTimers.get(blockName) + if not recipe: + return + self._writeTimerData(dim, pos, recipe, playerId) + + # ============================================================================ + # 二、tick 检查与到点转换 + # ============================================================================ + + def _tickProcess(self, dim, pos, blockName): + """ + tick 主逻辑: + 1) 按事件自带的当前方块名判断是否计时源 + 2) 是计时源但没有计时数据 → autoStart=True 自动补写计时(挂上即自动晾干) + 3) 有计时数据 → 到点执行转换(重启后时间戳照常生效) + 方块被拆掉后 tick 停止、数据随方块消失,不会在原位置继续计时转换。 + 注意:GetBlockEntityData 返回的对象不支持 .get(),这里统一用 [] 读取。 + """ + try: + recipe = self.deadmobTimers.get(blockName) + if not recipe: + return # 当前方块不是计时源(tick 只对计时源方块开启) + + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + + # 读取计时标识;没有或与当前方块不符 + try: + tid = blockData["rtdd_tid"] + except Exception: + tid = "" + if tid != blockName: + # 鱼晾干 autoStart=True:挂上后自动补写计时 + if recipe.get("autoStart", True): + self._writeTimerData(dim, pos, recipe, None) + print "[Deadmob-Tick-Start]", blockName, pos + return + + tstart = blockData["rtdd_tstart"] + tdur = blockData["rtdd_tdur"] + + if time.time() - tstart >= tdur: + self._finishTimer(dim, pos, recipe, blockData) + except Exception: + pass + + def _finishTimer(self, dim, pos, recipe, data): + """ + 计时到点:确认方块仍是计时源,才执行转换(晾干无环境条件检查)。 + - 方块已被玩家改变 → 清掉过期计时 + - 转换后清除计时数据,若启动者在线发完成提示 + """ + try: + cur = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim) + if cur and cur.get("name") != recipe["source"]: + # 方块已被玩家改变 → 清掉过期计时 + self._clearTimerData(dim, pos) + return + # 替换方块 + self.replaceBlock(pos, dim, recipe["target"], None) + print "[Deadmob-Tick-Finish]", recipe["source"], "->", recipe["target"], pos + nextRecipe = self.deadmobTimers.get(recipe["target"]) + if nextRecipe: + # 下一级计时(换新的开始时间戳) + self._writeTimerData(dim, pos, nextRecipe, data["rtdd_tp"]) + else: + # 整条链结束:清数据 + 完成提示 + self._clearTimerData(dim, pos) + doneTip = recipe.get("doneTip") + if doneTip: + self._notifyIfOnline(data["rtdd_tp"], doneTip) + except Exception: + pass + + # ============================================================================ + # 三、计时数据读写(存在方块实体数据里,重启不丢) + # ============================================================================ + + def _writeTimerData(self, dim, pos, recipe, playerId=None): + """把 计时标识+开始时刻+时长+启动者 写进方块实体数据""" + try: + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + now = time.time() + blockData["rtdd_tid"] = recipe["source"] + blockData["rtdd_tstart"] = now + blockData["rtdd_tdur"] = recipe.get("time", 300) + blockData["rtdd_tp"] = playerId if playerId else "" + except Exception: + pass + + def _clearTimerData(self, dim, pos): + """清除方块上的计时数据""" + try: + blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if blockData is None: + return + blockData["rtdd_tid"] = "" + blockData["rtdd_tstart"] = 0 + blockData["rtdd_tdur"] = 0 + blockData["rtdd_tp"] = "" + except Exception: + pass + + # ============================================================================ + # 四、工具方法:完成提示 + # ============================================================================ + + def _notifyIfOnline(self, playerId, tip): + """若启动者在线,发送完成提示(离线则跳过)""" + if not tip or not playerId: + return + try: + ids = comp.CreateGame(LevelId).GetAllPlayerIds() + if playerId in ids: + comp.CreateMsg(playerId).NotifyOneMessage(playerId, tip) + except Exception: + pass + + # ============================================================================ + # 五、工具方法:替换方块 + # ============================================================================ + + def replaceBlock(self, pos, dim, newBlockName, playerId=None): + """ + 替换指定位置的方块。 + newBlockName="minecraft:air" 表示清除该方块。 + playerId 可为 None(计时回调时没有玩家),用 LevelId 创建组件。 + """ + creator = playerId if playerId is not None else LevelId + aux = 0 + try: + oldBlock = comp.CreateBlockInfo(creator).GetBlockNew(pos, dim) + aux = oldBlock.get("aux", 0) + except Exception: + aux = 0 + comp.CreateBlockInfo(creator).SetBlockNew( + pos, + blockDict={"name": newBlockName, "aux": aux}, + oldBlockHandling=0, + dimensionId=dim) diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/__init__.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/xiangyemod/siwangshengwu/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_grape.json b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_grape.json new file mode 100644 index 0000000..b6f8c2d --- /dev/null +++ b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_grape.json @@ -0,0 +1,18 @@ +{ + "format_version": "1.10", + "minecraft:item": { + "description": { + "identifier": "rtdd:grape", + "category": "xy_ingredients" + }, + "components": { + "minecraft:max_stack_size": 64, + "minecraft:use_animation": "eat", + "minecraft:use_duration": 1.6, + "minecraft:food": { + "nutrition": 2, + "saturation_modifier": "low" + } + } + } +} diff --git a/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_orange.json b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_orange.json new file mode 100644 index 0000000..60f573e --- /dev/null +++ b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_orange.json @@ -0,0 +1,18 @@ +{ + "format_version": "1.10", + "minecraft:item": { + "description": { + "identifier": "rtdd:orange", + "category": "xy_ingredients" + }, + "components": { + "minecraft:max_stack_size": 64, + "minecraft:use_animation": "eat", + "minecraft:use_duration": 1.6, + "minecraft:food": { + "nutrition": 2, + "saturation_modifier": "low" + } + } + } +} diff --git a/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_peach.json b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_peach.json new file mode 100644 index 0000000..9d21cf2 --- /dev/null +++ b/behavior_pack_v8jz0zSR/netease_items_beh/rtdd_peach.json @@ -0,0 +1,18 @@ +{ + "format_version": "1.10", + "minecraft:item": { + "description": { + "identifier": "rtdd:peach", + "category": "xy_ingredients" + }, + "components": { + "minecraft:max_stack_size": 64, + "minecraft:use_animation": "eat", + "minecraft:use_duration": 1.6, + "minecraft:food": { + "nutrition": 2, + "saturation_modifier": "low" + } + } + } +}