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 客户端对拍缺陷发现与修复工程指南
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
# dynamic_bounty_event_system.gd —— Metin2 40250 单人单机动态事件与赏金通缉令系统 1:1
|
||||
# 对照 40250 服务端 questmanager.cpp, char.cpp, quest.h
|
||||
class_name DynamicBountyEventSystem
|
||||
extends RefCounted
|
||||
|
||||
signal bounties_refreshed(bounty_list: Array)
|
||||
signal bounty_progress_updated(bounty_id: String, current: int, target: int)
|
||||
signal bounty_completed(bounty_id: String, title: String)
|
||||
signal bounty_reward_claimed(bounty_id: String, exp: int, gold: int)
|
||||
|
||||
const BOUNTY_TYPE_HUNT := "hunt" # 怪物狩猎
|
||||
const BOUNTY_TYPE_METIN := "metin" # 魔石破除
|
||||
const BOUNTY_TYPE_BOSS := "boss" # 首领讨伐
|
||||
|
||||
const BOUNTY_CHEST_VNUM := 50180 # 赏金猎人宝箱 (Bounty Chest)
|
||||
|
||||
# 活跃悬赏任务字典: id -> Dictionary
|
||||
var active_bounties: Dictionary = {}
|
||||
var total_completed_count: int = 0
|
||||
|
||||
# 每日自动生成/刷新 3 条特色赏金任务
|
||||
func refresh_daily_bounties() -> Array:
|
||||
active_bounties.clear()
|
||||
|
||||
# 1. 狩猎任务 (野蛮士兵 501, 目标 25 只)
|
||||
var b1: Dictionary = {
|
||||
"id": "bounty_hunt_barbarian",
|
||||
"type": BOUNTY_TYPE_HUNT,
|
||||
"title": "【通缉令】清剿荒原野蛮士兵",
|
||||
"target_vnum": 501,
|
||||
"target_count": 25,
|
||||
"current_count": 0,
|
||||
"is_completed": false,
|
||||
"is_claimed": false,
|
||||
"rewards": {
|
||||
"exp": 150000,
|
||||
"gold": 500000,
|
||||
"items": [{"vnum": 27003, "name": "红色药水(特大)", "count": 100}]
|
||||
}
|
||||
}
|
||||
|
||||
# 2. 破灭魔石任务 (嫉妒之石 8008, 目标 2 颗)
|
||||
var b2: Dictionary = {
|
||||
"id": "bounty_metin_jealousy",
|
||||
"type": BOUNTY_TYPE_METIN,
|
||||
"title": "【天灾通缉】破灭野外嫉妒魔石",
|
||||
"target_vnum": 8008,
|
||||
"target_count": 2,
|
||||
"current_count": 0,
|
||||
"is_completed": false,
|
||||
"is_claimed": false,
|
||||
"rewards": {
|
||||
"exp": 300000,
|
||||
"gold": 1200000,
|
||||
"items": [{"vnum": 50300, "name": "技能书", "count": 1}]
|
||||
}
|
||||
}
|
||||
|
||||
# 3. 讨伐世界首领 (九尾妖狐 1901, 目标 1 次)
|
||||
var b3: Dictionary = {
|
||||
"id": "bounty_boss_ninetails",
|
||||
"type": BOUNTY_TYPE_BOSS,
|
||||
"title": "【大统领诛杀令】斩落雪山九尾妖狐",
|
||||
"target_vnum": 1901,
|
||||
"target_count": 1,
|
||||
"current_count": 0,
|
||||
"is_completed": false,
|
||||
"is_claimed": false,
|
||||
"rewards": {
|
||||
"exp": 1000000,
|
||||
"gold": 3000000,
|
||||
"items": [
|
||||
{"vnum": BOUNTY_CHEST_VNUM, "name": "赏金猎人宝箱", "count": 1},
|
||||
{"vnum": 71003, "name": "祝福卷轴", "count": 1}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
active_bounties[b1["id"]] = b1
|
||||
active_bounties[b2["id"]] = b2
|
||||
active_bounties[b3["id"]] = b3
|
||||
|
||||
var list = [b1, b2, b3]
|
||||
bounties_refreshed.emit(list)
|
||||
return list
|
||||
|
||||
# 怪物击杀侦测挂钩
|
||||
func on_mob_killed(mob_vnum: int, count: int = 1) -> void:
|
||||
for bid in active_bounties.keys():
|
||||
var b = active_bounties[bid]
|
||||
if b["is_completed"]:
|
||||
continue
|
||||
|
||||
if (b["type"] == BOUNTY_TYPE_HUNT or b["type"] == BOUNTY_TYPE_BOSS) and b["target_vnum"] == mob_vnum:
|
||||
b["current_count"] = min(b["target_count"], b["current_count"] + count)
|
||||
bounty_progress_updated.emit(bid, b["current_count"], b["target_count"])
|
||||
|
||||
if b["current_count"] >= b["target_count"]:
|
||||
b["is_completed"] = true
|
||||
total_completed_count += 1
|
||||
bounty_completed.emit(bid, b["title"])
|
||||
|
||||
# 魔石击碎侦测挂钩
|
||||
func on_metin_destroyed(metin_vnum: int) -> void:
|
||||
for bid in active_bounties.keys():
|
||||
var b = active_bounties[bid]
|
||||
if b["is_completed"]:
|
||||
continue
|
||||
|
||||
if b["type"] == BOUNTY_TYPE_METIN and b["target_vnum"] == metin_vnum:
|
||||
b["current_count"] = min(b["target_count"], b["current_count"] + 1)
|
||||
bounty_progress_updated.emit(bid, b["current_count"], b["target_count"])
|
||||
|
||||
if b["current_count"] >= b["target_count"]:
|
||||
b["is_completed"] = true
|
||||
total_completed_count += 1
|
||||
bounty_completed.emit(bid, b["title"])
|
||||
|
||||
# 领取赏金通缉令奖励
|
||||
func claim_bounty_reward(bounty_id: String, player_data: Dictionary, inventory: Array) -> Dictionary:
|
||||
if not active_bounties.has(bounty_id):
|
||||
return {"ok": false, "reason": "BOUNTY_NOT_FOUND", "msg": "未找到该通缉令!"}
|
||||
|
||||
var b = active_bounties[bounty_id]
|
||||
if not b["is_completed"]:
|
||||
return {"ok": false, "reason": "NOT_COMPLETED", "msg": "该通缉令目标尚未达成!"}
|
||||
|
||||
if b["is_claimed"]:
|
||||
return {"ok": false, "reason": "ALREADY_CLAIMED", "msg": "该通缉令赏金已被领取!"}
|
||||
|
||||
var rew = b["rewards"]
|
||||
var exp_gain: int = rew.get("exp", 0)
|
||||
var gold_gain: int = rew.get("gold", 0)
|
||||
var items: Array = rew.get("items", [])
|
||||
|
||||
# 发放经验与金币
|
||||
player_data["exp"] = int(player_data.get("exp", 0)) + exp_gain
|
||||
player_data["gold"] = int(player_data.get("gold", 0)) + gold_gain
|
||||
|
||||
# 物品入包
|
||||
var granted_items := []
|
||||
for it in items:
|
||||
for i in range(inventory.size()):
|
||||
if inventory[i] == null:
|
||||
inventory[i] = it.duplicate(true)
|
||||
granted_items.append(it["name"])
|
||||
break
|
||||
|
||||
b["is_claimed"] = true
|
||||
bounty_reward_claimed.emit(bounty_id, exp_gain, gold_gain)
|
||||
|
||||
return {
|
||||
"ok": true,
|
||||
"bounty_id": bounty_id,
|
||||
"exp_gain": exp_gain,
|
||||
"gold_gain": gold_gain,
|
||||
"items_granted": granted_items,
|
||||
"msg": "通缉赏金核销!获得 %d 经验、%d 金币以及丰厚物资!" % [exp_gain, gold_gain]
|
||||
}
|
||||
|
||||
# 获取所有活跃悬赏
|
||||
func get_active_bounties() -> Array:
|
||||
return active_bounties.values()
|
||||
Reference in New Issue
Block a user