提供物品放置方块功能

This commit is contained in:
Giao committed 2026-10-06 16:18:20 +08:00
1 parent e65cebaec1
commit 43c1859aad
8 files changed
+190 -6

No files matched your search

@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
@@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
"""
================================================================================
【物品放置方块】(placeblock) 服务 —— 创造栏显示 2D 物品图标, 放置出指定方块模型
================================================================================
背景: 网易 1.10 自定义物品不支持 minecraft:block_placer(实测"放不下去");
原版 1.21.60 物品虽能 block_placer, 但其 menu_category 只能进原版分页,
进不了网易自定义分页(如 xy_finished)。两者无法兼得。
方案A(脚本补位): 方块本体 register_to_creative_menu:false 不出现在创造栏;
另建带 2D 图标、category 指向自定义分页的网易 1.10 物品(分页正确);
手持该物品右键任意方块 -> 在被点面的相邻格放置对应方块, 消耗 1 个
(创造模式不消耗)。
路由: 走 FeatureDispatcher 的 itemUseOn(itemName -> 服务), 与其它转换服务同机制。
================================================================================
"""
import time
import mod.server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls() # 服务端系统基类
comp = serverApi.GetEngineCompFactory() # 组件工厂
from ....BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxServerSystem
from ....BlueFoxTools.ToolsConfig import LevelId
from ...service.FeatureDispatcher import FeatureDispatcher, FACE_OFFSET
# ---- 物品 -> 放置出的方块(要新增的成品菜在这里登记) ----
PLACE_ITEMS = {
"rtdd:plate_48": "rtdd:plate_48_block",
}
# ---- 视为"可放置"的目标方块(空气/水) ----
AIR_LIKE = ("minecraft:air", "minecraft:water", "minecraft:flowing_water")
# ---- 同一玩家两次放置的最小间隔(秒, 防连点重复放置) ----
PLACE_COOLDOWN = 0.2
# ---- 朝向微调: 0=方块正面朝玩家视线方向; 2=方块正面朝玩家(视线反方向, 与正常摆放一致) ----
FACING_FLIP = 0
class PlaceBlockService(BlueFoxServerSystem):
"""物品放置方块服务(创造栏 2D 图标 -> 放置 3D 方块)"""
def __init__(self, namespace, systemName):
BlueFoxServerSystem.__init__(self, namespace, systemName)
self.itemBlocks = dict(PLACE_ITEMS)
self.cooldown = {}
FeatureDispatcher.register(self)
# ------------------------------------------------------------------
# 分发器路由表(FeatureDispatcher 调用)
# ------------------------------------------------------------------
def getFeatureBlocks(self):
"""本服务不接管任何"右键方块", 只处理 itemUseOn"""
return []
def getItemUseOnRules(self):
"""手持放置物品右键方块表面 -> 由本服务处理"""
return {item: True for item in self.itemBlocks}
def ownsItemUseOn(self, itemName, clickedName):
"""声明本服务明确处理这些物品的 itemUseOn(不受被点方块归属影响)"""
return itemName in self.itemBlocks
# ------------------------------------------------------------------
# 入口: 手持放置物品右键方块 -> 在相邻格放置方块 + 消耗1
# ------------------------------------------------------------------
def handleItemUseOn(self, args):
itemName = args.get("itemName")
blockName = self.itemBlocks.get(itemName)
if not blockName:
return False
playerId = args.get("playerId") or args.get("entityId")
if not playerId:
return False
# 冷却: 防连点重复放置
now = time.time()
if now - self.cooldown.get(playerId, 0) < PLACE_COOLDOWN:
return True
self.cooldown[playerId] = now
x, y, z = args.get("x"), args.get("y"), args.get("z")
face = args.get("face", 1)
if x is None or y is None or z is None:
return False
dim = args.get("dimensionId")
if dim is None:
try:
dim = comp.CreateDimension(playerId).GetEntityDimensionId()
except Exception:
return False
# 落点 = 被点方块相邻的那一格(0下 1上 2北 3南 4西 5东)
off = FACE_OFFSET.get(face, (0, 1, 0))
target = (x + off[0], y + off[1], z + off[2])
# 目标位置必须是空气/水, 否则不放置(交还原版)
try:
b = comp.CreateBlockInfo(LevelId).GetBlockNew(target, dim)
name = b.get("name") if b else None
if name not in AIR_LIKE:
return False
except Exception:
return False
# 放置方块(朝向按玩家水平朝向, 与正常摆放一致)
try:
aux = self._facingAux(playerId)
ok = comp.CreateBlockInfo(LevelId).SetBlockNew(
target, blockDict={"name": blockName, "aux": aux},
oldBlockHandling=0, dimensionId=dim)
except Exception:
ok = False
if not ok:
return False
# 消耗 1 个(创造模式不消耗)
if not self._isCreative(playerId):
self._consumeOne(playerId, itemName)
return True
# ------------------------------------------------------------------
# 工具
# ------------------------------------------------------------------
def _facingAux(self, playerId):
"""
netease:face_directional(type=direction) 的朝向值:
0=南(+Z) 1=西(-X) 2=北(-Z) 3=东(+X)
按玩家水平朝向(视线方向)取值; 若方向相反/偏移, 调 FACING_FLIP。
"""
try:
rot = comp.CreateRot(playerId).GetRot()
dx, _dy, dz = serverApi.GetDirFromRot(rot)
if abs(dx) >= abs(dz):
face = 3 if dx > 0 else 1 # +X 东 / -X 西
else:
face = 0 if dz > 0 else 2 # +Z 南 / -Z 北
except Exception:
face = 0
# 与正常摆放一致的微调: 方块"正面"朝向玩家(视线反方向)时改为 face+2
return (face + FACING_FLIP) % 4
def _isCreative(self, playerId):
try:
return comp.CreateGame(LevelId).GetPlayerGameType(playerId) == 1
except Exception:
return False
def _consumeOne(self, playerId, itemName):
try:
itemComp = comp.CreateItem(playerId)
carried = itemComp.GetPlayerItem(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED, 0, True)
if not carried or carried.get("newItemName") != itemName:
return
count = carried.get("count", 1)
itemComp.SetEntityItem(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
{"itemName": itemName,
"count": max(count - 1, 0),
"auxValue": carried.get("newAuxValue", 0),
"userData": carried.get("userData", {})})
except Exception:
pass
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-