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