Files
mtgodot-poc/project/spider_dungeon_system.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

162 lines
5.1 KiB
GDScript

# spider_dungeon_system.gd —— Metin2 40250 蜘蛛洞穴三层与男爵夫人巢穴 1:1
# 对照 40250 服务端 spider_dungeon_3.quest, dungeon.cpp, Boss 2094 & Mob 2095
class_name SpiderDungeonSystem
extends RefCounted
signal dungeon_started(duration: float)
signal egg_destroyed(eggs_left: int, current_atk: int, current_def: int)
signal minions_summoned()
signal dungeon_cleared(drops: Array[Dictionary])
signal dungeon_failed(reason: String)
const NPC_GRAVESTONE := 20371 # 守墓人碑文
const ITEM_ARACHNID_KEY := 30327 # 蜘蛛之钥
const BOSS_SPIDER_BARONESS := 2094 # 蜘蛛男爵夫人
const MOB_SPIDER_EGG := 2095 # 蜘蛛卵
const ITEM_BARONESS_CHEST := 50157 # 男爵宝箱
const ITEM_TITAN_SHIELD := 13129 # 泰坦盾牌+9
const REQUIRED_LEVEL := 50
const DUNGEON_TIME_LIMIT := 1200.0 # 20 分钟 (秒)
const BASE_BARONESS_MAX_HP := 250000
const BASE_BARONESS_DEF := 400
const BASE_BARONESS_ATK := 800
var is_active: bool = false
var remaining_time: float = 0.0
var eggs_remaining: int = 4
var baroness_hp: int = 0
var baroness_max_hp: int = BASE_BARONESS_MAX_HP
var baroness_def: int = BASE_BARONESS_DEF
var baroness_atk: int = BASE_BARONESS_ATK
var minions_spawned: bool = false
var dungeon_completed: bool = false
# 开启地牢密室 (spider_dungeon_3.quest)
func enter_dungeon(player_level: int, inventory: Array) -> Dictionary:
if is_active:
return {"ok": false, "reason": "ALREADY_ACTIVE", "msg": "地牢密室已经在进行中!"}
if player_level < REQUIRED_LEVEL:
return {"ok": false, "reason": "LEVEL_TOO_LOW", "msg": "等级未满 50 级,无法承受蜘蛛巢穴的剧毒!"}
# 查找并扣除蜘蛛之钥 (30327)
var key_slot := -1
for i in range(inventory.size()):
var it = inventory[i]
if it != null and int(it.get("vnum", 0)) == ITEM_ARACHNID_KEY:
key_slot = i
break
if key_slot == -1:
return {"ok": false, "reason": "MISSING_KEY", "msg": "进入蜘蛛男爵夫人巢穴需要消耗【蜘蛛钥匙】!"}
inventory[key_slot] = null
# 初始化地牢状态
is_active = true
remaining_time = DUNGEON_TIME_LIMIT
eggs_remaining = 4
baroness_max_hp = BASE_BARONESS_MAX_HP
baroness_hp = baroness_max_hp
baroness_def = BASE_BARONESS_DEF
baroness_atk = BASE_BARONESS_ATK
minions_spawned = false
dungeon_completed = false
dungeon_started.emit(remaining_time)
return {
"ok": true,
"time_limit": remaining_time,
"eggs_count": eggs_remaining,
"baroness_hp": baroness_hp,
"baroness_atk": baroness_atk,
"baroness_def": baroness_def,
"msg": "密室封印已破除!蜘蛛男爵夫人苏醒,你有 20 分钟时间击杀它!"
}
# 击碎一枚蜘蛛卵 (狂暴激怒机制: 防御 -15%, 攻击 +20%)
func destroy_egg() -> Dictionary:
if not is_active:
return {"ok": false, "reason": "DUNGEON_NOT_ACTIVE"}
if eggs_remaining <= 0:
return {"ok": false, "reason": "NO_EGGS_LEFT", "msg": "所有蜘蛛卵均已被击碎!"}
eggs_remaining -= 1
var eggs_broken := 4 - eggs_remaining
# 每碎 1 卵:防御降低 15% (最高 -60%), 攻击提升 20% (最高 +80%)
var def_reduction_pct: float = float(eggs_broken) * 0.15
var atk_bonus_pct: float = float(eggs_broken) * 0.20
baroness_def = int(round(float(BASE_BARONESS_DEF) * (1.0 - def_reduction_pct)))
baroness_atk = int(round(float(BASE_BARONESS_ATK) * (1.0 + atk_bonus_pct)))
egg_destroyed.emit(eggs_remaining, baroness_atk, baroness_def)
return {
"ok": true,
"eggs_left": eggs_remaining,
"eggs_broken": eggs_broken,
"baroness_def": baroness_def,
"baroness_atk": baroness_atk,
"msg": "蜘蛛卵被击碎!男爵夫人防御大幅削弱,但陷入了极度狂暴!"
}
# 攻击男爵夫人
func damage_baroness(raw_damage: int) -> Dictionary:
if not is_active:
return {"ok": false, "reason": "DUNGEON_NOT_ACTIVE"}
if dungeon_completed:
return {"ok": false, "reason": "ALREADY_COMPLETED"}
var eff_dmg: int = maxi(1, raw_damage - baroness_def)
baroness_hp = maxi(0, baroness_hp - eff_dmg)
# 50% 血量召唤蜘蛛群
var triggered_minions := false
if not minions_spawned and baroness_hp <= int(baroness_max_hp * 0.5):
minions_spawned = true
triggered_minions = true
minions_summoned.emit()
if baroness_hp <= 0:
dungeon_completed = true
is_active = false
var drops: Array[Dictionary] = [
{"vnum": ITEM_BARONESS_CHEST, "count": 1, "name": "男爵宝箱"},
{"vnum": ITEM_TITAN_SHIELD, "count": 1, "name": "泰坦盾牌+9"}
]
dungeon_cleared.emit(drops)
return {
"ok": true,
"dead": true,
"baroness_hp": 0,
"damage_dealt": eff_dmg,
"drops": drops,
"msg": "蜘蛛男爵夫人轰然倒地!巢穴通关!"
}
return {
"ok": true,
"dead": false,
"baroness_hp": baroness_hp,
"damage_dealt": eff_dmg,
"minions_summoned": triggered_minions
}
# 逐帧更新倒计时
func update(dt: float) -> Dictionary:
if not is_active:
return {}
remaining_time -= dt
if remaining_time <= 0.0:
remaining_time = 0.0
is_active = false
dungeon_failed.emit("TIME_OUT")
return {"failed": true, "reason": "TIME_OUT", "msg": "挑战超时!你已被移出蜘蛛男爵夫人巢穴!"}
return {"failed": false, "remaining_time": remaining_time}