死亡 生成 黄豆获取1
This commit is contained in:
1 parent
faa3a22631
commit
104603fd64
6 files changed
+654
-2
No files matched your search
+191
@@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
================================================================================
|
||||
死亡生物·生成引擎(服务端逻辑类)
|
||||
================================================================================
|
||||
功能:
|
||||
玩家杀死对应生物(牛/猪/羊/鱿鱼/村民)后, 在该生物死亡位置附近生成对应
|
||||
死亡方块(尸体), 供其后的"挂肉钩挂放/切肉"体系使用。
|
||||
|
||||
【这个类为什么是"普通类"、不继承 BlueFoxServerSystem、也不监听事件】
|
||||
全模组只有 FeatureDispatcher 监听游戏事件(架构约定), 本类不监听任何东西。
|
||||
由于此前本类继承 BlueFoxServerSystem 想靠框架自动注册监听, 但新类未被打进
|
||||
运行环境 -> 一直没被实例化, 日志里连 init 都没有。现在改为:
|
||||
- 本类只是"逻辑处理类": __init__ 不需要 namespace/systemName, 方法用模块级
|
||||
comp / LevelId 直接工作。
|
||||
- 真正的 ServerEntityDieEvent 监听在 FeatureDispatcher, 由它实例化本类并
|
||||
把事件转发给本类的 OnEntityDie。
|
||||
|
||||
【黄豆打草掉黄豆】
|
||||
已移回"乡野食材"(shicai): 常量在 ShicaiData, 掉落方法在 ShicaiService,
|
||||
由 FeatureDispatcher 的破坏方块路由调用。本类不再包含黄豆逻辑。
|
||||
|
||||
【死亡方块清单】
|
||||
死亡方块: 牛=rtdd:dead_cow, 猪=rtdd:dead_pig, 羊=rtdd:dead_sheep,
|
||||
鱿鱼=rtdd:deadsquida, 村民=rtdd:dead_villager
|
||||
对应实体类型(minecraft EntityType 枚举名): Cow/Pig/Sheep/Squid/Villager
|
||||
================================================================================
|
||||
"""
|
||||
import mod.server.extraServerApi as serverApi
|
||||
|
||||
# ---- 网易API基础对象 ----
|
||||
comp = serverApi.GetEngineCompFactory() # 组件工厂
|
||||
|
||||
# ---- 只有方块信息组件用 LevelId(查询全维方块) ----
|
||||
from ....BlueFoxTools.ToolsConfig import LevelId
|
||||
|
||||
# ---- 尸体生成延迟(秒): 死亡 1.5 秒后才生成尸体方块, 不会刚死瞬间就出现 ----
|
||||
GEN_DELAY = 1.5
|
||||
|
||||
|
||||
class DeadmobGeneratorService(object):
|
||||
"""
|
||||
死亡生物·生成引擎(逻辑类, 由 FeatureDispatcher 实例化并调用)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
print "===== DeadmobGeneratorService 逻辑类已挂载到 FeatureDispatcher ====="
|
||||
|
||||
# ============================================================================
|
||||
# 一、玩家杀死生物 → 生成死亡方块(由 FeatureDispatcher 的
|
||||
# MobDieEvent 监听转发到这里)
|
||||
# ============================================================================
|
||||
|
||||
def OnEntityDie(self, args):
|
||||
"""
|
||||
玩家杀死对应生物时触发(网易死亡事件 MobDieEvent)。args:
|
||||
id 死亡实体
|
||||
attacker 伤害来源(击杀者)实体(近战=玩家; 远程=玩家射出的抛射物)
|
||||
仅当击杀者是玩家、且死亡生物属于牛/猪/羊/鱿鱼/村民时, 在死亡位置延迟生成尸体。
|
||||
"""
|
||||
try:
|
||||
killedId = args.get("id") or args.get("entityId")
|
||||
attackerId = args.get("attacker") or args.get("attackedByEntId")
|
||||
if not killedId or not attackerId:
|
||||
return
|
||||
# 解析"由玩家击杀": 近战 attacker 就是玩家; 远程 attacker 是抛射物, 取其发射者
|
||||
if not self._resolvePlayerAttacker(attackerId):
|
||||
return
|
||||
# 命中"被玩家击杀": 打印实际类型串与映射结果, 便于确认事件与类型判断是否命中
|
||||
blockName = self._mobTypeToBlock(killedId)
|
||||
print "[DeadmobGen-Attack] killedType=%s -> block=%s" % (
|
||||
self._mobTypeStr(killedId), blockName)
|
||||
if not blockName:
|
||||
return
|
||||
# 死亡位置: 取实体脚下坐标(死亡时实体仍在场)
|
||||
pos = self._getFootPos(killedId)
|
||||
if not pos:
|
||||
return
|
||||
px, py, pz = (int(pos[0]), int(pos[1]), int(pos[2]))
|
||||
dim = self._getDim(killedId)
|
||||
# 该格若非空气则垫高找到可放格, 避免覆盖已存在的方块(如草/其他方块)
|
||||
target = (px, py, pz)
|
||||
if not self._isPlaceable(target, dim):
|
||||
for dy in range(1, 4):
|
||||
candidate = (px, py + dy, pz)
|
||||
if self._isAir(candidate, dim):
|
||||
target = candidate
|
||||
break
|
||||
else:
|
||||
return # 周围都放不下
|
||||
# 延迟生成尸体方块: 死亡后约 1.5 秒才出现(不是刚死瞬间)
|
||||
comp.CreateGame(LevelId).AddTimer(
|
||||
GEN_DELAY, self._placeBlockDelayed, target, blockName, dim)
|
||||
except Exception as e:
|
||||
print "[DeadmobGen-ERR]", e
|
||||
|
||||
def _resolvePlayerAttacker(self, attackerId):
|
||||
"""判断击杀者是否玩家(近战直接是玩家; 远程是玩家射出的抛射物)。
|
||||
返回 True 表示由玩家击杀。"""
|
||||
if not attackerId:
|
||||
return False
|
||||
if self._isPlayer(attackerId):
|
||||
return True
|
||||
# 远程: attacker 是抛射物(箭等), 取发射者, 发射者是玩家则算玩家击杀
|
||||
try:
|
||||
owner = comp.CreateBulletAttributes(attackerId).GetSourceEntityId()
|
||||
return bool(owner) and self._isPlayer(owner)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _placeBlockDelayed(self, target, blockName, dim):
|
||||
"""AddTimer 到点后真正放置尸体方块"""
|
||||
self._setBlock(target, blockName, dim)
|
||||
print "[DeadmobGen]", blockName, "at", target
|
||||
|
||||
def _mobTypeStr(self, entityId):
|
||||
"""取实体类型字符串(如 minecraft:pig), 小写; 取不到返回空串"""
|
||||
try:
|
||||
return (comp.CreateEngineType(entityId).GetEngineTypeStr() or "").lower()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _mobTypeToBlock(self, entityId):
|
||||
"""返回该实体对应的死亡方块名; 非目标生物返回 None。
|
||||
用 GetEngineTypeStr() 拿类型字符串精确匹配(项目已有用法见 EventFilter),
|
||||
不使用 EntityType 位掩码(不同版本的枚举/Squid/Villager 存在性不稳)。"""
|
||||
return {
|
||||
"minecraft:pig": "rtdd:dead_pig",
|
||||
"minecraft:cow": "rtdd:dead_cow",
|
||||
"minecraft:sheep": "rtdd:dead_sheep",
|
||||
"minecraft:squid": "rtdd:deadsquida",
|
||||
"minecraft:villager": "rtdd:dead_villager",
|
||||
"minecraft:villager_v2": "rtdd:dead_villager",
|
||||
}.get(self._mobTypeStr(entityId))
|
||||
|
||||
# ============================================================================
|
||||
# 二、工具方法
|
||||
# ============================================================================
|
||||
|
||||
def _isPlayer(self, entityId):
|
||||
try:
|
||||
ET = serverApi.GetMinecraftEnum().EntityType
|
||||
v = comp.CreateEngineType(entityId).GetEngineType()
|
||||
return v & ET.Player == ET.Player
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _getFootPos(self, entityId):
|
||||
try:
|
||||
return comp.CreatePos(entityId).GetFootPos()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _getDim(self, entityId):
|
||||
try:
|
||||
return comp.CreateDimension(entityId).GetEntityDimensionId()
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def _isAir(self, pos, dim):
|
||||
try:
|
||||
b = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
return b is None or b.get("name") == "minecraft:air"
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
def _isPlaceable(self, pos, dim):
|
||||
"""目标格要么是空气、要么是可通过的草/花等非实体方块(可直接替换)"""
|
||||
try:
|
||||
b = comp.CreateBlockInfo(LevelId).GetBlockNew(pos, dim)
|
||||
if not b:
|
||||
return True
|
||||
name = b.get("name", "minecraft:air")
|
||||
if name == "minecraft:air":
|
||||
return True
|
||||
if name in ("minecraft:tallgrass", "minecraft:short_grass",
|
||||
"minecraft:red_flower", "minecraft:yellow_flower",
|
||||
"minecraft:double_plant"):
|
||||
return True
|
||||
return False
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
def _setBlock(self, pos, blockName, dim):
|
||||
"""放置死亡方块, aux 强制 0"""
|
||||
try:
|
||||
comp.CreateBlockInfo(LevelId).SetBlockNew(
|
||||
pos, blockDict={"name": blockName, "aux": 0},
|
||||
oldBlockHandling=0, dimensionId=dim)
|
||||
except Exception:
|
||||
pass
|
||||
Reference in new issue
Block a user