Compare commits

..

No commits in common. "9808a03d13b21536c6af95fc2e478464dd414e25" and "24a58b60db0631e5fb0c8d5a77810b1b2daa5c88" have entirely different histories.

77 changed files with 123 additions and 1420 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bgs1htiuvgmic"
path="res://.godot/imported/gamma.png-96d3080e05fe2aad0fc3e1de127f7d0d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/gamma.png"
dest_files=["res://.godot/imported/gamma.png-96d3080e05fe2aad0fc3e1de127f7d0d.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

View File

@ -1,10 +0,0 @@
# Działko gamma
Generated with the built-in image_gen tool.
Style reference: `assets/icons/plasma.png`.
Output: `assets/icons/gamma.png`, transparent background.
## Prompt
Create a production-quality square game inventory icon for a spaceship GAMMA CANNON, matching the supplied plasma-cannon reference's detailed metallic 3D science-fiction rendering, three-quarter isometric view and crisp strong silhouette. Reference image is for style and perspective only. Design a DIFFERENT distinct weapon: compact heavy orbital gamma emitter with layered gunmetal and brushed silver shielding plates, dark mechanical rear mount, a broad circular emitter muzzle facing lower right, a bright lime-green irradiated spherical core contained between three radial containment prongs, a few small orbiting green particles within the muzzle, luminous green coolant conduits and small restrained brass accents. Industrial believable spaceship-mounted turret, no pistol handle. Radiation aesthetic in green, no purple plasma. The cannon alone occupies 85 percent of the square, centered, all parts fully in frame, no cropped extremities. Readable at 48px thumbnail, bold broad shapes and controlled highlights, no background scene, no border, no labels, no lettering, no text. Genuine transparent background and clean alpha edges, no ground or drop shadow. Single isolated icon.

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96"><defs><radialGradient id="g"><stop stop-color="#edffd0"/><stop offset=".5" stop-color="#80ff45"/><stop offset="1" stop-color="#174823"/></radialGradient></defs><path d="M27 80V47L34 24H62L69 47V80Z" fill="#293e46" stroke="#95b7ad" stroke-width="3"/><path d="M38 60V18H58V60" fill="#11241a" stroke="#59d44c" stroke-width="3"/><circle cx="48" cy="25" r="17" fill="url(#g)"/><ellipse cx="48" cy="25" rx="25" ry="10" fill="none" stroke="#b7ff72" stroke-width="2" transform="rotate(-35 48 25)"/><circle cx="65" cy="10" r="3" fill="#e0ffc3"/><path d="M34 68H62M34 74H62" stroke="#7eff4f" stroke-width="3"/></svg>

Before

Width:  |  Height:  |  Size: 689 B

View File

@ -1,43 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b30nivtfvxw31"
path="res://.godot/imported/gamma.svg-877dc4511099b181b72b5bbafe97b7fd.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/gamma.svg"
dest_files=["res://.godot/imported/gamma.svg-877dc4511099b181b72b5bbafe97b7fd.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ooii0cnkunnb"
path="res://.godot/imported/aluminium.png-63588dbb597f01d62b3f0366767573a6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/aluminium.png"
dest_files=["res://.godot/imported/aluminium.png-63588dbb597f01d62b3f0366767573a6.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvxxyh35g4qgh"
path="res://.godot/imported/ammunition.png-55b812a61f21303b828ce907b57955d0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/ammunition.png"
dest_files=["res://.godot/imported/ammunition.png-55b812a61f21303b828ce907b57955d0.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bt0ve4g2lbcxc"
path="res://.godot/imported/iron.png-9f0bab611f228be580c28e352ca1d8cc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/iron.png"
dest_files=["res://.godot/imported/iron.png-9f0bab611f228be580c28e352ca1d8cc.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bwg8xyrhjg2ql"
path="res://.godot/imported/irradiated_ammunition.png-43f90162db35d57c3d57de6de22435e4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/irradiated_ammunition.png"
dest_files=["res://.godot/imported/irradiated_ammunition.png-43f90162db35d57c3d57de6de22435e4.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 988 KiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ns3mapyv4n2g"
path="res://.godot/imported/missile.png-d91a6d6e7348b7fba50d4cc3ac087245.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/missile.png"
dest_files=["res://.godot/imported/missile.png-d91a6d6e7348b7fba50d4cc3ac087245.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b1gltl1goxq3a"
path="res://.godot/imported/parts.png-f14343f401c851f9b669606251dc7106.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/parts.png"
dest_files=["res://.godot/imported/parts.png-f14343f401c851f9b669606251dc7106.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

View File

@ -1,46 +0,0 @@
{
"tool": "built-in image_gen",
"transparent_background": true,
"prompts": [
{
"key": "iron",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: a rugged dark graphite-gray iron ore nugget with a few rusty orange seams and raw crystalline metallic facets, clearly a mined mineral",
"asset": "assets/icons/resources/iron.png"
},
{
"key": "titanium",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: a chunky cool blue-gray titanium ore crystal with silver-blue metallic facets and pale blue edge highlights, clearly different from iron",
"asset": "assets/icons/resources/titanium.png"
},
{
"key": "aluminium",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: two small stacked refined aluminium ingots, bright matte silvery metal, pale cool white reflections, clean beveled edges",
"asset": "assets/icons/resources/aluminium.png"
},
{
"key": "uranium",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: a rugged uranium ore chunk, dark stone with luminous toxic lime-green mineral veins and tiny green crystal inclusions, unmistakable radiation resource",
"asset": "assets/icons/resources/uranium.png"
},
{
"key": "parts",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: a compact cluster of mechanical salvage parts: one steel gear, one brass bearing and a small bent circuit board, bold readable silhouette",
"asset": "assets/icons/resources/parts.png"
},
{
"key": "ammunition",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: three short brass-cased autocannon cartridges with metallic copper tips, grouped neatly, one cartridge slightly taller",
"asset": "assets/icons/resources/ammunition.png"
},
{
"key": "irradiated_ammunition",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: two futuristic gamma cannon ammunition capsules: armored dark steel cylindrical casings, small containment bands and luminous lime-green sealed cores, no conventional bullets",
"asset": "assets/icons/resources/irradiated_ammunition.png"
},
{
"key": "missile",
"prompt": "Generate one high-quality isolated game inventory resource icon. Square image with a genuine transparent background. Detailed stylized 3D science-fiction game item render, crisp metal and mineral materials, broad readable silhouette, three-quarter view, controlled highlights. Center the object with 12 percent transparent padding on all sides; keep every part completely inside the frame. Must read clearly as a 32px HUD icon. No scene, no floor, no drop shadow, no border, no text, no lettering, no labels. Subject: a single compact spaceship missile with pointed silver nose, dark steel casing, two small red fins and a restrained orange rear nozzle, diagonal three-quarter view",
"asset": "assets/icons/resources/missile.png"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://e3hyjgddtws3"
path="res://.godot/imported/titanium.png-0b4998bc95056c5a7906568e449b30ff.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/titanium.png"
dest_files=["res://.godot/imported/titanium.png-0b4998bc95056c5a7906568e449b30ff.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

View File

@ -1,40 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b6enhdjvecuep"
path="res://.godot/imported/uranium.png-ceac43be56616b10258ea19f379a1280.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/icons/resources/uranium.png"
dest_files=["res://.godot/imported/uranium.png-ceac43be56616b10258ea19f379a1280.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

View File

@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="ItemAffixDatabase" load_steps=18 format=3]
[gd_resource type="Resource" script_class="ItemAffixDatabase" load_steps=15 format=3]
[ext_resource type="Script" path="res://scripts/items/item_affix_database.gd" id="1_database"]
[ext_resource type="Script" path="res://scripts/items/item_affix_definition.gd" id="2_type"]
@ -17,10 +17,6 @@
[ext_resource type="Resource" path="res://resources/items/affixes/shield_on_damage.tres" id="14_affix"]
[ext_resource type="Resource" path="res://resources/items/affixes/kinetic_absorption.tres" id="kinetic"]
[ext_resource type="Resource" path="res://resources/items/affixes/energy_absorption.tres" id="energy"]
[ext_resource type="Resource" path="res://resources/items/affixes/radiation_absorption.tres" id="radiation"]
[resource]
script = ExtResource("1_database")
affixes = Array[ExtResource("2_type")]([ExtResource("3_affix"), ExtResource("4_affix"), ExtResource("5_affix"), ExtResource("6_affix"), ExtResource("7_affix"), ExtResource("8_affix"), ExtResource("9_affix"), ExtResource("10_affix"), ExtResource("11_affix"), ExtResource("12_affix"), ExtResource("13_affix"), ExtResource("14_affix"), ExtResource("kinetic"), ExtResource("energy"), ExtResource("radiation")])
affixes = Array[ExtResource("2_type")]([ExtResource("3_affix"), ExtResource("4_affix"), ExtResource("5_affix"), ExtResource("6_affix"), ExtResource("7_affix"), ExtResource("8_affix"), ExtResource("9_affix"), ExtResource("10_affix"), ExtResource("11_affix"), ExtResource("12_affix"), ExtResource("13_affix"), ExtResource("14_affix")])

View File

@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="ItemAffixDefinition" load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/items/item_affix_definition.gd" id="1"]
[resource]
script = ExtResource("1")
affix_id = "energy_absorption"
display_name = "Absorpcja obrażeń energetycznych"
stat_key = "energy_absorption"
description = "Niweluje obrażenia tego typu przed tarczą i pancerzem. Łączny limit statku: 60%."
category_mask = 95
minimum_value_level_1 = 1.0
maximum_value_level_1 = 1.0
minimum_value_level_1000 = 10.0
maximum_value_level_1000 = 15.0

View File

@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="ItemAffixDefinition" load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/items/item_affix_definition.gd" id="1"]
[resource]
script = ExtResource("1")
affix_id = "kinetic_absorption"
display_name = "Absorpcja obrażeń kinetycznych"
stat_key = "kinetic_absorption"
description = "Niweluje obrażenia tego typu przed tarczą i pancerzem. Łączny limit statku: 60%."
category_mask = 95
minimum_value_level_1 = 1.0
maximum_value_level_1 = 1.0
minimum_value_level_1000 = 10.0
maximum_value_level_1000 = 15.0

View File

@ -1,13 +0,0 @@
[gd_resource type="Resource" script_class="ItemAffixDefinition" load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/items/item_affix_definition.gd" id="1"]
[resource]
script = ExtResource("1")
affix_id = "radiation_absorption"
display_name = "Absorpcja obrażeń radiacyjnych"
stat_key = "radiation_absorption"
description = "Niweluje obrażenia tego typu przed tarczą i pancerzem. Łączny limit statku: 60%."
category_mask = 95
minimum_value_level_1 = 1.0
maximum_value_level_1 = 1.0
minimum_value_level_1000 = 10.0
maximum_value_level_1000 = 15.0

View File

@ -1,21 +0,0 @@
[gd_resource type="Resource" script_class="ItemDefinition" load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/items/item_definition.gd" id="1"]
[ext_resource type="Texture2D" path="res://assets/icons/gamma.png" id="icon"]
[resource]
script = ExtResource("1")
icon = ExtResource("icon")
item_id = "gamma_cannon"
display_name = "Działko gamma"
category = 0
description = "Radiacyjna kula. Napromieniowanie: 3 s, 25% obrażeń trafienia/s. Kolejne trafienia odświeżają czas. Zużywa napromieniowaną amunicję."
primary_stat = "damage"
primary_value = 15.0
stat_suffix = " DMG"
weapon_kind = 4
fire_interval = 0.8
projectile_speed = 430.0
weapon_range = 800.0
damage_lower_roll_min = 10
damage_lower_roll_max = 14
damage_spread_roll_min = 4
damage_spread_roll_max = 8

View File

@ -1,16 +1,6 @@
[gd_resource type="Theme" load_steps=3 format=3]
[gd_resource type="Theme" load_steps=2 format=3]
[ext_resource type="FontFile" path="res://assets/fonts/font2.ttf" id="1_font"]
[sub_resource type="StyleBoxFlat" id="TooltipShell"]
bg_color = Color(0.035, 0.055, 0.085, 1)
content_margin_left = 2.0
content_margin_top = 2.0
content_margin_right = 2.0
content_margin_bottom = 2.0
shadow_size = 0
[resource]
default_font = ExtResource("1_font")
TooltipPanel/styles/panel = SubResource("TooltipShell")
TooltipLabel/font_sizes/font_size = 11

View File

@ -3,7 +3,7 @@
[ext_resource type="Script" path="res://scripts/ui/planet_services_menu.gd" id="1_services"]
[sub_resource type="StyleBoxFlat" id="PanelStyle"]
bg_color = Color(0.045, 0.075, 0.11, 0.64)
bg_color = Color(0.045, 0.075, 0.11, 1)
border_width_left = 1
border_width_top = 1
border_width_right = 1

View File

@ -43,7 +43,7 @@ text = "SYSTEMY POKŁADOWE"
horizontal_alignment = 1
[node name="EquipmentLayout" type="Control" parent="Contents"]
custom_minimum_size = Vector2(284, 552)
custom_minimum_size = Vector2(284, 456)
layout_mode = 2
mouse_filter = 2
@ -100,9 +100,9 @@ overlay_slot = true
[node name="Module2" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 14.0
offset_top = 200.0
offset_top = 178.0
offset_right = 78.0
offset_bottom = 264.0
offset_bottom = 242.0
slot_kind = "module"
slot_index = 1
overlay_slot = true
@ -110,9 +110,9 @@ overlay_slot = true
[node name="Module3" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 14.0
offset_top = 292.0
offset_top = 248.0
offset_right = 78.0
offset_bottom = 356.0
offset_bottom = 312.0
slot_kind = "module"
slot_index = 2
overlay_slot = true
@ -120,9 +120,9 @@ overlay_slot = true
[node name="Module4" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 14.0
offset_top = 384.0
offset_top = 318.0
offset_right = 78.0
offset_bottom = 448.0
offset_bottom = 382.0
slot_kind = "module"
slot_index = 3
overlay_slot = true
@ -130,9 +130,9 @@ overlay_slot = true
[node name="Module5" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 14.0
offset_top = 476.0
offset_top = 388.0
offset_right = 78.0
offset_bottom = 540.0
offset_bottom = 452.0
slot_kind = "module"
slot_index = 4
overlay_slot = true
@ -150,9 +150,9 @@ overlay_slot = true
[node name="Extension2" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 206.0
offset_top = 200.0
offset_top = 178.0
offset_right = 270.0
offset_bottom = 264.0
offset_bottom = 242.0
slot_kind = "extension"
slot_index = 1
overlay_slot = true
@ -160,9 +160,9 @@ overlay_slot = true
[node name="Extension3" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 206.0
offset_top = 292.0
offset_top = 248.0
offset_right = 270.0
offset_bottom = 356.0
offset_bottom = 312.0
slot_kind = "extension"
slot_index = 2
overlay_slot = true
@ -170,9 +170,9 @@ overlay_slot = true
[node name="Extension4" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 206.0
offset_top = 384.0
offset_top = 318.0
offset_right = 270.0
offset_bottom = 448.0
offset_bottom = 382.0
slot_kind = "extension"
slot_index = 3
overlay_slot = true
@ -180,9 +180,9 @@ overlay_slot = true
[node name="Extension5" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
layout_mode = 0
offset_left = 206.0
offset_top = 476.0
offset_top = 388.0
offset_right = 270.0
offset_bottom = 540.0
offset_bottom = 452.0
slot_kind = "extension"
slot_index = 4
overlay_slot = true

View File

@ -49,6 +49,16 @@ layout_mode = 2
theme_override_font_sizes/font_size = 11
text = "ZAMKNIJ"
[node name="Ammunition" type="HBoxContainer" parent="Contents"]
layout_mode = 2
theme_override_constants/separation = 12
[node name="Count" type="Label" parent="Contents/Ammunition"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 11
text = "AMUNICJA: 50"
[node name="CargoGrid" type="GridContainer" parent="Contents"]
layout_mode = 2
size_flags_horizontal = 4

View File

@ -7,7 +7,7 @@ const BALANCE := preload("res://scripts/captain/captain_balance.gd")
@export var experience: int = 0
@export var credits: int = 0
@export var parts: float = 0.0
@export var minerals: Dictionary = {"uranium": 0, "titanium": 0, "iron": 0, "aluminium": 0}
@export var minerals: Dictionary = {"titanium": 0, "iron": 0, "aluminium": 0}
@export var learning_points: int = 0
@export var skills: Dictionary = {
"piloting": 0, "gunner": 0, "mechanic": 0, "sharp_eye": 0,

View File

@ -1,28 +0,0 @@
extends RefCounted
class_name DamageTypes
enum Type { KINETIC, ENERGY, RADIATION }
const KEYS := ["kinetic_absorption", "energy_absorption", "radiation_absorption"]
const NAMES := ["Kinetyczne", "Energetyczne", "Radiacyjne"]
static func for_weapon(kind: int) -> int:
if kind == ItemDefinition.WeaponKind.GAMMA:
return Type.RADIATION
if kind in [ItemDefinition.WeaponKind.LASER, ItemDefinition.WeaponKind.PLASMA]:
return Type.ENERGY
return Type.KINETIC
static func capped(values: Vector3) -> Vector3:
values = values.max(Vector3.ZERO)
var total := values.x + values.y + values.z
return values * (60.0 / total) if total > 60.0 else values
static func enemy_profile(level: int, seed_value: int) -> Dictionary:
var rng := RandomNumberGenerator.new()
rng.seed = seed_value
var weights := Vector3(rng.randf_range(0.1, 1.0), rng.randf_range(0.1, 1.0), rng.randf_range(0.1, 1.0))
var total := lerpf(2.0, 60.0, pow(float(clampi(level, 1, 1000) - 1) / 999.0, 1.25))
return {"kind": rng.randi_range(0, 4), "absorption": weights * total / (weights.x + weights.y + weights.z)}
static func describe(values: Vector3) -> String:
return "Absorpcja: K %.1f%% · E %.1f%% · R %.1f%%" % [values.x, values.y, values.z]

View File

@ -1 +0,0 @@
uid://cumj03bmuxvlr

View File

@ -46,7 +46,5 @@ func _draw() -> void:
var background := Rect2(-width * 0.5, -3.0, width, 6.0)
draw_rect(background.grow(1.0), Color("08111dee"))
draw_rect(background, Color("39232bee"))
draw_rect(Rect2(-width * 0.5, 5.0, width, 3.0), Color("142a3d"))
draw_rect(Rect2(-width * 0.5, 5.0, width * clampf(enemy.shields / maxf(1.0, enemy.max_shields), 0.0, 1.0), 3.0), Color("67cfff"))
var color := _tier_color()
draw_rect(Rect2(background.position, Vector2(width * get_health_ratio(), 6.0)), color)

View File

@ -49,8 +49,5 @@ func _hit_target(target: Variant) -> void:
get_parent().add_child(effect)
effect.call("configure", false, Color(1.0, 0.32, 0.38))
if target is Node and target.has_method("take_damage"):
if target is CharacterBody2D:
target.call("take_damage", damage, false, DamageTypes.Type.ENERGY)
else:
target.call("take_damage", damage)
target.call("take_damage", damage)
queue_free()

View File

@ -2,14 +2,12 @@ extends CharacterBody2D
const DAMAGE_POPUP_SCENE := preload("res://scenes/game/damage_popup.tscn")
const IMPACT_EFFECT_SCENE := preload("res://scenes/game/impact_effect.tscn")
const PLASMA_PROJECTILE_SCENE := preload("res://scenes/game/enemy_plasma_projectile.tscn")
const WEAPON_AUDIO_SCRIPT := preload("res://scripts/game/weapon_audio.gd")
const ITEM_GENERATOR := preload("res://scripts/items/item_generator.gd")
const LOOT_PICKUP := preload("res://scripts/items/loot_pickup.gd")
const LOOT_OFFSET := Vector2(0, 36)
const LEVEL_BALANCE := preload("res://scripts/levels/level_balance.gd")
const LASER_COOLDOWN_MULTIPLIER := 2.0
const LASER_MINIMUM_COOLDOWN := 1.4
const LASER_MISS_CHANCE := 0.35
@export_enum("Normal", "Elite", "Boss") var loot_tier: int = 0
@ -22,12 +20,6 @@ const LASER_MISS_CHANCE := 0.35
@export var weapon_cooldown := 1.5
@export var weapon_damage := 8.0
var max_shields := 20.0
var shields := 20.0
var absorption := Vector3.ZERO
var weapon_kind := ItemDefinition.WeaponKind.PLASMA
var _shield_visual: Node2D
var hull := 50.0
var _player: CharacterBody2D
var _activated := false
@ -37,13 +29,8 @@ var _weapon_timer := 0.0
func _ready() -> void:
hull = max_hull
_sprite = $Sprite2D
_shield_visual = Node2D.new()
_shield_visual.set_script(preload("res://scripts/game/shield_visual.gd"))
_shield_visual.scale = Vector2.ONE * get_visual_size() / 64.0
add_child(_shield_visual)
_shield_visual.set_shield_values(shields, max_shields)
add_to_group("enemies")
_weapon_timer = randf_range(0.2, get_fire_interval())
_weapon_timer = randf_range(0.2, weapon_cooldown)
func apply_level_settings(level: int, tier: int) -> void:
loot_tier = tier
@ -55,11 +42,6 @@ func apply_level_settings(level: int, tier: int) -> void:
var damage_bonus := 4.0 if tier == 2 else (1.3 if tier == 1 else 1.0)
max_hull = 50.0 * power * hull_multiplier * combat_multiplier
hull = max_hull
max_shields = max_hull * 0.4
shields = max_shields
var profile := DamageTypes.enemy_profile(level, GameState.system_seed)
weapon_kind = int(profile["kind"])
absorption = profile["absorption"]
weapon_damage = 8.0 * power * damage_bonus * combat_multiplier
approach_speed = 62.0 * (1.0 + 0.0005 * (clampi(level, 1, 1000) - 1)) * boss_multiplier
approach_acceleration = 85.0 * boss_multiplier
@ -68,9 +50,6 @@ func apply_level_settings(level: int, tier: int) -> void:
weapon_range = 680.0 * boss_multiplier
weapon_cooldown = maxf(0.55, 1.5 / (1.0 + 0.001 * (clampi(level, 1, 1000) - 1))) / boss_multiplier
$CollisionShape2D.scale = Vector2.ONE * boss_multiplier
if is_instance_valid(_shield_visual):
_shield_visual.scale = Vector2.ONE * get_visual_size() / 64.0
_shield_visual.set_shield_values(shields, max_shields)
func configure(player: CharacterBody2D, ship_texture: Texture2D) -> void:
_player = player
@ -105,7 +84,7 @@ func _physics_process(delta: float) -> void:
_weapon_timer = maxf(0.0, _weapon_timer - delta)
if to_player.length() <= weapon_range and _weapon_timer <= 0.0:
_fire_plasma(direction)
_weapon_timer = get_fire_interval()
_weapon_timer = weapon_cooldown
else:
velocity = velocity.move_toward(Vector2.ZERO, approach_acceleration * delta)
@ -119,77 +98,31 @@ func _check_player_collision() -> void:
_player.call("register_enemy_collision", global_position)
func _fire_plasma(direction: Vector2) -> void:
var muzzle := global_position + direction * (get_visual_size() * 0.5 + 8.0)
if weapon_kind == ItemDefinition.WeaponKind.LASER:
var endpoint := muzzle + _laser_aim_direction(direction) * weapon_range
var query := PhysicsRayQueryParameters2D.create(muzzle, endpoint, 3, [get_rid()])
var hit := get_world_2d().direct_space_state.intersect_ray(query)
if not hit.is_empty():
endpoint = hit["position"]
var target: Node = hit.get("collider")
if target is CharacterBody2D and target.has_method("take_damage"):
target.take_damage(weapon_damage, false, DamageTypes.Type.ENERGY)
var beam := Line2D.new()
beam.set_script(preload("res://scripts/game/laser_beam_visual.gd"))
beam.width = 4.0
beam.default_color = Color(1.0, 0.3, 0.3)
beam.add_point(muzzle)
beam.add_point(endpoint)
get_parent().add_child(beam)
get_tree().create_timer(0.1).timeout.connect(beam.queue_free)
else:
var projectile := preload("res://scenes/game/weapon_projectile.tscn").instantiate()
projectile.enemy_shot = true
projectile.collision_mask = 3
projectile.missile_homing = false
var speed := 430.0 if weapon_kind == ItemDefinition.WeaponKind.GAMMA else (950.0 if weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN else 600.0)
projectile.configure(muzzle, direction, weapon_kind, speed, weapon_damage, weapon_range)
get_parent().add_child(projectile)
WEAPON_AUDIO_SCRIPT.play(get_parent(), global_position, weapon_kind, -4.0)
var projectile := PLASMA_PROJECTILE_SCENE.instantiate() as Area2D
projectile.call("configure", global_position + direction * (get_visual_size() * 0.5), direction, weapon_damage)
get_parent().add_child(projectile)
# Relative to the Plasma bus: enemy shots are 4 dB quieter than player shots.
WEAPON_AUDIO_SCRIPT.play(get_parent(), global_position, ItemDefinition.WeaponKind.PLASMA, -4.0)
func take_weapon_hit(amount: float, critical: bool, damage_type: int = DamageTypes.Type.KINETIC) -> void:
if is_queued_for_deletion() or hull <= 0.0:
return
var before := hull + shields
take_damage(amount, critical, damage_type)
func take_weapon_hit(amount: float, critical: bool) -> void:
var actual_damage := minf(maxf(0.0, amount), maxf(0.0, hull))
if is_queued_for_deletion():
actual_damage = 0.0
take_damage(amount, critical)
if is_instance_valid(_player):
_player.call("restore_shields_from_damage", maxf(0.0, before - hull - shields))
_player.call("restore_shields_from_damage", actual_damage)
func take_damage(amount: float, critical: bool = false, damage_type: int = DamageTypes.Type.KINETIC) -> void:
func take_damage(amount: float, critical: bool = false) -> void:
if is_queued_for_deletion() or hull <= 0.0:
return
var actual := maxf(0.0, amount) * (1.0 - absorption[damage_type] / 100.0)
var shield_damage := minf(shields, actual)
shields -= shield_damage
hull = maxf(0.0, hull - actual + shield_damage)
if is_instance_valid(_shield_visual):
_shield_visual.set_shield_values(shields, max_shields)
if shield_damage > 0.0:
_shield_visual.flash_hit()
_show_damage_popup(actual, critical)
hull = maxf(0.0, hull - maxf(0.0, amount))
_show_damage_popup(amount, critical)
if hull <= 0.0:
_reward_captain()
_drop_loot()
_spawn_destruction_effect()
queue_free()
func get_fire_interval() -> float:
if weapon_kind == ItemDefinition.WeaponKind.LASER:
return maxf(LASER_MINIMUM_COOLDOWN, weapon_cooldown * LASER_COOLDOWN_MULTIPLIER)
return weapon_cooldown
func _laser_aim_direction(direction: Vector2) -> Vector2:
if randf() < LASER_MISS_CHANCE:
var side := Vector2(-direction.y, direction.x)
var distance := global_position.distance_to(_player.global_position) if is_instance_valid(_player) else weapon_range
var offset := randf_range(60.0, 110.0) * (-1.0 if randf() < 0.5 else 1.0)
return (direction * maxf(1.0, distance) + side * offset).normalized()
return direction.rotated(randf_range(-0.02, 0.02))
func get_weapon_dps() -> float:
var interval := get_fire_interval()
return weapon_damage / maxf(0.001, interval) * (1.0 + 0.25 * minf(3.0, interval) if weapon_kind == ItemDefinition.WeaponKind.GAMMA else 1.0)
func _reward_captain() -> void:
if is_instance_valid(_player):
var reward := GameState.captain.reward_enemy(GameState.system_level, loot_tier, float(_player.get("experience_multiplier")), float(_player.get("credits_multiplier")), false)

View File

@ -19,9 +19,6 @@ var _sidebar_tween: Tween
const SIDEBAR_WIDTH := 306.0
const EQUIPMENT_UI_SCALE := 0.8
var _debug_scroll: ScrollContainer
var _debug_actions: VBoxContainer
var _debug_item_button: Button
var _debug_item_status: Label
var _captain_panel: PanelContainer
var _cockpit: Control
@ -43,10 +40,6 @@ func _ready() -> void:
# The display font has unusually tall vertical metrics; keep debug rows compact.
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
_create_sidebar()
var enemy_panel := Control.new()
enemy_panel.name = "EnemyDrawer"
enemy_panel.set_script(preload("res://scripts/ui/enemy_drawer.gd"))
$HUDRoot.add_child(enemy_panel)
_inventory_clip = Control.new()
_inventory_clip.name = "InventoryDrawer"
_inventory_clip.clip_contents = true
@ -73,23 +66,6 @@ func _create_sidebar() -> void:
_captain_panel.position = Vector2(12, 12)
_captain_panel.size = Vector2(320, _captain_panel.get_combined_minimum_size().y)
_captain_panel.resized.connect(_layout_debug)
_debug_actions = VBoxContainer.new()
_debug_actions.name = "DebugActions"
_debug_actions.add_theme_constant_override("separation", 6)
$HUDRoot.add_child(_debug_actions)
_debug_item_button = Button.new()
_debug_item_button.name = "GenerateItem"
_debug_item_button.custom_minimum_size.y = 36
preload("res://scripts/ui/hud_button_style.gd").apply(_debug_item_button, Color("a9ef83"))
_debug_item_button.pressed.connect(_give_debug_item)
_debug_actions.add_child(_debug_item_button)
_debug_item_status = Label.new()
_debug_item_status.add_theme_font_size_override("font_size", 10)
_debug_item_status.add_theme_color_override("font_color", Color("a9ef83"))
_debug_item_status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_debug_item_status.hide()
_debug_actions.add_child(_debug_item_status)
_debug_actions.minimum_size_changed.connect(func() -> void: _layout_debug.call_deferred())
_debug_scroll = ScrollContainer.new()
_debug_scroll.name = "DebugScroll"
_debug_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
@ -130,14 +106,13 @@ func _create_sidebar() -> void:
_sidebar_toggle.name = "ShipToggle"
_sidebar_toggle.text = "STATEK ▾" if _sidebar_open else "STATEK ◀"
_sidebar_toggle.add_theme_font_size_override("font_size", 12)
preload("res://scripts/ui/hud_button_style.gd").apply(_sidebar_toggle)
preload("res://scripts/ui/hud_button_style.gd").apply(inventory_button, Color("b9a0ef"))
_sidebar_toggle.focus_mode = Control.FOCUS_NONE
$HUDRoot.add_child(_sidebar_toggle)
_sidebar_toggle.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
_sidebar_toggle.offset_left = -180
_sidebar_toggle.offset_right = -12
_sidebar_toggle.offset_top = 12
_sidebar_toggle.offset_bottom = 48
_sidebar_toggle.offset_bottom = 40
_sidebar_toggle.pressed.connect(_toggle_sidebar)
_cockpit = Control.new()
_cockpit.name = "Cockpit"
@ -152,12 +127,8 @@ func _layout_debug() -> void:
return
var top := _captain_panel.position.y + _captain_panel.size.y + 6.0
debug_toggle.position = Vector2(12, top)
_debug_actions.position = Vector2(18, top + 30)
_debug_actions.size = Vector2(310, _debug_actions.get_combined_minimum_size().y)
_debug_actions.visible = _debug_expanded
var scroll_top := _debug_actions.position.y + _debug_actions.size.y + 8.0
_debug_scroll.position = Vector2(18, scroll_top)
_debug_scroll.size = Vector2(310, maxf(40, $HUDRoot.size.y - scroll_top - 12))
_debug_scroll.position = Vector2(18, top + 30)
_debug_scroll.size = Vector2(310, maxf(40, $HUDRoot.size.y - top - 42))
_debug_scroll.visible = _debug_expanded
func _toggle_sidebar() -> void:
@ -183,14 +154,10 @@ func _layout_sidebar() -> void:
_sidebar.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
_sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), minf(_sidebar.get_node("Scroll/Contents").get_combined_minimum_size().y + 16.0, maxf(100.0, ($HUDRoot.size.y - 64.0) / EQUIPMENT_UI_SCALE)))
_cockpit.scale = Vector2.ONE * 0.85
_cockpit.size = Vector2(minf(640.0, ($HUDRoot.size.x - 24) / 0.85), 104)
_cockpit.size = Vector2(minf(640.0, ($HUDRoot.size.x - 24) / 0.85), 126)
_cockpit.position = Vector2(($HUDRoot.size.x - _cockpit.size.x * 0.85) * 0.5, $HUDRoot.size.y - _cockpit.size.y * 0.85 - 12)
if _ability_bar != null:
var abilities := _ability_bar.get_parent() as Control
abilities.offset_bottom = _cockpit.position.y - $HUDRoot.size.y - 14.0
abilities.offset_top = abilities.offset_bottom - 44.0
_layout_debug()
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 56)
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 44)
_sidebar.visible = _sidebar_open
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
_layout_inventory()
@ -206,10 +173,6 @@ func _process(_delta: float) -> void:
_update_ability_bar()
if not _debug_expanded:
return
var inventory := player.get_node("ShipInventory") as ShipInventory
_debug_item_button.text = "LOSUJ PRZEDMIOT · POZ. %d" % GameState.system_level
_debug_item_button.disabled = inventory.get_cargo_used() >= ShipInventory.CARGO_SIZE
_debug_item_button.tooltip_text = "Magazyn jest pełny — zwolnij miejsce." if _debug_item_button.disabled else "Losuje przedmiot i addony dla poziomu aktualnego układu."
var game := get_parent()
debug_info.text = """POZIOM UKŁADU: %d
WROGOWIE: %d
@ -233,25 +196,7 @@ func _toggle_debug() -> void:
GameState.hud_panel_state["debug_expanded"] = _debug_expanded
debug_info.visible = _debug_expanded
_debug_scroll.visible = _debug_expanded
_debug_actions.visible = _debug_expanded
_update_debug_toggle()
_layout_debug()
func _give_debug_item() -> bool:
var inventory := player.get_node("ShipInventory") as ShipInventory
_debug_item_status.show()
if inventory.get_cargo_used() >= ShipInventory.CARGO_SIZE:
_debug_item_status.text = "Magazyn pełny — zwolnij miejsce."
_debug_item_status.add_theme_color_override("font_color", Color("ff8989"))
_layout_debug.call_deferred()
return false
var item := preload("res://scripts/items/item_generator.gd").generate(GameState.system_level)
if not inventory.add_item(item):
return false
_debug_item_status.text = "Dodano: %s · Poz. %d" % [item.definition.display_name, item.item_level]
_debug_item_status.add_theme_color_override("font_color", Color("a9ef83"))
_layout_debug.call_deferred()
return true
func _update_debug_toggle() -> void:
debug_toggle.text = "DEBUG [−]" if _debug_expanded else "DEBUG [+]"
@ -312,8 +257,8 @@ func _create_ability_bar() -> void:
panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM)
panel.offset_left = -120.0
panel.offset_right = 120.0
panel.offset_top = -220.0
panel.offset_bottom = -176.0
panel.offset_top = -176.0
panel.offset_bottom = -132.0
panel.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
_ability_bar = HBoxContainer.new()
_ability_bar.name = "AbilityButtons"

View File

@ -10,7 +10,7 @@ const WORLD_SIZE := SYSTEM_GENERATOR.WORLD_SIZE
const SERVICE_APPROACH_DISTANCE := 180.0
const WORMHOLE_PLANET_DISTANCE := 620.0
const WORMHOLE_ENTRY_RADIUS := 48.0
const BOSS_SPAWN_DISTANCE := 1800.0
const BOSS_SPAWN_DISTANCE := 900.0
const BOSS_SPAWN_MARGIN := 160.0
const ENEMY_SCENE := preload("res://scenes/game/enemy_ship.tscn")
const ENEMY_TEXTURES: Array[Texture2D] = preload("res://scripts/items/alien_ship_catalog.gd").TEXTURES

View File

@ -1,43 +0,0 @@
extends Node
var remaining := 0.0
var tick_damage := 0.0
var credited_to_player := false
var _tick_elapsed := 0.0
var _base_color := Color.WHITE
var _sprite: Sprite2D
static func apply(target: Node, damage_per_second: float, player_hit: bool) -> void:
if not target.has_method("take_damage") or not target is CharacterBody2D:
return
var status := target.get_node_or_null("RadiationStatus")
if status == null:
status = Node.new()
status.set_script(load("res://scripts/game/radiation_status.gd"))
status.name = "RadiationStatus"
target.add_child(status)
status.remaining = 3.0
status.tick_damage = maxf(status.tick_damage, damage_per_second)
status.credited_to_player = player_hit
func _ready() -> void:
_sprite = get_parent().get_node_or_null("Sprite2D")
if _sprite != null:
_base_color = _sprite.modulate
func _physics_process(delta: float) -> void:
var step := minf(delta, remaining)
remaining = maxf(0.0, remaining - delta)
var target := get_parent()
if _sprite != null:
_sprite.modulate = Color(0.3, 1.0, 0.35) if remaining > 0.0 else _base_color
_tick_elapsed += step
if _tick_elapsed >= 0.5 or (remaining <= 0.0 and _tick_elapsed > 0.0):
step = _tick_elapsed
_tick_elapsed = 0.0
if credited_to_player and target.has_method("take_weapon_hit"):
target.take_weapon_hit(tick_damage * step, false, DamageTypes.Type.RADIATION)
else:
target.take_damage(tick_damage * step, false, DamageTypes.Type.RADIATION)
if remaining <= 0.0:
tick_damage = 0.0

View File

@ -1 +0,0 @@
uid://drtwspjwle1py

View File

@ -35,7 +35,6 @@ var shields := 100.0
var energy := 100.0
var energy_regeneration := 5.0
var shield_regeneration := 0.0
var absorption := Vector3.ZERO
var damage_multiplier := 1.0
var experience_multiplier := 1.0
var credits_multiplier := 1.0
@ -70,7 +69,6 @@ func _enter_tree() -> void:
base_max_speed = float(chassis["speed"])
func apply_equipment_bonuses(bonuses: Dictionary) -> void:
absorption = DamageTypes.capped(Vector3(float(bonuses.get("kinetic_absorption", 0.0)), float(bonuses.get("energy_absorption", 0.0)), float(bonuses.get("radiation_absorption", 0.0))))
max_hull = (base_max_hull + float(bonuses.get("max_hull", 0.0))) * (1.0 + float(bonuses.get("hull_percent", 0.0)) / 100.0)
max_shields = (base_max_shields + float(bonuses.get("max_shields", 0.0))) * (1.0 + float(bonuses.get("shield_percent", 0.0)) / 100.0)
scanner_range = base_scanner_range + float(bonuses.get("scanner_range", 0.0))
@ -136,10 +134,10 @@ func _ensure_input_action(action_name: StringName, physical_key: Key) -> void:
if not InputMap.action_has_event(action_name, event):
InputMap.action_add_event(action_name, event)
func take_damage(amount: float, _critical: bool = false, damage_type: int = DamageTypes.Type.KINETIC) -> void:
func take_damage(amount: float) -> void:
if _is_destroyed or is_docked or amount <= 0.0:
return
var hit := COMBAT_ROLLS.incoming_hit(amount * (1.0 - absorption[damage_type] / 100.0), get_evasion_chance(), get_damage_reduction())
var hit := COMBAT_ROLLS.incoming_hit(amount, get_evasion_chance(), get_damage_reduction())
if hit["evaded"]:
var flash := Node2D.new()
flash.set_script(COMBAT_FLASH)

View File

@ -2,7 +2,6 @@ extends RefCounted
class_name WeaponAudio
const SOUND_PATHS := {
ItemDefinition.WeaponKind.GAMMA: "res://assets/sounds/effects/plasma.mp3",
ItemDefinition.WeaponKind.PLASMA: "res://assets/sounds/effects/plasma.mp3",
ItemDefinition.WeaponKind.MACHINE_GUN: "res://assets/sounds/effects/machine_gun.mp3",
ItemDefinition.WeaponKind.LASER: "res://assets/sounds/effects/laser.mp3",
@ -12,7 +11,6 @@ const SOUND_PATHS := {
static var _streams: Dictionary = {}
const BUS_NAMES := {
ItemDefinition.WeaponKind.GAMMA: &"Plasma",
ItemDefinition.WeaponKind.PLASMA: &"Plasma",
ItemDefinition.WeaponKind.MACHINE_GUN: &"MachineGun",
ItemDefinition.WeaponKind.LASER: &"Laser",

View File

@ -205,8 +205,6 @@ func _muzzle_position() -> Vector2:
func _consume_shot_resource(item: ItemInstance) -> bool:
if item.definition.weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN:
return inventory.consume_ammunition()
if item.definition.weapon_kind == ItemDefinition.WeaponKind.GAMMA:
return inventory.consume_irradiated_ammunition()
return _consume_energy(item.get_energy_cost())
func _consume_energy(amount: float) -> bool:
@ -218,7 +216,7 @@ func _consume_energy(amount: float) -> bool:
func _apply_damage(target: Variant, damage: float, critical: bool = false) -> void:
if target is Node and target.has_method("take_weapon_hit"):
target.call("take_weapon_hit", damage, critical, DamageTypes.Type.ENERGY)
target.call("take_weapon_hit", damage, critical)
elif target is Node and target.has_method("take_damage"):
target.call("take_damage", damage)

View File

@ -4,7 +4,6 @@ const IMPACT_EFFECT_SCENE := preload("res://scenes/game/impact_effect.tscn")
const PLASMA_TEXTURE := preload("res://assets/other/bullet.png")
const PLASMA_VISUAL_WIDTH := 24.0
var enemy_shot := false
var direction := Vector2.UP
var projectile_speed := 600.0
var damage := 1.0
@ -125,9 +124,6 @@ func _physics_process(delta: float) -> void:
queue_free()
func _process(delta: float) -> void:
if weapon_kind == ItemDefinition.WeaponKind.GAMMA:
_engine_time += delta
queue_redraw()
if weapon_kind == ItemDefinition.WeaponKind.MISSILE:
_engine_time += delta
_smoke_emission_timer += delta
@ -161,8 +157,6 @@ func _update_smoke_puffs(delta: float) -> void:
func _find_enemy_in_acquisition_range() -> Node2D:
var nearest_enemy: Node2D
var nearest_distance_squared := target_acquisition_range * target_acquisition_range
if enemy_shot:
return null
for candidate in get_tree().get_nodes_in_group("enemies"):
if not candidate is Node2D or candidate.is_queued_for_deletion():
continue
@ -176,15 +170,6 @@ func _draw() -> void:
if weapon_kind == ItemDefinition.WeaponKind.MISSILE:
_draw_missile_smoke()
match weapon_kind:
ItemDefinition.WeaponKind.GAMMA:
var radius := 9.0 + sin(_engine_time * 9.0) * 2.0
draw_circle(Vector2.ZERO, radius * 1.8, Color(0.3, 1.0, 0.1, 0.15))
draw_circle(Vector2.ZERO, radius, Color(0.35, 0.95, 0.15, 0.8))
draw_circle(Vector2.ZERO, radius * 0.5, Color(0.86, 1.0, 0.6))
for index in 8:
var angle := _engine_time * 5.0 + index * TAU / 8.0
var orbit := Vector2(cos(angle), sin(angle) * 0.65) * (radius + 6.0)
draw_circle(orbit, 2.0, Color(0.7, 1.0, 0.4))
ItemDefinition.WeaponKind.PLASMA:
var texture_size := PLASMA_TEXTURE.get_size() * (PLASMA_VISUAL_WIDTH / PLASMA_TEXTURE.get_width())
# The source image points left; projectiles travel along their local +X.
@ -264,9 +249,6 @@ func _spawn_impact_effect(is_explosion: bool) -> void:
effect.global_position = global_position
get_parent().add_child(effect)
var effect_color := Color(1.0, 0.48, 0.22) if is_explosion else Color(1.0, 0.78, 0.38)
if weapon_kind == ItemDefinition.WeaponKind.GAMMA:
effect_color = Color(0.4, 1.0, 0.15)
is_explosion = true
effect.call("configure", is_explosion, effect_color)
func _explode_at_target(impact_target: Node) -> void:
@ -276,7 +258,7 @@ func _explode_at_target(impact_target: Node) -> void:
var query := PhysicsShapeQueryParameters2D.new()
query.shape = explosion_shape
query.transform = Transform2D(0.0, global_position)
query.collision_mask = 2 if enemy_shot else 4
query.collision_mask = 4 # Enemy physics layer 3.
for result in get_world_2d().direct_space_state.intersect_shape(query, 32):
_apply_damage_once(result.get("collider"))
@ -288,11 +270,6 @@ func _apply_damage_once(target: Variant) -> void:
return
_already_hit[instance_id] = true
if target.has_method("take_weapon_hit"):
target.call("take_weapon_hit", damage, is_critical, DamageTypes.for_weapon(weapon_kind))
target.call("take_weapon_hit", damage, is_critical)
elif target.has_method("take_damage"):
if target is CharacterBody2D:
target.call("take_damage", damage, is_critical, DamageTypes.for_weapon(weapon_kind))
else:
target.call("take_damage", damage)
if weapon_kind == ItemDefinition.WeaponKind.GAMMA:
preload("res://scripts/game/radiation_status.gd").apply(target, damage * 0.25, not enemy_shot)
target.call("take_damage", damage)

View File

@ -2,10 +2,7 @@ extends Resource
class_name ItemDefinition
enum Category { WEAPON, SHIELD, ARMOR, REACTOR, ENGINE, ACTIVE_EXTENSION, COMPUTER }
enum WeaponKind { PLASMA, MACHINE_GUN, LASER, MISSILE, GAMMA }
func get_damage_type() -> int:
return DamageTypes.for_weapon(weapon_kind)
enum WeaponKind { PLASMA, MACHINE_GUN, LASER, MISSILE }
@export var item_id: String = ""
@export var display_name: String = ""

View File

@ -1,7 +1,6 @@
extends RefCounted
const DEFINITIONS := [
preload("res://resources/items/gamma_cannon.tres"),
preload("res://resources/items/plasma_cannon.tres"),
preload("res://resources/items/beam_laser.tres"),
preload("res://resources/items/machine_gun.tres"),

View File

@ -55,7 +55,7 @@ func roll_damage() -> float:
func get_energy_cost() -> float:
if definition == null or definition.category != ItemDefinition.Category.WEAPON:
return 0.0
if definition.weapon_kind in [ItemDefinition.WeaponKind.MACHINE_GUN, ItemDefinition.WeaponKind.MISSILE, ItemDefinition.WeaponKind.GAMMA]:
if definition.weapon_kind in [ItemDefinition.WeaponKind.MACHINE_GUN, ItemDefinition.WeaponKind.MISSILE]:
return 0.0
# Compare this weapon with its starter damage. Captain/global gear buffs are free.
var base_lower := roundf((definition.damage_lower_roll_min + definition.damage_lower_roll_max) * 0.5)
@ -88,8 +88,7 @@ func get_addon_count() -> int:
return count
func get_background_color() -> Color:
# No addons and one addon are black; the strongest tier is burgundy.
return [Color("080b10"), Color("080b10"), Color("603e28"), Color("285b91"), Color("8050b8"), Color("70243c")][clampi(get_addon_count(), 0, 5)]
return [Color("101925"), Color("414b58"), Color("65432d"), Color("244c79"), Color("796025")][mini(get_addon_count(), 4)]
func get_type_color() -> Color:
if definition == null:
@ -113,12 +112,9 @@ func get_tooltip_text() -> String:
if definition.energy_regeneration_bonus > 0.0:
lines.append("Regeneracja generatora: +%.1f /s" % ((energy_regeneration_override if energy_regeneration_override >= 0.0 else definition.energy_regeneration_bonus) * ENERGY_REGENERATION_BONUS_MULTIPLIER))
if definition.category == ItemDefinition.Category.WEAPON:
lines.append("Typ obrażeń: %s" % DamageTypes.NAMES[definition.get_damage_type()])
lines.append("Każdy strzał / impuls losuje obrażenia z tego zakresu.")
if definition.weapon_kind == ItemDefinition.WeaponKind.MACHINE_GUN:
lines.append("Zasięg: %.0f px • Amunicja: 1 / strzał" % definition.weapon_range)
elif definition.weapon_kind == ItemDefinition.WeaponKind.GAMMA:
lines.append("Zasięg: %.0f px • Napromieniowana amunicja: 1 / strzał" % definition.weapon_range)
elif definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE:
lines.append("Amunicja: 1 rakieta / strzał • Cooldown bazowy: 5 s • Bez kosztu energii")
else:
@ -133,4 +129,4 @@ func get_tooltip_text() -> String:
return "\n".join(lines)
static func stat_name(key: String) -> String:
return str({"kinetic_absorption": "Absorpcja kinetyczna", "energy_absorption": "Absorpcja energetyczna", "radiation_absorption": "Absorpcja radiacyjna", "missile_cooldown_reduction": "Skrócenie cooldownu rakiet", "scanner_range": "Zasięg skanera", "shield_on_damage_percent": "Odzysk tarcz z obrażeń", "damage": "Obrażenia", "max_hull": "Kadłub", "max_shields": "Tarcze", "max_energy": "Energia", "max_speed": "Prędkość", "energy_regeneration": "Regeneracja energii", "shield_regeneration": "Regeneracja tarcz", "hull_percent": "Kadłub", "shield_percent": "Tarcze", "damage_percent": "Obrażenia broni", "speed_percent": "Prędkość statku", "experience_percent": "Doświadczenie (EXP)", "credits_percent": "Kredyty", "critical_chance_percent": "Szansa na trafienie krytyczne", "critical_damage_percent": "Obrażenia krytyczne"}.get(key, key))
return str({"missile_cooldown_reduction": "Skrócenie cooldownu rakiet", "scanner_range": "Zasięg skanera", "shield_on_damage_percent": "Odzysk tarcz z obrażeń", "damage": "Obrażenia", "max_hull": "Kadłub", "max_shields": "Tarcze", "max_energy": "Energia", "max_speed": "Prędkość", "energy_regeneration": "Regeneracja energii", "shield_regeneration": "Regeneracja tarcz", "hull_percent": "Kadłub", "shield_percent": "Tarcze", "damage_percent": "Obrażenia broni", "speed_percent": "Prędkość statku", "experience_percent": "Doświadczenie (EXP)", "credits_percent": "Kredyty", "critical_chance_percent": "Szansa na trafienie krytyczne", "critical_damage_percent": "Obrażenia krytyczne"}.get(key, key))

View File

@ -20,8 +20,6 @@ const AMMUNITION_CRAFT_PARTS_COST := 50.0
const AMMUNITION_BUY_AMOUNT := 100
const AMMUNITION_BUY_BASE_COST := 20
var irradiated_ammunition := 0
var ammunition: int = STARTING_AMMUNITION
var missile_ammunition: int = STARTING_MISSILE_AMMUNITION
@ -47,7 +45,6 @@ func _ready() -> void:
_restore_modules(GameState.restart_inventory)
ammunition = int(GameState.restart_inventory.get("ammunition", STARTING_AMMUNITION))
missile_ammunition = int(GameState.restart_inventory.get("missile_ammunition", STARTING_MISSILE_AMMUNITION))
irradiated_ammunition = int(GameState.restart_inventory.get("irradiated_ammunition", 0))
GameState.restart_inventory.clear()
_remove_extension_addons()
_recalculate_ship_stats()
@ -67,7 +64,6 @@ func preserve_for_restart() -> void:
"weapon": weapon_slots.duplicate(),
"module": module_slots.duplicate(),
"extension": extension_slots.duplicate(),
"irradiated_ammunition": irradiated_ammunition,
"ammunition": ammunition,
"missile_ammunition": missile_ammunition,
}
@ -235,7 +231,7 @@ func _recalculate_ship_stats() -> void:
for item in weapon_slots + module_slots:
if item == null or item.definition == null:
continue
for stat_key in ["kinetic_absorption", "energy_absorption", "radiation_absorption", "shield_on_damage_percent", "scanner_range", "max_hull", "max_shields", "max_energy", "max_speed", "energy_regeneration", "shield_regeneration", "hull_percent", "shield_percent", "damage_percent", "speed_percent", "experience_percent", "credits_percent", "critical_chance_percent", "critical_damage_percent"]:
for stat_key in ["shield_on_damage_percent", "scanner_range", "max_hull", "max_shields", "max_energy", "max_speed", "energy_regeneration", "shield_regeneration", "hull_percent", "shield_percent", "damage_percent", "speed_percent", "experience_percent", "credits_percent", "critical_chance_percent", "critical_damage_percent"]:
bonuses[stat_key] = float(bonuses.get(stat_key, 0.0)) + item.get_stat_total(stat_key)
ship.call("apply_equipment_bonuses", bonuses)
@ -292,7 +288,7 @@ func _comparison_bonuses(incoming: ItemInstance = null, outgoing: ItemInstance =
return bonuses
func _add_comparison_bonuses(bonuses: Dictionary, item: ItemInstance) -> void:
for key in ["kinetic_absorption", "energy_absorption", "radiation_absorption", "max_hull", "max_shields", "max_energy", "max_speed", "scanner_range", "hull_percent", "shield_percent", "speed_percent", "damage_percent", "critical_damage_percent", "critical_chance_percent", "shield_on_damage_percent", "energy_regeneration", "shield_regeneration", "missile_cooldown_reduction", "experience_percent", "credits_percent"]:
for key in ["max_hull", "max_shields", "max_energy", "max_speed", "scanner_range", "hull_percent", "shield_percent", "speed_percent", "damage_percent", "critical_damage_percent", "critical_chance_percent", "shield_on_damage_percent", "energy_regeneration", "shield_regeneration", "missile_cooldown_reduction", "experience_percent", "credits_percent"]:
bonuses[key] = float(bonuses.get(key, 0.0)) + item.get_stat_total(key)
func _comparison_stats(bonuses: Dictionary) -> Dictionary:
@ -303,12 +299,9 @@ func _comparison_stats(bonuses: Dictionary) -> Dictionary:
for data in [["max_energy", "base_max_energy", "Energia", "pkt"], ["scanner_range", "base_scanner_range", "Zasięg skanera", "px"], ["energy_regeneration", "base_energy_regeneration", "Regeneracja energii", "pkt/s"], ["shield_regeneration", "base_shield_regeneration", "Regeneracja tarcz", "pkt/s"]]:
stats[data[0]] = {"value": float(ship.get(data[1])) + float(bonuses.get(data[0], 0.0)), "name": data[2], "unit": data[3]}
# These stats are bonuses in their own right, not rewards or hit scenarios.
for data in [["kinetic_absorption", "Absorpcja kinetyczna"], ["energy_absorption", "Absorpcja energetyczna"], ["radiation_absorption", "Absorpcja radiacyjna"], ["damage_percent", "Obrażenia broni"], ["critical_damage_percent", "Obrażenia krytyczne"], ["critical_chance_percent", "Szansa na krytyk"], ["shield_on_damage_percent", "Odzysk tarcz z obrażeń"], ["experience_percent", "Doświadczenie"], ["credits_percent", "Kredyty"]]:
for data in [["damage_percent", "Obrażenia broni"], ["critical_damage_percent", "Obrażenia krytyczne"], ["critical_chance_percent", "Szansa na krytyk"], ["shield_on_damage_percent", "Odzysk tarcz z obrażeń"], ["experience_percent", "Doświadczenie"], ["credits_percent", "Kredyty"]]:
stats[data[0]] = {"value": float(bonuses.get(data[0], 0.0)), "name": data[1], "unit": "%"}
stats["missile_cooldown_reduction"] = {"value": float(bonuses.get("missile_cooldown_reduction", 0.0)), "name": "Skrócenie cooldownu rakiet", "unit": "s"}
var absorption_values := DamageTypes.capped(Vector3(float(bonuses.get("kinetic_absorption", 0.0)), float(bonuses.get("energy_absorption", 0.0)), float(bonuses.get("radiation_absorption", 0.0))))
for index in 3:
stats[DamageTypes.KEYS[index]]["value"] = absorption_values[index]
return stats
func get_weapon_comparison(item: ItemInstance, slot_index: int) -> Dictionary:
@ -341,45 +334,3 @@ func _weapon_dps(item: ItemInstance, bonuses: Dictionary) -> float:
if item.definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE:
interval = maxf(1.0, 5.0 - float(bonuses.get("missile_cooldown_reduction", 0.0)))
return item.get_stat_total("damage") * damage_factor * (1.0 + chance * (critical_factor - 1.0)) / maxf(0.001, interval)
func consume_irradiated_ammunition() -> bool:
if irradiated_ammunition <= 0:
return false
irradiated_ammunition -= 1
ammunition_changed.emit()
return true
func can_craft_irradiated_ammunition() -> bool:
return int(GameState.captain.minerals.get("uranium", 0)) >= 1 and GameState.captain.parts >= 10.0
func craft_irradiated_ammunition() -> bool:
if not can_craft_irradiated_ammunition():
return false
GameState.captain.minerals["uranium"] -= 1
GameState.captain.parts -= 10.0
irradiated_ammunition += 10
GameState.captain.emit_changed()
ammunition_changed.emit()
return true
func can_buy_irradiated_ammunition() -> bool:
return GameState.captain.credits >= 15 * maxi(1, GameState.system_level)
func buy_irradiated_ammunition() -> bool:
if not can_buy_irradiated_ammunition():
return false
GameState.captain.credits -= 15 * maxi(1, GameState.system_level)
irradiated_ammunition += 10
GameState.captain.emit_changed()
ammunition_changed.emit()
return true
func get_total_dps() -> float:
var total := 0.0
var bonuses := _comparison_bonuses()
for item in weapon_slots:
var value := _weapon_dps(item, bonuses)
if item != null and item.definition.weapon_kind == ItemDefinition.WeaponKind.GAMMA:
value *= 1.0 + 0.25 * minf(3.0, item.definition.fire_interval)
total += value
return total

View File

@ -22,7 +22,7 @@ var _asteroid_spin := 0.0
func _ready() -> void:
add_to_group("exploration_pois")
_rng.seed = reward_seed
_mineral = ["iron", "aluminium", "titanium", "uranium"][_rng.randi_range(0, 3)]
_mineral = ["iron", "aluminium", "titanium"][_rng.randi_range(0, 2)]
hull = 20.0 * reward_scale()
collision_layer = 4 if kind == "asteroid" else 0
collision_mask = 0
@ -79,7 +79,7 @@ func _update_label(near: bool) -> void:
if kind == "ruin":
_label.text = "?"
elif kind == "asteroid":
_label.text = "Asteroida · %s\nHP: %.0f" % [{"iron": "Żelazo", "aluminium": "Aluminium", "titanium": "Tytan", "uranium": "Uran"}[_mineral], hull]
_label.text = "Asteroida · %s\nHP: %.0f" % [{"iron": "Żelazo", "aluminium": "Aluminium", "titanium": "Tytan"}[_mineral], hull]
else:
_label.text = ("Wrak statku" if kind == "wreck" else "Antyczne znalezisko") + ("\n[E] Przeszukaj" if near else "\nPodleć, aby przeszukać")
@ -91,7 +91,7 @@ func _physics_process(_delta: float) -> void:
if near and (kind == "ruin" or (kind == "wreck" and Input.is_physical_key_pressed(KEY_E))):
claim()
func take_weapon_hit(amount: float, _critical: bool = false, _damage_type: int = 0) -> void:
func take_weapon_hit(amount: float, _critical: bool = false) -> void:
take_damage(amount)
func take_damage(amount: float) -> void:
@ -114,7 +114,7 @@ func claim() -> bool:
if kind == "asteroid":
var amount := maxi(1, roundi(_rng.randi_range(1, 3) * scale))
GameState.captain.minerals[_mineral] = int(GameState.captain.minerals.get(_mineral, 0)) + amount
summary = "+%d %s" % [amount, {"iron": "żelaza", "aluminium": "aluminium", "titanium": "tytanu", "uranium": "uranu"}[_mineral]]
summary = "+%d %s" % [amount, {"iron": "żelaza", "aluminium": "aluminium", "titanium": "tytanu"}[_mineral]]
elif kind == "ruin":
var xp := roundi(_rng.randi_range(10, 20) * scale)
GameState.captain.add_rewards(xp, 0)

View File

@ -1,60 +0,0 @@
extends Control
var _panel: PanelContainer
var _label: Label
var _toggle: Button
var _open := false
var _tween: Tween
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
_panel = PanelContainer.new()
var style := StyleBoxFlat.new()
style.bg_color = Color("0b1520f5")
style.border_color = Color("a54e61")
style.set_border_width_all(1)
style.set_content_margin_all(12)
_panel.add_theme_stylebox_override("panel", style)
add_child(_panel)
_label = Label.new()
_label.add_theme_font_size_override("font_size", 12)
_panel.add_child(_label)
_toggle = Button.new()
_toggle.text = "WRÓG ▾"
preload("res://scripts/ui/hud_button_style.gd").apply(_toggle, Color("ff9aae"))
add_child(_toggle)
_toggle.pressed.connect(_toggle_drawer)
_open = bool(GameState.hud_panel_state.get("enemy_open", false))
get_viewport().size_changed.connect(_layout)
_layout()
func _layout() -> void:
position.x = (get_viewport_rect().size.x - 390.0) * 0.5
_panel.size = Vector2(390, 150)
_panel.position.y = 56.0 if _open else -160.0
_toggle.position = Vector2(130, 12)
_toggle.size = Vector2(130, 36)
func _toggle_drawer() -> void:
_open = not _open
GameState.hud_panel_state["enemy_open"] = _open
_toggle.text = "WRÓG ▴" if _open else "WRÓG ▾"
if _tween != null:
_tween.kill()
_tween = create_tween()
_tween.tween_property(_panel, "position:y", 56.0 if _open else -160.0, 0.25).set_trans(Tween.TRANS_CUBIC)
func _process(_delta: float) -> void:
var enemy: Node = null
for candidate in get_tree().get_nodes_in_group("enemies"):
if not candidate.is_queued_for_deletion() and (enemy == null or int(candidate.loot_tier) < int(enemy.loot_tier)):
enemy = candidate
var profile := DamageTypes.enemy_profile(GameState.system_level, GameState.system_seed)
var kind := int(profile["kind"])
var names := ["Działko plazmowe", "Działko maszynowe", "Laser", "Rakiety", "Działko gamma"]
_label.text = "WRÓG · UKŁAD %d\n%s · %s\n%s" % [GameState.system_level, names[kind], DamageTypes.NAMES[DamageTypes.for_weapon(kind)], DamageTypes.describe(profile["absorption"])]
if enemy != null:
_label.text += "\nDPS: %.1f (%s)\nTarcze: %.0f / %.0f · bez regeneracji" % [enemy.get_weapon_dps(), ["zwykły", "elita", "boss"][enemy.loot_tier], enemy.shields, enemy.max_shields]
else:
_label.text += "\nUkład oczyszczony"

View File

@ -1 +0,0 @@
uid://d0ejbqbvdb2kq

View File

@ -59,7 +59,7 @@ func _ready() -> void:
var binding := Label.new()
binding.mouse_filter = Control.MOUSE_FILTER_IGNORE
binding.text = (["LPM", "PPM", "ŚPM"][slot_index]) if slot_kind == "weapon" else (str(slot_index + 1) if slot_kind == "extension" else ShipInventory.MODULE_NAMES[slot_index])
binding.position = Vector2(2, -24)
binding.position = Vector2(2, -14)
binding.add_theme_font_size_override("font_size", 9)
binding.add_theme_color_override("font_color", Color("a0b2c4"))
# Use a plain overlay to keep the binding outside the slot's content layout.
@ -134,108 +134,42 @@ func _delete_item() -> void:
func _make_custom_tooltip(for_text: String) -> Object:
var panel := PanelContainer.new()
panel.set_script(preload("res://scripts/ui/item_tooltip_frame.gd"))
var background := StyleBoxFlat.new()
background.bg_color = Color("101925")
background.border_color = Color("52677e")
background.set_border_width_all(1)
background.border_width_top = 2
background.set_content_margin_all(12)
background.set_corner_radius_all(3)
background.shadow_size = 0
panel.add_theme_stylebox_override("panel", background)
var label := RichTextLabel.new()
label.fit_content = true
label.scroll_active = false
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
label.custom_minimum_size.x = 290
label.size.x = 290
label.custom_minimum_size.x = 330
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.add_theme_font_size_override("normal_font_size", 11)
label.add_theme_constant_override("line_separation", -2)
label.add_theme_color_override("default_color", Color("c4d2df"))
label.add_theme_font_size_override("normal_font_size", 13)
var item := _inventory.get_item(slot_kind, slot_index)
if item:
# Dark neutral body with an accent tied to the equipment category.
background.bg_color = Color("0c1520").lerp(item.get_background_color().darkened(0.8), 0.25)
background.bg_color = item.get_background_color()
background.border_color = item.get_type_color()
panel.set("accent", item.get_type_color())
var columns := HBoxContainer.new()
columns.add_theme_constant_override("separation", 12)
panel.add_child(columns)
var details := VBoxContainer.new()
details.custom_minimum_size.x = 290
details.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
details.add_theme_constant_override("separation", 8)
columns.add_child(details)
var lines := for_text.split("\n")
if item != null:
var header_frame := PanelContainer.new()
var header_style := StyleBoxFlat.new()
header_style.bg_color = item.get_type_color().darkened(0.8)
header_style.border_color = item.get_type_color().darkened(0.35)
header_style.border_width_left = 3
header_style.set_content_margin_all(8)
header_frame.add_theme_stylebox_override("panel", header_style)
details.add_child(header_frame)
var header := HBoxContainer.new()
header.add_theme_constant_override("separation", 10)
header_frame.add_child(header)
if item.definition.icon != null:
var icon := TextureRect.new()
icon.texture = item.definition.icon
icon.custom_minimum_size = Vector2(54, 54)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
header.add_child(icon)
var heading := VBoxContainer.new()
heading.size_flags_horizontal = Control.SIZE_EXPAND_FILL
heading.add_theme_constant_override("separation", 2)
header.add_child(heading)
var title := _comparison_label(item.definition.display_name, 0, Color("f1f4f8"))
title.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
title.add_theme_font_size_override("font_size", 13)
heading.add_child(title)
heading.add_child(_comparison_label("%s · POZ. %d" % [item.get_rarity_name(), item.item_level], 0, item.get_type_color().lightened(0.25)))
heading.add_child(_comparison_label("%s · ADDONY: %d/5" % [_category_name(item.definition.category), item.get_addon_count()], 0, Color("8ba2b7")))
lines = lines.slice(2)
while not lines.is_empty() and lines[0].is_empty():
lines.remove_at(0)
details.add_child(label)
for line in lines:
if line == "CECHA GŁÓWNA" or line.contains("ADDONY (aktywne"):
label.push_font_size(10)
label.push_color(Color("91dbea"))
label.add_text("PARAMETRY\n" if line == "CECHA GŁÓWNA" else "ADDONY · PO ZAMONTOWANIU\n")
label.pop()
label.pop()
continue
label.add_theme_color_override("default_color", Color("f1f4f8"))
for line in for_text.split("\n"):
var quality := 0
var is_addon := false
if item:
for modifier in item.additional_modifiers:
if modifier != null and line == modifier.get_display_text():
is_addon = true
quality = modifier.get_roll_quality(item.item_level)
break
var color := Color("ffd166") if quality == 2 else (Color("79e6a0") if quality == 1 else Color("c4d2df"))
if line.begins_with("Obrażenia:"):
label.push_font_size(13)
color = Color("ffd166")
if line.begins_with("Typ obrażeń:"):
color = [Color("efb568"), Color("91dbea"), Color("a9ef83")][item.definition.get_damage_type()] if item != null else Color("e6f2fc")
if line.begins_with("Każdy strzał"):
color = Color("8298ad")
label.push_color(color)
label.add_text(("★ " if quality == 2 else ("• " if is_addon else "")) + line + "\n")
label.pop()
if line.begins_with("Obrażenia:"):
if quality > 0:
label.push_color(Color("ffd166") if quality == 2 else Color("79e6a0"))
label.add_text(("★ " if quality == 2 else "") + line + "\n")
if quality > 0:
label.pop()
var columns := HBoxContainer.new()
columns.add_theme_constant_override("separation", 16)
panel.add_child(columns)
columns.add_child(label)
if slot_kind == "cargo" and item != null:
_add_comparison_column(columns, item)
# Wrapped text can become shorter after the first layout pass.
panel.minimum_size_changed.connect(func() -> void: panel.reset_size.call_deferred())
panel.reset_size.call_deferred()
return panel
func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void:
@ -251,18 +185,13 @@ func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void:
comparison.fit_content = true
comparison.scroll_active = false
comparison.mouse_filter = Control.MOUSE_FILTER_IGNORE
comparison.custom_minimum_size.x = 270
comparison.size.x = 270
comparison.custom_minimum_size.x = 290
comparison.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
comparison.add_theme_font_size_override("normal_font_size", 11)
comparison.add_theme_constant_override("line_separation", -2)
comparison.add_theme_color_override("default_color", Color("c4d2df"))
comparison.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
comparison.add_theme_font_size_override("normal_font_size", 13)
comparison.add_theme_color_override("default_color", Color("f1f4f8"))
columns.add_child(comparison)
var equipped := _inventory.get_item("module", module_index)
comparison.push_color(Color("91dbea"))
comparison.add_text("ZMIANY PO ZAŁOŻENIU\n")
comparison.pop()
comparison.add_text("Slot: %s\n" % ShipInventory.MODULE_NAMES[module_index])
comparison.add_text("Obecnie: %s\n\n" % ("%s • Poz. %d" % [equipped.definition.display_name, equipped.item_level] if equipped != null else "pusty"))
comparison.add_text("Zmiana statystyk statku:\n")
@ -274,17 +203,13 @@ func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void:
var name: String = row["name"]
comparison.add_text("%s: %+.2f %s\n" % [name, row["delta"], row["unit"]])
comparison.pop()
comparison.push_color(Color("8298ad"))
comparison.push_font_size(10)
comparison.add_text("\nUwzględnia utratę bonusów zdejmowanego przedmiotu.")
comparison.pop()
comparison.pop()
func _comparison_label(text_value: String, width: float, color: Color = Color("f1f4f8")) -> Label:
var label := Label.new()
label.text = text_value
label.custom_minimum_size.x = width
label.add_theme_font_size_override("font_size", 10)
label.add_theme_font_size_override("font_size", 11)
label.add_theme_color_override("font_color", color)
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
return label

View File

@ -32,34 +32,32 @@ func _draw() -> void:
closed.append(outline[0])
draw_polyline(closed, Color("376779"), 1.5, true)
draw_line(Vector2(28, 2), Vector2(size.x - 28, 2), CYAN, 2, true)
# Put weapon cards beside the counters rather than stacking three rows.
_text("DPS: %.1f" % _inventory.get_total_dps(), Vector2(20, 81), 172, 12, CYAN)
_text(DamageTypes.describe(player.get("absorption")), Vector2(205, 81), size.x - 225, 11, Color("a9d69a"))
var cell := (size.x - 238) / 3.0
var cell := (size.x - 40) / 3.0
var values := [_inventory.missile_ammunition, _inventory.ammunition, int(game.call("get_remaining_enemies"))]
var captions := ["RAKIETY", "AMUNICJA", "WROGOWIE"]
for index in 3:
var x := 218.0 + index * cell
var x := 20.0 + index * cell
var color := AMBER if index == 0 else (CYAN if index == 1 else Color("ff8989"))
_text(captions[index], Vector2(x, 21), cell, 9, Color("8aa9bb"))
if index == 1:
_text("ZWYKŁE %02d" % _inventory.ammunition, Vector2(x, 35), cell, 11, CYAN)
_text("GAMMA %02d" % _inventory.irradiated_ammunition, Vector2(x, 49), cell, 11, Color("a9ef83"))
else:
_text(str(values[index]).pad_zeros(2), Vector2(x, 45), cell, 21, color)
_text(str(values[index]).pad_zeros(2), Vector2(x, 45), cell, 21, color)
if index < 2:
draw_line(Vector2(x + cell, 14), Vector2(x + cell, 45), Color("2b4354"), 1)
var card := Rect2(20 + index * 60, 12, 52, 52)
var card := Rect2(x + cell * 0.5 - 32, 52, 64, 64)
var slot_index: int = WEAPON_DISPLAY_ORDER[index]
var item := _inventory.weapon_slots[slot_index]
var border := item.get_type_color() if item != null else Color("344657")
draw_style_box(_card_style(border), card)
var binding: String = ["LPM", "PPM", "ŚPM"][slot_index]
_text(binding, card.position + Vector2(0, 12), card.size.x, 9, CYAN)
_text(binding, card.position + Vector2(0, 14), card.size.x, 9, CYAN)
if item != null and item.definition.icon != null:
draw_texture_rect(item.definition.icon, Rect2(card.position + Vector2(9, 16), Vector2(34, 34)), false)
draw_texture_rect(item.definition.icon, Rect2(card.position + Vector2(11, 18), Vector2(42, 42)), false)
else:
_text("—", card.position + Vector2(0, 36), card.size.x, 14, Color("637d90"))
_text("—", card.position + Vector2(0, 40), card.size.x, 14, Color("637d90"))
var ratio := float(player.get(["hull", "shields", "energy"][index])) / maxf(1, float(player.get(["max_hull", "max_shields", "max_energy"][index])))
var gauge := Rect2(x + 5, 121, cell - 10, 3)
draw_rect(gauge, Color("172632"))
gauge.size.x *= clampf(ratio, 0, 1)
draw_rect(gauge, [Color("ea9277"), CYAN, AMBER][index])
func _card_style(color: Color) -> StyleBoxFlat:
var style := StyleBoxFlat.new()

View File

@ -1,30 +0,0 @@
extends RefCounted
static func apply(button: Button, accent: Color = Color("91dbea")) -> void:
button.focus_mode = Control.FOCUS_NONE
button.add_theme_font_size_override("font_size", 12)
button.add_theme_color_override("font_color", accent.lightened(0.25))
button.add_theme_color_override("font_hover_color", Color.WHITE)
button.add_theme_color_override("font_pressed_color", Color.WHITE)
button.add_theme_color_override("font_disabled_color", Color("566b7d"))
button.add_theme_stylebox_override("normal", _style(Color("10212e"), accent.darkened(0.45)))
button.add_theme_stylebox_override("hover", _style(Color("193446"), accent))
button.add_theme_stylebox_override("pressed", _style(accent.darkened(0.72), accent.lightened(0.15)))
button.add_theme_stylebox_override("disabled", _style(Color("101820"), Color("2c3b46")))
button.add_theme_constant_override("outline_size", 0)
static func _style(background: Color, border: Color) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = background
style.border_color = border
style.set_border_width_all(1)
style.border_width_left = 3
style.set_corner_radius_all(7)
style.content_margin_left = 14
style.content_margin_right = 14
style.content_margin_top = 6
style.content_margin_bottom = 6
style.shadow_color = Color(0, 0, 0, 0.24)
style.shadow_size = 3
style.shadow_offset = Vector2(0, 2)
return style

View File

@ -1 +0,0 @@
uid://c8mg5jsryv3ig

View File

@ -1,71 +0,0 @@
extends PanelContainer
var resource_name := ""
var description := ""
var accent := Color("91dbea")
var icon_texture: Texture2D
var _count: Label
func _ready() -> void:
custom_minimum_size = Vector2(86, 44)
size_flags_horizontal = Control.SIZE_EXPAND_FILL
var style := _style(false)
add_theme_stylebox_override("panel", style)
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 5)
add_child(row)
var icon := TextureRect.new()
icon.texture = icon_texture
icon.custom_minimum_size = Vector2(32, 32)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
row.add_child(icon)
_count = Label.new()
_count.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_count.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
_count.add_theme_font_size_override("font_size", 11)
_count.add_theme_color_override("font_color", accent.lightened(0.2))
_count.mouse_filter = Control.MOUSE_FILTER_IGNORE
row.add_child(_count)
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
mouse_entered.connect(func() -> void: add_theme_stylebox_override("panel", _style(true)))
mouse_exited.connect(func() -> void: add_theme_stylebox_override("panel", _style(false)))
set_amount(0)
func _style(hovered: bool) -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color("172737") if hovered else Color("0c1723")
style.border_color = accent if hovered else accent.darkened(0.65)
style.set_border_width_all(1)
style.set_corner_radius_all(4)
style.content_margin_left = 6
style.content_margin_right = 6
style.content_margin_top = 5
style.content_margin_bottom = 5
return style
func set_amount(amount: float) -> void:
if _count == null:
return
_count.text = str(roundi(amount)) if is_equal_approx(amount, roundf(amount)) else "%.1f" % amount
tooltip_text = "%s\n%s\nZapas: %s" % [resource_name, description, _count.text]
func _make_custom_tooltip(_text: String) -> Object:
var panel := PanelContainer.new()
panel.add_theme_stylebox_override("panel", _style(true))
var contents := VBoxContainer.new()
contents.add_theme_constant_override("separation", 4)
panel.add_child(contents)
var title := Label.new()
title.text = resource_name
title.add_theme_font_size_override("font_size", 12)
title.add_theme_color_override("font_color", accent)
contents.add_child(title)
var body := Label.new()
body.text = description + "\nZapas: " + _count.text
body.custom_minimum_size.x = 240
body.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
body.add_theme_font_size_override("font_size", 10)
contents.add_child(body)
return panel

View File

@ -1 +0,0 @@
uid://c7o65mkccm8vf

View File

@ -1,30 +0,0 @@
extends PanelContainer
var accent := Color("91dbea")
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
z_index = 1
resized.connect(queue_redraw)
minimum_size_changed.connect(func() -> void: _fit_popup.call_deferred())
_fit_popup.call_deferred()
func _fit_popup() -> void:
reset_size()
var shell := get_parent()
if shell is Control and shell.get_parent() is Window:
shell.reset_size()
shell = shell.get_parent()
if shell is Window:
# The engine popup otherwise retains its first, unwrapped text height.
shell.size = Vector2i(get_combined_minimum_size().ceil()) + Vector2i(4, 4)
func _draw() -> void:
# Crisp etched corner brackets and a top accent; no blurred shadow.
var inset := 5.0
var length := 12.0
for corner in [Vector2(inset, inset), Vector2(size.x - inset, inset), Vector2(inset, size.y - inset), Vector2(size.x - inset, size.y - inset)]:
var inward := Vector2(1 if corner.x < size.x * 0.5 else -1, 1 if corner.y < size.y * 0.5 else -1)
draw_line(corner, corner + Vector2(inward.x * length, 0), accent, 1.5, true)
draw_line(corner, corner + Vector2(0, inward.y * length), accent, 1.5, true)
draw_line(Vector2(22, 1), Vector2(minf(size.x - 22, 112), 1), accent, 2, true)

View File

@ -1 +0,0 @@
uid://dtfbtajrculf0

View File

@ -14,16 +14,7 @@ var planet: Node2D
var _open_tween: Tween
var _missile_button: Button
var _gamma_buy: Button
func _ready() -> void:
_gamma_buy = Button.new()
_gamma_buy.name = "BuyIrradiatedAmmunition"
_gamma_buy.custom_minimum_size.y = 36
_gamma_buy.add_theme_font_size_override("font_size", 12)
_gamma_buy.focus_mode = Control.FOCUS_NONE
$Center/Panel/Contents.add_child(_gamma_buy)
_gamma_buy.pressed.connect(func() -> void: _inventory.buy_irradiated_ammunition())
next_button.pressed.connect(func() -> void: next_level_requested.emit())
buy_ammunition_button.pressed.connect(_buy_ammunition)
reset_skills_button.pressed.connect(_reset_skills)
@ -84,9 +75,6 @@ func _position_at_planet() -> void:
position = Vector2(clampf(target.x, 8.0, maxf(8.0, screen_right - size.x)), clampf(target.y, 8.0, maxf(8.0, viewport_size.y - size.y - 150.0)))
func _refresh_ammunition_service() -> void:
if _gamma_buy != null:
_gamma_buy.text = "KUP 10 NAPROMIENIOWANEJ — %d KREDYTÓW" % (15 * maxi(1, GameState.system_level))
_gamma_buy.disabled = _inventory == null or not _inventory.can_buy_irradiated_ammunition()
_missile_button.disabled = _inventory == null or not _inventory.can_buy_missile()
_missile_button.text = "KUP +1 RAKIETĘ — %d KREDYTÓW" % (_inventory.get_missile_buy_cost() if _inventory else ShipInventory.MISSILE_BUY_BASE_COST)
reset_skills_button.disabled = not GameState.captain.can_reset_skills()

View File

@ -7,7 +7,6 @@ var _details: VBoxContainer
var _parts: Label
var _stock: Label
var _ammo: Button
var _gamma: Button
var _missile: Button
func _ready() -> void:
@ -22,7 +21,7 @@ func _ready() -> void:
_toggle.text = "TWÓRZ ▾"
_toggle.custom_minimum_size.y = 28
_toggle.add_theme_font_size_override("font_size", 12)
preload("res://scripts/ui/hud_button_style.gd").apply(_toggle, Color("efb568"))
_toggle.focus_mode = Control.FOCUS_NONE
_toggle.pressed.connect(_toggle_expanded)
contents.add_child(_toggle)
_details = VBoxContainer.new()
@ -37,8 +36,6 @@ func _ready() -> void:
_missile = _button("CraftMissile", "+1 RAKIETA · %.0f CZĘŚCI" % ShipInventory.MISSILE_CRAFT_PARTS_COST)
_missile.tooltip_text = "Tworzy rakietę do dowolnej wyrzutni."
_missile.pressed.connect(func() -> void: _inventory.craft_missile())
_gamma = _button("CraftGamma", "+10 NAPROMIENIOWANEJ · 1 URAN + 10 CZĘŚCI")
_gamma.pressed.connect(func() -> void: _inventory.craft_irradiated_ammunition())
_details.hide()
if bool(GameState.hud_panel_state.get("crafting_expanded", false)):
_toggle_expanded()
@ -73,6 +70,4 @@ func _refresh() -> void:
_parts.text = "DOSTĘPNE CZĘŚCI: %.1f" % GameState.captain.parts
_stock.text = "AMUNICJA: %d / RAKIETY: %d" % [_inventory.ammunition, _inventory.missile_ammunition]
_ammo.disabled = not _inventory.can_craft_ammunition()
_gamma.disabled = not _inventory.can_craft_irradiated_ammunition()
_stock.text += "\nNAPROMIENIOWANA: %d · URAN: %d" % [_inventory.irradiated_ammunition, int(GameState.captain.minerals.get("uranium", 0))]
_missile.disabled = not _inventory.can_craft_missile()

View File

@ -6,18 +6,20 @@ const CARGO_SIZE := 20
@onready var cargo_grid: GridContainer = $Contents/CargoGrid
@onready var close_button: Button = $Contents/Header/CloseButton
@onready var title_label: Label = $Contents/Header/Title
@onready var ammunition_label: Label = $Contents/Ammunition/Count
var _inventory: ShipInventory
var _drawer_open := false
var _slide_tween: Tween
var _resource_cards: Dictionary = {}
var _minerals_label: Label
var drawer_width := 560.0
func _ready() -> void:
close_button.pressed.connect(close_drawer)
preload("res://scripts/ui/hud_button_style.gd").apply(close_button, Color("91dbea"))
close_button.add_theme_font_size_override("font_size", 10)
_create_resource_section()
_minerals_label = Label.new()
_minerals_label.add_theme_font_size_override("font_size", 10)
$Contents.add_child(_minerals_label)
$Contents.move_child(_minerals_label, $Contents/CargoGrid.get_index())
_inventory = get_tree().get_first_node_in_group("ship_inventory") as ShipInventory
if _inventory:
_inventory.inventory_changed.connect(_refresh_title)
@ -32,75 +34,10 @@ func _ready() -> void:
_refresh_title()
func _refresh_title() -> void:
for key in ["iron", "titanium", "aluminium", "uranium"]:
_resource_cards[key].set_amount(float(GameState.captain.minerals.get(key, 0)))
_resource_cards["parts"].set_amount(GameState.captain.parts)
_minerals_label.text = "SUROWCE · Tytan: %d · Żelazo: %d · Aluminium: %d" % [GameState.captain.minerals.get("titanium", 0), GameState.captain.minerals.get("iron", 0), GameState.captain.minerals.get("aluminium", 0)]
if _inventory:
title_label.text = "MAGAZYN · %d / %d" % [_inventory.get_cargo_used(), CARGO_SIZE]
_resource_cards["ammunition"].set_amount(_inventory.ammunition)
_resource_cards["irradiated_ammunition"].set_amount(_inventory.irradiated_ammunition)
_resource_cards["missile"].set_amount(_inventory.get_missile_count())
func _create_resource_section() -> void:
var frame := PanelContainer.new()
frame.name = "ResourceSection"
var style := StyleBoxFlat.new()
style.bg_color = Color("101e2d")
style.border_color = Color("2d4c60")
style.set_border_width_all(1)
style.set_corner_radius_all(5)
style.set_content_margin_all(8)
frame.add_theme_stylebox_override("panel", style)
$Contents.add_child(frame)
$Contents.move_child(frame, cargo_grid.get_index())
var section := VBoxContainer.new()
section.name = "Resources"
section.add_theme_constant_override("separation", 6)
frame.add_child(section)
var minerals := _resource_row(section, "SUROWCE", Color("d4b58a"))
for data in [
["iron", "Żelazo", "Surowiec pozyskiwany z asteroid.", Color("c5b5a0")],
["titanium", "Tytan", "Surowiec pozyskiwany z asteroid.", Color("9ecbe9")],
["aluminium", "Aluminium", "Surowiec pozyskiwany z asteroid.", Color("dce8ef")],
["uranium", "Uran", "Surowiec z asteroid. Do produkcji napromieniowanej amunicji.", Color("a9ef83")],
["parts", "Części", "Odzyskiwane z przedmiotów i wraków. Do produkcji amunicji i napraw.", Color("efb568")],
]:
_add_resource_card(minerals, data)
var ammunition := _resource_row(section, "AMUNICJA", Color("91dbea"))
for data in [
["ammunition", "Zwykła amunicja", "Pociski do działka maszynowego. Obrażenia kinetyczne.", Color("efb568")],
["irradiated_ammunition", "Napromieniowana amunicja", "Kapsuły do działka gamma. Obrażenia radiacyjne.", Color("a9ef83")],
["missile", "Rakiety", "Amunicja do wyrzutni rakiet. Obrażenia kinetyczne.", Color("ffac8b")],
]:
_add_resource_card(ammunition, data)
var cargo_title := Label.new()
cargo_title.text = "PRZEDMIOTY"
cargo_title.add_theme_font_size_override("font_size", 10)
cargo_title.add_theme_color_override("font_color", Color("91dbea"))
$Contents.add_child(cargo_title)
$Contents.move_child(cargo_title, cargo_grid.get_index())
func _resource_row(section: VBoxContainer, caption: String, color: Color) -> HBoxContainer:
var title := Label.new()
title.text = caption
title.add_theme_font_size_override("font_size", 10)
title.add_theme_color_override("font_color", color)
section.add_child(title)
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 5)
section.add_child(row)
return row
func _add_resource_card(row: HBoxContainer, data: Array) -> void:
var card := PanelContainer.new()
card.set_script(preload("res://scripts/ui/inventory_resource_card.gd"))
card.name = str(data[0]).to_pascal_case()
card.set("resource_name", data[1])
card.set("description", data[2])
card.set("accent", data[3])
card.set("icon_texture", load("res://assets/icons/resources/%s.png" % data[0]))
row.add_child(card)
_resource_cards[data[0]] = card
title_label.text = "MAGAZYN (%d/%d) · Części: %.1f" % [_inventory.get_cargo_used(), CARGO_SIZE, GameState.captain.parts]
ammunition_label.text = "AMUNICJA: %d · RAKIETY: %d" % [_inventory.ammunition, _inventory.get_missile_count()]
func toggle_drawer() -> void:
_set_drawer_open(not _drawer_open)

View File

@ -10,7 +10,7 @@ const BALANCE := preload("res://scripts/captain/captain_balance.gd")
class DamageReceiver:
extends Node2D
var hits: Array[Dictionary] = []
func take_weapon_hit(amount: float, critical: bool, _damage_type: int = 0) -> void:
func take_weapon_hit(amount: float, critical: bool) -> void:
hits.append({"damage": amount, "critical": critical})
func _ready() -> void:

View File

@ -4,15 +4,12 @@ const GENERATOR := preload("res://scripts/items/item_generator.gd")
const DATABASE := preload("res://resources/items/affix_database.tres")
func _ready() -> void:
assert(DATABASE.affixes.size() == 15)
assert(DATABASE.affixes.size() == 10)
for level in [1, 50, 1000]:
for tier in [0, 1, 2]:
for sample in 50:
var item := GENERATOR.generate(level, tier)
assert(item.item_level == level)
if item.definition.category == ItemDefinition.Category.ACTIVE_EXTENSION:
assert(item.additional_modifiers.is_empty())
continue
assert(item.additional_modifiers.size() >= 1 and item.additional_modifiers.size() <= 5)
if tier == 2:
assert(item.additional_modifiers.size() >= 4)
@ -21,9 +18,9 @@ func _ready() -> void:
assert(modifier.affix.can_roll(item.definition, level))
assert(modifier.affix.affix_id not in ids)
ids.append(modifier.affix.affix_id)
var bounds := modifier.affix.get_value_range(level)
assert(modifier.value >= bounds.x - 0.0001)
assert(modifier.value <= bounds.y + 0.0001)
var progression := float(level - 1) / 999.0
assert(modifier.value >= lerpf(modifier.affix.minimum_value_level_1, modifier.affix.minimum_value_level_1000, progression))
assert(modifier.value <= lerpf(modifier.affix.maximum_value_level_1, modifier.affix.maximum_value_level_1000, progression))
assert(item.get_tooltip_text().contains(item.additional_modifiers[0].affix.display_name))
var weapon := ItemDefinition.new()

View File

@ -22,7 +22,7 @@ func _ready() -> void:
enemy.set("_player", ship)
enemy.call("apply_level_settings", 4, 2)
var expected := roundi(BALANCE.KILL_CREDITS[2] * 1.15 * 1.5)
enemy.call("take_damage", 10.0 * (float(enemy.get("max_hull")) + float(enemy.get("max_shields"))))
enemy.call("take_damage", enemy.get("max_hull"))
assert(GameState.captain.credits == 0)
assert(GameState.captain.level > 1 or GameState.captain.experience > 0)
await get_tree().process_frame

View File

@ -1,84 +0,0 @@
extends Node2D
func _ready() -> void:
GameState.start_new_run()
GameState.system_seed = 12345
assert(DamageTypes.for_weapon(ItemDefinition.WeaponKind.MACHINE_GUN) == DamageTypes.Type.KINETIC)
assert(DamageTypes.for_weapon(ItemDefinition.WeaponKind.MISSILE) == DamageTypes.Type.KINETIC)
assert(DamageTypes.for_weapon(ItemDefinition.WeaponKind.PLASMA) == DamageTypes.Type.ENERGY)
assert(DamageTypes.for_weapon(ItemDefinition.WeaponKind.LASER) == DamageTypes.Type.ENERGY)
assert(DamageTypes.for_weapon(ItemDefinition.WeaponKind.GAMMA) == DamageTypes.Type.RADIATION)
var capped := DamageTypes.capped(Vector3(40, 40, 40))
assert(capped.is_equal_approx(Vector3(20, 20, 20)))
for level in [1, 500, 1000]:
var profile := DamageTypes.enemy_profile(level, 12345)
assert(profile == DamageTypes.enemy_profile(level, 12345))
var values: Vector3 = profile["absorption"]
assert(values.x + values.y + values.z <= 60.001)
if level == 1:
assert(is_equal_approx(values.x + values.y + values.z, 2.0))
var ship := preload("res://scenes/game/player_ship.tscn").instantiate()
add_child(ship)
ship.set_physics_process(false)
ship.get_node("WeaponController").set_physics_process(false)
var inventory: ShipInventory = ship.get_node("ShipInventory")
var module := ItemInstance.new()
module.definition = preload("res://resources/items/hull_plating.tres")
var modifier := ItemStatModifier.new()
modifier.stat_key = "kinetic_absorption"
modifier.value = 20.0
module.additional_modifiers.append(modifier)
assert(inventory.add_item(module))
assert(inventory.move_item("cargo", inventory.cargo.find(module), "module", 1))
assert(is_equal_approx(ship.absorption.x, 20.0))
ship.shields = 100.0
ship.take_damage(10.0, false, DamageTypes.Type.KINETIC)
assert(is_equal_approx(ship.shields, 92.0))
assert(inventory.move_item("module", 1, "cargo", inventory.cargo.find(null)))
assert(ship.absorption == Vector3.ZERO)
GameState.captain.parts = 10
GameState.captain.minerals["uranium"] = 1
assert(inventory.craft_irradiated_ammunition())
assert(inventory.irradiated_ammunition == 10)
assert(not inventory.craft_irradiated_ammunition())
assert(inventory.consume_irradiated_ammunition())
GameState.captain.credits = 15
assert(inventory.buy_irradiated_ammunition())
assert(inventory.irradiated_ammunition == 19)
inventory.preserve_for_restart()
assert(GameState.restart_inventory["irradiated_ammunition"] == 19)
var enemy := preload("res://scenes/game/enemy_ship.tscn").instantiate()
enemy.apply_level_settings(1, 0)
add_child(enemy)
enemy.set_physics_process(false)
enemy.absorption = Vector3(20, 0, 0)
enemy.shields = 10
enemy.hull = 100
enemy.take_damage(20, false, DamageTypes.Type.KINETIC)
assert(is_equal_approx(enemy.shields, 0))
assert(is_equal_approx(enemy.hull, 94))
enemy.take_damage(10, false, DamageTypes.Type.ENERGY)
assert(is_equal_approx(enemy.hull, 84))
var projectile := preload("res://scenes/game/weapon_projectile.tscn").instantiate()
projectile.weapon_kind = ItemDefinition.WeaponKind.GAMMA
projectile.damage = 8
add_child(projectile)
projectile.set_physics_process(false)
projectile._apply_damage_once(enemy)
assert(is_equal_approx(enemy.hull, 76))
var status: Node = enemy.get_node("RadiationStatus")
status.set_physics_process(false)
for index in 6:
status._physics_process(0.5)
assert(is_equal_approx(enemy.hull, 70))
assert(is_equal_approx(status.remaining, 0))
assert(enemy.get_node("Sprite2D").modulate == Color.WHITE)
for kind in 5:
enemy.weapon_kind = kind
enemy._fire_plasma(Vector2.RIGHT)
for key in ["kinetic", "energy", "radiation"]:
var affix: ItemAffixDefinition = load("res://resources/items/affixes/%s_absorption.tres" % key)
assert(is_equal_approx(affix.roll_value(1), 1.0))
assert(preload("res://resources/items/gamma_cannon.tres") in preload("res://scripts/items/item_generator.gd").DEFINITIONS)
print("DAMAGE_TYPES_TEST: PASS (types, caps, profiles, crafting, purchase, persistence, shield overflow, radiation duration)")
get_tree().quit()

View File

@ -1 +0,0 @@
uid://b2tol3o50xrk7

View File

@ -1,4 +0,0 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://tests/items/damage_types_test.gd" id="1"]
[node name="DamageTypesTest" type="Node2D"]
script = ExtResource("1")

View File

@ -1,32 +0,0 @@
extends Node2D
func _ready() -> void:
GameState.start_new_run()
seed(56789)
var ship := preload("res://scenes/game/player_ship.tscn").instantiate()
add_child(ship)
ship.set_physics_process(false)
ship.get_node("WeaponController").set_physics_process(false)
ship.shields = 1000.0
ship.max_shields = 1000.0
var enemy := preload("res://scenes/game/enemy_ship.tscn").instantiate()
enemy.position = Vector2(400, 0)
add_child(enemy)
enemy.set_physics_process(false)
enemy.set("_player", ship)
enemy.weapon_kind = ItemDefinition.WeaponKind.LASER
enemy.weapon_damage = 1.0
enemy.weapon_cooldown = 1.5
assert(is_equal_approx(enemy.get_fire_interval(), 3.0))
assert(is_equal_approx(enemy.get_weapon_dps(), 1.0 / 3.0))
enemy.weapon_cooldown = 0.275
assert(enemy.get_fire_interval() >= 1.4)
await get_tree().physics_frame
await get_tree().physics_frame
for index in 100:
enemy._fire_plasma(Vector2.LEFT)
var hits := 1000.0 - float(ship.shields)
assert(hits >= 45.0 and hits <= 85.0)
print("ENEMY_LASER_TEST: PASS (slower cooldown, minimum interval, displayed DPS, %d real hits / 100 shots)" % roundi(hits))
await get_tree().create_timer(0.15).timeout
get_tree().quit()

View File

@ -1 +0,0 @@
uid://ca6c7s2fwiptt

View File

@ -1,4 +0,0 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://tests/items/enemy_laser_test.gd" id="1"]
[node name="EnemyLaserTest" type="Node2D"]
script = ExtResource("1")

View File

@ -49,7 +49,6 @@ func _ready() -> void:
enemy.set("_player", ship)
enemy.set("max_hull", 10000.0)
enemy.set("hull", 10000.0)
enemy.set("shields", 0.0)
var controller := AimedGun.new()
ship.add_child(controller)
await get_tree().physics_frame

View File

@ -73,5 +73,4 @@ func _enemy(ship: CharacterBody2D, hp: float) -> CharacterBody2D:
enemy.set_physics_process(false)
enemy.set("_player", ship)
enemy.set("hull", hp)
enemy.set("shields", 0.0)
return enemy

View File

@ -32,7 +32,7 @@ func _ready() -> void:
addon.stat_key = "damage_percent"
addon.value = 10.0
stronger.additional_modifiers.append(addon)
if definition.weapon_kind in [ItemDefinition.WeaponKind.MACHINE_GUN, ItemDefinition.WeaponKind.MISSILE, ItemDefinition.WeaponKind.GAMMA]:
if definition.weapon_kind in [ItemDefinition.WeaponKind.MACHINE_GUN, ItemDefinition.WeaponKind.MISSILE]:
assert(stronger.get_energy_cost() == 0.0)
else:
assert(stronger.get_energy_cost() > cost_before_addon)

View File

@ -5,7 +5,6 @@ func _ready() -> void:
GameState.start_new_run()
GameState.system_seed = 314159
var game := GAME.instantiate()
assert(game.BOSS_SPAWN_DISTANCE >= 1800.0)
add_child(game)
game.set_physics_process(false)
var player: CharacterBody2D = game.get_node("PlayerShip")

View File

@ -210,9 +210,6 @@ func _assert_boss_and_health(game: Node2D) -> void:
assert(boss.get_node("CollisionShape2D").scale == Vector2(2, 2))
var bar: Node2D = boss.get_node("HealthBar")
assert(is_equal_approx(bar.get_health_ratio(), 1.0))
# Isolate the hull bar from the new shield and absorption layers.
boss.shields = 0.0
boss.absorption = Vector3.ZERO
boss.take_damage(boss.max_hull * 0.5)
assert(is_equal_approx(bar.get_health_ratio(), 0.5))
boss.rotation = 1.2