- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
231 lines
7.1 KiB
GDScript
231 lines
7.1 KiB
GDScript
# monarch_election_ui_system.gd —— 40250 帝国君主大选投票公示系统 1:1
|
|
# 严格对照:
|
|
# metin2/src/server/game/src/monarch.h, monarch.cpp (CMonarch)
|
|
# metin2/src/server/share/locale/english/quest/xxx_monarch.quest
|
|
# 40250/Server Client TMP4/mysql/player/monarch_candidacy.MYD, monarch_election.MYD
|
|
extends RefCounted
|
|
|
|
const EMPIRE_SHINSOO := 1 # 赤蛇国 (进国)
|
|
const EMPIRE_CHUNJO := 2 # 白虎国 (顺天)
|
|
const EMPIRE_JINNO := 3 # 苍龙国 (忍国)
|
|
|
|
const STATUS_CLOSED := 0 # 选举未开放
|
|
const STATUS_CANDIDACY := 1 # 候选人报名登记期
|
|
const STATUS_VOTING := 2 # 全民大选投票期
|
|
const STATUS_CONCLUDED := 3 # 大选揭晓,君主即位
|
|
|
|
const MIN_CANDIDATE_LEVEL := 50
|
|
const MIN_VOTER_LEVEL := 50
|
|
const CANDIDACY_FEE := 1000000 # 报名保证金 1,000,000 金币
|
|
const MAX_CANDIDATES_PER_EMPIRE := 8 # 每国最多 8 名候选人
|
|
|
|
class CandidateInfo:
|
|
var pid: int
|
|
var name: String
|
|
var empire: int
|
|
var level: int
|
|
var votes: int = 0
|
|
var manifesto: String = ""
|
|
|
|
func _init(p_pid: int, p_name: String, p_emp: int, p_lvl: int, p_manifesto: String) -> void:
|
|
pid = p_pid
|
|
name = p_name
|
|
empire = p_emp
|
|
level = p_lvl
|
|
votes = 0
|
|
manifesto = p_manifesto
|
|
|
|
# 选举状态字典: empire_id -> status
|
|
var election_states: Dictionary = {
|
|
EMPIRE_SHINSOO: STATUS_CLOSED,
|
|
EMPIRE_CHUNJO: STATUS_CLOSED,
|
|
EMPIRE_JINNO: STATUS_CLOSED
|
|
}
|
|
|
|
# 候选人名册: empire_id -> Array of CandidateInfo
|
|
var candidates: Dictionary = {
|
|
EMPIRE_SHINSOO: [],
|
|
EMPIRE_CHUNJO: [],
|
|
EMPIRE_JINNO: []
|
|
}
|
|
|
|
# 选民投票记录: empire_id -> { voter_pid: candidate_pid }
|
|
var voter_records: Dictionary = {
|
|
EMPIRE_SHINSOO: {},
|
|
EMPIRE_CHUNJO: {},
|
|
EMPIRE_JINNO: {}
|
|
}
|
|
|
|
# 现任君主数据: empire_id -> { "pid": int, "name": String, "treasury": int, "tax_rate": int }
|
|
var current_monarchs: Dictionary = {
|
|
EMPIRE_SHINSOO: { "pid": 0, "name": "", "treasury": 10000000, "tax_rate": 3 },
|
|
EMPIRE_CHUNJO: { "pid": 0, "name": "", "treasury": 10000000, "tax_rate": 3 },
|
|
EMPIRE_JINNO: { "pid": 0, "name": "", "treasury": 10000000, "tax_rate": 3 },
|
|
}
|
|
|
|
func _init() -> void:
|
|
reset_all_elections()
|
|
|
|
func reset_all_elections() -> void:
|
|
for emp in [EMPIRE_SHINSOO, EMPIRE_CHUNJO, EMPIRE_JINNO]:
|
|
election_states[emp] = STATUS_CLOSED
|
|
candidates[emp] = []
|
|
voter_records[emp] = {}
|
|
|
|
# 1. 管理员开启大选阶段 (GM: Kaiser Wahl Einstellung)
|
|
func set_election_stage(empire: int, new_status: int) -> bool:
|
|
if not election_states.has(empire):
|
|
return false
|
|
election_states[empire] = new_status
|
|
return true
|
|
|
|
# 2. 候选人报名参选 (monarch_candidacy)
|
|
func register_candidacy(
|
|
pid: int,
|
|
name: String,
|
|
empire: int,
|
|
level: int,
|
|
player_gold: int,
|
|
manifesto: String
|
|
) -> Dictionary:
|
|
var res := { "success": false, "gold_cost": 0, "error_code": "OK", "message": "" }
|
|
|
|
if election_states.get(empire) != STATUS_CANDIDACY:
|
|
res["error_code"] = "CANDIDACY_NOT_OPEN"
|
|
res["message"] = "当前未处于君主参选登记期。"
|
|
return res
|
|
|
|
if level < MIN_CANDIDATE_LEVEL:
|
|
res["error_code"] = "LEVEL_TOO_LOW"
|
|
res["message"] = "参选君主等级必须达到 Lv 50。"
|
|
return res
|
|
|
|
if player_gold < CANDIDACY_FEE:
|
|
res["error_code"] = "INSUFFICIENT_GOLD"
|
|
res["message"] = "报名参选需缴纳 1,000,000 金币保证金。"
|
|
return res
|
|
|
|
var emp_candidates: Array = candidates[empire]
|
|
if emp_candidates.size() >= MAX_CANDIDATES_PER_EMPIRE:
|
|
res["error_code"] = "CANDIDATES_FULL"
|
|
res["message"] = "本届君主候选人名额已满 (最多 8 人)。"
|
|
return res
|
|
|
|
for c in emp_candidates:
|
|
if c.pid == pid:
|
|
res["error_code"] = "ALREADY_REGISTERED"
|
|
res["message"] = "您已成功报名,无需重复登记。"
|
|
return res
|
|
|
|
var cand = CandidateInfo.new(pid, name, empire, level, manifesto)
|
|
emp_candidates.append(cand)
|
|
|
|
res["success"] = true
|
|
res["gold_cost"] = CANDIDACY_FEE
|
|
res["message"] = "恭喜!您已成功登记为第 %d 帝国君主候选人。" % empire
|
|
return res
|
|
|
|
# 3. 选民投票 (monarch_election)
|
|
func cast_vote(voter_pid: int, voter_empire: int, voter_level: int, target_candidate_pid: int) -> Dictionary:
|
|
var res := { "success": false, "error_code": "OK", "message": "" }
|
|
|
|
if election_states.get(voter_empire) != STATUS_VOTING:
|
|
res["error_code"] = "VOTING_NOT_OPEN"
|
|
res["message"] = "当前未处于全民投票大选阶段。"
|
|
return res
|
|
|
|
if voter_level < MIN_VOTER_LEVEL:
|
|
res["error_code"] = "VOTER_LEVEL_TOO_LOW"
|
|
res["message"] = "等级未达到 Lv 50,尚无大选投票权。"
|
|
return res
|
|
|
|
var records: Dictionary = voter_records[voter_empire]
|
|
if records.has(voter_pid):
|
|
res["error_code"] = "ALREADY_VOTED"
|
|
res["message"] = "您在本届选举中已经投过神圣的一票,不可重复投票。"
|
|
return res
|
|
|
|
# 查找目标候选人
|
|
var target_cand: CandidateInfo = null
|
|
for c in candidates[voter_empire]:
|
|
if c.pid == target_candidate_pid:
|
|
target_cand = c
|
|
break
|
|
|
|
if target_cand == null:
|
|
res["error_code"] = "CANDIDATE_NOT_FOUND"
|
|
res["message"] = "未找到该候选人。"
|
|
return res
|
|
|
|
# 计票
|
|
target_cand.votes += 1
|
|
records[voter_pid] = target_candidate_pid
|
|
|
|
res["success"] = true
|
|
res["message"] = "投票成功!感谢您支持候选人 %s。" % target_cand.name
|
|
return res
|
|
|
|
# 4. 统计揭晓并任命新君主
|
|
func conclude_election(empire: int) -> Dictionary:
|
|
var res := { "success": false, "winner_pid": 0, "winner_name": "", "votes": 0 }
|
|
if not election_states.has(empire):
|
|
return res
|
|
|
|
var emp_candidates: Array = candidates[empire]
|
|
if emp_candidates.is_empty():
|
|
election_states[empire] = STATUS_CLOSED
|
|
return res
|
|
|
|
# 选出最高票者
|
|
var top_cand: CandidateInfo = emp_candidates[0]
|
|
for c in emp_candidates:
|
|
if c.votes > top_cand.votes:
|
|
top_cand = c
|
|
|
|
# 就任君主
|
|
current_monarchs[empire]["pid"] = top_cand.pid
|
|
current_monarchs[empire]["name"] = top_cand.name
|
|
election_states[empire] = STATUS_CONCLUDED
|
|
|
|
res["success"] = true
|
|
res["winner_pid"] = top_cand.pid
|
|
res["winner_name"] = top_cand.name
|
|
res["votes"] = top_cand.votes
|
|
return res
|
|
|
|
# 5. 君主行使特权:调整帝国商业税率 (1% ~ 10%)
|
|
func set_tax_rate(caller_pid: int, empire: int, new_tax: int) -> Dictionary:
|
|
var res := { "success": false, "error_code": "OK", "message": "" }
|
|
var monarch = current_monarchs[empire]
|
|
|
|
if caller_pid != monarch["pid"]:
|
|
res["error_code"] = "NOT_MONARCH"
|
|
res["message"] = "只有现任帝国君主有权调整商业税率。"
|
|
return res
|
|
|
|
var clamped_tax: int = clamp(new_tax, 1, 10)
|
|
monarch["tax_rate"] = clamped_tax
|
|
|
|
res["success"] = true
|
|
res["tax_rate"] = clamped_tax
|
|
res["message"] = "帝国商业税率已调整为 %d%%。" % clamped_tax
|
|
return res
|
|
|
|
# 6. 君主行使特权:释放全帝国神佑祝福
|
|
func activate_monarch_blessing(caller_pid: int, empire: int) -> Dictionary:
|
|
var res := { "success": false, "error_code": "OK", "blessing_buff": {}, "message": "" }
|
|
var monarch = current_monarchs[empire]
|
|
|
|
if caller_pid != monarch["pid"]:
|
|
res["error_code"] = "NOT_MONARCH"
|
|
return res
|
|
|
|
res["success"] = true
|
|
res["blessing_buff"] = {
|
|
"exp_bonus": 10,
|
|
"att_bonus": 10,
|
|
"duration_sec": 7200
|
|
}
|
|
res["message"] = "君主神佑降临!帝国全体子民获得 +10% 经验与 +10% 攻击力加成 (持续2小时)。"
|
|
return res
|