Compare commits
No commits in common. "weapon-development" and "main" have entirely different histories.
weapon-dev
...
main
19 changed files with 34 additions and 484 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 272 B |
|
|
@ -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
|
|
||||||
|
|
@ -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="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://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="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]
|
[resource]
|
||||||
script = ExtResource("2_4b7mw")
|
script = ExtResource("2_4b7mw")
|
||||||
shot_name = "spread"
|
shot_name = "spread"
|
||||||
bullet_scene = ExtResource("1_pn64g")
|
bullet_scene = ExtResource("1_pn64g")
|
||||||
pickup_icon = ExtResource("3_ak5x4")
|
|
||||||
damage = 5
|
damage = 5
|
||||||
speed = 720
|
speed = 720
|
||||||
projectiles = 5
|
projectiles = 5
|
||||||
|
|
@ -17,6 +15,4 @@ horizontal_offset = 8.25
|
||||||
spread_angle = 25.0
|
spread_angle = 25.0
|
||||||
stagger_offset = 0.5
|
stagger_offset = 0.5
|
||||||
stagger_animation = true
|
stagger_animation = true
|
||||||
power_level = 1
|
|
||||||
force_replace_all = true
|
|
||||||
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
||||||
|
|
|
||||||
|
|
@ -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="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://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="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]
|
[resource]
|
||||||
script = ExtResource("2_ym48c")
|
script = ExtResource("2_ym48c")
|
||||||
shot_name = "stock"
|
shot_name = "stock"
|
||||||
bullet_scene = ExtResource("1_lcw6o")
|
bullet_scene = ExtResource("1_lcw6o")
|
||||||
pickup_icon = ExtResource("3_e245k")
|
|
||||||
damage = 2
|
damage = 2
|
||||||
speed = 650
|
speed = 650
|
||||||
horizontal_offset = 12.0
|
horizontal_offset = 12.0
|
||||||
stagger_offset = 0.0
|
stagger_offset = 0.0
|
||||||
power_level = 1
|
|
||||||
force_replace_all = true
|
|
||||||
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,17 @@
|
||||||
[gd_resource type="Resource" script_class="WeaponShot" format=3 uid="uid://cck3gmnhu5agc"]
|
[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://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="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]
|
[resource]
|
||||||
script = ExtResource("2_syw6j")
|
script = ExtResource("2_syw6j")
|
||||||
shot_name = "tri"
|
shot_name = "tri"
|
||||||
bullet_scene = ExtResource("1_bhv8k")
|
bullet_scene = ExtResource("1_07hyh")
|
||||||
pickup_icon = ExtResource("3_fw2v3")
|
|
||||||
damage = 3
|
damage = 3
|
||||||
speed = 700
|
speed = 700
|
||||||
projectiles = 3
|
projectiles = 3
|
||||||
horizontal_offset = 12.5
|
horizontal_offset = 12.5
|
||||||
stagger_offset = 0.35
|
stagger_offset = 0.35
|
||||||
stagger_animation = true
|
stagger_animation = true
|
||||||
power_level = 1
|
|
||||||
force_replace_all = true
|
|
||||||
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
||||||
|
|
|
||||||
|
|
@ -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://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="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="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]
|
[resource]
|
||||||
script = ExtResource("3_fh2eq")
|
script = ExtResource("3_fh2eq")
|
||||||
shot_name = "vanguard+"
|
shot_name = "vanguard+"
|
||||||
bullet_scene = ExtResource("1_lqlv8")
|
bullet_scene = ExtResource("1_lqlv8")
|
||||||
pickup_icon = ExtResource("4_fh2eq")
|
|
||||||
damage = 5
|
damage = 5
|
||||||
speed = 500
|
speed = 500
|
||||||
projectiles = 5
|
projectiles = 5
|
||||||
|
|
@ -20,6 +18,4 @@ compensate_spawn_geometry = true
|
||||||
stagger_animation = true
|
stagger_animation = true
|
||||||
follow_angle = true
|
follow_angle = true
|
||||||
groups = Array[ExtResource("2_qeb4b")]([ExtResource("3_qeb4b")])
|
groups = Array[ExtResource("2_qeb4b")]([ExtResource("3_qeb4b")])
|
||||||
power_level = 1
|
|
||||||
force_replace_all = true
|
|
||||||
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
||||||
|
|
|
||||||
|
|
@ -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="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://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="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]
|
[resource]
|
||||||
script = ExtResource("2_w7xlm")
|
script = ExtResource("2_w7xlm")
|
||||||
shot_name = "vanguard"
|
shot_name = "vanguard"
|
||||||
bullet_scene = ExtResource("1_hm46f")
|
bullet_scene = ExtResource("1_hm46f")
|
||||||
pickup_icon = ExtResource("3_xf183")
|
|
||||||
damage = 5
|
damage = 5
|
||||||
speed = 500
|
speed = 500
|
||||||
projectiles = 5
|
projectiles = 5
|
||||||
horizontal_offset = 8.25
|
horizontal_offset = 8.25
|
||||||
stagger_offset = 0.5
|
stagger_offset = 0.5
|
||||||
stagger_animation = true
|
stagger_animation = true
|
||||||
power_level = 1
|
|
||||||
force_replace_all = true
|
|
||||||
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
metadata/_custom_type_script = "uid://7n1itonn35fm"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ class_name Player extends Area2D
|
||||||
@onready var movement_component: MovementComponent = %MovementComponent
|
@onready var movement_component: MovementComponent = %MovementComponent
|
||||||
@onready var shoot_component: ShootComponent = %ShootComponent
|
@onready var shoot_component: ShootComponent = %ShootComponent
|
||||||
@onready var weapon_component: WeaponComponent = %WeaponComponent
|
@onready var weapon_component: WeaponComponent = %WeaponComponent
|
||||||
@onready var powerup_spawner := _find_powerup_spawner()
|
|
||||||
|
|
||||||
# Get the viewport size for positioning
|
# Get the viewport size for positioning
|
||||||
@onready var screensize = get_viewport().content_scale_size
|
@onready var screensize = get_viewport().content_scale_size
|
||||||
|
|
@ -56,20 +55,3 @@ func _process(delta) -> void:
|
||||||
if input_component.cycle_weapon:
|
if input_component.cycle_weapon:
|
||||||
weapon_component.cycle_weapon()
|
weapon_component.cycle_weapon()
|
||||||
$EffectsComponent.apply_effect(flash_effect)
|
$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)
|
|
||||||
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
|
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
[ext_resource type="Script" uid="uid://ylmao2ndp22y" path="res://scripts/weapon_component.gd" id="7_d2wvv"]
|
[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://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="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="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="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="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://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="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"]
|
[sub_resource type="AtlasTexture" id="AtlasTexture_tuyoq"]
|
||||||
atlas = ExtResource("5_qlg0r")
|
atlas = ExtResource("5_qlg0r")
|
||||||
|
|
@ -128,15 +128,12 @@ shape = SubResource("RectangleShape2D_qlg0r")
|
||||||
disabled = true
|
disabled = true
|
||||||
debug_color = Color(1, 0.5058824, 0.21960784, 0.41960785)
|
debug_color = Color(1, 0.5058824, 0.21960784, 0.41960785)
|
||||||
|
|
||||||
[node name="HelpBoxArea" type="Area2D" parent="." unique_id=938667427]
|
[node name="HelpBox" type="CollisionShape2D" parent="." unique_id=938667427]
|
||||||
collision_layer = 2
|
|
||||||
|
|
||||||
[node name="HelpBoxShape" type="CollisionShape2D" parent="HelpBoxArea" unique_id=938667428]
|
|
||||||
position = Vector2(0, 3)
|
position = Vector2(0, 3)
|
||||||
shape = SubResource("RectangleShape2D_tuyoq")
|
shape = SubResource("RectangleShape2D_tuyoq")
|
||||||
debug_color = Color(0, 1, 0, 0.41960785)
|
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)
|
position = Vector2(0, 2)
|
||||||
shape = SubResource("RectangleShape2D_dqkch")
|
shape = SubResource("RectangleShape2D_dqkch")
|
||||||
debug_color = Color(0.9843137, 0, 0, 0.80784315)
|
debug_color = Color(0.9843137, 0, 0, 0.80784315)
|
||||||
|
|
@ -167,9 +164,8 @@ script = ExtResource("6_y4r1p")
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("7_d2wvv")
|
script = ExtResource("7_d2wvv")
|
||||||
weapon_data = ExtResource("8_stock")
|
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")
|
weapon_change_flash = SubResource("Resource_jej6c")
|
||||||
test_mode = true
|
|
||||||
metadata/_custom_type_script = "uid://ylmao2ndp22y"
|
metadata/_custom_type_script = "uid://ylmao2ndp22y"
|
||||||
|
|
||||||
[node name="EffectsComponent" type="Node" parent="." unique_id=1393438576 node_paths=PackedStringArray("target_node")]
|
[node name="EffectsComponent" type="Node" parent="." unique_id=1393438576 node_paths=PackedStringArray("target_node")]
|
||||||
|
|
@ -178,5 +174,4 @@ script = ExtResource("12_effcomp")
|
||||||
target_node = NodePath("../Ship")
|
target_node = NodePath("../Ship")
|
||||||
metadata/_custom_type_script = "uid://65d3hbrpm21x"
|
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"]
|
[connection signal="timeout" from="WeaponCooldown" to="." method="_on_weapon_cooldown_timeout"]
|
||||||
|
|
|
||||||
|
|
@ -1,23 +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"]
|
|
||||||
|
|
||||||
[node name="PowerUpSpawner" type="Node2D" unique_id=1191463626]
|
|
||||||
script = ExtResource("2_9m3n7")
|
|
||||||
pickup_scene = ExtResource("1_xk8p2")
|
|
||||||
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"]
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[gd_scene format=3 uid="uid://dsqfuv86suu17"]
|
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_mmgg1"]
|
|
||||||
size = Vector2(14, 14)
|
|
||||||
|
|
||||||
[node name="PowerUpPickup" type="Area2D" unique_id=478708932]
|
|
||||||
collision_mask = 0
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=347643413]
|
|
||||||
shape = SubResource("RectangleShape2D_mmgg1")
|
|
||||||
|
|
@ -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://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="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="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]
|
[node name="World" type="Node2D" unique_id=1317852169]
|
||||||
|
|
||||||
|
|
@ -93,5 +92,3 @@ hframes = 4
|
||||||
frame = 3
|
frame = 3
|
||||||
|
|
||||||
[node name="Player" parent="Level" unique_id=652131079 instance=ExtResource("3_k0juu")]
|
[node name="Player" parent="Level" unique_id=652131079 instance=ExtResource("3_k0juu")]
|
||||||
|
|
||||||
[node name="PowerupSpawner" parent="Level" unique_id=1191463626 instance=ExtResource("7_i7141")]
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,4 @@ extends Node
|
||||||
|
|
||||||
signal weapon_changed(weapon_name: String, player_node: Node2D, player_sprite_size: Vector2, origin_offset: float)
|
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")
|
@warning_ignore_restore("unused_signal")
|
||||||
|
|
|
||||||
|
|
@ -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"
|
var icon_path = "res://graphics/" + weapon_name.to_lower() + ".png"
|
||||||
if ResourceLoader.exists(icon_path):
|
if ResourceLoader.exists(icon_path):
|
||||||
icon.texture = load(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:
|
else:
|
||||||
icon.texture = null
|
icon.texture = null
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,126 +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
|
|
||||||
|
|
||||||
# All weapon resources to spawn as pickups.
|
|
||||||
# Populate in the editor with your weapon_shot resources.
|
|
||||||
@export var all_weapon_shots: Array[WeaponShot] = []
|
|
||||||
|
|
||||||
var _pickups_spawned: int = 0
|
|
||||||
var _pickups_remaining: int = 0
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
if spawn_timer == null:
|
|
||||||
return
|
|
||||||
spawn_timer.wait_time = spawn_interval
|
|
||||||
|
|
||||||
|
|
||||||
func _on_spawn_timer_timeout() -> void:
|
|
||||||
if test_mode:
|
|
||||||
_spawn_test_pickups()
|
|
||||||
else:
|
|
||||||
_spawn_normal_pickup()
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
for weapon: WeaponShot in all_weapon_shots:
|
|
||||||
if weapon == null or weapon.shot_name.is_empty():
|
|
||||||
continue
|
|
||||||
|
|
||||||
var pickup := _spawn_pickup(weapon)
|
|
||||||
if not is_instance_valid(pickup):
|
|
||||||
continue # skipped — no valid icon
|
|
||||||
|
|
||||||
# Random position within viewport, with edge padding.
|
|
||||||
var margin: float = 15.0
|
|
||||||
pickup.position = Vector2(
|
|
||||||
randf_range(margin, screensize.x - margin),
|
|
||||||
randf_range(margin, screensize.y - margin)
|
|
||||||
)
|
|
||||||
|
|
||||||
add_child(pickup)
|
|
||||||
_pickups_spawned += 1
|
|
||||||
_pickups_remaining = _pickups_spawned
|
|
||||||
|
|
||||||
# Pause the timer while pickups are on screen; resume when all gone.
|
|
||||||
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
|
|
||||||
# Decrement counter; resume timer once all pickups are gone.
|
|
||||||
_pickups_remaining = max(0, _pickups_remaining - 1)
|
|
||||||
if _pickups_remaining <= 0:
|
|
||||||
if spawn_timer != null:
|
|
||||||
spawn_timer.start()
|
|
||||||
_pickups_spawned = 0
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
return pickup
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://qf6mgn4117o
|
|
||||||
|
|
@ -5,24 +5,13 @@ class_name ShootComponent extends Node
|
||||||
@onready var ship: Sprite2D = get_node("../Ship")
|
@onready var ship: Sprite2D = get_node("../Ship")
|
||||||
@onready var fire_helper: WeaponFireHelper = %WeaponFireHelper
|
@onready var fire_helper: WeaponFireHelper = %WeaponFireHelper
|
||||||
|
|
||||||
func shoot() -> void:
|
func shoot():
|
||||||
var active_weapons: Array[WeaponShot] = weapon_component.get_active_weapons()
|
var weapon_data = weapon_component.weapon_data
|
||||||
if active_weapons.is_empty():
|
var current_time = Time.get_ticks_msec() / 1000.0
|
||||||
return
|
|
||||||
|
|
||||||
var current_time: float = Time.get_ticks_msec() / 1000.0
|
if player.travel > weapon_data.projectile_vertical_spacing:
|
||||||
|
_shoot_standard(weapon_data, current_time)
|
||||||
# Burst gating: find the minimum projectile_vertical_spacing among all
|
_shoot_grouped(weapon_data, current_time)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Reset travel after firing to prevent multiple bursts per tap
|
# Reset travel after firing to prevent multiple bursts per tap
|
||||||
player.travel = 0.0
|
player.travel = 0.0
|
||||||
|
|
@ -37,12 +26,12 @@ func _get_spawn_position(weapon_data: WeaponShot) -> Vector2:
|
||||||
sprite_height = ship.texture.get_height()
|
sprite_height = ship.texture.get_height()
|
||||||
|
|
||||||
# Top edge of the sprite in world space, then offset by origin.
|
# 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)
|
return Vector2(ship.global_position.x, top_edge + weapon_data.origin)
|
||||||
|
|
||||||
|
|
||||||
# Standard projectile logic (delegates to WeaponFireHelper)
|
# 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
|
var total_projectiles: int = weapon_data.projectiles
|
||||||
|
|
||||||
if total_projectiles > 0:
|
if total_projectiles > 0:
|
||||||
|
|
@ -51,8 +40,8 @@ func _shoot_standard(weapon_data: WeaponShot, current_time: float) -> void:
|
||||||
get_tree().root.add_child(bullet)
|
get_tree().root.add_child(bullet)
|
||||||
|
|
||||||
# Delegate math to the helper
|
# Delegate math to the helper
|
||||||
var spawn_position: Vector2 = _get_spawn_position(weapon_data)
|
var spawn_position = _get_spawn_position(weapon_data)
|
||||||
var props: Dictionary = fire_helper.compute_standard_props(weapon_data, b, spawn_position, current_time)
|
var props = fire_helper.compute_standard_props(weapon_data, b, spawn_position, current_time)
|
||||||
bullet.position = props.position
|
bullet.position = props.position
|
||||||
bullet.angle = props.angle
|
bullet.angle = props.angle
|
||||||
bullet.time_offset = props.time_offset
|
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)
|
# 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 group: WeaponProjectileGroup in weapon_data.groups.filter(func(g): return g != null):
|
||||||
for b in range(group.bullet_count):
|
for b in range(group.bullet_count):
|
||||||
var bullet := weapon_data.bullet_scene.instantiate() as Area2D
|
var bullet := weapon_data.bullet_scene.instantiate() as Area2D
|
||||||
get_tree().root.add_child(bullet)
|
get_tree().root.add_child(bullet)
|
||||||
|
|
||||||
# Apply computed properties for the original bullet
|
# Apply computed properties for the original bullet
|
||||||
var spawn_position: Vector2 = _get_spawn_position(weapon_data)
|
var spawn_position = _get_spawn_position(weapon_data)
|
||||||
var props: Dictionary = fire_helper.compute_grouped_props(group, weapon_data, b, spawn_position, current_time, false)
|
var props = fire_helper.compute_grouped_props(group, weapon_data, b, spawn_position, current_time, false)
|
||||||
bullet.position = props.position
|
bullet.position = props.position
|
||||||
bullet.angle = props.angle
|
bullet.angle = props.angle
|
||||||
bullet.time_offset = props.time_offset
|
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)
|
get_tree().root.add_child(mirror_bullet)
|
||||||
|
|
||||||
# Apply computed properties for the mirrored bullet
|
# Apply computed properties for the mirrored bullet
|
||||||
var mirror_spawn: Vector2 = _get_spawn_position(weapon_data)
|
var mirror_spawn = _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_props = fire_helper.compute_grouped_props(group, weapon_data, b, mirror_spawn, current_time, true)
|
||||||
mirror_bullet.position = mirror_props.position
|
mirror_bullet.position = mirror_props.position
|
||||||
mirror_bullet.angle = mirror_props.angle
|
mirror_bullet.angle = mirror_props.angle
|
||||||
mirror_bullet.time_offset = mirror_props.time_offset
|
mirror_bullet.time_offset = mirror_props.time_offset
|
||||||
|
|
|
||||||
|
|
@ -4,186 +4,41 @@ class_name WeaponComponent extends Node
|
||||||
@export var available_weapons: Array[WeaponShot] = []
|
@export var available_weapons: Array[WeaponShot] = []
|
||||||
@export var weapon_change_flash: BaseEffect = null
|
@export var weapon_change_flash: BaseEffect = null
|
||||||
|
|
||||||
@export var test_mode: bool = false
|
var _current_weapon_index: int = 0
|
||||||
|
|
||||||
var _current_weapon_index = 0
|
@onready var effects_component: EffectComponent = %EffectsComponent
|
||||||
var _current_power_level = 0
|
|
||||||
var _cycled_weapon: WeaponShot = null
|
|
||||||
|
|
||||||
@onready var effects_component: EffectComponent = get_node("../EffectsComponent")
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
||||||
# Set the default weapon to be stock, i.e. index 1
|
# Set the default weapon to be stock, i.e. index 1
|
||||||
if weapon_data and not available_weapons.is_empty():
|
if weapon_data and not available_weapons.is_empty():
|
||||||
for i in range(available_weapons.size()):
|
for i in range(available_weapons.size()):
|
||||||
if available_weapons[i] == weapon_data:
|
if available_weapons[i] == weapon_data:
|
||||||
_current_weapon_index = i
|
_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
|
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:
|
func _emit_weapon_changed() -> void:
|
||||||
if weapon_data == null:
|
|
||||||
return
|
|
||||||
var ship: Sprite2D = get_parent().get_node("Ship")
|
var ship: Sprite2D = get_parent().get_node("Ship")
|
||||||
var sprite_size = ship.get_rect().size
|
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)
|
EventBus.weapon_changed.emit(weapon_data.shot_name, get_parent(), sprite_size, origin_offset)
|
||||||
|
|
||||||
|
|
||||||
func get_bullet_scene() -> PackedScene:
|
func get_bullet_scene() -> PackedScene:
|
||||||
return weapon_data.bullet_scene if weapon_data else null
|
return weapon_data.bullet_scene if weapon_data else null
|
||||||
|
|
||||||
|
|
||||||
func get_weapon_resource() -> Resource:
|
func get_weapon_resource() -> Resource:
|
||||||
return weapon_data
|
return weapon_data
|
||||||
|
|
||||||
|
|
||||||
func select_weapon_by_name(weapon_name: String) -> bool:
|
func select_weapon_by_name(weapon_name: String) -> bool:
|
||||||
for i in range(available_weapons.size()):
|
for i in range(available_weapons.size()):
|
||||||
var candidate: WeaponShot = available_weapons[i]
|
if available_weapons[i].shot_name == weapon_name:
|
||||||
if candidate != null and candidate.shot_name == weapon_name:
|
_current_weapon_index = i
|
||||||
_cycled_weapon = candidate # override additive behavior
|
weapon_data = available_weapons[i]
|
||||||
print("Switched to: ", weapon_name)
|
print("Switched to: ", weapon_name)
|
||||||
|
|
||||||
|
_emit_weapon_changed()
|
||||||
_trigger_change_flash()
|
_trigger_change_flash()
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
@ -194,36 +49,14 @@ func _trigger_change_flash() -> void:
|
||||||
effects_component.apply_effect(weapon_change_flash)
|
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():
|
if available_weapons.is_empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
_current_weapon_index = (_current_weapon_index + 1) % available_weapons.size()
|
_current_weapon_index = (_current_weapon_index + 1) % available_weapons.size()
|
||||||
var new_weapon: WeaponShot = available_weapons[_current_weapon_index]
|
weapon_data = available_weapons[_current_weapon_index]
|
||||||
if new_weapon == null:
|
print("Switched to: ", weapon_data.shot_name)
|
||||||
return
|
|
||||||
print("Switched to: ", new_weapon.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()
|
_emit_weapon_changed()
|
||||||
_trigger_change_flash()
|
_trigger_change_flash()
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,6 @@ extends Resource
|
||||||
# PackedScene reference to the bullet / projectile scene instantiated on fire.
|
# PackedScene reference to the bullet / projectile scene instantiated on fire.
|
||||||
@export var bullet_scene: PackedScene
|
@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")
|
@export_subgroup("Launch")
|
||||||
|
|
||||||
|
|
@ -85,20 +82,3 @@ extends Resource
|
||||||
|
|
||||||
# Array of projectile groups defining layered, multi-directional fire patterns.
|
# Array of projectile groups defining layered, multi-directional fire patterns.
|
||||||
@export var groups: Array[WeaponProjectileGroup] = []
|
@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
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue