# horse_combat_system.gd —— Metin2 40250 战马骑乘战斗与 4 大战马专属技能 1:1 # 对照 40250 服务端 char_skill.cpp, char_horse.cpp, skilldesc.txt:137-140 class_name HorseCombatSystem extends RefCounted signal horse_attack_performed(target_count: int, total_dmg: int) signal horse_skill_used(skill_id: int, skill_name: String, cost_sp: int) signal horse_stamina_changed(current: float, max_val: float) const HORSE_TIER_BEGINNER := 1 # 初级马 (Lv 1-10, 仅代步) const HORSE_TIER_COMBAT := 2 # 战斗马 (Lv 11-20, 可马上挥砍) const HORSE_TIER_MILITARY := 3 # 军马 (Lv 21-30, 可马上挥砍并施展 4 大马术技能) const MOUNTED_ATTACK_RANGE := 5.0 const FOOT_ATTACK_RANGE := 3.0 const ITEM_FOOD_CARROT := 50054 # 胡萝卜 (战斗马饲料) const ITEM_FOOD_GINSENG := 50058 # 人参 (军马饲料) const HORSE_SKILLS: Dictionary = { 137: { "name": "战马砍击", "sp_cost": 30, "cooldown": 15.0, "dmg_mult": 2.5, "effect": "KNOCKBACK", "range": 5.0 }, 138: { "name": "战马突刺", "sp_cost": 35, "cooldown": 20.0, "dmg_mult": 2.0, "effect": "STUMBLE", "range": 8.0 }, 139: { "name": "战马震击", "sp_cost": 40, "cooldown": 25.0, "dmg_mult": 3.0, "effect": "AOE_STUN", "range": 6.0 }, 140: { "name": "破浪击", "sp_cost": 30, "cooldown": 12.0, "dmg_mult": 2.2, "effect": "RANGED_WAVE", "range": 12.0 } } var is_riding: bool = false var horse_tier: int = HORSE_TIER_NONE var horse_stamina: float = 100.0 var max_stamina: float = 100.0 var skill_cooldowns: Dictionary = { 137: 0.0, 138: 0.0, 139: 0.0, 140: 0.0 } const HORSE_TIER_NONE := 0 # 设置骑乘状态 func set_mounted_state(riding: bool, tier: int = HORSE_TIER_MILITARY) -> void: is_riding = riding horse_tier = tier if riding else HORSE_TIER_NONE # 马上普通挥砍攻击 (Mounted Basic Sweep Attack) func perform_mounted_basic_attack( base_att: int, targets_in_range: Array ) -> Dictionary: if not is_riding: return {"ok": false, "reason": "NOT_RIDING", "msg": "未处于骑乘状态!"} if horse_tier < HORSE_TIER_COMBAT: return {"ok": false, "reason": "HORSE_TIER_TOO_LOW", "msg": "当前马匹等级不足,无法在马上战斗!需要战斗马或军马。"} # 精力衰减 horse_stamina = maxf(0.0, horse_stamina - 0.5) horse_stamina_changed.emit(horse_stamina, max_stamina) # 伤害计算:马上横扫增伤 1.2x (精力枯竭时惩罚为 0.8x) var mult: float = 1.2 if horse_stamina > 0.0 else 0.8 var dmg_per_target := int(base_att * mult) var total_dmg: int = 0 var hit_targets: Array = [] for target in targets_in_range: var dist: float = float(target.get("distance", 999.0)) if dist <= MOUNTED_ATTACK_RANGE: hit_targets.append(target) total_dmg += dmg_per_target horse_attack_performed.emit(hit_targets.size(), total_dmg) return { "ok": true, "attack_range": MOUNTED_ATTACK_RANGE, "hit_count": hit_targets.size(), "damage_per_hit": dmg_per_target, "total_damage": total_dmg, "stamina": horse_stamina } # 施展 4 大战马专属技能 (137, 138, 139, 140) func use_horse_skill( skill_id: int, player_sp: int, base_att: int ) -> Dictionary: if not is_riding: return {"ok": false, "reason": "NOT_RIDING", "msg": "未在马背上,无法施展马术技能!"} if horse_tier < HORSE_TIER_MILITARY: return {"ok": false, "reason": "REQUIRES_MILITARY_HORSE", "msg": "马术专属技能需要军马 (Lv21+) 方可施展!"} if not HORSE_SKILLS.has(skill_id): return {"ok": false, "reason": "INVALID_SKILL_ID", "msg": "未知的战马技能!"} var cfg: Dictionary = HORSE_SKILLS[skill_id] # 检查冷却 if skill_cooldowns[skill_id] > 0.0: return { "ok": false, "reason": "SKILL_ON_COOLDOWN", "remaining_cd": skill_cooldowns[skill_id], "msg": "技能【%s】冷却中,剩余 %.1f 秒。" % [cfg["name"], skill_cooldowns[skill_id]] } # 检查玩家法力 (SP) var sp_cost: int = cfg["sp_cost"] if player_sp < sp_cost: return { "ok": false, "reason": "INSUFFICIENT_SP", "cost": sp_cost, "current_sp": player_sp, "msg": "法力值不足,无法施展【%s】!" % cfg["name"] } # 检查马匹精力 if horse_stamina < 5.0: return { "ok": false, "reason": "HORSE_EXHAUSTED", "msg": "战马体力已透支,请先喂食胡萝卜或人参!" } # 施法成功:扣除精力、进入冷却 horse_stamina = maxf(0.0, horse_stamina - 5.0) skill_cooldowns[skill_id] = cfg["cooldown"] horse_stamina_changed.emit(horse_stamina, max_stamina) horse_skill_used.emit(skill_id, cfg["name"], sp_cost) var dealt_dmg := int(base_att * cfg["dmg_mult"]) return { "ok": true, "skill_id": skill_id, "skill_name": cfg["name"], "sp_cost": sp_cost, "cooldown": cfg["cooldown"], "damage": dealt_dmg, "effect": cfg["effect"], "range": cfg["range"], "msg": "施展了战马技能【%s】!" % cfg["name"] } # 喂食恢复马匹精力 (胡萝卜 +40, 人参 +80) func feed_horse(food_vnum: int) -> Dictionary: if horse_tier == HORSE_TIER_NONE: return {"ok": false, "reason": "NO_HORSE", "msg": "当前没有马匹!"} var restore_amount := 0.0 var food_name := "" if food_vnum == ITEM_FOOD_CARROT: restore_amount = 40.0 food_name = "胡萝卜" elif food_vnum == ITEM_FOOD_GINSENG: restore_amount = 80.0 food_name = "人参" else: return {"ok": false, "reason": "WRONG_FOOD", "msg": "这不是战马适合的食物!"} horse_stamina = minf(max_stamina, horse_stamina + restore_amount) horse_stamina_changed.emit(horse_stamina, max_stamina) return { "ok": true, "food_name": food_name, "restored": restore_amount, "current_stamina": horse_stamina, "msg": "战马食用了【%s】,恢复了体力!" % food_name } # 冷却逐帧更新 func update(dt: float) -> void: for sk in skill_cooldowns.keys(): if skill_cooldowns[sk] > 0.0: skill_cooldowns[sk] = maxf(0.0, skill_cooldowns[sk] - dt)