提供物品放置方块功能

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

@@ -27,6 +27,7 @@ from xiangyemod.tangguo.service.TangguoTimerService import TangguoTimerService
from xiangyemod.chaoguo.service.ChaoguoService import ChaoguoService from xiangyemod.chaoguo.service.ChaoguoService import ChaoguoService
from xiangyemod.chaoguo.service.ChaoguoTimerService import ChaoguoTimerService from xiangyemod.chaoguo.service.ChaoguoTimerService import ChaoguoTimerService
from xiangyemod.shiyongfood.service.ShiyongfoodService import ShiyongfoodService from xiangyemod.shiyongfood.service.ShiyongfoodService import ShiyongfoodService
from xiangyemod.placeblock.service.PlaceBlockService import PlaceBlockService
if not serverApi.IsInServer(): if not serverApi.IsInServer():
# 客户端进程: 导入客户端拦截系统(继承 BlueFoxClientSystem, 框架自动注册), # 客户端进程: 导入客户端拦截系统(继承 BlueFoxClientSystem, 框架自动注册),
@@ -619,7 +619,7 @@ SWITCH_RULES = [
# 盖饭转换清单(正向+反向共用): dish=成品菜, target=盖饭 # 盖饭转换清单(正向+反向共用): dish=成品菜, target=盖饭
GAI_FAN_ITEMS = [ GAI_FAN_ITEMS = [
{"dish": PLATE_47, "target": PLATE_48, {"dish": PLATE_47, "target": PLATE_48, "giveItem": PLATE_48,
"name": "辣子鸡丁", "gaiName": "辣子鸡丁盖饭(全份)"}, "name": "辣子鸡丁", "gaiName": "辣子鸡丁盖饭(全份)"},
{"dish": PLATE_54, "target": PLATE_55, {"dish": PLATE_54, "target": PLATE_55,
"name": "香菇滑鸡", "gaiName": "香菇滑鸡盖饭(全份)"}, "name": "香菇滑鸡", "gaiName": "香菇滑鸡盖饭(全份)"},
@@ -635,16 +635,17 @@ for _g in GAI_FAN_ITEMS:
_target = _g["target"] _target = _g["target"]
_name = _g["name"] _name = _g["name"]
_gai = _g["gaiName"] _gai = _g["gaiName"]
_give = _g.get("giveItem") # 给定则"被点方块消失 + 给玩家物品", 否则原位替换成 target 方块
# 正向: 手持成品菜 右键 米饭(三形态任一) -> 米饭变盖饭 # 正向: 手持成品菜 右键 米饭(三形态任一) -> 米饭变盖饭
for _rice in RICE_FORMS: for _rice in RICE_FORMS:
ITEM_USE_ON_RULES.append({ ITEM_USE_ON_RULES.append({
"item": _dish, "surface": _rice, "target": _target, "consume": 1, "item": _dish, "surface": _rice, "target": _target, "consume": 1,
"tip": None}) "giveItem": _give, "tip": None})
# 反向: 手持 米饭(三形态任一) 右键 成品菜 -> 菜品变盖饭 # 反向: 手持 米饭(三形态任一) 右键 成品菜 -> 菜品变盖饭
for _rice in RICE_FORMS: for _rice in RICE_FORMS:
ITEM_USE_ON_RULES.append({ ITEM_USE_ON_RULES.append({
"item": _rice, "surface": _dish, "target": _target, "consume": 1, "item": _rice, "surface": _dish, "target": _target, "consume": 1,
"tip": None}) "giveItem": _give, "tip": None})
# 蛋炒饭 加米饭(形态1/2/3都行) -> 炒锅_87(蛋炒饭_加米饭_熟) # 蛋炒饭 加米饭(形态1/2/3都行) -> 炒锅_87(蛋炒饭_加米饭_熟)
for _rice in RICE_FORMS: for _rice in RICE_FORMS:
ITEM_USE_ON_RULES.append({ ITEM_USE_ON_RULES.append({
@@ -198,7 +198,13 @@ class ChaoguoService(BlueFoxServerSystem):
self.consumeItem(playerId, itemDict, consume) self.consumeItem(playerId, itemDict, consume)
# 替换被右键的表面方块(米饭变盖饭 / 菜品变盖饭 / 蛋炒饭变熟) # 替换被右键的表面方块(米饭变盖饭 / 菜品变盖饭 / 蛋炒饭变熟)
self.replaceBlock(pos, dim, recipe["target"], playerId) # giveItem: 新成品菜体系 —— 被点方块消失, 改为给玩家"成品菜物品"(可再放置出对应方块)
giveItem = recipe.get("giveItem")
if giveItem:
self.replaceBlock(pos, dim, "minecraft:air", playerId)
self.giveItemToPlayer(playerId, giveItem, 1, pos, dim)
else:
self.replaceBlock(pos, dim, recipe["target"], playerId)
self._clearWokData(dim, pos) self._clearWokData(dim, pos)
return True return True
@@ -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 -*-
@@ -451,7 +451,16 @@ EAT_ITEMS = {
"rtdd:beancurd", # 豆腐 "rtdd:beancurd", # 豆腐
} }
ITEM_USE_ON_CANCEL_BY_ITEM = PLACE_ITEMS | EAT_ITEMS # ---- 方案A: 物品放置方块 (placeblock) ----
# 手持该物品右键任意方块表面 -> 客户端 ret 掉默认行为并显式 ItemUseOnRequest,
# 服务端 PlaceBlockService.handleItemUseOn 负责在被点面相邻格放置对应方块。
# (原版 1.21 物品的 block_placer 可用但只能进原版分页; 网易 1.10 物品能进自定义
# 分页但不支持 block_placer, 故用脚本放置, 两者兼得。)
PLACE_BLOCK_ITEMS = {
"rtdd:plate_48",
}
ITEM_USE_ON_CANCEL_BY_ITEM = PLACE_ITEMS | EAT_ITEMS | PLACE_BLOCK_ITEMS
# ============================================================================ # ============================================================================
@@ -206,7 +206,7 @@ EAT_RULES = [
{"block": "rtdd:cookedpancake", "hunger": 9, {"block": "rtdd:cookedpancake", "hunger": 9,
"effects": [("saturation", 30, 1), ("resistance", 30, 1)]}, "effects": [("saturation", 30, 1), ("resistance", 30, 1)]},
# 盖饭 # 盖饭
{"block": "rtdd:plate_48", "target": "rtdd:plate_48a", "hunger": 9, {"block": "rtdd:plate_48_block", "target": "rtdd:plate_48a", "hunger": 9,
"effects": [("regeneration", 20, 2), ("health_boost", 60, 4)]}, "effects": [("regeneration", 20, 2), ("health_boost", 60, 4)]},
{"block": "rtdd:plate_48a", "hunger": 8, "drop": PLATE_DIRTY}, {"block": "rtdd:plate_48a", "hunger": 8, "drop": PLATE_DIRTY},
{"block": "rtdd:plate_55", "target": "rtdd:plate_55a", "hunger": 9, {"block": "rtdd:plate_55", "target": "rtdd:plate_55a", "hunger": 9,