Files
mtgodot-poc/project/monster_card_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

168 lines
4.4 KiB
GDScript

# monster_card_system.gd —— Metin2 40250 怪物图鉴与百科全书系统 1:1
# 对照 40250 服务端 monster_card.cpp, uimonstercard.py, char_battle.cpp
class_name MonsterCardSystem
extends RefCounted
signal card_collected(mob_vnum: int, mob_name: String, count: int)
signal zone_completed(zone_name: String, bonuses: Dictionary)
# 区域图鉴数据库配置
const BESTIARY_DATA: Dictionary = {
"village": {
"zone_name": "新手原野",
"bonus": {"max_hp": 300},
"monsters": {
101: {
"name": "野狼",
"level": 1,
"element": "无",
"weakness": "物理",
"drops": ["木剑+0", "小红药水"],
"card_vnum": 70201
},
391: {
"name": "白灵女将",
"level": 25,
"element": "风",
"weakness": "火",
"drops": ["狼剑+9", "红药水(中)"],
"card_vnum": 70202
}
}
},
"desert": {
"zone_name": "永壁沙漠",
"bonus": {"monster_dmg_pct": 3},
"monsters": {
2108: {
"name": "沙漠强盗",
"level": 50,
"element": "地",
"weakness": "风",
"drops": ["红药水(大)", "胡萝卜"],
"card_vnum": 70203
},
2191: {
"name": "巨型沙漠龟",
"level": 67,
"element": "地",
"weakness": "风",
"drops": ["沙漠龟宝箱", "灵魂之石"],
"card_vnum": 70204
}
}
},
"catacomb": {
"zone_name": "地下墓穴",
"bonus": {"def": 50},
"monsters": {
2597: {
"name": "塔耳塔洛斯",
"level": 82,
"element": "暗",
"weakness": "圣",
"drops": ["冷酷之石"],
"card_vnum": 70205
},
2598: {
"name": "冥王阿兹瑞尔",
"level": 91,
"element": "暗",
"weakness": "圣",
"drops": ["阿兹瑞尔宝箱", "泰坦之盾"],
"card_vnum": 70206
}
}
}
}
var collected_cards: Dictionary = {} # mob_vnum -> count
var completed_zones: Dictionary = {} # zone_key -> bool
# 使用并收集怪物卡片
func register_card(card_vnum: int) -> Dictionary:
# 查找对应怪物
var target_mob_vnum := -1
var target_mob_name := ""
var target_zone := ""
for z_key in BESTIARY_DATA.keys():
var z_data = BESTIARY_DATA[z_key]
for mv in z_data["monsters"].keys():
var minfo = z_data["monsters"][mv]
if minfo["card_vnum"] == card_vnum:
target_mob_vnum = mv
target_mob_name = minfo["name"]
target_zone = z_key
break
if target_mob_vnum != -1:
break
if target_mob_vnum == -1:
return {"ok": false, "reason": "INVALID_CARD", "msg": "这不是有效的怪物图鉴卡片!"}
var new_count: int = int(collected_cards.get(target_mob_vnum, 0)) + 1
collected_cards[target_mob_vnum] = new_count
card_collected.emit(target_mob_vnum, target_mob_name, new_count)
# 检查所属区域是否全部完成收集
var is_newly_completed := false
var zone_bonus: Dictionary = {}
if not completed_zones.get(target_zone, false):
var all_done := true
var z_mobs = BESTIARY_DATA[target_zone]["monsters"]
for m_id in z_mobs.keys():
if int(collected_cards.get(m_id, 0)) < 1:
all_done = false
break
if all_done:
completed_zones[target_zone] = true
is_newly_completed = true
zone_bonus = BESTIARY_DATA[target_zone]["bonus"]
zone_completed.emit(BESTIARY_DATA[target_zone]["zone_name"], zone_bonus)
return {
"ok": true,
"mob_vnum": target_mob_vnum,
"mob_name": target_mob_name,
"count": new_count,
"zone_completed": is_newly_completed,
"zone_bonus": zone_bonus,
"msg": "已将【%s】录入怪物图鉴!" % target_mob_name
}
# 查看图鉴词条 (未解锁则模糊显示)
func get_monster_entry(mob_vnum: int) -> Dictionary:
for z_data in BESTIARY_DATA.values():
if z_data["monsters"].has(mob_vnum):
var info: Dictionary = z_data["monsters"][mob_vnum].duplicate(true)
var count: int = int(collected_cards.get(mob_vnum, 0))
if count >= 1:
info["is_unlocked"] = true
info["collected_count"] = count
return info
else:
return {
"is_unlocked": false,
"name": "???",
"level": "??",
"element": "??",
"weakness": "??",
"drops": ["未知"],
"collected_count": 0
}
return {"is_unlocked": false, "reason": "NOT_FOUND"}
# 汇总所有已完成区域图鉴的常驻被动战力加成
func get_active_collection_bonuses() -> Dictionary:
var total_bonuses: Dictionary = {}
for z_key in completed_zones.keys():
if completed_zones[z_key]:
var b = BESTIARY_DATA[z_key]["bonus"]
for k in b.keys():
total_bonuses[k] = total_bonuses.get(k, 0) + int(b[k])
return total_bonuses