- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
164 lines
5.0 KiB
GDScript
164 lines
5.0 KiB
GDScript
# 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()
|