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

191 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# solo_guild_domain_system.gd —— Metin2 40250 离线单机个人公会领地与专属建筑建造 1:1
# 对照 40250 服务端 guild.cpp, guild_manager.cpp, building.cpp
class_name SoloGuildDomainSystem
extends RefCounted
signal domain_claimed(guild_name: String, cost: int)
signal building_constructed(btype: int, bname: String, level: int)
signal building_upgraded(btype: int, new_level: int)
signal guild_refine_result(success: bool, new_level: int)
enum BuildingType {
BLACKSMITH = 1, # 公会神兵铁匠 (强化成功率 +10%)
ALCHEMIST = 2, # 公会炼金熔炉 (原矿熔炼成功率 70%)
GUARDIAN_ALTAR = 3 # 公会守护神坛 (生命 +1500, 经验 +10%)
}
const DOMAIN_COST := 20000000 # 2000 万金币
const MIN_GUILD_LEVEL := 10 # 公会等级门槛
const BUILDING_BLUEPRINTS: Dictionary = {
BuildingType.BLACKSMITH: {
"name": "公会神兵铁匠铺",
"cost": 10000000,
"wood": 100,
"stone": 100,
"max_level": 3
},
BuildingType.ALCHEMIST: {
"name": "公会炼金大师熔炉",
"cost": 8000000,
"wood": 80,
"stone": 80,
"max_level": 3
},
BuildingType.GUARDIAN_ALTAR: {
"name": "公会守护天龙神坛",
"cost": 15000000,
"wood": 150,
"stone": 150,
"max_level": 3
}
}
var has_domain: bool = false
var guild_name: String = ""
var guild_level: int = 1
var constructed_buildings: Dictionary = {} # btype -> { "level": int, "name": String }
# 开辟专属公会领地
func claim_domain(gname: String, glevel: int, player_data: Dictionary) -> Dictionary:
if has_domain:
return {"ok": false, "reason": "ALREADY_HAS_DOMAIN", "msg": "你已拥有一片公会领地!"}
if glevel < MIN_GUILD_LEVEL:
return {"ok": false, "reason": "GUILD_LEVEL_TOO_LOW", "msg": "开辟领地需要公会等级达到 10 级!"}
var gold: int = int(player_data.get("gold", 0))
if gold < DOMAIN_COST:
return {"ok": false, "reason": "NOT_ENOUGH_GOLD", "msg": "金币不足!需要 20,000,000 金币买下领地地契。"}
player_data["gold"] = gold - DOMAIN_COST
has_domain = true
guild_name = gname
guild_level = glevel
constructed_buildings.clear()
domain_claimed.emit(guild_name, DOMAIN_COST)
return {
"ok": true,
"guild_name": guild_name,
"cost": DOMAIN_COST,
"remaining_gold": player_data["gold"],
"msg": "【%s】公会专属领地落成!城堡旗帜高高飘扬!" % guild_name
}
# 建造领地建筑
func construct_building(btype: int, player_data: Dictionary) -> Dictionary:
if not has_domain:
return {"ok": false, "reason": "NO_DOMAIN", "msg": "尚未开辟领地!"}
if not BUILDING_BLUEPRINTS.has(btype):
return {"ok": false, "reason": "INVALID_BUILDING_TYPE"}
if constructed_buildings.has(btype):
return {"ok": false, "reason": "ALREADY_CONSTRUCTED", "msg": "该建筑已建造完成!可选择升级。"}
var bp = BUILDING_BLUEPRINTS[btype]
var gold: int = int(player_data.get("gold", 0))
var cost: int = bp["cost"]
if gold < cost:
return {"ok": false, "reason": "NOT_ENOUGH_GOLD", "msg": "建造金币不足!需要 %d 金币。" % cost}
player_data["gold"] = gold - cost
constructed_buildings[btype] = {
"type": btype,
"name": bp["name"],
"level": 1
}
building_constructed.emit(btype, bp["name"], 1)
return {
"ok": true,
"type": btype,
"name": bp["name"],
"level": 1,
"msg": "【%s】在公会领地拔地而起,正式竣工!" % bp["name"]
}
# 升级领地建筑
func upgrade_building(btype: int, player_data: Dictionary) -> Dictionary:
if not constructed_buildings.has(btype):
return {"ok": false, "reason": "NOT_CONSTRUCTED_YET"}
var b = constructed_buildings[btype]
var cur_lvl: int = b["level"]
var bp = BUILDING_BLUEPRINTS[btype]
if cur_lvl >= bp["max_level"]:
return {"ok": false, "reason": "MAX_LEVEL_REACHED", "msg": "该建筑已达最高等级 (3级)"}
var upgrade_cost: int = int(bp["cost"] * 0.8)
var gold: int = int(player_data.get("gold", 0))
if gold < upgrade_cost:
return {"ok": false, "reason": "NOT_ENOUGH_GOLD", "msg": "升级金币不足!需要 %d 金币。" % upgrade_cost}
player_data["gold"] = gold - upgrade_cost
b["level"] = cur_lvl + 1
building_upgraded.emit(btype, b["level"])
return {
"ok": true,
"type": btype,
"new_level": b["level"],
"msg": "【%s】成功升级至等级 %d" % [b["name"], b["level"]]
}
# 公会铁匠专属精炼 (+10% 强化成功率)
func guild_blacksmith_refine(item: Dictionary, player_data: Dictionary, force_success: bool = false) -> Dictionary:
if not constructed_buildings.has(BuildingType.BLACKSMITH):
return {"ok": false, "reason": "BLACKSMITH_NOT_BUILT", "msg": "领地尚未建造公会铁匠铺!"}
var cur_refine: int = int(item.get("refine_level", 0))
if cur_refine >= 9:
return {"ok": false, "reason": "ALREADY_MAX_REFINE", "msg": "装备强化等级已达 +9 极限!"}
# 官方基准成功率 + 公会铁匠额外 +10% 成功率 (随铁匠等级提升)
var b_lvl: int = constructed_buildings[BuildingType.BLACKSMITH]["level"]
var bonus_rate: int = 10 + (b_lvl - 1) * 3 # Lv1: +10%, Lv2: +13%, Lv3: +16%
var base_rates := [100, 90, 80, 70, 60, 50, 40, 30, 20] # +1~+9
var rate = min(100, base_rates[cur_refine] + bonus_rate)
var roll = randi_range(1, 100)
var is_succ = force_success or (roll <= rate)
if is_succ:
item["refine_level"] = cur_refine + 1
guild_refine_result.emit(true, item["refine_level"])
return {
"ok": true,
"success": true,
"new_level": item["refine_level"],
"msg": "公会神兵铁匠挥锤如电!精炼成功,装备提升至 +%d" % item["refine_level"]
}
else:
# 失败降 1 级 (最低 0)
item["refine_level"] = max(0, cur_refine - 1)
guild_refine_result.emit(false, item["refine_level"])
return {
"ok": true,
"success": false,
"new_level": item["refine_level"],
"msg": "精炼失败!铁砧迸出火星,装备跌落至 +%d。" % item["refine_level"]
}
# 公会守护神坛常驻被动光环
func get_altar_passive_buffs() -> Dictionary:
if not constructed_buildings.has(BuildingType.GUARDIAN_ALTAR):
return {}
var b_lvl: int = constructed_buildings[BuildingType.GUARDIAN_ALTAR]["level"]
return {
"max_hp": 1500 * b_lvl,
"exp_rate_pct": 10 * b_lvl,
"all_stats": 5 * b_lvl
}