diff --git a/assets/Ships/player/bastion.png b/assets/Ships/player/bastion.png new file mode 100644 index 0000000..bf2e5cf Binary files /dev/null and b/assets/Ships/player/bastion.png differ diff --git a/assets/Ships/player/bastion.png.import b/assets/Ships/player/bastion.png.import new file mode 100644 index 0000000..cdf54de --- /dev/null +++ b/assets/Ships/player/bastion.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dvr81d4dwt6t0" +path="res://.godot/imported/bastion.png-15006c633cecef3ba43fa69c64e8c34d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/Ships/player/bastion.png" +dest_files=["res://.godot/imported/bastion.png-15006c633cecef3ba43fa69c64e8c34d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/Ships/player/szerszen.png b/assets/Ships/player/szerszen.png new file mode 100644 index 0000000..da402c9 Binary files /dev/null and b/assets/Ships/player/szerszen.png differ diff --git a/assets/Ships/player/szerszen.png.import b/assets/Ships/player/szerszen.png.import new file mode 100644 index 0000000..a644d7c --- /dev/null +++ b/assets/Ships/player/szerszen.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d3xmihoeil2k6" +path="res://.godot/imported/szerszen.png-e23ede0a372aa7e06ec1d33ee683770d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/Ships/player/szerszen.png" +dest_files=["res://.godot/imported/szerszen.png-e23ede0a372aa7e06ec1d33ee683770d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/Ships/player/wedrowiec.png b/assets/Ships/player/wedrowiec.png new file mode 100644 index 0000000..3d673ee Binary files /dev/null and b/assets/Ships/player/wedrowiec.png differ diff --git a/assets/Ships/player/wedrowiec.png.import b/assets/Ships/player/wedrowiec.png.import new file mode 100644 index 0000000..b472e6c --- /dev/null +++ b/assets/Ships/player/wedrowiec.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://u26qmt4md8wa" +path="res://.godot/imported/wedrowiec.png-c336464cc226bb3133caac3cdec5a157.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/Ships/player/wedrowiec.png" +dest_files=["res://.godot/imported/wedrowiec.png-c336464cc226bb3133caac3cdec5a157.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/Ships/player/zmija.png b/assets/Ships/player/zmija.png new file mode 100644 index 0000000..a97bc7a Binary files /dev/null and b/assets/Ships/player/zmija.png differ diff --git a/assets/Ships/player/zmija.png.import b/assets/Ships/player/zmija.png.import new file mode 100644 index 0000000..9ea1d92 --- /dev/null +++ b/assets/Ships/player/zmija.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvnbaa77y2sim" +path="res://.godot/imported/zmija.png-40bffd0ae89f8efb16dd5e7cf79adc7c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/Ships/player/zmija.png" +dest_files=["res://.godot/imported/zmija.png-40bffd0ae89f8efb16dd5e7cf79adc7c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/icons/afterburner.png b/assets/icons/afterburner.png new file mode 100644 index 0000000..4a16ee8 Binary files /dev/null and b/assets/icons/afterburner.png differ diff --git a/assets/icons/afterburner.png.import b/assets/icons/afterburner.png.import new file mode 100644 index 0000000..1b3ccf6 --- /dev/null +++ b/assets/icons/afterburner.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kukr8bhgqa7p" +path="res://.godot/imported/afterburner.png-95d9a1239ba9614a4ef2d001e06a4092.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/afterburner.png" +dest_files=["res://.godot/imported/afterburner.png-95d9a1239ba9614a4ef2d001e06a4092.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/icons/extensions_prompts.json b/assets/icons/extensions_prompts.json new file mode 100644 index 0000000..e51e9f1 --- /dev/null +++ b/assets/icons/extensions_prompts.json @@ -0,0 +1,25 @@ +{ + "tool": "built-in imagegen", + "assets": [ + { + "id": "afterburner", + "path": "assets/icons/afterburner.png", + "prompt": "Use case: stylized-concept. Asset type: square sci-fi game active ability inventory icon, legible at 42 pixels. Subject: a compact spaceship afterburner module, twin engine boost nozzles with bright blue acceleration trails. Centered in a square, occupying 80% of the canvas. Polished gunmetal and cyan/amber game art, crisp silhouette, consistent three-quarter view, restrained details. Transparent background. No text, UI, border, background, watermark or other items." + }, + { + "id": "shield_charge", + "path": "assets/icons/shield_charge.png", + "prompt": "Use case: stylized-concept. Asset type: square sci-fi game active ability inventory icon, legible at 42 pixels. Subject: an emergency shield recharge capacitor, compact battery bank with a cyan protective hexagonal shield and a small electric arc. Centered in a square, occupying 80% of the canvas. Polished gunmetal and cyan/amber game art, crisp silhouette, consistent three-quarter view, restrained details. Transparent background. No text, UI, border, background, watermark or other items." + }, + { + "id": "repair_drones", + "path": "assets/icons/repair_drones.png", + "prompt": "Use case: stylized-concept. Asset type: square sci-fi game active ability inventory icon, legible at 42 pixels. Subject: two small autonomous spaceship repair drones, articulated manipulator arms, visible welding tool, warm amber welding spark, no hull plating. Centered in a square, occupying 80% of the canvas. Polished gunmetal and cyan/amber game art, crisp silhouette, consistent three-quarter view, restrained details. Transparent background. No text, UI, border, background, watermark or other items." + }, + { + "id": "screwdriver", + "path": "assets/icons/screwdriver.png", + "prompt": "Use case: stylized-concept. Asset type: square sci-fi game salvage action button icon, legible at 16 pixels. Subject: one simple precision screwdriver, amber handle and polished steel shaft, diagonal orientation, bold readable silhouette, no other tools. Centered in a square, occupying 80% of the canvas. Polished gunmetal and cyan/amber game art, crisp silhouette, consistent three-quarter view, restrained details. Transparent background. No text, UI, border, background, watermark or other items." + } + ] +} diff --git a/assets/icons/repair_drones.png b/assets/icons/repair_drones.png new file mode 100644 index 0000000..9f990f3 Binary files /dev/null and b/assets/icons/repair_drones.png differ diff --git a/assets/icons/repair_drones.png.import b/assets/icons/repair_drones.png.import new file mode 100644 index 0000000..5a04ac7 --- /dev/null +++ b/assets/icons/repair_drones.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b1v55dh3vgl78" +path="res://.godot/imported/repair_drones.png-1c06bb8551ddc2b1cecc2eee29b821a1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/repair_drones.png" +dest_files=["res://.godot/imported/repair_drones.png-1c06bb8551ddc2b1cecc2eee29b821a1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/icons/screwdriver.png b/assets/icons/screwdriver.png new file mode 100644 index 0000000..e568f46 Binary files /dev/null and b/assets/icons/screwdriver.png differ diff --git a/assets/icons/screwdriver.png.import b/assets/icons/screwdriver.png.import new file mode 100644 index 0000000..ca9c05a --- /dev/null +++ b/assets/icons/screwdriver.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dclr2ixo322yp" +path="res://.godot/imported/screwdriver.png-7ce78b723732e8b7321d8be8b4729825.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/screwdriver.png" +dest_files=["res://.godot/imported/screwdriver.png-7ce78b723732e8b7321d8be8b4729825.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/icons/shield_charge.png b/assets/icons/shield_charge.png new file mode 100644 index 0000000..509e72b Binary files /dev/null and b/assets/icons/shield_charge.png differ diff --git a/assets/icons/shield_charge.png.import b/assets/icons/shield_charge.png.import new file mode 100644 index 0000000..3167189 --- /dev/null +++ b/assets/icons/shield_charge.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bckqak06f6qxc" +path="res://.godot/imported/shield_charge.png-de5c7d31ad3e9bddb64f261edd5632cb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/shield_charge.png" +dest_files=["res://.godot/imported/shield_charge.png-de5c7d31ad3e9bddb64f261edd5632cb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/wrecks/prompts.json b/assets/wrecks/prompts.json new file mode 100644 index 0000000..5757c1a --- /dev/null +++ b/assets/wrecks/prompts.json @@ -0,0 +1,20 @@ +{ + "tool": "built-in imagegen", + "assets": [ + { + "id": "wreck_frigate", + "path": "assets/wrecks/wreck_frigate.png", + "prompt": "Use case: stylized-concept. Asset type: isolated top-down wreck sprite for a 2D space game. Subject: a broken small space frigate, fractured gunmetal hull, exposed ribs, one torn blue engine nacelle. Single wreck centered in a square, occupying 80% of the canvas. Premium painted realistic sci-fi game art, crisp readable silhouette at 80 pixels, detailed metallic surfaces, neutral upper-left lighting. Transparent background. No text, UI, border, stars, surrounding scenery, outside shadows, explosion or flames." + }, + { + "id": "wreck_freighter", + "path": "assets/wrecks/wreck_freighter.png", + "prompt": "Use case: stylized-concept. Asset type: isolated top-down wreck sprite for a 2D space game. Subject: a destroyed compact cargo spacecraft, crushed angular hull, scattered attached cargo pods, rusted orange panels. Single wreck centered in a square, occupying 80% of the canvas. Premium painted realistic sci-fi game art, crisp readable silhouette at 80 pixels, detailed metallic surfaces, neutral upper-left lighting. Transparent background. No text, UI, border, stars, surrounding scenery, outside shadows, explosion or flames." + }, + { + "id": "wreck_interceptor", + "path": "assets/wrecks/wreck_interceptor.png", + "prompt": "Use case: stylized-concept. Asset type: isolated top-down wreck sprite for a 2D space game. Subject: a ruined sleek interceptor spaceship, snapped wing, burned navy armor, exposed glowing dim cyan wiring. Single wreck centered in a square, occupying 80% of the canvas. Premium painted realistic sci-fi game art, crisp readable silhouette at 80 pixels, detailed metallic surfaces, neutral upper-left lighting. Transparent background. No text, UI, border, stars, surrounding scenery, outside shadows, explosion or flames." + } + ] +} diff --git a/assets/wrecks/wreck_freighter.png b/assets/wrecks/wreck_freighter.png new file mode 100644 index 0000000..65049f7 Binary files /dev/null and b/assets/wrecks/wreck_freighter.png differ diff --git a/assets/wrecks/wreck_freighter.png.import b/assets/wrecks/wreck_freighter.png.import new file mode 100644 index 0000000..a7a987e --- /dev/null +++ b/assets/wrecks/wreck_freighter.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cwx1k1akiuhfm" +path="res://.godot/imported/wreck_freighter.png-db535807eab479ebfbba43be7cd0e2b2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/wrecks/wreck_freighter.png" +dest_files=["res://.godot/imported/wreck_freighter.png-db535807eab479ebfbba43be7cd0e2b2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/wrecks/wreck_frigate.png b/assets/wrecks/wreck_frigate.png new file mode 100644 index 0000000..2078f2e Binary files /dev/null and b/assets/wrecks/wreck_frigate.png differ diff --git a/assets/wrecks/wreck_frigate.png.import b/assets/wrecks/wreck_frigate.png.import new file mode 100644 index 0000000..3d00d20 --- /dev/null +++ b/assets/wrecks/wreck_frigate.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bvj1hdlnec6nj" +path="res://.godot/imported/wreck_frigate.png-c70e885156f2b58c93f9f1a465c39b94.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/wrecks/wreck_frigate.png" +dest_files=["res://.godot/imported/wreck_frigate.png-c70e885156f2b58c93f9f1a465c39b94.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/wrecks/wreck_interceptor.png b/assets/wrecks/wreck_interceptor.png new file mode 100644 index 0000000..e8ddbc9 Binary files /dev/null and b/assets/wrecks/wreck_interceptor.png differ diff --git a/assets/wrecks/wreck_interceptor.png.import b/assets/wrecks/wreck_interceptor.png.import new file mode 100644 index 0000000..527ef3e --- /dev/null +++ b/assets/wrecks/wreck_interceptor.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6hcpbikedl6n" +path="res://.godot/imported/wreck_interceptor.png-db737464df7a4040a1be1a610f183903.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/wrecks/wreck_interceptor.png" +dest_files=["res://.godot/imported/wreck_interceptor.png-db737464df7a4040a1be1a610f183903.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/resources/items/afterburner.tres b/resources/items/afterburner.tres index 2bc9915..6a6dc8f 100644 --- a/resources/items/afterburner.tres +++ b/resources/items/afterburner.tres @@ -3,7 +3,7 @@ [ext_resource type="Script" uid="uid://d2fdtd4wjk7d" path="res://scripts/items/item_definition.gd" id="1"] [ext_resource type="Script" uid="uid://bpaiuqwsxswii" path="res://scripts/abilities/afterburner_ability.gd" id="2"] -[ext_resource type="Texture2D" path="res://assets/icons/engine.png" id="icon"] +[ext_resource type="Texture2D" path="res://assets/icons/afterburner.png" id="icon"] [sub_resource type="Resource" id="Ability"] script = ExtResource("2") diff --git a/resources/items/repair_drones.tres b/resources/items/repair_drones.tres index 8abca17..1dc7b77 100644 --- a/resources/items/repair_drones.tres +++ b/resources/items/repair_drones.tres @@ -3,7 +3,7 @@ [ext_resource type="Script" uid="uid://d2fdtd4wjk7d" path="res://scripts/items/item_definition.gd" id="1"] [ext_resource type="Script" uid="uid://bgofku10gduus" path="res://scripts/abilities/repair_drones_ability.gd" id="2"] -[ext_resource type="Texture2D" path="res://assets/icons/armor.png" id="icon"] +[ext_resource type="Texture2D" path="res://assets/icons/repair_drones.png" id="icon"] [sub_resource type="Resource" id="Ability"] script = ExtResource("2") diff --git a/resources/items/shield_charge.tres b/resources/items/shield_charge.tres index 8defe39..2779dee 100644 --- a/resources/items/shield_charge.tres +++ b/resources/items/shield_charge.tres @@ -3,7 +3,7 @@ [ext_resource type="Script" uid="uid://d2fdtd4wjk7d" path="res://scripts/items/item_definition.gd" id="1"] [ext_resource type="Script" uid="uid://bwka1dwxq64sl" path="res://scripts/abilities/shield_charge_ability.gd" id="2"] -[ext_resource type="Texture2D" path="res://assets/icons/shield.png" id="icon"] +[ext_resource type="Texture2D" path="res://assets/icons/shield_charge.png" id="icon"] [sub_resource type="Resource" id="Ability"] script = ExtResource("2") diff --git a/scenes/game/player_ship.tscn b/scenes/game/player_ship.tscn index 07e0e29..7c5a58a 100644 --- a/scenes/game/player_ship.tscn +++ b/scenes/game/player_ship.tscn @@ -16,7 +16,7 @@ collision_mask = 5 script = ExtResource("1_controller") [node name="Sprite2D" type="Sprite2D" parent="."] -texture_filter = 1 +texture_filter = 2 [node name="ShieldVisual" type="Node2D" parent="."] z_index = 2 @@ -39,3 +39,4 @@ script = ExtResource("5_engine_audio") [node name="EngineVisual" type="Node2D" parent="."] script = ExtResource("6_engine_visual") + diff --git a/scenes/ui/ship_selection.tscn b/scenes/ui/ship_selection.tscn index c1af85e..5ef25a1 100644 --- a/scenes/ui/ship_selection.tscn +++ b/scenes/ui/ship_selection.tscn @@ -36,13 +36,13 @@ theme_override_constants/separation = 14 [node name="Heading" type="Label" parent="Layout"] layout_mode = 2 theme_override_colors/font_color = Color(0.92, 0.96, 1, 1) -theme_override_font_sizes/font_size = 34 -text = "Wybierz statek" +theme_override_font_sizes/font_size = 26 +text = "HANGAR / WYBÓR STATKU" [node name="Instructions" type="Label" parent="Layout"] layout_mode = 2 theme_override_colors/font_color = Color(0.55, 0.7, 0.82, 1) -text = "Kliknij statek, aby od razu rozpocząć grę." +text = "Wybierz jednostkę i rozpocznij wyprawę." [node name="ShipScroll" type="ScrollContainer" parent="Layout"] layout_mode = 2 @@ -63,7 +63,8 @@ theme_override_constants/separation = 20 [node name="CloseButton" type="Button" parent="Layout/Footer"] unique_name_in_owner = true -custom_minimum_size = Vector2(220, 52) +custom_minimum_size = Vector2(180, 40) layout_mode = 2 -theme_override_font_sizes/font_size = 20 -text = "WRÓĆ" +theme_override_font_sizes/font_size = 16 +text = "‹ WRÓĆ / ESC" + diff --git a/scripts/game/flight_hud.gd b/scripts/game/flight_hud.gd index f5043eb..0242299 100644 --- a/scripts/game/flight_hud.gd +++ b/scripts/game/flight_hud.gd @@ -17,6 +17,7 @@ var _sidebar_toggle: Button var _sidebar_open := true var _sidebar_tween: Tween const SIDEBAR_WIDTH := 306.0 +const EQUIPMENT_UI_SCALE := 0.8 var _debug_scroll: ScrollContainer var _captain_panel: PanelContainer var _cockpit: Control @@ -48,6 +49,8 @@ func _ready() -> void: inventory_panel.set("layout_mode", 0) inventory_panel.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT) inventory_panel.size = Vector2(560, inventory_panel.get_combined_minimum_size().y) + inventory_panel.scale = Vector2.ONE * EQUIPMENT_UI_SCALE + inventory_panel.set("drawer_width", inventory_panel.size.x * EQUIPMENT_UI_SCALE) $HUDRoot.resized.connect(func() -> void: _layout_sidebar.call_deferred()) _create_ability_bar() debug_toggle.pressed.connect(_toggle_debug) @@ -120,7 +123,7 @@ func _create_sidebar() -> void: _layout_debug() func _layout_debug() -> void: - if _debug_scroll == null: + if not is_instance_valid(_debug_scroll) or not is_instance_valid(_captain_panel): return var top := _captain_panel.position.y + _captain_panel.size.y + 6.0 debug_toggle.position = Vector2(12, top) @@ -139,28 +142,31 @@ func _toggle_sidebar() -> void: else: close_inventory() _sidebar_tween = create_tween() - _sidebar_tween.tween_property(_sidebar, "position:x", $HUDRoot.size.x - _sidebar.size.x - 8 if _sidebar_open else $HUDRoot.size.x, 0.25).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT if _sidebar_open else Tween.EASE_IN) + _sidebar_tween.tween_property(_sidebar, "position:x", $HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 0.25).set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT if _sidebar_open else Tween.EASE_IN) if not _sidebar_open: _sidebar_tween.tween_callback(_sidebar.hide) func _layout_sidebar() -> void: + if not is_instance_valid(_sidebar) or not is_instance_valid(_cockpit): + return if _sidebar_tween != null and _sidebar_tween.is_running(): _sidebar_tween.kill() - _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))) + _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), 126) _cockpit.position = Vector2(($HUDRoot.size.x - _cockpit.size.x * 0.85) * 0.5, $HUDRoot.size.y - _cockpit.size.y * 0.85 - 12) _layout_debug() - _sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x - 8 if _sidebar_open else $HUDRoot.size.x, 44) + _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 + _inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE _layout_inventory() func _layout_inventory() -> void: - if _inventory_clip == null: + if not is_instance_valid(_inventory_clip) or not is_instance_valid(_sidebar): return var equipment := _sidebar.get_node("Scroll/Contents/ShipEquipmentPanel") as Control - _inventory_clip.position = Vector2(maxf(12.0, _sidebar.position.x - _inventory_clip.size.x - 8), minf(equipment.global_position.y, maxf(8.0, $HUDRoot.size.y - inventory_panel.size.y - 8))) + _inventory_clip.position = Vector2(maxf(12.0, _sidebar.position.x - _inventory_clip.size.x - 8), minf(equipment.global_position.y, maxf(8.0, $HUDRoot.size.y - inventory_panel.size.y * EQUIPMENT_UI_SCALE - 8))) func _process(_delta: float) -> void: _layout_inventory() @@ -249,17 +255,30 @@ func _create_ability_bar() -> void: panel.name = "ActiveAbilities" $HUDRoot.add_child(panel) panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM) - panel.offset_left = -300.0 - panel.offset_right = 300.0 - panel.offset_top = -188.0 - panel.offset_bottom = -146.0 + panel.offset_left = -120.0 + panel.offset_right = 120.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" + _ability_bar.alignment = BoxContainer.ALIGNMENT_CENTER _ability_bar.add_theme_constant_override("separation", 4) panel.add_child(_ability_bar) for index in ShipInventory.EXTENSION_SLOT_COUNT: var button := Button.new() button.name = "Ability%d" % (index + 1) - button.custom_minimum_size = Vector2(110, 36) + button.custom_minimum_size = Vector2(44, 44) + button.expand_icon = true + button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER + button.add_theme_constant_override("icon_max_width", 30) + button.focus_mode = Control.FOCUS_NONE + var shortcut := Label.new() + shortcut.text = str(index + 1) + shortcut.position = Vector2(3, 1) + shortcut.mouse_filter = Control.MOUSE_FILTER_IGNORE + shortcut.add_theme_font_size_override("font_size", 9) + button.add_child(shortcut) button.add_theme_font_size_override("font_size", 11) button.pressed.connect(func() -> void: player.call("activate_ability", index)) _ability_bar.add_child(button) @@ -277,14 +296,17 @@ func _update_ability_bar() -> void: any_equipped = true var reason: String = player.call("ability_unavailable_reason", index) button.disabled = not reason.is_empty() - button.text = "[%d] %s\n%s" % [index + 1, ability.display_name, "GOTOWE" if reason.is_empty() else reason] + var item: ItemInstance = player.get_node("ShipInventory").get_item("extension", index) + button.icon = item.definition.icon + button.text = "" + button.add_theme_color_override("icon_disabled_color", Color(0.45, 0.5, 0.55, 0.65)) var style := _ability_ready_style if reason.is_empty() else _ability_blocked_style button.add_theme_stylebox_override("normal", style) button.add_theme_stylebox_override("disabled", style) button.add_theme_stylebox_override("hover", style) button.add_theme_color_override("font_color", Color("a5ffda")) button.add_theme_color_override("font_disabled_color", Color("a3aebb")) - button.tooltip_text = ability.description + ("\n" + reason if not reason.is_empty() else "\nGotowe") + button.tooltip_text = "[%d] %s\n" % [index + 1, ability.display_name] + ability.description + ("\n" + reason if not reason.is_empty() else "\nGotowe") _ability_bar.get_parent().visible = any_equipped func _make_ability_style(ready: bool) -> StyleBoxFlat: diff --git a/scripts/game/ship_catalog.gd b/scripts/game/ship_catalog.gd index 86779d2..f26e640 100644 --- a/scripts/game/ship_catalog.gd +++ b/scripts/game/ship_catalog.gd @@ -3,18 +3,18 @@ class_name ShipCatalog # Base hull values, before equipment and captain bonuses. const SHIPS := [ - {"number": 0, "name": "Aegis", "role": "Wzmocnione tarcze", "hull": 100.0, "shields": 125.0, "energy": 100.0, "speed": 90.0}, - {"number": 2, "name": "Titan", "role": "Wzmocniony kadłub", "hull": 125.0, "shields": 100.0, "energy": 100.0, "speed": 90.0}, - {"number": 3, "name": "Eagle", "role": "Szybki myśliwiec", "hull": 100.0, "shields": 100.0, "energy": 100.0, "speed": 108.0}, - {"number": 7, "name": "Phantom", "role": "Większy zapas energii", "hull": 100.0, "shields": 100.0, "energy": 125.0, "speed": 90.0}, + {"number": 0, "name": "Szerszeń", "asset": "szerszen", "role": "MYŚLIWIEC • SZYBKOŚĆ", "accent": Color("efb866"), "hull": 100.0, "shields": 100.0, "energy": 100.0, "speed": 108.0}, + {"number": 2, "name": "Bastion", "asset": "bastion", "role": "FREGATA • PANCERZ", "accent": Color("de976b"), "hull": 125.0, "shields": 100.0, "energy": 100.0, "speed": 90.0}, + {"number": 3, "name": "Wędrowiec", "asset": "wedrowiec", "role": "ZWIADEK • TARCZE", "accent": Color("64d8ed"), "hull": 100.0, "shields": 125.0, "energy": 100.0, "speed": 90.0}, + {"number": 7, "name": "Żmija", "asset": "zmija", "role": "KORSARZ • ENERGIA", "accent": Color("b796ff"), "hull": 100.0, "shields": 100.0, "energy": 125.0, "speed": 90.0}, ] static func texture_path(ship: Dictionary) -> String: - return "res://assets/Ships/Spaceship_%d.png" % int(ship["number"]) + return "res://assets/Ships/player/%s.png" % ship["asset"] static func for_path(path: String) -> Dictionary: for ship in SHIPS: - if texture_path(ship) == path: + if texture_path(ship) == path or path == "res://assets/Ships/Spaceship_%d.png" % int(ship["number"]): return ship return SHIPS[0] diff --git a/scripts/items/ship_inventory.gd b/scripts/items/ship_inventory.gd index 0a788a2..1b5614c 100644 --- a/scripts/items/ship_inventory.gd +++ b/scripts/items/ship_inventory.gd @@ -298,26 +298,39 @@ func _comparison_stats(bonuses: Dictionary) -> Dictionary: stats[data[0]] = {"value": value, "name": data[3], "unit": data[4]} 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]} - var captain_damage := 1.0 + GameState.captain.get_skill_bonus("gunner") - var damage_factor := (1.0 + float(bonuses.get("damage_percent", 0.0)) / 100.0) * captain_damage - var critical_factor := preload("res://scripts/captain/captain_balance.gd").BASE_CRITICAL_MULTIPLIER * (1.0 + maxf(0.0, float(bonuses.get("critical_damage_percent", 0.0)) / 100.0)) - for index in weapon_slots.size(): - var weapon := weapon_slots[index] - if weapon == null: - continue - var damage := weapon.get_stat_total("damage") * damage_factor - var binding: String = ["LPM", "PPM", "ŚPM"][index] - stats["weapon_damage_%d" % index] = {"value": damage, "name": "Śr. obrażenia [%s]" % binding, "unit": "DMG"} - stats["weapon_critical_%d" % index] = {"value": damage * critical_factor, "name": "Śr. krytyk [%s]" % binding, "unit": "DMG"} - if weapon.definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE: - stats["missile_cooldown_%d" % index] = {"value": -maxf(1.0, 5.0 - float(bonuses.get("missile_cooldown_reduction", 0.0))), "name": "Skrócenie cooldownu [%s]" % binding, "unit": "s"} - var chance := clampf(GameState.captain.get_skill_bonus("sharp_eye") + float(bonuses.get("critical_chance_percent", 0.0)) / 100.0, 0.0, 1.0) - stats["critical_chance"] = {"value": chance * 100.0, "name": "Krytyki na 100 strzałów", "unit": "trafień"} - stats["shield_recovery"] = {"value": maxf(0.0, float(bonuses.get("shield_on_damage_percent", 0.0))), "name": "Odzysk tarcz na 100 DMG", "unit": "pkt"} - var balance := preload("res://scripts/captain/captain_balance.gd") - var scale_factor := 1.0 + 0.05 * (clampi(GameState.system_level, 1, 1000) - 1) - for tier in 3: - var caption: String = ["wróg", "elita", "boss"][tier] - stats["xp_%d" % tier] = {"value": roundi(balance.KILL_XP[tier] * scale_factor * maxf(0.0, 1.0 + float(bonuses.get("experience_percent", 0.0)) / 100.0)), "name": "EXP / %s" % caption, "unit": "EXP"} - stats["credits_%d" % tier] = {"value": roundi(balance.KILL_CREDITS[tier] * scale_factor * maxf(0.0, 1.0 + float(bonuses.get("credits_percent", 0.0)) / 100.0)), "name": "Kredyty / %s" % caption, "unit": "kr"} + # These stats are bonuses in their own right, not rewards or hit scenarios. + 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"} return stats + +func get_weapon_comparison(item: ItemInstance, slot_index: int) -> Dictionary: + if item == null or item.definition == null or item.definition.category != ItemDefinition.Category.WEAPON or slot_index < 0 or slot_index >= weapon_slots.size(): + return {} + var equipped := weapon_slots[slot_index] + var before := _comparison_bonuses() + var after := _comparison_bonuses(item, equipped) + var rows := get_item_comparison(item, equipped) + var flat_damage := _weapon_flat_damage_addons(item) - _weapon_flat_damage_addons(equipped) + if not is_zero_approx(flat_damage): + rows.append({"key": "flat_damage_addon", "name": "Obrażenia z addonów", "delta": flat_damage, "unit": "DMG"}) + return {"slot": slot_index, "dps_delta": _weapon_dps(item, after) - _weapon_dps(equipped, before), "stats": rows} + +func _weapon_flat_damage_addons(item: ItemInstance) -> float: + var total := 0.0 + if item != null: + for modifier in item.additional_modifiers: + if modifier != null and modifier.stat_key == "damage": + total += modifier.value + return total + +func _weapon_dps(item: ItemInstance, bonuses: Dictionary) -> float: + if item == null or item.definition == null: + return 0.0 + var damage_factor := (1.0 + float(bonuses.get("damage_percent", 0.0)) / 100.0) * (1.0 + GameState.captain.get_skill_bonus("gunner")) + var chance := clampf(GameState.captain.get_skill_bonus("sharp_eye") + float(bonuses.get("critical_chance_percent", 0.0)) / 100.0, 0.0, 1.0) + var critical_factor := preload("res://scripts/captain/captain_balance.gd").BASE_CRITICAL_MULTIPLIER * (1.0 + maxf(0.0, float(bonuses.get("critical_damage_percent", 0.0)) / 100.0)) + var interval := item.definition.fire_interval + 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) diff --git a/scripts/levels/exploration_poi.gd b/scripts/levels/exploration_poi.gd index 5dbfb26..6a0d5c1 100644 --- a/scripts/levels/exploration_poi.gd +++ b/scripts/levels/exploration_poi.gd @@ -2,6 +2,7 @@ extends StaticBody2D const GENERATOR := preload("res://scripts/items/item_generator.gd") const ASTEROID_TEXTURES := [preload("res://assets/asteroids/asteroid_rock.png"), preload("res://assets/asteroids/asteroid_metal.png"), preload("res://assets/asteroids/asteroid_ice.png")] +const WRECK_TEXTURES := [preload("res://assets/wrecks/wreck_frigate.png"), preload("res://assets/wrecks/wreck_freighter.png"), preload("res://assets/wrecks/wreck_interceptor.png")] const LOOT := preload("res://scripts/items/loot_pickup.gd") var kind := "wreck" var poi_id := "" @@ -41,6 +42,14 @@ func _ready() -> void: _asteroid_sprite.rotation = deg_to_rad(posmod(reward_seed, 360)) _asteroid_spin = (0.025 + posmod(reward_seed, 12) * 0.005) * (1.0 if reward_seed % 2 == 0 else -1.0) add_child(_asteroid_sprite) + if kind == "wreck": + var sprite := Sprite2D.new() + sprite.name = "WreckSprite" + sprite.texture = WRECK_TEXTURES[posmod(reward_seed, WRECK_TEXTURES.size())] + var texture_size := sprite.texture.get_size() + sprite.scale = Vector2.ONE * (92.0 / maxf(texture_size.x, texture_size.y)) + sprite.rotation = deg_to_rad(posmod(reward_seed, 360)) + add_child(sprite) _label = Label.new() _label.light_mask = 4 _label.position = Vector2(-160, 40) @@ -66,12 +75,6 @@ func _process(delta: float) -> void: func reward_scale() -> float: return 1.0 + 0.05 * (clampi(GameState.system_level, 1, 1000) - 1) -func _draw() -> void: - if kind == "wreck": - draw_colored_polygon(PackedVector2Array([Vector2(-38, 20), Vector2(-16, -30), Vector2(7, -12), Vector2(22, 27), Vector2(-5, 14)]), Color("626b78")) - draw_line(Vector2(-12, -20), Vector2(4, 15), Color("df9c67"), 3, true) - draw_colored_polygon(PackedVector2Array([Vector2(28, -16), Vector2(42, -5), Vector2(29, 8)]), Color("434e60")) - func _update_label(near: bool) -> void: if kind == "ruin": _label.text = "?" diff --git a/scripts/ui/equipment_slot_view.gd b/scripts/ui/equipment_slot_view.gd index 5a828a0..c61318c 100644 --- a/scripts/ui/equipment_slot_view.gd +++ b/scripts/ui/equipment_slot_view.gd @@ -13,23 +13,34 @@ var _delete_button: Button func _ready() -> void: if slot_kind == "cargo": var overlay := Control.new() + overlay.name = "ActionOverlay" overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(overlay) _delete_button = Button.new() - _delete_button.text = "x" + _delete_button.name = "SalvageButton" + _delete_button.icon = preload("res://assets/icons/screwdriver.png") + _delete_button.expand_icon = true + _delete_button.add_theme_constant_override("icon_max_width", 18) _delete_button.z_index = 5 _delete_button.tooltip_text = "Rozłóż przedmiot: +10 części" - _delete_button.flat = true - _delete_button.add_theme_font_size_override("font_size", 13) - _delete_button.add_theme_color_override("font_color", Color("ff8989")) + _delete_button.focus_mode = Control.FOCUS_NONE + var tool_style := StyleBoxFlat.new() + tool_style.bg_color = Color(0.04, 0.07, 0.1, 0.96) + tool_style.border_color = Color("b68c53") + tool_style.set_border_width_all(1) + tool_style.set_corner_radius_all(3) + tool_style.set_content_margin_all(2) + _delete_button.add_theme_stylebox_override("normal", tool_style) + _delete_button.add_theme_stylebox_override("hover", tool_style) + _delete_button.add_theme_stylebox_override("pressed", tool_style) overlay.add_child(_delete_button) _delete_button.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT) - # Overlay follows the panel's content rect (5 px top, 18 px right padding). - _delete_button.offset_left = -4 - _delete_button.offset_right = 16 - _delete_button.offset_top = -3 - _delete_button.offset_bottom = 17 - _delete_button.custom_minimum_size = Vector2(20, 20) + _delete_button.offset_left = -24 + _delete_button.offset_right = -2 + _delete_button.offset_top = 2 + _delete_button.offset_bottom = 24 + _delete_button.custom_minimum_size = Vector2(22, 22) + _delete_button.hide() _delete_button.pressed.connect(_delete_item) _inventory = get_tree().get_first_node_in_group("ship_inventory") as ShipInventory if _inventory: @@ -59,9 +70,17 @@ func _ready() -> void: _refresh() func _process(_delta: float) -> void: + _update_salvage_visibility() if slot_kind == "weapon" and _has_missile(): queue_redraw() +func _update_salvage_visibility() -> void: + if _delete_button == null or _inventory == null: + return + var hovered_control := get_viewport().gui_get_hovered_control() + var hovered := hovered_control == self or (hovered_control != null and is_ancestor_of(hovered_control)) + _delete_button.visible = is_visible_in_tree() and hovered and _inventory.get_item(slot_kind, slot_index) != null and not get_viewport().gui_is_dragging() + func _draw() -> void: if slot_kind != "weapon" or not _has_missile() or _inventory == null: return @@ -83,8 +102,7 @@ func _refresh() -> void: tooltip_text = item.get_tooltip_text() if item != null else _empty_slot_text() if item != null and slot_kind == "weapon" and _has_missile(): tooltip_text += "\nCooldown po addonach: %.2f s\nZapas rakiet: %d" % [_inventory.get_missile_cooldown(item), _inventory.get_missile_count()] - if _delete_button: - _delete_button.visible = item != null + _update_salvage_visibility() if item == null or item.definition == null: slot_label.add_theme_color_override("font_color", Color("82909e")) slot_label.text = "—" @@ -155,6 +173,9 @@ func _make_custom_tooltip(for_text: String) -> Object: return panel func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void: + if item.definition.category == ItemDefinition.Category.WEAPON: + _add_weapon_comparison(columns, item) + return var module_index := ShipInventory.MODULE_CATEGORIES.find(item.definition.category) if module_index < 0: return @@ -184,6 +205,63 @@ func _add_comparison_column(columns: HBoxContainer, item: ItemInstance) -> void: comparison.pop() comparison.add_text("\nUwzględnia utratę bonusów zdejmowanego przedmiotu.") +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", 11) + label.add_theme_color_override("font_color", color) + label.mouse_filter = Control.MOUSE_FILTER_IGNORE + return label + +func _comparison_delta(value: float) -> Label: + var label := _comparison_label(("%+.2f" % value).replace(".", ",") if not is_zero_approx(value) else "—", 64, Color("79e6a0") if value > 0.0 else (Color("ff8989") if value < 0.0 else Color("82909e"))) + label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + return label + +func _add_weapon_comparison(columns: HBoxContainer, item: ItemInstance) -> void: + columns.add_child(VSeparator.new()) + var section := VBoxContainer.new() + section.name = "WeaponComparison" + section.add_theme_constant_override("separation", 6) + columns.add_child(section) + section.add_child(_comparison_label("ZMIANY PO WYMIANIE BRONI", 0, Color("91dbea"))) + var grid := GridContainer.new() + grid.columns = 4 + grid.add_theme_constant_override("h_separation", 8) + grid.add_theme_constant_override("v_separation", 4) + section.add_child(grid) + grid.add_child(_comparison_label("", 185)) + var comparisons: Array[Dictionary] = [] + for index in [0, 2, 1]: + grid.add_child(_comparison_label("%d · %s" % [index + 1, ["LPM", "PPM", "ŚPM"][index]], 64, Color("91dbea"))) + comparisons.append(_inventory.get_weapon_comparison(item, index)) + grid.add_child(_comparison_label("DPS broni", 185, Color("ffd166"))) + for comparison in comparisons: + grid.add_child(_comparison_delta(comparison["dps_delta"])) + var keys := {} + for comparison in comparisons: + for row in comparison["stats"]: + keys[row["key"]] = row + for key in keys: + var row: Dictionary = keys[key] + var compact_name: String = {"missile_cooldown_reduction": "Skróc. cooldownu rakiet", "critical_damage_percent": "Obraż. krytyczne", "energy_regeneration": "Regener. energii", "shield_regeneration": "Regener. tarcz", "shield_on_damage_percent": "Odzysk tarcz z obr."}.get(key, row["name"]) + var caption: Label = _comparison_label("%s (%s)" % [compact_name, row["unit"]], 185) + caption.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + caption.custom_minimum_size.x = 185 + caption.size_flags_horizontal = Control.SIZE_FILL + grid.add_child(caption) + for comparison in comparisons: + var delta := 0.0 + for change in comparison["stats"]: + if change["key"] == key: + delta = float(change["delta"]) + break + grid.add_child(_comparison_delta(delta)) + var note := _comparison_label("Średni DPS z krytykami i cooldownem.\nBez ograniczeń energii i amunicji.\nAddony uwzględniają zdjęcie obecnej broni.", 0, Color("a0b2c4")) + note.add_theme_font_size_override("font_size", 10) + section.add_child(note) + func _get_drag_data(_at_position: Vector2) -> Variant: if not _inventory: return null diff --git a/scripts/ui/main_menu.gd b/scripts/ui/main_menu.gd index defd332..afb06f9 100644 --- a/scripts/ui/main_menu.gd +++ b/scripts/ui/main_menu.gd @@ -40,7 +40,7 @@ func _layout_ship_panel() -> void: return if _drawer_tween != null and _drawer_tween.is_running(): _drawer_tween.kill() - _ship_panel.size = Vector2(minf(700.0, size.x * 0.55), size.y) + _ship_panel.size = Vector2(minf(820.0, size.x), size.y) _ship_panel.position = Vector2(size.x - _ship_panel.size.x if _panel_open else size.x, 0) func _close_ship_panel() -> void: diff --git a/scripts/ui/ship_inventory_panel.gd b/scripts/ui/ship_inventory_panel.gd index 7af0bab..2b7b25a 100644 --- a/scripts/ui/ship_inventory_panel.gd +++ b/scripts/ui/ship_inventory_panel.gd @@ -27,6 +27,7 @@ func _ready() -> void: GameState.captain.changed.connect(_refresh_title) for index in CARGO_SIZE: var slot := SLOT_SCENE.instantiate() + slot.name = "Cargo%d" % (index + 1) slot.set("slot_kind", "cargo") slot.set("slot_index", index) cargo_grid.add_child(slot) diff --git a/scripts/ui/ship_selection.gd b/scripts/ui/ship_selection.gd index cd898d4..71fd22f 100644 --- a/scripts/ui/ship_selection.gd +++ b/scripts/ui/ship_selection.gd @@ -6,7 +6,6 @@ const CATALOG := preload("res://scripts/game/ship_catalog.gd") @onready var ship_grid: GridContainer = %ShipGrid @onready var close_button: Button = %CloseButton - var button_group := ButtonGroup.new() func _ready() -> void: @@ -14,43 +13,74 @@ func _ready() -> void: close_button.pressed.connect(func() -> void: close_requested.emit()) _load_ships() +func _frame(accent: Color, highlighted: bool) -> StyleBoxFlat: + var style := StyleBoxFlat.new() + style.bg_color = Color("142b42") if highlighted else Color("0b192a") + style.border_color = accent if highlighted else Color("29435a") + style.set_border_width_all(2 if highlighted else 1) + style.set_corner_radius_all(10) + style.shadow_color = Color(0, 0, 0, 0.3) + style.shadow_size = 5 + return style + +func _label(text: String, font_size: int, color: Color) -> Label: + var label := Label.new() + label.text = text + label.mouse_filter = Control.MOUSE_FILTER_IGNORE + label.add_theme_font_size_override("font_size", font_size) + label.add_theme_color_override("font_color", color) + return label + func _load_ships() -> void: var selected_ship := CATALOG.for_path(GameState.selected_ship_path) for chassis in CATALOG.SHIPS: - var ship_path := CATALOG.texture_path(chassis) - var texture := load(ship_path) as Texture2D + var texture := load(CATALOG.texture_path(chassis)) as Texture2D if texture == null: continue + var accent: Color = chassis["accent"] var button := Button.new() button.name = "Ship%d" % int(chassis["number"]) - button.custom_minimum_size = Vector2(250, 240) + button.custom_minimum_size = Vector2(250, 258) button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.toggle_mode = true button.button_group = button_group + button.add_theme_stylebox_override("normal", _frame(accent, false)) + for state in ["hover", "pressed", "hover_pressed", "focus"]: + button.add_theme_stylebox_override(state, _frame(accent, true)) var contents := VBoxContainer.new() contents.mouse_filter = Control.MOUSE_FILTER_IGNORE button.add_child(contents) contents.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) - contents.offset_left = 12 - contents.offset_right = -12 + contents.offset_left = 16 + contents.offset_right = -16 contents.offset_top = 12 contents.offset_bottom = -12 - contents.add_theme_constant_override("separation", 12) + contents.add_theme_constant_override("separation", 5) + contents.add_child(_label(chassis["name"].to_upper(), 22, Color("edf6ff"))) + contents.add_child(_label(chassis["role"], 11, accent)) var preview := TextureRect.new() - preview.custom_minimum_size.y = 100 + preview.custom_minimum_size.y = 128 + preview.size_flags_vertical = Control.SIZE_EXPAND_FILL preview.texture = texture preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED preview.mouse_filter = Control.MOUSE_FILTER_IGNORE + preview.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR contents.add_child(preview) - var description := Label.new() - description.mouse_filter = Control.MOUSE_FILTER_IGNORE - description.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - description.text = "%s\n%s\n\n%s" % [chassis["name"], chassis["role"], CATALOG.stats_text(chassis)] - description.add_theme_font_size_override("font_size", 13) - contents.add_child(description) - button.add_theme_font_size_override("font_size", 13) - button.tooltip_text = "%s — %s\nStatystyki bazowe przed bonusami modułów.\n%s" % [chassis["name"], chassis["role"], CATALOG.stats_text(chassis)] + var stats := GridContainer.new() + stats.columns = 2 + stats.mouse_filter = Control.MOUSE_FILTER_IGNORE + stats.add_theme_constant_override("h_separation", 14) + stats.add_theme_constant_override("v_separation", 4) + for stat in [["Kadłub", "hull", ""], ["Tarcze", "shields", ""], ["Energia", "energy", ""], ["Prędkość", "speed", " px/s"]]: + var value := _label("%s %d%s" % [stat[0], chassis[stat[1]], stat[2]], 13, Color("bfd4e5")) + value.size_flags_horizontal = Control.SIZE_EXPAND_FILL + stats.add_child(value) + contents.add_child(stats) + var launch := _label("WYBIERZ I WYSTARTUJ ›", 11, accent) + launch.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + contents.add_child(launch) + button.tooltip_text = "%s\nStatystyki bazowe przed bonusami modułów.\n%s" % [chassis["name"], CATALOG.stats_text(chassis)] button.pressed.connect(_choose_ship.bind(chassis, button)) ship_grid.add_child(button) if chassis["number"] == selected_ship["number"]: diff --git a/tests/items/equipment_comparison_test.gd b/tests/items/equipment_comparison_test.gd index bc3d21d..5ee2d30 100644 --- a/tests/items/equipment_comparison_test.gd +++ b/tests/items/equipment_comparison_test.gd @@ -31,6 +31,8 @@ func _ready() -> void: candidate.additional_modifiers.append(_modifier("damage_percent", 2.0)) candidate.additional_modifiers.append(_modifier("damage_percent", 3.0)) candidate.additional_modifiers.append(_modifier("energy_regeneration", 4.0)) + candidate.additional_modifiers.append(_modifier("shield_on_damage_percent", 2.0)) + candidate.additional_modifiers.append(_modifier("experience_percent", 5.0)) assert(inventory.add_item(old)) assert(inventory.move_item("cargo", inventory.cargo.find(old), "module", 0)) assert(inventory.add_item(candidate)) @@ -41,8 +43,7 @@ func _ready() -> void: for row in rows: deltas[row["key"]] = row["delta"] assert(is_equal_approx(deltas["max_shields"], 1.0)) - var weapon: ItemInstance = inventory.weapon_slots[0] - assert(is_equal_approx(deltas["weapon_damage_0"], weapon.get_stat_total("damage") * -0.05)) + assert(is_equal_approx(deltas["damage_percent"], -5.0)) assert(is_equal_approx(deltas["shield_regeneration"], -1.0)) assert(is_equal_approx(deltas["energy_regeneration"], 2.0)) assert(inventory.get_item_comparison(old, old).is_empty()) @@ -83,9 +84,13 @@ func _ready() -> void: assert(columns.get_child_count() == 3) var comparison: RichTextLabel = columns.get_child(2) assert(comparison.get_parsed_text().contains("Tarcze: +1.00 pkt")) - assert(comparison.get_parsed_text().contains("Śr. obrażenia [LPM]:")) + assert(comparison.get_parsed_text().contains("Obrażenia broni: -5.00 %")) + assert(comparison.get_parsed_text().contains("Odzysk tarcz z obrażeń: +2.00 %")) + assert(comparison.get_parsed_text().contains("Doświadczenie: +5.00 %")) + assert(not comparison.get_parsed_text().contains("100 DMG")) + assert(not comparison.get_parsed_text().contains("EXP /")) + assert(not comparison.get_parsed_text().contains("[LPM]")) assert(not comparison.get_parsed_text().contains("→")) - assert(not comparison.get_parsed_text().contains("%")) assert(not comparison.get_parsed_text().contains("p.p.")) assert(comparison.get_parsed_text().contains("Regeneracja tarcz: -1.00 pkt/s")) var debug: Label = hud.get_node("DebugScroll/DebugInfo") diff --git a/tests/items/item_icons_test.gd b/tests/items/item_icons_test.gd index a7520d9..df88581 100644 --- a/tests/items/item_icons_test.gd +++ b/tests/items/item_icons_test.gd @@ -13,16 +13,26 @@ func _ready() -> void: for enemy in get_tree().get_nodes_in_group("enemies"): enemy.set_physics_process(false) var inventory: ShipInventory = ship.get_node("ShipInventory") + var extension_index := 0 for definition in GENERATOR.DEFINITIONS: assert(definition.icon != null) assert(definition.icon.get_width() == definition.icon.get_height()) - if definition.item_id in ["reinforced_shield", "hull_plating", "engine_extension", "compact_generator", "ship_computer", "beam_laser", "machine_gun", "plasma_cannon", "missile_launcher", "unguided_missile"]: + if definition.item_id in ["reinforced_shield", "hull_plating", "engine_extension", "compact_generator", "ship_computer", "beam_laser", "machine_gun", "plasma_cannon", "missile_launcher", "unguided_missile", "afterburner", "shield_charge", "repair_drones"]: assert(inventory.add_item(GENERATOR.generate_for_definition(definition, 8))) var category_index := ShipInventory.MODULE_CATEGORIES.find(definition.category) if category_index >= 0 and inventory.module_slots[category_index] == null: var item := GENERATOR.generate_for_definition(definition, 8) assert(inventory.add_item(item)) assert(inventory.move_item("cargo", inventory.cargo.find(item), "module", category_index)) + if definition.category == ItemDefinition.Category.ACTIVE_EXTENSION: + var extension := GENERATOR.generate_for_definition(definition, 8) + assert(inventory.add_item(extension)) + assert(inventory.move_item("cargo", inventory.cargo.find(extension), "extension", extension_index)) + extension_index += 1 + GameState.captain.parts = 20 + ship.set("hull", ship.get("max_hull") - 10) + ship.set("shields", ship.get("max_shields") - 20) + ship.set("energy", ship.get("max_energy")) var hud := game.get_node("HUD") hud._toggle_inventory() await get_tree().create_timer(0.3).timeout @@ -36,4 +46,14 @@ func _ready() -> void: var style: StyleBoxFlat = slot.get_theme_stylebox("panel") assert(style.bg_color == item.get_background_color()) assert(style.border_color == item.get_type_color()) + var salvage: Button = slot.get_node("ActionOverlay/SalvageButton") + assert(salvage.icon != null and salvage.text.is_empty()) + assert(slot.get_global_rect().encloses(salvage.get_global_rect())) + for index in extension_index: + var ability_button: Button = hud._ability_buttons[index] + assert(ability_button.icon == inventory.extension_slots[index].definition.icon) + assert(ability_button.text.is_empty()) + assert(ability_button.size.x < 64 * 0.8) + assert(ability_button.size.y < 64 * 0.8) + assert(inventory.extension_slots[2].definition.icon != preload("res://resources/items/hull_plating.tres").icon) print("ITEM_ICONS_TEST: PASS square textures and slots, icon assignment, rarity backgrounds and borders, inventory and equipment") diff --git a/tests/items/weapon_comparison_test.gd b/tests/items/weapon_comparison_test.gd new file mode 100644 index 0000000..df6bc95 --- /dev/null +++ b/tests/items/weapon_comparison_test.gd @@ -0,0 +1,80 @@ +extends Node +const GAME := preload("res://scenes/game/game.tscn") + +func weapon(damage: float, interval: float = 0.5, missile: bool = false) -> ItemInstance: + var item := ItemInstance.new() + item.definition = preload("res://resources/items/plasma_cannon.tres").duplicate() + item.definition.fire_interval = interval + if missile: + item.definition.weapon_kind = ItemDefinition.WeaponKind.MISSILE + item.damage_min = damage + item.damage_max = damage + return item + +func modifier(item: ItemInstance, key: String, value: float) -> void: + var addon := ItemStatModifier.new() + addon.stat_key = key + addon.value = value + item.additional_modifiers.append(addon) + +func delta_for(comparison: Dictionary, key: String) -> float: + for row in comparison["stats"]: + if row["key"] == key: + return row["delta"] + return 0.0 + +func _ready() -> void: + GameState.start_new_run() + var game := GAME.instantiate() + add_child(game) + game.set_physics_process(false) + var ship := game.get_node("PlayerShip") + ship.set_physics_process(false) + ship.get_node("WeaponController").set_physics_process(false) + for enemy in get_tree().get_nodes_in_group("enemies"): + enemy.set_physics_process(false) + var inventory: ShipInventory = ship.get_node("ShipInventory") + var old_first := weapon(10) + var old_second := weapon(20) + inventory.weapon_slots.assign([old_first, old_second, null]) + var candidate := weapon(12, 0.4) + for index in 3: + assert(is_equal_approx(inventory.get_weapon_comparison(candidate, index)["dps_delta"], [10.0, -10.0, 30.0][index])) + modifier(old_first, "credits_percent", 4) + modifier(old_second, "credits_percent", 9) + modifier(candidate, "credits_percent", 6) + for index in 3: + assert(is_equal_approx(delta_for(inventory.get_weapon_comparison(candidate, index), "credits_percent"), [2.0, -3.0, 6.0][index])) + modifier(old_first, "missile_cooldown_reduction", 0.5) + var rocket := weapon(40, 0.1, true) + modifier(rocket, "missile_cooldown_reduction", 2) + assert(is_equal_approx(inventory.get_weapon_comparison(rocket, 0)["dps_delta"], 40.0 / 3.0 - 20.0)) + assert(is_equal_approx(inventory.get_weapon_comparison(rocket, 2)["dps_delta"], 40.0 / 2.5)) + modifier(rocket, "missile_cooldown_reduction", 100) + assert(is_equal_approx(inventory.get_weapon_comparison(rocket, 2)["dps_delta"], 40.0)) + modifier(old_first, "damage_percent", 5) + modifier(candidate, "damage_percent", 20) + modifier(candidate, "critical_chance_percent", 10) + assert(is_equal_approx(inventory.get_weapon_comparison(candidate, 0)["dps_delta"], 39.6 - 21.0)) + assert(is_equal_approx(inventory.get_weapon_comparison(candidate, 1)["dps_delta"], 41.25 - 42.0)) + modifier(candidate, "damage", 3) + assert(is_equal_approx(delta_for(inventory.get_weapon_comparison(candidate, 0), "flat_damage_addon"), 3.0)) + assert(is_equal_approx(inventory.get_weapon_comparison(candidate, 0)["dps_delta"], 49.5 - 21.0)) + assert(inventory.weapon_slots[0] == old_first and inventory.weapon_slots[1] == old_second and inventory.weapon_slots[2] == null) + assert(inventory.missile_ammunition == ShipInventory.STARTING_MISSILE_AMMUNITION) + assert(inventory.add_item(candidate)) + inventory._recalculate_ship_stats() + inventory.inventory_changed.emit() + var hud := game.get_node("HUD/HUDRoot") + var slot := preload("res://scenes/ui/inventory_slot_view.tscn").instantiate() + slot.slot_index = inventory.cargo.find(candidate) + hud.add_child(slot) + slot.hide() + var tooltip: PanelContainer = slot._make_custom_tooltip(candidate.get_tooltip_text()) + hud.add_child(tooltip) + tooltip.position = Vector2(340, 100) + assert(tooltip.get_child(0).get_child(2).name == "WeaponComparison") + await get_tree().process_frame + await get_tree().process_frame + assert(tooltip.size.x < 1100 and tooltip.size.y < 620) + print("WEAPON_COMPARISON_TEST: PASS three slot DPS, empty slot, affix gains/losses, projected global bonuses, crits, missile cooldown and clamp, no inventory mutation, compact table") diff --git a/tests/items/weapon_comparison_test.gd.uid b/tests/items/weapon_comparison_test.gd.uid new file mode 100644 index 0000000..ad08f0c --- /dev/null +++ b/tests/items/weapon_comparison_test.gd.uid @@ -0,0 +1 @@ +uid://du6051e32i5k7 diff --git a/tests/items/weapon_comparison_test.tscn b/tests/items/weapon_comparison_test.tscn new file mode 100644 index 0000000..f17722b --- /dev/null +++ b/tests/items/weapon_comparison_test.tscn @@ -0,0 +1,4 @@ +[gd_scene load_steps=2 format=3] +[ext_resource type="Script" path="res://tests/items/weapon_comparison_test.gd" id="1"] +[node name="WeaponComparisonTest" type="Node"] +script = ExtResource("1") diff --git a/tests/levels/exploration_test.gd b/tests/levels/exploration_test.gd index 31aa379..4ce1f8a 100644 --- a/tests/levels/exploration_test.gd +++ b/tests/levels/exploration_test.gd @@ -31,6 +31,9 @@ func _ready() -> void: poi.take_weapon_hit(1) assert(poi._claimed) else: + if kind == "wreck": + assert(poi.has_node("WreckSprite")) + assert(poi.WRECK_TEXTURES.has(poi.get_node("WreckSprite").texture)) assert(poi.claim()) assert(not poi.claim()) await get_tree().process_frame diff --git a/tests/ships/captain_ship_drawer_test.gd b/tests/ships/captain_ship_drawer_test.gd index 49ad151..b76278a 100644 --- a/tests/ships/captain_ship_drawer_test.gd +++ b/tests/ships/captain_ship_drawer_test.gd @@ -23,6 +23,8 @@ func _ready() -> void: var captain := root.get_node("CaptainPanel") var equipment := drawer.get_node("Scroll/Contents/ShipEquipmentPanel") assert(captain.get_parent() != equipment.get_parent()) + assert(drawer.scale.is_equal_approx(Vector2.ONE * 0.8)) + assert(hud.inventory_panel.scale.is_equal_approx(Vector2.ONE * 0.8)) assert(captain.get_node("Contents/PortraitRow/AvatarFrame/CaptainAvatar").texture != null) assert(root.has_node("Cockpit")) var cockpit := root.get_node("Cockpit") as Control @@ -73,7 +75,7 @@ func _ready() -> void: assert(not drawer.visible) toggle.pressed.emit() await get_tree().create_timer(0.3).timeout - assert(drawer.visible and is_equal_approx(drawer.position.x + drawer.size.x, root.size.x - 8.0)) + assert(drawer.visible and is_equal_approx(drawer.position.x + drawer.size.x * drawer.scale.x, root.size.x - 8.0)) GameState.captain.learning_points = 1 GameState.captain.emit_changed() captain.get_node("Contents/Skills/gunner/Train_gunner").pressed.emit()