Add floating combat text

This commit is contained in:
2026-06-18 03:24:13 +09:00
parent 6bba22a562
commit 889b5a3743
5 changed files with 126 additions and 3 deletions

View File

@@ -31,6 +31,8 @@ const SFX_VICTORY_STING := preload("res://audio/sfx/victory_sting_01.wav")
const BGM_VOLUME_DB := -14.0
const SFX_VOLUME_DB := -4.0
const UI_SFX_VOLUME_DB := -8.0
const FLOATING_TEXT_LIFETIME := 1.0
const FLOATING_TEXT_RISE := 42.0
var state: BattleState = BattleStateScript.new()
var campaign_state: CampaignState = CampaignStateScript.new()
@@ -107,6 +109,7 @@ var active_dialogue_index := 0
var bgm_player: AudioStreamPlayer
var current_bgm_key := ""
var last_announced_battle_status := ""
var floating_texts: Array[Dictionary] = []
func _ready() -> void:
@@ -115,6 +118,7 @@ func _ready() -> void:
state.changed.connect(_on_state_changed)
state.log_added.connect(_on_log_added)
state.dialogue_requested.connect(_on_dialogue_requested)
state.combat_feedback_requested.connect(_on_combat_feedback_requested)
campaign_state.load_campaign(CAMPAIGN_PATH)
campaign_state.load_or_start(campaign_state.get_start_scenario_id())
if campaign_state.has_pending_post_battle_choice():
@@ -152,6 +156,7 @@ func _draw() -> void:
_draw_map()
_draw_overlays()
_draw_units()
_draw_floating_texts()
func _create_hud() -> void:
@@ -732,6 +737,19 @@ func _handle_key(event: InputEventKey) -> void:
_on_equip_pressed()
func _process(delta: float) -> void:
if floating_texts.is_empty():
return
var active_texts: Array[Dictionary] = []
for popup in floating_texts:
var next_popup := popup.duplicate(true)
next_popup["age"] = float(next_popup.get("age", 0.0)) + delta
if float(next_popup["age"]) < float(next_popup.get("duration", FLOATING_TEXT_LIFETIME)):
active_texts.append(next_popup)
floating_texts = active_texts
queue_redraw()
func _can_select(unit: Dictionary) -> bool:
if unit.is_empty():
return false
@@ -818,6 +836,44 @@ func _draw_units() -> void:
draw_arc(center, 29, 0.0, TAU, 48, Color(1.0, 0.92, 0.25), 3.0)
func _draw_floating_texts() -> void:
if floating_texts.is_empty():
return
var font := ThemeDB.fallback_font
for popup in floating_texts:
var duration := max(0.01, float(popup.get("duration", FLOATING_TEXT_LIFETIME)))
var age := float(popup.get("age", 0.0))
var progress := clampf(age / duration, 0.0, 1.0)
var alpha := 1.0 - progress
var color := _floating_text_color(str(popup.get("kind", "")))
color.a = alpha
var shadow := Color(0.02, 0.02, 0.025, 0.82 * alpha)
var center = popup.get("pos", Vector2.ZERO)
var text := str(popup.get("text", ""))
var width := 118.0
var draw_position := center - Vector2(width * 0.5, 24.0 + progress * FLOATING_TEXT_RISE)
draw_string(font, draw_position + Vector2(1, 1), text, HORIZONTAL_ALIGNMENT_CENTER, width, 18, shadow)
draw_string(font, draw_position, text, HORIZONTAL_ALIGNMENT_CENTER, width, 18, color)
func _floating_text_color(kind: String) -> Color:
if kind == "damage":
return Color(1.0, 0.30, 0.20)
if kind == "heal":
return Color(0.36, 1.0, 0.46)
if kind == "mp":
return Color(0.48, 0.74, 1.0)
if kind == "support":
return Color(1.0, 0.88, 0.28)
if kind == "miss" or kind == "fade":
return Color(0.82, 0.84, 0.90)
if kind == "defeat":
return Color(1.0, 0.18, 0.16)
if kind == "progress":
return Color(1.0, 0.78, 0.28)
return Color.WHITE
func _cell_from_screen(screen_position: Vector2) -> Vector2i:
var local := screen_position - BOARD_OFFSET
if local.x < 0 or local.y < 0:
@@ -1188,6 +1244,29 @@ func _on_dialogue_requested(lines: Array) -> void:
_show_next_dialogue()
func _on_combat_feedback_requested(unit_id: String, text: String, kind: String) -> void:
var unit := state.get_unit(unit_id)
if unit.is_empty():
return
var cell: Vector2i = unit.get("pos", Vector2i(-1, -1))
if not state.is_inside(cell):
return
var jitter := float((floating_texts.size() % 3) - 1) * 12.0
floating_texts.append({
"text": text,
"kind": kind,
"pos": _floating_text_origin(cell) + Vector2(jitter, 0),
"age": 0.0,
"duration": FLOATING_TEXT_LIFETIME
})
queue_redraw()
func _floating_text_origin(cell: Vector2i) -> Vector2:
var rect := _rect_for_cell(cell)
return rect.position + Vector2(TILE_SIZE * 0.5, 18.0)
func _normalized_dialogue_lines(lines: Array) -> Array:
var result := []
for line in lines:
@@ -1367,6 +1446,7 @@ func _on_restart_pressed() -> void:
battle_result_applied = false
battle_result_summary.clear()
post_battle_dialogue_started = false
floating_texts.clear()
shop_sell_mode = false
selected_skill_id = ""
selected_item_id = ""
@@ -1410,6 +1490,7 @@ func _load_scenario(scenario_id: String) -> void:
battle_result_applied = false
battle_result_summary.clear()
post_battle_dialogue_started = false
floating_texts.clear()
selected_skill_id = ""
selected_item_id = ""
_hide_tactic_menu()
@@ -2288,6 +2369,7 @@ func _show_campaign_complete() -> void:
battle_result_applied = true
battle_result_summary.clear()
post_battle_dialogue_started = true
floating_texts.clear()
move_cells.clear()
attack_cells.clear()
skill_cells.clear()