diff --git a/scripts/event_bus.gd b/scripts/event_bus.gd index 07bd2b5..fc12a05 100644 --- a/scripts/event_bus.gd +++ b/scripts/event_bus.gd @@ -2,6 +2,6 @@ extends Node @warning_ignore_start("unused_signal") # since otherwise Godot will throw a warning that the signal is unused in current scope -signal weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: Vector2) +signal weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: float) @warning_ignore_restore("unused_signal") diff --git a/scripts/notification_display.gd b/scripts/notification_display.gd index 38622e0..4dbbc48 100644 --- a/scripts/notification_display.gd +++ b/scripts/notification_display.gd @@ -6,7 +6,7 @@ class_name NotificationDisplay extends Control var _player: Node2D = null var _sprite_size: Vector2 = Vector2.ZERO -var _origin_offset: Vector2 = Vector2.ZERO +var _origin_offset: float = 0.0 var _follow_player: bool = false var _connector_line: Line2D = null @@ -23,7 +23,7 @@ func _ready() -> void: _container.add_theme_constant_override("margin_right", 4) -func show_notification(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: Vector2) -> void: +func show_notification(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: float) -> void: modulate.a = 0.0 label.add_theme_font_size_override("font_size", 8) @@ -107,7 +107,7 @@ func _update_position() -> void: var player_pos = _player.position # Y position uses the weapon origin offset from player center - var notif_y = player_pos.y + _origin_offset.y - (size.y / 2.0) + var notif_y = player_pos.y + _origin_offset - (size.y / 2.0) # X position is 10px to the right of player's sprite edge var right_edge = player_pos.x + (_sprite_size.x / 2.0) @@ -164,7 +164,7 @@ func _update_connector() -> void: # Point 2: player's origin + ENDPOINT_OFFSET, converted to notification's local space const ENDPOINT_OFFSET = Vector2(4, 7) # offset from player's origin to connector endpoint - var target = _player.position + Vector2(+ENDPOINT_OFFSET.x, _origin_offset.y + ENDPOINT_OFFSET.y) + var target = _player.position + Vector2(+ENDPOINT_OFFSET.x, _origin_offset + ENDPOINT_OFFSET.y) var point2 = target - position _connector_line.points = PackedVector2Array([point0, point1, point2]) diff --git a/scripts/notification_manager.gd b/scripts/notification_manager.gd index cc60141..eb8028e 100644 --- a/scripts/notification_manager.gd +++ b/scripts/notification_manager.gd @@ -8,7 +8,7 @@ func _ready() -> void: EventBus.weapon_changed.connect(_on_weapon_changed) -func _on_weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: Vector2) -> void: +func _on_weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: float) -> void: if notification_scene == null: return diff --git a/scripts/shoot_component.gd b/scripts/shoot_component.gd index 90b2344..bf783c1 100644 --- a/scripts/shoot_component.gd +++ b/scripts/shoot_component.gd @@ -17,6 +17,19 @@ func shoot(): player.travel = 0.0 +func _get_spawn_position(weapon_data: WeaponShot) -> Vector2: + # Determine sprite height with a region_rect → texture fallback. + var sprite_height: float = 0.0 + if ship.region_rect.has_area(): + sprite_height = ship.region_rect.size.y + else: + sprite_height = ship.texture.get_height() + + # Top edge of the sprite in world space, then offset by origin. + var top_edge = ship.global_position.y - (sprite_height / 2.0) + return Vector2(ship.global_position.x, top_edge + weapon_data.origin) + + # Standard projectile logic (delegates to WeaponFireHelper) func _shoot_standard(weapon_data: WeaponShot, current_time: float): var total_projectiles: int = weapon_data.projectiles @@ -26,8 +39,8 @@ func _shoot_standard(weapon_data: WeaponShot, current_time: float): var bullet := weapon_data.bullet_scene.instantiate() as Area2D get_tree().root.add_child(bullet) - # Delegate math to the helper, with spawn position offset by weapon origin - var spawn_position = ship.global_position + weapon_data.origin + # Delegate math to the helper + var spawn_position = _get_spawn_position(weapon_data) var props = fire_helper.compute_standard_props(weapon_data, b, spawn_position, current_time) bullet.position = props.position bullet.angle = props.angle @@ -50,7 +63,7 @@ func _shoot_grouped(weapon_data: WeaponShot, current_time: float): get_tree().root.add_child(bullet) # Apply computed properties for the original bullet - var spawn_position = ship.global_position + weapon_data.origin + var spawn_position = _get_spawn_position(weapon_data) var props = fire_helper.compute_grouped_props(group, weapon_data, b, spawn_position, current_time, false) bullet.position = props.position bullet.angle = props.angle @@ -66,7 +79,7 @@ func _shoot_grouped(weapon_data: WeaponShot, current_time: float): get_tree().root.add_child(mirror_bullet) # Apply computed properties for the mirrored bullet - var mirror_spawn = ship.global_position + weapon_data.origin + var mirror_spawn = _get_spawn_position(weapon_data) var mirror_props = fire_helper.compute_grouped_props(group, weapon_data, b, mirror_spawn, current_time, true) mirror_bullet.position = mirror_props.position mirror_bullet.angle = mirror_props.angle diff --git a/scripts/weapon_component.gd b/scripts/weapon_component.gd index 3dd3497..1442102 100644 --- a/scripts/weapon_component.gd +++ b/scripts/weapon_component.gd @@ -21,7 +21,7 @@ func _ready() -> void: func _emit_weapon_changed() -> void: var ship: Sprite2D = get_parent().get_node("Ship") var sprite_size = ship.get_rect().size - var origin_offset: Vector2 = weapon_data.origin if weapon_data else Vector2.ZERO + var origin_offset: float = weapon_data.origin if weapon_data else 0.0 EventBus.weapon_changed.emit(weapon_data.shot_name, get_parent(), sprite_size, origin_offset) diff --git a/scripts/weapon_fire_helper.gd b/scripts/weapon_fire_helper.gd index 5d7b788..8b6777a 100644 --- a/scripts/weapon_fire_helper.gd +++ b/scripts/weapon_fire_helper.gd @@ -15,11 +15,11 @@ 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 higher - # than the center projectile. Negate the offset so outer bullets are pushed upward - # (lower y values in Godot), creating a ^ pattern relative to the origin. + # 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) + var position: Vector2 = spawn_position + Vector2(bullet_horizontal_offset, +vertical_offset) # Angle (only when projectiles >= 3, division-by-zero guard) var bullet_angle: float = 0.0 @@ -68,8 +68,11 @@ func compute_grouped_props(group: WeaponProjectileGroup, weapon_data: WeaponShot else: bullet_angle += spread_contribution - # Position: horizontal offset only (vertical position comes from spawn_position) - var position: Vector2 = spawn_position + Vector2(bullet_horizontal_offset, 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. + 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) # Timing: group delay + within-group stagger var time_offset: float = group.group_delay + abs(index_from_center) * group.stagger_offset diff --git a/scripts/weapon_shot.gd b/scripts/weapon_shot.gd index 66c946b..04ed7a2 100644 --- a/scripts/weapon_shot.gd +++ b/scripts/weapon_shot.gd @@ -10,16 +10,11 @@ extends Resource # 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. @export var projectile_vertical_spacing: float = 35 -# Spawn offset strictly relative to the ship node's global position (origin). -# This offset is measured from the ship node's center, not the sprite texture. -# x is usually 0 (horizontal centering), y controls vertical fire position. -# Negative Y moves bullets "above" the ship (in front of it); positive Y -# moves them "below" (behind it). The default of (0, -25) places the burst -# 25 pixels above (in front of) the ship center. -# All weapon resources in this project use the default. Custom weapons with -# explicit origin.y > 0 (positive) will have their burst pattern appear -# behind or below the ship -- verify visually if adjusting. -@export var origin: Vector2 = Vector2(0, -25) +# 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, +# positive values fire behind (below) it. The default of -25 places the +# burst 25 pixels above the sprite's top edge. +@export var origin: float = -25.0 @export_category("Spread") # Horizontal distance between projectiles