Add area tactic effects

This commit is contained in:
2026-06-18 11:09:24 +09:00
parent 65e2508257
commit 47d02e1537
9 changed files with 314 additions and 94 deletions

View File

@@ -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: