Show move attack targets

This commit is contained in:
2026-06-19 19:51:31 +09:00
parent 6d95f13794
commit 36001c8b2f
3 changed files with 157 additions and 6 deletions

View File

@@ -3460,12 +3460,13 @@ func _draw_units() -> void:
func _draw_target_selection_markers() -> void:
if not _is_targeting_mode():
var markers := _target_selection_marker_entries()
if markers.is_empty():
return
var font := _draw_ui_font(true)
for marker in _target_selection_marker_entries():
for marker in markers:
var cell: Vector2i = marker.get("cell", Vector2i(-1, -1))
if not state.is_inside(cell):
if not state.is_inside(cell) or not _cell_is_in_visible_bounds(cell):
continue
var rect := _rect_for_cell(cell)
var marker_rect := Rect2(rect.position + Vector2(7.0, TILE_SIZE - 24.0), Vector2(TILE_SIZE - 14.0, 18.0))
@@ -3521,9 +3522,49 @@ func _attack_target_marker_entries() -> Array[Dictionary]:
"text": "%s %d%%" % [result_text, hit_chance],
"kind": _attack_preview_badge_kind(preview)
})
if not basic_attack_targeting and not _has_pending_move():
_append_auto_attack_target_marker_entries(result, selected)
return result
func _append_auto_attack_target_marker_entries(result: Array[Dictionary], selected: Dictionary) -> void:
if selected.is_empty() or bool(selected.get("moved", false)) or bool(selected.get("acted", false)):
return
var current_cell: Vector2i = selected.get("pos", Vector2i(-1, -1))
for target in state.units:
if not bool(target.get("alive", false)) or not bool(target.get("deployed", true)):
continue
if target.get("team", "") == selected.get("team", ""):
continue
var target_id := str(target.get("id", ""))
if target_id.is_empty() or _marker_entries_have_target(result, target_id):
continue
var attack_origin := _best_auto_attack_origin(selected, target)
if attack_origin == Vector2i(-1, -1) or attack_origin == current_cell:
continue
var target_cell: Vector2i = target.get("pos", Vector2i(-1, -1))
if not state.is_inside(target_cell):
continue
var badge := _auto_attack_target_preview_badge_for_origin(selected, target, attack_origin)
if badge.is_empty():
continue
result.append({
"cell": target_cell,
"target_id": target_id,
"text": "행공",
"kind": str(badge.get("kind", "advance")),
"origin": attack_origin,
"requires_move": true
})
func _marker_entries_have_target(entries: Array[Dictionary], target_id: String) -> bool:
for entry in entries:
if str(entry.get("target_id", "")) == target_id:
return true
return false
func _skill_target_marker_entries() -> Array[Dictionary]:
var result: Array[Dictionary] = []
if selected_skill_id.is_empty():
@@ -3630,7 +3671,11 @@ func _try_auto_move_attack(selected: Dictionary, target: Dictionary) -> bool:
func _best_auto_attack_origin(selected: Dictionary, target: Dictionary) -> Vector2i:
var unit_id := str(selected.get("id", ""))
if unit_id.is_empty():
var target_id := str(target.get("id", ""))
if unit_id.is_empty() or target_id.is_empty():
return Vector2i(-1, -1)
var base_preview := state.get_damage_preview(unit_id, target_id)
if base_preview.is_empty() or bool(base_preview.get("attack_locked", false)):
return Vector2i(-1, -1)
var current_cell: Vector2i = selected.get("pos", Vector2i(-1, -1))
var target_cell: Vector2i = target.get("pos", Vector2i(-1, -1))
@@ -4605,6 +4650,11 @@ func _target_preview_badge() -> Dictionary:
if _has_pending_move() and not basic_attack_targeting:
return {}
var attack_badge := _attack_target_preview_badge(selected, target)
if not attack_badge.is_empty() and (str(attack_badge.get("kind", "")) != "invalid" or basic_attack_targeting or _has_pending_move()):
return attack_badge
var auto_attack_badge := _auto_attack_target_preview_badge(selected, target)
if not auto_attack_badge.is_empty():
return auto_attack_badge
if not attack_badge.is_empty():
return attack_badge
return _hover_intent_preview_badge(selected, target)
@@ -4645,6 +4695,59 @@ func _attack_preview_badge_kind(preview: Dictionary) -> String:
return "damage"
func _auto_attack_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
if selected.is_empty() or target.is_empty():
return {}
if selected.get("team", "") == target.get("team", ""):
return {}
if bool(selected.get("moved", false)) or bool(selected.get("acted", false)):
return {}
var current_cell: Vector2i = selected.get("pos", Vector2i(-1, -1))
var attack_origin := _best_auto_attack_origin(selected, target)
if attack_origin == Vector2i(-1, -1) or attack_origin == current_cell:
return {}
return _auto_attack_target_preview_badge_for_origin(selected, target, attack_origin)
func _auto_attack_target_preview_badge_for_origin(selected: Dictionary, target: Dictionary, attack_origin: Vector2i) -> Dictionary:
var target_cell: Vector2i = target.get("pos", Vector2i(-1, -1))
if not state.is_inside(attack_origin) or not state.is_inside(target_cell):
return {}
var candidate_attacker := selected.duplicate(true)
candidate_attacker["pos"] = attack_origin
var damage := state.calculate_damage(candidate_attacker, target)
var hit_chance := state.calculate_hit_chance(candidate_attacker, target)
var result_text := "행공 퇴각" if int(target.get("hp", 0)) - damage <= 0 else "행공 -%d" % damage
var counter_text := _format_auto_attack_counter_badge_suffix(candidate_attacker, target, damage)
return _make_target_preview_badge_at("%s %d%%%s" % [result_text, hit_chance, counter_text], _auto_attack_preview_badge_kind(candidate_attacker, target, damage), target_cell)
func _format_auto_attack_counter_badge_suffix(candidate_attacker: Dictionary, target: Dictionary, damage: int) -> String:
if int(target.get("hp", 0)) - damage <= 0:
return ""
var origin: Vector2i = candidate_attacker.get("pos", Vector2i(-1, -1))
var target_cell: Vector2i = target.get("pos", Vector2i(-1, -1))
if not _can_attack_target_from(target, target_cell, origin):
return ""
var counter_damage := state.calculate_damage(target, candidate_attacker)
if int(candidate_attacker.get("hp", 0)) - counter_damage <= 0:
return " 반격!"
return "%d" % counter_damage
func _auto_attack_preview_badge_kind(candidate_attacker: Dictionary, target: Dictionary, damage: int) -> String:
if int(target.get("hp", 0)) - damage <= 0:
return "advance"
var origin: Vector2i = candidate_attacker.get("pos", Vector2i(-1, -1))
var target_cell: Vector2i = target.get("pos", Vector2i(-1, -1))
if not _can_attack_target_from(target, target_cell, origin):
return "advance"
var counter_damage := state.calculate_damage(target, candidate_attacker)
if int(candidate_attacker.get("hp", 0)) - counter_damage <= 0:
return "danger"
return "counter"
func _skill_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
return _skill_target_preview_badge_for_cell(selected, target, hover_cell)
@@ -4810,6 +4913,8 @@ func _target_preview_badge_color(kind: String) -> Color:
return Color(1.0, 0.64, 0.22)
if kind == "danger":
return Color(1.0, 0.18, 0.14)
if kind == "advance":
return Color(0.44, 0.82, 0.76)
if kind == "move":
return Color(0.38, 0.70, 1.0)
if kind == "select":