Account for counter risk in auto attacks

This commit is contained in:
2026-06-19 20:49:45 +09:00
parent 184a542b04
commit 46df282eab
3 changed files with 220 additions and 43 deletions

View File

@@ -15,6 +15,7 @@ func _init() -> void:
_check_scene_post_move_blocked_command_labels(failures)
_check_scene_post_move_tactic_picker_flow(failures)
_check_scene_post_move_item_picker_flow(failures)
_check_scene_auto_attack_safe_origin_and_counter_preview(failures)
_check_scene_enemy_click_auto_attack(failures)
if failures.is_empty():
@@ -552,6 +553,122 @@ func _check_scene_enemy_click_auto_attack(failures: Array[String]) -> void:
scene.free()
func _check_scene_auto_attack_safe_origin_and_counter_preview(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
scene._create_hud()
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("could not load opening battle for safe auto attack origin")
scene.free()
return
scene.battle_started = true
scene.campaign_complete_screen = false
scene.briefing_panel.visible = false
scene.result_panel.visible = false
scene._clear_pending_move_state()
for unit in scene.state.units:
if unit.get("team", "") == BattleStateScript.TEAM_ENEMY:
unit["pos"] = Vector2i(scene.state.map_size.x - 2, scene.state.map_size.y - 2)
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
var enemy: Dictionary = scene.state.get_unit("yellow_turban_1")
cao_cao["pos"] = Vector2i(1, 3)
cao_cao["move"] = 4
cao_cao["range"] = 2
cao_cao["min_range"] = 1
cao_cao["atk"] = 30
cao_cao["agi"] = 999
cao_cao["hp"] = 80
cao_cao["max_hp"] = 80
cao_cao["moved"] = false
cao_cao["acted"] = false
enemy["pos"] = Vector2i(5, 3)
enemy["range"] = 1
enemy["min_range"] = 1
enemy["atk"] = 70
enemy["agi"] = 0
enemy["hp"] = 80
enemy["max_hp"] = 80
enemy["def"] = 1
enemy["alive"] = true
enemy["controllable"] = true
scene.state.rng.seed = 1
if not scene.state.select_unit("cao_cao"):
failures.append("could not select Cao Cao for safe auto attack origin")
scene.free()
return
scene._refresh_ranges()
var auto_markers := scene._target_selection_marker_entries()
var safe_marker := {}
for marker in auto_markers:
if marker.get("target_id", "") == "yellow_turban_1":
safe_marker = marker
break
if safe_marker.is_empty():
failures.append("safe auto attack setup should mark the reachable enemy: %s" % str(auto_markers))
else:
if safe_marker.get("origin", Vector2i.ZERO) != Vector2i(3, 3):
failures.append("auto attack should prefer the non-counter origin: %s" % str(safe_marker))
if bool(safe_marker.get("counter_in_range", true)):
failures.append("safe auto attack marker should expose no counter risk: %s" % str(safe_marker))
if str(safe_marker.get("kind", "")) != "advance":
failures.append("safe auto attack marker should use advance color: %s" % str(safe_marker))
scene.hover_cell = Vector2i(5, 3)
var safe_badge := scene._target_preview_badge()
if str(safe_badge.get("kind", "")) != "advance" or str(safe_badge.get("text", "")).contains(""):
failures.append("safe auto attack hover should not show counter risk: %s" % str(safe_badge))
scene._handle_board_click(_screen_for_cell(Vector2i(5, 3)))
cao_cao = scene.state.get_unit("cao_cao")
if cao_cao.get("pos", Vector2i.ZERO) != Vector2i(3, 3):
failures.append("auto attack click should use the same safe origin as marker and hover: %s" % str(cao_cao.get("pos", Vector2i.ZERO)))
scene.free()
var risky_scene = BattleSceneScript.new()
risky_scene._create_hud()
if not risky_scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("could not load opening battle for risky auto attack preview")
risky_scene.free()
return
risky_scene.battle_started = true
risky_scene.campaign_complete_screen = false
risky_scene.briefing_panel.visible = false
risky_scene.result_panel.visible = false
risky_scene._clear_pending_move_state()
var risky_attacker: Dictionary = risky_scene.state.get_unit("cao_cao")
var risky_enemy: Dictionary = risky_scene.state.get_unit("yellow_turban_1")
risky_attacker["pos"] = Vector2i(1, 3)
risky_attacker["range"] = 1
risky_attacker["min_range"] = 1
risky_attacker["atk"] = 80
risky_attacker["agi"] = 0
risky_attacker["hp"] = 40
risky_attacker["max_hp"] = 40
risky_enemy["pos"] = Vector2i(5, 3)
risky_enemy["range"] = 1
risky_enemy["min_range"] = 1
risky_enemy["atk"] = 70
risky_enemy["agi"] = 50
risky_enemy["hp"] = 20
risky_enemy["max_hp"] = 20
risky_enemy["def"] = 1
risky_enemy["alive"] = true
risky_enemy["controllable"] = true
var risky_preview := risky_scene._auto_attack_preview_for_origin(risky_attacker, risky_enemy, Vector2i(4, 3))
if risky_preview.is_empty() or not bool(risky_preview.get("would_defeat", false)) or not bool(risky_preview.get("counter_in_range", false)):
failures.append("risky auto attack preview should expose miss-counter risk on a potential KO: %s" % str(risky_preview))
var risky_badge := risky_scene._auto_attack_target_preview_badge_for_origin(risky_attacker, risky_enemy, Vector2i(4, 3))
if not str(risky_badge.get("text", "")).contains("") or not ["counter", "danger"].has(str(risky_badge.get("kind", ""))):
failures.append("risky auto attack badge should show counter risk: %s" % str(risky_badge))
risky_enemy["controllable"] = false
var escort_preview := risky_scene._auto_attack_preview_for_origin(risky_attacker, risky_enemy, Vector2i(4, 3))
if bool(escort_preview.get("counter_in_range", false)):
failures.append("non-controllable targets should not show counter risk: %s" % str(escort_preview))
risky_scene.free()
func _check_scene_post_move_tactic_picker_flow(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
scene._create_hud()