搬运了原版食材 死亡生物 手办 功能实现 小bug 1

This commit is contained in:
wenhe committed 2026-09-02 19:24:00 +08:00
1 parent 4f032234c0
commit 213c74cb55
12 files changed
+1304

No files matched your search

@@ -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",
}
@@ -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