- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
295 lines
12 KiB
GDScript
295 lines
12 KiB
GDScript
# monarch_empire_treasury_system.gd —— Metin2 40250 官方君主国王特权与王国国库祈愿系统 1:1
|
|
# 100% 对照 40250 服务端 monarch.cpp, monarch.h, quest/monarch.quest, tables.h
|
|
class_name MonarchEmpireTreasurySystem
|
|
extends RefCounted
|
|
|
|
signal monarch_healed_empire(empire: int, cost: int, restored_citizens_count: int)
|
|
signal monarch_power_up_activated(empire: int, cost: int, duration: float)
|
|
signal monarch_defense_up_activated(empire: int, cost: int, duration: float)
|
|
signal treasury_tax_collected(empire: int, amount: int, new_balance: int)
|
|
signal monarch_appointed(empire: int, player_id: int, player_name: String)
|
|
signal monarch_error(reason: String)
|
|
|
|
# 40250 官方三大帝国常数
|
|
const EMPIRE_NONE := 0
|
|
const EMPIRE_SHINSOO := 1 # 辛秀帝国 (红国)
|
|
const EMPIRE_CHUNJO := 2 # 春祖帝国 (黄国)
|
|
const EMPIRE_JINNO := 3 # 镇奴帝国 (蓝国)
|
|
|
|
const EMPIRE_NAMES: Dictionary = {
|
|
EMPIRE_SHINSOO: "辛秀帝国 (Shinsoo - 红国)",
|
|
EMPIRE_CHUNJO: "春祖帝国 (Chunjo - 黄国)",
|
|
EMPIRE_JINNO: "镇奴帝国 (Jinno - 蓝国)"
|
|
}
|
|
|
|
# 国库金币上限 (40250 monarch.cpp: 20亿上限)
|
|
const MAX_TREASURY_GOLD := 2000000000
|
|
|
|
# 技能费用与冷却
|
|
const COST_HEAL_EMPIRE := 100000 # 恩泽圣愈国库消耗 100,000 金币
|
|
const COST_POWER_UP := 500000 # 帝国狂暴国库消耗 500,000 金币
|
|
const COST_DEFENSE_UP := 500000 # 铁壁光环国库消耗 500,000 金币
|
|
|
|
const COOLDOWN_HEAL_EMPIRE := 600.0 # 恩泽圣愈冷却 10 分钟 (600 秒)
|
|
const COOLDOWN_POWER_UP := 600.0 # 战力觉醒冷却 10 分钟
|
|
const COOLDOWN_DEFENSE_UP := 600.0 # 铁壁守护冷却 10 分钟
|
|
|
|
const DURATION_POWER_UP := 1800.0 # 战力狂暴持续 30 分钟 (1800 秒)
|
|
const DURATION_DEFENSE_UP := 1800.0 # 铁壁守护持续 30 分钟
|
|
|
|
# 数据存储
|
|
var _monarch_pids: Dictionary = {1: 0, 2: 0, 3: 0} # empire -> player_id
|
|
var _monarch_names: Dictionary = {1: "", 2: "", 3: ""}
|
|
var _treasury: Dictionary = {1: 0, 2: 0, 3: 0} # empire -> gold
|
|
|
|
var _heal_cooldown: Dictionary = {1: 0.0, 2: 0.0, 3: 0.0}
|
|
var _power_up_cooldown: Dictionary = {1: 0.0, 2: 0.0, 3: 0.0}
|
|
var _defense_up_cooldown: Dictionary = {1: 0.0, 2: 0.0, 3: 0.0}
|
|
|
|
var _power_up_active: Dictionary = {1: false, 2: false, 3: false}
|
|
var _defense_up_active: Dictionary = {1: false, 2: false, 3: false}
|
|
|
|
var _power_up_timer: Dictionary = {1: 0.0, 2: 0.0, 3: 0.0}
|
|
var _defense_up_timer: Dictionary = {1: 0.0, 2: 0.0, 3: 0.0}
|
|
|
|
# ==========================================
|
|
# 1. 国王任命与身份核验 (monarch.cpp: IsMonarch)
|
|
# ==========================================
|
|
func appoint_monarch(empire: int, player_id: int, player_name: String) -> bool:
|
|
if not _is_valid_empire(empire):
|
|
monarch_error.emit("INVALID_EMPIRE")
|
|
return false
|
|
_monarch_pids[empire] = player_id
|
|
_monarch_names[empire] = player_name
|
|
monarch_appointed.emit(empire, player_id, player_name)
|
|
return true
|
|
|
|
func is_monarch(player_id: int, empire: int) -> bool:
|
|
if not _is_valid_empire(empire):
|
|
return false
|
|
return int(_monarch_pids.get(empire, 0)) == player_id
|
|
|
|
func get_monarch_info(empire: int) -> Dictionary:
|
|
if not _is_valid_empire(empire):
|
|
return {}
|
|
return {
|
|
"empire": empire,
|
|
"empire_name": EMPIRE_NAMES[empire],
|
|
"player_id": _monarch_pids.get(empire, 0),
|
|
"player_name": _monarch_names.get(empire, ""),
|
|
"treasury_gold": _treasury.get(empire, 0)
|
|
}
|
|
|
|
# ==========================================
|
|
# 2. 国库金币与税赋 (monarch.cpp: AddMoney, DecMoney)
|
|
# ==========================================
|
|
func get_treasury_gold(empire: int) -> int:
|
|
if not _is_valid_empire(empire):
|
|
return 0
|
|
return int(_treasury.get(empire, 0))
|
|
|
|
func add_treasury_gold(empire: int, amount: int) -> bool:
|
|
if not _is_valid_empire(empire) or amount <= 0:
|
|
return false
|
|
var current = int(_treasury.get(empire, 0))
|
|
var new_val = min(current + amount, MAX_TREASURY_GOLD)
|
|
_treasury[empire] = new_val
|
|
return true
|
|
|
|
func collect_monster_tax(empire: int, monster_gold_drop: int, tax_rate: float = 0.02) -> int:
|
|
if not _is_valid_empire(empire) or monster_gold_drop <= 0:
|
|
return 0
|
|
var tax = int(floor(monster_gold_drop * tax_rate))
|
|
if tax > 0:
|
|
add_treasury_gold(empire, tax)
|
|
treasury_tax_collected.emit(empire, tax, _treasury[empire])
|
|
return tax
|
|
|
|
func dec_treasury_gold(empire: int, amount: int) -> bool:
|
|
if not _is_valid_empire(empire) or amount <= 0:
|
|
return false
|
|
var current = int(_treasury.get(empire, 0))
|
|
if current < amount:
|
|
return false
|
|
_treasury[empire] = current - amount
|
|
return true
|
|
|
|
# ==========================================
|
|
# 3. 君主全境治国神技 (monarch.cpp: HealMyEmpire, PowerUp, DefenseUp)
|
|
# ==========================================
|
|
|
|
# 恩泽圣愈:全境国民 HP/SP 瞬间全满 (FHealMyEmpire)
|
|
func heal_my_empire(
|
|
player_id: int,
|
|
empire: int,
|
|
online_citizens: Array[Dictionary]
|
|
) -> Dictionary:
|
|
if not is_monarch(player_id, empire):
|
|
monarch_error.emit("NOT_MONARCH")
|
|
return {"ok": false, "reason": "NOT_MONARCH", "msg": "你并非本帝国加冕的君主,无权调动国库施加恩泽!"}
|
|
|
|
if float(_heal_cooldown.get(empire, 0.0)) > 0.0:
|
|
monarch_error.emit("COOLDOWN_ACTIVE")
|
|
return {
|
|
"ok": false,
|
|
"reason": "COOLDOWN_ACTIVE",
|
|
"msg": "恩泽圣愈冷却中!还需等待 %d 秒。" % int(_heal_cooldown[empire])
|
|
}
|
|
|
|
if get_treasury_gold(empire) < COST_HEAL_EMPIRE:
|
|
monarch_error.emit("NOT_ENOUGH_TREASURY")
|
|
return {
|
|
"ok": false,
|
|
"reason": "NOT_ENOUGH_TREASURY",
|
|
"msg": "国库资金匮乏!施展恩泽圣愈需要 %d 国库金币,当前只有 %d 金币。" % [COST_HEAL_EMPIRE, get_treasury_gold(empire)]
|
|
}
|
|
|
|
# 扣除国库金币
|
|
dec_treasury_gold(empire, COST_HEAL_EMPIRE)
|
|
_heal_cooldown[empire] = COOLDOWN_HEAL_EMPIRE
|
|
|
|
# 治愈同帝国所有国民 (40250 ch->PointChange(POINT_HP, max - hp))
|
|
var healed_count := 0
|
|
for citizen in online_citizens:
|
|
if int(citizen.get("empire", 0)) == empire:
|
|
citizen["hp"] = citizen.get("max_hp", 1000)
|
|
citizen["sp"] = citizen.get("max_sp", 500)
|
|
healed_count += 1
|
|
|
|
monarch_healed_empire.emit(empire, COST_HEAL_EMPIRE, healed_count)
|
|
return {
|
|
"ok": true,
|
|
"healed_count": healed_count,
|
|
"remaining_treasury": get_treasury_gold(empire),
|
|
"msg": "君主高举天子权杖,浩荡圣光普照四野!全境 %d 位国民生命法力全部盈满!" % healed_count
|
|
}
|
|
|
|
# 帝国战力狂暴 (PowerUp: 攻击力 +10%)
|
|
func activate_power_up(player_id: int, empire: int) -> Dictionary:
|
|
if not is_monarch(player_id, empire):
|
|
monarch_error.emit("NOT_MONARCH")
|
|
return {"ok": false, "reason": "NOT_MONARCH", "msg": "只有君主方可激发帝国战力觉醒!"}
|
|
|
|
if float(_power_up_cooldown.get(empire, 0.0)) > 0.0:
|
|
monarch_error.emit("COOLDOWN_ACTIVE")
|
|
return {
|
|
"ok": false,
|
|
"reason": "COOLDOWN_ACTIVE",
|
|
"msg": "帝国狂暴冷却中!还需等待 %d 秒。" % int(_power_up_cooldown[empire])
|
|
}
|
|
|
|
if get_treasury_gold(empire) < COST_POWER_UP:
|
|
monarch_error.emit("NOT_ENOUGH_TREASURY")
|
|
return {"ok": false, "reason": "NOT_ENOUGH_TREASURY", "msg": "国库资金不足 %d 金币!" % COST_POWER_UP}
|
|
|
|
dec_treasury_gold(empire, COST_POWER_UP)
|
|
_power_up_cooldown[empire] = COOLDOWN_POWER_UP
|
|
_power_up_active[empire] = true
|
|
_power_up_timer[empire] = DURATION_POWER_UP
|
|
|
|
monarch_power_up_activated.emit(empire, COST_POWER_UP, DURATION_POWER_UP)
|
|
return {
|
|
"ok": true,
|
|
"duration": DURATION_POWER_UP,
|
|
"att_grade_bonus": 0.10,
|
|
"remaining_treasury": get_treasury_gold(empire),
|
|
"msg": "战神咆哮!君主调动帝国龙脉之力,全体国民攻击力提升 10%,持续 30 分钟!"
|
|
}
|
|
|
|
# 帝国铁壁守护 (DefenseUp: 防御力 +10%)
|
|
func activate_defense_up(player_id: int, empire: int) -> Dictionary:
|
|
if not is_monarch(player_id, empire):
|
|
monarch_error.emit("NOT_MONARCH")
|
|
return {"ok": false, "reason": "NOT_MONARCH", "msg": "只有君主方可激发帝国铁壁守护!"}
|
|
|
|
if float(_defense_up_cooldown.get(empire, 0.0)) > 0.0:
|
|
monarch_error.emit("COOLDOWN_ACTIVE")
|
|
return {
|
|
"ok": false,
|
|
"reason": "COOLDOWN_ACTIVE",
|
|
"msg": "铁壁守护冷却中!还需等待 %d 秒。" % int(_defense_up_cooldown[empire])
|
|
}
|
|
|
|
if get_treasury_gold(empire) < COST_DEFENSE_UP:
|
|
monarch_error.emit("NOT_ENOUGH_TREASURY")
|
|
return {"ok": false, "reason": "NOT_ENOUGH_TREASURY", "msg": "国库资金不足 %d 金币!" % COST_DEFENSE_UP}
|
|
|
|
dec_treasury_gold(empire, COST_DEFENSE_UP)
|
|
_defense_up_cooldown[empire] = COOLDOWN_DEFENSE_UP
|
|
_defense_up_active[empire] = true
|
|
_defense_up_timer[empire] = DURATION_DEFENSE_UP
|
|
|
|
monarch_defense_up_activated.emit(empire, COST_DEFENSE_UP, DURATION_DEFENSE_UP)
|
|
return {
|
|
"ok": true,
|
|
"duration": DURATION_DEFENSE_UP,
|
|
"def_grade_bonus": 0.10,
|
|
"remaining_treasury": get_treasury_gold(empire),
|
|
"msg": "坚不可摧!君主召唤玄龟金钟大阵,全体国民防御力提升 10%,持续 30 分钟!"
|
|
}
|
|
|
|
# 查询帝国国民当前享受的光环加成
|
|
func get_citizen_empire_buffs(empire: int) -> Dictionary:
|
|
if not _is_valid_empire(empire):
|
|
return {"att_bonus": 0.0, "def_bonus": 0.0}
|
|
return {
|
|
"att_bonus": 0.10 if bool(_power_up_active.get(empire, false)) else 0.0,
|
|
"def_bonus": 0.10 if bool(_defense_up_active.get(empire, false)) else 0.0,
|
|
"power_up_time_left": float(_power_up_timer.get(empire, 0.0)),
|
|
"defense_up_time_left": float(_defense_up_timer.get(empire, 0.0))
|
|
}
|
|
|
|
# 定时器时间步进
|
|
func update(delta: float) -> void:
|
|
for emp in [1, 2, 3]:
|
|
# 冷却倒计时
|
|
if _heal_cooldown[emp] > 0.0:
|
|
_heal_cooldown[emp] = max(_heal_cooldown[emp] - delta, 0.0)
|
|
if _power_up_cooldown[emp] > 0.0:
|
|
_power_up_cooldown[emp] = max(_power_up_cooldown[emp] - delta, 0.0)
|
|
if _defense_up_cooldown[emp] > 0.0:
|
|
_defense_up_cooldown[emp] = max(_defense_up_cooldown[emp] - delta, 0.0)
|
|
|
|
# 增益持续倒计时
|
|
if _power_up_active[emp]:
|
|
_power_up_timer[emp] -= delta
|
|
if _power_up_timer[emp] <= 0.0:
|
|
_power_up_timer[emp] = 0.0
|
|
_power_up_active[emp] = false
|
|
|
|
if _defense_up_active[emp]:
|
|
_defense_up_timer[emp] -= delta
|
|
if _defense_up_timer[emp] <= 0.0:
|
|
_defense_up_timer[emp] = 0.0
|
|
_defense_up_active[emp] = false
|
|
|
|
func _is_valid_empire(empire: int) -> bool:
|
|
return empire == EMPIRE_SHINSOO or empire == EMPIRE_CHUNJO or empire == EMPIRE_JINNO
|
|
|
|
# 序列化与反序列化
|
|
func serialize() -> Dictionary:
|
|
return {
|
|
"monarch_pids": _monarch_pids.duplicate(),
|
|
"monarch_names": _monarch_names.duplicate(),
|
|
"treasury": _treasury.duplicate(),
|
|
"heal_cooldown": _heal_cooldown.duplicate(),
|
|
"power_up_cooldown": _power_up_cooldown.duplicate(),
|
|
"defense_up_cooldown": _defense_up_cooldown.duplicate(),
|
|
"power_up_active": _power_up_active.duplicate(),
|
|
"defense_up_active": _defense_up_active.duplicate(),
|
|
"power_up_timer": _power_up_timer.duplicate(),
|
|
"defense_up_timer": _defense_up_timer.duplicate()
|
|
}
|
|
|
|
func deserialize(data: Dictionary) -> void:
|
|
_monarch_pids = data.get("monarch_pids", {1: 0, 2: 0, 3: 0}).duplicate()
|
|
_monarch_names = data.get("monarch_names", {1: "", 2: "", 3: ""}).duplicate()
|
|
_treasury = data.get("treasury", {1: 0, 2: 0, 3: 0}).duplicate()
|
|
_heal_cooldown = data.get("heal_cooldown", {1: 0.0, 2: 0.0, 3: 0.0}).duplicate()
|
|
_power_up_cooldown = data.get("power_up_cooldown", {1: 0.0, 2: 0.0, 3: 0.0}).duplicate()
|
|
_defense_up_cooldown = data.get("defense_up_cooldown", {1: 0.0, 2: 0.0, 3: 0.0}).duplicate()
|
|
_power_up_active = data.get("power_up_active", {1: false, 2: false, 3: false}).duplicate()
|
|
_defense_up_active = data.get("defense_up_active", {1: false, 2: false, 3: false}).duplicate()
|
|
_power_up_timer = data.get("power_up_timer", {1: 0.0, 2: 0.0, 3: 0.0}).duplicate()
|
|
_defense_up_timer = data.get("defense_up_timer", {1: 0.0, 2: 0.0, 3: 0.0}).duplicate()
|