# compass_treasure_hunt_system.gd —— Metin2 40250 官方魔石地脉探测罗盘与寻宝系统 1:1 # 对照 40250 服务端 questlua_item.cpp, locale_en/itemdesc.txt:27989 class_name CompassTreasureHuntSystem extends RefCounted signal compass_scanned(target_name: String, distance: float, angle_deg: float, proximity: String, charges_left: int) signal treasure_excavated(chest_vnum: int, chest_name: String) signal treasure_opened(chest_name: String, rewards: Array) # 40250 官方核心道具 Vnum const VNUM_COMPASS_OF_METIN := 27989 # 魔石探测罗盘 (Compass for Metin Stones, 6次充能) const VNUM_GOLD_CHEST := 50005 # 黄金宝箱 const VNUM_SILVER_CHEST := 50006 # 白银宝箱 const VNUM_GOLD_KEY := 50007 # 黄金钥匙 const VNUM_SILVER_KEY := 50008 # 白银钥匙 const MAX_CHARGES := 6 # 探测距离等级 const PROXIMITY_FAR := "FAR" # > 100m ("微弱微光 - 遥远") const PROXIMITY_MEDIUM := "MEDIUM" # 50m ~ 100m ("泛起银光 - 渐近") const PROXIMITY_NEAR := "NEAR" # 20m ~ 50m ("强烈明光 - 附近") const PROXIMITY_VERY_CLOSE := "VERY_CLOSE" # < 20m ("璀璨耀光 - 极近") # 激活魔石探测罗盘扫描最近的魔石或地脉宝藏 func scan_nearest_metin(player_pos: Vector2, target_list: Array, inventory: Array, slot_index: int) -> Dictionary: if slot_index < 0 or slot_index >= inventory.size() or inventory[slot_index] == null: return {"ok": false, "reason": "INVALID_SLOT"} var compass: Dictionary = inventory[slot_index] if int(compass.get("vnum", 0)) != VNUM_COMPASS_OF_METIN: return {"ok": false, "reason": "NOT_A_COMPASS", "msg": "该物品不是魔石探测罗盘!"} var charges: int = int(compass.get("charges", MAX_CHARGES)) if charges <= 0: inventory[slot_index] = null return {"ok": false, "reason": "COMPASS_EXHAUSTED", "msg": "罗盘灵力已耗尽,化作飞灰!"} if target_list.is_empty(): return {"ok": false, "reason": "NO_TARGETS_FOUND", "msg": "当前地图未探测到任何魔石波动!"} # 寻找最近的目标 var nearest_target: Dictionary = {} var min_dist := INF for t in target_list: var pos: Vector2 = t.get("pos", Vector2.ZERO) var d := player_pos.distance_to(pos) if d < min_dist: min_dist = d nearest_target = t # 扣除 1 次充能计数 (40250 官方 6 次限制) charges -= 1 compass["charges"] = charges if charges <= 0: inventory[slot_index] = null # 充能用完销毁 # 计算方位角与距离等级 var target_pos: Vector2 = nearest_target.get("pos", Vector2.ZERO) var dir_vec: Vector2 = (target_pos - player_pos).normalized() var angle := rad_to_deg(dir_vec.angle()) var proximity := PROXIMITY_FAR var desc := "微弱微光(遥远)" if min_dist < 20.0: proximity = PROXIMITY_VERY_CLOSE desc = "璀璨耀光(极近)" elif min_dist < 50.0: proximity = PROXIMITY_NEAR desc = "强烈明光(附近)" elif min_dist < 100.0: proximity = PROXIMITY_MEDIUM desc = "泛起银光(渐近)" var target_name: String = str(nearest_target.get("name", "魔石")) compass_scanned.emit(target_name, min_dist, angle, proximity, charges) return { "ok": true, "target_name": target_name, "target_pos": nearest_target.get("pos", Vector2.ZERO), "distance": min_dist, "angle_deg": angle, "proximity": proximity, "proximity_desc": desc, "charges_left": charges, "msg": "罗盘指针震颤!锁定【%s】,距离 %.1f 米,光芒等级:%s,剩余使用次数:%d。" % [target_name, min_dist, desc, charges] } # 在地脉极近坐标处挖掘秘宝 func excavate_treasure(player_pos: Vector2, treasure_nodes: Array, inventory: Array) -> Dictionary: var target_idx := -1 for i in range(treasure_nodes.size()): var node_pos: Vector2 = treasure_nodes[i].get("pos", Vector2.ZERO) if player_pos.distance_to(node_pos) <= 5.0: # 5米范围内可挖掘 target_idx = i break if target_idx == -1: return {"ok": false, "reason": "NO_TREASURE_NEARBY", "msg": "周围 5 米内未探测到地脉埋藏的宝箱!"} var node: Dictionary = treasure_nodes[target_idx] var is_gold: bool = bool(node.get("is_gold", false)) var chest_vnum = VNUM_GOLD_CHEST if is_gold else VNUM_SILVER_CHEST var chest_name = "黄金宝箱" if is_gold else "白银宝箱" # 寻找背包空格放入宝箱 var free_slot := -1 for i in range(inventory.size()): if inventory[i] == null: free_slot = i break if free_slot == -1: return {"ok": false, "reason": "INVENTORY_FULL", "msg": "背包已满,无法容纳挖出的宝箱!"} inventory[free_slot] = { "vnum": chest_vnum, "name": chest_name, "count": 1 } treasure_nodes.remove_at(target_idx) treasure_excavated.emit(chest_vnum, chest_name) return { "ok": true, "chest_vnum": chest_vnum, "chest_name": chest_name, "slot": free_slot, "msg": "破土而出!在地脉深处成功发掘出【%s】!" % chest_name } # 使用对应的黄金钥匙或白银钥匙开启宝箱 func open_treasure_chest(chest_slot: int, key_slot: int, inventory: Array) -> Dictionary: if chest_slot < 0 or chest_slot >= inventory.size() or inventory[chest_slot] == null: return {"ok": false, "reason": "INVALID_CHEST_SLOT"} if key_slot < 0 or key_slot >= inventory.size() or inventory[key_slot] == null: return {"ok": false, "reason": "INVALID_KEY_SLOT"} var chest: Dictionary = inventory[chest_slot] var key: Dictionary = inventory[key_slot] var chest_vnum := int(chest.get("vnum", 0)) var key_vnum := int(key.get("vnum", 0)) var match_ok := false if chest_vnum == VNUM_GOLD_CHEST and key_vnum == VNUM_GOLD_KEY: match_ok = true elif chest_vnum == VNUM_SILVER_CHEST and key_vnum == VNUM_SILVER_KEY: match_ok = true if not match_ok: return {"ok": false, "reason": "KEY_MISMATCH", "msg": "钥匙与宝箱类型不匹配!黄金宝箱需用黄金钥匙开启。"} # 消耗钥匙与宝箱 inventory[chest_slot] = null var key_cnt := int(key.get("count", 1)) if key_cnt > 1: key["count"] = key_cnt - 1 else: inventory[key_slot] = null # 生成高额宝物奖励 var rewards: Array = [] if chest_vnum == VNUM_GOLD_CHEST: rewards = [ {"vnum": 25040, "name": "祝福卷轴", "count": 3}, {"vnum": 51501, "name": "龙石原石", "count": 2}, {"vnum": 28430, "name": "+4 灵石", "count": 1} ] else: rewards = [ {"vnum": 25040, "name": "祝福卷轴", "count": 1}, {"vnum": 50300, "name": "技能书", "count": 1} ] # 放入背包 for r in rewards: for i in range(inventory.size()): if inventory[i] == null: inventory[i] = r.duplicate(true) break treasure_opened.emit(chest.get("name", "宝箱"), rewards) return { "ok": true, "chest_name": chest.get("name", "宝箱"), "rewards": rewards, "msg": "清脆的开锁声响起!开启宝箱,获得珍稀秘宝!" }