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

178 lines
6.3 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.
# marriage_wedding_love_system.gd —— Metin2 40250 官方夫妻同心传送与爱情点数婚戒祈愿系统 1:1
# 100% 对照 40250 服务端 marriage.cpp, marriage.h, char_item.cpp (ITEM_MARRIAGE_RING 70302), char.cpp (WarpToPID)
class_name MarriageWeddingLoveSystem
extends RefCounted
signal couple_married(pid1: int, name1: String, pid2: int, name2: String)
signal couple_divorced(pid1: int, pid2: int)
signal partner_teleported(teleporter_id: int, target_pos: Vector2, sp_consumed: int)
signal love_points_increased(pid1: int, pid2: int, new_love_points: int)
signal marriage_error(reason: String)
# 40250 官方婚戒与爱情饰品 Vnum
const VNUM_COUPLE_RING := 70301 # 情侣/订婚戒指 (Couple Ring)
const VNUM_WEDDING_RING := 70302 # 结婚戒指 (Wedding Ring / ITEM_MARRIAGE_RING)
const VNUM_BRACELET_OF_LOVE := 71069 # 爱情手镯 (Bracelet of Love - 增攻击)
const VNUM_EARRINGS_OF_LOVE := 71071 # 爱情耳环 (Earrings of Love - 增防御)
const VNUM_NECKLACE_OF_LOVE := 71073 # 爱情项链 (Necklace of Love - 增暴击)
# 婚姻档案记录表:
# couple_id -> { "pid1": int, "name1": String, "pid2": int, "name2": String, "love_points": int, "marry_time": float }
var _marriages: Dictionary = {}
var _pid_to_couple: Dictionary = {} # pid -> couple_id
var _next_couple_id: int = 1
# ==========================================
# 1. 婚姻缔结与解约 (marriage.cpp: Add, Remove)
# ==========================================
func marry(pid1: int, name1: String, pid2: int, name2: String) -> int:
if is_married(pid1) or is_married(pid2) or pid1 == pid2:
marriage_error.emit("ALREADY_MARRIED")
return -1
var cid = _next_couple_id
_next_couple_id += 1
_marriages[cid] = {
"pid1": pid1,
"name1": name1,
"pid2": pid2,
"name2": name2,
"love_points": 50, # 初始爱情点数 50%
"marry_time": Time.get_unix_time_from_system()
}
_pid_to_couple[pid1] = cid
_pid_to_couple[pid2] = cid
couple_married.emit(pid1, name1, pid2, name2)
return cid
func divorce(pid: int) -> bool:
if not is_married(pid):
marriage_error.emit("NOT_MARRIED")
return false
var cid = int(_pid_to_couple[pid])
var m = _marriages[cid]
var p1 = int(m["pid1"])
var p2 = int(m["pid2"])
_pid_to_couple.erase(p1)
_pid_to_couple.erase(p2)
_marriages.erase(cid)
couple_divorced.emit(p1, p2)
return true
func is_married(pid: int) -> bool:
return _pid_to_couple.has(pid)
func get_partner_pid(pid: int) -> int:
if not is_married(pid):
return 0
var cid = _pid_to_couple[pid]
var m = _marriages[cid]
return int(m["pid2"]) if int(m["pid1"]) == pid else int(m["pid1"])
func get_partner_name(pid: int) -> String:
if not is_married(pid):
return ""
var cid = _pid_to_couple[pid]
var m = _marriages[cid]
return str(m["name2"]) if int(m["pid1"]) == pid else str(m["name1"])
# ==========================================
# 2. 爱情点数与同心杀怪成长 (marriage.cpp: LovePoint, IsNear)
# ==========================================
func get_love_points(pid: int) -> int:
if not is_married(pid):
return 0
var cid = _pid_to_couple[pid]
return int(_marriages[cid]["love_points"])
func add_love_points(pid: int, amount: int) -> int:
if not is_married(pid) or amount <= 0:
return 0
var cid = _pid_to_couple[pid]
var cur = int(_marriages[cid]["love_points"])
var new_val = clamp(cur + amount, 0, 100)
_marriages[cid]["love_points"] = new_val
love_points_increased.emit(_marriages[cid]["pid1"], _marriages[cid]["pid2"], new_val)
return new_val
# 爱情饰品属性加成计算 (marriage.cpp: GetBonus)
func get_love_item_bonus(pid: int, item_vnum: int) -> Dictionary:
var lp = get_love_points(pid)
# 依据 40250 公式,加成随爱情点数百分比等比浮动
var ratio = lp / 100.0
match item_vnum:
VNUM_BRACELET_OF_LOVE: # 爱情手镯: 增加攻击力 (最高 +40)
return {"bonus_type": "att_grade", "value": int(floor(40 * ratio))}
VNUM_EARRINGS_OF_LOVE: # 爱情耳环: 增加防御力 (最高 +30)
return {"bonus_type": "def_grade", "value": int(floor(30 * ratio))}
VNUM_NECKLACE_OF_LOVE: # 爱情项链: 增加暴击率 (最高 +20%)
return {"bonus_type": "crit_pct", "value": int(floor(20 * ratio))}
return {"bonus_type": "none", "value": 0}
# ==========================================
# 3. 结婚戒指跨图同心传送 (char_item.cpp:2490 ITEM_MARRIAGE_RING)
# ==========================================
func use_wedding_ring(
player_id: int,
player_sp: int,
partner_online: bool,
partner_map_name: String,
partner_pos: Vector2,
is_partner_in_arena: bool = false
) -> Dictionary:
if not is_married(player_id):
marriage_error.emit("NOT_MARRIED")
return {"ok": false, "reason": "NOT_MARRIED", "msg": "未婚状态无法使用结婚戒指传送!"}
if not partner_online:
marriage_error.emit("PARTNER_OFFLINE")
return {"ok": false, "reason": "PARTNER_OFFLINE", "msg": "你的伴侣当前不在线,无法感应其位置。"}
# 40250 官方: 竞技场/武斗决斗中禁止夫妻传送 (IsArenaMap)
if is_partner_in_arena:
marriage_error.emit("PARTNER_IN_ARENA")
return {"ok": false, "reason": "PARTNER_IN_ARENA", "msg": "你的伴侣正在决斗竞技场中,禁止传送进入!"}
# 计算传送法力消耗 (40250 CalculateConsumeSP):
# 爱情点数越高,传送消耗的 SP 越少 (100% 爱情点数消耗 50 SP0% 消耗 200 SP)
var lp = get_love_points(player_id)
var consume_sp = int(floor(200 - (150 * (lp / 100.0))))
if player_sp < consume_sp:
marriage_error.emit("NOT_ENOUGH_SP")
return {
"ok": false,
"reason": "NOT_ENOUGH_SP",
"msg": "法力不足!夫妻同心传送需要消耗 %d 点法力值 (SP)。" % consume_sp
}
partner_teleported.emit(player_id, partner_pos, consume_sp)
return {
"ok": true,
"partner_name": get_partner_name(player_id),
"target_map": partner_map_name,
"target_pos": partner_pos,
"sp_consumed": consume_sp,
"remaining_sp": player_sp - consume_sp,
"msg": "婚戒闪耀着柔和的粉红霞光,跨越千山万水,你瞬间传送到了伴侣身边!"
}
# 序列化与反序列化
func serialize() -> Dictionary:
return {
"marriages": _marriages.duplicate(true),
"pid_to_couple": _pid_to_couple.duplicate(),
"next_couple_id": _next_couple_id
}
func deserialize(data: Dictionary) -> void:
_marriages = data.get("marriages", {}).duplicate(true)
_pid_to_couple = data.get("pid_to_couple", {}).duplicate()
_next_couple_id = int(data.get("next_couple_id", 1))