Add area tactic effects
This commit is contained in:
@@ -623,28 +623,36 @@ func try_cast_selected_skill(skill_id: String, target_cell: Vector2i) -> bool:
|
||||
if not _skill_cells_from(caster, skill).has(target_cell):
|
||||
return false
|
||||
|
||||
var target := get_unit_at(target_cell)
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
return false
|
||||
|
||||
var skill_kind := str(skill.get("kind", "damage"))
|
||||
if skill_kind == "heal" and calculate_skill_heal(caster, target, skill) <= 0:
|
||||
_emit_log("%s is already at full HP." % target["name"])
|
||||
var targets := _skill_targets_for_cast(caster, skill_id, skill, target_cell)
|
||||
if targets.is_empty():
|
||||
if skill_kind == "heal":
|
||||
_emit_log("%s has no injured targets in range." % skill.get("name", "Skill"))
|
||||
else:
|
||||
_emit_log("%s has no valid targets." % skill.get("name", "Skill"))
|
||||
return false
|
||||
if skill_kind == "support" and not _skill_has_support_effects(skill):
|
||||
return false
|
||||
|
||||
caster["mp"] = max(0, int(caster.get("mp", 0)) - int(skill.get("mp_cost", 0)))
|
||||
if skill_kind == "heal":
|
||||
var healed := _resolve_skill_heal(caster, target, skill)
|
||||
if healed > 0:
|
||||
var total_healed := 0
|
||||
for target in targets:
|
||||
total_healed += _resolve_skill_heal(caster, target, skill)
|
||||
if total_healed > 0:
|
||||
_grant_experience(caster, EXP_HEAL)
|
||||
elif skill_kind == "support":
|
||||
if _resolve_skill_support(caster, target, skill_id, skill):
|
||||
var applied_support := false
|
||||
for target in targets:
|
||||
applied_support = _resolve_skill_support(caster, target, skill_id, skill) or applied_support
|
||||
if applied_support:
|
||||
_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))
|
||||
var defeated_count := 0
|
||||
for target in targets:
|
||||
if _resolve_skill_damage(caster, target, skill):
|
||||
defeated_count += 1
|
||||
_grant_experience(caster, EXP_SKILL + defeated_count * EXP_DEFEAT_BONUS)
|
||||
|
||||
caster["acted"] = true
|
||||
caster["moved"] = true
|
||||
@@ -741,6 +749,9 @@ func get_skill_preview(caster_id: String, skill_id: String, target_cell: Vector2
|
||||
var target := get_unit_at(target_cell)
|
||||
var skill_kind := str(skill.get("kind", "damage"))
|
||||
var skill_locked := _unit_has_action_lock(caster, "skill")
|
||||
var targets := _skill_targets_for_cast(caster, skill_id, skill, target_cell)
|
||||
var area_cells := _skill_area_cells(target_cell, skill)
|
||||
var has_area := _skill_has_area(skill)
|
||||
var preview := {
|
||||
"skill_id": skill_id,
|
||||
"skill_name": str(skill.get("name", skill_id)),
|
||||
@@ -748,25 +759,42 @@ func get_skill_preview(caster_id: String, skill_id: String, target_cell: Vector2
|
||||
"mp_cost": int(skill.get("mp_cost", 0)),
|
||||
"has_mp": int(caster.get("mp", 0)) >= int(skill.get("mp_cost", 0)),
|
||||
"in_range": not skill_locked and _skill_cells_from(caster, skill).has(target_cell),
|
||||
"valid_target": not skill_locked and _skill_target_valid(caster, target, skill),
|
||||
"skill_locked": skill_locked
|
||||
"valid_target": not skill_locked and not targets.is_empty(),
|
||||
"skill_locked": skill_locked,
|
||||
"has_area": has_area,
|
||||
"area_cells": area_cells,
|
||||
"target_count": targets.size()
|
||||
}
|
||||
if target.is_empty():
|
||||
if targets.is_empty():
|
||||
return preview
|
||||
|
||||
preview["target_id"] = target.get("id", "")
|
||||
var primary_target: Dictionary = target if not target.is_empty() and _skill_target_valid(caster, target, skill) else targets[0]
|
||||
preview["target_id"] = primary_target.get("id", "")
|
||||
if skill_kind == "heal":
|
||||
var heal_amount := calculate_skill_heal(caster, target, skill)
|
||||
var heal_amount := calculate_skill_heal(caster, primary_target, skill)
|
||||
var total_heal := 0
|
||||
for area_target in targets:
|
||||
total_heal += calculate_skill_heal(caster, area_target, skill)
|
||||
preview["heal"] = heal_amount
|
||||
preview["target_hp_after"] = min(int(target.get("max_hp", 1)), int(target.get("hp", 0)) + heal_amount)
|
||||
preview["total_heal"] = total_heal
|
||||
preview["target_hp_after"] = min(int(primary_target.get("max_hp", 1)), int(primary_target.get("hp", 0)) + heal_amount)
|
||||
elif skill_kind == "support":
|
||||
preview["effect_text"] = _format_support_effects(skill)
|
||||
preview["already_active"] = _has_status_effect_from_skill(target, skill_id)
|
||||
preview["already_active"] = _has_status_effect_from_skill(primary_target, skill_id)
|
||||
else:
|
||||
var damage := calculate_skill_damage(caster, target, skill)
|
||||
var damage := calculate_skill_damage(caster, primary_target, skill)
|
||||
var total_damage := 0
|
||||
var defeat_count := 0
|
||||
for area_target in targets:
|
||||
var area_damage := calculate_skill_damage(caster, area_target, skill)
|
||||
total_damage += area_damage
|
||||
if int(area_target.get("hp", 0)) - area_damage <= 0:
|
||||
defeat_count += 1
|
||||
preview["damage"] = damage
|
||||
preview["target_hp_after"] = max(0, int(target.get("hp", 0)) - damage)
|
||||
preview["would_defeat"] = int(target.get("hp", 0)) - damage <= 0
|
||||
preview["total_damage"] = total_damage
|
||||
preview["defeat_count"] = defeat_count
|
||||
preview["target_hp_after"] = max(0, int(primary_target.get("hp", 0)) - damage)
|
||||
preview["would_defeat"] = int(primary_target.get("hp", 0)) - damage <= 0
|
||||
return preview
|
||||
|
||||
|
||||
@@ -1209,6 +1237,18 @@ func get_skill_cells(unit_id: String, skill_id: String) -> Array[Vector2i]:
|
||||
return _skill_cells_from(unit, skill)
|
||||
|
||||
|
||||
func get_skill_area_cells(unit_id: String, skill_id: String, target_cell: Vector2i) -> Array[Vector2i]:
|
||||
var unit := get_unit(unit_id)
|
||||
var skill := get_skill_def(skill_id)
|
||||
if unit.is_empty() or skill.is_empty() or not is_inside(target_cell):
|
||||
return []
|
||||
if not _unit_has_skill(unit, skill_id):
|
||||
return []
|
||||
if not _skill_cells_from(unit, skill).has(target_cell):
|
||||
return []
|
||||
return _skill_area_cells(target_cell, skill)
|
||||
|
||||
|
||||
func get_item_target_cells(unit_id: String, item_id: String) -> Array[Vector2i]:
|
||||
var unit := get_unit(unit_id)
|
||||
var item := get_item_def(item_id)
|
||||
@@ -1762,9 +1802,10 @@ func _unit_skill_threat_cells(unit: Dictionary) -> Array[Vector2i]:
|
||||
if not _skill_threatens_opponents(skill):
|
||||
continue
|
||||
for origin in _unit_threat_origins(unit):
|
||||
for cell in _skill_cells_from_origin(unit, skill, origin):
|
||||
if not result.has(cell):
|
||||
result.append(cell)
|
||||
for center_cell in _skill_cells_from_origin(unit, skill, origin):
|
||||
for cell in _skill_area_cells(center_cell, skill):
|
||||
if not result.has(cell):
|
||||
result.append(cell)
|
||||
return result
|
||||
|
||||
|
||||
@@ -1835,6 +1876,57 @@ func _skill_cells_from_origin(_unit: Dictionary, skill: Dictionary, origin: Vect
|
||||
return result
|
||||
|
||||
|
||||
func _skill_area_cells(center: Vector2i, skill: Dictionary) -> Array[Vector2i]:
|
||||
var result: Array[Vector2i] = []
|
||||
if not is_inside(center):
|
||||
return result
|
||||
var radius: int = _skill_area_radius(skill)
|
||||
var shape := _skill_area_shape(skill)
|
||||
if radius <= 0 or shape == "single":
|
||||
result.append(center)
|
||||
return result
|
||||
|
||||
for y in range(center.y - radius, center.y + radius + 1):
|
||||
for x in range(center.x - radius, center.x + radius + 1):
|
||||
var cell := Vector2i(x, y)
|
||||
if not is_inside(cell):
|
||||
continue
|
||||
var distance := _manhattan(center, cell)
|
||||
if shape == "diamond" and distance <= radius:
|
||||
result.append(cell)
|
||||
return result
|
||||
|
||||
|
||||
func _skill_has_area(skill: Dictionary) -> bool:
|
||||
return _skill_area_radius(skill) > 0 and _skill_area_shape(skill) != "single"
|
||||
|
||||
|
||||
func _skill_area_shape(skill: Dictionary) -> String:
|
||||
var shape := str(skill.get("area_shape", "single")).strip_edges().to_lower()
|
||||
if shape == "diamond":
|
||||
return shape
|
||||
return "single"
|
||||
|
||||
|
||||
func _skill_area_radius(skill: Dictionary) -> int:
|
||||
return maxi(0, int(skill.get("area_radius", 0)))
|
||||
|
||||
|
||||
func _skill_targets_for_cast(caster: Dictionary, skill_id: String, skill: Dictionary, target_cell: Vector2i, skip_existing_support := false) -> Array[Dictionary]:
|
||||
var result: Array[Dictionary] = []
|
||||
var skill_kind := str(skill.get("kind", "damage"))
|
||||
for cell in _skill_area_cells(target_cell, skill):
|
||||
var target := get_unit_at(cell)
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
continue
|
||||
if skill_kind == "heal" and calculate_skill_heal(caster, target, skill) <= 0:
|
||||
continue
|
||||
if skill_kind == "support" and skip_existing_support and _has_status_effect_from_skill(target, skill_id):
|
||||
continue
|
||||
result.append(target)
|
||||
return result
|
||||
|
||||
|
||||
func _skill_range_pair(skill: Dictionary) -> Dictionary:
|
||||
var value = skill.get("range", 1)
|
||||
if typeof(value) == TYPE_ARRAY:
|
||||
@@ -2230,25 +2322,31 @@ func _best_ai_heal_skill_action(caster: Dictionary) -> Dictionary:
|
||||
continue
|
||||
if int(caster.get("mp", 0)) < int(skill.get("mp_cost", 0)):
|
||||
continue
|
||||
for ally in get_living_units(str(caster.get("team", ""))):
|
||||
if not _skill_cells_from(caster, skill).has(ally["pos"]):
|
||||
for target_cell in _skill_cells_from(caster, skill):
|
||||
var targets := _skill_targets_for_cast(caster, normalized_skill_id, skill, target_cell)
|
||||
if targets.is_empty():
|
||||
continue
|
||||
if not _skill_target_valid(caster, ally, skill):
|
||||
var total_heal := 0
|
||||
var total_missing_hp := 0
|
||||
var lowest_hp_ratio := 1.0
|
||||
for ally in targets:
|
||||
var heal_amount := calculate_skill_heal(caster, ally, skill)
|
||||
var missing_hp := int(ally.get("max_hp", 1)) - int(ally.get("hp", 0))
|
||||
var hp_ratio := float(ally.get("hp", 0)) / float(max(1, int(ally.get("max_hp", 1))))
|
||||
total_heal += heal_amount
|
||||
total_missing_hp += missing_hp
|
||||
lowest_hp_ratio = minf(lowest_hp_ratio, hp_ratio)
|
||||
if lowest_hp_ratio > 0.5 and total_missing_hp < 10:
|
||||
continue
|
||||
var heal_amount := calculate_skill_heal(caster, ally, skill)
|
||||
if heal_amount <= 0:
|
||||
continue
|
||||
var missing_hp := int(ally.get("max_hp", 1)) - int(ally.get("hp", 0))
|
||||
var hp_ratio := float(ally.get("hp", 0)) / float(max(1, int(ally.get("max_hp", 1))))
|
||||
if hp_ratio > 0.5 and missing_hp < 10:
|
||||
continue
|
||||
var score := heal_amount * 10 + missing_hp
|
||||
var score := total_heal * 10 + total_missing_hp + targets.size() * 4
|
||||
score -= _manhattan(caster["pos"], target_cell)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_action = {
|
||||
"skill_id": normalized_skill_id,
|
||||
"skill": skill,
|
||||
"target": ally
|
||||
"target": targets[0],
|
||||
"target_cell": target_cell
|
||||
}
|
||||
return best_action
|
||||
|
||||
@@ -2265,23 +2363,24 @@ func _best_ai_support_skill_action(caster: Dictionary) -> Dictionary:
|
||||
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"]):
|
||||
for target_cell in _skill_cells_from(caster, skill):
|
||||
var targets := _skill_targets_for_cast(caster, normalized_skill_id, skill, target_cell, true)
|
||||
if targets.is_empty():
|
||||
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)
|
||||
var score := 0
|
||||
for target in targets:
|
||||
score += _support_ai_score(caster, target, skill)
|
||||
if score <= 0:
|
||||
continue
|
||||
score -= _manhattan(caster["pos"], target["pos"])
|
||||
score += targets.size() * 3
|
||||
score -= _manhattan(caster["pos"], target_cell)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_action = {
|
||||
"skill_id": normalized_skill_id,
|
||||
"skill": skill,
|
||||
"target": target
|
||||
"target": targets[0],
|
||||
"target_cell": target_cell
|
||||
}
|
||||
return best_action
|
||||
|
||||
@@ -2404,25 +2503,32 @@ func _best_ai_damage_skill_action(caster: Dictionary, defeating_only := false) -
|
||||
continue
|
||||
if int(caster.get("mp", 0)) < int(skill.get("mp_cost", 0)):
|
||||
continue
|
||||
for target in get_living_units(target_team):
|
||||
if not _skill_cells_from(caster, skill).has(target["pos"]):
|
||||
for target_cell in _skill_cells_from(caster, skill):
|
||||
var targets := _skill_targets_for_cast(caster, normalized_skill_id, skill, target_cell)
|
||||
if targets.is_empty():
|
||||
continue
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
var total_damage := 0
|
||||
var defeat_count := 0
|
||||
var priority_score := 0
|
||||
for target in targets:
|
||||
var damage := calculate_skill_damage(caster, target, skill)
|
||||
total_damage += damage
|
||||
priority_score += _ai_target_priority_score(target)
|
||||
if int(target.get("hp", 0)) - damage <= 0:
|
||||
defeat_count += 1
|
||||
if defeating_only and defeat_count <= 0:
|
||||
continue
|
||||
var damage := calculate_skill_damage(caster, target, skill)
|
||||
var would_defeat := int(target.get("hp", 0)) - damage <= 0
|
||||
if defeating_only and not would_defeat:
|
||||
continue
|
||||
var score := damage + _ai_target_priority_score(target)
|
||||
if would_defeat:
|
||||
score += 1000
|
||||
score -= _manhattan(caster["pos"], target["pos"])
|
||||
var score := total_damage + priority_score + targets.size() * 5
|
||||
if defeat_count > 0:
|
||||
score += 1000 * defeat_count
|
||||
score -= _manhattan(caster["pos"], target_cell)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_action = {
|
||||
"skill_id": normalized_skill_id,
|
||||
"skill": skill,
|
||||
"target": target
|
||||
"target": targets[0],
|
||||
"target_cell": target_cell
|
||||
}
|
||||
return best_action
|
||||
|
||||
@@ -2431,7 +2537,7 @@ func _execute_ai_skill_action(caster: Dictionary, action: Dictionary) -> bool:
|
||||
var skill_id := str(action.get("skill_id", ""))
|
||||
var skill: Dictionary = action.get("skill", {})
|
||||
var target: Dictionary = action.get("target", {})
|
||||
if skill_id.is_empty() or skill.is_empty() or target.is_empty():
|
||||
if skill_id.is_empty() or skill.is_empty():
|
||||
return false
|
||||
if not _unit_has_skill(caster, skill_id):
|
||||
return false
|
||||
@@ -2439,29 +2545,38 @@ func _execute_ai_skill_action(caster: Dictionary, action: Dictionary) -> bool:
|
||||
return false
|
||||
if int(caster.get("mp", 0)) < int(skill.get("mp_cost", 0)):
|
||||
return false
|
||||
if not target.get("alive", false):
|
||||
return false
|
||||
if not _skill_cells_from(caster, skill).has(target["pos"]):
|
||||
return false
|
||||
if not _skill_target_valid(caster, target, skill):
|
||||
var target_cell: Vector2i = action.get("target_cell", target.get("pos", Vector2i(-999, -999)))
|
||||
if not is_inside(target_cell):
|
||||
return false
|
||||
var skill_kind := str(skill.get("kind", "damage"))
|
||||
if skill_kind == "heal" and calculate_skill_heal(caster, target, skill) <= 0:
|
||||
var skip_existing_support := skill_kind == "support"
|
||||
var targets := _skill_targets_for_cast(caster, skill_id, skill, target_cell, skip_existing_support)
|
||||
if targets.is_empty():
|
||||
return false
|
||||
if skill_kind == "support" and (not _skill_has_support_effects(skill) or _has_status_effect_from_skill(target, skill_id)):
|
||||
if not _skill_cells_from(caster, skill).has(target_cell):
|
||||
return false
|
||||
if skill_kind == "support" and not _skill_has_support_effects(skill):
|
||||
return false
|
||||
|
||||
caster["mp"] = max(0, int(caster.get("mp", 0)) - int(skill.get("mp_cost", 0)))
|
||||
if skill_kind == "heal":
|
||||
var healed := _resolve_skill_heal(caster, target, skill)
|
||||
if healed > 0:
|
||||
var total_healed := 0
|
||||
for area_target in targets:
|
||||
total_healed += _resolve_skill_heal(caster, area_target, skill)
|
||||
if total_healed > 0:
|
||||
_grant_experience(caster, EXP_HEAL)
|
||||
elif skill_kind == "support":
|
||||
if _resolve_skill_support(caster, target, skill_id, skill):
|
||||
var applied_support := false
|
||||
for area_target in targets:
|
||||
applied_support = _resolve_skill_support(caster, area_target, skill_id, skill) or applied_support
|
||||
if applied_support:
|
||||
_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))
|
||||
var defeated_count := 0
|
||||
for area_target in targets:
|
||||
if _resolve_skill_damage(caster, area_target, skill):
|
||||
defeated_count += 1
|
||||
_grant_experience(caster, EXP_SKILL + defeated_count * EXP_DEFEAT_BONUS)
|
||||
_emit_log("%s spends %d MP." % [caster["name"], int(skill.get("mp_cost", 0))])
|
||||
return true
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const PLAYER_COLOR := Color(0.20, 0.42, 0.82)
|
||||
const ENEMY_COLOR := Color(0.72, 0.18, 0.16)
|
||||
const GRID_COLOR := Color(0.05, 0.06, 0.07, 0.45)
|
||||
const SKILL_OVERLAY_COLOR := Color(0.56, 0.28, 0.95, 0.28)
|
||||
const SKILL_AREA_OVERLAY_COLOR := Color(0.72, 0.46, 1.0, 0.30)
|
||||
const ITEM_OVERLAY_COLOR := Color(0.24, 0.74, 0.34, 0.30)
|
||||
const FORMATION_OVERLAY_COLOR := Color(0.95, 0.76, 0.25, 0.34)
|
||||
const OBJECTIVE_OVERLAY_COLOR := Color(1.0, 0.78, 0.18, 0.30)
|
||||
@@ -959,6 +960,14 @@ func _draw_overlays() -> void:
|
||||
for cell in skill_cells:
|
||||
draw_rect(_rect_for_cell(cell), SKILL_OVERLAY_COLOR)
|
||||
|
||||
if not selected_skill_id.is_empty() and skill_cells.has(hover_cell):
|
||||
var area_selected := state.get_selected_unit()
|
||||
if not area_selected.is_empty():
|
||||
for cell in state.get_skill_area_cells(area_selected["id"], selected_skill_id, hover_cell):
|
||||
var area_rect := _rect_for_cell(cell)
|
||||
draw_rect(area_rect, SKILL_AREA_OVERLAY_COLOR)
|
||||
draw_rect(area_rect.grow(-7.0), Color(0.84, 0.62, 1.0, 0.62), false, 1.5)
|
||||
|
||||
for cell in item_cells:
|
||||
draw_rect(_rect_for_cell(cell), ITEM_OVERLAY_COLOR)
|
||||
|
||||
@@ -1223,26 +1232,37 @@ func _skill_target_preview_badge(selected: Dictionary, target: Dictionary) -> Di
|
||||
|
||||
if bool(preview.get("skill_locked", false)):
|
||||
return _make_target_preview_badge("SEALED", "seal")
|
||||
if target.is_empty():
|
||||
return {}
|
||||
if not bool(preview.get("valid_target", false)):
|
||||
return _make_target_preview_badge("INVALID", "invalid")
|
||||
if not bool(preview.get("in_range", false)):
|
||||
return _make_target_preview_badge("OUT", "invalid")
|
||||
if not bool(preview.get("has_mp", false)):
|
||||
return _make_target_preview_badge("NO MP", "invalid")
|
||||
if not bool(preview.get("valid_target", false)):
|
||||
return _make_target_preview_badge("NO TGT" if bool(preview.get("has_area", false)) else "INVALID", "invalid")
|
||||
if target.is_empty() and not bool(preview.get("has_area", false)):
|
||||
return {}
|
||||
|
||||
var kind := str(preview.get("kind", "damage"))
|
||||
var target_count := int(preview.get("target_count", 1))
|
||||
if kind == "heal":
|
||||
var heal := int(preview.get("heal", 0))
|
||||
if heal <= 0:
|
||||
var total_heal := int(preview.get("total_heal", heal))
|
||||
if total_heal <= 0:
|
||||
return _make_target_preview_badge("FULL", "invalid")
|
||||
if bool(preview.get("has_area", false)):
|
||||
return _make_target_preview_badge("+%d x%d" % [total_heal, target_count], "heal")
|
||||
return _make_target_preview_badge("+%d HP" % heal, "heal")
|
||||
if kind == "support":
|
||||
var effect_text := str(preview.get("effect_text", "Support"))
|
||||
var badge_kind := "disarm" if effect_text.contains("Disarm") else ("snare" if effect_text.contains("Snare") else ("seal" if effect_text.contains("Seal") else ("poison" if effect_text.contains("Poison") else ("debuff" if effect_text.contains("-") else "support"))))
|
||||
if bool(preview.get("has_area", false)):
|
||||
return _make_target_preview_badge("%s x%d" % [_compact_support_preview_text(effect_text), target_count], badge_kind)
|
||||
return _make_target_preview_badge(_compact_support_preview_text(effect_text), badge_kind)
|
||||
|
||||
if bool(preview.get("has_area", false)):
|
||||
var defeat_count := int(preview.get("defeat_count", 0))
|
||||
if defeat_count > 0:
|
||||
return _make_target_preview_badge("KO x%d" % defeat_count, "damage")
|
||||
return _make_target_preview_badge("-%d x%d" % [int(preview.get("total_damage", 0)), target_count], "damage")
|
||||
var result_text := "KO" if bool(preview.get("would_defeat", false)) else "-%d" % int(preview.get("damage", 0))
|
||||
return _make_target_preview_badge(result_text, "damage")
|
||||
|
||||
@@ -1542,7 +1562,6 @@ func _update_skill_forecast(selected: Dictionary) -> void:
|
||||
forecast_label.text = "Forecast: -"
|
||||
return
|
||||
|
||||
var target := state.get_unit_at(hover_cell)
|
||||
var range_text := "ready" if preview["in_range"] else "out of range"
|
||||
var mp_text := "" if preview["has_mp"] else " Not enough MP."
|
||||
if bool(preview.get("skill_locked", false)):
|
||||
@@ -1551,7 +1570,7 @@ func _update_skill_forecast(selected: Dictionary) -> void:
|
||||
preview["mp_cost"]
|
||||
]
|
||||
return
|
||||
if target.is_empty() or not preview["valid_target"]:
|
||||
if not preview["valid_target"]:
|
||||
forecast_label.text = "%s: %s, %d MP.%s\nNo valid target." % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
@@ -1560,36 +1579,71 @@ func _update_skill_forecast(selected: Dictionary) -> void:
|
||||
]
|
||||
return
|
||||
|
||||
var target := state.get_unit(str(preview.get("target_id", "")))
|
||||
var target_max_hp := int(target.get("max_hp", 1)) if not target.is_empty() else 1
|
||||
var target_count := int(preview.get("target_count", 1))
|
||||
var has_area := bool(preview.get("has_area", false))
|
||||
var area_text := " Area targets: %d." % target_count if has_area else ""
|
||||
if preview["kind"] == "heal":
|
||||
forecast_label.text = "%s: %s, %d MP.%s\nRestore %d HP, target HP %d/%d." % [
|
||||
var total_heal := int(preview.get("total_heal", preview.get("heal", 0)))
|
||||
if not has_area:
|
||||
forecast_label.text = "%s: %s, %d MP.%s\nRestore %d HP, target HP %d/%d." % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
preview["mp_cost"],
|
||||
mp_text,
|
||||
preview["heal"],
|
||||
preview["target_hp_after"],
|
||||
target_max_hp
|
||||
]
|
||||
return
|
||||
forecast_label.text = "%s: %s, %d MP.%s%s\nRestore %d HP total, focus HP %d/%d." % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
preview["mp_cost"],
|
||||
mp_text,
|
||||
preview["heal"],
|
||||
area_text,
|
||||
total_heal,
|
||||
preview["target_hp_after"],
|
||||
target["max_hp"]
|
||||
target_max_hp
|
||||
]
|
||||
elif preview["kind"] == "support":
|
||||
var refresh_text := " Refresh." if bool(preview.get("already_active", false)) else ""
|
||||
forecast_label.text = "%s: %s, %d MP.%s\n%s.%s" % [
|
||||
forecast_label.text = "%s: %s, %d MP.%s%s\n%s.%s" % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
preview["mp_cost"],
|
||||
mp_text,
|
||||
area_text,
|
||||
preview.get("effect_text", "Support effect"),
|
||||
refresh_text
|
||||
]
|
||||
else:
|
||||
var defeat_text := " Defeat" if preview["would_defeat"] else ""
|
||||
forecast_label.text = "%s: %s, %d MP.%s\n%d damage, target HP %d/%d.%s" % [
|
||||
var defeat_count := int(preview.get("defeat_count", 0))
|
||||
var defeat_text := " Defeat x%d" % defeat_count if defeat_count > 0 else ""
|
||||
var total_damage := int(preview.get("total_damage", preview.get("damage", 0)))
|
||||
if not has_area:
|
||||
var single_defeat_text := " Defeat" if bool(preview.get("would_defeat", false)) else ""
|
||||
forecast_label.text = "%s: %s, %d MP.%s\n%d damage, target HP %d/%d.%s" % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
preview["mp_cost"],
|
||||
mp_text,
|
||||
preview["damage"],
|
||||
preview["target_hp_after"],
|
||||
target_max_hp,
|
||||
single_defeat_text
|
||||
]
|
||||
return
|
||||
forecast_label.text = "%s: %s, %d MP.%s%s\n%d total damage, focus HP %d/%d.%s" % [
|
||||
preview["skill_name"],
|
||||
range_text,
|
||||
preview["mp_cost"],
|
||||
mp_text,
|
||||
preview["damage"],
|
||||
area_text,
|
||||
total_damage,
|
||||
preview["target_hp_after"],
|
||||
target["max_hp"],
|
||||
target_max_hp,
|
||||
defeat_text
|
||||
]
|
||||
|
||||
@@ -3421,6 +3475,7 @@ func _format_tactic_button_text(skill_id: String, skill: Dictionary, selected: D
|
||||
|
||||
|
||||
func _format_tactic_effect_text(skill: Dictionary) -> String:
|
||||
var area_text := _format_tactic_area_text(skill)
|
||||
if str(skill.get("kind", "")) == "support":
|
||||
var parts := []
|
||||
for effect in skill.get("effects", []):
|
||||
@@ -3442,8 +3497,18 @@ func _format_tactic_effect_text(skill: Dictionary) -> String:
|
||||
_action_lock_effect_label(str(effect.get("action", "")))
|
||||
])
|
||||
if not parts.is_empty():
|
||||
return _join_strings(parts, ", ")
|
||||
return "P%d" % int(skill.get("power", 0))
|
||||
var support_text := _join_strings(parts, ", ")
|
||||
return "%s, %s" % [support_text, area_text] if not area_text.is_empty() else support_text
|
||||
var power_text := "P%d" % int(skill.get("power", 0))
|
||||
return "%s, %s" % [power_text, area_text] if not area_text.is_empty() else power_text
|
||||
|
||||
|
||||
func _format_tactic_area_text(skill: Dictionary) -> String:
|
||||
var shape := str(skill.get("area_shape", "single"))
|
||||
var radius := int(skill.get("area_radius", 0))
|
||||
if shape == "diamond" and radius > 0:
|
||||
return "Area %d" % radius
|
||||
return ""
|
||||
|
||||
|
||||
func _format_tactic_range_text(skill: Dictionary) -> String:
|
||||
|
||||
Reference in New Issue
Block a user