吃 3 功能

This commit is contained in:
wenhe committed 2026-09-21 16:13:53 +08:00
1 parent 4176eb293c
commit fb817b5d34
12 files changed
+1021 -118

No files matched your search

@@ -0,0 +1,218 @@
# -*- coding: utf-8 -*-
"""
================================================================================
【总食用】(shiyongfood) 服务 —— 所有成品的"吃"
================================================================================
【入口(由 FeatureDispatcher 统一转发)】
1. handleBlockUse 空手右键 已放置的成品方块 -> 吃一级
- 有 target: 方块转换成下一级(半份/下一份)
- 无 target: 方块完全消耗(消失) + 掉落容器(脏盘子/木碗/玻璃杯/支杆)
2. handleItemUseOn 手持 饭团/水煮蛋/豆腐 右键方块表面 -> 直接吃(消耗1个)
3. handleItemTryUse 手持 饭团/水煮蛋/豆腐 长按右键(空中使用) -> 直接吃(消耗1个)
【数值】
- 饱食度: SetPlayerHunger(0~20), 1点 = 半个鸡腿(原版饥饿值)
- 效果: AddEffectToEntity(effectName, 时长秒, amplifier)
amplifier = 等级-1 (效果I=0, 效果II=1, ...)
- 冷却: 0.4 秒(防长按右键连吃刷屏)
================================================================================
"""
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 ..ShiyongfoodData import EAT_RULE_MAP, TRY_USE_RULES, EFFECT_CN
class ShiyongfoodService(BlueFoxServerSystem):
"""总食用服务: 所有成品右键吃(方块吃 + 手持物品直接吃)"""
def __init__(self, namespace, systemName):
BlueFoxServerSystem.__init__(self, namespace, systemName)
self.eatBlocks = set(EAT_RULE_MAP.keys()) # 可吃的成品方块
self.tryUseItems = set(TRY_USE_RULES.keys()) # 手持直接吃的物品
self.cooldown = {} # 右键冷却
# ---- 注册到统一分发器: 右键方块 + 手持右键表面 + 手持直接吃 ----
from ...service.FeatureDispatcher import FeatureDispatcher
FeatureDispatcher.register(self)
FeatureDispatcher.registerTryUse(self)
# ------------------------------------------------------------------
# 分发器路由表(FeatureDispatcher 调用)
# ------------------------------------------------------------------
def getFeatureBlocks(self):
"""成品方块(空手右键吃)"""
return self.eatBlocks
def getItemUseOnRules(self):
"""手持吃物品也注册进"右键方块表面"路由(点方块表面直接吃)"""
return {item: True for item in self.tryUseItems}
def getTryUseRules(self):
"""手持直接吃(长按右键/空中使用)"""
return {item: True for item in self.tryUseItems}
# ====================================================================
# 入口1: 空手右键 已放置的成品方块 -> 吃一级
# ====================================================================
def handleBlockUse(self, args):
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
rule = EAT_RULE_MAP.get(blockName)
if not rule:
return False
if not self.checkCooldown(key, 0.4):
return True
self._applyFood(playerId, rule.get("hunger", 0), rule.get("effects", []))
target = rule.get("target")
if target:
# 方块转换成下一级(半份/下一份)
self._replaceBlock(playerId, pos, dim, target)
else:
# 完全消耗消失 + 掉落容器
self._clearBlock(playerId, pos, dim)
drop = rule.get("drop")
if drop:
self.dropItem(dim, pos, drop)
self._notify(playerId, rule)
return True
# ====================================================================
# 入口2: 手持 饭团/水煮蛋/豆腐 右键方块表面 -> 直接吃
# ====================================================================
def handleItemUseOn(self, args):
itemName = args.get("itemName")
rule = TRY_USE_RULES.get(itemName)
if not rule:
return False
playerId = args.get("playerId") or args.get("entityId")
return self._eatItemInHand(playerId, itemName, rule)
# ====================================================================
# 入口3: 手持 饭团/水煮蛋/豆腐 长按右键(空中使用) -> 直接吃
# ====================================================================
def handleItemTryUse(self, args):
itemName = args.get("itemName")
rule = TRY_USE_RULES.get(itemName)
if not rule:
return False
playerId = args.get("playerId") or args.get("entityId")
return self._eatItemInHand(playerId, itemName, rule)
# ------------------------------------------------------------------
# 手持吃公共: 消耗1个 + 回复饱食度 + 效果(无容器掉落)
# ------------------------------------------------------------------
def _eatItemInHand(self, playerId, itemName, rule):
if not playerId:
return False
if not self.checkCooldown(("p", playerId), 0.4):
return True
itemDict = self.getCarriedItem(playerId)
if not itemDict or itemDict.get("newItemName") != itemName:
return False
count = itemDict.get("count", 1)
comp.CreateItem(playerId).SetEntityItem(
serverApi.GetMinecraftEnum().ItemPosType.CARRIED,
{"itemName": itemName,
"count": max(count - 1, 0),
"auxValue": itemDict.get("newAuxValue", 0),
"userData": itemDict.get("userData", {})})
self._applyFood(playerId, rule.get("hunger", 0), rule.get("effects", []))
self._notify(playerId, rule)
return True
# ====================================================================
# 工具: 回复饱食度 + 增益效果
# ====================================================================
def _applyFood(self, playerId, hunger, effects):
try:
# 饱食度【在现有值上累加】(不是覆盖): 当前值 + 回复值, 上限20点
attrComp = comp.CreateAttr(playerId)
cur = attrComp.GetPlayerHunger()
if cur is None:
cur = 0
newVal = max(0.0, min(float(cur) + float(hunger), 20.0))
attrComp.SetPlayerHunger(newVal)
except Exception:
pass
if effects:
try:
effectComp = comp.CreateEffect(LevelId)
for name, dur, lvl in effects:
# 效果I -> amplifier 0, 效果II -> amplifier 1 ...
effectComp.AddEffectToEntity(playerId, name, dur, lvl - 1, False)
except Exception:
pass
# ====================================================================
# 工具: 替换/清除方块
# ====================================================================
def _replaceBlock(self, creator, pos, dim, blockName):
comp.CreateBlockInfo(creator).SetBlockNew(
pos, blockDict={"name": blockName, "aux": 0},
oldBlockHandling=0, dimensionId=dim)
def _clearBlock(self, creator, pos, dim):
comp.CreateBlockInfo(creator).SetBlockNew(
pos, blockDict={"name": "minecraft:air", "aux": 0},
oldBlockHandling=0, dimensionId=dim)
# ====================================================================
# 工具: 以掉落物形式生成物品(出现在方块上方)
# ====================================================================
def dropItem(self, dim, pos, itemName, count=1, aux=0):
try:
itemDict = {"itemName": itemName, "count": count, "auxValue": aux}
self.CreateEngineItemEntity(itemDict, dim, (pos[0], pos[1] + 0.5, pos[2]))
except Exception:
pass
# ====================================================================
# 工具: 食用提示
# ====================================================================
def _notify(self, playerId, rule):
try:
hunger = rule.get("hunger", 0)
msg = "\u00a7a回复 %d 点饱食度" % hunger
effects = rule.get("effects", [])
if effects:
parts = []
for name, dur, lvl in effects:
cn = EFFECT_CN.get(name, name)
parts.append("%s%s(%d秒)" % (cn, self._roman(lvl), dur))
msg += ", 获得 " + " ".join(parts)
comp.CreateMsg(playerId).NotifyOneMessage(playerId, msg)
except Exception:
pass
@staticmethod
def _roman(lvl):
return {1: "I", 2: "II", 3: "III", 4: "IV", 5: "V"}.get(lvl, "")
# ====================================================================
# 工具: 冷却检查(0.4秒, 防长按右键连吃)
# ====================================================================
def checkCooldown(self, key, cd=0.4):
now = time.time()
last = self.cooldown.get(key, 0)
if now - last < cd:
return False
self.cooldown[key] = now
return True
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-