Add floating combat text
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user