Add support and debuff tactics
This commit is contained in:
@@ -852,7 +852,7 @@ func _resolve_skill_support(caster: Dictionary, target: Dictionary, skill_id: St
|
||||
target["name"],
|
||||
_format_support_effects(skill)
|
||||
])
|
||||
_emit_combat_feedback(target, _format_support_popup(skill), "support")
|
||||
_emit_combat_feedback(target, _format_support_popup(skill), _support_feedback_kind(skill))
|
||||
return true
|
||||
|
||||
|
||||
@@ -910,6 +910,15 @@ func _format_support_popup(skill: Dictionary) -> String:
|
||||
return _join_strings(parts, ", ")
|
||||
|
||||
|
||||
func _support_feedback_kind(skill: Dictionary) -> String:
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) == "stat_bonus" and int(effect.get("amount", 0)) < 0:
|
||||
return "debuff"
|
||||
return "support"
|
||||
|
||||
|
||||
func _format_active_status_effects(unit: Dictionary) -> String:
|
||||
var parts := []
|
||||
for effect in _active_status_effects(unit):
|
||||
@@ -1871,6 +1880,10 @@ func _try_enemy_skill_action(enemy: Dictionary) -> bool:
|
||||
if not heal_action.is_empty() and _execute_ai_skill_action(enemy, heal_action):
|
||||
return true
|
||||
|
||||
var support_action := _best_ai_support_skill_action(enemy)
|
||||
if not support_action.is_empty() and _execute_ai_skill_action(enemy, support_action):
|
||||
return true
|
||||
|
||||
var damage_action := _best_ai_damage_skill_action(enemy, false)
|
||||
if not damage_action.is_empty() and _execute_ai_skill_action(enemy, damage_action):
|
||||
return true
|
||||
@@ -1911,6 +1924,89 @@ func _best_ai_heal_skill_action(caster: Dictionary) -> Dictionary:
|
||||
return best_action
|
||||
|
||||
|
||||
func _best_ai_support_skill_action(caster: Dictionary) -> Dictionary:
|
||||
var best_action := {}
|
||||
var best_score := -1
|
||||
for skill_id in caster.get("skills", []):
|
||||
var normalized_skill_id := str(skill_id)
|
||||
var skill := get_skill_def(normalized_skill_id)
|
||||
if skill.is_empty() or str(skill.get("kind", "")) != "support":
|
||||
continue
|
||||
if int(caster.get("mp", 0)) < int(skill.get("mp_cost", 0)):
|
||||
continue
|
||||
if not _skill_has_support_effects(skill):
|
||||
continue
|
||||
for target in _support_ai_targets_for_skill(caster, skill):
|
||||
if not _skill_cells_from(caster, skill).has(target["pos"]):
|
||||
continue
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
continue
|
||||
if _has_status_effect_from_skill(target, normalized_skill_id):
|
||||
continue
|
||||
var score := _support_ai_score(caster, target, skill)
|
||||
if score <= 0:
|
||||
continue
|
||||
score -= _manhattan(caster["pos"], target["pos"])
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_action = {
|
||||
"skill_id": normalized_skill_id,
|
||||
"skill": skill,
|
||||
"target": target
|
||||
}
|
||||
return best_action
|
||||
|
||||
|
||||
func _support_ai_targets_for_skill(caster: Dictionary, skill: Dictionary) -> Array[Dictionary]:
|
||||
var target_rule := str(skill.get("target", "ally"))
|
||||
if target_rule == "self":
|
||||
return [caster]
|
||||
if target_rule == "enemy":
|
||||
var target_team: String = _opposing_team(str(caster.get("team", "")))
|
||||
return get_living_units(target_team) if not target_team.is_empty() else []
|
||||
if target_rule == "any":
|
||||
var targets: Array[Dictionary] = get_living_units(str(caster.get("team", "")))
|
||||
var opposing_team: String = _opposing_team(str(caster.get("team", "")))
|
||||
if not opposing_team.is_empty():
|
||||
targets.append_array(get_living_units(opposing_team))
|
||||
return targets
|
||||
return get_living_units(str(caster.get("team", "")))
|
||||
|
||||
|
||||
func _support_ai_score(caster: Dictionary, target: Dictionary, skill: Dictionary) -> int:
|
||||
var score: int = 0
|
||||
var target_is_ally: bool = target.get("team", "") == caster.get("team", "")
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) != "stat_bonus":
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
if amount > 0 and target_is_ally:
|
||||
score += amount * _support_ai_stat_weight(stat)
|
||||
if target.get("id", "") == caster.get("id", ""):
|
||||
score += 3
|
||||
elif amount < 0 and not target_is_ally:
|
||||
score += absi(amount) * _support_ai_stat_weight(stat)
|
||||
score += _ai_target_priority_score(target)
|
||||
return score
|
||||
|
||||
|
||||
func _support_ai_stat_weight(stat: String) -> int:
|
||||
if stat == "atk":
|
||||
return 9
|
||||
if stat == "def":
|
||||
return 7
|
||||
if stat == "int":
|
||||
return 6
|
||||
if stat == "agi":
|
||||
return 4
|
||||
return 1
|
||||
|
||||
|
||||
func _best_ai_damage_skill_action(caster: Dictionary, defeating_only := false) -> Dictionary:
|
||||
var best_action := {}
|
||||
var best_score := -1
|
||||
@@ -1964,14 +2060,20 @@ func _execute_ai_skill_action(caster: Dictionary, action: Dictionary) -> bool:
|
||||
return false
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
return false
|
||||
if str(skill.get("kind", "")) == "heal" and calculate_skill_heal(caster, target, skill) <= 0:
|
||||
var skill_kind := str(skill.get("kind", "damage"))
|
||||
if skill_kind == "heal" and calculate_skill_heal(caster, target, skill) <= 0:
|
||||
return false
|
||||
if skill_kind == "support" and (not _skill_has_support_effects(skill) or _has_status_effect_from_skill(target, skill_id)):
|
||||
return false
|
||||
|
||||
caster["mp"] = max(0, int(caster.get("mp", 0)) - int(skill.get("mp_cost", 0)))
|
||||
if str(skill.get("kind", "")) == "heal":
|
||||
if skill_kind == "heal":
|
||||
var healed := _resolve_skill_heal(caster, target, skill)
|
||||
if healed > 0:
|
||||
_grant_experience(caster, EXP_HEAL)
|
||||
elif skill_kind == "support":
|
||||
if _resolve_skill_support(caster, target, skill_id, skill):
|
||||
_grant_experience(caster, EXP_SUPPORT)
|
||||
else:
|
||||
var defeated := _resolve_skill_damage(caster, target, skill)
|
||||
_grant_experience(caster, EXP_SKILL + (EXP_DEFEAT_BONUS if defeated else 0))
|
||||
|
||||
Reference in New Issue
Block a user