# biologist_quest_system.gd —— Metin2 40250 生物学研究家任务链与永久属性加成系统 1:1 # 对照 40250 服务端 collect_quest_lv30.quest ~ collect_quest_lv80.quest 及 char.cpp:ComputePoints class_name BiologistQuestSystem extends RefCounted const NPC_CHAEGIRAB := 20084 const ITEM_SPIRAL_POTION := 71035 # 灵魂秘药 / 研究家的秘药 const STAGES: Array[Dictionary] = [ { "id": 0, "name": "Lv30 兽人的牙齿研究", "required_level": 30, "collect_vnum": 30006, # 兽人齿 "collect_name": "兽人齿", "collect_count": 10, "soul_stone_vnum": 30220, # 兽人灵魂石 "soul_stone_name": "金龙之灵魂石", "reward_type": "move_speed", "reward_value": 10, "reward_title": "永久移动速度 +10" }, { "id": 1, "name": "Lv40 诅咒之书研究", "required_level": 40, "collect_vnum": 30047, # 诅咒书 "collect_name": "诅咒之书", "collect_count": 15, "soul_stone_vnum": 30221, # 诅咒灵魂石 "soul_stone_name": "神殿之灵魂石", "reward_type": "att_speed", "reward_value": 5, "reward_title": "永久攻击速度 +5" }, { "id": 2, "name": "Lv50 恶魔纪念物研究", "required_level": 50, "collect_vnum": 30015, # 恶魔纪念物 "collect_name": "恶魔纪念物", "collect_count": 15, "soul_stone_vnum": 30222, # 恶魔灵魂石 "soul_stone_name": "恶魔之灵魂石", "reward_type": "def_grade", "reward_value": 60, "reward_title": "永久防御力 +60" }, { "id": 3, "name": "Lv60 冰球研究", "required_level": 60, "collect_vnum": 30050, # 冰球 "collect_name": "冰球", "collect_count": 20, "soul_stone_vnum": 30223, # 冰球灵魂石 "soul_stone_name": "雪山之灵魂石", "reward_type": "att_grade", "reward_value": 50, "reward_title": "永久攻击力 +50" }, { "id": 4, "name": "Lv70 幽灵树枝研究", "required_level": 70, "collect_vnum": 30165, # 幽灵树枝 "collect_name": "幽灵树枝", "collect_count": 25, "soul_stone_vnum": 30224, # 幽灵灵魂石 "soul_stone_name": "鬼树之灵魂石", "reward_type": "damage_reduction", "reward_value": 10, "reward_title": "受到伤害永久减少 10%" }, { "id": 5, "name": "Lv80 图基石板研究", "required_level": 80, "collect_vnum": 30166, # 图基石板 "collect_name": "图基石板", "collect_count": 30, "soul_stone_vnum": 30225, # 图基灵魂石 "soul_stone_name": "图基之灵魂石", "reward_type": "att_grade_pct", "reward_value": 10, "reward_title": "物理攻击力永久提升 10%" } ] var player_level: int = 1 var current_stage_idx: int = -1 # -1: 未开始 var delivered_count: int = 0 var soul_stone_phase: bool = false var completed_stages: Array[int] = [] var cooldown_remaining: float = 0.0 var permanent_bonuses: Dictionary = { "move_speed": 0, "att_speed": 0, "def_grade": 0, "att_grade": 0, "damage_reduction": 0, "att_grade_pct": 0 } func init_for_player(level: int) -> void: player_level = level _check_available_stage() func _check_available_stage() -> void: if current_stage_idx >= 0 and current_stage_idx < STAGES.size(): return # 正在进行中 for i in range(STAGES.size()): if i not in completed_stages: var stage: Dictionary = STAGES[i] if player_level >= int(stage["required_level"]): current_stage_idx = i delivered_count = 0 soul_stone_phase = false break func get_current_stage() -> Dictionary: if current_stage_idx >= 0 and current_stage_idx < STAGES.size(): return STAGES[current_stage_idx] return {} func can_submit() -> bool: return current_stage_idx >= 0 and not soul_stone_phase and cooldown_remaining <= 0.0 func submit_item(inventory: Array, use_potion: bool = false, force_success: bool = false) -> Dictionary: if current_stage_idx < 0 or current_stage_idx >= STAGES.size(): return {"ok": false, "reason": "NO_ACTIVE_QUEST"} if soul_stone_phase: return {"ok": false, "reason": "NEED_SOUL_STONE"} if cooldown_remaining > 0.0 and not use_potion: return {"ok": false, "reason": "COOLDOWN_ACTIVE", "remaining": cooldown_remaining} var stage: Dictionary = STAGES[current_stage_idx] var req_vnum: int = int(stage["collect_vnum"]) # 查找收集物槽位 var item_slot := -1 for i in range(inventory.size()): var it = inventory[i] if it != null and int(it.get("vnum", 0)) == req_vnum: item_slot = i break if item_slot == -1: return {"ok": false, "reason": "MISSING_ITEM", "vnum": req_vnum} # 检查秘药 var potion_consumed := false if use_potion: for i in range(inventory.size()): var it = inventory[i] if it != null and int(it.get("vnum", 0)) == ITEM_SPIRAL_POTION: inventory[i] = null potion_consumed = true cooldown_remaining = 0.0 break # 扣除收集物 inventory[item_slot] = null # 成功判定 (默认 60%,秘药 90%) var success_chance: float = 0.90 if potion_consumed else 0.60 var roll: float = 0.0 if force_success else randf() var success := roll < success_chance if not success: cooldown_remaining = 0.0 if potion_consumed else 3600.0 # 1 小时 return { "ok": true, "success": false, "delivered": delivered_count, "target": stage["collect_count"], "msg": "样本已损坏,未能提取有效数据!" } # 成功交付 delivered_count += 1 var target_count: int = int(stage["collect_count"]) if delivered_count >= target_count: soul_stone_phase = true return { "ok": true, "success": true, "delivered": delivered_count, "target": target_count, "stage_completed": true, "msg": "研究样本收集完毕!接下来请获取专属灵魂石交付!" } else: cooldown_remaining = 0.0 if potion_consumed else 3600.0 return { "ok": true, "success": true, "delivered": delivered_count, "target": target_count, "stage_completed": false, "msg": "样本质量极佳!已记录样本数据。" } func submit_soul_stone(inventory: Array) -> Dictionary: if current_stage_idx < 0 or current_stage_idx >= STAGES.size(): return {"ok": false, "reason": "NO_ACTIVE_QUEST"} if not soul_stone_phase: return {"ok": false, "reason": "NOT_IN_SOUL_STONE_PHASE"} var stage: Dictionary = STAGES[current_stage_idx] var soul_vnum: int = int(stage["soul_stone_vnum"]) var found_slot := -1 for i in range(inventory.size()): var it = inventory[i] if it != null and int(it.get("vnum", 0)) == soul_vnum: found_slot = i break if found_slot == -1: return {"ok": false, "reason": "MISSING_SOUL_STONE", "vnum": soul_vnum} # 消耗灵魂石 inventory[found_slot] = null # 结算阶段奖励 var reward_type: String = String(stage["reward_type"]) var reward_val: int = int(stage["reward_value"]) permanent_bonuses[reward_type] = int(permanent_bonuses.get(reward_type, 0)) + reward_val completed_stages.append(current_stage_idx) var finished_stage_id: int = current_stage_idx current_stage_idx = -1 delivered_count = 0 soul_stone_phase = false cooldown_remaining = 0.0 # 检查下一阶段 _check_available_stage() return { "ok": true, "completed_stage": finished_stage_id, "reward_title": stage["reward_title"], "reward_type": reward_type, "reward_value": reward_val, "all_bonuses": permanent_bonuses.duplicate() } func get_quest_status() -> Dictionary: return { "player_level": player_level, "stage_idx": current_stage_idx, "stage_info": get_current_stage(), "delivered": delivered_count, "target": int(get_current_stage().get("collect_count", 0)), "soul_stone_phase": soul_stone_phase, "cooldown_remaining": cooldown_remaining, "completed_stages": completed_stages, "permanent_bonuses": permanent_bonuses }