# horse_level_growth_system.gd —— Metin2 40250 官方马匹等级进化与养殖草料喂食系统 1:1 # 对照 40250 服务端 horse_rider.cpp, char_horse.cpp, constants.h class_name HorseLevelGrowthSystem extends RefCounted signal horse_level_up(new_level: int, tier_name: String) signal horse_fed(new_health: int, max_health: int) signal horse_revived(new_health: int) signal horse_mounted(horse_level: int, stats_applied: Dictionary) signal horse_dismounted() signal stamina_exhausted() # 40250 官方马牌与召唤道具 const VNUM_HORSE_PICTURE := 50051 # 马牌 (初级小马召唤道具) const VNUM_ARMOR_HORSE_BOOK := 50052 # 战马书 (战斗装甲马召唤道具) const VNUM_MILITARY_HORSE_BOOK:= 50053 # 军马令 (军阶战马召唤道具) # 40250 官方草料饲料 const VNUM_HAY := 50054 # 干草 (初级小马食物) const VNUM_CARROT := 50055 # 胡萝卜 (战斗装甲马食物) const VNUM_RED_GINSENG := 50056 # 红参 (军阶战马食物) # 40250 官方复活猴草 const VNUM_HERB_EASY := 50057 # 初级猴草 (复活初级小马) const VNUM_HERB_NORMAL := 50058 # 中级猴草 (复活战斗装甲马) const VNUM_HERB_EXPERT := 50059 # 高级猴草 (复活军阶战马) # 3 大战马阶层定义 const TIER_NONE := 0 const TIER_BEGINNER := 1 # 1~10 级 初级小马 (Race 20101, 人物需求 Lv 25) const TIER_COMBAT := 2 # 11~20 级 战斗装甲马 (Race 20104, 人物需求 Lv 35) const TIER_MILITARY := 3 # 21~30 级 军阶战马 (Race 20107, 人物需求 Lv 50) # 40250 官方经典等级属性表 (选样代表级与通用成长模型) const HORSE_STAT_TABLE: Dictionary = { 1: {"min_lv": 25, "race": 20101, "max_health": 3, "max_stamina": 4, "st": 26, "dx": 35, "ht": 18, "iq": 9, "atk": 54, "def": 32}, 5: {"min_lv": 25, "race": 20101, "max_health": 8, "max_stamina": 6, "st": 30, "dx": 40, "ht": 20, "iq": 10, "atk": 58, "def": 34}, 10: {"min_lv": 25, "race": 20101, "max_health": 15, "max_stamina": 10, "st": 35, "dx": 46, "ht": 23, "iq": 12, "atk": 63, "def": 37}, 11: {"min_lv": 35, "race": 20104, "max_health": 18, "max_stamina": 30, "st": 40, "dx": 53, "ht": 27, "iq": 13, "atk": 69, "def": 41}, 15: {"min_lv": 35, "race": 20104, "max_health": 24, "max_stamina": 55, "st": 44, "dx": 58, "ht": 29, "iq": 15, "atk": 73, "def": 43}, 20: {"min_lv": 35, "race": 20104, "max_health": 32, "max_stamina": 100, "st": 48, "dx": 64, "ht": 32, "iq": 16, "atk": 78, "def": 46}, 21: {"min_lv": 50, "race": 20107, "max_health": 35, "max_stamina": 120, "st": 53, "dx": 71, "ht": 36, "iq": 18, "atk": 84, "def": 50}, 25: {"min_lv": 50, "race": 20107, "max_health": 40, "max_stamina": 140, "st": 60, "dx": 80, "ht": 40, "iq": 20, "atk": 91, "def": 54}, 30: {"min_lv": 50, "race": 20107, "max_health": 50, "max_stamina": 200, "st": 67, "dx": 89, "ht": 45, "iq": 22, "atk": 99, "def": 59} } var horse_level: int = 0 var current_health: int = 0 var current_stamina: int = 0 var is_mounted: bool = false var applied_stats: Dictionary = {} # 获取当前战马阶层 func get_tier() -> int: if horse_level <= 0: return TIER_NONE elif horse_level <= 10: return TIER_BEGINNER elif horse_level <= 20: return TIER_COMBAT else: return TIER_MILITARY # 获取阶层名称 func get_tier_name() -> String: match get_tier(): TIER_BEGINNER: return "初级小马" TIER_COMBAT: return "战斗装甲马" TIER_MILITARY: return "军阶战马" _: return "无战马" # 获取对应等级的基础属性 func get_horse_stat(lv: int) -> Dictionary: if HORSE_STAT_TABLE.has(lv): return HORSE_STAT_TABLE[lv].duplicate(true) # 近似插值计算 var base_lv := 1 for k in HORSE_STAT_TABLE.keys(): if int(k) <= lv and int(k) > base_lv: base_lv = int(k) return HORSE_STAT_TABLE[base_lv].duplicate(true) # 初始化/领养战马 func initialize_horse(level: int = 1) -> void: horse_level = clampi(level, 1, 30) var stat = get_horse_stat(horse_level) current_health = int(stat["max_health"]) current_stamina = int(stat["max_stamina"]) # 马匹等级升级 func upgrade_horse_level(char_level: int) -> Dictionary: if horse_level >= 30: return {"ok": false, "reason": "MAX_HORSE_LEVEL", "msg": "战马已达最高等级 30 级!"} var target_lv := horse_level + 1 var target_stat := get_horse_stat(target_lv) var req_char_lv := int(target_stat["min_lv"]) if char_level < req_char_lv: return { "ok": false, "reason": "CHAR_LEVEL_TOO_LOW", "msg": "角色等级不足!晋升至 %d 级战马需要角色达到 %d 级。" % [target_lv, req_char_lv] } horse_level = target_lv var new_stat := get_horse_stat(horse_level) current_health = int(new_stat["max_health"]) current_stamina = int(new_stat["max_stamina"]) horse_level_up.emit(horse_level, get_tier_name()) return { "ok": true, "horse_level": horse_level, "tier": get_tier_name(), "msg": "战马成功进阶至 %d 级【%s】!" % [horse_level, get_tier_name()] } # 喂食草料恢复健康值 (40250 horse_rider.cpp:FeedHorse) func feed_horse(inventory: Array, slot_index: int) -> Dictionary: if horse_level <= 0: return {"ok": false, "reason": "NO_HORSE"} if current_health <= 0: return {"ok": false, "reason": "HORSE_IS_DEAD", "msg": "战马已重伤休克!需要喂食复活猴草唤醒。"} var stat := get_horse_stat(horse_level) var max_hp := int(stat["max_health"]) if current_health >= max_hp: return {"ok": false, "reason": "HEALTH_ALREADY_FULL", "msg": "战马饱食度与健康值已满!"} if slot_index < 0 or slot_index >= inventory.size() or inventory[slot_index] == null: return {"ok": false, "reason": "INVALID_SLOT"} var food: Dictionary = inventory[slot_index] var food_vnum := int(food.get("vnum", 0)) var tier := get_tier() # 校验草料与阶层匹配 (40250 规则: 初级吃干草, 战斗吃胡萝卜, 军马吃红参) var match_ok := false if tier == TIER_BEGINNER and food_vnum == VNUM_HAY: match_ok = true elif tier == TIER_COMBAT and food_vnum == VNUM_CARROT: match_ok = true elif tier == TIER_MILITARY and food_vnum == VNUM_RED_GINSENG: match_ok = true if not match_ok: return {"ok": false, "reason": "FOOD_TIER_MISMATCH", "msg": "该饲料与当前战马阶层不匹配!"} # 消耗 1 份食物 var cnt := int(food.get("count", 1)) if cnt > 1: food["count"] = cnt - 1 else: inventory[slot_index] = null current_health = mini(max_hp, current_health + 2) horse_fed.emit(current_health, max_hp) return { "ok": true, "current_health": current_health, "max_health": max_hp, "msg": "战马享用了饲料,健康值恢复至 %d/%d!" % [current_health, max_hp] } # 使用猴草唤醒复活战马 func revive_horse(inventory: Array, slot_index: int) -> Dictionary: if current_health > 0: return {"ok": false, "reason": "HORSE_NOT_DEAD", "msg": "战马依然健在,无需使用复活猴草!"} if slot_index < 0 or slot_index >= inventory.size() or inventory[slot_index] == null: return {"ok": false, "reason": "INVALID_SLOT"} var herb: Dictionary = inventory[slot_index] var herb_vnum := int(herb.get("vnum", 0)) var tier := get_tier() var herb_ok := false if tier == TIER_BEGINNER and herb_vnum == VNUM_HERB_EASY: herb_ok = true elif tier == TIER_COMBAT and herb_vnum == VNUM_HERB_NORMAL: herb_ok = true elif tier == TIER_MILITARY and herb_vnum == VNUM_HERB_EXPERT: herb_ok = true if not herb_ok: return {"ok": false, "reason": "HERB_TIER_MISMATCH", "msg": "复活猴草阶层与战马不匹配!"} # 消耗 1 株猴草 var cnt := int(herb.get("count", 1)) if cnt > 1: herb["count"] = cnt - 1 else: inventory[slot_index] = null var stat := get_horse_stat(horse_level) current_health = maxi(1, int(stat["max_health"]) / 2) current_stamina = maxi(1, int(stat["max_stamina"]) / 2) horse_revived.emit(current_health) return { "ok": true, "health": current_health, "msg": "灵草生效!战马重焕生机复活苏醒。" } # 骑乘上马 func mount_horse(player_stats: Dictionary) -> Dictionary: if horse_level <= 0: return {"ok": false, "reason": "NO_HORSE", "msg": "尚未获得战马!"} if current_health <= 0: return {"ok": false, "reason": "HORSE_IS_DEAD", "msg": "战马已昏迷,需先喂食猴草复活!"} if is_mounted: return {"ok": false, "reason": "ALREADY_MOUNTED"} var stat := get_horse_stat(horse_level) # 注入战马加成 applied_stats = { "attack_power": int(stat["atk"]), "defense": int(stat["def"]) } for k in applied_stats.keys(): player_stats[k] = int(player_stats.get(k, 0)) + applied_stats[k] is_mounted = true horse_mounted.emit(horse_level, applied_stats) return {"ok": true, "msg": "成功乘骑战马!人马合一属性生效。"} # 下马 func dismount_horse(player_stats: Dictionary) -> Dictionary: if not is_mounted: return {"ok": false, "reason": "NOT_MOUNTED"} for k in applied_stats.keys(): player_stats[k] = int(player_stats.get(k, 0)) - applied_stats[k] applied_stats.clear() is_mounted = false horse_dismounted.emit() return {"ok": true, "msg": "已安全下马。"} # 消耗耐力 func consume_stamina(amount: int, player_stats: Dictionary) -> void: if not is_mounted: return current_stamina = maxi(0, current_stamina - amount) if current_stamina <= 0: stamina_exhausted.emit() dismount_horse(player_stats) # 耐力耗尽自动下马 # 恢复耐力 func recover_stamina(amount: int) -> void: var stat := get_horse_stat(horse_level) var max_stamina := int(stat["max_stamina"]) current_stamina = mini(max_stamina, current_stamina + amount) # 序列化 func serialize() -> Dictionary: return { "horse_level": horse_level, "current_health": current_health, "current_stamina": current_stamina, "is_mounted": is_mounted } # 反序列化 func deserialize(data: Dictionary) -> void: horse_level = int(data.get("horse_level", 0)) current_health = int(data.get("current_health", 0)) current_stamina = int(data.get("current_stamina", 0)) is_mounted = bool(data.get("is_mounted", false))