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

@@ -6,6 +6,7 @@ const DataCatalogScript := preload("res://scripts/core/data_catalog.gd")
signal changed
signal log_added(message: String)
signal dialogue_requested(lines: Array)
signal combat_feedback_requested(unit_id: String, text: String, kind: String)
const TEAM_PLAYER := "player"
const TEAM_ENEMY := "enemy"
@@ -848,6 +849,7 @@ func _resolve_skill_support(caster: Dictionary, target: Dictionary, skill_id: St
target["name"],
_format_support_effects(skill)
])
_emit_combat_feedback(target, _format_support_popup(skill), "support")
return true
@@ -887,6 +889,24 @@ func _format_support_effects(skill: Dictionary) -> String:
return _join_strings(parts, ", ")
func _format_support_popup(skill: Dictionary) -> String:
var parts := []
for effect in skill.get("effects", []):
if typeof(effect) != TYPE_DICTIONARY:
continue
if str(effect.get("type", "")) != "stat_bonus":
continue
var stat := str(effect.get("stat", ""))
var amount := int(effect.get("amount", 0))
if not _is_supported_status_stat(stat) or amount == 0:
continue
var sign := "+" if amount > 0 else ""
parts.append("%s %s%d" % [stat.to_upper(), sign, amount])
if parts.is_empty():
return "Support"
return _join_strings(parts, ", ")
func _format_active_status_effects(unit: Dictionary) -> String:
var parts := []
for effect in _active_status_effects(unit):
@@ -1621,11 +1641,13 @@ func _apply_item_effects(user: Dictionary, target: Dictionary, item: Dictionary)
if healed > 0:
applied = true
_emit_log("%s recovers %d HP." % [target["name"], healed])
_emit_combat_feedback(target, "+%d HP" % healed, "heal")
elif effect_type == "heal_mp":
var restored := _apply_item_mp_heal(target, int(effect.get("amount", 0)))
if restored > 0:
applied = true
_emit_log("%s recovers %d MP." % [target["name"], restored])
_emit_combat_feedback(target, "+%d MP" % restored, "mp")
if not applied:
_emit_log("%s has no effect on %s." % [item.get("name", "Item"), target["name"]])
return applied
@@ -1997,14 +2019,17 @@ func _resolve_attack(attacker: Dictionary, target: Dictionary, is_counter := fal
var verb := "counterattacks" if is_counter else "attacks"
if rng.randi_range(1, 100) > hit_chance:
_emit_log("%s %s %s but misses. Hit %d%%." % [attacker["name"], verb, target["name"], hit_chance])
_emit_combat_feedback(target, "MISS", "miss")
return {"hit": false, "defeated": false}
var damage := calculate_damage(attacker, target)
target["hp"] = max(0, int(target["hp"]) - damage)
_emit_log("%s %s %s for %d damage. Hit %d%%." % [attacker["name"], verb, target["name"], damage, hit_chance])
_emit_combat_feedback(target, "-%d" % damage, "damage")
if int(target["hp"]) <= 0:
target["alive"] = false
_emit_log("%s is defeated." % target["name"])
_emit_combat_feedback(target, "DOWN", "defeat")
return {"hit": true, "defeated": true}
return {"hit": true, "defeated": false}
@@ -2018,10 +2043,12 @@ func _resolve_skill_damage(caster: Dictionary, target: Dictionary, skill: Dictio
target["name"],
damage
])
_emit_combat_feedback(target, "-%d" % damage, "damage")
if int(target["hp"]) <= 0:
target["alive"] = false
_emit_log("%s is defeated." % target["name"])
_emit_combat_feedback(target, "DOWN", "defeat")
return true
return false
@@ -2035,6 +2062,7 @@ func _resolve_skill_heal(caster: Dictionary, target: Dictionary, skill: Dictiona
target["name"],
healed
])
_emit_combat_feedback(target, "+%d HP" % healed, "heal")
return healed
@@ -2060,6 +2088,7 @@ func _level_up(unit: Dictionary) -> void:
for stat in ["atk", "def", "int", "agi"]:
unit[stat] = int(unit.get(stat, 0)) + _growth_gain(unit, stat, 1)
_emit_log("%s reaches Lv.%d." % [unit["name"], unit["level"]])
_emit_combat_feedback(unit, "Lv.%d" % int(unit["level"]), "progress")
if str(unit.get("team", "")) == TEAM_PLAYER:
_record_progression_event({
"type": "level_up",
@@ -2095,6 +2124,7 @@ func _try_promote_unit(unit: Dictionary) -> void:
unit["class_id"] = next_class_id
_apply_class_runtime_fields(unit, next_class_id)
_emit_log("%s promotes to %s." % [unit["name"], unit["class"]])
_emit_combat_feedback(unit, "PROMOTE", "progress")
if str(unit.get("team", "")) == TEAM_PLAYER:
_record_progression_event({
"type": "promotion",
@@ -2526,6 +2556,7 @@ func _expire_status_effects_for_team(team: String) -> void:
unit["status_effects"] = kept_effects
for expired_effect_name in expired_names:
_emit_log("%s's %s fades." % [unit["name"], expired_effect_name])
_emit_combat_feedback(unit, "FADES", "fade")
func _manhattan(a: Vector2i, b: Vector2i) -> int:
@@ -2549,5 +2580,14 @@ func _emit_log(message: String) -> void:
log_added.emit(message)
func _emit_combat_feedback(unit: Dictionary, text: String, kind: String) -> void:
if unit.is_empty() or text.is_empty():
return
var unit_id := str(unit.get("id", ""))
if unit_id.is_empty():
return
combat_feedback_requested.emit(unit_id, text, kind)
func _notify_changed() -> void:
changed.emit()