ZMianu HUD
This commit is contained in:
parent
5dd71181bc
commit
d2bf034d98
6
assets/icons/ui/captain_skills.svg
Normal file
6
assets/icons/ui/captain_skills.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||||
|
<defs><linearGradient id="badge" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#c9b7ff"/><stop offset="1" stop-color="#8970df"/></linearGradient></defs>
|
||||||
|
<path d="M32 7l20 8v17c0 12-9 20-20 25C21 52 12 44 12 32V15z" fill="#211c35" stroke="#8875c0" stroke-width="2" stroke-linejoin="round"/>
|
||||||
|
<path d="M32 14l4.3 8.7 9.6 1.4-7 6.8 1.7 9.6-8.6-4.5-8.6 4.5 1.7-9.6-7-6.8 9.6-1.4z" fill="url(#badge)" stroke="#e2d7ff" stroke-width="1.3" stroke-linejoin="round"/>
|
||||||
|
<path d="M24 44l8 5 8-5M27 50l5 3 5-3" fill="none" stroke="#b7a2f3" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 692 B |
43
assets/icons/ui/captain_skills.svg.import
Normal file
43
assets/icons/ui/captain_skills.svg.import
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cn3fyvgfbwqye"
|
||||||
|
path="res://.godot/imported/captain_skills.svg-cbdac3f3096be57c282840f44fa14935.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/icons/ui/captain_skills.svg"
|
||||||
|
dest_files=["res://.godot/imported/captain_skills.svg-cbdac3f3096be57c282840f44fa14935.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
8
assets/icons/ui/crafting.svg
Normal file
8
assets/icons/ui/crafting.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
|
||||||
|
<defs><linearGradient id="metal" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#ffe2a6"/><stop offset="1" stop-color="#e9a34c"/></linearGradient></defs>
|
||||||
|
<path d="M32 7v5M32 52v5M7 32h5M52 32h5M14 14l4 4M46 46l4 4M14 50l4-4M46 18l4-4" fill="none" stroke="#b77c3e" stroke-width="3" stroke-linecap="round"/>
|
||||||
|
<circle cx="32" cy="32" r="20" fill="#251f19" stroke="#735431" stroke-width="2"/>
|
||||||
|
<path d="M45 17a12 12 0 0 0-16 15L15 46a5 5 0 0 0 7 7l14-14a12 12 0 0 0 15-16l-8 8-7-2-2-7z" fill="url(#metal)" stroke="#ffedc2" stroke-width="1.3" stroke-linejoin="round"/>
|
||||||
|
<circle cx="19" cy="49" r="2" fill="#49301b"/>
|
||||||
|
<path d="M23 43l7-7" stroke="#fff1cb" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 791 B |
43
assets/icons/ui/crafting.svg.import
Normal file
43
assets/icons/ui/crafting.svg.import
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cn00i5tkhcxyw"
|
||||||
|
path="res://.godot/imported/crafting.svg-c0cc0c27f763904bea856d7dc30d600d.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/icons/ui/crafting.svg"
|
||||||
|
dest_files=["res://.godot/imported/crafting.svg-c0cc0c27f763904bea856d7dc30d600d.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
svg/scale=1.0
|
||||||
|
editor/scale_with_editor_scale=false
|
||||||
|
editor/convert_colors_with_editor_theme=false
|
||||||
@ -9,9 +9,7 @@ const BALANCE := preload("res://scripts/captain/captain_balance.gd")
|
|||||||
@export var parts: float = 0.0
|
@export var parts: float = 0.0
|
||||||
@export var minerals: Dictionary = {"uranium": 0, "titanium": 0, "iron": 0, "aluminium": 0}
|
@export var minerals: Dictionary = {"uranium": 0, "titanium": 0, "iron": 0, "aluminium": 0}
|
||||||
@export var learning_points: int = 0
|
@export var learning_points: int = 0
|
||||||
@export var skills: Dictionary = {
|
@export var skills: Dictionary = {}
|
||||||
"piloting": 0, "gunner": 0, "mechanic": 0, "sharp_eye": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_xp_required() -> int:
|
func get_xp_required() -> int:
|
||||||
return BALANCE.xp_required(level)
|
return BALANCE.xp_required(level)
|
||||||
@ -19,20 +17,17 @@ func get_xp_required() -> int:
|
|||||||
func get_skill_rank(skill: String) -> int:
|
func get_skill_rank(skill: String) -> int:
|
||||||
return clampi(int(skills.get(skill, 0)), 0, BALANCE.MAX_SKILL_RANK)
|
return clampi(int(skills.get(skill, 0)), 0, BALANCE.MAX_SKILL_RANK)
|
||||||
|
|
||||||
func get_skill_bonus(skill: String) -> float:
|
func get_skill_bonus(_skill: String) -> float:
|
||||||
return BALANCE.skill_bonus(skill, get_skill_rank(skill))
|
return 0.0 # The old captain skill system has been retired.
|
||||||
|
|
||||||
func normalize_skill_ranks() -> void:
|
func normalize_skill_ranks() -> void:
|
||||||
var changed_ranks := false
|
# Refund legacy ranks for the future captain skill system.
|
||||||
for skill in BALANCE.SKILL_NAMES:
|
var refunded := get_spent_learning_points()
|
||||||
var previous := maxi(0, int(skills.get(skill, 0)))
|
if refunded > 0:
|
||||||
var rank := mini(previous, BALANCE.MAX_SKILL_RANK)
|
learning_points += refunded
|
||||||
learning_points += previous - rank
|
skills.clear()
|
||||||
if int(skills.get(skill, 0)) != rank:
|
|
||||||
changed_ranks = true
|
|
||||||
skills[skill] = rank
|
|
||||||
if changed_ranks:
|
|
||||||
emit_changed()
|
emit_changed()
|
||||||
|
return
|
||||||
|
|
||||||
func get_spent_learning_points() -> int:
|
func get_spent_learning_points() -> int:
|
||||||
var spent := 0
|
var spent := 0
|
||||||
@ -56,8 +51,8 @@ func reset_skills() -> bool:
|
|||||||
emit_changed()
|
emit_changed()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
func can_train(skill: String) -> bool:
|
func can_train(_skill: String) -> bool:
|
||||||
return skills.has(skill) and learning_points > 0 and get_skill_rank(skill) < BALANCE.MAX_SKILL_RANK
|
return false
|
||||||
|
|
||||||
func train_skill(skill: String) -> bool:
|
func train_skill(skill: String) -> bool:
|
||||||
if not can_train(skill):
|
if not can_train(skill):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
const DURATION := 1.6
|
const SETTLE_TIME := 8.0
|
||||||
var _texture: Texture2D
|
var _texture: Texture2D
|
||||||
var _fragments: Array[Dictionary] = []
|
var _fragments: Array[Dictionary] = []
|
||||||
var _dust: Array[Dictionary] = []
|
var _dust: Array[Dictionary] = []
|
||||||
@ -24,23 +24,35 @@ func configure(sprite: Sprite2D) -> void:
|
|||||||
if y > 0 and y < 4:
|
if y > 0 and y < 4:
|
||||||
vertex.y += _rng.randf_range(-0.3, 0.3) * dimensions.y / 4.0
|
vertex.y += _rng.randf_range(-0.3, 0.3) * dimensions.y / 4.0
|
||||||
grid.append(vertex)
|
grid.append(vertex)
|
||||||
|
# Shared jagged cracks keep neighbouring pieces fitted together.
|
||||||
|
var horizontal: Array[PackedVector2Array] = []
|
||||||
|
var vertical: Array[PackedVector2Array] = []
|
||||||
|
for y in 5:
|
||||||
|
for x in 4:
|
||||||
|
horizontal.append(_fracture_edge(grid[y * 5 + x], grid[y * 5 + x + 1], y > 0 and y < 4))
|
||||||
|
for y in 4:
|
||||||
|
for x in 5:
|
||||||
|
vertical.append(_fracture_edge(grid[y * 5 + x], grid[(y + 1) * 5 + x], x > 0 and x < 4))
|
||||||
for y in 4:
|
for y in 4:
|
||||||
for x in 4:
|
for x in 4:
|
||||||
var a := grid[y * 5 + x]
|
var outline := PackedVector2Array()
|
||||||
var b := grid[y * 5 + x + 1]
|
outline.append_array(_edge_vertices(horizontal[y * 4 + x], false))
|
||||||
var c := grid[(y + 1) * 5 + x + 1]
|
outline.append_array(_edge_vertices(vertical[y * 5 + x + 1], false))
|
||||||
var d := grid[(y + 1) * 5 + x]
|
outline.append_array(_edge_vertices(horizontal[(y + 1) * 4 + x], true))
|
||||||
for triangle in [[a, b, c], [a, c, d]]:
|
outline.append_array(_edge_vertices(vertical[y * 5 + x], true))
|
||||||
var center: Vector2 = (triangle[0] + triangle[1] + triangle[2]) / 3.0
|
var center := Vector2.ZERO
|
||||||
|
for vertex in outline:
|
||||||
|
center += vertex
|
||||||
|
center /= outline.size()
|
||||||
var origin := (center - dimensions * 0.5) * sprite_scale
|
var origin := (center - dimensions * 0.5) * sprite_scale
|
||||||
var points := PackedVector2Array()
|
var points := PackedVector2Array()
|
||||||
var uv := PackedVector2Array()
|
var uv := PackedVector2Array()
|
||||||
for vertex: Vector2 in triangle:
|
for vertex in outline:
|
||||||
points.append((vertex - center) * sprite_scale)
|
points.append((vertex - center) * sprite_scale)
|
||||||
uv.append(vertex / dimensions)
|
uv.append(vertex / dimensions)
|
||||||
var direction := origin.normalized() if origin.length_squared() > 0.1 else Vector2.RIGHT
|
var direction := origin.normalized() if origin.length_squared() > 0.1 else Vector2.RIGHT
|
||||||
_fragments.append({"points": points, "uv": uv, "position": origin,
|
_fragments.append({"points": points, "uv": uv, "position": origin,
|
||||||
"velocity": direction.rotated(_rng.randf_range(-0.35, 0.35)) * _rng.randf_range(70.0, 170.0),
|
"velocity": direction.rotated(_rng.randf_range(-0.35, 0.35)) * _rng.randf_range(40.0, 95.0),
|
||||||
"angle": 0.0, "spin": _rng.randf_range(-4.0, 4.0)})
|
"angle": 0.0, "spin": _rng.randf_range(-4.0, 4.0)})
|
||||||
_dust_color = Color("a1c7df") if "ice" in _texture.resource_path else Color("b4a99a")
|
_dust_color = Color("a1c7df") if "ice" in _texture.resource_path else Color("b4a99a")
|
||||||
for index in 28:
|
for index in 28:
|
||||||
@ -49,22 +61,43 @@ func configure(sprite: Sprite2D) -> void:
|
|||||||
z_index = 35
|
z_index = 35
|
||||||
light_mask = 4
|
light_mask = 4
|
||||||
|
|
||||||
|
func _fracture_edge(start: Vector2, end: Vector2, jagged: bool) -> PackedVector2Array:
|
||||||
|
var edge := PackedVector2Array([start])
|
||||||
|
if jagged:
|
||||||
|
var normal := (end - start).orthogonal().normalized()
|
||||||
|
var roughness := start.distance_to(end) * 0.13
|
||||||
|
var side := 1.0 if _rng.randf() > 0.5 else -1.0
|
||||||
|
for index in 3:
|
||||||
|
var along := (index + 1.0) / 4.0 + _rng.randf_range(-0.05, 0.05)
|
||||||
|
edge.append(start.lerp(end, along) + normal * roughness * side * _rng.randf_range(0.5, 1.0))
|
||||||
|
side *= -1.0
|
||||||
|
edge.append(end)
|
||||||
|
return edge
|
||||||
|
|
||||||
|
func _edge_vertices(edge: PackedVector2Array, backwards: bool) -> PackedVector2Array:
|
||||||
|
var vertices := PackedVector2Array()
|
||||||
|
# The next edge supplies the end vertex, avoiding duplicate polygon points.
|
||||||
|
for index in edge.size() - 1:
|
||||||
|
vertices.append(edge[edge.size() - 1 - index] if backwards else edge[index])
|
||||||
|
return vertices
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
_elapsed += delta
|
_elapsed += delta
|
||||||
if _elapsed >= DURATION:
|
|
||||||
queue_free()
|
|
||||||
return
|
|
||||||
for fragment in _fragments:
|
for fragment in _fragments:
|
||||||
fragment.position += fragment.velocity * delta
|
fragment.position += fragment.velocity * delta
|
||||||
fragment.velocity *= exp(-1.2 * delta)
|
fragment.velocity *= exp(-1.2 * delta)
|
||||||
fragment.angle += fragment.spin * delta
|
fragment.angle += fragment.spin * delta
|
||||||
|
fragment.spin *= exp(-0.7 * delta)
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
# Leave the settled debris visible without updating it every frame.
|
||||||
|
if _elapsed >= SETTLE_TIME:
|
||||||
|
_dust.clear()
|
||||||
|
set_process(false)
|
||||||
|
|
||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
var fade := 1.0 - smoothstep(0.65, DURATION, _elapsed)
|
|
||||||
for fragment in _fragments:
|
for fragment in _fragments:
|
||||||
draw_set_transform(fragment.position, fragment.angle)
|
draw_set_transform(fragment.position, fragment.angle)
|
||||||
draw_polygon(fragment.points, PackedColorArray([Color(1.0, 1.0, 1.0, fade)]), fragment.uv, _texture)
|
draw_polygon(fragment.points, PackedColorArray([Color.WHITE]), fragment.uv, _texture)
|
||||||
draw_set_transform(Vector2.ZERO)
|
draw_set_transform(Vector2.ZERO)
|
||||||
var dust_fade := maxf(0.0, 1.0 - _elapsed / 0.8)
|
var dust_fade := maxf(0.0, 1.0 - _elapsed / 0.8)
|
||||||
for mote in _dust:
|
for mote in _dust:
|
||||||
|
|||||||
@ -24,6 +24,89 @@ var _debug_item_button: Button
|
|||||||
var _debug_item_status: Label
|
var _debug_item_status: Label
|
||||||
var _captain_panel: PanelContainer
|
var _captain_panel: PanelContainer
|
||||||
var _cockpit: Control
|
var _cockpit: Control
|
||||||
|
var _crafting_window: PanelContainer
|
||||||
|
var _skills_window: PanelContainer
|
||||||
|
|
||||||
|
func _create_navigation() -> void:
|
||||||
|
var navigation := HBoxContainer.new()
|
||||||
|
navigation.name = "TopNavigation"
|
||||||
|
$HUDRoot.add_child(navigation)
|
||||||
|
navigation.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP)
|
||||||
|
navigation.offset_left = -60
|
||||||
|
navigation.offset_right = 60
|
||||||
|
navigation.offset_top = 12
|
||||||
|
navigation.offset_bottom = 64
|
||||||
|
navigation.add_theme_constant_override("separation", 8)
|
||||||
|
_crafting_window = _create_window("CraftingWindow", "TWÓRZ", Vector2(440, 250))
|
||||||
|
var crafting := PanelContainer.new()
|
||||||
|
crafting.name = "CraftingPanel"
|
||||||
|
crafting.set_script(preload("res://scripts/ui/ship_crafting_panel.gd"))
|
||||||
|
crafting.set("standalone", true)
|
||||||
|
_crafting_window.get_node("Contents").add_child(crafting)
|
||||||
|
_skills_window = _create_window("CaptainSkillsWindow", "UMIEJĘTNOŚCI KAPITANA", Vector2(480, 300))
|
||||||
|
var empty := Control.new()
|
||||||
|
empty.name = "SkillsContent"
|
||||||
|
empty.custom_minimum_size = Vector2(440, 230)
|
||||||
|
_skills_window.get_node("Contents").add_child(empty)
|
||||||
|
for entry in [["Twórz", _crafting_window, "res://assets/icons/ui/crafting.svg", Color("efb568")], ["Umiejętności kapitana", _skills_window, "res://assets/icons/ui/captain_skills.svg", Color("b9a0ef")]]:
|
||||||
|
var button := Button.new()
|
||||||
|
button.tooltip_text = entry[0]
|
||||||
|
button.icon = load(entry[2]) as Texture2D
|
||||||
|
button.expand_icon = true
|
||||||
|
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
button.custom_minimum_size = Vector2(56, 52)
|
||||||
|
button.add_theme_constant_override("icon_max_width", 36)
|
||||||
|
preload("res://scripts/ui/hud_button_style.gd").apply(button, entry[3])
|
||||||
|
button.add_theme_constant_override("h_separation", 0)
|
||||||
|
for state in ["normal", "hover", "pressed", "disabled"]:
|
||||||
|
var style := button.get_theme_stylebox(state).duplicate() as StyleBoxFlat
|
||||||
|
style.set_content_margin_all(8)
|
||||||
|
button.add_theme_stylebox_override(state, style)
|
||||||
|
navigation.add_child(button)
|
||||||
|
button.pressed.connect(_toggle_window.bind(entry[1]))
|
||||||
|
|
||||||
|
func _create_window(node_name: String, title: String, dimensions: Vector2) -> PanelContainer:
|
||||||
|
var window := PanelContainer.new()
|
||||||
|
window.name = node_name
|
||||||
|
var style := StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color("0b1520fa")
|
||||||
|
style.border_color = Color("376779")
|
||||||
|
style.set_border_width_all(1)
|
||||||
|
style.set_corner_radius_all(8)
|
||||||
|
style.set_content_margin_all(16)
|
||||||
|
window.add_theme_stylebox_override("panel", style)
|
||||||
|
$HUDRoot.add_child(window)
|
||||||
|
window.set_anchors_and_offsets_preset(Control.PRESET_CENTER)
|
||||||
|
window.offset_left = -dimensions.x * 0.5
|
||||||
|
window.offset_right = dimensions.x * 0.5
|
||||||
|
window.offset_top = -dimensions.y * 0.5
|
||||||
|
window.offset_bottom = dimensions.y * 0.5
|
||||||
|
var contents := VBoxContainer.new()
|
||||||
|
contents.name = "Contents"
|
||||||
|
contents.add_theme_constant_override("separation", 12)
|
||||||
|
window.add_child(contents)
|
||||||
|
var header := HBoxContainer.new()
|
||||||
|
contents.add_child(header)
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = title
|
||||||
|
label.add_theme_font_size_override("font_size", 14)
|
||||||
|
label.add_theme_color_override("font_color", Color("91dbea"))
|
||||||
|
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
header.add_child(label)
|
||||||
|
var close := Button.new()
|
||||||
|
close.text = "×"
|
||||||
|
close.tooltip_text = "Zamknij"
|
||||||
|
close.custom_minimum_size = Vector2(32, 28)
|
||||||
|
close.pressed.connect(window.hide)
|
||||||
|
header.add_child(close)
|
||||||
|
window.hide()
|
||||||
|
return window
|
||||||
|
|
||||||
|
func _toggle_window(window: PanelContainer) -> void:
|
||||||
|
var opening := not window.visible
|
||||||
|
_crafting_window.hide()
|
||||||
|
_skills_window.hide()
|
||||||
|
window.visible = opening
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_sidebar_open = bool(GameState.hud_panel_state.get("ship_open", false))
|
_sidebar_open = bool(GameState.hud_panel_state.get("ship_open", false))
|
||||||
@ -43,10 +126,6 @@ func _ready() -> void:
|
|||||||
# The display font has unusually tall vertical metrics; keep debug rows compact.
|
# The display font has unusually tall vertical metrics; keep debug rows compact.
|
||||||
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
|
debug_info.add_theme_constant_override("line_spacing", mini(0, 13 - ceili(debug_info.get_theme_font("font").get_height(10))))
|
||||||
_create_sidebar()
|
_create_sidebar()
|
||||||
var enemy_panel := Control.new()
|
|
||||||
enemy_panel.name = "EnemyDrawer"
|
|
||||||
enemy_panel.set_script(preload("res://scripts/ui/enemy_drawer.gd"))
|
|
||||||
$HUDRoot.add_child(enemy_panel)
|
|
||||||
_inventory_clip = Control.new()
|
_inventory_clip = Control.new()
|
||||||
_inventory_clip.name = "InventoryDrawer"
|
_inventory_clip.name = "InventoryDrawer"
|
||||||
_inventory_clip.clip_contents = true
|
_inventory_clip.clip_contents = true
|
||||||
@ -121,23 +200,21 @@ func _create_sidebar() -> void:
|
|||||||
equipment.reparent(contents, false)
|
equipment.reparent(contents, false)
|
||||||
equipment.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
equipment.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
equipment.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
|
equipment.add_theme_stylebox_override("panel", StyleBoxEmpty.new())
|
||||||
var crafting := PanelContainer.new()
|
|
||||||
crafting.name = "CraftingPanel"
|
|
||||||
crafting.set_script(preload("res://scripts/ui/ship_crafting_panel.gd"))
|
|
||||||
equipment.get_node("Contents").add_child(crafting)
|
|
||||||
contents.minimum_size_changed.connect(func() -> void: _layout_sidebar.call_deferred())
|
contents.minimum_size_changed.connect(func() -> void: _layout_sidebar.call_deferred())
|
||||||
_sidebar_toggle = Button.new()
|
_sidebar_toggle = Button.new()
|
||||||
|
_sidebar_toggle.set_script(preload("res://scripts/ui/ship_profile.gd"))
|
||||||
|
_sidebar_toggle.set("player", player)
|
||||||
_sidebar_toggle.name = "ShipToggle"
|
_sidebar_toggle.name = "ShipToggle"
|
||||||
_sidebar_toggle.text = "STATEK ▾" if _sidebar_open else "STATEK ◀"
|
_sidebar_toggle.tooltip_text = "Zamknij okno statku" if _sidebar_open else "Otwórz statek i sloty wyposażenia"
|
||||||
_sidebar_toggle.add_theme_font_size_override("font_size", 12)
|
_sidebar_toggle.add_theme_font_size_override("font_size", 12)
|
||||||
preload("res://scripts/ui/hud_button_style.gd").apply(_sidebar_toggle)
|
preload("res://scripts/ui/hud_button_style.gd").apply(_sidebar_toggle)
|
||||||
preload("res://scripts/ui/hud_button_style.gd").apply(inventory_button, Color("b9a0ef"))
|
preload("res://scripts/ui/hud_button_style.gd").apply(inventory_button, Color("b9a0ef"))
|
||||||
$HUDRoot.add_child(_sidebar_toggle)
|
$HUDRoot.add_child(_sidebar_toggle)
|
||||||
_sidebar_toggle.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
|
_sidebar_toggle.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
|
||||||
_sidebar_toggle.offset_left = -180
|
_sidebar_toggle.offset_left = -352
|
||||||
_sidebar_toggle.offset_right = -12
|
_sidebar_toggle.offset_right = -12
|
||||||
_sidebar_toggle.offset_top = 12
|
_sidebar_toggle.offset_top = 12
|
||||||
_sidebar_toggle.offset_bottom = 48
|
_sidebar_toggle.offset_bottom = 122
|
||||||
_sidebar_toggle.pressed.connect(_toggle_sidebar)
|
_sidebar_toggle.pressed.connect(_toggle_sidebar)
|
||||||
_cockpit = Control.new()
|
_cockpit = Control.new()
|
||||||
_cockpit.name = "Cockpit"
|
_cockpit.name = "Cockpit"
|
||||||
@ -145,6 +222,7 @@ func _create_sidebar() -> void:
|
|||||||
_cockpit.set("player", player)
|
_cockpit.set("player", player)
|
||||||
_cockpit.set("game", get_parent())
|
_cockpit.set("game", get_parent())
|
||||||
$HUDRoot.add_child(_cockpit)
|
$HUDRoot.add_child(_cockpit)
|
||||||
|
_create_navigation()
|
||||||
_layout_debug()
|
_layout_debug()
|
||||||
|
|
||||||
func _layout_debug() -> void:
|
func _layout_debug() -> void:
|
||||||
@ -165,7 +243,7 @@ func _toggle_sidebar() -> void:
|
|||||||
GameState.hud_panel_state["ship_open"] = _sidebar_open
|
GameState.hud_panel_state["ship_open"] = _sidebar_open
|
||||||
if _sidebar_tween != null and _sidebar_tween.is_running():
|
if _sidebar_tween != null and _sidebar_tween.is_running():
|
||||||
_sidebar_tween.kill()
|
_sidebar_tween.kill()
|
||||||
_sidebar_toggle.text = "STATEK ▾" if _sidebar_open else "STATEK ◀"
|
_sidebar_toggle.tooltip_text = "Zamknij okno statku" if _sidebar_open else "Otwórz statek i sloty wyposażenia"
|
||||||
if _sidebar_open:
|
if _sidebar_open:
|
||||||
_sidebar.show()
|
_sidebar.show()
|
||||||
else:
|
else:
|
||||||
@ -181,16 +259,16 @@ func _layout_sidebar() -> void:
|
|||||||
if _sidebar_tween != null and _sidebar_tween.is_running():
|
if _sidebar_tween != null and _sidebar_tween.is_running():
|
||||||
_sidebar_tween.kill()
|
_sidebar_tween.kill()
|
||||||
_sidebar.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
|
_sidebar.scale = Vector2.ONE * EQUIPMENT_UI_SCALE
|
||||||
_sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), minf(_sidebar.get_node("Scroll/Contents").get_combined_minimum_size().y + 16.0, maxf(100.0, ($HUDRoot.size.y - 64.0) / EQUIPMENT_UI_SCALE)))
|
_sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), minf(_sidebar.get_node("Scroll/Contents").get_combined_minimum_size().y + 16.0, maxf(100.0, ($HUDRoot.size.y - 148.0) / EQUIPMENT_UI_SCALE)))
|
||||||
_cockpit.scale = Vector2.ONE * 0.85
|
_cockpit.scale = Vector2.ONE * 0.85
|
||||||
_cockpit.size = Vector2(minf(640.0, ($HUDRoot.size.x - 24) / 0.85), 104)
|
_cockpit.size = Vector2(minf(850.0, ($HUDRoot.size.x - 24) / 0.85), 120)
|
||||||
_cockpit.position = Vector2(($HUDRoot.size.x - _cockpit.size.x * 0.85) * 0.5, $HUDRoot.size.y - _cockpit.size.y * 0.85 - 12)
|
_cockpit.position = Vector2(($HUDRoot.size.x - _cockpit.size.x * 0.85) * 0.5, $HUDRoot.size.y - _cockpit.size.y * 0.85 - 12)
|
||||||
if _ability_bar != null:
|
if _ability_bar != null:
|
||||||
var abilities := _ability_bar.get_parent() as Control
|
var abilities := _ability_bar.get_parent() as Control
|
||||||
abilities.offset_bottom = _cockpit.position.y - $HUDRoot.size.y - 14.0
|
abilities.offset_bottom = _cockpit.position.y - $HUDRoot.size.y - 14.0
|
||||||
abilities.offset_top = abilities.offset_bottom - 44.0
|
abilities.offset_top = abilities.offset_bottom - 44.0
|
||||||
_layout_debug()
|
_layout_debug()
|
||||||
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 56)
|
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8 if _sidebar_open else $HUDRoot.size.x, 136)
|
||||||
_sidebar.visible = _sidebar_open
|
_sidebar.visible = _sidebar_open
|
||||||
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
|
_inventory_clip.size = inventory_panel.size * EQUIPMENT_UI_SCALE
|
||||||
_layout_inventory()
|
_layout_inventory()
|
||||||
|
|||||||
@ -20,6 +20,9 @@ func _ready() -> void:
|
|||||||
_background.bg_color = Color("101925")
|
_background.bg_color = Color("101925")
|
||||||
_background.border_color = Color("456277")
|
_background.border_color = Color("456277")
|
||||||
_background.set_border_width_all(1)
|
_background.set_border_width_all(1)
|
||||||
|
_background.set_corner_radius_all(4)
|
||||||
|
_background.shadow_color = Color(0, 0, 0, 0.5)
|
||||||
|
_background.shadow_size = 3
|
||||||
|
|
||||||
func _physics_process(_delta: float) -> void:
|
func _physics_process(_delta: float) -> void:
|
||||||
if is_instance_valid(player):
|
if is_instance_valid(player):
|
||||||
@ -58,13 +61,26 @@ func _hull_ratio() -> float:
|
|||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
if not is_instance_valid(player):
|
if not is_instance_valid(player):
|
||||||
return
|
return
|
||||||
var color := Color("71e3ff") if _energy_ratio() >= 0.25 else Color("ffbd66")
|
_draw_bar(Vector2(-46, 58), _display_ratio, Color("efbd62"), 0)
|
||||||
var bar_position := Vector2(-44, 48)
|
_draw_bar(Vector2(-46, 72), _display_shield_ratio, Color("66cfff"), 1)
|
||||||
draw_style_box(_background, Rect2(bar_position - Vector2(2, 2), Vector2(92, 10)))
|
_draw_bar(Vector2(-46, 86), _display_hull_ratio, Color("ef6875"), 2)
|
||||||
draw_rect(Rect2(bar_position, Vector2(88 * _display_ratio, 6)), color)
|
|
||||||
var shield_bar_position := bar_position + Vector2(0, 14)
|
func _draw_bar(position: Vector2, ratio: float, color: Color, kind: int) -> void:
|
||||||
draw_style_box(_background, Rect2(shield_bar_position - Vector2(2, 2), Vector2(92, 10)))
|
draw_style_box(_background, Rect2(position - Vector2(14, 3), Vector2(110, 12)))
|
||||||
draw_rect(Rect2(shield_bar_position, Vector2(88 * _display_shield_ratio, 6)), Color.WHITE)
|
# Ten illuminated cells, with partial cells tracking the exact current value.
|
||||||
var hull_bar_position := bar_position + Vector2(0, 28)
|
for index in 10:
|
||||||
draw_style_box(_background, Rect2(hull_bar_position - Vector2(2, 2), Vector2(92, 10)))
|
var cell := Rect2(position + Vector2(index * 9.2, 0), Vector2(7.6, 6))
|
||||||
draw_rect(Rect2(hull_bar_position, Vector2(88 * _display_hull_ratio, 6)), Color("ef5967"))
|
draw_rect(cell, color.darkened(0.8))
|
||||||
|
var fraction := clampf(ratio * 10.0 - index, 0.0, 1.0)
|
||||||
|
if fraction > 0:
|
||||||
|
cell.size.x *= fraction
|
||||||
|
draw_rect(cell, color)
|
||||||
|
draw_rect(Rect2(cell.position, Vector2(cell.size.x, 2)), color.lightened(0.4))
|
||||||
|
var center := position + Vector2(-8, 3)
|
||||||
|
match kind:
|
||||||
|
0:
|
||||||
|
draw_polyline(PackedVector2Array([center + Vector2(1, -4), center + Vector2(-2, 0), center + Vector2(2, 0), center + Vector2(-1, 4)]), color, 1.4, true)
|
||||||
|
1:
|
||||||
|
draw_polyline(PackedVector2Array([center + Vector2(-3, -3), center + Vector2(3, -3), center + Vector2(2, 2), center + Vector2(0, 4), center + Vector2(-2, 2), center + Vector2(-3, -3)]), color, 1.2, true)
|
||||||
|
2:
|
||||||
|
draw_polyline(PackedVector2Array([center + Vector2(0, -4), center + Vector2(3, 0), center + Vector2(2, 3), center + Vector2(-2, 3), center + Vector2(-3, 0), center + Vector2(0, -4)]), color, 1.2, true)
|
||||||
|
|||||||
@ -11,6 +11,8 @@ var _label: Label
|
|||||||
var _sprite: Sprite2D
|
var _sprite: Sprite2D
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
if not is_star:
|
||||||
|
add_to_group("scanner_planets")
|
||||||
var sprite := Sprite2D.new()
|
var sprite := Sprite2D.new()
|
||||||
_sprite = sprite
|
_sprite = sprite
|
||||||
sprite.name = "BodySprite"
|
sprite.name = "BodySprite"
|
||||||
|
|||||||
@ -119,8 +119,8 @@ func claim() -> bool:
|
|||||||
elif kind == "ruin":
|
elif kind == "ruin":
|
||||||
var xp := roundi(_rng.randi_range(10, 20) * scale)
|
var xp := roundi(_rng.randi_range(10, 20) * scale)
|
||||||
GameState.captain.add_rewards(xp, 0)
|
GameState.captain.add_rewards(xp, 0)
|
||||||
var module := _drop_module(true)
|
_drop_module(true)
|
||||||
summary = "%d XP oraz %s (poziom %d)" % [xp, module.definition.display_name, module.item_level]
|
summary = "+%d doświadczenia · +1 moduł" % xp
|
||||||
_show_discovery(summary)
|
_show_discovery(summary)
|
||||||
else:
|
else:
|
||||||
match _rng.randi_range(0, 3):
|
match _rng.randi_range(0, 3):
|
||||||
@ -163,28 +163,29 @@ func _show_discovery(reward: String) -> void:
|
|||||||
layer.layer = 30
|
layer.layer = 30
|
||||||
layer.name = "AncientDiscoveryMessage"
|
layer.name = "AncientDiscoveryMessage"
|
||||||
get_parent().add_child(layer)
|
get_parent().add_child(layer)
|
||||||
var panel := PanelContainer.new()
|
|
||||||
panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
||||||
layer.add_child(panel)
|
|
||||||
panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP)
|
|
||||||
panel.offset_left = -300
|
|
||||||
panel.offset_right = 300
|
|
||||||
panel.offset_top = 70
|
|
||||||
var label := Label.new()
|
var label := Label.new()
|
||||||
label.custom_minimum_size = Vector2(600, 95)
|
layer.add_child(label)
|
||||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
label.set_anchors_and_offsets_preset(Control.PRESET_CENTER_TOP)
|
||||||
|
label.offset_left = -300
|
||||||
|
label.offset_right = 300
|
||||||
|
label.offset_top = 145
|
||||||
|
label.offset_bottom = 181
|
||||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
label.add_theme_font_size_override("font_size", 18)
|
label.add_theme_font_size_override("font_size", 15)
|
||||||
label.add_theme_color_override("font_color", Color("ffd166"))
|
label.add_theme_color_override("font_color", Color("d8d4b7"))
|
||||||
label.text = "Znalazłeś ślady starożytnej upadłej cywilizacji!\nZdobyłeś: " + reward
|
label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.85))
|
||||||
|
label.add_theme_constant_override("shadow_offset_x", 1)
|
||||||
|
label.add_theme_constant_override("shadow_offset_y", 2)
|
||||||
|
label.text = reward
|
||||||
if _module_left_outside:
|
if _module_left_outside:
|
||||||
label.text += "\nMagazyn pełny — moduł czeka obok planety."
|
label.text += " (obok planety)"
|
||||||
panel.add_child(label)
|
label.modulate.a = 0.0
|
||||||
var tween := layer.create_tween()
|
var tween := layer.create_tween()
|
||||||
tween.tween_interval(7.0)
|
tween.tween_property(label, "modulate:a", 1.0, 0.35)
|
||||||
tween.tween_property(panel, "modulate:a", 0.0, 0.5)
|
tween.tween_interval(3.5)
|
||||||
|
tween.tween_property(label, "modulate:a", 0.0, 0.8)
|
||||||
tween.tween_callback(layer.queue_free)
|
tween.tween_callback(layer.queue_free)
|
||||||
|
|
||||||
func _drop_module(ancient: bool) -> ItemInstance:
|
func _drop_module(ancient: bool) -> ItemInstance:
|
||||||
|
|||||||
@ -12,55 +12,17 @@ const BALANCE := preload("res://scripts/captain/captain_balance.gd")
|
|||||||
var _collapsed := false
|
var _collapsed := false
|
||||||
var _collapse_button: Button
|
var _collapse_button: Button
|
||||||
var _captain: CaptainProgress
|
var _captain: CaptainProgress
|
||||||
var _rank_labels: Dictionary = {}
|
|
||||||
var _effect_labels: Dictionary = {}
|
|
||||||
var _buttons: Dictionary = {}
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_build_portrait()
|
_build_portrait()
|
||||||
_captain = GameState.captain
|
_captain = GameState.captain
|
||||||
_captain.normalize_skill_ranks()
|
_captain.normalize_skill_ranks()
|
||||||
for skill in BALANCE.SKILL_NAMES:
|
|
||||||
var row := HBoxContainer.new()
|
|
||||||
row.name = skill
|
|
||||||
row.add_theme_constant_override("separation", 5)
|
|
||||||
var title := Label.new()
|
|
||||||
title.text = BALANCE.SKILL_NAMES[skill]
|
|
||||||
title.tooltip_text = BALANCE.SKILL_HELP[skill]
|
|
||||||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
||||||
title.add_theme_font_size_override("font_size", 11)
|
|
||||||
row.add_child(title)
|
|
||||||
var rank := Label.new()
|
|
||||||
rank.custom_minimum_size.x = 40
|
|
||||||
rank.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
||||||
rank.add_theme_font_size_override("font_size", 11)
|
|
||||||
row.add_child(rank)
|
|
||||||
_rank_labels[skill] = rank
|
|
||||||
var effect := Label.new()
|
|
||||||
effect.custom_minimum_size.x = 74
|
|
||||||
effect.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
|
||||||
effect.add_theme_font_size_override("font_size", 10)
|
|
||||||
row.add_child(effect)
|
|
||||||
_effect_labels[skill] = effect
|
|
||||||
var button := Button.new()
|
|
||||||
button.name = "Train_" + skill
|
|
||||||
button.text = "+"
|
|
||||||
button.custom_minimum_size = Vector2(24, 22)
|
|
||||||
button.add_theme_font_size_override("font_size", 13)
|
|
||||||
button.tooltip_text = "Rozwiń umiejętność za 1 punkt nauki.\n" + BALANCE.SKILL_HELP[skill]
|
|
||||||
button.pressed.connect(_train.bind(skill))
|
|
||||||
row.add_child(button)
|
|
||||||
_buttons[skill] = button
|
|
||||||
$Contents/Skills.add_child(row)
|
|
||||||
_captain.changed.connect(_refresh)
|
_captain.changed.connect(_refresh)
|
||||||
player.get_node("ShipInventory").connect("inventory_changed", _refresh)
|
player.get_node("ShipInventory").connect("inventory_changed", _refresh)
|
||||||
_refresh()
|
_refresh()
|
||||||
if bool(GameState.hud_panel_state.get("captain_collapsed", true)):
|
if bool(GameState.hud_panel_state.get("captain_collapsed", true)):
|
||||||
_toggle_collapsed()
|
_toggle_collapsed()
|
||||||
|
|
||||||
func _train(skill: String) -> void:
|
|
||||||
_captain.train_skill(skill)
|
|
||||||
|
|
||||||
func _refresh() -> void:
|
func _refresh() -> void:
|
||||||
level_label.text = "POZ. %d" % _captain.level
|
level_label.text = "POZ. %d" % _captain.level
|
||||||
xp_label.text = "XP %d / %d" % [_captain.experience, _captain.get_xp_required()]
|
xp_label.text = "XP %d / %d" % [_captain.experience, _captain.get_xp_required()]
|
||||||
@ -68,15 +30,6 @@ func _refresh() -> void:
|
|||||||
xp_bar.value = _captain.experience
|
xp_bar.value = _captain.experience
|
||||||
credits_label.text = "Kredyty: %d" % _captain.credits
|
credits_label.text = "Kredyty: %d" % _captain.credits
|
||||||
points_label.text = "Punkty: %d" % _captain.learning_points
|
points_label.text = "Punkty: %d" % _captain.learning_points
|
||||||
for skill in _buttons:
|
|
||||||
var rank := _captain.get_skill_rank(skill)
|
|
||||||
_rank_labels[skill].text = "%d/%d" % [rank, BALANCE.MAX_SKILL_RANK]
|
|
||||||
var suffix: String = {"piloting": "unik", "gunner": "DMG", "mechanic": "red.", "sharp_eye": "kryt"}[skill]
|
|
||||||
var bonus := _captain.get_skill_bonus(skill)
|
|
||||||
_effect_labels[skill].text = "%.1f%% %s" % [bonus * 100.0, suffix]
|
|
||||||
var next_bonus := BALANCE.skill_bonus(skill, rank + 1)
|
|
||||||
_buttons[skill].tooltip_text = BALANCE.SKILL_HELP[skill] + ("\nOsiągnięto limit 20 punktów." if rank >= BALANCE.MAX_SKILL_RANK else "\nNastępny punkt: %.1f%% → %.1f%%" % [bonus * 100.0, next_bonus * 100.0])
|
|
||||||
_buttons[skill].disabled = not _captain.can_train(skill)
|
|
||||||
|
|
||||||
func _build_portrait() -> void:
|
func _build_portrait() -> void:
|
||||||
custom_minimum_size = Vector2(320, 0)
|
custom_minimum_size = Vector2(320, 0)
|
||||||
|
|||||||
@ -1,49 +1,17 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
var _panel: PanelContainer
|
|
||||||
var _label: Label
|
var _label: Label
|
||||||
var _toggle: Button
|
|
||||||
var _open := false
|
|
||||||
var _tween: Tween
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
|
|
||||||
_panel = PanelContainer.new()
|
|
||||||
var style := StyleBoxFlat.new()
|
|
||||||
style.bg_color = Color("0b1520f5")
|
|
||||||
style.border_color = Color("a54e61")
|
|
||||||
style.set_border_width_all(1)
|
|
||||||
style.set_content_margin_all(12)
|
|
||||||
_panel.add_theme_stylebox_override("panel", style)
|
|
||||||
add_child(_panel)
|
|
||||||
_label = Label.new()
|
_label = Label.new()
|
||||||
_label.add_theme_font_size_override("font_size", 12)
|
_label.name = "EnemyDetails"
|
||||||
_panel.add_child(_label)
|
_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
_toggle = Button.new()
|
_label.add_theme_font_size_override("font_size", 9)
|
||||||
_toggle.text = "WRÓG ▾"
|
_label.add_theme_color_override("font_color", Color("c9acb5"))
|
||||||
preload("res://scripts/ui/hud_button_style.gd").apply(_toggle, Color("ff9aae"))
|
_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
add_child(_toggle)
|
_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
_toggle.pressed.connect(_toggle_drawer)
|
add_child(_label)
|
||||||
_open = bool(GameState.hud_panel_state.get("enemy_open", false))
|
|
||||||
get_viewport().size_changed.connect(_layout)
|
|
||||||
_layout()
|
|
||||||
|
|
||||||
func _layout() -> void:
|
|
||||||
position.x = (get_viewport_rect().size.x - 390.0) * 0.5
|
|
||||||
_panel.size = Vector2(390, 150)
|
|
||||||
_panel.position.y = 56.0 if _open else -160.0
|
|
||||||
_toggle.position = Vector2(130, 12)
|
|
||||||
_toggle.size = Vector2(130, 36)
|
|
||||||
|
|
||||||
func _toggle_drawer() -> void:
|
|
||||||
_open = not _open
|
|
||||||
GameState.hud_panel_state["enemy_open"] = _open
|
|
||||||
_toggle.text = "WRÓG ▴" if _open else "WRÓG ▾"
|
|
||||||
if _tween != null:
|
|
||||||
_tween.kill()
|
|
||||||
_tween = create_tween()
|
|
||||||
_tween.tween_property(_panel, "position:y", 56.0 if _open else -160.0, 0.25).set_trans(Tween.TRANS_CUBIC)
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
var enemy: Node = null
|
var enemy: Node = null
|
||||||
@ -53,8 +21,8 @@ func _process(_delta: float) -> void:
|
|||||||
var profile := DamageTypes.enemy_profile(GameState.system_level, GameState.system_seed)
|
var profile := DamageTypes.enemy_profile(GameState.system_level, GameState.system_seed)
|
||||||
var kind := int(profile["kind"])
|
var kind := int(profile["kind"])
|
||||||
var names := ["Działko plazmowe", "Działko maszynowe", "Laser", "Rakiety", "Działko gamma"]
|
var names := ["Działko plazmowe", "Działko maszynowe", "Laser", "Rakiety", "Działko gamma"]
|
||||||
_label.text = "WRÓG · UKŁAD %d\n%s · %s\n%s" % [GameState.system_level, names[kind], DamageTypes.NAMES[DamageTypes.for_weapon(kind)], DamageTypes.describe(profile["absorption"])]
|
_label.text = "%s\n%s" % [names[kind], DamageTypes.describe(profile["absorption"])]
|
||||||
if enemy != null:
|
if enemy != null:
|
||||||
_label.text += "\nDPS: %.1f (%s)\nTarcze: %.0f / %.0f · bez regeneracji" % [enemy.get_weapon_dps(), ["zwykły", "elita", "boss"][enemy.loot_tier], enemy.shields, enemy.max_shields]
|
_label.text += "\nDPS: %.1f (%s)\nTarcze: %.0f / %.0f" % [enemy.get_weapon_dps(), ["zwykły", "elita", "boss"][enemy.loot_tier], enemy.shields, enemy.max_shields]
|
||||||
else:
|
else:
|
||||||
_label.text += "\nUkład oczyszczony"
|
_label.text += "\nUkład oczyszczony"
|
||||||
|
|||||||
@ -6,11 +6,16 @@ const WEAPON_DISPLAY_ORDER := [0, 2, 1]
|
|||||||
const AMBER := Color("efb568")
|
const AMBER := Color("efb568")
|
||||||
var player: CharacterBody2D
|
var player: CharacterBody2D
|
||||||
var game: Node2D
|
var game: Node2D
|
||||||
|
var _enemy_details: Control
|
||||||
var _inventory: ShipInventory
|
var _inventory: ShipInventory
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
_inventory = player.get_node("ShipInventory")
|
_inventory = player.get_node("ShipInventory")
|
||||||
|
_enemy_details = Control.new()
|
||||||
|
_enemy_details.name = "EnemyDetailsPanel"
|
||||||
|
_enemy_details.set_script(preload("res://scripts/ui/enemy_drawer.gd"))
|
||||||
|
add_child(_enemy_details)
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if is_instance_valid(player):
|
if is_instance_valid(player):
|
||||||
@ -18,6 +23,9 @@ func _process(delta: float) -> void:
|
|||||||
var local := get_global_transform().affine_inverse() * screen
|
var local := get_global_transform().affine_inverse() * screen
|
||||||
var overlaps := Rect2(Vector2.ZERO, size).grow(36).has_point(local)
|
var overlaps := Rect2(Vector2.ZERO, size).grow(36).has_point(local)
|
||||||
modulate.a = move_toward(modulate.a, 0.18 if overlaps else 1.0, delta * 4.0)
|
modulate.a = move_toward(modulate.a, 0.18 if overlaps else 1.0, delta * 4.0)
|
||||||
|
var cell := (size.x - 238) / 3.0
|
||||||
|
_enemy_details.position = Vector2(218.0 + 2.0 * cell, 57)
|
||||||
|
_enemy_details.size = Vector2(cell, 56)
|
||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
func _text(value: String, point: Vector2, width: float, font_size: int, color: Color) -> void:
|
func _text(value: String, point: Vector2, width: float, font_size: int, color: Color) -> void:
|
||||||
@ -34,7 +42,9 @@ func _draw() -> void:
|
|||||||
draw_line(Vector2(28, 2), Vector2(size.x - 28, 2), CYAN, 2, true)
|
draw_line(Vector2(28, 2), Vector2(size.x - 28, 2), CYAN, 2, true)
|
||||||
# Put weapon cards beside the counters rather than stacking three rows.
|
# Put weapon cards beside the counters rather than stacking three rows.
|
||||||
_text("DPS: %.1f" % _inventory.get_total_dps(), Vector2(20, 81), 172, 12, CYAN)
|
_text("DPS: %.1f" % _inventory.get_total_dps(), Vector2(20, 81), 172, 12, CYAN)
|
||||||
_text(DamageTypes.describe(player.get("absorption")), Vector2(205, 81), size.x - 225, 11, Color("a9d69a"))
|
var absorption: Vector3 = player.get("absorption")
|
||||||
|
for index in 3:
|
||||||
|
_text("%s - %.1f%%" % [DamageTypes.NAMES[index], absorption[index]], Vector2(205, 77 + index * 15), 2.0 * (size.x - 238) / 3.0, 11, Color("a9d69a"))
|
||||||
var cell := (size.x - 238) / 3.0
|
var cell := (size.x - 238) / 3.0
|
||||||
var values := [_inventory.missile_ammunition, _inventory.ammunition, int(game.call("get_remaining_enemies"))]
|
var values := [_inventory.missile_ammunition, _inventory.ammunition, int(game.call("get_remaining_enemies"))]
|
||||||
var captions := ["RAKIETY", "AMUNICJA", "WROGOWIE"]
|
var captions := ["RAKIETY", "AMUNICJA", "WROGOWIE"]
|
||||||
|
|||||||
@ -26,7 +26,7 @@ func _ready() -> void:
|
|||||||
_gamma_buy.pressed.connect(func() -> void: _inventory.buy_irradiated_ammunition())
|
_gamma_buy.pressed.connect(func() -> void: _inventory.buy_irradiated_ammunition())
|
||||||
next_button.pressed.connect(func() -> void: next_level_requested.emit())
|
next_button.pressed.connect(func() -> void: next_level_requested.emit())
|
||||||
buy_ammunition_button.pressed.connect(_buy_ammunition)
|
buy_ammunition_button.pressed.connect(_buy_ammunition)
|
||||||
reset_skills_button.pressed.connect(_reset_skills)
|
reset_skills_button.hide()
|
||||||
_missile_button = Button.new()
|
_missile_button = Button.new()
|
||||||
_missile_button.custom_minimum_size.y = 36
|
_missile_button.custom_minimum_size.y = 36
|
||||||
_missile_button.add_theme_font_size_override("font_size", 12)
|
_missile_button.add_theme_font_size_override("font_size", 12)
|
||||||
|
|||||||
@ -10,9 +10,6 @@ func _process(_delta: float) -> void:
|
|||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
if not is_instance_valid(game) or not is_instance_valid(game.get("protected_planet")):
|
if not is_instance_valid(game) or not is_instance_valid(game.get("protected_planet")):
|
||||||
return
|
return
|
||||||
var planet: Node2D = game.get("protected_planet")
|
|
||||||
var color := Color("77e8bc") if bool(game.get("system_cleared")) else Color("ffbd83")
|
|
||||||
_draw_target(planet.global_position, str(planet.get("display_name")) + " — zamieszkana", color)
|
|
||||||
var portal: Node2D = game.get("wormhole")
|
var portal: Node2D = game.get("wormhole")
|
||||||
if is_instance_valid(portal):
|
if is_instance_valid(portal):
|
||||||
_draw_target(portal.global_position, "WORMHOLE — następny układ", Color("89e8ff"))
|
_draw_target(portal.global_position, "WORMHOLE — następny układ", Color("89e8ff"))
|
||||||
|
|||||||
@ -1,21 +1,45 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
const FONT := preload("res://assets/fonts/font2.ttf")
|
const DOT_RADIUS := 3.0
|
||||||
|
const SCAN_RING_RADIUS := 48.0
|
||||||
var player: CharacterBody2D
|
var player: CharacterBody2D
|
||||||
var targets: Array[Node2D] = []
|
var targets: Array[Node2D] = []
|
||||||
|
var _elapsed := 0.0
|
||||||
|
var _previous_player_position := Vector2.ZERO
|
||||||
|
var _player_position := Vector2.ZERO
|
||||||
|
var _positions: Dictionary = {}
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
process_physics_priority = 10
|
||||||
|
_player_position = player.global_position
|
||||||
|
_previous_player_position = _player_position
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
_elapsed += delta
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func _physics_process(_delta: float) -> void:
|
||||||
if not is_instance_valid(player):
|
if not is_instance_valid(player):
|
||||||
return
|
return
|
||||||
|
_previous_player_position = _player_position
|
||||||
|
_player_position = player.global_position
|
||||||
|
if _previous_player_position.distance_to(_player_position) > 600.0:
|
||||||
|
_previous_player_position = _player_position
|
||||||
targets.clear()
|
targets.clear()
|
||||||
for group in ["enemies", "exploration_pois"]:
|
var next_positions: Dictionary = {}
|
||||||
|
for group in ["enemies", "exploration_pois", "scanner_planets"]:
|
||||||
for target in get_tree().get_nodes_in_group(group):
|
for target in get_tree().get_nodes_in_group(group):
|
||||||
if player.get_parent().is_ancestor_of(target) and is_target_in_range(target):
|
if player.get_parent().is_ancestor_of(target) and not target.is_queued_for_deletion() and is_target_in_range(target):
|
||||||
|
if group == "scanner_planets" and (not bool(target.get("is_protected")) or not bool(player.get_parent().get("system_cleared"))):
|
||||||
|
continue
|
||||||
targets.append(target)
|
targets.append(target)
|
||||||
queue_redraw()
|
var current: Vector2 = target.global_position
|
||||||
|
var previous: Vector2 = _positions.get(target.get_instance_id(), [current, current])[1]
|
||||||
|
if previous.distance_to(current) > 600.0:
|
||||||
|
previous = current
|
||||||
|
next_positions[target.get_instance_id()] = [previous, current]
|
||||||
|
_positions = next_positions
|
||||||
|
|
||||||
func get_scan_range() -> float:
|
func get_scan_range() -> float:
|
||||||
var transform := get_viewport().get_canvas_transform()
|
var transform := get_viewport().get_canvas_transform()
|
||||||
@ -29,38 +53,39 @@ func is_target_in_range(target: Node2D) -> bool:
|
|||||||
func _draw() -> void:
|
func _draw() -> void:
|
||||||
if not is_instance_valid(player) or bool(player.get("_is_destroyed")) or bool(player.get("is_docked")):
|
if not is_instance_valid(player) or bool(player.get("_is_destroyed")) or bool(player.get("is_docked")):
|
||||||
return
|
return
|
||||||
var bounds := Rect2(Vector2(344, 70), Vector2(maxf(100, size.x - 680), maxf(100, size.y - 230)))
|
var canvas := get_viewport().get_canvas_transform()
|
||||||
var origin := get_viewport().get_canvas_transform() * player.global_position
|
var to_local := get_global_transform().affine_inverse()
|
||||||
var anchor := Vector2(clampf(origin.x, bounds.position.x, bounds.end.x), clampf(origin.y, bounds.position.y, bounds.end.y))
|
# Match the rendered ship and targets, rather than their discrete physics positions.
|
||||||
|
var fraction := Engine.get_physics_interpolation_fraction()
|
||||||
|
var origin: Vector2 = to_local * (canvas * _previous_player_position.lerp(_player_position, fraction))
|
||||||
|
var radius := SCAN_RING_RADIUS * canvas.x.length()
|
||||||
|
var pulse := (sin(_elapsed * TAU * 0.9) + 1.0) * 0.5
|
||||||
for target in targets:
|
for target in targets:
|
||||||
if not is_instance_valid(target) or target.is_queued_for_deletion():
|
if not is_instance_valid(target) or target.is_queued_for_deletion():
|
||||||
continue
|
continue
|
||||||
var color := Color("ff7777")
|
var color := Color("ff5555")
|
||||||
var caption := "WRÓG"
|
|
||||||
if target.is_in_group("enemies"):
|
if target.is_in_group("enemies"):
|
||||||
if float(target.get("hull")) <= 0:
|
if float(target.get("hull")) <= 0:
|
||||||
continue
|
continue
|
||||||
|
elif target.is_in_group("scanner_planets"):
|
||||||
|
color = Color("b68aff")
|
||||||
else:
|
else:
|
||||||
|
if bool(target.get("_claimed")):
|
||||||
|
continue
|
||||||
match str(target.get("kind")):
|
match str(target.get("kind")):
|
||||||
"asteroid":
|
"asteroid":
|
||||||
color = Color("a6c8e0")
|
color = Color("a8adb5")
|
||||||
caption = "AST"
|
|
||||||
"wreck":
|
"wreck":
|
||||||
color = Color("ffd166")
|
color = Color("b98050")
|
||||||
caption = "WRAK"
|
|
||||||
"ruin":
|
"ruin":
|
||||||
color = Color("d2a4ff")
|
color = Color("6be58b")
|
||||||
caption = "?"
|
_:
|
||||||
var screen := get_viewport().get_canvas_transform() * target.global_position
|
|
||||||
if get_viewport_rect().has_point(screen):
|
|
||||||
continue
|
continue
|
||||||
var direction := (screen - anchor).normalized()
|
var positions: Array = _positions[target.get_instance_id()]
|
||||||
var point := screen
|
var target_position: Vector2 = positions[0].lerp(positions[1], fraction)
|
||||||
if not bounds.has_point(screen):
|
var offset: Vector2 = to_local * (canvas * target_position) - origin
|
||||||
var dx := (bounds.end.x - anchor.x) if direction.x > 0 else (anchor.x - bounds.position.x)
|
if offset.length_squared() < 0.01:
|
||||||
var dy := (bounds.end.y - anchor.y) if direction.y > 0 else (anchor.y - bounds.position.y)
|
continue
|
||||||
var distance := minf(dx / maxf(absf(direction.x), 0.001), dy / maxf(absf(direction.y), 0.001))
|
var point := origin + offset.normalized() * radius
|
||||||
point = anchor + direction * distance
|
draw_circle(point, DOT_RADIUS + 3.0 + pulse * 2.0, Color(color, 0.08 + pulse * 0.12), true, -1.0, true)
|
||||||
var side := Vector2(-direction.y, direction.x)
|
draw_circle(point, DOT_RADIUS, Color(color, 0.55 + pulse * 0.45), true, -1.0, true)
|
||||||
draw_colored_polygon(PackedVector2Array([point + direction * 10, point - direction * 6 + side * 6, point - direction * 6 - side * 6]), color)
|
|
||||||
draw_string(FONT, point + Vector2(-18, 25), caption, HORIZONTAL_ALIGNMENT_LEFT, -1, 11, color)
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
extends PanelContainer
|
extends PanelContainer
|
||||||
|
|
||||||
|
@export var standalone := false
|
||||||
var _inventory: ShipInventory
|
var _inventory: ShipInventory
|
||||||
var _expanded := false
|
var _expanded := false
|
||||||
var _toggle: Button
|
var _toggle: Button
|
||||||
@ -39,8 +40,9 @@ func _ready() -> void:
|
|||||||
_missile.pressed.connect(func() -> void: _inventory.craft_missile())
|
_missile.pressed.connect(func() -> void: _inventory.craft_missile())
|
||||||
_gamma = _button("CraftGamma", "+10 NAPROMIENIOWANEJ · 1 URAN + 10 CZĘŚCI")
|
_gamma = _button("CraftGamma", "+10 NAPROMIENIOWANEJ · 1 URAN + 10 CZĘŚCI")
|
||||||
_gamma.pressed.connect(func() -> void: _inventory.craft_irradiated_ammunition())
|
_gamma.pressed.connect(func() -> void: _inventory.craft_irradiated_ammunition())
|
||||||
_details.hide()
|
_details.visible = standalone
|
||||||
if bool(GameState.hud_panel_state.get("crafting_expanded", false)):
|
_toggle.visible = not standalone
|
||||||
|
if not standalone and bool(GameState.hud_panel_state.get("crafting_expanded", false)):
|
||||||
_toggle_expanded()
|
_toggle_expanded()
|
||||||
_inventory.ammunition_changed.connect(_refresh)
|
_inventory.ammunition_changed.connect(_refresh)
|
||||||
GameState.captain.changed.connect(_refresh)
|
GameState.captain.changed.connect(_refresh)
|
||||||
|
|||||||
56
scripts/ui/ship_profile.gd
Normal file
56
scripts/ui/ship_profile.gd
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
extends Button
|
||||||
|
|
||||||
|
const FONT := preload("res://assets/fonts/font2.ttf")
|
||||||
|
var player: CharacterBody2D
|
||||||
|
var _texture: Texture2D
|
||||||
|
var _ship_name := ""
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
focus_mode = Control.FOCUS_NONE
|
||||||
|
text = ""
|
||||||
|
for state in ["normal", "hover", "pressed", "focus"]:
|
||||||
|
add_theme_stylebox_override(state, StyleBoxEmpty.new())
|
||||||
|
var ship := ShipCatalog.for_path(GameState.selected_ship_path)
|
||||||
|
_ship_name = ship["name"]
|
||||||
|
_texture = load(ShipCatalog.texture_path(ship)) as Texture2D
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
queue_redraw()
|
||||||
|
|
||||||
|
func _draw() -> void:
|
||||||
|
if not is_instance_valid(player):
|
||||||
|
return
|
||||||
|
var portrait_center := Vector2(size.x - 54, 55)
|
||||||
|
var color := Color("91dbea") if is_hovered() else Color("376779")
|
||||||
|
var style := StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color("091522f5")
|
||||||
|
style.border_color = color
|
||||||
|
style.set_border_width_all(1)
|
||||||
|
style.set_corner_radius_all(12)
|
||||||
|
draw_style_box(style, Rect2(0, 9, size.x - 48, 94))
|
||||||
|
draw_circle(portrait_center, 50, Color("0a1725"))
|
||||||
|
draw_arc(portrait_center, 50, 0, TAU, 64, color, 3, true)
|
||||||
|
draw_arc(portrait_center, 44, 0, TAU, 64, Color("efb568"), 1, true)
|
||||||
|
if _texture != null:
|
||||||
|
var dimensions := _texture.get_size()
|
||||||
|
var display_size := dimensions * (70.0 / maxf(dimensions.x, dimensions.y))
|
||||||
|
draw_texture_rect(_texture, Rect2(portrait_center - display_size * 0.5, display_size), false)
|
||||||
|
draw_string(FONT, Vector2(14, 29), _ship_name.to_upper(), HORIZONTAL_ALIGNMENT_LEFT, size.x - 120, 13, Color("c9e8ff"))
|
||||||
|
_bar(Rect2(12, 38, size.x - 118, 24), float(player.get("hull")), float(player.get("max_hull")), Color("df7158"), "KADŁUB")
|
||||||
|
_bar(Rect2(12, 68, size.x - 118, 24), float(player.get("shields")), float(player.get("max_shields")), Color("478ddd"), "TARCZE")
|
||||||
|
|
||||||
|
func _bar(rect: Rect2, value: float, maximum: float, color: Color, caption: String) -> void:
|
||||||
|
var background := StyleBoxFlat.new()
|
||||||
|
background.bg_color = Color("060e18")
|
||||||
|
background.border_color = color.darkened(0.4)
|
||||||
|
background.set_border_width_all(1)
|
||||||
|
background.set_corner_radius_all(7)
|
||||||
|
draw_style_box(background, rect)
|
||||||
|
var fill_rect := rect.grow(-3)
|
||||||
|
fill_rect.size.x *= clampf(value / maxf(1.0, maximum), 0.0, 1.0)
|
||||||
|
if fill_rect.size.x > 0:
|
||||||
|
var fill := StyleBoxFlat.new()
|
||||||
|
fill.bg_color = color
|
||||||
|
fill.set_corner_radius_all(4)
|
||||||
|
draw_style_box(fill, fill_rect)
|
||||||
|
draw_string(FONT, rect.position + Vector2(0, 16), "%s %.0f / %.0f" % [caption, value, maximum], HORIZONTAL_ALIGNMENT_CENTER, rect.size.x, 10, Color.WHITE)
|
||||||
1
scripts/ui/ship_profile.gd.uid
Normal file
1
scripts/ui/ship_profile.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dsxp2c0jn6qlh
|
||||||
40
tests/ships/flight_profile_test.gd
Normal file
40
tests/ships/flight_profile_test.gd
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
GameState.start_new_run()
|
||||||
|
GameState.captain.skills = {"piloting": 4, "gunner": 3}
|
||||||
|
GameState.captain.learning_points = 2
|
||||||
|
var game := preload("res://scenes/game/game.tscn").instantiate()
|
||||||
|
add_child(game)
|
||||||
|
await get_tree().process_frame
|
||||||
|
await get_tree().process_frame
|
||||||
|
assert(GameState.captain.learning_points == 9)
|
||||||
|
assert(GameState.captain.skills.is_empty())
|
||||||
|
GameState.captain.normalize_skill_ranks()
|
||||||
|
assert(GameState.captain.learning_points == 9)
|
||||||
|
assert(GameState.captain.get_skill_bonus("piloting") == 0.0)
|
||||||
|
assert(not GameState.captain.train_skill("gunner"))
|
||||||
|
var root := game.get_node("HUD/HUDRoot")
|
||||||
|
assert(not root.has_node("EnemyDrawer"))
|
||||||
|
assert(root.get_node("CaptainPanel/Contents/Skills").get_child_count() == 0)
|
||||||
|
var drawer := root.get_node("ShipDrawer") as Control
|
||||||
|
assert(not drawer.visible)
|
||||||
|
root.get_node("ShipToggle").emit_signal("pressed")
|
||||||
|
assert(drawer.visible)
|
||||||
|
assert(drawer.has_node("Scroll/Contents/ShipEquipmentPanel/Contents/EquipmentLayout/Weapon1"))
|
||||||
|
assert(not drawer.has_node("Scroll/Contents/ShipEquipmentPanel/Contents/CraftingPanel"))
|
||||||
|
var navigation := root.get_node("TopNavigation")
|
||||||
|
navigation.get_child(0).emit_signal("pressed")
|
||||||
|
assert(root.get_node("CraftingWindow").visible)
|
||||||
|
assert(root.get_node("CraftingWindow/Contents/CraftingPanel/Contents/CraftingDetails").visible)
|
||||||
|
navigation.get_child(1).emit_signal("pressed")
|
||||||
|
assert(not root.get_node("CraftingWindow").visible)
|
||||||
|
assert(root.get_node("CaptainSkillsWindow").visible)
|
||||||
|
assert(root.get_node("CaptainSkillsWindow/Contents/SkillsContent").get_child_count() == 0)
|
||||||
|
root.get_node("CaptainSkillsWindow/Contents").get_child(0).get_child(1).emit_signal("pressed")
|
||||||
|
assert(not root.get_node("CaptainSkillsWindow").visible)
|
||||||
|
assert(root.has_node("Cockpit/EnemyDetailsPanel/EnemyDetails"))
|
||||||
|
print("FLIGHT_PROFILE_TEST: PASS navigation, ship slots, crafting, empty skills, enemy details and legacy point refund")
|
||||||
|
# Keep the result available briefly for the runtime debugger.
|
||||||
|
await get_tree().create_timer(15.0).timeout
|
||||||
|
get_tree().quit()
|
||||||
1
tests/ships/flight_profile_test.gd.uid
Normal file
1
tests/ships/flight_profile_test.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://da1s61ut1jgjg
|
||||||
6
tests/ships/flight_profile_test.tscn
Normal file
6
tests/ships/flight_profile_test.tscn
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tests/ships/flight_profile_test.gd" id="1"]
|
||||||
|
|
||||||
|
[node name="FlightProfileTest" type="Node"]
|
||||||
|
script = ExtResource("1")
|
||||||
Loading…
x
Reference in New Issue
Block a user