# event_manager_system.gd —— Metin2 40250 单人模式全服掉落活动与倍率调度器 1:1 # 对照 40250 服务端 cmd_gm.cpp (do_priv_empire), priv_manager.cpp, event.cpp, Items 50011, 50037 class_name EventManagerSystem extends RefCounted signal rate_changed(rate_type: String, multiplier: float) signal event_state_changed(event_name: String, is_active: bool) signal event_box_dropped(box_vnum: int, box_name: String) const ITEM_MOONLIGHT_BOX := 50011 # 月光宝盒 const ITEM_HEXAGONAL_BOX := 50037 # 六角宝盒 # 宝盒奖励奖池 (40250 官方 special_item_group.txt 1:1) const EVENT_BOX_LOOT: Dictionary = { 50011: [ {"vnum": 25040, "name": "祝福卷轴", "count": 1, "weight": 30}, {"vnum": 71084, "name": "高级附魔石", "count": 1, "weight": 25}, {"vnum": 71085, "name": "高级洗练石", "count": 1, "weight": 25}, {"vnum": 70024, "name": "祝福大理石", "count": 1, "weight": 10}, {"vnum": 72723, "name": "月之灵药(特)", "count": 1, "weight": 10} ], 50037: [ {"vnum": 72001, "name": "经验之戒", "count": 1, "weight": 20}, {"vnum": 72002, "name": "盗贼手套", "count": 1, "weight": 20}, {"vnum": 27102, "name": "绿药水(大)", "count": 5, "weight": 30}, {"vnum": 27105, "name": "紫药水(大)", "count": 5, "weight": 30} ] } var exp_rate: float = 1.0 var drop_rate: float = 1.0 var gold_rate: float = 1.0 var is_moonlight_active: bool = false var is_hexagonal_active: bool = false # 设置倍率 (exp, drop, gold) func set_rate(rate_type: String, multiplier: float) -> Dictionary: if multiplier < 1.0 or multiplier > 5.0: return {"ok": false, "reason": "INVALID_MULTIPLIER", "msg": "倍率必须在 1.0 到 5.0 之间!"} match rate_type: "exp": exp_rate = multiplier "drop": drop_rate = multiplier "gold": gold_rate = multiplier _: return {"ok": false, "reason": "INVALID_RATE_TYPE", "msg": "未知的倍率类型!"} rate_changed.emit(rate_type, multiplier) return { "ok": true, "rate_type": rate_type, "multiplier": multiplier, "msg": "全服【%s】倍率已设置为 %.1fx!" % [rate_type.to_upper(), multiplier] } # 开关经典限时掉落活动 func toggle_event(event_name: String, enable: bool) -> Dictionary: match event_name: "moonlight": is_moonlight_active = enable "hexagonal": is_hexagonal_active = enable _: return {"ok": false, "reason": "INVALID_EVENT_NAME", "msg": "未知的活动名称!"} event_state_changed.emit(event_name, enable) var state_str := "开启" if enable else "关闭" var ev_desc := "月光宝盒掉落狂欢" if event_name == "moonlight" else "六角宝盒冲级庆典" return { "ok": true, "event_name": event_name, "is_active": enable, "msg": "【%s】活动已%s!" % [ev_desc, state_str] } # 怪物击杀时判定额外活动宝盒掉落 func check_event_drops(mob_level: int, player_level: int, forced_roll: float = -1.0) -> Array: var drops: Array = [] # 等级差在 15 级以内才掉落活动宝盒 if absi(mob_level - player_level) > 15: return drops # 1. 月光宝盒判定 (基础概率 10% * drop_rate) if is_moonlight_active: var chance := 0.10 * drop_rate var roll := forced_roll if forced_roll >= 0.0 else randf() if roll <= chance: drops.append({"vnum": ITEM_MOONLIGHT_BOX, "name": "月光宝盒", "count": 1}) event_box_dropped.emit(ITEM_MOONLIGHT_BOX, "月光宝盒") # 2. 六角宝盒判定 (基础概率 15% * drop_rate) if is_hexagonal_active: var chance := 0.15 * drop_rate var roll := forced_roll if forced_roll >= 0.0 else randf() if roll <= chance: drops.append({"vnum": ITEM_HEXAGONAL_BOX, "name": "六角宝盒", "count": 1}) event_box_dropped.emit(ITEM_HEXAGONAL_BOX, "六角宝盒") return drops # 计算经倍率加成后的经验与金币 func calculate_exp(base_exp: int) -> int: return int(round(base_exp * exp_rate)) func calculate_gold(base_gold: int) -> int: return int(round(base_gold * gold_rate)) # 打开活动宝盒 func open_event_box(slot_idx: int, inventory: Array) -> Dictionary: if slot_idx < 0 or slot_idx >= inventory.size(): return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的背包栏位!"} var box = inventory[slot_idx] if box == null: return {"ok": false, "reason": "EMPTY_SLOT", "msg": "该格子上没有物品!"} var box_vnum: int = int(box.get("vnum", 0)) if not EVENT_BOX_LOOT.has(box_vnum): return {"ok": false, "reason": "NOT_AN_EVENT_BOX", "msg": "这不是活动宝盒!"} var pool: Array = EVENT_BOX_LOOT[box_vnum] # 权重抽奖 var total_weight := 0 for item in pool: total_weight += int(item.get("weight", 1)) var roll := randi_range(1, total_weight) var accumulated := 0 var reward: Dictionary = {} for item in pool: accumulated += int(item.get("weight", 1)) if roll <= accumulated: reward = item.duplicate() break if reward.is_empty(): reward = pool[0].duplicate() # 扣除宝盒,存入奖励 var cnt: int = int(box.get("count", 1)) if cnt <= 1: inventory[slot_idx] = null else: box["count"] = cnt - 1 var stored_slot := -1 for i in range(inventory.size()): if inventory[i] == null: inventory[i] = { "vnum": reward["vnum"], "name": reward["name"], "count": reward.get("count", 1) } stored_slot = i break return { "ok": true, "reward": reward, "stored_slot": stored_slot, "msg": "打开活动宝盒,获得了【%s x%d】!" % [reward["name"], reward.get("count", 1)] }