From a655f9332f0a0d5a2e5a11028e7a37fb5f507206 Mon Sep 17 00:00:00 2001 From: henry Date: Sun, 28 Jun 2026 18:11:26 +0100 Subject: [PATCH] Further refinements in weapone fire helper logic. --- .../player_weapon_resources/vanguard+.tres | 1 + scripts/weapon_fire_helper.gd | 52 ++++++++++++++++--- scripts/weapon_shot.gd | 13 ++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/resources/player_weapon_resources/vanguard+.tres b/resources/player_weapon_resources/vanguard+.tres index 294d890..1e62887 100644 --- a/resources/player_weapon_resources/vanguard+.tres +++ b/resources/player_weapon_resources/vanguard+.tres @@ -14,6 +14,7 @@ speed = 500 projectiles = 5 horizontal_offset = 8.25 stagger_offset = 0.5 +compensate_spawn_geometry = true stagger_animation = true follow_angle = true groups = Array[ExtResource("2_qeb4b")]([ExtResource("3_qeb4b")]) diff --git a/scripts/weapon_fire_helper.gd b/scripts/weapon_fire_helper.gd index 8b6777a..6da3c5d 100644 --- a/scripts/weapon_fire_helper.gd +++ b/scripts/weapon_fire_helper.gd @@ -15,17 +15,34 @@ func compute_standard_props(weapon_data: WeaponShot, projectile_index: int, spaw # Timing: center-first, pairs outward var time_offset: float = abs(index_from_center) * weapon_data.stagger_offset - # Vertical spacing creates the ^ (inverted-V) geometry: outer projectiles fire lower - # (higher Y values, further from the player) than the center projectile, which sits - # higher on screen (lower Y values). This creates a ^ pattern relative to the origin. - var vertical_offset: float = (abs(index_from_center) * weapon_data.projectile_vertical_spacing) / 2.0 - var position: Vector2 = spawn_position + Vector2(bullet_horizontal_offset, +vertical_offset) - # Angle (only when projectiles >= 3, division-by-zero guard) + # Moved before vertical offset to support compensation var bullet_angle: float = 0.0 if total_projectiles >= 3: bullet_angle = (index_from_center / center_index) * (weapon_data.spread_angle / 2.0) + # Vertical spacing creates the ^ (inverted-V) geometry: outer projectiles fire lower + # (higher Y values, further from the player) than the center projectile, which sits + # higher on screen (lower Y values). This creates a ^ pattern relative to the origin. + # When compensate_spawn_geometry is enabled and spread_angle > 0, compute vertical + # offsets to align the geometric spread with velocity vectors. + var vertical_offset: float + if weapon_data.compensate_spawn_geometry and weapon_data.spread_angle > 0 and total_projectiles >= 3: + var velocity_angle_rad: float = abs(bullet_angle) * PI / 180.0 + if index_from_center == 0: + # Center bullet in compensated mode: use full projectile_vertical_spacing + # so the V-shape stays visually smooth (center = 35, outer ≈ 75) + # instead of a broken gap (center = 8.75, outer ≈ 75). + vertical_offset = weapon_data.projectile_vertical_spacing + elif velocity_angle_rad > 0.1: # Guard against division by zero for very small angles + vertical_offset = abs(bullet_horizontal_offset) / tan(velocity_angle_rad) + else: + # Very small angle: fall back to full spacing + vertical_offset = weapon_data.projectile_vertical_spacing + else: + vertical_offset = (abs(index_from_center) * weapon_data.projectile_vertical_spacing) / 2.0 + var position: Vector2 = spawn_position + Vector2(bullet_horizontal_offset, +vertical_offset) + # Fire time var fire_time: float = current_time + time_offset @@ -61,8 +78,9 @@ func compute_grouped_props(group: WeaponProjectileGroup, weapon_data: WeaponShot bullet_angle = -group.angle_offset # Symmetrical spread (guard against < 3 bullets) + var spread_contribution: float = 0.0 if group.bullet_count >= 3: - var spread_contribution = (index_from_center / center_index) * (group.spread_angle / 2.0) + spread_contribution = (index_from_center / center_index) * (group.spread_angle / 2.0) if mirror: bullet_angle -= spread_contribution else: @@ -71,7 +89,25 @@ func compute_grouped_props(group: WeaponProjectileGroup, weapon_data: WeaponShot # Vertical spacing creates the ^ (inverted-V) geometry: outer projectiles fire lower # (higher Y values, further from the player) than the center projectile, which sits # higher on screen (lower Y values). This creates a ^ pattern relative to the origin. - var vertical_offset: float = (abs(index_from_center) * weapon_data.projectile_vertical_spacing) / 2.0 + # When compensate_spawn_geometry is enabled and group.spread_angle > 0, compute + # vertical offsets to preserve the within-group V-shape independently of + # angle_offset, which handles global tilt. + # + # angle_offset tilts the entire group — mixing it into the compensation formula + # breaks the V-shape because the formula assumes velocity vectors converge at the + # origin, but with angle_offset > 0 they fan out from an offset line instead. + # Solution: calculate compensation using only within-group parameters. + var vertical_offset: float + if weapon_data.compensate_spawn_geometry and group.spread_angle > 0 and group.bullet_count >= 3 and index_from_center != 0: + var within_group_angle: float = abs(spread_contribution) # exclude angle_offset + var within_group_angle_rad: float = within_group_angle * PI / 180.0 + var within_group_horizontal: float = abs(index_from_center * group.horizontal_spacing) # exclude group.horizontal_offset + if within_group_angle_rad > 0.1: # Guard against division by zero for very small angles + vertical_offset = within_group_horizontal / tan(within_group_angle_rad) + else: + vertical_offset = (abs(index_from_center) * weapon_data.projectile_vertical_spacing) / 2.0 + else: + vertical_offset = (abs(index_from_center) * weapon_data.projectile_vertical_spacing) / 2.0 var position: Vector2 = spawn_position + Vector2(bullet_horizontal_offset, +vertical_offset) # Timing: group delay + within-group stagger diff --git a/scripts/weapon_shot.gd b/scripts/weapon_shot.gd index 04ed7a2..7d641f8 100644 --- a/scripts/weapon_shot.gd +++ b/scripts/weapon_shot.gd @@ -8,7 +8,10 @@ extends Resource @export var speed: int = 135 @export var projectiles: int = 2 # Vertical spacing between projectiles in a burst (used by WeaponFireHelper to compute the ^ pattern). -# This controls spatial burst geometry, NOT temporal spacing between continuous fire bursts. +# This parameter has a dual role: spatial (computing the ^ burst geometry) and temporal +# (serving as the burst-gating threshold in ShootComponent.shoot()). When compensate_spawn_geometry +# is enabled, this parameter is overridden by geometric compensation for spatial calculations; +# temporal behavior remains unchanged and continues to use this value. @export var projectile_vertical_spacing: float = 35 # Vertical offset from the top of the player's sprite where bullets spawn. # Measured in pixels; negative values fire in front of (above) the ship, @@ -23,6 +26,14 @@ extends Resource @export var spread_angle: float = 0.0 # Time delay per projectile index @export var stagger_offset: float = 0.25 +# When enabled, adjusts spawn vertical offsets so the geometric line +# from the center bullet to each outer bullet matches their velocity +# direction. Eliminates the optical illusion where the spread appears +# wider than the actual bullet trajectories (e.g. spread.tres flies at +# half the angle it visually appears to fan). + +## WARNING: This flag uses a trigonometric formula (horizontal_offset / tan(angle)) that evaluates independently-tuned horizontal and spread angles in grouped shots ONLY! +@export var compensate_spawn_geometry: bool = false @export_category("Effects") # Offset animation playback per projectile index