This commit is contained in:
Tomasz Boruc 2026-10-25 16:12:08 +01:00
parent 31a352e4b7
commit d350ac4d80
86 changed files with 1948 additions and 30 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://704p8qbp70pg"
path="res://.godot/imported/alien_hull_icons_preview.png-4a3b2e2d5939f4e51fda8b8d77040443.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://artifacts/alien_hull_icons_preview.png"
dest_files=["res://.godot/imported/alien_hull_icons_preview.png-4a3b2e2d5939f4e51fda8b8d77040443.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: 920 KiB

After

Width:  |  Height:  |  Size: 951 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 KiB

After

Width:  |  Height:  |  Size: 928 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dgmgbflblcs3s"
path="res://.godot/imported/freighter_warp_acceleration.png-2943d3abad9a49b02edd0d2c9cf8d95a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://artifacts/freighter_warp_acceleration.png"
dest_files=["res://.godot/imported/freighter_warp_acceleration.png-2943d3abad9a49b02edd0d2c9cf8d95a.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.

After

Width:  |  Height:  |  Size: 79 KiB

View File

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

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c5hkitkuwtovx"
path="res://.godot/imported/freighter_warp_entry.png-2b4f5151c10760ae8b4d285ac6133ce2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://artifacts/freighter_warp_entry.png"
dest_files=["res://.godot/imported/freighter_warp_entry.png-2b4f5151c10760ae8b4d285ac6133ce2.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.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://oalcejwjiwpd"
path="res://.godot/imported/freighter_warp_flash.png-72c8ce8e0bb8325d7e983c195b9a9706.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://artifacts/freighter_warp_flash.png"
dest_files=["res://.godot/imported/freighter_warp_flash.png-72c8ce8e0bb8325d7e983c195b9a9706.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.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1021 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://hddf6dysayqd"
path="res://.godot/imported/hull_b_next_next.png-3e316dfea67c2be3b933a7ae26374744.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/hull_b_next_next.png"
dest_files=["res://.godot/imported/hull_b_next_next.png-3e316dfea67c2be3b933a7ae26374744.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.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cpiidaiu1vopq"
path="res://.godot/imported/hull_b_next_next_next.png-731c4c53fecbac302e625aa59619a13f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/hull_b_next_next_next.png"
dest_files=["res://.godot/imported/hull_b_next_next_next.png-731c4c53fecbac302e625aa59619a13f.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

@ -0,0 +1,36 @@
# Ikony technologii kadłuba
Osiem osobnych ikon wygenerowanych wbudowanym ImageGen. Ramki pomarańczowe i zielone nakłada interfejs gry.
## hull_a.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Structural analysis: a dark alien armored hull segment shown as a cutaway, bright amber scanning lines revealing internal load-bearing ribs and one orange weak seam. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Offensive hull technology: amber and orange accents.
## hull_a_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Armor breach: a cracked angular alien armor plate with a deep glowing orange fracture, a precision projectile forcing apart the seam and exposing molten inner metal. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Offensive hull technology: amber and orange accents.
## hull_a_next_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Overpenetration: one orange projectile passing completely through a dark alien spaceship hull and continuing toward a second smaller alien ship behind it; readable single trajectory. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Offensive hull technology: amber and orange accents.
## hull_a_next_next_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Structural collapse execution: a severely damaged angular alien spaceship breaking cleanly into several large armor sections with bright orange internal light and flying fragments. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Offensive hull technology: amber and orange accents.
## hull_b.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Reinforced plating: three thick overlapping futuristic metallic hull plates, strong emerald green rim light, a small green structural reinforcement brace across them. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Defensive hull technology: emerald green accents.
## hull_b_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Reactive armor: a red projectile strikes an outer metallic plate that bursts outward in a controlled emerald green protective pulse, inner ship hull remains intact. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Defensive hull technology: emerald green accents.
## hull_b_next_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Emergency bulkheads: a heavy spaceship bulkhead sealing a glowing hull breach while a compact green repair mechanism welds the armored seam closed. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Defensive hull technology: emerald green accents.
## hull_b_next_next_next.png
Use case: stylized-concept. Asset type: one square research technology icon for a gritty top-down science fiction space game. Last bastion survival: a battered futuristic spaceship hull with one tiny brilliant emerald core still alive inside a short luminous green protective bubble, sparks and broken armor outside. Polished hand-painted game inventory illustration, dark navy-black background, strong centered silhouette readable at 34 pixels, restrained metallic detail, high contrast, subject occupies 85 percent of square. No text, letters, numbers, outer frame, border, interface, or icon grid. Defensive hull technology: emerald green accents.

View File

@ -0,0 +1,26 @@
# Shield research icons — built-in ImageGen
## shields_a
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: orange spectral scanner tracing resonant frequency rings across an alien energy shield. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous orange amber energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_a_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: orange electromagnetic clamps locking a cracked alien shield emitter, halted energy flow. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous orange amber energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_a_next_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: alien spaceship shield shattering into an enormous orange circular shockwave striking nearby alien silhouettes. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous orange amber energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_a_next_next_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: incoming red energy bolt striking a shield and a powerful orange counter lightning arc returning to the attacker. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous orange amber energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_b
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: three large metallic shield capacitors powering a vivid emerald energy dome. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous emerald green energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_b_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: emerald backup capacitor diverting bright energy from a spaceship gun to its weakening shield. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous emerald green energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_b_next_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: a strong emerald energy shield dispersing the first heavy red projectile into harmless sparks. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous emerald green energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.
## shields_b_next_next_next
Use case: stylized-concept. Asset type: square sci-fi game technology icon, matching premium metallic alien hull research icons. Subject: a shattered shield instantly reforming as a bright layered emerald emergency dome around a spaceship. Dark navy near-black background, one bold centered subject filling nearly entire square with narrow margins. Highly detailed painted 3D science fiction, graphite and silver metal, luminous emerald green energy, dramatic rim lighting. Readable silhouette at 36 pixels. No text, letters, labels, borders, watermark, grids or collage.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3mhgj3q16l0a"
path="res://.godot/imported/shields_a.png-81305acbe425e8b6acb388cb5cbb600c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/shields_a.png"
dest_files=["res://.godot/imported/shields_a.png-81305acbe425e8b6acb388cb5cbb600c.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.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

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

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ccq4xnmocy0ge"
path="res://.godot/imported/shields_a_next_next.png-261eb3b0577d581d41217f7869fd89d4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/shields_a_next_next.png"
dest_files=["res://.godot/imported/shields_a_next_next.png-261eb3b0577d581d41217f7869fd89d4.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.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

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

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bp7trn18i1jmu"
path="res://.godot/imported/shields_b_next.png-5e6bb5164d1c5527b08cdbb7784835f5.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/shields_b_next.png"
dest_files=["res://.godot/imported/shields_b_next.png-5e6bb5164d1c5527b08cdbb7784835f5.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.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cukfmpunubiv2"
path="res://.godot/imported/shields_b_next_next.png-3adfd60c436efd9b9efaba78d7751e57.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/freighter/research/shields_b_next_next.png"
dest_files=["res://.godot/imported/shields_b_next_next.png-3adfd60c436efd9b9efaba78d7751e57.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.

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

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

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

View File

@ -5,13 +5,13 @@ static func roll_disabled_system(roll := -1.0, system := -1) -> int:
if not DATA.learned("weapons_a_next") or (randf() if roll < 0 else roll) >= 0.01: return -1
return randi_range(0,2) if system < 0 else clampi(system,0,2)
static func apply_hit(target: Variant, damage: float, critical: bool, kind: int, piercing: float, visited: Dictionary, splash := true, disabled_system := -2) -> void:
static func apply_hit(target: Variant, damage: float, critical: bool, kind: int, piercing: float, visited: Dictionary, splash := true, disabled_system := -2, damage_scale := 1.0) -> void:
if not target is Node or target.is_queued_for_deletion(): return
var id: int = target.get_instance_id()
if visited.has(id): return
visited[id] = true
if target.has_method("take_research_weapon_hit"):
target.take_research_weapon_hit(damage,critical,kind,piercing,disabled_system)
target.take_research_weapon_hit(damage,critical,kind,piercing,disabled_system,damage_scale)
elif target.has_method("take_piercing_weapon_hit"):
target.take_piercing_weapon_hit(damage,critical,kind,piercing)
elif target.has_method("take_weapon_hit"):
@ -23,4 +23,4 @@ static func apply_hit(target: Variant, damage: float, critical: bool, kind: int,
for other in target.get_tree().get_nodes_in_group("enemies"):
if other == target or other.is_queued_for_deletion() or float(other.get("hull")) <= 0: continue
if target.global_position.distance_to(other.global_position) <= radius+float(other.get_visual_size())*0.5:
apply_hit(other,damage,critical,kind,piercing,visited,false,-1)
apply_hit(other,damage,critical,kind,piercing,visited,false,-1,damage_scale)

View File

@ -37,6 +37,20 @@ func _ready() -> void:
panel.add_child(column)
_status = ui.label(column,"",12,Color("e9b4fc"))
_update_status()
if GameState.distant_warp_arrival:
GameState.distant_warp_arrival = false
var arrival := CanvasLayer.new()
arrival.name = "WarpArrival"
arrival.layer = 100
game.add_child(arrival)
var fade := ColorRect.new()
fade.color = Color("04070cff")
fade.mouse_filter = Control.MOUSE_FILTER_IGNORE
fade.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
arrival.add_child(fade)
var tween := fade.create_tween()
tween.tween_property(fade,"color:a",0.0,0.55)
tween.tween_callback(arrival.queue_free)
func _build_structures() -> void:
var group := Node2D.new()
@ -88,6 +102,7 @@ func _return_home() -> bool:
return true
func _load_home() -> void:
var bastion_used := GameState.hull_bastion_used
var active: Dictionary = GameState.active_distant_map.duplicate(true)
var home: Dictionary = GameState.distant_home.duplicate(true)
var level := GameState.system_level
@ -97,6 +112,7 @@ func _load_home() -> void:
GameState.return_to_freighter = true
var error := get_tree().reload_current_scene()
if error != OK:
GameState.hull_bastion_used = bastion_used
GameState.return_to_freighter = false
GameState.active_distant_map = active
GameState.distant_home = home

View File

@ -50,6 +50,11 @@ var combat_level := 1
var _reward_claimed := false
var _disabled_systems := [0.0,0.0,0.0]
var _offline_shields := 0.0
var _hull_breach_remaining := 0.0
var _execution_finish := false
var _shield_regen_lock := 0.0
var _shield_overload_cooldown := 0.0
var _research_damage_scale := 1.0
func roll_virus_infection() -> bool:
if loot_tier == 2 or _virus_infection_attempted:
@ -84,6 +89,10 @@ func set_virus_controlled(value: bool) -> void:
queue_redraw()
func _draw() -> void:
if _hull_breach_remaining > 0.0:
for arc in 4:
var angle := arc*PI*0.5+0.12
draw_arc(Vector2.ZERO,get_visual_size()*0.55,angle,angle+0.9,12,Color(1.0,0.5,0.12,minf(0.9,_hull_breach_remaining)),1.8,true)
if virus_controlled:
draw_arc(Vector2.ZERO, get_visual_size() * 0.6, 0.0, TAU, 48, Color("c57dff"), 2.0, true)
for index in 3:
@ -172,11 +181,18 @@ func regenerate_defenses(delta: float) -> void:
if loot_tier == 2 or hull <= 0.0 or is_queued_for_deletion():
return
hull = minf(max_hull, hull + max_hull * 0.01 * delta)
if not system_disabled(2): shields = minf(max_shields, shields + max_shields * 0.01 * delta)
if not system_disabled(2) and _shield_regen_lock <= 0.0: shields = minf(max_shields, shields + max_shields * 0.01 * delta)
if is_instance_valid(_shield_visual):
_shield_visual.set_shield_values(shields, max_shields)
func tick_shield_technologies(delta: float) -> void:
_shield_regen_lock = maxf(0.0,_shield_regen_lock-delta)
_shield_overload_cooldown = maxf(0.0,_shield_overload_cooldown-delta)
func _physics_process(delta: float) -> void:
tick_shield_technologies(delta)
if _hull_breach_remaining > 0.0: queue_redraw()
_hull_breach_remaining = maxf(0.0,_hull_breach_remaining-delta)
tick_disabled_systems(delta)
regenerate_defenses(delta)
_slow_remaining = maxf(0.0, _slow_remaining - delta)
@ -319,7 +335,10 @@ func _fire_plasma(direction: Vector2) -> void:
endpoint = hit["position"]
var target: Node = hit.get("collider")
if target is CharacterBody2D and target.has_method("take_damage"):
target.take_damage(get_shot_damage(), false, DamageTypes.Type.ENERGY)
if target.has_method("take_enemy_weapon_hit"):
target.take_enemy_weapon_hit(get_shot_damage(), false, DamageTypes.Type.ENERGY, self)
else:
target.take_damage(get_shot_damage(), false, DamageTypes.Type.ENERGY)
var beam := Line2D.new()
beam.set_script(preload("res://scripts/game/laser_beam_visual.gd"))
beam.width = 4.0
@ -347,10 +366,12 @@ func take_piercing_weapon_hit(amount: float, critical: bool, damage_type: int, p
func take_weapon_hit(amount: float, critical: bool, damage_type: int = DamageTypes.Type.KINETIC) -> void:
_take_player_hit(amount,critical,damage_type,true,-2)
func take_research_weapon_hit(amount: float, critical: bool, damage_type: int, piercing: float, disabled_system: int) -> void:
func take_research_weapon_hit(amount: float, critical: bool, damage_type: int, piercing: float, disabled_system: int, damage_scale := 1.0) -> void:
_research_damage_scale = damage_scale
_weapon_piercing = clampf(piercing,0.0,0.5)
_take_player_hit(amount,critical,damage_type,true,disabled_system)
_weapon_piercing = 0.0
_research_damage_scale = 1.0
func take_research_dot(amount: float, damage_type: int, piercing: float) -> void:
_weapon_piercing = clampf(piercing,0.0,0.5)
@ -363,7 +384,7 @@ func _take_player_hit(amount: float, critical: bool, damage_type: int, direct: b
var before := hull + shields
_research_player_hit = true
var research := preload("res://scripts/planets/alien_research.gd")
if direct and amount > 0 and research.learned("weapons_a"): amount += 5.0
if direct and amount > 0 and research.learned("weapons_a"): amount += 5.0*_research_damage_scale
_research_direct_hit = direct
take_damage(amount, critical, damage_type)
_research_direct_hit = false
@ -402,20 +423,38 @@ func research_aim_direction(direction: Vector2, roll := -1.0, side := 0.0) -> Ve
func take_damage(amount: float, critical: bool = false, damage_type: int = DamageTypes.Type.KINETIC, bypass_absorption: bool = false) -> void:
if is_queued_for_deletion() or hull <= 0.0:
return
var actual := maxf(0.0, amount) * (1.0 if bypass_absorption else 1.0 - absorption[damage_type] / 100.0)
var research := preload("res://scripts/planets/alien_research.gd")
var absorption_factor := 0.5 if _research_player_hit and _hull_breach_remaining > 0.0 else 1.0
var actual := maxf(0.0, amount) * (1.0 if bypass_absorption else 1.0 - absorption[damage_type]*absorption_factor / 100.0)
var progress: Resource = GameState.captain.expedition
var shield_multiplier := 1.0 + (research.bonus(progress, "shields") if _research_player_hit else 0.0)
if _research_player_hit and research.learned("shields_a"): shield_multiplier += 0.05
var hull_multiplier := 1.0 + (research.bonus(progress, "hull") if _research_player_hit else 0.0)
if _research_player_hit and research.learned("hull_a"): hull_multiplier += 0.05
var piercing_damage := actual * _weapon_piercing if _research_player_hit else 0.0
var shield_damage := minf(shields, (actual - piercing_damage) * shield_multiplier)
var double_hit := _research_direct_hit and research.learned("weapons_a_next_next_next")
if double_hit: shield_damage = minf(shields,actual*shield_multiplier)
var remaining := actual if double_hit else maxf(0.0, actual - shield_damage / shield_multiplier)
var shields_before := shields
shields -= shield_damage
var hull_damage := minf(hull, remaining * hull_multiplier)
hull = maxf(0.0, hull - hull_damage)
if _research_direct_hit and hull_damage > 0.0:
if shields <= 0.0 and research.learned("hull_a_next"):
_hull_breach_remaining = 5.0
queue_redraw()
var execution_threshold := 0.02 if loot_tier == 2 else 0.08
if research.learned("hull_a_next_next_next") and hull > 0.0 and hull <= max_hull*execution_threshold:
hull_damage += hull
hull = 0.0
_execution_finish = true
actual = shield_damage + hull_damage
if _research_player_hit and shield_damage > 0.0:
if research.learned("shields_a_next"): _shield_regen_lock = 5.0
if shields_before > 0.0 and shields <= 0.0 and research.learned("shields_a_next_next") and _shield_overload_cooldown <= 0.0:
_shield_overload_cooldown = 10.0
_shield_overload(amount*4.0,damage_type)
if is_instance_valid(_shield_visual):
_shield_visual.set_shield_values(shields, max_shields)
if shield_damage > 0.0:
@ -429,6 +468,17 @@ func take_damage(amount: float, critical: bool = false, damage_type: int = Damag
_spawn_destruction_effect()
queue_free()
func _shield_overload(damage: float, damage_type: int) -> void:
var effect := Node2D.new()
effect.set_script(preload("res://scripts/game/shield_research_effect.gd"))
effect.position = global_position
get_parent().add_child(effect)
for other in get_tree().get_nodes_in_group("enemies"):
if other == self or other.is_queued_for_deletion() or float(other.get("hull")) <= 0.0: continue
if global_position.distance_to(other.global_position) <= 120.0:
# Raw damage deliberately bypasses research triggers and player-hit chains.
other.take_damage(damage,false,damage_type)
func get_fire_interval() -> float:
if weapon_kind == ItemDefinition.WeaponKind.LASER:
return maxf(LASER_MINIMUM_COOLDOWN, weapon_cooldown * LASER_COOLDOWN_MULTIPLIER)
@ -482,6 +532,13 @@ func _drop_loot() -> void:
get_parent().call_deferred("add_child", pickup)
func _spawn_destruction_effect() -> void:
if _execution_finish and _sprite.texture != null:
var collapse := Node2D.new()
collapse.set_script(preload("res://scripts/game/hull_collapse_effect.gd"))
collapse.configure(_sprite.texture,get_visual_size(),global_rotation)
collapse.position = position
get_parent().add_child(collapse)
return
var effect := IMPACT_EFFECT_SCENE.instantiate() as Node2D
effect.global_position = global_position
get_parent().add_child(effect)

View File

@ -0,0 +1,149 @@
extends Control
signal finished
const DURATION := 4.0
const SHIP := preload("res://assets/planets/freighter.png")
var start_position := Vector2.ZERO
var destination_name := "ODLEGŁY SEKTOR"
var elapsed := 0.0
var _ship: Sprite2D
var _flash: ColorRect
var _title: Label
var _status: Label
var _stars: Array[Vector3] = []
var _done := false
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
mouse_filter = Control.MOUSE_FILTER_STOP
var rng := RandomNumberGenerator.new()
rng.seed = 872901
for star in 170: _stars.append(Vector3(rng.randf(),rng.randf(),rng.randf_range(0.3,1.0)))
_ship = Sprite2D.new()
_ship.texture = SHIP
_ship.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
add_child(_ship)
_flash = ColorRect.new()
_flash.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_flash.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(_flash)
_title = _label(destination_name.to_upper(),18,Color("c4f3ff"))
_status = _label("INICJALIZACJA NAPĘDU SKOKOWEGO",11,Color("6eaeba"))
_play_audio()
_update_frame()
func _label(value: String, font_size: int, color: Color) -> Label:
var label := Label.new()
label.text = value
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.add_theme_font_size_override("font_size",font_size)
label.add_theme_color_override("font_color",color)
label.add_theme_color_override("font_outline_color",Color("020810"))
label.add_theme_constant_override("outline_size",5)
add_child(label)
return label
func _input(_event: InputEvent) -> void:
get_viewport().set_input_as_handled()
func _process(delta: float) -> void:
elapsed = minf(DURATION,elapsed+delta)
_update_frame()
if elapsed >= DURATION and not _done:
_done = true
finished.emit()
func _update_frame() -> void:
var charge := smoothstep(0.0,0.9,elapsed)
var acceleration := smoothstep(0.8,2.6,elapsed)
var jump := smoothstep(2.6,3.15,elapsed)
var center := Vector2(size.x*0.5,size.y*0.64)
var departure := start_position if start_position != Vector2.ZERO else center
var ship_position := departure.lerp(center,charge)
ship_position.y -= size.y*(0.16*acceleration+0.24*jump)
var height := lerpf(285.0,minf(440.0,size.y*0.61),charge)
height *= 1.0-jump*0.93
_ship.position = ship_position
_ship.scale = Vector2(height/SHIP.get_height()*(1.0-jump*0.6),height/SHIP.get_height())
_ship.modulate = Color(1.0+acceleration*0.3,1.0+acceleration*0.5,1.0+acceleration*0.7,1.0-jump)
_ship.visible = elapsed < 3.18
var flash := exp(-pow((elapsed-3.15)/0.10,2.0))*0.9
var blackout := smoothstep(3.55,DURATION,elapsed)
_flash.color = Color(0.55,0.87,1.0,flash) if flash > blackout else Color(0.015,0.025,0.045,blackout)
_title.position = Vector2(0,28)
_title.size = Vector2(size.x,30)
_status.position = Vector2(0,61)
_status.size = Vector2(size.x,22)
_status.text = "SKOK WARP" if elapsed > 2.6 else ("PRZYSPIESZANIE · TRASA ZABEZPIECZONA" if elapsed > 0.9 else "INICJALIZACJA NAPĘDU SKOKOWEGO")
_title.modulate.a = 1.0-blackout
_status.modulate.a = 1.0-blackout
queue_redraw()
func _draw() -> void:
var charge := smoothstep(0.0,0.9,elapsed)
var acceleration := smoothstep(0.8,2.6,elapsed)
var jump := smoothstep(2.6,3.15,elapsed)
draw_rect(Rect2(Vector2.ZERO,size),Color(0.005,0.014,0.03,charge*0.96))
for star in _stars:
var y := fmod(star.y*size.y+elapsed*(18.0+acceleration*180.0+jump*1200.0)*star.z,size.y+160.0)-80.0
var point := Vector2(star.x*size.x,y)
var length := 1.0+acceleration*star.z*34.0+jump*star.z*150.0
draw_line(point,point+Vector2(0,length),Color(0.4,0.75,1.0,charge*star.z*0.55),1.0,true)
if _ship != null and _ship.visible and jump < 0.98:
var height: float = SHIP.get_height()*_ship.scale.y
var tail: Vector2 = _ship.position+Vector2(0,height*0.45)
var power := (0.2+charge*0.5+acceleration*0.7)*(1.0-jump)
for side in [-1.0,1.0]:
var nozzle := tail+Vector2(side*height*0.075,0)
for halo in range(6,0,-1): draw_circle(nozzle,halo*7.0*power,Color(0.1,0.7,1.0,0.018*power))
var flame := (35.0+acceleration*170.0)*power*(0.94+sin(elapsed*71.0)*0.06)
for layer in 3:
var width := (13.0-layer*3.2)*power
var tip := nozzle+Vector2(sin(elapsed*43.0+side)*minf(4.0,width*0.2),flame*(1.0-layer*0.2))
var colors := [Color(0.08,0.4,1.0,0.25),Color(0.2,0.85,1.0,0.65),Color(0.85,1.0,1.0,0.95)]
draw_colored_polygon(PackedVector2Array([nozzle+Vector2(-width,-8),nozzle+Vector2(width,-8),nozzle+Vector2(width*0.65,flame*0.3),tip,nozzle+Vector2(-width*0.65,flame*0.3)]),colors[layer])
var portal := smoothstep(1.75,2.75,elapsed)*(1.0-smoothstep(3.18,3.6,elapsed))
var gate := Vector2(size.x*0.5,size.y*0.24)
if portal > 0.001:
draw_set_transform(gate,0,Vector2(1,0.24))
for halo in range(8,0,-1): draw_circle(Vector2.ZERO,(100.0+halo*6)*portal,Color(0.15,0.65,1.0,0.014))
draw_circle(Vector2.ZERO,104*portal,Color(0.01,0.035,0.065,0.95))
for ring in 3:
draw_arc(Vector2.ZERO,(102+ring*5)*portal,elapsed*(2+ring),elapsed*(2+ring)+TAU*0.91,96,Color(0.4+ring*0.2,0.8+ring*0.07,1.0,0.8),2.0,true)
draw_set_transform(Vector2.ZERO)
if elapsed > 3.05:
var flare := exp(-pow((elapsed-3.16)/0.12,2.0))
for layer in range(6,0,-1):
draw_line(gate-Vector2(size.x*0.45,0),gate+Vector2(size.x*0.45,0),Color(0.3,0.8,1.0,flare*0.07),float(layer*4),true)
var ring_time := maxf(0.0,elapsed-3.15)
draw_arc(gate,20.0+ring_time*650.0,0,TAU,96,Color(0.4,0.85,1.0,maxf(0.0,0.65-ring_time)),2.0,true)
draw_line(gate,gate+Vector2(0,-size.y),Color(0.8,0.98,1.0,flare),4.0+flare*24.0,true)
func _play_audio() -> void:
var rate := 22050
var data := PackedByteArray()
data.resize(int(DURATION*rate)*2)
var rng := RandomNumberGenerator.new()
rng.seed = 73902
var phase := 0.0
var noise := 0.0
for sample in int(DURATION*rate):
var t := float(sample)/rate
var rise := smoothstep(0.0,3.1,t)
phase += TAU*(38.0+rise*140.0)/rate
noise = lerpf(noise,rng.randf_range(-1,1),0.12+rise*0.2)
var envelope := smoothstep(0,0.3,t)*(1.0-smoothstep(3.15,3.8,t))
var rush := exp(-pow((t-3.14)/0.16,2.0))
var value := (sin(phase)*0.13+sin(phase*1.51)*0.05+noise*(0.1+rise*0.25))*envelope+noise*rush*0.55
data.encode_s16(sample*2,int(clampf(value,-0.9,0.9)*32767))
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = rate
stream.data = data
var audio := AudioStreamPlayer.new()
audio.stream = stream
audio.volume_db = -9.0
if AudioServer.get_bus_index("Effects") >= 0: audio.bus = &"Effects"
add_child(audio)
audio.play()

View File

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

View File

@ -232,6 +232,8 @@ func advance_to_next_level() -> bool:
return true
func _load_next_level() -> void:
var bastion_used := GameState.hull_bastion_used
GameState.hull_bastion_used = false
var previous_level := GameState.system_level
var previous_seed := GameState.system_seed
player.get_node("ShipInventory").call("preserve_for_restart")
@ -240,6 +242,7 @@ func _load_next_level() -> void:
var error := get_tree().reload_current_scene()
if error != OK:
GameState.system_level = previous_level
GameState.hull_bastion_used = bastion_used
GameState.system_seed = previous_seed
GameState.restart_inventory.clear()
_transitioning = false
@ -263,9 +266,38 @@ func _load_distant(map_id: String) -> void:
return
var cost: int = preload("res://scripts/planets/distant_expeditions.gd").fuel_cost(GameState.active_distant_map)
var map_snapshot: Dictionary = GameState.active_distant_map.duplicate(true)
var previous_pause := get_tree().paused
var hud := get_node("HUD/HUDRoot") as CanvasItem
var hud_visible := hud.visible
var player_visible := player.visible
var freighter := get_node_or_null("Freighter") as Node2D
var freighter_visible := freighter.visible if freighter != null else false
var layer := CanvasLayer.new()
layer.name = "FreighterWarpDeparture"
layer.layer = 100
layer.process_mode = Node.PROCESS_MODE_ALWAYS
var departure := Control.new()
departure.set_script(preload("res://scripts/game/freighter_warp_departure.gd"))
departure.destination_name = str(map_snapshot.get("name","ODLEGŁY SEKTOR"))
if freighter != null:
departure.start_position = freighter.get_global_transform_with_canvas().origin
freighter.hide()
player.hide()
hud.hide()
get_tree().paused = true
add_child(layer)
layer.add_child(departure)
await departure.finished
GameState.distant_warp_arrival = true
get_tree().paused = false
var error := get_tree().reload_current_scene()
if error != OK:
GameState.distant_warp_arrival = false
layer.queue_free()
player.visible = player_visible
hud.visible = hud_visible
if freighter != null: freighter.visible = freighter_visible
get_tree().paused = previous_pause
GameState.restore_distant_home()
GameState.captain.expedition.distant_maps[map_id] = map_snapshot
GameState.captain.expedition.deuter += cost
@ -280,6 +312,8 @@ func travel_to_system(level: int) -> bool:
return true
func _load_known_system(level: int) -> void:
var bastion_used := GameState.hull_bastion_used
GameState.hull_bastion_used = false
var previous_level := GameState.system_level
var previous_seed := GameState.system_seed
player.get_node("ShipInventory").preserve_for_restart()
@ -290,6 +324,7 @@ func _load_known_system(level: int) -> void:
var error := get_tree().reload_current_scene()
if error != OK:
GameState.system_level = previous_level
GameState.hull_bastion_used = bastion_used
GameState.system_seed = previous_seed
GameState.restart_inventory.clear()
_transitioning = false

View File

@ -10,6 +10,8 @@ var system_seed: int = 0
var active_distant_map: Dictionary = {}
var distant_home: Dictionary = {}
var return_to_freighter := false
var distant_warp_arrival := false
var hull_bastion_used := false
var highest_system := 1
var system_seeds: Dictionary = {}
var cleared_systems: Dictionary = {}
@ -24,7 +26,8 @@ func depart_distant(map_id: String) -> bool:
var map: Dictionary = captain.expedition.distant_maps[map_id]
var cost: int = preload("res://scripts/planets/distant_expeditions.gd").fuel_cost(map)
if captain.expedition.deuter < cost: return false
distant_home = {"level":system_level,"seed":system_seed}
distant_home = {"level":system_level,"seed":system_seed,"hull_bastion_used":hull_bastion_used}
hull_bastion_used = false
active_distant_map = map.duplicate(true)
captain.expedition.distant_maps.erase(map_id)
captain.expedition.deuter -= cost
@ -37,8 +40,10 @@ func restore_distant_home() -> void:
if distant_home.is_empty(): return
system_level = int(distant_home.level)
system_seed = int(distant_home.seed)
hull_bastion_used = bool(distant_home.get("hull_bastion_used",false))
active_distant_map.clear()
distant_home.clear()
distant_warp_arrival = false
func create_system_seed() -> int:
var rng := RandomNumberGenerator.new()
@ -58,12 +63,14 @@ func reset_current_system_exploration() -> void:
explored_pois.erase(id)
func start_new_run() -> void:
hull_bastion_used = false
captain = CAPTAIN_PROGRESS.new()
system_level = 1
highest_system = 1
active_distant_map.clear()
distant_home.clear()
return_to_freighter = false
distant_warp_arrival = false
system_seeds.clear()
cleared_systems.clear()
_production_signal_time = 0

View File

@ -0,0 +1,40 @@
extends Node2D
var _texture: Texture2D
var _visual_size := 56.0
var _angle := 0.0
var _time := 0.0
var _pieces: Array[Dictionary] = []
func configure(texture: Texture2D, visual_size: float, angle: float) -> void:
_texture = texture
_visual_size = visual_size
_angle = angle
var grid := 3
var cell := Vector2(texture.get_size())/grid
for y in grid:
for x in grid:
var offset := Vector2(x-1,y-1)*visual_size/3.0
var direction := offset.normalized() if offset.length()>0 else Vector2.RIGHT.rotated(randf()*TAU)
_pieces.append({"region":Rect2(Vector2(x,y)*cell,cell),"offset":offset.rotated(angle),"velocity":direction.rotated(angle)*randf_range(75,180),"spin":randf_range(-5,5)})
func _ready() -> void:
z_index = 35
light_mask = 4
func _process(delta: float) -> void:
_time += delta
if _time >= 1.1: queue_free()
queue_redraw()
func _draw() -> void:
if _texture == null: return
var fade := clampf(1.0-_time/1.1,0,1)
for ring in 3:
draw_arc(Vector2.ZERO,_visual_size*0.3+_time*(95+ring*45),0,TAU,48,Color(1,0.45+ring*0.15,0.1,fade*0.4),2,true)
for piece in _pieces:
var center: Vector2 = piece.offset+piece.velocity*_time
draw_set_transform(center,_angle+piece.spin*_time)
var extent := Vector2.ONE*_visual_size/3.0
draw_texture_rect_region(_texture,Rect2(-extent*0.5,extent),piece.region,Color(1.0,0.8,0.6,fade))
draw_line(Vector2.ZERO,Vector2(-8,0),Color(1,0.6,0.12,fade),2,true)
draw_set_transform(Vector2.ZERO)

View File

@ -0,0 +1 @@
uid://2bk23d1djp3e

View File

@ -0,0 +1,20 @@
extends Node2D
var radius := 120.0
var tint := Color("ffaa46")
var endpoint := Vector2.ZERO
var _age := 0.0
func _ready() -> void:
z_index = 26
func _process(delta: float) -> void:
_age += delta
if _age >= 0.5: queue_free()
else: queue_redraw()
func _draw() -> void:
var t := clampf(_age/0.5,0.0,1.0)
var color := Color(tint,1.0-t)
if endpoint != Vector2.ZERO:
draw_line(Vector2.ZERO,endpoint,Color(tint,(1.0-t)*0.2),9.0,true)
draw_line(Vector2.ZERO,endpoint,color,2.0,true)
else:
draw_arc(Vector2.ZERO,radius*t,0,TAU,64,color,3.0,true)
draw_arc(Vector2.ZERO,radius*t*0.8,0,TAU,64,Color(tint,(1.0-t)*0.35),5.0,true)

View File

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

View File

@ -54,6 +54,7 @@ var angular_velocity := 0.0
var _sprite: Sprite2D
var _contact_damage_timer := 0.0
var _is_destroyed := false
var _restart_requested := false
var is_docked := false
var landing_combat_lock := 0.0
var _boost_speed := 0.0
@ -65,6 +66,15 @@ var _mini_boost_exhausted := false
var _mini_boost_coast_speed := 0.0
var _shield_leech_budget := -1.0
var _polarization_budget := -1.0
var _reactive_armor_cooldown := 0.0
var _shield_reserve_cooldown := 0.0
var _shield_reserve_remaining := 0.0
var _salvo_cooldown := 0.0
var _emergency_shield_cooldown := 0.0
var _emergency_shield_remaining := 0.0
var _bulkhead_cooldown := 0.0
var _bulkhead_repair_remaining := 0.0
var _bastion_invulnerability := 0.0
@onready var _shield_visual: Node2D = $ShieldVisual
func _enter_tree() -> void:
@ -80,7 +90,9 @@ func apply_equipment_bonuses(bonuses: Dictionary) -> void:
bonuses = EQUIPMENT_BALANCE.bonuses(bonuses)
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)
if preload("res://scripts/planets/alien_research.gd").learned("hull_b"): max_hull *= 1.1
max_shields = (base_max_shields + float(bonuses.get("max_shields", 0.0))) * (1.0 + float(bonuses.get("shield_percent", 0.0)) / 100.0)
if preload("res://scripts/planets/alien_research.gd").learned("shields_b"): max_shields *= 1.1
scanner_range = base_scanner_range + float(bonuses.get("scanner_range", 0.0))
max_energy = base_max_energy + float(bonuses.get("max_energy", 0.0))
max_speed = EQUIPMENT_BALANCE.speed(base_max_speed,float(bonuses.get("max_speed",0.0)),float(bonuses.get("speed_percent",0.0)))
@ -158,11 +170,18 @@ 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_enemy_weapon_hit(amount: float, critical: bool, damage_type: int, source: Node) -> void:
take_damage(amount,critical,damage_type,source)
func get_weapon_energy_cost(amount: float) -> float:
return amount*0.5 if _shield_reserve_remaining > 0.0 else amount
func take_damage(amount: float, _critical: bool = false, damage_type: int = DamageTypes.Type.KINETIC, source: Node = null) -> void:
if amount > 0 and not is_docked:
landing_combat_lock = 6.0
if _is_destroyed or is_docked or amount <= 0.0:
return
if _bastion_invulnerability > 0.0: return
var hit := COMBAT_ROLLS.incoming_hit(amount * (1.0 - absorption[damage_type] / 100.0), get_evasion_chance(), get_damage_reduction())
if hit["evaded"]:
var flash := Node2D.new()
@ -178,6 +197,15 @@ func take_damage(amount: float, _critical: bool = false, damage_type: int = Dama
get_parent().add_child(popup)
return
var remaining_damage: float = hit["damage"]
var research := preload("res://scripts/planets/alien_research.gd")
var shields_before := shields
if shields > 0.0:
if research.learned("shields_b_next_next") and _salvo_cooldown <= 0.0:
remaining_damage *= 0.1
_salvo_cooldown = 60.0
_hull_technology_feedback("ROZPROSZENIE SALWY")
elif _emergency_shield_remaining > 0.0:
remaining_damage *= 0.5
var shield_damage := minf(shields, remaining_damage)
shields -= shield_damage
if shield_damage > 0.0:
@ -188,13 +216,70 @@ func take_damage(amount: float, _critical: bool = false, damage_type: int = Dama
energy += recovered
_polarization_budget -= recovered
remaining_damage -= shield_damage
if shields_before >= max_shields*0.3 and shields < max_shields*0.3 and research.learned("shields_b_next") and _shield_reserve_cooldown <= 0.0:
_shield_reserve_cooldown = 15.0
_shield_reserve_remaining = 2.0
_hull_technology_feedback("REZERWA KONDENSATORÓW")
if shields_before > 0.0 and shields <= 0.0 and research.learned("shields_b_next_next_next") and _emergency_shield_cooldown <= 0.0 and not GameState.captain.is_skill_active("power_redirect"):
shields = max_shields*0.25
_emergency_shield_cooldown = 30.0
_emergency_shield_remaining = 2.0
_hull_technology_feedback("TARCZA AWARYJNA")
if shield_damage > 0.0 and research.learned("shields_a_next_next_next") and is_instance_valid(source) and source.is_in_group("enemies") and not bool(source.get("virus_controlled")) and float(source.get("hull")) > 0.0:
var effect := Node2D.new()
effect.set_script(preload("res://scripts/game/shield_research_effect.gd"))
effect.position = global_position
effect.endpoint = source.global_position-global_position
get_parent().add_child(effect)
source.take_damage(shield_damage*0.2,false,DamageTypes.Type.ENERGY)
if remaining_damage > 0.0 and research.learned("hull_b_next") and _reactive_armor_cooldown <= 0.0:
remaining_damage *= 0.3
_reactive_armor_cooldown = 15.0
_hull_technology_feedback("PANCERZ REAKTYWNY")
var hull_before := hull
hull = maxf(0.0, hull - remaining_damage)
if hull <= 0.0 and research.learned("hull_b_next_next_next") and not GameState.hull_bastion_used:
hull = 1.0
GameState.hull_bastion_used = true
_bastion_invulnerability = 2.0
_hull_technology_feedback("OSTATNI BASTION")
if hull > 0.0 and hull_before >= max_hull*0.3 and hull < max_hull*0.3 and research.learned("hull_b_next_next") and _bulkhead_cooldown <= 0.0:
_bulkhead_cooldown = 45.0
_bulkhead_repair_remaining = 5.0
_hull_technology_feedback("AWARYJNE GRODZIE")
if shield_damage > 0.0 and shields > 0.0:
_shield_visual.call("flash_hit")
_update_shield_visual()
if hull <= 0.0:
_destroy_ship()
func tick_hull_technologies(delta: float) -> void:
if _is_destroyed or is_docked: return
_shield_reserve_cooldown = maxf(0.0,_shield_reserve_cooldown-delta)
_shield_reserve_remaining = maxf(0.0,_shield_reserve_remaining-delta)
_salvo_cooldown = maxf(0.0,_salvo_cooldown-delta)
_emergency_shield_cooldown = maxf(0.0,_emergency_shield_cooldown-delta)
_emergency_shield_remaining = maxf(0.0,_emergency_shield_remaining-delta)
_reactive_armor_cooldown = maxf(0.0,_reactive_armor_cooldown-delta)
_bulkhead_cooldown = maxf(0.0,_bulkhead_cooldown-delta)
_bastion_invulnerability = maxf(0.0,_bastion_invulnerability-delta)
if _bulkhead_repair_remaining > 0.0:
var step := minf(delta,_bulkhead_repair_remaining)
_bulkhead_repair_remaining = maxf(0.0,_bulkhead_repair_remaining-delta)
hull = minf(max_hull,hull+max_hull*0.03*step)
func _hull_technology_feedback(text: String) -> void:
var popup := DAMAGE_POPUP_SCENE.instantiate() as Label
popup.text = text
popup.set("text_color",Color("79efb2"))
popup.position = position+Vector2(-60,-48)
get_parent().add_child(popup)
var flash := Node2D.new()
flash.set_script(COMBAT_FLASH)
flash.set("flash_color",Color("79efb2"))
flash.position = position
get_parent().add_child(flash)
func restore_shields_from_damage(actual_damage: float) -> void:
if _is_destroyed or actual_damage <= 0.0 or shield_on_damage_percent <= 0.0 or GameState.captain.is_skill_active("power_redirect"):
return
@ -337,10 +422,35 @@ func _show_defeat_screen() -> void:
get_parent().add_child(overlay)
func _restart_level_after_destruction() -> void:
if is_inside_tree():
$ShipInventory.preserve_for_restart()
if not is_inside_tree() or _restart_requested: return
_restart_requested = true
_reload_after_destruction.call_deferred()
func _reload_after_destruction() -> void:
var bastion_used := GameState.hull_bastion_used
var active: Dictionary = GameState.active_distant_map.duplicate(true)
var home: Dictionary = GameState.distant_home.duplicate(true)
var level := GameState.system_level
var seed_value := GameState.system_seed
$ShipInventory.preserve_for_restart()
if not active.is_empty():
GameState.restore_distant_home()
GameState.return_to_freighter = true
else:
GameState.reset_current_system_exploration()
get_tree().reload_current_scene.call_deferred()
var error := get_tree().reload_current_scene()
if error != OK:
GameState.hull_bastion_used = bastion_used
GameState.active_distant_map = active
GameState.distant_home = home
GameState.system_level = level
GameState.system_seed = seed_value
GameState.return_to_freighter = false
GameState.restart_inventory.clear()
_restart_requested = false
var overlay := get_parent().get_node_or_null("DefeatOverlay")
if overlay != null: overlay.set("_retrying",false)
push_error("Could not recover after destruction: %s" % error_string(error))
func _physics_process(delta: float) -> void:
landing_combat_lock = maxf(0, landing_combat_lock - delta)
@ -353,6 +463,7 @@ func _physics_process(delta: float) -> void:
_mini_boost_active = false
return
_refill_recovery_budgets(delta)
tick_hull_technologies(delta)
_contact_damage_timer = maxf(0.0, _contact_damage_timer - delta)
energy = minf(max_energy, energy + get_energy_regeneration() * delta)
var was_mini_boosting := _mini_boost_active

View File

@ -16,6 +16,7 @@ var _fire_just_pressed := [false, false, false]
var _drag_fire_blocked := false
var missile_cooldown_remaining := 0.0
var missile_cooldown_duration := 5.0
var _last_hit_targets: Dictionary = {}
func _ready() -> void:
_ensure_input_action(&"ship_fire_primary", MOUSE_BUTTON_LEFT)
@ -175,7 +176,7 @@ func _exit_tree() -> void:
func _fire_laser(slot_index: int, item: ItemInstance) -> void:
var definition := item.definition
var line := _laser_lines[slot_index]
if _shot_timers[slot_index] > 0.0 or float(ship.get("energy")) < item.get_energy_cost():
if _shot_timers[slot_index] > 0.0 or float(ship.get("energy")) < _effective_energy_cost(item.get_energy_cost()):
return
var muzzle_position := _muzzle_position()
var direction := _aim_direction_from(muzzle_position)
@ -200,8 +201,24 @@ func _fire_laser(slot_index: int, item: ItemInstance) -> void:
_break_cloak_on_fire()
if not result.is_empty():
var hit: Dictionary = ship.call("roll_weapon_hit", item)
_apply_damage(result.get("collider"), float(hit["damage"]), bool(hit["critical"]), item.shield_piercing(), item.get_damage_type())
var target: Node = result.get("collider")
var hull_before := float(target.get("hull")) if target.has_method("take_research_weapon_hit") else -1.0
_apply_damage(target, float(hit["damage"]), bool(hit["critical"]), item.shield_piercing(), item.get_damage_type())
_spawn_laser_impact(endpoint)
if hull_before >= 0 and float(target.get("hull")) < hull_before and preload("res://scripts/planets/alien_research.gd").learned("hull_a_next_next"):
var visited := _last_hit_targets.duplicate()
visited[target.get_instance_id()] = true
var exclusions: Array[RID] = [ship.get_rid(),target.get_rid()]
for hit_id in visited:
var prior := instance_from_id(hit_id) as CollisionObject2D
if is_instance_valid(prior) and prior.get_rid() not in exclusions: exclusions.append(prior.get_rid())
var through := PhysicsRayQueryParameters2D.create(endpoint+direction*2.0,muzzle_position+direction*definition.weapon_range,RAY_COLLISION_MASK,exclusions)
var second := ship.get_world_2d().direct_space_state.intersect_ray(through)
var second_end: Vector2 = second.get("position",muzzle_position+direction*definition.weapon_range)
line.add_point(second_end)
if not second.is_empty():
preload("res://scripts/game/alien_weapon_effects.gd").apply_hit(second.get("collider"),float(hit.damage)*0.5,bool(hit.critical),item.get_damage_type(),item.shield_piercing(),visited,false,-1,0.5)
_spawn_laser_impact(second_end)
_laser_pulse_timers[slot_index] = definition.laser_pulse_duration
_shot_timers[slot_index] = item.get_fire_interval()
@ -227,14 +244,19 @@ func _consume_shot_resource(item: ItemInstance) -> bool:
return _consume_energy(item.get_energy_cost())
func _consume_energy(amount: float) -> bool:
amount = _effective_energy_cost(amount)
var current_energy := float(ship.get("energy"))
if current_energy < amount:
return false
ship.set("energy", current_energy - amount)
return true
func _effective_energy_cost(amount: float) -> float:
return float(ship.get_weapon_energy_cost(amount)) if ship.has_method("get_weapon_energy_cost") else amount
func _apply_damage(target: Variant, damage: float, critical: bool = false, piercing: float = 0.0, damage_type: int = DamageTypes.Type.ENERGY) -> void:
preload("res://scripts/game/alien_weapon_effects.gd").apply_hit(target,damage,critical,damage_type,piercing,{})
_last_hit_targets = {}
preload("res://scripts/game/alien_weapon_effects.gd").apply_hit(target,damage,critical,damage_type,piercing,_last_hit_targets)
func _spawn_laser_impact(at_position: Vector2) -> void:
var effect := IMPACT_EFFECT_SCENE.instantiate() as Node2D

View File

@ -36,6 +36,9 @@ var source_ship: Node2D
var research_disabled_system := -2
var _interception_checked := false
var _harmless_intercepted := false
var _overpenetration_used := false
var _pierced_ship: Node
var _research_damage_scale := 1.0
func get_damage_type() -> int:
return damage_type_override if damage_type_override >= 0 else DamageTypes.for_weapon(weapon_kind)
@ -125,6 +128,10 @@ func _physics_process(delta: float) -> void:
var next_position := previous_position + displacement
var exclusions: Array[RID] = [get_rid()]
if is_instance_valid(source_ship) and source_ship is CollisionObject2D: exclusions.append(source_ship.get_rid())
if is_instance_valid(_pierced_ship) and _pierced_ship is CollisionObject2D: exclusions.append(_pierced_ship.get_rid())
for hit_id in _already_hit:
var prior := instance_from_id(hit_id) as CollisionObject2D
if is_instance_valid(prior) and prior.get_rid() not in exclusions: exclusions.append(prior.get_rid())
var query := PhysicsRayQueryParameters2D.create(
previous_position,
next_position,
@ -266,6 +273,7 @@ func _on_area_entered(area: Area2D) -> void:
func _hit_target(target: Node) -> void:
if target == source_ship: return
if target == _pierced_ship or _already_hit.has(target.get_instance_id()): return
if enemy_shot and weapon_kind == ItemDefinition.WeaponKind.MISSILE and target is Node2D and target.name == "PlayerShip":
if try_intercept(target): return
if _impact_handled:
@ -276,12 +284,32 @@ func _hit_target(target: Node) -> void:
if enemy_shot and target.is_in_group("asteroids"):
queue_free()
return
if weapon_kind == ItemDefinition.WeaponKind.MISSILE:
var hull_before := float(target.get("hull")) if target.has_method("take_research_weapon_hit") else -1.0
if weapon_kind == ItemDefinition.WeaponKind.MISSILE and not _overpenetration_used:
_explode_at_target(target)
else:
_apply_damage_once(target)
if not enemy_shot and not _overpenetration_used and hull_before >= 0 and float(target.get("hull")) < hull_before and preload("res://scripts/planets/alien_research.gd").learned("hull_a_next_next"):
_spawn_overpenetration(target)
queue_free()
func _spawn_overpenetration(target: Node) -> void:
var remaining_range := maxf(0.0,max_range-_distance_travelled)
if remaining_range < 4.0: return
var shot := (load("res://scenes/game/weapon_projectile.tscn") as PackedScene).instantiate()
shot.configure(global_position+direction*2.0,direction,weapon_kind,projectile_speed,damage*0.5,remaining_range,null,is_critical)
shot.damage_type_override = damage_type_override
shot.shield_piercing = shield_piercing
shot.collision_mask = collision_mask
shot.source_ship = source_ship
shot.missile_homing = false
shot._overpenetration_used = true
shot._pierced_ship = target
shot._already_hit = _already_hit.duplicate()
shot._research_damage_scale = 0.5
shot.research_disabled_system = -1
get_parent().call_deferred("add_child",shot)
func _spawn_impact_effect(is_explosion: bool) -> void:
var effect := IMPACT_EFFECT_SCENE.instantiate() as Node2D
effect.global_position = global_position
@ -320,13 +348,16 @@ func _apply_damage_once(target: Variant) -> void:
return
if not enemy_shot:
if research_disabled_system == -2: research_disabled_system = preload("res://scripts/game/alien_weapon_effects.gd").roll_disabled_system()
preload("res://scripts/game/alien_weapon_effects.gd").apply_hit(target,damage,is_critical,get_damage_type(),shield_piercing,_already_hit,weapon_kind != ItemDefinition.WeaponKind.MISSILE,research_disabled_system)
preload("res://scripts/game/alien_weapon_effects.gd").apply_hit(target,damage,is_critical,get_damage_type(),shield_piercing,_already_hit,not _overpenetration_used and weapon_kind != ItemDefinition.WeaponKind.MISSILE,research_disabled_system,_research_damage_scale)
if weapon_kind == ItemDefinition.WeaponKind.GAMMA and get_damage_type() == DamageTypes.Type.RADIATION and not target.is_queued_for_deletion():
preload("res://scripts/game/radiation_status.gd").apply(target,damage*0.25,true,shield_piercing)
return
_already_hit[instance_id] = true
if enemy_shot and target is CharacterBody2D and target.has_method("take_damage"):
target.call("take_damage", damage, is_critical, get_damage_type())
if target.has_method("take_enemy_weapon_hit"):
target.call("take_enemy_weapon_hit",damage,is_critical,get_damage_type(),source_ship)
else:
target.call("take_damage", damage, is_critical, get_damage_type())
elif target.has_method("take_piercing_weapon_hit"):
target.call("take_piercing_weapon_hit", damage, is_critical, get_damage_type(), shield_piercing)
elif target.has_method("take_weapon_hit"):

View File

@ -465,6 +465,8 @@ func _comparison_stats(bonuses: Dictionary) -> Dictionary:
var value := (float(ship.get(data[1])) + float(bonuses.get(data[0], 0.0))) * (1.0 + float(bonuses.get(data[2], 0.0)) / 100.0)
stats[data[0]] = {"value": value, "name": data[3], "unit": data[4]}
stats.max_speed.value = minf(240.0,float(stats.max_speed.value))
if preload("res://scripts/planets/alien_research.gd").learned("hull_b"): stats.max_hull.value *= 1.1
if preload("res://scripts/planets/alien_research.gd").learned("shields_b"): stats.max_shields.value *= 1.1
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 balance := preload("res://scripts/items/equipment_balance.gd")

View File

@ -1,8 +1,11 @@
extends RefCounted
const ACTIVE_IDS := ["general", "weapons", "hull", "shields", "weapons_a", "weapons_a_next", "weapons_a_next_next", "weapons_a_next_next_next", "weapons_b", "weapons_b_next", "weapons_b_next_next", "weapons_b_next_next_next"]
const ACTIVE_IDS := ["general", "weapons", "hull", "shields", "weapons_a", "weapons_a_next", "weapons_a_next_next", "weapons_a_next_next_next", "weapons_b", "weapons_b_next", "weapons_b_next_next", "weapons_b_next_next_next", "hull_a", "hull_a_next", "hull_a_next_next", "hull_a_next_next_next", "hull_b", "hull_b_next", "hull_b_next_next", "hull_b_next_next_next", "shields_a", "shields_a_next", "shields_a_next_next", "shields_a_next_next_next", "shields_b", "shields_b_next", "shields_b_next_next", "shields_b_next_next_next"]
const BRANCHES := ["weapons", "hull", "shields"]
const SYSTEM_DISABLE_DURATION := 5.0
const TITLES := {"general":"Ogólna wiedza", "weapons":"Wiedza o broni", "hull":"Wiedza o kadłubie", "shields":"Wiedza o tarczach", "weapons_a":"Słaby punkt", "weapons_a_next":"Celny strzał", "weapons_a_next_next":"Rykoszet", "weapons_a_next_next_next":"Strzał w 10", "weapons_b":"Manewr wyprzedzający", "weapons_b_next":"System obrony przed obcymi", "weapons_b_next_next":"Dezorientacja celowania", "weapons_b_next_next_next":"Zakłócenia systemów obcych"}
const TITLES := {"hull_a":"Analiza konstrukcji", "hull_a_next":"Pęknięcie poszycia", "hull_a_next_next":"Przestrzał", "hull_a_next_next_next":"Rozpad konstrukcji", "hull_b":"Wzmocnione poszycie", "hull_b_next":"Pancerz reaktywny", "hull_b_next_next":"Awaryjne grodzie", "hull_b_next_next_next":"Ostatni bastion", "general":"Ogólna wiedza", "weapons":"Wiedza o broni", "hull":"Wiedza o kadłubie", "shields":"Wiedza o tarczach", "weapons_a":"Słaby punkt", "weapons_a_next":"Celny strzał", "weapons_a_next_next":"Rykoszet", "weapons_a_next_next_next":"Strzał w 10", "weapons_b":"Manewr wyprzedzający", "weapons_b_next":"System obrony przed obcymi", "weapons_b_next_next":"Dezorientacja celowania", "weapons_b_next_next_next":"Zakłócenia systemów obcych"}
const SHIELD_IDS := ["shields_a", "shields_a_next", "shields_a_next_next", "shields_a_next_next_next", "shields_b", "shields_b_next", "shields_b_next_next", "shields_b_next_next_next"]
const SHIELD_TITLES := ["Analiza częstotliwości", "Blokada regeneracji", "Przeciążenie osłon", "Odwrócona polaryzacja", "Pojemne kondensatory", "Rezerwa kondensatorów", "Rozproszenie salwy", "Tarcza awaryjna"]
static func learned(id: String) -> bool:
return id in ACTIVE_IDS and bool(GameState.captain.expedition.alien_skills.get(id,false))
@ -19,14 +22,31 @@ static func cost(id: String) -> int:
return 10 + depth * 5
static func title(id: String) -> String:
if id in SHIELD_IDS: return SHIELD_TITLES[SHIELD_IDS.find(id)]
return str(TITLES.get(id, "Pusty slot"))
static func description(id: String) -> String:
match id:
"shields_a": return "Dodatkowe 5% obrażeń tarczom obcych. Sumuje się z Wiedzą o tarczach."
"shields_a_next": return "Trafienie tarczy obcego blokuje regenerację na 5 s. Kolejne trafienia odświeżają czas."
"shields_a_next_next": return "Zbicie tarczy obcego razi pozostałych obcych w promieniu 120 px za 400% obrażeń trafienia, które zbiło tarczę. Bez reakcji łańcuchowych. Ten sam wróg wywołuje impuls najwyżej raz na 10 s."
"shields_a_next_next_next": return "Obcy trafiający twoją aktywną tarczę otrzymuje wyładowanie równe 20% obrażeń faktycznie pochłoniętych przez osłonę. Bez kolejnych efektów trafienia."
"shields_b": return "Zwiększa maksymalne tarcze o 10%. Zakup nie uzupełnia osłon."
"shields_b_next": return "Spadek tarcz z co najmniej 30% poniżej tego progu zmniejsza koszt energii broni o 50% przez 2 s. Odnowienie: 15 s."
"shields_b_next_next": return "Pierwsze trafienie w aktywną tarczę zadaje tylko 10% obrażeń. Odnowienie: 60 s. Redukcja obejmuje też przebicie tego trafienia na kadłub."
"shields_b_next_next_next": return "Po zbiciu tarcz natychmiast odzyskujesz 25% ich maksimum. Przez 2 s obrażenia kierowane w tarczę są zmniejszone o 50%. Odnowienie: 30 s. Nie sumuje redukcji z Rozproszeniem salwy; działa silniejsza. Dokowanie nie resetuje odnowienia."
"general": return "Ujawnia paski HP i tarcz wrogów."
"weapons": return "Broń obcych zadaje o 10% mniej obrażeń."
"hull": return "Twoja broń zadaje o 10% więcej obrażeń kadłubom obcych."
"shields": return "Twoja broń zadaje o 10% więcej obrażeń tarczom obcych."
"hull_a": return "Twoja broń zadaje dodatkowe 5% obrażeń kadłubom obcych. Sumuje się z Wiedzą o kadłubie."
"hull_a_next": return "Bezpośrednie trafienie odsłoniętego kadłuba otwiera wyrwę na 5 s. Kolejne twoje trafienia ignorują połowę absorpcji tego statku. Wyrwy nie sumują się."
"hull_a_next_next": return "Po uszkodzeniu kadłuba pocisk lub laser przebija jeden dodatkowy cel za 50% obrażeń. Rakieta wypuszcza dodatkowy pocisk penetrujący bez kolejnej eksplozji. Bez łańcuchowych przestrzałów i dodatkowego kosztu strzału."
"hull_a_next_next_next": return "Bezpośrednie trafienie kadłuba dobija zwykłego obcego przy maksymalnie 8% kadłuba, a bossa przy 2%. Statek rozpada się na fragmenty."
"hull_b": return "Zwiększa maksymalny kadłub twojego statku o 10%. Zakup nie odnawia utraconego kadłuba."
"hull_b_next": return "Pierwsze trafienie sięgające kadłuba po utracie tarcz zadaje kadłubowi o 70% mniej obrażeń. Odnowienie: 15 s aktywnego lotu."
"hull_b_next_next": return "Spadek kadłuba z co najmniej 30% poniżej tego progu uruchamia naprawę 15% maksymalnego kadłuba przez 5 s. Odnowienie: 45 s. Trafienia nie przerywają naprawy."
"hull_b_next_next_next": return "Śmiertelne trafienie zostawia 1 punkt kadłuba i daje 2 s niewrażliwości. Raz na wyprawę lub wejście do zwykłego układu. Dokowanie ani restart po porażce nie odnawiają efektu."
"weapons_a": return "Każde bezpośrednie trafienie twojej broni zadaje obcym dodatkowe 5 obrażeń przed absorpcją."
"weapons_a_next": return "Każdy strzał ma 1% szansy wyłączyć na 5 sekund losowy system obcego: silnik, broń lub tarcze. Czerwona ikona nad wrogiem wskazuje wyłączony system. Obrażenia okresowe nie losują ponownie."
"weapons_a_next_next": return "Trafienie dowolną bronią razi też obcych do 10 px od obrysu trafionego statku. Promień eksplozji twoich rakiet rośnie o 10 px. Bez łańcuchowych rykoszetów."

View File

@ -632,6 +632,7 @@ func _build_alien_research() -> void:
_content.add_child(tree)
func _research_completed() -> void:
if is_instance_valid(inventory): inventory._recalculate_ship_stats()
_audio.service_completed()
captain.emit_changed()
_refresh()

View File

@ -38,6 +38,8 @@ func _ready() -> void:
contents.add_child(title)
var note := Label.new()
note.text = "Układ %d\nWyposażenie i zapasy zostają zachowane." % GameState.system_level
if not GameState.active_distant_map.is_empty():
note.text = "Wyprawa zakończona. Mapa została zużyta.\nPowrót do układu %d.\nWyposażenie i zapasy zostają zachowane." % int(GameState.distant_home.get("level",1))
note.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
note.add_theme_font_size_override("font_size", 11)
note.add_theme_color_override("font_color", Color("a6bdce"))
@ -45,6 +47,8 @@ func _ready() -> void:
var retry := Button.new()
retry.name = "RetryButton"
retry.text = "SPRÓBUJ PONOWNIE [R]"
if not GameState.active_distant_map.is_empty():
retry.text = "WRÓĆ DO UKŁADU %d [R]" % int(GameState.distant_home.get("level",1))
preload("res://scripts/ui/hud_button_style.gd").apply(retry)
var key := InputEventKey.new()
key.physical_keycode = KEY_R

View File

@ -155,12 +155,25 @@ func _refresh_values() -> void:
func _parameters() -> void:
var card := _section("01 / PANCERZ I OCHRONA", DEFENSE)
_metric(card, "hull", "Kadłub", func(): return "%.0f / %.0f" % [player.hull,player.max_hull], DEFENSE)
var hull_research := preload("res://scripts/planets/alien_research.gd")
if hull_research.learned("hull_b_next"):
_metric(card,"reactive_armor","Pancerz reaktywny",func(): return "%.1f s" % player._reactive_armor_cooldown if player._reactive_armor_cooldown > 0 else "GOTOWY",DEFENSE)
if hull_research.learned("hull_b_next_next"):
_metric(card,"bulkheads","Awaryjne grodzie",func(): return "NAPRAWA" if player._bulkhead_repair_remaining > 0 else ("%.1f s" % player._bulkhead_cooldown if player._bulkhead_cooldown > 0 else "GOTOWE"),DEFENSE)
if hull_research.learned("hull_b_next_next_next"):
_metric(card,"last_bastion","Ostatni bastion",func(): return "ZUŻYTY" if GameState.hull_bastion_used else "GOTOWY",DEFENSE)
_metric(card, "reduction", "Redukcja obrażeń", func(): return "%.1f%%" % (player.get_damage_reduction()*100), DEFENSE, "Uwzględnia mechanika i aktywny efekt pancerza z tytanu.")
_metric(card, "evasion", "Szansa na unik", func(): return "%.1f%%" % (player.get_evasion_chance()*100), DEFENSE)
for index in range(3):
_metric(card, DamageTypes.KEYS[index], "Absorpcja: " + DamageTypes.NAMES[index].to_lower(), func(): return "%.1f%%" % player.absorption[index], DEFENSE)
card = _section("02 / TARCZE I ENERGIA", ENERGY)
_metric(card, "shields", "Tarcze", func(): return "%.0f / %.0f" % [player.shields,player.max_shields], CYAN)
if preload("res://scripts/planets/alien_research.gd").learned("shields_b_next"):
_metric(card,"shield_reserve","Rezerwa kondensatorów",func(): return "AKTYWNA %.1f s" % player._shield_reserve_remaining if player._shield_reserve_remaining > 0 else ("%.1f s" % player._shield_reserve_cooldown if player._shield_reserve_cooldown > 0 else "GOTOWA"),CYAN)
if preload("res://scripts/planets/alien_research.gd").learned("shields_b_next_next"):
_metric(card,"salvo","Rozproszenie salwy",func(): return "%.1f s" % player._salvo_cooldown if player._salvo_cooldown > 0 else "GOTOWE",CYAN)
if preload("res://scripts/planets/alien_research.gd").learned("shields_b_next_next_next"):
_metric(card,"emergency_shield","Tarcza awaryjna",func(): return "AKTYWNA %.1f s" % player._emergency_shield_remaining if player._emergency_shield_remaining > 0 else ("%.1f s" % player._emergency_shield_cooldown if player._emergency_shield_cooldown > 0 else "GOTOWA"),CYAN)
_metric(card, "shield_regeneration", "Regeneracja tarcz", func(): return "%.1f / s" % player.shield_regeneration, CYAN, "Do 3% maksymalnych tarcz na sekundę.")
_metric(card, "shield_on_damage", "Odzysk tarcz z obrażeń", func(): return "%.1f%%" % player.shield_on_damage_percent, CYAN, "Łącznie do 10% faktycznie zadanych obrażeń. Tempo: do 5% maksymalnych tarcz/s, z rezerwą na jedno takie odnowienie.")
_metric(card, "energy", "Energia", func(): return "%.0f / %.0f" % [player.energy,player.max_energy], ENERGY)
@ -189,7 +202,7 @@ func _parameters() -> void:
_metric(card, "luck", "Szansa szczęścia", func(): return "%.1f%%" % (player.get_luck_chance()*100), GOLD)
var research := preload("res://scripts/planets/alien_research.gd")
for branch in research.BRANCHES:
_metric(card, "research_" + branch, {"weapons":"Osłabienie broni obcych","hull":"Obrażenia kadłubów obcych","shields":"Obrażenia tarcz obcych"}[branch], func(): return "%.0f%%" % (research.bonus(GameState.captain.expedition,branch)*100), ENERGY)
_metric(card, "research_" + branch, {"weapons":"Osłabienie broni obcych","hull":"Obrażenia kadłubów obcych","shields":"Obrażenia tarcz obcych"}[branch], func(): return "%.0f%%" % ((research.bonus(GameState.captain.expedition,branch)+(0.05 if branch=="hull" and research.learned("hull_a") else (0.05 if branch=="shields" and research.learned("shields_a") else 0.0)))*100), ENERGY)
func _weapons() -> void:
for index in range(inventory.WEAPON_SLOT_COUNT):
@ -205,7 +218,7 @@ func _weapons() -> void:
_metric(card, "weapon_dps_%d" % index, "DPS", func(): return "%.1f" % inventory.get_weapon_dps(index), GOLD)
_metric(card, "weapon_damage_%d" % index, "Obrażenia trafienia", func(): return "%.1f – %.1f" % [item.get_damage_range().x*player.get_weapon_damage_multiplier(), item.get_damage_range().y*player.get_weapon_damage_multiplier()], GOLD, "Zakres przed krytykiem i obroną przeciwnika.")
_metric(card, "weapon_interval_%d" % index, "Odstęp między strzałami", func(): return "%.2f s" % (inventory.get_missile_cooldown(item) if item.definition.weapon_kind == ItemDefinition.WeaponKind.MISSILE else item.get_fire_interval()))
_metric(card, "weapon_energy_%d" % index, "Energia na strzał", func(): return "%.1f" % item.get_energy_cost(), ENERGY)
_metric(card, "weapon_energy_%d" % index, "Energia na strzał", func(): return "%.1f" % player.get_weapon_energy_cost(item.get_energy_cost()), ENERGY)
_metric(card, "weapon_range_%d" % index, "Zasięg", func(): return "%.0f px" % item.definition.weapon_range)
UI.label(card,"Typ: " + DamageTypes.NAMES[item.get_damage_type()],12,UI.MUTED)
var info := _section("ODCZYT PARAMETRÓW", CYAN)

View File

@ -0,0 +1,84 @@
extends Node
var is_runner := false
func _ready() -> void:
if not is_runner:
var runner = get_script().new()
runner.is_runner = true
get_tree().root.add_child.call_deferred(runner)
else:
_run.call_deferred()
func frames(count := 5) -> void:
for frame in count: await get_tree().process_frame
func freeze(game: Node) -> void:
game.set_physics_process(false)
game.player.set_physics_process(false)
game.player.get_node("WeaponController").set_physics_process(false)
for enemy in get_tree().get_nodes_in_group("enemies"): enemy.set_physics_process(false)
func _run() -> void:
GameState.start_new_run()
GameState.system_level = 7
GameState.system_seed = 777
GameState.remember_system()
GameState.hull_bastion_used = true
var progress: Resource = GameState.captain.expedition
progress.distant_maps["defeat_test"] = {"id":"defeat_test","level":125,"seed":123456,"name":"Sektor testowy","source_planet":"Test","source_level":120}
progress.deuter = 130000
assert(GameState.depart_distant("defeat_test"))
assert(not GameState.hull_bastion_used,"New expedition receives its own Last Bastion use")
GameState.hull_bastion_used = true
assert(not progress.distant_maps.has("defeat_test") and progress.deuter==5000)
get_tree().change_scene_to_file("res://scenes/game/game.tscn")
await frames()
var game := get_tree().current_scene
freeze(game)
var inventory: ShipInventory = game.player.get_node("ShipInventory")
var weapon: ItemInstance = inventory.weapon_slots[0]
weapon.special_orbs.append("piercing")
inventory.ammunition = 123
progress.distant_maps["found_on_expedition"] = {"id":"found_on_expedition","level":130,"seed":98765,"name":"Zdobyta mapa"}
GameState.explored_pois["777:kept"] = true
game.player._destroy_ship()
var explosion: Node = game.get_node("PlayerDestructionEffect")
explosion.set_process(false)
explosion._process(6.0)
assert(game.has_node("DefeatOverlay"))
var overlay: Node = game.get_node("DefeatOverlay")
var retry: Button = overlay.find_child("RetryButton",true,false)
assert(retry.text.contains("UKŁADU 7") and not retry.text.contains("PONOWNIE"))
overlay._retry()
overlay._retry()
await frames(10)
game = get_tree().current_scene
freeze(game)
assert(GameState.system_level==7 and GameState.system_seed==777)
assert(GameState.active_distant_map.is_empty() and GameState.distant_home.is_empty())
assert(GameState.hull_bastion_used,"Return restores the home's spent Last Bastion use")
assert(not progress.distant_maps.has("defeat_test") and progress.distant_maps.has("found_on_expedition"))
assert(progress.deuter==5000,"Defeat cannot refund flight fuel")
assert(not GameState.depart_distant("defeat_test"),"Consumed expedition cannot be retried")
assert(GameState.explored_pois.has("777:kept"),"Failed expedition must not reset home exploration")
assert(game.has_node("Freighter") and not game.has_node("DistantSector"))
inventory = game.player.get_node("ShipInventory")
assert(inventory.ammunition==123 and "piercing" in inventory.weapon_slots[0].special_orbs)
assert(not game.player._is_destroyed)
assert(game.player.is_docked and is_instance_valid(game.get_node("PlanetExpeditions").screen))
game.get_node("PlanetExpeditions")._leave()
GameState.explored_pois["777:regular_retry"] = true
game.player._destroy_ship()
explosion = game.get_node("PlayerDestructionEffect")
explosion.set_process(false)
explosion._process(6.0)
game.get_node("DefeatOverlay")._retry()
await frames(10)
game = get_tree().current_scene
freeze(game)
assert(GameState.system_level==7 and GameState.system_seed==777)
assert(GameState.asteroid_retry_seeds.has(777) and not GameState.explored_pois.has("777:regular_retry"))
assert(not game.player.is_docked and not get_tree().paused,"Normal-system defeat still retries the current system")
assert(GameState.hull_bastion_used,"Retry cannot renew Last Bastion")
assert(not progress.distant_maps.has("defeat_test") and progress.deuter==5000)
print("DISTANT_DEFEAT_RETURN: PASS (original system and seed, consumed map/fuel, preserved inventory and found maps, freighter docking, duplicate retry guarded)")

View File

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

View File

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

View File

@ -75,6 +75,12 @@ func _run() -> void:
check(not player.has_free_mini_boost(),"Unequipped engine no effect")
inventory.module_slots[2] = engine
check(game.travel_to_distant("integration"),"Flight requested")
await frames(3)
check(game.has_node("FreighterWarpDeparture") and get_tree().paused,"Departure cinematic freezes combat")
check(not game.travel_to_distant("integration"),"Cannot pay again during warp")
var warp_deadline := Time.get_ticks_msec()+7000
while get_tree().current_scene == game and Time.get_ticks_msec()<warp_deadline: await get_tree().process_frame
check(get_tree().current_scene != game and not get_tree().paused,"Warp completes and resumes gameplay")
await frames(8)
game = get_tree().current_scene
freeze_game(game)

View File

@ -0,0 +1,15 @@
extends Control
func _ready() -> void:
var effect := preload("res://scripts/game/freighter_warp_departure.gd").new()
effect.start_position = Vector2(640,440)
effect.destination_name = "Odległy sektor 120"
add_child(effect)
effect.set_process(false)
for phase in [["charge",0.65],["acceleration",2.35],["entry",2.95],["flash",3.15]]:
effect.elapsed = phase[1]
effect._update_frame()
await get_tree().process_frame
await RenderingServer.frame_post_draw
get_viewport().get_texture().get_image().save_png("res://artifacts/freighter_warp_%s.png" % phase[0])
print("FREIGHTER_WARP_PREVIEW_OK: charge, acceleration, entry and flash captured")

View File

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

View File

@ -0,0 +1,10 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://tests/previews/freighter_warp_preview.gd" id="1"]
[node name="FreighterWarpPreview" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1")

View File

@ -0,0 +1,184 @@
extends Node2D
const DATA := preload("res://scripts/planets/alien_research.gd")
const ENEMY := preload("res://scenes/game/enemy_ship.tscn")
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
var player: CharacterBody2D
var progress: Resource
class StraightLaser extends "res://scripts/game/weapon_controller.gd":
func _aim_direction_from(_origin: Vector2) -> Vector2:
return Vector2.RIGHT
func enemy_at(point: Vector2, hp := 1000.0, tier := 0) -> CharacterBody2D:
var enemy := ENEMY.instantiate() as CharacterBody2D
add_child(enemy)
enemy.set_physics_process(false)
enemy.position = point
enemy.loot_tier = tier
enemy.max_hull = hp
enemy.hull = hp
enemy.shields = 0.0
enemy.absorption = Vector3.ZERO
enemy._player = player
return enemy
func _ready() -> void:
GameState.start_new_run()
progress = GameState.captain.expedition
progress.analysis_samples = 1000
assert(DATA.purchase(progress,"general") and DATA.purchase(progress,"hull"))
for side in ["a","b"]:
var id: String = "hull_"+side
for level in 4:
assert(DATA.cost(id)==20+level*5 and DATA.purchase(progress,id))
assert(not DATA.purchase(progress,id))
id+="_next"
assert(not DATA.purchase(progress,"shields_a"),"Shield category prerequisite is required")
progress.alien_skills = {"hull_a":true}
player = preload("res://scenes/game/player_ship.tscn").instantiate()
add_child(player)
player.set_physics_process(false)
player.get_node("WeaponController").set_physics_process(false)
var enemy := enemy_at(Vector2(5000,0))
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(is_equal_approx(enemy.hull,895.0))
progress.alien_skills = {"hull_a_next":true}
enemy.hull = 1000.0
enemy.absorption = Vector3(0,60,0)
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(enemy.hull==960.0 and enemy._hull_breach_remaining==5.0)
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(enemy.hull==890.0 and enemy._hull_breach_remaining==5.0,"Breach halves absorption without stacking")
enemy._physics_process(5.1)
assert(enemy._hull_breach_remaining==0.0)
enemy.set_physics_process(false)
progress.alien_skills = {"hull_a_next_next_next":true}
var normal := enemy_at(Vector2(6000,0))
normal.configure(player,preload("res://scripts/items/alien_ship_catalog.gd").TEXTURES[0])
normal.hull = 90.0
normal.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(normal.hull==0.0 and normal._execution_finish)
var collapse_found := false
for child in get_children():
if child.get_script()==preload("res://scripts/game/hull_collapse_effect.gd"):
assert(child._pieces.size()==9)
child._process(0.1)
collapse_found = true
assert(collapse_found,"Execution uses actual ship texture fragments")
var boss := enemy_at(Vector2(7000,0),1000,2)
boss.hull = 40.0
boss.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(boss.hull==30.0)
boss.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(boss.hull==0.0)
var shielded := enemy_at(Vector2(8000,0))
shielded.hull = 50.0
shielded.shields = 100.0
shielded.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(shielded.hull==50.0,"A hit absorbed by shields cannot execute")
progress.alien_skills = {"hull_a_next_next":true}
var first := enemy_at(Vector2(100,100))
var second := enemy_at(Vector2(300,100))
var third := enemy_at(Vector2(500,100))
var shot := PROJECTILE.instantiate()
shot.configure(first.position,Vector2.RIGHT,ItemDefinition.WeaponKind.PLASMA,600,100,800)
add_child(shot)
shot.set_physics_process(false)
shot._hit_target(first)
await get_tree().process_frame
var continuation: Node
for child in get_children():
if child.get_script()==preload("res://scripts/game/weapon_projectile.gd") and child._overpenetration_used: continuation=child
assert(continuation!=null and continuation.damage==50.0)
continuation.set_physics_process(false)
continuation._physics_process(0.2)
assert(continuation.global_position.x>150.0,"Continuation exits the pierced ship instead of getting stuck")
continuation._physics_process(0.2)
assert(first.hull==900.0 and second.hull==950.0 and third.hull==1000.0)
assert(continuation.is_queued_for_deletion(),"A penetrating shot cannot create a third hit")
first.hull = 1000.0
second.hull = 1000.0
var missile := PROJECTILE.instantiate()
missile.configure(first.position,Vector2.RIGHT,ItemDefinition.WeaponKind.MISSILE,600,100,800)
add_child(missile)
missile.set_physics_process(false)
missile._hit_target(first)
await get_tree().process_frame
var missile_continuation: Node
for child in get_children():
if child.get_script()==preload("res://scripts/game/weapon_projectile.gd") and child._overpenetration_used and child.weapon_kind==ItemDefinition.WeaponKind.MISSILE: missile_continuation=child
assert(missile_continuation!=null)
missile_continuation.set_physics_process(false)
missile_continuation._physics_process(0.2)
missile_continuation._physics_process(0.2)
assert(first.hull==900.0 and second.hull==950.0 and third.hull==1000.0,"Missile continuation deals half damage to one additional target")
first.position = Vector2(160,-28)
second.position = Vector2(300,-28)
third.position = Vector2(450,-28)
first.hull = 1000.0
second.hull = 1000.0
var laser := StraightLaser.new()
player.add_child(laser)
laser.set_physics_process(false)
var laser_item := ItemInstance.new()
laser_item.definition = preload("res://resources/items/beam_laser.tres")
laser_item.damage_min = 100.0
laser_item.damage_max = 100.0
player.energy = 1000.0
await get_tree().physics_frame
await get_tree().physics_frame
laser._fire_laser(0,laser_item)
assert(first.hull==900.0 and second.hull==950.0 and third.hull==1000.0,"Laser penetrates precisely one additional target")
assert(laser._laser_lines[0].get_point_count()==3)
laser.queue_free()
progress.alien_skills = {"hull_b":true}
var inventory: ShipInventory = player.get_node("ShipInventory")
player.hull = 50.0
inventory._recalculate_ship_stats()
assert(is_equal_approx(player.max_hull,player.base_max_hull*1.1) and player.hull==50.0)
progress.alien_skills = {"hull_b_next":true}
player.max_hull = 100.0
player.hull = 100.0
player.shields = 5.0
player.absorption = Vector3.ZERO
player.take_damage(25.0)
assert(player.hull==94.0 and player._reactive_armor_cooldown==15.0)
player.take_damage(10.0)
assert(player.hull==84.0)
player.tick_hull_technologies(15.0)
player.take_damage(10.0)
assert(player.hull==81.0)
progress.alien_skills = {"hull_b_next_next":true}
player.hull = 40.0
player.shields = 0.0
player.take_damage(20.0)
assert(player.hull==20.0 and player._bulkhead_cooldown==45.0)
player.tick_hull_technologies(2.0)
assert(player.hull==26.0)
player.take_damage(1.0)
player.tick_hull_technologies(3.0)
assert(player.hull==34.0,"Hits do not interrupt five-second repair")
player.take_damage(10.0)
assert(player._bulkhead_repair_remaining==0.0,"Repair cannot retrigger during cooldown")
progress.alien_skills = {"hull_b_next_next_next":true}
player.hull = 10.0
player.take_damage(1000.0)
assert(player.hull==1.0 and not player._is_destroyed and GameState.hull_bastion_used)
player.take_damage(1000.0)
assert(player.hull==1.0)
player.is_docked = true
player.tick_hull_technologies(10.0)
assert(GameState.hull_bastion_used and player._bastion_invulnerability==2.0,"Docking does not renew Last Bastion")
player.is_docked = false
player.tick_hull_technologies(2.1)
player.take_damage(1000.0)
assert(player._is_destroyed,"Last Bastion works only once")
GameState.system_level = 7
GameState.system_seed = 777
progress.distant_maps["hull_test"] = {"id":"hull_test","level":125,"seed":123}
progress.deuter = 125000
assert(GameState.depart_distant("hull_test") and not GameState.hull_bastion_used)
GameState.hull_bastion_used = true
GameState.restore_distant_home()
assert(GameState.hull_bastion_used and GameState.system_level==7,"Return preserves the home's used rescue")
print("ALIEN_HULL_TECHNOLOGIES_TEST: PASS (8 technologies, costs, breach, execution thresholds, penetration, reactive armor, uninterrupted repair, persistent one-use rescue)")

View File

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

View File

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

View File

@ -10,12 +10,12 @@ func _ready() -> void:
assert(not DATA.purchase(progress, "general"))
assert(DATA.purchase(progress, "hull"))
assert(progress.analysis_samples == 75)
assert(not DATA.purchase(progress, "hull_a"))
assert(progress.analysis_samples == 75)
assert(DATA.purchase(progress, "hull_a"))
assert(progress.analysis_samples == 55)
assert(is_equal_approx(DATA.bonus(progress, "hull"), 0.1))
progress.alien_skills["hull_a"] = true
assert(is_equal_approx(DATA.bonus(progress, "hull"), 0.1))
assert(DATA.learned_count(progress) == 2)
assert(DATA.learned_count(progress) == 3)
progress.analysis_samples = 0
assert(not DATA.purchase(progress, "hull_b"))
progress.analysis_samples = 76
@ -31,7 +31,7 @@ func _ready() -> void:
enemy.shields = 5
enemy.hull = 100
enemy.take_weapon_hit(10, false)
assert(is_equal_approx(enemy.hull, 94.5))
assert(is_equal_approx(enemy.hull, 94.25))
progress.analysis_samples = 100
assert(DATA.purchase(progress, "shields"))
assert(DATA.purchase(progress, "weapons"))
@ -39,7 +39,7 @@ func _ready() -> void:
enemy.shields = 5.5
enemy.hull = 100
enemy.take_weapon_hit(10, false)
assert(is_equal_approx(enemy.hull, 94.5))
assert(is_equal_approx(enemy.hull, 94.25))
enemy.free()
var screen := preload("res://scenes/planets/freighter_interior.tscn").instantiate()
progress.freighter_rooms["expansion_2"] = "alien_research"

View File

@ -0,0 +1,159 @@
extends Node2D
const DATA := preload("res://scripts/planets/alien_research.gd")
const ENEMY := preload("res://scenes/game/enemy_ship.tscn")
var player: CharacterBody2D
var progress: Resource
class StraightAlien extends "res://scripts/game/enemy_ship.gd":
func _laser_aim_direction(direction: Vector2) -> Vector2:
return direction
func enemy_at(point: Vector2) -> CharacterBody2D:
var enemy := ENEMY.instantiate() as CharacterBody2D
enemy.set_script(StraightAlien)
add_child(enemy)
enemy.set_physics_process(false)
enemy.position = point
enemy.max_hull = 10000.0
enemy.hull = 10000.0
enemy.max_shields = 1000.0
enemy.shields = 1000.0
enemy.absorption = Vector3.ZERO
enemy._player = player
return enemy
func _ready() -> void:
GameState.start_new_run()
progress = GameState.captain.expedition
progress.analysis_samples = 1000
assert(DATA.purchase(progress,"general") and DATA.purchase(progress,"shields"))
for side in ["a","b"]:
var id: String = "shields_"+side
for level in 4:
assert(DATA.cost(id)==20+level*5 and DATA.purchase(progress,id))
assert(not DATA.purchase(progress,id))
id+="_next"
assert(DATA.ACTIVE_IDS.size()==28)
player = preload("res://scenes/game/player_ship.tscn").instantiate()
add_child(player)
player.set_physics_process(false)
var weapon := player.get_node("WeaponController")
weapon.set_physics_process(false)
var enemy := enemy_at(Vector2(1000,0))
var nearby := enemy_at(Vector2(1100,0))
var chain := enemy_at(Vector2(1200,0))
progress.alien_skills = {"shields":true,"shields_a":true}
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(is_equal_approx(enemy.shields,885.0),"Shield bonuses add to 15 percent")
progress.alien_skills = {"shields_a_next":true}
enemy.shields = 500
enemy.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(enemy._shield_regen_lock==5.0)
enemy.regenerate_defenses(1.0)
assert(enemy.shields==490.0)
enemy.tick_shield_technologies(3.0)
enemy.take_research_weapon_hit(10,false,DamageTypes.Type.ENERGY,0,-1)
assert(enemy._shield_regen_lock==5.0)
enemy.tick_shield_technologies(5.1)
enemy.regenerate_defenses(1.0)
assert(enemy.shields==490.0)
progress.alien_skills = {"shields_a_next_next":true}
enemy.shields = 50
nearby.shields = 50
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(nearby.shields==0 and nearby.hull==9650,"Overload deals 400 percent, including spill")
assert(chain.shields==1000,"Overload cannot trigger a chain")
assert(enemy._shield_overload_cooldown==10)
enemy.shields = 1
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(nearby.hull==9650,"Regrown shield cannot spam overload")
enemy.tick_shield_technologies(10.1)
enemy.shields = 1
enemy.take_research_weapon_hit(100,false,DamageTypes.Type.ENERGY,0,-1)
assert(nearby.hull==9250)
progress.alien_skills = {"shields_b":true}
player.shields = 20
player.apply_equipment_bonuses({})
assert(is_equal_approx(player.max_shields,player.base_max_shields*1.1) and player.shields==20)
player.max_shields = 100
player.max_hull = 1000
player.hull = 1000
player.absorption = Vector3.ZERO
progress.alien_skills = {"shields_b_next":true}
player.shields = 40
player.take_damage(20)
assert(player._shield_reserve_remaining==2 and player._shield_reserve_cooldown==15)
player.energy = 5
assert(weapon._consume_energy(10) and player.energy==0,"Reduced cost is applied once")
player.tick_hull_technologies(2.1)
player.energy = 5
assert(not weapon._consume_energy(10),"Normal cost after reserve expires")
player.shields = 40
player.take_damage(20)
assert(player._shield_reserve_remaining==0,"Reserve cooldown prevents retrigger")
progress.alien_skills = {"shields_b_next_next":true}
player.shields = 100
player.take_damage(100)
assert(player.shields==90 and player._salvo_cooldown==60)
player.take_damage(20)
assert(player.shields==70,"Only first hit is reduced")
player.is_docked = true
player.tick_hull_technologies(60)
assert(player._salvo_cooldown==60,"Docking does not refresh cooldown")
player.is_docked = false
player.tick_hull_technologies(60)
player.shields = 5
player.take_damage(100)
assert(player.shields==0 and player.hull==995,"First-hit reduction also covers spill")
progress.alien_skills = {"shields_b_next_next_next":true}
player.hull = 1000
player.shields = 10
player.take_damage(20)
assert(player.shields==25 and player.hull==990 and player._emergency_shield_cooldown==30)
player.take_damage(20)
assert(player.shields==15,"Emergency shield halves following hits")
progress.alien_skills["shields_b_next_next"] = true
player._salvo_cooldown = 0
player.take_damage(100)
assert(player.shields==5,"Stronger protection applies without multiplying reductions")
player.tick_hull_technologies(2.1)
player.take_damage(10)
assert(player.shields==0 and player.hull==985,"Emergency shield cannot retrigger during cooldown")
progress.alien_skills = {"shields_a_next_next_next":true}
player.shields = 100
enemy.shields = 100
player.take_enemy_weapon_hit(50,false,DamageTypes.Type.ENERGY,enemy)
assert(player.shields==50 and enemy.shields==90,"Reflection uses absorbed damage and actual shooter")
player.shields = 5
player.take_enemy_weapon_hit(50,false,DamageTypes.Type.ENERGY,enemy)
assert(enemy.shields==89,"Hull damage is not reflected")
player.take_enemy_weapon_hit(10,false,DamageTypes.Type.ENERGY,enemy)
assert(enemy.shields==89,"No reflection without shields")
player.shields = 100
enemy.virus_controlled = true
player.take_enemy_weapon_hit(10,false,DamageTypes.Type.ENERGY,enemy)
assert(enemy.shields==89,"Friendly controlled aliens do not receive reflection")
enemy.virus_controlled = false
player.shields = 100
var projectile := preload("res://scenes/game/weapon_projectile.tscn").instantiate()
projectile.enemy_shot = true
projectile.source_ship = enemy
projectile.damage = 20
add_child(projectile)
projectile.set_physics_process(false)
projectile._apply_damage_once(player)
assert(player.shields==80 and is_equal_approx(enemy.shields,85),"Enemy projectile retains its shooter for reflection")
projectile.queue_free()
var shooter := enemy_at(Vector2(0,-180))
shooter.weapon_kind = ItemDefinition.WeaponKind.LASER
shooter.weapon_damage = 20
shooter.weapon_range = 500
shooter._combat_target = player
player.shields = 100
await get_tree().physics_frame
await get_tree().physics_frame
var laser_damage: float = shooter.get_shot_damage()
shooter._fire_plasma(Vector2.DOWN)
assert(is_equal_approx(player.shields,100-laser_damage),"Actual laser hits the player")
assert(is_equal_approx(shooter.shields,1000-laser_damage*0.2),"Actual laser reflection returns to the shooter")
print("ALIEN_SHIELD_TECHNOLOGIES_TEST: PASS (8 skills, costs, 400% overload without chains, regen lock, energy reserve, 10% first hit/60s, emergency shield, reflection)")

View File

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

View File

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

View File

@ -10,6 +10,9 @@ func shot(path: String) -> void:
func _ready() -> void:
GameState.start_new_run()
for id in preload("res://scripts/planets/alien_research.gd").SHIELD_IDS:
GameState.captain.expedition.alien_skills[id] = true
GameState.captain.expedition.alien_skills["shields"] = true
GameState.system_level = 7
GameState.selected_ship_path = "res://assets/Ships/player/bastion.png"
var game := preload("res://scenes/game/game.tscn").instantiate()
@ -46,6 +49,8 @@ func _ready() -> void:
var stats: PanelContainer = hud._ship_statistics
assert(stats.visible and stats.size.x == 690)
assert(stats._bindings.has("hull") and stats._bindings.has("critical") and stats._bindings.has("cargo"))
assert(stats._bindings.has("shield_reserve") and stats._bindings.has("salvo") and stats._bindings.has("emergency_shield"))
assert(stats._bindings.research_shields.label.text == "15%")
ship.absorption = Vector3(7,11,13)
ship.hull = 25
stats._refresh_values()
@ -64,9 +69,12 @@ func _ready() -> void:
stats._select_tab("equipment")
assert(stats._bindings.has("extension_0"))
await shot("res://artifacts/ship_statistics_systems.png")
GameState.system_level = 61
GameState.system_level = 120
await settle()
assert(hud._system_title.text == "System 61" and hud._system_mode.text == "HARD")
assert(hud._system_title.text == "System 120" and hud._system_mode.text != "HARD")
GameState.system_level = 121
await settle()
assert(hud._system_title.text == "System 121" and hud._system_mode.text == "HARD")
var event := InputEventKey.new()
event.pressed = true
event.keycode = KEY_ESCAPE