Add target preview badges

This commit is contained in:
2026-06-18 03:32:11 +09:00
parent 05af755da0
commit e81ecf691a
4 changed files with 150 additions and 3 deletions

View File

@@ -34,6 +34,7 @@ const UI_SFX_VOLUME_DB := -8.0
const FLOATING_TEXT_LIFETIME := 1.0
const FLOATING_TEXT_RISE := 42.0
const UNIT_MOVE_ANIMATION_DURATION := 0.18
const TARGET_PREVIEW_BADGE_SIZE := Vector2(92, 22)
var state: BattleState = BattleStateScript.new()
var campaign_state: CampaignState = CampaignStateScript.new()
@@ -159,6 +160,7 @@ func _draw() -> void:
_draw_map()
_draw_overlays()
_draw_units()
_draw_target_preview_badge()
_draw_floating_texts()
@@ -911,6 +913,150 @@ func _floating_text_color(kind: String) -> Color:
return Color.WHITE
func _draw_target_preview_badge() -> void:
var badge := _target_preview_badge()
if badge.is_empty():
return
var cell: Vector2i = badge.get("cell", hover_cell)
if not state.is_inside(cell):
return
var rect := _rect_for_cell(cell)
var size := TARGET_PREVIEW_BADGE_SIZE
var badge_rect := Rect2(
rect.position + Vector2((TILE_SIZE - size.x) * 0.5, 5.0),
size
)
var kind := str(badge.get("kind", ""))
var color := _target_preview_badge_color(kind)
var background := Color(0.025, 0.028, 0.034, 0.88)
var font := ThemeDB.fallback_font
var text := str(badge.get("text", ""))
draw_rect(badge_rect, background)
draw_rect(badge_rect, color, false, 2.0)
draw_string(font, badge_rect.position + Vector2(0, 16), text, HORIZONTAL_ALIGNMENT_CENTER, badge_rect.size.x, 14, color)
func _target_preview_badge() -> Dictionary:
if _is_input_locked() or not state.is_inside(hover_cell):
return {}
var selected := state.get_selected_unit()
if selected.is_empty():
return {}
var target := state.get_unit_at(hover_cell)
if not selected_skill_id.is_empty():
return _skill_target_preview_badge(selected, target)
if not selected_item_id.is_empty():
return _item_target_preview_badge(selected, target)
return _attack_target_preview_badge(selected, target)
func _attack_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
if target.is_empty() or target.get("team", "") == selected.get("team", ""):
return {}
var preview := state.get_damage_preview(selected["id"], target["id"])
if preview.is_empty():
return {}
if not bool(preview.get("in_range", false)):
return _make_target_preview_badge("OUT", "invalid")
var hit_chance := int(preview.get("hit_chance", 100))
var result_text := "KO" if bool(preview.get("would_defeat", false)) else "-%d" % int(preview.get("damage", 0))
return _make_target_preview_badge("%s %d%%" % [result_text, hit_chance], "damage")
func _skill_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
var preview := state.get_skill_preview(selected["id"], selected_skill_id, hover_cell)
if preview.is_empty() or 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")
var kind := str(preview.get("kind", "damage"))
if kind == "heal":
var heal := int(preview.get("heal", 0))
if heal <= 0:
return _make_target_preview_badge("FULL", "invalid")
return _make_target_preview_badge("+%d HP" % heal, "heal")
if kind == "support":
return _make_target_preview_badge(_compact_support_preview_text(str(preview.get("effect_text", "Support"))), "support")
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")
func _item_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
var preview := state.get_item_preview(selected["id"], selected_item_id, hover_cell)
if preview.is_empty() or 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 int(preview.get("count", 0)) <= 0:
return _make_target_preview_badge("NONE", "invalid")
var hp_heal := int(preview.get("heal", 0))
var mp_heal := int(preview.get("mp_heal", 0))
if hp_heal <= 0 and mp_heal <= 0:
return _make_target_preview_badge("FULL", "invalid")
if hp_heal > 0 and mp_heal > 0:
return _make_target_preview_badge("+%d/%d" % [hp_heal, mp_heal], "heal")
if hp_heal > 0:
return _make_target_preview_badge("+%d HP" % hp_heal, "heal")
return _make_target_preview_badge("+%d MP" % mp_heal, "mp")
func _make_target_preview_badge(text: String, kind: String) -> Dictionary:
if text.is_empty():
return {}
return {
"text": text,
"kind": kind,
"cell": hover_cell
}
func _compact_support_preview_text(effect_text: String) -> String:
var parts := []
for raw_part in effect_text.split(","):
var part := raw_part.strip_edges()
var until_index := part.find(" until ")
if until_index >= 0:
part = part.substr(0, until_index)
if not part.is_empty() and part != "No support effect.":
parts.append(part)
if parts.is_empty():
return "SUPPORT"
if parts.size() > 1:
return "%s+" % parts[0]
return str(parts[0])
func _target_preview_badge_color(kind: String) -> Color:
if kind == "damage":
return Color(1.0, 0.38, 0.26)
if kind == "heal":
return Color(0.44, 1.0, 0.52)
if kind == "mp":
return Color(0.54, 0.78, 1.0)
if kind == "support":
return Color(1.0, 0.88, 0.30)
return Color(0.78, 0.80, 0.86)
func _cell_from_screen(screen_position: Vector2) -> Vector2i:
var local := screen_position - BOARD_OFFSET
if local.x < 0 or local.y < 0: