diff --git a/graphics/vanguard+.png b/graphics/vanguard+.png deleted file mode 100644 index dee5a97..0000000 Binary files a/graphics/vanguard+.png and /dev/null differ diff --git a/graphics/vanguard+.png.import b/graphics/vanguard+.png.import deleted file mode 100644 index e7f08c0..0000000 --- a/graphics/vanguard+.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dsjhp3mh5pqg3" -path="res://.godot/imported/vanguard+.png-f0cc118ae237fbac7d80b37a3b3de33e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://graphics/vanguard+.png" -dest_files=["res://.godot/imported/vanguard+.png-f0cc118ae237fbac7d80b37a3b3de33e.ctex"] - -[params] - -compress/mode=0 -compress/high_quality=false -compress/lossy_quality=0.7 -compress/uastc_level=0 -compress/rdo_quality_loss=0.0 -compress/hdr_compression=1 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/channel_remap/red=0 -process/channel_remap/green=1 -process/channel_remap/blue=2 -process/channel_remap/alpha=3 -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1 diff --git a/resources/pickups/drift_diagonal.tres b/resources/pickups/drift_diagonal.tres deleted file mode 100644 index f7daa49..0000000 --- a/resources/pickups/drift_diagonal.tres +++ /dev/null @@ -1,14 +0,0 @@ -[gd_resource type="Resource" script_class="PickupDriftBehavior" format=3 uid="uid://cdiglwsw46g4y"] - -[ext_resource type="Script" uid="uid://b4lar4f3526b1" path="res://resources/pickups/pickup_drift_behavior.gd" id="1_drift"] - -[resource] -script = ExtResource("1_drift") -drift_type = 3 -wave_amplitude = 100.0 -wave_frequency = 2.0 -drift_speed = 30.0 -diagonal_forward = true -drift_along_x = true -reverse_direction = false -randomize_origin = false diff --git a/resources/pickups/drift_leaf_fall.tres b/resources/pickups/drift_leaf_fall.tres deleted file mode 100644 index bc11619..0000000 --- a/resources/pickups/drift_leaf_fall.tres +++ /dev/null @@ -1,14 +0,0 @@ -[gd_resource type="Resource" script_class="PickupDriftBehavior" format=3 uid="uid://pickup_drift_leaf"] - -[ext_resource type="Script" uid="uid://pickup_drift_uid" path="res://resources/pickups/pickup_drift_behavior.gd" id="1_drift"] - -[resource] -script = ExtResource("1_drift") -drift_type = 2 -drift_speed = 30.0 -fall_speed = 15.0 -wave_amplitude = 40.0 -wave_frequency = 1.0 -drift_along_x = true -reverse_direction = false -randomize_origin = false diff --git a/resources/pickups/drift_sine_wave.tres b/resources/pickups/drift_sine_wave.tres deleted file mode 100644 index a295e87..0000000 --- a/resources/pickups/drift_sine_wave.tres +++ /dev/null @@ -1,13 +0,0 @@ -[gd_resource type="Resource" script_class="PickupDriftBehavior" format=3 uid="uid://cr7lye10sx1q6"] - -[ext_resource type="Script" uid="uid://b4lar4f3526b1" path="res://resources/pickups/pickup_drift_behavior.gd" id="1_drift"] - -[resource] -script = ExtResource("1_drift") -drift_type = 1 -drift_speed = 30.0 -wave_amplitude = 40.0 -wave_frequency = 1.0 -drift_along_x = true -reverse_direction = false -randomize_origin = false diff --git a/resources/pickups/pickup_behavior.gd b/resources/pickups/pickup_behavior.gd deleted file mode 100644 index 2387151..0000000 --- a/resources/pickups/pickup_behavior.gd +++ /dev/null @@ -1,40 +0,0 @@ -class_name PickupBehavior extends Resource - -## Base resource that defines how a pickup moves and looks on screen. -## Assign to [member pickup_behavior] on a PickupBehaviorController. - -@export_group("Animation") -## Whether the pickup bobs up and down in place. -@export var bob_enabled: bool = true -## Bob oscillation frequency in cycles per second. -@export var bob_frequency: float = 2.0 -## Bob vertical amplitude in pixels (peak-to-center). -@export var bob_amplitude: float = 4.0 - -## Whether the pickup subtly shakes (jitter) in place. -@export var shake_enabled: bool = false -## Shake frequency in Hz (how fast the jitter oscillates). -@export var shake_frequency: float = 8.0 -## Shake amplitude in pixels (peak-to-center). -@export var shake_amplitude: float = 1.5 - -## Optional fade-in duration (0 = no fade). -@export var fade_in_duration: float = 0.0 - -var _elapsed: float = 0.0 - - -func start() -> void: - _elapsed = 0.0 - - -func process(delta: float) -> float: - # Returns the current opacity factor (0..1) based on fade timers. - _elapsed += delta - - var target_alpha: float = 1.0 - - if fade_in_duration > 0 and _elapsed < fade_in_duration: - target_alpha = clampf(_elapsed / fade_in_duration, 0.0, 1.0) - - return target_alpha diff --git a/resources/pickups/pickup_behavior.gd.uid b/resources/pickups/pickup_behavior.gd.uid deleted file mode 100644 index 3d10751..0000000 --- a/resources/pickups/pickup_behavior.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d0l8e2ymk0lx1 diff --git a/resources/pickups/pickup_drift_behavior.gd b/resources/pickups/pickup_drift_behavior.gd deleted file mode 100644 index 24f531b..0000000 --- a/resources/pickups/pickup_drift_behavior.gd +++ /dev/null @@ -1,48 +0,0 @@ -class_name PickupDriftBehavior extends Resource - -## Base resource that defines the global travel pattern of a pickup across -## the screen. Assign to [member drift_behavior] on a PickupBehaviorController. - -enum DriftType { - STATIONARY, ## Does not move (aside from bob/shake). - SINE_WAVE, ## Travels in a sine curve. - LEAF_FALL, ## Drifts downward with gentle horizontal sway. - DIAGONAL, ## Moves diagonally in one direction. -} - -@export_group("Drift") -## The drift pattern to apply. -@export var drift_type: DriftType = DriftType.STATIONARY - -## Horizontal speed in pixels per second (applies to SINE_WAVE, LEAF_FALL). -@export var drift_speed: float = 30.0 -## Vertical fall speed in pixels per second (applies to LEAF_FALL). -@export var fall_speed: float = 15.0 - -## Horizontal wave amplitude in pixels (applies to SINE_WAVE, LEAF_FALL). -@export var wave_amplitude: float = 40.0 -## Wave frequency in cycles per second (applies to SINE_WAVE, LEAF_FALL). -@export var wave_frequency: float = 1.0 - -## Diagonal direction: +X forward, -X backward (applies to DIAGONAL). -@export var diagonal_forward: bool = true - -## Main drift axis for SINE_WAVE and LEAF_FALL. -## true = horizontal travel (x), false = vertical travel (y). -@export var drift_along_x: bool = true - -## Reverse the primary direction (left/right or up/down). -@export var reverse_direction: bool = false - -## Randomize the perpendicular axis origin (y if horizontal, x if vertical). -@export var randomize_origin: bool = false - -## Phase offset in radians — randomize per-instance for variety. -@export var phase_offset: float = 0.0 - -var _elapsed: float = 0.0 - - -func start() -> void: - ## Reset drift state when a new pickup is spawned. - _elapsed = 0.0 diff --git a/resources/pickups/pickup_drift_behavior.gd.uid b/resources/pickups/pickup_drift_behavior.gd.uid deleted file mode 100644 index f097edf..0000000 --- a/resources/pickups/pickup_drift_behavior.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4lar4f3526b1 diff --git a/resources/pickups/rare_powerup.tres b/resources/pickups/rare_powerup.tres deleted file mode 100644 index 007d667..0000000 --- a/resources/pickups/rare_powerup.tres +++ /dev/null @@ -1,13 +0,0 @@ -[gd_resource type="Resource" script_class="PickupBehavior" format=3 uid="uid://pickup_rare_powerup"] - -[ext_resource type="Script" uid="uid://pickup_behavior_uid" path="res://resources/pickups/pickup_behavior.gd" id="1_behavior"] - -[resource] -script = ExtResource("1_behavior") -bob_enabled = true -bob_frequency = 3.5 -bob_amplitude = 6.0 -shake_enabled = true -shake_frequency = 12.0 -shake_amplitude = 1.5 -fade_in_duration = 0.5 diff --git a/resources/pickups/std_powerup.tres b/resources/pickups/std_powerup.tres deleted file mode 100644 index e72a0c0..0000000 --- a/resources/pickups/std_powerup.tres +++ /dev/null @@ -1,8 +0,0 @@ -[gd_resource type="Resource" script_class="PickupBehavior" format=3 uid="uid://dflj31qle75ol"] - -[ext_resource type="Script" uid="uid://d0l8e2ymk0lx1" path="res://resources/pickups/pickup_behavior.gd" id="1_behavior"] - -[resource] -script = ExtResource("1_behavior") -shake_enabled = true -fade_in_duration = 0.3 diff --git a/resources/player_weapon_resources/spread.tres b/resources/player_weapon_resources/spread.tres index 912569c..bb4794c 100644 --- a/resources/player_weapon_resources/spread.tres +++ b/resources/player_weapon_resources/spread.tres @@ -3,13 +3,11 @@ [ext_resource type="PackedScene" uid="uid://ddpclu2vdy2ve" path="res://scenes/player_weapons/weapon_stock.tscn" id="1_pn64g"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="2_4b7mw"] [ext_resource type="Script" uid="uid://d0ios82vubijg" path="res://scripts/weapon_projectile_group.gd" id="2_ak5x4"] -[ext_resource type="Texture2D" uid="uid://dwc5fjwhqikoo" path="res://graphics/spread.png" id="3_ak5x4"] [resource] script = ExtResource("2_4b7mw") shot_name = "spread" bullet_scene = ExtResource("1_pn64g") -pickup_icon = ExtResource("3_ak5x4") damage = 5 speed = 720 projectiles = 5 @@ -17,6 +15,4 @@ horizontal_offset = 8.25 spread_angle = 25.0 stagger_offset = 0.5 stagger_animation = true -power_level = 1 -force_replace_all = true metadata/_custom_type_script = "uid://7n1itonn35fm" diff --git a/resources/player_weapon_resources/stock.tres b/resources/player_weapon_resources/stock.tres index f975f9a..79f8f83 100644 --- a/resources/player_weapon_resources/stock.tres +++ b/resources/player_weapon_resources/stock.tres @@ -3,17 +3,13 @@ [ext_resource type="PackedScene" uid="uid://ddpclu2vdy2ve" path="res://scenes/player_weapons/weapon_stock.tscn" id="1_lcw6o"] [ext_resource type="Script" uid="uid://d0ios82vubijg" path="res://scripts/weapon_projectile_group.gd" id="2_e245k"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="2_ym48c"] -[ext_resource type="Texture2D" uid="uid://dtggedl0127vk" path="res://graphics/stock.png" id="3_e245k"] [resource] script = ExtResource("2_ym48c") shot_name = "stock" bullet_scene = ExtResource("1_lcw6o") -pickup_icon = ExtResource("3_e245k") damage = 2 speed = 650 horizontal_offset = 12.0 stagger_offset = 0.0 -power_level = 1 -force_replace_all = true metadata/_custom_type_script = "uid://7n1itonn35fm" diff --git a/resources/player_weapon_resources/tri.tres b/resources/player_weapon_resources/tri.tres index be0811a..c16b614 100644 --- a/resources/player_weapon_resources/tri.tres +++ b/resources/player_weapon_resources/tri.tres @@ -1,21 +1,17 @@ [gd_resource type="Resource" script_class="WeaponShot" format=3 uid="uid://cck3gmnhu5agc"] -[ext_resource type="PackedScene" uid="uid://ddpclu2vdy2ve" path="res://scenes/player_weapons/weapon_stock.tscn" id="1_bhv8k"] +[ext_resource type="PackedScene" uid="uid://clpyd8qfwthk2" path="res://scenes/player_weapons/weapon_spreadshot.tscn" id="1_07hyh"] [ext_resource type="Script" uid="uid://d0ios82vubijg" path="res://scripts/weapon_projectile_group.gd" id="2_bhv8k"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="2_syw6j"] -[ext_resource type="Texture2D" uid="uid://cne450xyao5xl" path="res://graphics/tri.png" id="3_fw2v3"] [resource] script = ExtResource("2_syw6j") shot_name = "tri" -bullet_scene = ExtResource("1_bhv8k") -pickup_icon = ExtResource("3_fw2v3") +bullet_scene = ExtResource("1_07hyh") damage = 3 speed = 700 projectiles = 3 horizontal_offset = 12.5 stagger_offset = 0.35 stagger_animation = true -power_level = 1 -force_replace_all = true metadata/_custom_type_script = "uid://7n1itonn35fm" diff --git a/resources/player_weapon_resources/vanguard+.tres b/resources/player_weapon_resources/vanguard+.tres index 6397f26..1e62887 100644 --- a/resources/player_weapon_resources/vanguard+.tres +++ b/resources/player_weapon_resources/vanguard+.tres @@ -4,13 +4,11 @@ [ext_resource type="Script" uid="uid://d0ios82vubijg" path="res://scripts/weapon_projectile_group.gd" id="2_qeb4b"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="3_fh2eq"] [ext_resource type="Resource" uid="uid://ch8qch6lx1mdx" path="res://resources/player_weapon_resources/patterns/phallanx.tres" id="3_qeb4b"] -[ext_resource type="Texture2D" uid="uid://dsjhp3mh5pqg3" path="res://graphics/vanguard+.png" id="4_fh2eq"] [resource] script = ExtResource("3_fh2eq") shot_name = "vanguard+" bullet_scene = ExtResource("1_lqlv8") -pickup_icon = ExtResource("4_fh2eq") damage = 5 speed = 500 projectiles = 5 @@ -20,6 +18,4 @@ compensate_spawn_geometry = true stagger_animation = true follow_angle = true groups = Array[ExtResource("2_qeb4b")]([ExtResource("3_qeb4b")]) -power_level = 1 -force_replace_all = true metadata/_custom_type_script = "uid://7n1itonn35fm" diff --git a/resources/player_weapon_resources/vanguard.tres b/resources/player_weapon_resources/vanguard.tres index 3e6a379..006c9a5 100644 --- a/resources/player_weapon_resources/vanguard.tres +++ b/resources/player_weapon_resources/vanguard.tres @@ -3,19 +3,15 @@ [ext_resource type="PackedScene" uid="uid://ddpclu2vdy2ve" path="res://scenes/player_weapons/weapon_stock.tscn" id="1_hm46f"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="2_w7xlm"] [ext_resource type="Script" uid="uid://d0ios82vubijg" path="res://scripts/weapon_projectile_group.gd" id="2_xf183"] -[ext_resource type="Texture2D" uid="uid://b34vaaw3u84ae" path="res://graphics/vanguard.png" id="3_xf183"] [resource] script = ExtResource("2_w7xlm") shot_name = "vanguard" bullet_scene = ExtResource("1_hm46f") -pickup_icon = ExtResource("3_xf183") damage = 5 speed = 500 projectiles = 5 horizontal_offset = 8.25 stagger_offset = 0.5 stagger_animation = true -power_level = 1 -force_replace_all = true metadata/_custom_type_script = "uid://7n1itonn35fm" diff --git a/scenes/player.gd b/scenes/player.gd index 281afe9..ace3c7a 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -5,7 +5,6 @@ class_name Player extends Area2D @onready var movement_component: MovementComponent = %MovementComponent @onready var shoot_component: ShootComponent = %ShootComponent @onready var weapon_component: WeaponComponent = %WeaponComponent -@onready var powerup_spawner: PowerUpSpawner = _find_powerup_spawner() # Get the viewport size for positioning @onready var screensize = get_viewport().content_scale_size @@ -56,21 +55,3 @@ func _process(delta) -> void: if input_component.cycle_weapon: weapon_component.cycle_weapon() $EffectsComponent.apply_effect(flash_effect) - -func _on_help_box_area_entered(area: Area2D) -> void: - # Check if the colliding area is a powerup pickup. - if not area.has_meta("weapon_shot"): - return - var weapon_shot: WeaponShot = area.get_meta("weapon_shot") - weapon_component.collect_weapon(weapon_shot) - if powerup_spawner != null: - powerup_spawner.pickup_collected() - area.queue_free() - - -func _find_powerup_spawner() -> PowerUpSpawner: - # The spawner is a sibling under Level — find it by searching parent. - var level: Node2D = get_parent() as Node2D - if level == null: - return null - return level.get_node_or_null("PowerupSpawner") as PowerUpSpawner diff --git a/scenes/player.tscn b/scenes/player.tscn index 657d790..c240cfb 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -10,12 +10,12 @@ [ext_resource type="Script" uid="uid://ylmao2ndp22y" path="res://scripts/weapon_component.gd" id="7_d2wvv"] [ext_resource type="Resource" uid="uid://b75ae840k03dy" path="res://resources/player_weapon_resources/stock.tres" id="8_stock"] [ext_resource type="Resource" uid="uid://bhc6aja38vyr" path="res://resources/player_weapon_resources/vanguard.tres" id="9_ur7pv"] +[ext_resource type="Script" uid="uid://znfh8unmdxkvv" path="res://scripts/weapon_fire_helper.gd" id="15_firehelper"] [ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="10_d2wvv"] [ext_resource type="Resource" uid="uid://cck3gmnhu5agc" path="res://resources/player_weapon_resources/tri.tres" id="11_3v2ag"] [ext_resource type="Script" uid="uid://65d3hbrpm21x" path="res://scripts/effects_component.gd" id="12_effcomp"] [ext_resource type="Resource" uid="uid://ds53sryglmf27" path="res://resources/player_weapon_resources/spread.tres" id="13_f1ej7"] [ext_resource type="Resource" uid="uid://c41aoyka7nfr8" path="res://resources/player_weapon_resources/vanguard+.tres" id="14_oprun"] -[ext_resource type="Script" uid="uid://bpcv2ojtyityc" path="res://scripts/weapon_fire_helper.gd" id="15_firehelper"] [sub_resource type="AtlasTexture" id="AtlasTexture_tuyoq"] atlas = ExtResource("5_qlg0r") @@ -100,9 +100,6 @@ size = Vector2(6, 5.75) [node name="Player" type="Area2D" unique_id=652131079] script = ExtResource("1_ur7pv") -can_shoot = null -is_shooting = null -muzzle_flash = null [node name="Ship" type="Sprite2D" parent="." unique_id=1155866924] unique_name_in_owner = true @@ -131,15 +128,12 @@ shape = SubResource("RectangleShape2D_qlg0r") disabled = true debug_color = Color(1, 0.5058824, 0.21960784, 0.41960785) -[node name="HelpBoxArea" type="Area2D" parent="." unique_id=938667427] -collision_layer = 2 - -[node name="HelpBoxShape" type="CollisionShape2D" parent="HelpBoxArea" unique_id=938667428] +[node name="HelpBox" type="CollisionShape2D" parent="." unique_id=938667427] position = Vector2(0, 3) shape = SubResource("RectangleShape2D_tuyoq") debug_color = Color(0, 1, 0, 0.41960785) -[node name="HitBox" type="CollisionShape2D" parent="." unique_id=484826453] +[node name="HitBox" type="CollisionShape2D" parent="." unique_id=485826453] position = Vector2(0, 2) shape = SubResource("RectangleShape2D_dqkch") debug_color = Color(0.9843137, 0, 0, 0.80784315) @@ -170,9 +164,8 @@ script = ExtResource("6_y4r1p") unique_name_in_owner = true script = ExtResource("7_d2wvv") weapon_data = ExtResource("8_stock") -available_weapons = Array[ExtResource("10_d2wvv")]([ExtResource("8_stock"), ExtResource("11_3v2ag"), ExtResource("13_f1ej7"), ExtResource("9_ur7pv"), ExtResource("14_oprun")]) +available_weapons = Array[ExtResource("10_d2wvv")]([ExtResource("8_stock"), ExtResource("11_3v2ag"), ExtResource("9_ur7pv"), ExtResource("13_f1ej7"), ExtResource("14_oprun")]) weapon_change_flash = SubResource("Resource_jej6c") -test_mode = true metadata/_custom_type_script = "uid://ylmao2ndp22y" [node name="EffectsComponent" type="Node" parent="." unique_id=1393438576 node_paths=PackedStringArray("target_node")] @@ -181,5 +174,4 @@ script = ExtResource("12_effcomp") target_node = NodePath("../Ship") metadata/_custom_type_script = "uid://65d3hbrpm21x" -[connection signal="area_entered" from="HelpBoxArea" to="." method="_on_help_box_area_entered"] [connection signal="timeout" from="WeaponCooldown" to="." method="_on_weapon_cooldown_timeout"] diff --git a/scenes/power_up_spawner.tscn b/scenes/power_up_spawner.tscn deleted file mode 100644 index 50d09c3..0000000 --- a/scenes/power_up_spawner.tscn +++ /dev/null @@ -1,34 +0,0 @@ -[gd_scene format=3 uid="uid://du5nub2keaty3"] - -[ext_resource type="PackedScene" uid="uid://c9k2p5m8xrq4" path="res://scenes/powerup_pickup.tscn" id="1_xk8p2"] -[ext_resource type="Script" uid="uid://qf6mgn4117o" path="res://scripts/power_up_spawner.gd" id="2_9m3n7"] -[ext_resource type="Script" uid="uid://7n1itonn35fm" path="res://scripts/weapon_shot.gd" id="3_07x44"] -[ext_resource type="Resource" uid="uid://b75ae840k03dy" path="res://resources/player_weapon_resources/stock.tres" id="4_0eev3"] -[ext_resource type="Resource" uid="uid://cck3gmnhu5agc" path="res://resources/player_weapon_resources/tri.tres" id="5_qqjui"] -[ext_resource type="Resource" uid="uid://ds53sryglmf27" path="res://resources/player_weapon_resources/spread.tres" id="6_mwa7c"] -[ext_resource type="Resource" uid="uid://bhc6aja38vyr" path="res://resources/player_weapon_resources/vanguard.tres" id="7_vphcr"] -[ext_resource type="Resource" uid="uid://c41aoyka7nfr8" path="res://resources/player_weapon_resources/vanguard+.tres" id="8_g67dp"] -[ext_resource type="Resource" uid="uid://pickup_std_powerup" path="res://resources/pickups/std_powerup.tres" id="9_std"] -[ext_resource type="Resource" uid="uid://pickup_rare_powerup" path="res://resources/pickups/rare_powerup.tres" id="10_rare"] -[ext_resource type="Resource" uid="uid://pickup_drift_sine" path="res://resources/pickups/drift_sine_wave.tres" id="11_sine"] -[ext_resource type="Resource" uid="uid://pickup_drift_leaf" path="res://resources/pickups/drift_leaf_fall.tres" id="12_leaf"] -[ext_resource type="Resource" uid="uid://pickup_drift_diagonal" path="res://resources/pickups/drift_diagonal.tres" id="13_diag"] - -[node name="PowerUpSpawner" type="Node2D" unique_id=1191463626] -script = ExtResource("2_9m3n7") -pickup_scene = ExtResource("1_xk8p2") -pickup_behaviors = Array[ExtResource("3_07x44")]( - [ExtResource("9_std"), ExtResource("10_rare")] -) -drift_behaviors = Array[ExtResource("3_07x44")]( - [ExtResource("11_sine"), ExtResource("12_leaf"), ExtResource("13_diag")] -) -all_weapon_shots = Array[ExtResource("3_07x44")]( - [ExtResource("4_0eev3"), ExtResource("5_qqjui"), ExtResource("6_mwa7c"), ExtResource("7_vphcr"), ExtResource("8_g67dp")] -) - -[node name="SpawnTimer" type="Timer" parent="." unique_id=173111015] -wait_time = 5.0 -autostart = true - -[connection signal="timeout" from="SpawnTimer" to="." method="_on_spawn_timer_timeout"] diff --git a/scenes/powerup_pickup.tscn b/scenes/powerup_pickup.tscn deleted file mode 100644 index e878206..0000000 --- a/scenes/powerup_pickup.tscn +++ /dev/null @@ -1,17 +0,0 @@ -[gd_scene format=3 uid="uid://dsqfuv86suu17"] - -[ext_resource type="Script" uid="uid://pickup_controller_uid" path="res://scripts/pickups/pickup_behavior_controller.gd" id="1_ctrl"] -[ext_resource type="Resource" uid="uid://pickup_behavior_uid" path="res://resources/pickups/std_powerup.tres" id="2_std"] -[ext_resource type="Resource" uid="uid://pickup_drift_uid" path="res://resources/pickups/drift_sine_wave.tres" id="3_drift"] - -[sub_resource type="RectangleShape2D" id="RectangleShape2D_mmgg1"] -size = Vector2(14, 14) - -[node name="PowerUpPickup" type="Area2D" unique_id=478708932] -script = ExtResource("1_ctrl") -behavior = ExtResource("2_std") -drift_behavior = ExtResource("3_drift") -collision_mask = 0 - -[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=347643413] -shape = SubResource("RectangleShape2D_mmgg1") diff --git a/scenes/world.tscn b/scenes/world.tscn index e415429..515828a 100644 --- a/scenes/world.tscn +++ b/scenes/world.tscn @@ -6,7 +6,6 @@ [ext_resource type="PackedScene" uid="uid://c3scm07fgpkcp" path="res://scenes/notification_display.tscn" id="3_71j4m"] [ext_resource type="PackedScene" uid="uid://6wq3ynesnsha" path="res://scenes/player.tscn" id="3_k0juu"] [ext_resource type="Script" uid="uid://eblemtukrey6" path="res://scripts/utility/fps_display.gd" id="4_qfnet"] -[ext_resource type="PackedScene" uid="uid://du5nub2keaty3" path="res://scenes/power_up_spawner.tscn" id="7_i7141"] [node name="World" type="Node2D" unique_id=1317852169] @@ -93,5 +92,3 @@ hframes = 4 frame = 3 [node name="Player" parent="Level" unique_id=652131079 instance=ExtResource("3_k0juu")] - -[node name="PowerupSpawner" parent="Level" unique_id=1191463626 instance=ExtResource("7_i7141")] diff --git a/scripts/event_bus.gd b/scripts/event_bus.gd index fa3dcc0..fc12a05 100644 --- a/scripts/event_bus.gd +++ b/scripts/event_bus.gd @@ -4,7 +4,4 @@ extends Node signal weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: float) -# Emitted when a powerup is collected by the player. -signal powerup_collected(weapon_shot: WeaponShot, player_node: Node2D) - @warning_ignore_restore("unused_signal") diff --git a/scripts/notification_display.gd b/scripts/notification_display.gd index b401cc4..4dbbc48 100644 --- a/scripts/notification_display.gd +++ b/scripts/notification_display.gd @@ -33,9 +33,6 @@ func show_notification(weapon_name: String, player_node: Node2D, player_sprite_s var icon_path = "res://graphics/" + weapon_name.to_lower() + ".png" if ResourceLoader.exists(icon_path): icon.texture = load(icon_path) - elif ResourceLoader.exists("res://graphics/stock.png"): - # Fallback to stock icon when no dedicated icon exists - icon.texture = load("res://graphics/stock.png") else: icon.texture = null diff --git a/scripts/pickups/pickup_behavior_controller.gd b/scripts/pickups/pickup_behavior_controller.gd deleted file mode 100644 index 84a8a4f..0000000 --- a/scripts/pickups/pickup_behavior_controller.gd +++ /dev/null @@ -1,145 +0,0 @@ -class_name PickupBehaviorController extends Area2D - -## Controls visual movement (bob, shake) and drift of a pickup. -## Attach to the root Area2D node of your pickup scene. - -@export var behavior: PickupBehavior -@export var drift_behavior: PickupDriftBehavior - - -var _icon_node: TextureRect -var _spawn_position: Vector2 = Vector2.ZERO - - -func _ready() -> void: - # Find the runtime-added icon node. - for child in get_children(): - if child is TextureRect: - _icon_node = child as TextureRect - break - # Capture the initial spawn position. - _spawn_position = global_position - - if behavior != null: - behavior.start() - if drift_behavior != null: - drift_behavior.start() - - -func _process(delta: float) -> void: - # Handle partial configurations: if only behavior is set, compute bob/shake. - # If only drift_behavior is set, compute drift offset. If both are set, - # composite them (bob/shake on icon, drift on pickup position). - var has_behavior: bool = behavior != null - var has_drift: bool = drift_behavior != null - - if not has_behavior and not has_drift: - return - - var alpha: float = 1.0 - if has_behavior: - alpha = behavior.process(delta) - - if _icon_node != null: - _icon_node.visible = alpha > 0.01 - if behavior != null and behavior.fade_in_duration > 0 and _icon_node is CanvasItem: - _icon_node.modulate.a = alpha - - var offset := _compute_animation_offset() - var drift_offset := _compute_drift_offset() - - position = _spawn_position + Vector2(drift_offset.x, drift_offset.y) - - if _icon_node != null: - _icon_node.position = offset - - -func _compute_animation_offset() -> Vector2: - # Returns the bob + shake offset applied to the icon node. - var result := Vector2.ZERO - - if behavior == null: - return result - - # Bob (vertical oscillation). - if behavior.bob_enabled: - var bob_angle := _elapsed_time() * TAU * behavior.bob_frequency - result.y += sin(bob_angle) * behavior.bob_amplitude - - # Shake (subtle jitter). - if behavior.shake_enabled: - var shake_angle := _elapsed_time() * TAU * behavior.shake_frequency - result.x += sin(shake_angle) * behavior.shake_amplitude - - return result - - -func _compute_drift_offset() -> Vector2: - # Returns the global drift position offset from initial spawn position. - if drift_behavior == null: - return Vector2.ZERO - - match drift_behavior.drift_type: - PickupDriftBehavior.DriftType.STATIONARY: - return Vector2.ZERO - - PickupDriftBehavior.DriftType.SINE_WAVE: - var t := _elapsed_time() * drift_behavior.wave_frequency - var travel_speed: float = drift_behavior.drift_speed - if drift_behavior.reverse_direction: - travel_speed *= -1.0 - - var offset := Vector2.ZERO - if drift_behavior.drift_along_x: - # Travels along X; waves on Y. - offset.x = travel_speed * _elapsed_time() - offset.y = sin(t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude - else: - # Travels along Y; waves on X. - offset.y = travel_speed * _elapsed_time() - offset.x = sin(t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude - return offset - - PickupDriftBehavior.DriftType.LEAF_FALL: - var leaf_t := _elapsed_time() * drift_behavior.wave_frequency - var travel_speed: float = drift_behavior.fall_speed - if drift_behavior.reverse_direction: - travel_speed *= -1.0 - - var offset := Vector2.ZERO - if drift_behavior.drift_along_x: - # Travels along X; waves on Y. - offset.x = travel_speed * _elapsed_time() - offset.y = sin(leaf_t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude - else: - # Travels along Y; waves on X. - offset.y = travel_speed * _elapsed_time() - offset.x = sin(leaf_t + drift_behavior.phase_offset) * drift_behavior.wave_amplitude - return offset - - PickupDriftBehavior.DriftType.DIAGONAL: - var dir: float = 1.0 if drift_behavior.diagonal_forward else -1.0 - if drift_behavior.reverse_direction: - dir *= -1.0 - - var offset := Vector2.ZERO - if drift_behavior.drift_along_x: - # Travels along X; secondary on Y. - offset.x = dir * drift_behavior.drift_speed * _elapsed_time() - offset.y = dir * (drift_behavior.drift_speed * 0.5) * _elapsed_time() - else: - # Travels along Y; secondary on X. - offset.y = dir * drift_behavior.drift_speed * _elapsed_time() - offset.x = dir * (drift_behavior.drift_speed * 0.5) * _elapsed_time() - return offset - - return Vector2.ZERO - - -func _elapsed_time() -> float: - # Prefer behavior's elapsed time; fall back to drift_behavior's. - if behavior != null: - return behavior._elapsed - if drift_behavior != null: - return drift_behavior._elapsed - return 0.0 diff --git a/scripts/pickups/pickup_behavior_controller.gd.uid b/scripts/pickups/pickup_behavior_controller.gd.uid deleted file mode 100644 index 06e365b..0000000 --- a/scripts/pickups/pickup_behavior_controller.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c4a7husvaq2eo diff --git a/scripts/power_up_spawner.gd b/scripts/power_up_spawner.gd deleted file mode 100644 index 65c62ab..0000000 --- a/scripts/power_up_spawner.gd +++ /dev/null @@ -1,282 +0,0 @@ -class_name PowerUpSpawner extends Node2D - -@export var spawn_interval: float = 5.0 -@export var test_mode: bool = true - -# PackedScene for the pickup template — configure in the scene editor. -@export var pickup_scene: PackedScene - -@onready var screensize: Vector2 = get_viewport().content_scale_size -@onready var spawn_timer: Timer = $SpawnTimer - -# Optional behavior resources to assign randomly on spawn. -@export var pickup_behaviors: Array[PickupBehavior] = [] - -# All weapon resources to spawn as pickups. -# Populate in the editor with your weapon_shot resources. -@export var all_weapon_shots: Array[WeaponShot] = [] - -# Optional drift behaviors to assign randomly on spawn. -# Leave empty to use whatever is baked into the pickup scene. -@export var drift_behaviors: Array[PickupDriftBehavior] = [] - -# How long a pickup can linger off-screen before being cleaned up. -@export var offscreen_timeout: float = 5.0 - -# How long to wait after a pickup is collected before spawning the next one. -@export var cooldown_after_collection: float = 5.0 - -var _pickups_spawned: int = 0 -var _pickups_remaining: int = 0 - -# Tracks active pickups so we can check off-screen timeouts. -var _active_pickups: Array[Area2D] = [] - -# Cooldown state for post-collection pause. -var _cooldown_timer: Timer -var _in_cooldown: bool = false - -func _ready() -> void: - if spawn_timer == null: - return - spawn_timer.wait_time = spawn_interval - - # Create a one-shot cooldown timer. - _cooldown_timer = Timer.new() - _cooldown_timer.one_shot = true - _cooldown_timer.timeout.connect(_on_cooldown_finished) - add_child(_cooldown_timer) - - -func _on_spawn_timer_timeout() -> void: - if test_mode: - _spawn_test_pickups() - else: - _spawn_normal_pickup() - - -func _on_cooldown_finished() -> void: - _in_cooldown = false - if spawn_timer != null and test_mode: - spawn_timer.start() - - -func _spawn_normal_pickup() -> void: - # Spawn a single random pickup at a position near the player. - if all_weapon_shots.is_empty(): - return - - var pickup := _spawn_pickup( - all_weapon_shots[randi() % all_weapon_shots.size()] - ) - if pickup == null or not is_instance_valid(pickup): - return - - # Position: centered horizontally, above the player with vertical padding. - var player_node := get_node_or_null("../Player") - if player_node != null: - pickup.position = Vector2( - randf_range(40.0, screensize.x - 40.0), - player_node.position.y - randf_range(80.0, 200.0) - ) - else: - pickup.position = Vector2( - randf_range(40.0, screensize.x - 40.0), - randf_range(30.0, screensize.y * 0.4) - ) - - add_child(pickup) - _pickups_spawned += 1 - _pickups_remaining = max(_pickups_remaining, _pickups_spawned) - - -func _spawn_test_pickups() -> void: - # In test mode: pick ONE random weapon and spawn a single pickup. - if all_weapon_shots.is_empty(): - return - - var chosen_weapon: WeaponShot = all_weapon_shots[randi() % all_weapon_shots.size()] - if chosen_weapon == null or chosen_weapon.shot_name.is_empty(): - return - - var pickup := _spawn_pickup(chosen_weapon) - if not is_instance_valid(pickup): - return - - # Random position within viewport, with edge padding. - var margin: float = 15.0 - if _should_randomize_origin(pickup): - # Randomize the perpendicular axis so pickups don't all start at one edge. - var drift := _get_pickup_drift(pickup) - if drift != null: - var offset := Vector2.ZERO - if drift.drift_along_x: - # Travels along X; randomize Y origin. - offset.x = randf_range(margin, screensize.x - margin) - offset.y = 0.0 - else: - # Travels along Y; randomize X origin. - offset.x = 0.0 - offset.y = randf_range(margin, screensize.y - margin) - pickup.position = offset - else: - pickup.position = Vector2( - randf_range(margin, screensize.x - margin), - randf_range(margin, screensize.y - margin) - ) - else: - pickup.position = Vector2( - randf_range(margin, screensize.x - margin), - randf_range(margin, screensize.y - margin) - ) - - add_child(pickup) - _active_pickups.append(pickup) - _pickups_spawned += 1 - _pickups_remaining = _pickups_spawned - - # Pause the timer while pickup is on screen; resume when gone or expired. - if spawn_timer != null: - spawn_timer.stop() - - -func pickup_collected() -> void: - # Called by the player when a pickup is collected. - # Sync WeaponComponent's test_mode so tab cycling stays independent of - # pickups when in test mode, and normal behavior is restored otherwise. - var level: Node2D = get_parent() as Node2D - if level != null: - var wc: WeaponComponent = level.get_node_or_null("WeaponComponent") - if wc != null: - wc.test_mode = test_mode - # Remove from active list and start cooldown. - _active_pickups.clear() - if spawn_timer != null: - spawn_timer.stop() - _start_cooldown() - - -func _process(delta: float) -> void: - if test_mode: - _update_active_pickups(delta) - - -func _spawn_pickup(weapon: WeaponShot) -> Area2D: - # Validate that the weapon has a pickup icon configured. - if weapon.pickup_icon == null: - printerr("[PowerUpSpawner] No pickup icon set for weapon: ", weapon.shot_name) - var dead := Area2D.new() # return a dead node — caller skips it - dead.queue_free() - return dead - - # Instantiate the scene template and attach weapon metadata. - var pickup := pickup_scene.instantiate() as Area2D - if pickup == null: - printerr("[PowerUpSpawner] Failed to instantiate pickup scene.") - return null - - pickup.set_meta("weapon_shot", weapon) - - # Add the icon node at runtime (scene has no TextureRect to avoid null-dependency issues). - var icon := TextureRect.new() - icon.texture = weapon.pickup_icon - icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - icon.size = Vector2(16, 16) - pickup.add_child(icon) - - # Assign a random behavior for visual variety. - _apply_random_behavior(pickup) - - # Assign a random drift behavior for visual variety. - _apply_random_drift(pickup) - - return pickup - - -func _cleanup_pickup(pickup: Area2D) -> void: - # Remove from active list if present. - for i in _active_pickups.size(): - if _active_pickups[i] == pickup: - _active_pickups.remove_at(i) - break - pickup.queue_free() - # Trigger cooldown/respawn when a pickup drifts off-screen. - _start_cooldown() - -func _update_active_pickups(_delta: float) -> void: - # Check each active pickup for off-screen timeout. - var viewport_rect := get_viewport_rect().size - var margin: float = 50.0 # how far off-screen counts as "gone" - var to_remove: Array[Area2D] = [] - - for pickup in _active_pickups: - if not is_instance_valid(pickup): - continue - # Simple off-screen check: outside viewport + margin. - if (pickup.position.x < -margin - or pickup.position.x > viewport_rect.x + margin - or pickup.position.y < -margin - or pickup.position.y > viewport_rect.y + margin): - to_remove.append(pickup) - - for bad_pickup in to_remove: - _cleanup_pickup(bad_pickup) - - -func _start_cooldown() -> void: - if offscreen_timeout <= 0 or cooldown_after_collection <= 0: - return - _in_cooldown = true - _cooldown_timer.wait_time = cooldown_after_collection - _cooldown_timer.start() - - -func _apply_random_behavior(pickup: Area2D) -> void: - if pickup_behaviors.is_empty(): - return - - var controller := pickup.get_node_or_null("PickupBehaviorController") - if controller == null: - return - - var chosen: PickupBehavior = pickup_behaviors[randi() % pickup_behaviors.size()].duplicate() as PickupBehavior - if chosen != null: - controller.behavior = chosen - - -func _apply_random_drift(pickup: Area2D) -> void: - # Randomly assign a drift behavior to the spawned pickup instance. - if drift_behaviors.is_empty(): - return - - var controller := pickup.get_node_or_null("PickupBehaviorController") - if controller == null: - return - - var chosen: PickupDriftBehavior = drift_behaviors[randi() % drift_behaviors.size()].duplicate() as PickupDriftBehavior - if chosen == null: - return - # Randomize phase offset for per-instance variety. - chosen.phase_offset = randf_range(0.0, TAU) - # Randomize axis (horizontal vs vertical travel). - chosen.drift_along_x = randf() > 0.5 - # Randomize direction (forward vs reverse). - chosen.reverse_direction = randf() > 0.5 - # Randomize perpendicular origin. - chosen.randomize_origin = randf() > 0.5 - # When randomizing origin, zero the phase so the wave starts at center (0 offset). - if chosen.randomize_origin: - chosen.phase_offset = 0.0 - controller.drift_behavior = chosen - - -func _get_pickup_drift(pickup: Area2D) -> PickupDriftBehavior: - var controller := pickup.get_node_or_null("PickupBehaviorController") - if controller == null: - return null - return controller.drift_behavior - - -func _should_randomize_origin(pickup: Area2D) -> bool: - var drift := _get_pickup_drift(pickup) - return drift != null and drift.randomize_origin diff --git a/scripts/power_up_spawner.gd.uid b/scripts/power_up_spawner.gd.uid deleted file mode 100644 index 7121649..0000000 --- a/scripts/power_up_spawner.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://qf6mgn4117o diff --git a/scripts/shoot_component.gd b/scripts/shoot_component.gd index 0fc9234..bf783c1 100644 --- a/scripts/shoot_component.gd +++ b/scripts/shoot_component.gd @@ -5,24 +5,13 @@ class_name ShootComponent extends Node @onready var ship: Sprite2D = get_node("../Ship") @onready var fire_helper: WeaponFireHelper = %WeaponFireHelper -func shoot() -> void: - var active_weapons: Array[WeaponShot] = weapon_component.get_active_weapons() - if active_weapons.is_empty(): - return +func shoot(): + var weapon_data = weapon_component.weapon_data + var current_time = Time.get_ticks_msec() / 1000.0 - var current_time: float = Time.get_ticks_msec() / 1000.0 - - # Burst gating: find the minimum projectile_vertical_spacing among all - # active weapons. This is the most restrictive timing threshold. - var min_spacing: float = INF - for w: WeaponShot in active_weapons: - if w.projectile_vertical_spacing < min_spacing: - min_spacing = w.projectile_vertical_spacing - - if player.travel > min_spacing: - for w: WeaponShot in active_weapons: - _shoot_standard(w, current_time) - _shoot_grouped(w, current_time) + if player.travel > weapon_data.projectile_vertical_spacing: + _shoot_standard(weapon_data, current_time) + _shoot_grouped(weapon_data, current_time) # Reset travel after firing to prevent multiple bursts per tap player.travel = 0.0 @@ -37,12 +26,12 @@ func _get_spawn_position(weapon_data: WeaponShot) -> Vector2: sprite_height = ship.texture.get_height() # Top edge of the sprite in world space, then offset by origin. - var top_edge: float = ship.global_position.y - (sprite_height / 2.0) + 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) -> void: +func _shoot_standard(weapon_data: WeaponShot, current_time: float): var total_projectiles: int = weapon_data.projectiles if total_projectiles > 0: @@ -51,8 +40,8 @@ func _shoot_standard(weapon_data: WeaponShot, current_time: float) -> void: get_tree().root.add_child(bullet) # Delegate math to the helper - var spawn_position: Vector2 = _get_spawn_position(weapon_data) - var props: Dictionary = fire_helper.compute_standard_props(weapon_data, b, spawn_position, current_time) + 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 bullet.time_offset = props.time_offset @@ -67,15 +56,15 @@ func _shoot_standard(weapon_data: WeaponShot, current_time: float) -> void: # Grouped projectile logic (delegates to WeaponFireHelper) -func _shoot_grouped(weapon_data: WeaponShot, current_time: float) -> void: +func _shoot_grouped(weapon_data: WeaponShot, current_time: float): for group: WeaponProjectileGroup in weapon_data.groups.filter(func(g): return g != null): for b in range(group.bullet_count): var bullet := weapon_data.bullet_scene.instantiate() as Area2D get_tree().root.add_child(bullet) # Apply computed properties for the original bullet - var spawn_position: Vector2 = _get_spawn_position(weapon_data) - var props: Dictionary = fire_helper.compute_grouped_props(group, weapon_data, b, spawn_position, current_time, false) + 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 bullet.time_offset = props.time_offset @@ -90,8 +79,8 @@ func _shoot_grouped(weapon_data: WeaponShot, current_time: float) -> void: get_tree().root.add_child(mirror_bullet) # Apply computed properties for the mirrored bullet - var mirror_spawn: Vector2 = _get_spawn_position(weapon_data) - var mirror_props: Dictionary = fire_helper.compute_grouped_props(group, weapon_data, b, mirror_spawn, current_time, true) + 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 mirror_bullet.time_offset = mirror_props.time_offset diff --git a/scripts/weapon_component.gd b/scripts/weapon_component.gd index 90083b0..1442102 100644 --- a/scripts/weapon_component.gd +++ b/scripts/weapon_component.gd @@ -4,186 +4,41 @@ class_name WeaponComponent extends Node @export var available_weapons: Array[WeaponShot] = [] @export var weapon_change_flash: BaseEffect = null -@export var test_mode: bool = false +var _current_weapon_index: int = 0 -var _current_weapon_index = 0 -var _current_power_level = 0 -var _cycled_weapon: WeaponShot = null - -@onready var effects_component: EffectComponent = get_node("../EffectsComponent") +@onready var effects_component: EffectComponent = %EffectsComponent func _ready() -> void: + # Set the default weapon to be stock, i.e. index 1 if weapon_data and not available_weapons.is_empty(): for i in range(available_weapons.size()): if available_weapons[i] == weapon_data: _current_weapon_index = i - # Initialize the power level from the default weapon so - # shooting works immediately without needing to cycle. - _current_power_level = weapon_data.power_level return - # If available_weapons was populated from the scene but no matching - # weapon_data was found (e.g. all weapons share the same power level), - # cap the power level to the minimum power level across all weapons. - # This ensures initial shooting works even when weapon_data is null or - # mismatches are found. - if not available_weapons.is_empty(): - var min_level: int = 999 - for w: WeaponShot in available_weapons: - if w != null and w.power_level < min_level: - min_level = w.power_level - _current_power_level = min_level - - -func _compute_active_weapons() -> Array[WeaponShot]: - # Filter available weapons by current power level. - var active: Array[WeaponShot] = [] - for weapon: WeaponShot in available_weapons: - if weapon != null and weapon.power_level <= _current_power_level: - active.append(weapon) - return active - - -func get_active_weapons(power_level: int = -1) -> Array[WeaponShot]: - # Return all weapons active at the given power level. - # - # If power_level is negative, uses the internally tracked power level. - # var _level: int = power_level if power_level >= 0 else _current_power_level - # (unused - power_level parameter is ignored by design) - # If the cycled weapon is set, return it exclusively — - # cycling overrides all additive logic. - if _cycled_weapon != null: - return [_cycled_weapon] - var base: Array[WeaponShot] = _compute_active_weapons() - if not base.is_empty(): - var additive: Array[WeaponShot] = base.filter( - func(w: WeaponShot): return not w.force_replace_all - ) - if not additive.is_empty(): - return additive - # If no non-force-replace weapons are active, return only the current - # default weapon (weapon_data). This ensures the starting weapon (stock) - # fires as a single shot rather than all force_replace_all weapons firing - # simultaneously. - if weapon_data != null and base.has(weapon_data): - return [weapon_data] - return base.filter( - func(w: WeaponShot): return w.force_replace_all - ) - - -func _get_force_replace_weapons() -> Array[WeaponShot]: - # Return weapons that override all others (force_replace_all = true). - return _compute_active_weapons().filter( - func(w: WeaponShot): return w.force_replace_all - ) - - -func collect_weapon(new_weapon: WeaponShot) -> void: - # Handle adding a weapon, applying replacement logic as needed. - # - # Replacement rules (applied in order): - # 1. force_replace_all: Discard all weapons, add only this one. - # Rule 2: replaces_at_same_level: Remove weapons at matching power level. - # 3. Normal: Add additive weapon (no removal). - # - # Sets the power level to this weapon's if higher than current. - # Emits weapon_changed with the new active set. - # - # Clear any active cycle — collecting a weapon during cycling restores - # normal additive behavior. - if _cycled_weapon != null and not test_mode: - reset_weapon_cycle() - # Rule 1: Force replace — discard everything, keep only this weapon - if new_weapon.force_replace_all: - available_weapons = [new_weapon] - _current_power_level = new_weapon.power_level - _update_data_from_active_weapons() - _emit_weapon_changed() - _trigger_change_flash() - return - - # Rule 2: Replace if same level — remove weapons at matching power level - if new_weapon.replaces_at_same_level: - var was_replaced: bool = false - for i in range(available_weapons.size()): - if available_weapons[i] != null and \ - available_weapons[i].replaces_at_same_level == false and \ - available_weapons[i].power_level == new_weapon.power_level: - available_weapons.remove_at(i) - was_replaced = true - break # Only replace the highest (first match at this level) - if was_replaced: - # Also remove any other weapons with the same power_level that may - # have sneaked in via previous power level upgrades - var filtered: Array[WeaponShot] = [] - for w: WeaponShot in available_weapons: - if w != null and w.power_level == new_weapon.power_level: - continue - filtered.append(w) - available_weapons = filtered - - # Rule 3: Additive — just add it (no removal) - var already_has: bool = false - for w: WeaponShot in available_weapons: - if w != null and w.shot_name == new_weapon.shot_name: - already_has = true - break - if not already_has: - available_weapons.append(new_weapon) - - # Update power level if this weapon is at a higher tier - if new_weapon.power_level > _current_power_level: - _current_power_level = new_weapon.power_level - - _update_data_from_active_weapons() - _emit_weapon_changed() - _trigger_change_flash() - - -func _update_data_from_active_weapons() -> void: - # Update weapon_data to point to the highest-priority active weapon. - # - # This is called after any weapon collection or power level change to - # keep the legacy `weapon_data` property in sync for backward compatibility. - # Force_replace_all weapons take priority over additive weapons. - # Check for force_replace weapons first (they override everything) - var force_replaces: Array[WeaponShot] = _get_force_replace_weapons() - if not force_replaces.is_empty(): - weapon_data = force_replaces[-1] - return - - # Otherwise, point to the highest-priority additive weapon - var active: Array[WeaponShot] = get_active_weapons() - if not active.is_empty(): - weapon_data = active[-1] - else: - weapon_data = null func _emit_weapon_changed() -> void: - if weapon_data == null: - return var ship: Sprite2D = get_parent().get_node("Ship") var sprite_size = ship.get_rect().size - var origin_offset: float = weapon_data.origin + 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) func get_bullet_scene() -> PackedScene: return weapon_data.bullet_scene if weapon_data else null - func get_weapon_resource() -> Resource: return weapon_data - func select_weapon_by_name(weapon_name: String) -> bool: for i in range(available_weapons.size()): - var candidate: WeaponShot = available_weapons[i] - if candidate != null and candidate.shot_name == weapon_name: - _cycled_weapon = candidate # override additive behavior + if available_weapons[i].shot_name == weapon_name: + _current_weapon_index = i + weapon_data = available_weapons[i] print("Switched to: ", weapon_name) + + _emit_weapon_changed() _trigger_change_flash() return true return false @@ -194,36 +49,14 @@ func _trigger_change_flash() -> void: effects_component.apply_effect(weapon_change_flash) -func cycle_weapon() -> void: # Used for testing weapon cycling + +func cycle_weapon() -> void: # Used for testing weapon cycling if available_weapons.is_empty(): return _current_weapon_index = (_current_weapon_index + 1) % available_weapons.size() - var new_weapon: WeaponShot = available_weapons[_current_weapon_index] - if new_weapon == null: - return - print("Switched to: ", new_weapon.shot_name) + weapon_data = available_weapons[_current_weapon_index] + print("Switched to: ", weapon_data.shot_name) - # Remove the previous weapon from the active set before engaging the next. - # Clearing _cycled_weapon forces get_active_weapons() to evaluate the - # additive state (which temporarily re-includes the old weapon). Then - # re-setting _cycled_weapon to the new weapon re-establishes the cycle - # override. This two-step process ensures the old weapon is fully removed - # from the active set and its power level doesn't leak into the new cycle. - _cycled_weapon = null # 1) Remove previous weapon - _cycled_weapon = new_weapon # 2) Engage next weapon - _current_power_level = new_weapon.power_level - - # Update weapon_data to reflect the cycled weapon directly, - # so _emit_weapon_changed() reports the correct name and origin. - weapon_data = new_weapon - _emit_weapon_changed() - _trigger_change_flash() - - -func reset_weapon_cycle() -> void: - # Restore normal additive behavior after cycling. - _cycled_weapon = null - _update_data_from_active_weapons() _emit_weapon_changed() _trigger_change_flash() diff --git a/scripts/weapon_shot.gd b/scripts/weapon_shot.gd index fb6ecdc..8abc83d 100644 --- a/scripts/weapon_shot.gd +++ b/scripts/weapon_shot.gd @@ -11,9 +11,6 @@ extends Resource # PackedScene reference to the bullet / projectile scene instantiated on fire. @export var bullet_scene: PackedScene -# Icon used when this weapon appears as a powerup pickup on the field. -@export var pickup_icon: Texture2D - @export_subgroup("Launch") @@ -85,20 +82,3 @@ extends Resource # Array of projectile groups defining layered, multi-directional fire patterns. @export var groups: Array[WeaponProjectileGroup] = [] - - -@export_subgroup("Power & Composition") - - -# The power level at which this weapon becomes active. -# Weapons with power_level <= current power level are considered active. -@export_range(0, 20, 1) var power_level: int = 0 - -# If true, collecting this weapon removes any existing weapon whose -# power_level matches this weapon's power_level. Use this instead of -# force_replace_all for rank-up replacements (e.g. stock → tri → spread). -@export var replaces_at_same_level: bool = false - -# If true, this weapon replaces ALL active weapons (ignores power_level). -# Used for rank-based transformations where the entire arsenal changes. -@export var force_replace_all: bool = false