diff --git a/enemies/EnemyLungeComponent.gd b/enemies/EnemyLungeComponent.gd index 74cbd62..762574d 100644 --- a/enemies/EnemyLungeComponent.gd +++ b/enemies/EnemyLungeComponent.gd @@ -32,14 +32,21 @@ enum Phase { HUNT, WINDUP, CHARGE, RECOVER } # The drift in HUNT is deliberately slower than the charge by an order of # magnitude: the approach is a stalk, the charge is the attack. @export var approach_speed_factor: float = 0.5 -@export var windup_time: float = 1.2 +# Shortened with the cycle count (#115): one back-and-forth in the time two +# and a half used to take would be a slow-motion tell. +@export var windup_time: float = 0.85 # #96 to the letter: the trajectory line appears 300ms before it leaves. @export var telegraph_time: float = 0.3 # The "small back and forth" - it recoils along the charge axis, so the # motion reads as winding up to spring rather than as drifting. -@export var windup_sway: float = 30.0 -@export var windup_cycles: float = 2.5 -@export var charge_speed: float = 950.0 +@export var windup_sway: float = 34.0 +# #115: exactly ONE back and forth, not two and a half. The tell is a recoil +# and a spring; repeating it turned a wind-up into a fidget, and the player +# stopped reading it as "about to happen". +@export var windup_cycles: float = 1.0 +# #115: and it leaves hard. At 950 the charge read as a fast drift rather +# than as the thing the whole wind-up was promising. +@export var charge_speed: float = 1650.0 # It flies THROUGH the locked point rather than stopping on it. A charge # that halted exactly where the ship used to be would look like it was # aiming at empty space on purpose. @@ -93,7 +100,10 @@ func _tick_hunt(enemy: Node2D, delta: float) -> void: return var to_target: Vector2 = target.global_position - enemy.global_position var distance: float = to_target.length() - if distance > detection_range or distance < 0.001: + if distance < 0.001: + return + # #113: see EnemyPursuitComponent. + if not EnemyTargets.ignores_range(enemy) and distance > detection_range: return var direction: Vector2 = to_target / distance if distance <= lock_range: diff --git a/tests/test_lancer_enemy.gd b/tests/test_lancer_enemy.gd index 2832aad..10cfea7 100644 --- a/tests/test_lancer_enemy.gd +++ b/tests/test_lancer_enemy.gd @@ -17,7 +17,8 @@ extends SceneTree # B. from beyond lock_range it does NOT charge - it closes slowly, so the # player sees it coming # C. once locked it holds station for the wind-up and rocks back and forth -# along the charge axis instead of drifting toward the target +# along the charge axis instead of drifting toward the target - ONCE +# (#115), because repeating the tell turned a wind-up into a fidget # D. the trajectory line comes up during the wind-up and lasts about # 300ms - not the whole wind-up, and not nothing # E. the charge is straight, fast, and aimed at where the target WAS: a @@ -42,6 +43,8 @@ var windup_seen: bool = false var windup_anchor: Vector2 = Vector2.ZERO var max_sway: float = 0.0 var max_lateral: float = 0.0 +var sway_crossings: int = 0 +var last_side: int = 0 var telegraph_time_seen: float = 0.0 var charge_start: Vector2 = Vector2.ZERO var dodged_to: Vector2 = Vector2.ZERO @@ -141,7 +144,16 @@ func _process(delta: float) -> bool: windup_anchor = lunge.charge_anchor charge_locked = lunge.locked_point var offset: Vector2 = lancer.global_position - windup_anchor - max_sway = maxf(max_sway, absf(offset.dot(lunge.charge_direction))) + var along: float = offset.dot(lunge.charge_direction) + max_sway = maxf(max_sway, absf(along)) + # One back-and-forth crosses the anchor exactly once: out one + # way, back through it, out the other. Two would cross three + # times. + if absf(along) > 2.0: + var side: int = 1 if along > 0.0 else -1 + if last_side != 0 and side != last_side: + sway_crossings += 1 + last_side = side var across: Vector2 = Vector2(-lunge.charge_direction.y, lunge.charge_direction.x) max_lateral = maxf(max_lateral, absf(offset.dot(across))) if lunge.is_telegraphing(): @@ -170,7 +182,10 @@ func _process(delta: float) -> bool: max_sway, lunge.windup_sway]) if max_lateral > 1.0: return _fail("it wandered %.0fpx sideways during the wind-up instead of holding station" % max_lateral) - print("wind-up: rocked %.0fpx back and forth, %.0fpx sideways" % [max_sway, max_lateral]) + if sway_crossings != 1: + return _fail("the wind-up crossed its anchor %d times - #115 asks for exactly one back and forth" \ + % sway_crossings) + print("wind-up: rocked %.0fpx back and forth once, %.0fpx sideways" % [max_sway, max_lateral]) # 300ms of line, give or take the frames either side of the # threshold. Both bounds matter: a line up for the whole wind-up