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

169 lines
5.7 KiB
GDScript

# wedding_marriage_system.gd —— Metin2 40250 官方结婚典礼与夫妻同心传送婚戒系统 1:1
# 对照 40250 服务端 wedding.cpp, wedding.h, marriage.h, char_item.cpp
class_name WeddingMarriageSystem
extends RefCounted
signal wedding_started(spouse_name: String)
signal marriage_completed(spouse_name: String)
signal spouse_warped(target_map: String, target_pos: Vector2)
signal love_points_updated(new_points: int)
signal divorced()
const NPC_OLD_WOMAN := 9006 # 老妪/月老花仙子 (Old Woman)
const WEDDING_MAP_INDEX := 81 # 官方婚礼殿堂地图 (Wedding Map)
const VNUM_ENGAGEMENT_RING := 70301 # 订婚戒指
const VNUM_WEDDING_RING := 70302 # 结婚戒指 (支持一键夫妻瞬间传送)
const VNUM_TUXEDO := 11901 # 新郎礼服
const VNUM_WEDDING_DRESS := 11903 # 新娘婚纱
const VNUM_RING_OF_LOVE := 71070 # 爱情之戒 (夫妻经验加成)
const VNUM_BRACELET_OF_LOVE:= 71073 # 爱情手镯 (夫妻暴击加成)
# 婚姻状态
var is_married: bool = false
var spouse_name: String = ""
var love_points: int = 0 # 爱情度 (0~100)
var marry_timestamp: int = 0
# 举行结婚典礼 (40250 WeddingManager::Request)
func conduct_wedding(partner_name: String, inventory: Array, player_gender: String = "M") -> Dictionary:
if is_married:
return {"ok": false, "reason": "ALREADY_MARRIED", "msg": "已有婚姻关系,无法重复结婚!"}
if partner_name.strip_edges().is_empty():
return {"ok": false, "reason": "INVALID_PARTNER_NAME", "msg": "伴侣角色名称无效!"}
# 检查背包是否拥有订婚戒指 (70301)
var ring_slot := -1
for i in range(inventory.size()):
if inventory[i] != null and int(inventory[i].get("vnum", 0)) == VNUM_ENGAGEMENT_RING:
ring_slot = i
break
if ring_slot == -1:
return {"ok": false, "reason": "NO_ENGAGEMENT_RING", "msg": "需要持有【订婚戒指】(70301)方可举办婚礼!"}
# 检查是否穿戴礼服或背包携带礼服
var required_dress := VNUM_TUXEDO if player_gender == "M" else VNUM_WEDDING_DRESS
var has_dress := false
for i in range(inventory.size()):
if inventory[i] != null and int(inventory[i].get("vnum", 0)) == required_dress:
has_dress = true
break
if not has_dress:
var dress_name := "新郎礼服" if player_gender == "M" else "新娘婚纱"
return {"ok": false, "reason": "NO_WEDDING_DRESS", "msg": "举办婚礼需身着【%s】!" % dress_name}
# 消耗订婚戒指,替换为正式的【结婚戒指】(70302)
inventory[ring_slot] = {
"vnum": VNUM_WEDDING_RING,
"name": "结婚戒指",
"spouse": partner_name,
"count": 1
}
is_married = true
spouse_name = partner_name
love_points = 50 # 初始爱情度 50
marry_timestamp = int(Time.get_unix_time_from_system())
wedding_started.emit(spouse_name)
marriage_completed.emit(spouse_name)
return {
"ok": true,
"spouse_name": spouse_name,
"love_points": love_points,
"wedding_ring_slot": ring_slot,
"msg": "恭喜!您与【%s】在月老见证下喜结良缘,获得【结婚戒指】!" % spouse_name
}
# 使用结婚戒指瞬间传送至配偶身旁 (40250 char_item.cpp: Wedding Ring Warp)
func use_wedding_ring(spouse_online: bool, spouse_map: String, spouse_pos: Vector2, current_map: String) -> Dictionary:
if not is_married:
return {"ok": false, "reason": "NOT_MARRIED", "msg": "您当前未婚,无法使用结婚戒指!"}
if not spouse_online:
return {"ok": false, "reason": "SPOUSE_OFFLINE", "msg": "您的配偶当前不在线!"}
# 禁止传送的特殊限制地图 (如竞技场、武斗场)
if current_map == "Arena" or spouse_map == "Arena":
return {"ok": false, "reason": "FORBIDDEN_MAP", "msg": "比武擂台区域禁止使用结婚戒指传送!"}
spouse_warped.emit(spouse_map, spouse_pos)
return {
"ok": true,
"target_map": spouse_map,
"target_pos": spouse_pos,
"msg": "心灵相通!已跨越山海传送至配偶【%s】身侧。" % spouse_name
}
# 夫妻相伴增加爱情度 (40250 marriage.h: love_point)
func add_love_points(amount: int) -> void:
if not is_married:
return
love_points = clampi(love_points + amount, 0, 100)
love_points_updated.emit(love_points)
# 计算夫妻同心定情信物光环加成
func calculate_marriage_buff(has_ring_of_love: bool, has_bracelet_of_love: bool, is_near_spouse: bool) -> Dictionary:
if not is_married:
return {}
var buffs: Dictionary = {}
if is_near_spouse:
# 夫妻在 10 米范围内享有同心基础增益
buffs["couple_exp_bonus_pct"] = 10 + int(love_points / 10)
if has_ring_of_love:
buffs["couple_exp_bonus_pct"] += 20 # 爱情之戒再加 20%
if has_bracelet_of_love:
buffs["crit_pct"] = 10 # 爱情手镯增加 10% 暴击
return buffs
# 协议离婚 (40250 WeddingManager: End Marriage)
func divorce(inventory: Array) -> Dictionary:
if not is_married:
return {"ok": false, "reason": "NOT_MARRIED"}
# 回收/作废背包中的结婚戒指
for i in range(inventory.size()):
if inventory[i] != null and int(inventory[i].get("vnum", 0)) == VNUM_WEDDING_RING:
inventory[i] = null
break
var old_spouse := spouse_name
is_married = false
spouse_name = ""
love_points = 0
marry_timestamp = 0
divorced.emit()
return {
"ok": true,
"old_spouse": old_spouse,
"msg": "已与【%s】解除婚姻关系,收回结婚戒指。" % old_spouse
}
# 序列化
func serialize() -> Dictionary:
return {
"is_married": is_married,
"spouse_name": spouse_name,
"love_points": love_points,
"marry_timestamp": marry_timestamp
}
# 反序列化
func deserialize(data: Dictionary) -> void:
is_married = bool(data.get("is_married", false))
spouse_name = str(data.get("spouse_name", ""))
love_points = int(data.get("love_points", 0))
marry_timestamp = int(data.get("marry_timestamp", 0))