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

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()