破坏掉落存储物品 (keyong)
This commit is contained in:
1 parent
0cb1da680d
commit
620500ab45
11 files changed
+373
-101
No files matched your search
+71
-2
@@ -19,7 +19,8 @@
|
||||
dropItem: 以掉落物形式给的物品 [{"name": "xx", "count": N}]
|
||||
record: "count"=记录一种物品+数量(箭桶) / "stack"=后放先取堆栈(书柜),
|
||||
记录写入方块实体数据(rtdd_swt/rtdd_swc/rtdd_swi/rtdd_swa/rtdd_sws),
|
||||
退出重进不丢
|
||||
退出重进不丢; 附魔以扁平ench字段([[附魔ID,等级],...])存储,
|
||||
不存含__type__/__value__的userData(引擎写不进方块实体)
|
||||
maxCount: record 类型最大容量(箭桶32 / 书柜13)
|
||||
arrowMode:"store"=箭桶存箭(批量) / "retrieve"=箭桶取箭(一次性全取)
|
||||
discPut: True=唱片机放入时把唱片写入方块实体(取回时原样返还)
|
||||
@@ -160,6 +161,43 @@ for _i in range(len(_BOOKCASE_CHAIN) - 1, 0, -1):
|
||||
"record": "stack", "tip": "§a取回了书",
|
||||
})
|
||||
|
||||
# ============================================================
|
||||
# 四b、药水架 (lotion 系列, 后放先取, 至多3瓶)
|
||||
# 放药水(任意药水: 普通/喷溅/滞留 minecraft:potion/splash_potion/lingering_potion):
|
||||
# 逐个放入, 消耗1瓶, stack记录物品名+aux(aux决定药水效果)
|
||||
# 取药水: 空手右键, 后放先取(LIFO), 原样返还该瓶药水
|
||||
# 方块链: 药水架_1(空)=lotion_0, 药水架_2(一瓶)=lotion_1,
|
||||
# 药水架_3(两瓶)=lotion_2, 药水架_4(三瓶)=lotion_3
|
||||
# ============================================================
|
||||
_POTIONRACK_CHAIN = [
|
||||
"rtdd:lotion_0", # 药水架_1(空)
|
||||
"rtdd:lotion_1", # 药水架_2(一瓶)
|
||||
"rtdd:lotion_2", # 药水架_3(两瓶)
|
||||
"rtdd:lotion_3", # 药水架_4(三瓶)
|
||||
]
|
||||
|
||||
POTIONRACK_RULES = []
|
||||
# 放入: 第 i 级 -> 第 i+1 级, 任意药水, 消耗1, stack记录
|
||||
for _i in range(len(_POTIONRACK_CHAIN) - 1):
|
||||
_src, _dst = _POTIONRACK_CHAIN[_i], _POTIONRACK_CHAIN[_i + 1]
|
||||
POTIONRACK_RULES.append({
|
||||
"source": _src, "item": "*potion", "target": _dst,
|
||||
"consume": 1, "record": "stack", "maxCount": 3,
|
||||
"tip": "§a放入1瓶药水",
|
||||
})
|
||||
# 取回: 第 i 级 -> 第 i-1 级, 空手, 后放先取
|
||||
for _i in range(len(_POTIONRACK_CHAIN) - 1, 0, -1):
|
||||
_src, _dst = _POTIONRACK_CHAIN[_i], _POTIONRACK_CHAIN[_i - 1]
|
||||
POTIONRACK_RULES.append({
|
||||
"source": _src, "item": None, "target": _dst,
|
||||
"record": "stack", "tip": "§a取回了1瓶药水",
|
||||
})
|
||||
# 满架: 手持药水右键三瓶满的药水架 -> 提示已满, 拦截默认行为(防止误喝药水)
|
||||
POTIONRACK_RULES.append({
|
||||
"source": "rtdd:lotion_3", "item": "*potion",
|
||||
"tip": "§c药水架已满!",
|
||||
})
|
||||
|
||||
# ============================================================
|
||||
# 五、厨具架 (kitchenwarerack, 至多挂2件)
|
||||
# 组合由方块状态+stack记录决定, 与放入顺序无关, 取回后放先取。
|
||||
@@ -203,6 +241,23 @@ KITCHENRACK_COMBO_BLOCK = {
|
||||
("rtdd:cleaver", "rtdd:cleaver"): "rtdd:kitchenwarerackd_3", # 双菜刀
|
||||
}
|
||||
|
||||
# 方块状态 -> 厨具物品列表(名称即组合, 顺序按方块名从左到右, 最后一件=后放入)
|
||||
# 用于"无记录"时推断(如创造模式直接放置带厨具的厨具架), 取回按此顺序后放先取
|
||||
KITCHENRACK_BLOCK_ITEMS = {
|
||||
"rtdd:kitchenwarerack": [], # 空
|
||||
"rtdd:kitchenwareracka_1": ["rtdd:spatula"], # 单锅铲
|
||||
"rtdd:kitchenwareracka_2": ["rtdd:spatula", "rtdd:spatula"], # 双锅铲
|
||||
"rtdd:kitchenwareracka_3": ["rtdd:spatula", "rtdd:potandspoon"], # 锅铲+锅勺
|
||||
"rtdd:kitchenwarerackb_1": ["rtdd:potandspoon"], # 单锅勺
|
||||
"rtdd:kitchenwarerackb_2": ["rtdd:potandspoon", "rtdd:potandspoon"], # 双锅勺
|
||||
"rtdd:kitchenwarerackc_1": ["rtdd:wok"], # 单炒锅
|
||||
"rtdd:kitchenwarerackc_2": ["rtdd:wok", "rtdd:cleaver"], # 炒锅+菜刀
|
||||
"rtdd:kitchenwarerackd_1": ["rtdd:cleaver"], # 单菜刀
|
||||
"rtdd:kitchenwarerackd_2": ["rtdd:cleaver", "rtdd:spatula"], # 菜刀+锅铲
|
||||
"rtdd:kitchenwarerackd_3": ["rtdd:cleaver", "rtdd:cleaver"], # 双菜刀
|
||||
"rtdd:kitchenwarerackd_4": ["rtdd:cleaver", "rtdd:potandspoon"], # 菜刀+锅勺
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 六、古式唱片机 (b_108 无碟 / b_109 有碟)
|
||||
# 放入: 手持任意原版唱片 -> 有碟, 消耗1, 记录唱片(rtdd_disc)
|
||||
@@ -239,8 +294,22 @@ TRASHBIN_RULES = [
|
||||
"target": "rtdd:storage_buckets", "tip": "§a垃圾桶已关闭"},
|
||||
]
|
||||
|
||||
# ============================================================
|
||||
# 八、左键破坏存物方块的掉落(书柜/药水架/厨具架)
|
||||
# 存了东西的方块被左键打碎时, 掉 空方块本体 + 全部存储物品(附魔/药水效果保留)。
|
||||
# 与售卖盒不同: 破坏后记录随方块一起消失, 重新放置右键取不到东西。
|
||||
# ============================================================
|
||||
STORAGE_EMPTY_BLOCK = {}
|
||||
for _b in _BOOKCASE_CHAIN[1:]: # 书柜_2~14(有书) -> 书柜_1(空)
|
||||
STORAGE_EMPTY_BLOCK[_b] = _BOOKCASE_CHAIN[0]
|
||||
for _b in _POTIONRACK_CHAIN[1:]: # 药水架_2~4(有药水) -> 药水架_1(空)
|
||||
STORAGE_EMPTY_BLOCK[_b] = _POTIONRACK_CHAIN[0]
|
||||
for _b in KITCHENRACK_BLOCKS[1:]: # 厨具架各形态 -> 厨具架(空)
|
||||
STORAGE_EMPTY_BLOCK[_b] = KITCHENRACK_BLOCKS[0]
|
||||
|
||||
# ============================================================
|
||||
# 汇总: 家具家私右键转换规则(厨具架为通用逻辑, 不在规则表, 单独注册)
|
||||
# ============================================================
|
||||
FURNITURE_SWITCHES = (ARROWBARREL_RULES + FLUME_RULES + TRAYRACKS_RULES
|
||||
+ BOOKCASE_RULES + RECORDPLAYER_RULES + TRASHBIN_RULES)
|
||||
+ BOOKCASE_RULES + POTIONRACK_RULES
|
||||
+ RECORDPLAYER_RULES + TRASHBIN_RULES)
|
||||
+222
-66
@@ -27,8 +27,9 @@
|
||||
记录写入方块实体数据, 退出重进不丢(rtdd_swt 类型标记):
|
||||
- count记录(箭桶): 记录一种箭的类型+数量, 字段 rtdd_swt=count /
|
||||
rtdd_swc=数量 / rtdd_swi=物品名 / rtdd_swa=aux
|
||||
- stack记录(书柜): 后放先取堆栈, 记录完整物品信息(含userData, 附魔不丢),
|
||||
字段 rtdd_swt=stack / rtdd_sws=堆栈列表
|
||||
- stack记录(书柜): 后放先取堆栈, 记录物品信息, 附魔以扁平格式 [[附魔ID,等级],...]
|
||||
存字段 ench(不存含__type__/__value__的userData, 引擎写不进方块实体),
|
||||
字段 rtdd_swt=stack / rtdd_sws=堆栈(原始扁平列表存储, 参考 demo.py)
|
||||
唱片机单独用 rtdd_disc 记录唱片。SetBlockNew 后 block entity 可能未初始化,
|
||||
写入带 0.1s 延迟兜底。
|
||||
|
||||
@@ -41,6 +42,7 @@
|
||||
================================================================================
|
||||
"""
|
||||
import time
|
||||
import json
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
# ---- 网易API基础对象 ----
|
||||
@@ -59,7 +61,8 @@ gameComp = comp.CreateGame(LevelId)
|
||||
# ---- 只导入家具家私自己的数据库 ----
|
||||
from ..FurnitureData import (FURNITURE_SWITCHES,
|
||||
KITCHENRACK_BLOCKS, KITCHENWARE_ITEMS,
|
||||
KITCHENRACK_SINGLE_BLOCK, KITCHENRACK_COMBO_BLOCK)
|
||||
KITCHENRACK_SINGLE_BLOCK, KITCHENRACK_COMBO_BLOCK,
|
||||
KITCHENRACK_BLOCK_ITEMS, STORAGE_EMPTY_BLOCK)
|
||||
|
||||
# ---- 统一分发器 ----
|
||||
from ...service.FeatureDispatcher import FeatureDispatcher
|
||||
@@ -133,7 +136,7 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
if blockName in self.kitchenrackBlockSet:
|
||||
if not self.checkCooldown(key, 0.5):
|
||||
return True
|
||||
self._handleKitchenRack(playerId, pos, dim, itemDict, itemName)
|
||||
self._handleKitchenRack(playerId, pos, dim, itemDict, itemName, blockName)
|
||||
return True
|
||||
|
||||
recipe = self.findRecipe(blockName, itemDict)
|
||||
@@ -153,6 +156,61 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
self.handleSwitch(recipe, playerId, pos, dim, itemDict, key)
|
||||
return True
|
||||
|
||||
def handleDestroy(self, args):
|
||||
"""
|
||||
由 FeatureDispatcher 转发的"玩家左键破坏方块"入口。
|
||||
存了东西的家具方块(书柜/药水架/厨具架)被打碎时:
|
||||
取消默认掉落 → 掉 空方块本体(保留朝向) + 全部存储物品(附魔/药水效果保留)
|
||||
→ 方块清成空气。
|
||||
与售卖盒不同: 破坏后记录随方块一起消失, 重新放置右键取不到东西。
|
||||
|
||||
返回 True=本功能处理了这次破坏; False=空方块本体/不归本功能管(交还默认掉落)。
|
||||
"""
|
||||
# ServerPlayerTryDestroyBlockEvent 的方块名字段是 fullName(不是 blockName)
|
||||
blockName = args.get("fullName") or args.get("blockName")
|
||||
emptyBlock = STORAGE_EMPTY_BLOCK.get(blockName)
|
||||
if not emptyBlock:
|
||||
return False
|
||||
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()
|
||||
|
||||
# 读存储记录(附魔/药水效果都在里面)
|
||||
record = self._loadRecord(pos, dim)
|
||||
stack = list(record.get("stack") or []) if record else []
|
||||
|
||||
# 取消默认破坏+默认掉落(不让"有东西的方块"本身掉出来)
|
||||
args['cancel'] = True
|
||||
args['spawnResources'] = False
|
||||
|
||||
# 记录朝向aux, 再清空方块为空气(aux必须为0)
|
||||
aux = 0
|
||||
try:
|
||||
old = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
aux = old.get("aux", 0)
|
||||
except Exception:
|
||||
aux = 0
|
||||
try:
|
||||
comp.CreateBlockInfo(LevelId).SetBlockNew(
|
||||
pos, blockDict={"name": "minecraft:air", "aux": 0},
|
||||
oldBlockHandling=0, dimensionId=dim)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 掉落空方块本体(保留朝向)
|
||||
try:
|
||||
self.CreateEngineItemEntity(
|
||||
{"itemName": emptyBlock, "count": 1, "auxValue": aux},
|
||||
dim, (x, y + 0.5, z))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 掉落全部存储物品
|
||||
for itemObj in stack:
|
||||
self._dropItemObj(dim, pos, itemObj)
|
||||
return True
|
||||
|
||||
# ============================================================================
|
||||
# 二、规则匹配(O(1) 字典查找)
|
||||
# ============================================================================
|
||||
@@ -185,6 +243,13 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
if recipe:
|
||||
return recipe
|
||||
|
||||
# 3b. 药水架:手持任意药水(普通/喷溅/滞留)
|
||||
if itemName in ("minecraft:potion", "minecraft:splash_potion",
|
||||
"minecraft:lingering_potion"):
|
||||
recipe = self.furnitureRecipes.get((blockName, "*potion"))
|
||||
if recipe:
|
||||
return recipe
|
||||
|
||||
# 4. 任意物品匹配(垃圾桶)
|
||||
if itemName and itemName != "minecraft:air":
|
||||
recipe = self.furnitureRecipes.get((blockName, "*any"))
|
||||
@@ -284,29 +349,36 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
# 四、厨具架通用逻辑(组合由 stack 记录计算, 与放入顺序无关, 取回后放先取)
|
||||
# ============================================================================
|
||||
|
||||
def _handleKitchenRack(self, playerId, pos, dim, itemDict, itemName):
|
||||
def _handleKitchenRack(self, playerId, pos, dim, itemDict, itemName, blockName):
|
||||
"""
|
||||
厨具架:放入/取回通用处理。
|
||||
放入: 手持 锅铲/锅勺/菜刀/炒锅 右键空架/单件架 -> 按组合(顺序无关)成型
|
||||
取回: 空手右键 -> 弹出最后放入的厨具并返还, 形态退回剩余单件/空
|
||||
无记录时(如创造模式直接放置带厨具的厨具架)按方块状态推断厨具。
|
||||
"""
|
||||
record = self._loadRecord(pos, dim)
|
||||
stack = record["stack"] if record else []
|
||||
if record and record.get("stack"):
|
||||
stack = record["stack"] # 有记录: 物品对象列表
|
||||
stackNames = [s.get("itemName") for s in stack]
|
||||
else:
|
||||
stack = [] # 无记录: 按方块状态推断
|
||||
stackNames = list(KITCHENRACK_BLOCK_ITEMS.get(blockName, []))
|
||||
|
||||
# ---- 取回: 空手 ----
|
||||
if not itemName or itemName == "minecraft:air":
|
||||
if not stack:
|
||||
if not stackNames:
|
||||
return
|
||||
topItem = stack.pop()
|
||||
self.giveItemObj(playerId, topItem)
|
||||
poppedName = stackNames.pop() # 后放先取, 弹最后一件
|
||||
if stack:
|
||||
target = KITCHENRACK_SINGLE_BLOCK.get(
|
||||
stack[0]["itemName"], "rtdd:kitchenwarerack")
|
||||
topItem = stack.pop()
|
||||
self.giveItemObj(playerId, topItem)
|
||||
else:
|
||||
target = "rtdd:kitchenwarerack"
|
||||
self.giveItem(playerId, poppedName)
|
||||
# 计算剩余 -> 目标方块, 并保存剩余记录(保证后续取回一致)
|
||||
target = self._rackTarget(stackNames)
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
if stack:
|
||||
self._saveRecordDelayed(pos, dim, {"stack": stack})
|
||||
if stackNames:
|
||||
self._saveRecordDelayed(pos, dim, {"stack": self._namesToObjs(stackNames)})
|
||||
else:
|
||||
self._clearRecord(pos, dim)
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a取回了厨具")
|
||||
@@ -315,25 +387,34 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
# ---- 放入: 手持厨具 ----
|
||||
if itemName not in KITCHENWARE_ITEMS:
|
||||
return
|
||||
if len(stack) >= 2:
|
||||
if len(stackNames) >= 2:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§c厨具架已满!")
|
||||
return
|
||||
# 计算组合目标方块(顺序无关: 先放A再放B == 先放B再放A)
|
||||
newItems = [s["itemName"] for s in stack] + [itemName]
|
||||
if len(newItems) == 1:
|
||||
target = KITCHENRACK_SINGLE_BLOCK.get(newItems[0])
|
||||
else:
|
||||
target = KITCHENRACK_COMBO_BLOCK.get(tuple(sorted(newItems)))
|
||||
newItems = stackNames + [itemName]
|
||||
target = self._rackTarget(newItems)
|
||||
if not target:
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§c这个搭配放不上厨具架!")
|
||||
return
|
||||
# 消耗1个, 压栈并替换
|
||||
self.consumeItem(playerId, itemDict, 1)
|
||||
stack.append(self._toItemObj(itemDict))
|
||||
stackNames.append(itemName)
|
||||
self.replaceBlock(pos, dim, target, playerId)
|
||||
self._saveRecordDelayed(pos, dim, {"stack": stack})
|
||||
self._saveRecordDelayed(pos, dim, {"stack": self._namesToObjs(stackNames)})
|
||||
comp.CreateMsg(playerId).NotifyOneMessage(playerId, "§a放上了厨具")
|
||||
|
||||
def _rackTarget(self, itemNames):
|
||||
"""根据厨具物品名列表计算厨具架目标方块(0件=空, 1件=单件, 2件=组合, 顺序无关)"""
|
||||
if not itemNames:
|
||||
return "rtdd:kitchenwarerack"
|
||||
if len(itemNames) == 1:
|
||||
return KITCHENRACK_SINGLE_BLOCK.get(itemNames[0])
|
||||
return KITCHENRACK_COMBO_BLOCK.get(tuple(sorted(itemNames)))
|
||||
|
||||
def _namesToObjs(self, itemNames):
|
||||
"""把厨具物品名字符串列表转成可持久化的物品对象列表"""
|
||||
return [{"itemName": n, "count": 1, "aux": 0} for n in itemNames]
|
||||
|
||||
# ============================================================================
|
||||
# 五、切换型规则处理(水槽/盘子架/书柜/唱片机/垃圾桶)
|
||||
# ============================================================================
|
||||
@@ -341,16 +422,31 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
def handleSwitch(self, recipe, playerId, pos, dim, itemDict, key):
|
||||
"""
|
||||
执行"右键直接转换"类规则:
|
||||
书柜取回/唱片机取回 → 先读记录
|
||||
消耗物品/清空主手 → 替换方块 → 记录放入 → 给/掉物品 → 提示
|
||||
书柜(记录堆栈): 替换方块会清空方块实体, 所以堆栈必须
|
||||
"替换前从旧方块读出 → 替换后写回新方块", 否则多本书只留最新一本
|
||||
唱片机取回: 先读记录
|
||||
消耗物品/清空主手 → 替换方块 → 给/掉物品 → 提示
|
||||
"""
|
||||
# ---- 取回类: 先读记录(此时方块还是满的/有碟的) ----
|
||||
# ---- 书柜 stack 记录: 替换方块前从旧方块读出堆栈 ----
|
||||
bookStack = None
|
||||
droppedBook = None
|
||||
if recipe.get("record") == "stack":
|
||||
oldRecord = self._loadRecord(pos, dim)
|
||||
bookStack = list(oldRecord.get("stack") or []) if oldRecord else []
|
||||
if recipe.get("item") is None:
|
||||
# 取回: 后放先取, 弹栈, 以掉落物形式给玩家
|
||||
if not bookStack:
|
||||
return
|
||||
droppedBook = bookStack.pop()
|
||||
elif itemDict:
|
||||
# 放入: 压栈(扁平化物品信息, 附魔存ench字段, 取回时重建)
|
||||
bookStack.append(self._toItemObj(itemDict))
|
||||
|
||||
# ---- 唱片机取回: 先读记录(此时方块还是有碟的) ----
|
||||
discToDrop = None
|
||||
if recipe.get("discGet"):
|
||||
discToDrop = self._readDiscData(pos, dim)
|
||||
self._clearDiscData(pos, dim)
|
||||
if recipe.get("record") == "stack" and recipe.get("item") is None:
|
||||
self._popStackAndGive(playerId, pos, dim)
|
||||
|
||||
# ---- 消耗物品 ----
|
||||
if recipe.get("clearHand"):
|
||||
@@ -362,13 +458,20 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
if recipe.get("target"):
|
||||
self.replaceBlock(pos, dim, recipe["target"], playerId)
|
||||
|
||||
# ---- 放入类: 记录到新方块(延迟兜底) ----
|
||||
if recipe.get("record") == "stack" and recipe.get("item") is not None and itemDict:
|
||||
self._pushStack(pos, dim, itemDict)
|
||||
# ---- 替换后把堆栈写回新方块(此时写才不会被清掉) ----
|
||||
if bookStack is not None:
|
||||
if bookStack:
|
||||
self._saveRecordDelayed(pos, dim, {"stack": bookStack})
|
||||
else:
|
||||
self._clearRecord(pos, dim)
|
||||
|
||||
# ---- 唱片机放入 ----
|
||||
if recipe.get("discPut") and itemDict:
|
||||
self._writeDiscData(dim, pos, itemDict)
|
||||
|
||||
# ---- 掉落物(厨具架取回/唱片取回) ----
|
||||
# ---- 掉落物(书柜取书/唱片取回) ----
|
||||
if droppedBook is not None:
|
||||
self._dropItemObj(dim, pos, droppedBook)
|
||||
if discToDrop:
|
||||
self._dropItemDict(dim, pos, discToDrop)
|
||||
if recipe.get("dropItem"):
|
||||
@@ -399,24 +502,43 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
"itemName": blockData["rtdd_swi"],
|
||||
"aux": blockData["rtdd_swa"]}
|
||||
if swt == "stack":
|
||||
return {"stack": blockData["rtdd_sws"]}
|
||||
raw = blockData["rtdd_sws"]
|
||||
stack = []
|
||||
if isinstance(raw, list):
|
||||
# 当前格式: 原始扁平列表
|
||||
stack = raw
|
||||
elif isinstance(raw, basestring):
|
||||
# 兼容历史遗留的JSON字符串数据
|
||||
try:
|
||||
parsed = json.loads(raw)
|
||||
if isinstance(parsed, list):
|
||||
stack = parsed
|
||||
except Exception:
|
||||
stack = []
|
||||
# None/异常/损坏数据一律按空堆栈处理, 保证不报错
|
||||
return {"stack": stack}
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _saveRecord(self, pos, dim, record):
|
||||
"""把记录写入方块实体数据"""
|
||||
"""把记录写入方块实体数据(参考 demo.py: GetBlockEntityData 返回对象 [] 赋值即持久化)。
|
||||
stack 直接存原始扁平列表: 附魔只存[[ID,等级],...]的ench(不含引擎NBT编码的userData),
|
||||
全部 str/int(无 py2 unicode), 才能被引擎 SetBlockEntityData 可靠写入。"""
|
||||
try:
|
||||
blockData = blockEntityDataComp.GetBlockEntityData(dim, pos)
|
||||
if blockData is None:
|
||||
return
|
||||
if "count" in record:
|
||||
blockData["rtdd_swt"] = "count"
|
||||
blockData["rtdd_swc"] = record["count"]
|
||||
blockData["rtdd_swi"] = record.get("itemName", "")
|
||||
blockData["rtdd_swa"] = record.get("aux", 0)
|
||||
blockData["rtdd_swc"] = int(record["count"])
|
||||
blockData["rtdd_swi"] = str(record.get("itemName", ""))
|
||||
blockData["rtdd_swa"] = int(record.get("aux", 0))
|
||||
elif "stack" in record:
|
||||
blockData["rtdd_swt"] = "stack"
|
||||
# 直接存原始扁平列表(见 demo.py 参考实现, GetBlockEntityData 的 []
|
||||
# 赋值即持久化)。不要用 json.dumps: 引擎会把 JSON 字符串反解析回结构,
|
||||
# 反解析出的 py2 unicode 会被引擎拒绝(SetBlockEntityData 报 type unicode)。
|
||||
blockData["rtdd_sws"] = record["stack"]
|
||||
except Exception:
|
||||
pass
|
||||
@@ -445,36 +567,49 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ---- 书柜 stack 记录: 压栈/弹栈 ----
|
||||
# ---- 书柜 stack 记录: 压栈/弹栈 已内联到 handleSwitch(替换前后读写) ----
|
||||
|
||||
def _pushStack(self, pos, dim, itemDict):
|
||||
"""书柜放入: 把书的完整信息压入堆栈(含userData, 附魔不丢)"""
|
||||
record = self._loadRecord(pos, dim)
|
||||
if not record:
|
||||
record = {"stack": []}
|
||||
record["stack"].append(self._toItemObj(itemDict))
|
||||
self._saveRecordDelayed(pos, dim, record)
|
||||
def _extractEnch(self, userData):
|
||||
"""从附魔书 userData 提取附魔为引擎安全格式: [[附魔ID, 等级], ...]。
|
||||
userData 里的 ench 是引擎 NBT 编码({__type__/__value__}), 直接存方块实体
|
||||
会触发 SetBlockEntityData 报错(写不进去), 只取 __value__ 里的纯数字。"""
|
||||
try:
|
||||
entries = (userData or {}).get("ench") or []
|
||||
out = []
|
||||
for e in entries:
|
||||
eid = int(e.get("id", {}).get("__value__", 0))
|
||||
lvl = int(e.get("lvl", {}).get("__value__", 1))
|
||||
out.append([eid, lvl])
|
||||
return out
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _popStackAndGive(self, playerId, pos, dim):
|
||||
"""书柜取回: 弹栈(后放先取)并把书给玩家"""
|
||||
record = self._loadRecord(pos, dim)
|
||||
if not record or not record.get("stack"):
|
||||
return
|
||||
topItem = record["stack"].pop()
|
||||
self.giveItemObj(playerId, topItem)
|
||||
if record["stack"]:
|
||||
self._saveRecordDelayed(pos, dim, record)
|
||||
else:
|
||||
self._clearRecord(pos, dim)
|
||||
def _buildEnchUserData(self, ench):
|
||||
"""把引擎安全格式 [[附魔ID, 等级], ...] 重建为引擎 NBT 编码的 userData(附魔书用)"""
|
||||
entries = []
|
||||
for pair in ench or []:
|
||||
entries.append({
|
||||
"id": {"__type__": 2, "__value__": int(pair[0])},
|
||||
"lvl": {"__type__": 2, "__value__": int(pair[1])},
|
||||
"modEnchant": {"__type__": 8, "__value__": ""},
|
||||
})
|
||||
return {"ench": entries}
|
||||
|
||||
def _toItemObj(self, itemDict):
|
||||
"""从手持物品 dict 提取可持久化的物品对象"""
|
||||
return {
|
||||
"itemName": itemDict.get("newItemName", ""),
|
||||
"count": itemDict.get("count", 1),
|
||||
"aux": itemDict.get("newAuxValue", 0),
|
||||
"userData": itemDict.get("userData", {}),
|
||||
"""从手持物品 dict 提取可持久化的物品对象。
|
||||
只存扁平字段(itemName/count/aux/ench), 全部转成 str/int:
|
||||
① 不存含引擎NBT编码(__type__/__value__)的userData —— 引擎写不进方块实体;
|
||||
② 不存 py2 unicode —— GetPlayerItem 返回的字符串是 unicode, 存方块实体也会被
|
||||
引擎拒绝(type unicode), 必须 str() 转成字节串。"""
|
||||
obj = {
|
||||
"itemName": str(itemDict.get("newItemName") or ""),
|
||||
"count": int(itemDict.get("count", 1)),
|
||||
"aux": int(itemDict.get("newAuxValue", 0)),
|
||||
}
|
||||
ench = self._extractEnch(itemDict.get("userData"))
|
||||
if ench:
|
||||
obj["ench"] = ench
|
||||
return obj
|
||||
|
||||
# ============================================================================
|
||||
# 六、唱片机唱片记录(rtdd_disc)
|
||||
@@ -537,14 +672,30 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _dropItemObj(self, dim, pos, itemObj):
|
||||
"""以掉落物形式生成一个物品(书柜取书, 按扁平ench重建附魔)"""
|
||||
try:
|
||||
itemDict = {
|
||||
"itemName": itemObj["itemName"],
|
||||
"count": itemObj.get("count", 1),
|
||||
"auxValue": itemObj.get("aux", 0),
|
||||
}
|
||||
ench = itemObj.get("ench")
|
||||
if ench:
|
||||
itemDict["userData"] = self._buildEnchUserData(ench)
|
||||
self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2]))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ============================================================================
|
||||
# 七、工具方法:获取玩家主手物品
|
||||
# ============================================================================
|
||||
|
||||
def getCarriedItem(self, playerId):
|
||||
itemComp = comp.CreateItem(playerId)
|
||||
# getUserData=True 必须传, 否则取不到 userData(附魔书词条在 userData 里)
|
||||
return itemComp.GetPlayerItem(
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0)
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0, True)
|
||||
|
||||
# ============================================================================
|
||||
# 八、工具方法:给玩家物品
|
||||
@@ -561,12 +712,15 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
pass
|
||||
|
||||
def giveItemObj(self, playerId, itemObj):
|
||||
"""按持久化物品对象给玩家(书柜弹栈取回, 保留附魔)"""
|
||||
"""按持久化物品对象给玩家(厨具架取回; 附魔书按扁平ench重建附魔)"""
|
||||
try:
|
||||
itemDict = {"itemName": itemObj["itemName"],
|
||||
"count": itemObj.get("count", 1),
|
||||
"auxValue": itemObj.get("aux", 0)}
|
||||
if itemObj.get("userData"):
|
||||
ench = itemObj.get("ench")
|
||||
if ench:
|
||||
itemDict["userData"] = self._buildEnchUserData(ench)
|
||||
elif itemObj.get("userData"):
|
||||
itemDict["userData"] = itemObj["userData"]
|
||||
comp.CreateItem(playerId).SpawnItemToPlayerInv(itemDict, playerId)
|
||||
except Exception:
|
||||
@@ -590,7 +744,8 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": itemDict["newItemName"],
|
||||
"count": itemDict["count"],
|
||||
"auxValue": itemDict.get("newAuxValue", 0)})
|
||||
"auxValue": itemDict.get("newAuxValue", 0),
|
||||
"userData": itemDict.get("userData", {})})
|
||||
|
||||
def _consumeCount(self, playerId, itemDict, amount):
|
||||
"""批量扣减指定数量(箭桶存箭)"""
|
||||
@@ -607,7 +762,8 @@ class FurnitureService(BlueFoxServerSystem):
|
||||
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
|
||||
{"itemName": itemDict["newItemName"],
|
||||
"count": itemDict["count"],
|
||||
"auxValue": itemDict.get("newAuxValue", 0)})
|
||||
"auxValue": itemDict.get("newAuxValue", 0),
|
||||
"userData": itemDict.get("userData", {})})
|
||||
|
||||
def clearHand(self, playerId):
|
||||
"""清空主手全部物品(垃圾桶扔垃圾)"""
|
||||
|
||||
+17
-7
@@ -287,21 +287,31 @@ class FeatureDispatcher(BlueFoxServerSystem):
|
||||
def OnDestroy(self, args):
|
||||
"""
|
||||
玩家拆方块时触发。
|
||||
部分"放置的半成品/死鱼"方块被拆掉时, 取消默认掉落(不把方块本身掉出来),
|
||||
手动清空方块并掉落对应的原版生食材:
|
||||
- killfish_0(死鲑鱼) → 原版生鲑鱼
|
||||
- killcod_0(死鳕鱼) → 原版生鳕鱼
|
||||
- roastchickenplace_0(生鸡肉放置后) → 原版生鸡肉
|
||||
注意: ServerPlayerTryDestroyBlockEvent 的方块名字段是 fullName(不是 blockName)!
|
||||
优先级:
|
||||
1. 有"破坏掉落"逻辑的服务(如家具的存物方块: 书柜/药水架/厨具架
|
||||
被拆掉 → 掉 空方块本体+全部存储物品, 与售卖盒不同, 破坏后取不到)
|
||||
2. 静态映射: 放置的死鱼/生鸡肉掉回原版生食材
|
||||
"""
|
||||
blockName = args.get("blockName")
|
||||
blockName = args.get("fullName") or args.get("blockName")
|
||||
args["blockName"] = blockName # 统一成 blockName 字段, 服务内部直接用
|
||||
|
||||
# 1. 优先: 归属的右键服务实现了 handleDestroy → 交给服务处理
|
||||
service = self.blockOwner.get(blockName)
|
||||
if service and hasattr(service, "handleDestroy"):
|
||||
if service.handleDestroy(args):
|
||||
return
|
||||
|
||||
# 2. 静态映射: 放置的死鱼/生鸡肉 → 原版生食材
|
||||
itemName = self.DESTROY_DROP.get(blockName)
|
||||
if not itemName:
|
||||
return
|
||||
playerId = args.get("playerId")
|
||||
x, y, z = args.get("x"), args.get("y"), args.get("z")
|
||||
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
|
||||
# 取消默认破坏(不让方块本身作为掉落物掉出来)
|
||||
# 取消默认破坏+默认掉落(不让方块本身作为掉落物掉出来)
|
||||
args['cancel'] = True
|
||||
args['spawnResources'] = False
|
||||
# 手动清空方块
|
||||
try:
|
||||
comp.CreateBlockInfo(LevelId).SetBlockNew(
|
||||
|
||||
@@ -10,26 +10,26 @@
|
||||
"netease:aabb": {
|
||||
"collision": {
|
||||
"min": [
|
||||
-0.0625,
|
||||
0.0,
|
||||
-0.4533
|
||||
0,
|
||||
0,
|
||||
0.1
|
||||
],
|
||||
"max": [
|
||||
0.875,
|
||||
1.125,
|
||||
1.3125
|
||||
1,
|
||||
1.1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"clip": {
|
||||
"min": [
|
||||
-0.0625,
|
||||
0.0,
|
||||
-0.4533
|
||||
0,
|
||||
0,
|
||||
0.1
|
||||
],
|
||||
"max": [
|
||||
0.875,
|
||||
1.125,
|
||||
1.3125
|
||||
1,
|
||||
1.1,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": {
|
||||
"identifier": "rtdd:lotion_0",
|
||||
"register_to_creative_menu": true,
|
||||
"category": "xy_medicine"
|
||||
"category": "xy_furniture"
|
||||
},
|
||||
"components": {
|
||||
"netease:aabb": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": {
|
||||
"identifier": "rtdd:lotion_1",
|
||||
"register_to_creative_menu": true,
|
||||
"category": "xy_medicine"
|
||||
"category": "xy_furniture"
|
||||
},
|
||||
"components": {
|
||||
"netease:aabb": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": {
|
||||
"identifier": "rtdd:lotion_2",
|
||||
"register_to_creative_menu": true,
|
||||
"category": "xy_medicine"
|
||||
"category": "xy_furniture"
|
||||
},
|
||||
"components": {
|
||||
"netease:aabb": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": {
|
||||
"identifier": "rtdd:lotion_3",
|
||||
"register_to_creative_menu": true,
|
||||
"category": "xy_medicine"
|
||||
"category": "xy_furniture"
|
||||
},
|
||||
"components": {
|
||||
"netease:aabb": {
|
||||
|
||||
+21
-3
@@ -209,9 +209,21 @@ FurnitureData+FurnitureService 实现, 注册到 FeatureDispatcher)
|
||||
- 取盘子: 空手右键逐个取回 [已实现]
|
||||
|
||||
四、书柜 (bookcase 系列, 后放先取, 至多13本)
|
||||
- 放书: 手持书/附魔书右键逐个放入, 消耗1本, stack记录完整物品信息(附魔不丢,
|
||||
字段 rtdd_swt=stack, 退出重进不丢) [已实现]
|
||||
- 取书: 空手右键后放先取(LIFO) [已实现]
|
||||
- 放书: 手持书/附魔书右键逐个放入, 消耗1本; 堆栈"替换方块前从旧方块读出→替换后写回新方块",
|
||||
多本连续放入不会丢 [已实现]
|
||||
- 取书: 空手右键后放先取(LIFO), 取回的书以掉落物形式掉在书柜前(不进背包) [已实现]
|
||||
- 附魔书: 附魔以扁平ench字段([[附魔ID,等级],...])存方块实体(rtdd_swt=stack/rtdd_sws),
|
||||
取回时重建userData; 不存含__type__/__value__的userData(引擎写不进方块实体),
|
||||
退出重进词条不丢(掉落保护仍是掉落保护) [已实现]
|
||||
- 健壮性: 方块实体里损坏/为None的旧堆栈数据按空堆栈处理, 不再报错(TypeError) [已实现]
|
||||
|
||||
四b、药水架 (lotion 系列, 后放先取, 至多3瓶)
|
||||
- 从药物系统(xy_medicine)移至家具分类(xy_furniture) [已实现]
|
||||
- 放药水: 手持任意药水(普通/喷溅/滞留)右键逐个放入, 消耗1瓶, 记录药水名+aux(aux决定效果) [已实现]
|
||||
- 取药水: 空手右键后放先取(LIFO), 取回的药水以掉落物形式掉在架前(原样返还, 含aux效果) [已实现]
|
||||
- 满架: 手持药水右键三瓶满的药水架 → 提示"已满", 拦截默认行为(防止误喝药水) [已实现]
|
||||
- 方块链: 药水架_1(空)=lotion_0, 药水架_2(一瓶)=lotion_1, 药水架_3(两瓶)=lotion_2,
|
||||
药水架_4(三瓶)=lotion_3 [已实现]
|
||||
|
||||
五、厨具架 (kitchenwarerack 系列, 组合顺序无关, 后放先取, 至多2件)
|
||||
- 放入: 手持 锅铲/锅勺/菜刀/炒锅 右键空架/未满架 → 消耗1个 → 按组合成型 [已实现]
|
||||
@@ -230,3 +242,9 @@ FurnitureData+FurnitureService 实现, 注册到 FeatureDispatcher)
|
||||
- 扔垃圾: 打开时手持任意物品右键 → 变为 有垃圾 状态 + 清空主手物品 [已实现]
|
||||
- 关闭: 右键(空手/手持均可) 打开/有垃圾 → 关闭桶盖 [已实现]
|
||||
|
||||
八、左键破坏存物方块掉落 (书柜/药水架/厨具架)
|
||||
- 存了东西的家具方块被左键打碎时: 取消默认掉落 → 掉 空方块本体(保留朝向) + 全部存储物品 [已实现]
|
||||
(书柜掉空书柜bookcase+里面的书; 药水架掉空药水架lotion_0+里面的药水; 厨具架掉空架+厨具)
|
||||
- 附魔书/药水效果在掉落物中保留(取回时重建, 词条/效果不丢) [已实现]
|
||||
- 与售卖盒区分: 售卖盒破坏重放仍可右键取物; 书柜这类破坏后记录随方块消失, 重放取不到 [已实现]
|
||||
|
||||
+22
-6
@@ -2,17 +2,20 @@
|
||||
【家具家私】(xy_furniture) 转化关系
|
||||
========================================
|
||||
说明:
|
||||
- 家具家私分类方块: 箭桶、水槽、盘子架、书柜、厨具架、唱片机、垃圾桶 系列
|
||||
- 家具家私分类方块: 箭桶、水槽、盘子架、书柜、药水架、厨具架、唱片机、垃圾桶 系列
|
||||
- 有交互转换的: 箭桶(存/取箭)、水槽(加水/取水/放洗脏盘/取脏盘)、
|
||||
盘子架(放/取盘)、书柜(放/取书)、厨具架(放/取厨具)
|
||||
盘子架(放/取盘)、书柜(放/取书)、药水架(放/取药水)、厨具架(放/取厨具)
|
||||
- 【方块存储物品实现】(FurnitureService 两种记录, 写入方块实体数据, 退出重进不丢):
|
||||
① count记录(箭桶): 记录一种物品+数量, 字段 rtdd_swt=count / rtdd_swc=数量 /
|
||||
rtdd_swi=物品名 / rtdd_swa=aux值
|
||||
② stack记录(书柜): 后放先取堆栈, 记录完整物品信息(含userData, 附魔不丢),
|
||||
字段 rtdd_swt=stack / rtdd_sws=堆栈列表
|
||||
- 【实现状态】: 箭桶/水槽/盘子架/书柜/厨具架/古式唱片机/垃圾桶 全部已实现
|
||||
② stack记录(书柜/药水架): 后放先取堆栈, 记录物品名+aux(药水)或附魔ench(书),
|
||||
字段 rtdd_swt=stack / rtdd_sws=堆栈(原始扁平列表, 不存引擎NBT编码的userData)
|
||||
- 【实现状态】: 箭桶/水槽/盘子架/书柜/药水架/厨具架/古式唱片机/垃圾桶 全部已实现
|
||||
(统一由 xiangyemod/jiaju/FurnitureService 处理, 注册到 FeatureDispatcher;
|
||||
旧 BlockSwitchService 已废弃); 唱片播放曲目 待后续实现
|
||||
- 【左键破坏掉落】: 书柜/药水架/厨具架这类"存了东西"的方块被打碎时, 掉 空方块本体
|
||||
+ 全部存储物品(附魔/药水效果保留); 与售卖盒不同: 售卖盒破坏重放仍可右键取物,
|
||||
这类方块破坏后记录随方块消失, 重放取不到
|
||||
- 其余为静态装饰家具, 无转换
|
||||
|
||||
========================================
|
||||
@@ -147,7 +150,19 @@
|
||||
-> 书柜_9(8本) -> 书柜_8(7本) -> 书柜_7(6本) -> 书柜_6(5本) -> 书柜_5(4本)
|
||||
-> 书柜_4(3本) -> 书柜_3(2本) -> 书柜_2(1本) -> 书柜_1(空)
|
||||
转换条件:[右键 进行转换, 后放先取, 取回书]
|
||||
机制: 空手右键逐本取回, 后放先取(LIFO), 取回的书保留原物品信息(附魔等)
|
||||
机制: 空手右键逐本取回, 后放先取(LIFO), 取回的书以掉落物形式掉在书柜前(不进背包),
|
||||
保留附魔(附魔以扁平ench字段存方块实体, 取回时重建userData, 退出重进词条不丢)
|
||||
|
||||
========================================
|
||||
四b、药水架(放药水/取药水, 后放先取, 至多3瓶)【已实现】
|
||||
========================================
|
||||
- 从药物系统(xy_medicine)移至家具分类(xy_furniture)
|
||||
- 可放药水: 任意药水(普通/喷溅/滞留 minecraft:potion / splash_potion / lingering_potion)
|
||||
- 放入: 手持任意药水右键 药水架(空/未满) → 消耗1瓶, 记录药水名+aux(aux决定药水效果)
|
||||
- 取回: 空手右键 → 后放先取(LIFO), 取回最后放入的那瓶药水(掉落物形式, 原样返还)
|
||||
- 满架: 手持药水右键三瓶满的药水架 → 提示"已满", 拦截默认行为(防止误喝药水)
|
||||
- 方块链: 药水架_1(空)=lotion_0, 药水架_2(一瓶)=lotion_1,
|
||||
药水架_3(两瓶)=lotion_2, 药水架_4(三瓶)=lotion_3
|
||||
|
||||
========================================
|
||||
五、厨具架(放厨具/取厨具, 至多2件)【已实现】
|
||||
@@ -156,6 +171,7 @@
|
||||
- 放入: 手持厨具右键 厨具架(空/未满) → 放入该厨具, 消耗1个, 厨具架形态变为对应组合
|
||||
(组合与放入顺序无关: 先放锅再放刀 == 先放刀再放锅, 都是 炒锅+菜刀)
|
||||
- 取回: 空手右键 → 后放先取(LIFO), 取回最后放入的厨具, 形态退回剩余单件/空
|
||||
(创造模式等直接放置的"带厨具"厨具架, 无记录时按方块状态推断厨具, 同样可右键取回)
|
||||
- 厨具架方块状态(方块名称即组合, 一块最多挂2件):
|
||||
kitchenwarerack = 厨具架(空)
|
||||
kitchenwareracka_1 = 厨具架(单锅铲) ← 放1把锅铲
|
||||
|
||||
+4
-1
@@ -7,8 +7,11 @@
|
||||
- 【待确认】表示该步骤所需的具体操作或配方尚未最终确定
|
||||
|
||||
========================================
|
||||
一、药水架(存/取药水,后放先取,至多3瓶)
|
||||
一、药水架(存/取药水,后放先取,至多3瓶)【已移至家具分类,见 家具家私转化关系.txt 四b】
|
||||
========================================
|
||||
说明: 药水架(lotion_0~3)已从药物系统移至家具分类(xy_furniture), 由 FurnitureService 实现:
|
||||
手持任意药水右键放入(消耗1瓶,记录药水名+aux), 空手右键后放先取取回(掉落物)。
|
||||
具体规则见 家具家私转化关系.txt 四b、药水架。
|
||||
1. 药水架_1(空) -> 药水架_2(一瓶药水)
|
||||
转换条件:[手持 任意药水 右键 进行转换,药水数量减少,方块记录药水类型]
|
||||
机制: 手持药水右键,消耗1瓶,放入药水并记录类型
|
||||
|
||||
Reference in new issue
Block a user