Zmiany
This commit is contained in:
parent
750c1075d8
commit
a0a2278421
Binary file not shown.
|
Before Width: | Height: | Size: 996 KiB After Width: | Height: | Size: 1014 KiB |
@ -25,3 +25,12 @@ Use case: stylized-concept. Final production square ability icon for a realistic
|
||||
## power_redirect
|
||||
|
||||
Use case: stylized-concept. Final production square ability icon for a realistic painterly space RPG. Highly polished hand-painted digital game art, cinematic material detail, strong simple silhouette readable at 40 pixels, centered composition with 12% safe margin. Dark navy outer corners, restrained luminous accent, no text, no letters, no numbers, no interface, no border. One iconic subject, not a scene. Power Redirect active skill: a heavy metallic spaceship shield emitter on the left splitting open and releasing a powerful curved orange-gold energy stream into a bright compact cyan reactor core on the right. A bold luminous flow of power visually connects the two components. Burnt orange, pale gold, electric teal highlights, realistic sci-fi machinery, strong simple two-part silhouette.
|
||||
|
||||
|
||||
## lucky (2026-10-17)
|
||||
Built-in image_gen, transparent background. Saved as assets/icons/captain/lucky.png.
|
||||
Final prompt: Use case: stylized-concept. Asset: single premium science fiction game captain skill inventory icon, square, transparent background. Skill LUCK: sculptural futuristic four-leaf clover made from luminous emerald crystal and brushed gold metal, with a small gold orbital ring and a few tiny golden sparks. Centered bold compact silhouette readable at 40 pixels, exquisite painted 3D game item rendering, dramatic highlights, rich gold and green, isolated cutout, no text, no letters, no frame, no backdrop. Save resulting asset for game integration.
|
||||
|
||||
## explorer (2026-10-17)
|
||||
Built-in image_gen, transparent background. Saved as assets/icons/captain/explorer.png.
|
||||
Final prompt: Use case: stylized-concept. Single premium science fiction game captain skill icon EXPLORER. Centered bold compact 3D sculptural deep space navigation compass, weathered titanium and gold ring surrounding luminous turquoise holographic miniature planet and three-point star, subtle emerald cyan trails suggesting discovery, exquisitely detailed painterly game item artwork with dramatic highlights, readable small silhouette, isolated on genuinely transparent background. Square composition, generous transparent margins, no text no numbers no frame no scenery. Match premium sci-fi inventory artwork.
|
||||
|
||||
BIN
assets/icons/captain/explorer.png
Normal file
BIN
assets/icons/captain/explorer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
40
assets/icons/captain/explorer.png.import
Normal file
40
assets/icons/captain/explorer.png.import
Normal file
@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://btk4ywrx737fw"
|
||||
path="res://.godot/imported/explorer.png-5f686e01e5775b401dc40513f1c279ea.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/icons/captain/explorer.png"
|
||||
dest_files=["res://.godot/imported/explorer.png-5f686e01e5775b401dc40513f1c279ea.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
|
||||
BIN
assets/icons/captain/lucky.png
Normal file
BIN
assets/icons/captain/lucky.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
40
assets/icons/captain/lucky.png.import
Normal file
40
assets/icons/captain/lucky.png.import
Normal file
@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ddyvf2s8nmvma"
|
||||
path="res://.godot/imported/lucky.png-8c04401bc3d2099ccc953cd3aeeed557.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/icons/captain/lucky.png"
|
||||
dest_files=["res://.godot/imported/lucky.png-8c04401bc3d2099ccc953cd3aeeed557.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
|
||||
@ -43,7 +43,7 @@ text = "SYSTEMY POKŁADOWE"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="EquipmentLayout" type="Control" parent="Contents"]
|
||||
custom_minimum_size = Vector2(284, 552)
|
||||
custom_minimum_size = Vector2(264, 432)
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
@ -59,130 +59,130 @@ script = ExtResource("1_schematic")
|
||||
|
||||
[node name="Weapon1" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 22.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 86.0
|
||||
offset_left = 12.0
|
||||
offset_top = 20.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 76.0
|
||||
slot_kind = "weapon"
|
||||
slot_index = 0
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Weapon2" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 22.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 86.0
|
||||
offset_left = 196.0
|
||||
offset_top = 20.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 76.0
|
||||
slot_kind = "weapon"
|
||||
slot_index = 1
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Weapon3" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 110.0
|
||||
offset_top = 22.0
|
||||
offset_right = 174.0
|
||||
offset_bottom = 86.0
|
||||
offset_left = 104.0
|
||||
offset_top = 20.0
|
||||
offset_right = 160.0
|
||||
offset_bottom = 76.0
|
||||
slot_kind = "weapon"
|
||||
slot_index = 2
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Module1" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 108.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 172.0
|
||||
offset_left = 12.0
|
||||
offset_top = 96.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 152.0
|
||||
slot_kind = "module"
|
||||
slot_index = 0
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Module2" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 200.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 264.0
|
||||
offset_left = 12.0
|
||||
offset_top = 164.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 220.0
|
||||
slot_kind = "module"
|
||||
slot_index = 1
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Module3" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 292.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 356.0
|
||||
offset_left = 12.0
|
||||
offset_top = 232.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 288.0
|
||||
slot_kind = "module"
|
||||
slot_index = 2
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Module4" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 384.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 448.0
|
||||
offset_left = 12.0
|
||||
offset_top = 300.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 356.0
|
||||
slot_kind = "module"
|
||||
slot_index = 3
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Module5" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 14.0
|
||||
offset_top = 476.0
|
||||
offset_right = 78.0
|
||||
offset_bottom = 540.0
|
||||
offset_left = 12.0
|
||||
offset_top = 368.0
|
||||
offset_right = 68.0
|
||||
offset_bottom = 424.0
|
||||
slot_kind = "module"
|
||||
slot_index = 4
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Extension1" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 108.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 172.0
|
||||
offset_left = 196.0
|
||||
offset_top = 96.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 152.0
|
||||
slot_kind = "extension"
|
||||
slot_index = 0
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Extension2" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 200.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 264.0
|
||||
offset_left = 196.0
|
||||
offset_top = 164.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 220.0
|
||||
slot_kind = "extension"
|
||||
slot_index = 1
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Extension3" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 292.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 356.0
|
||||
offset_left = 196.0
|
||||
offset_top = 232.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 288.0
|
||||
slot_kind = "extension"
|
||||
slot_index = 2
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Extension4" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 384.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 448.0
|
||||
offset_left = 196.0
|
||||
offset_top = 300.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 356.0
|
||||
slot_kind = "extension"
|
||||
slot_index = 3
|
||||
overlay_slot = true
|
||||
|
||||
[node name="Extension5" parent="Contents/EquipmentLayout" instance=ExtResource("2_slot")]
|
||||
layout_mode = 0
|
||||
offset_left = 206.0
|
||||
offset_top = 476.0
|
||||
offset_right = 270.0
|
||||
offset_bottom = 540.0
|
||||
offset_left = 196.0
|
||||
offset_top = 368.0
|
||||
offset_right = 252.0
|
||||
offset_bottom = 424.0
|
||||
slot_kind = "extension"
|
||||
slot_index = 4
|
||||
overlay_slot = true
|
||||
|
||||
@ -22,8 +22,7 @@ static func value(skill: String, rank: int) -> float:
|
||||
return float(DATA[skill]["values"][clampi(rank, 1, MAX_RANK) - 1])
|
||||
|
||||
static func icon(skill: String) -> Texture2D:
|
||||
var extension := "svg" if skill in ["lucky", "explorer"] else "png"
|
||||
return load("res://assets/icons/captain/%s.%s" % [skill, extension]) as Texture2D
|
||||
return load("res://assets/icons/captain/%s.png" % skill) as Texture2D
|
||||
|
||||
static func description(skill: String, rank: int) -> String:
|
||||
var amount := roundi(value(skill, rank) * 100.0)
|
||||
|
||||
@ -131,7 +131,16 @@ func get_tier_tint() -> Color:
|
||||
func get_visual_size() -> float:
|
||||
return 112.0 if loot_tier == 2 else (68.0 if loot_tier == 1 else 56.0)
|
||||
|
||||
func regenerate_defenses(delta: float) -> void:
|
||||
if hull <= 0.0 or is_queued_for_deletion():
|
||||
return
|
||||
hull = minf(max_hull, hull + max_hull * 0.01 * delta)
|
||||
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 _physics_process(delta: float) -> void:
|
||||
regenerate_defenses(delta)
|
||||
_slow_remaining = maxf(0.0, _slow_remaining - delta)
|
||||
_weapon_timer = maxf(0.0, _weapon_timer - delta)
|
||||
if not is_instance_valid(_player) or bool(_player.get("_is_destroyed")):
|
||||
|
||||
@ -24,7 +24,7 @@ var _sidebar: PanelContainer
|
||||
var _sidebar_toggle: Button
|
||||
var _sidebar_open := false
|
||||
var _sidebar_tween: Tween
|
||||
const SIDEBAR_WIDTH := 306.0
|
||||
const SIDEBAR_WIDTH := 286.0
|
||||
const EQUIPMENT_UI_SCALE := 0.8
|
||||
var _debug_scroll: ScrollContainer
|
||||
var _debug_actions: VBoxContainer
|
||||
@ -327,8 +327,13 @@ func _layout_sidebar() -> void:
|
||||
return
|
||||
if _sidebar_tween != null and _sidebar_tween.is_running():
|
||||
_sidebar_tween.kill()
|
||||
_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 - 148.0 - _ship_passive_reserved_height) / EQUIPMENT_UI_SCALE)))
|
||||
var scroll := _sidebar.get_node("Scroll") as ScrollContainer
|
||||
scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
var content_height: float = scroll.get_node("Contents").get_combined_minimum_size().y + 16.0
|
||||
var available_height := maxf(100.0, $HUDRoot.size.y - 148.0 - _ship_passive_reserved_height)
|
||||
var panel_scale := minf(EQUIPMENT_UI_SCALE, available_height / content_height)
|
||||
_sidebar.scale = Vector2.ONE * panel_scale
|
||||
_sidebar.size = Vector2(maxf(SIDEBAR_WIDTH, _sidebar.get_combined_minimum_size().x), content_height)
|
||||
_cockpit.scale = Vector2.ONE * 0.85
|
||||
_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)
|
||||
@ -341,7 +346,7 @@ func _layout_sidebar() -> void:
|
||||
_captain_skills_ui.call_deferred("_layout")
|
||||
_layout_debug()
|
||||
if not _sidebar.has_meta("floating_window_dragged"):
|
||||
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * EQUIPMENT_UI_SCALE - 8, 136)
|
||||
_sidebar.position = Vector2($HUDRoot.size.x - _sidebar.size.x * _sidebar.scale.x - 8, 136)
|
||||
else:
|
||||
_window_manager.call("clamp_window", _sidebar)
|
||||
_sidebar.visible = _sidebar_open
|
||||
|
||||
@ -8,6 +8,7 @@ var restart_inventory: Dictionary = {}
|
||||
var system_level: int = 1
|
||||
var system_seed: int = 0
|
||||
var explored_pois: Dictionary = {}
|
||||
var asteroid_retry_seeds: Dictionary = {}
|
||||
var exploration_wreck_bonuses: Dictionary = {}
|
||||
var hud_panel_state: Dictionary = {}
|
||||
|
||||
@ -17,7 +18,12 @@ func create_system_seed() -> int:
|
||||
return maxi(1, rng.randi())
|
||||
|
||||
func reset_current_system_exploration() -> void:
|
||||
# Retrying after death starts a fresh attempt at this board while keeping rewards.
|
||||
# Keep the system and rewards, but regenerate asteroid placement and minerals.
|
||||
var previous: int = asteroid_retry_seeds.get(system_seed, system_seed)
|
||||
var next_seed := create_system_seed()
|
||||
while next_seed == previous:
|
||||
next_seed = create_system_seed()
|
||||
asteroid_retry_seeds[system_seed] = next_seed
|
||||
var prefix := "%d:" % system_seed
|
||||
for id in explored_pois.keys():
|
||||
if str(id).begins_with(prefix):
|
||||
@ -29,6 +35,7 @@ func start_new_run() -> void:
|
||||
system_seed = create_system_seed()
|
||||
restart_inventory.clear()
|
||||
explored_pois.clear()
|
||||
asteroid_retry_seeds.clear()
|
||||
exploration_wreck_bonuses.clear()
|
||||
hud_panel_state.clear()
|
||||
|
||||
|
||||
@ -210,6 +210,20 @@ func can_move_item(from_kind: String, from_index: int, to_kind: String, to_index
|
||||
if displaced != null and displaced.definition != null:
|
||||
if not _accepts_item(from_kind, from_index, displaced):
|
||||
return false
|
||||
if from_kind == "extension" or to_kind == "extension":
|
||||
var projected := extension_slots.duplicate()
|
||||
if from_kind == "extension":
|
||||
projected[from_index] = displaced
|
||||
if to_kind == "extension":
|
||||
projected[to_index] = item
|
||||
var equipped_types := {}
|
||||
for extension in projected:
|
||||
if extension == null or extension.definition == null:
|
||||
continue
|
||||
var type_id: String = extension.definition.item_id
|
||||
if equipped_types.has(type_id):
|
||||
return false
|
||||
equipped_types[type_id] = true
|
||||
return true
|
||||
|
||||
func _accepts_item(kind: String, index: int, item: ItemInstance) -> bool:
|
||||
|
||||
@ -9,14 +9,17 @@ static func spawn(game: Node2D, data: Dictionary, player: CharacterBody2D) -> vo
|
||||
var wreck_bonus := int(GameState.exploration_wreck_bonuses[GameState.system_seed])
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = GameState.system_seed ^ 0x504f49
|
||||
var asteroid_rng := RandomNumberGenerator.new()
|
||||
asteroid_rng.seed = int(GameState.asteroid_retry_seeds.get(GameState.system_seed, GameState.system_seed)) ^ 0x41535452
|
||||
var occupied: Array[Vector2] = []
|
||||
var wreck_count := rng.randi_range(2, 4)
|
||||
for index in wreck_count + rng.randi_range(6, 10):
|
||||
var kind := "wreck" if index < wreck_count else "asteroid"
|
||||
var point_rng := asteroid_rng if kind == "asteroid" else rng
|
||||
var candidate := Vector2.ZERO
|
||||
var found := false
|
||||
for attempt in 200:
|
||||
candidate = Vector2(rng.randf_range(-2300, 2300), rng.randf_range(-1500, 1500))
|
||||
candidate = Vector2(point_rng.randf_range(-2300, 2300), point_rng.randf_range(-1500, 1500))
|
||||
if candidate.length() < 650 or candidate.distance_to(player.position) < 450:
|
||||
continue
|
||||
var clear := true
|
||||
@ -31,7 +34,7 @@ static func spawn(game: Node2D, data: Dictionary, player: CharacterBody2D) -> vo
|
||||
break
|
||||
if found:
|
||||
occupied.append(candidate)
|
||||
_add(game, player, kind, candidate, index, rng.randi())
|
||||
_add(game, player, kind, candidate, index, point_rng.randi())
|
||||
# Rare per system, at most one ruin on an uninhabited planet.
|
||||
if rng.randf() < 0.08:
|
||||
var candidates: Array[int] = []
|
||||
|
||||
@ -9,6 +9,7 @@ extends PanelContainer
|
||||
var _inventory: ShipInventory
|
||||
var _icon: TextureRect
|
||||
var _delete_button: Button
|
||||
var _drop_highlight := false
|
||||
|
||||
func _ready() -> void:
|
||||
if slot_kind == "cargo":
|
||||
@ -46,7 +47,7 @@ func _ready() -> void:
|
||||
if _inventory:
|
||||
_inventory.inventory_changed.connect(_refresh)
|
||||
_inventory.ammunition_changed.connect(_refresh)
|
||||
custom_minimum_size = Vector2(72, 72) if slot_kind == "cargo" else Vector2(64, 64)
|
||||
custom_minimum_size = Vector2(72, 72) if slot_kind == "cargo" else Vector2(56, 56)
|
||||
slot_label.custom_minimum_size = Vector2.ZERO
|
||||
slot_label.add_theme_font_size_override("font_size", 9)
|
||||
_icon = TextureRect.new()
|
||||
@ -55,10 +56,10 @@ func _ready() -> void:
|
||||
_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_icon)
|
||||
if overlay_slot:
|
||||
if overlay_slot and slot_kind == "weapon":
|
||||
var binding := Label.new()
|
||||
binding.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
binding.text = (["LPM", "PPM", "ŚPM"][slot_index]) if slot_kind == "weapon" else (str(slot_index + 1) if slot_kind == "extension" else ShipInventory.MODULE_NAMES[slot_index])
|
||||
binding.text = ["LPM", "PPM", "ŚPM"][slot_index]
|
||||
binding.position = Vector2(2, -24)
|
||||
binding.add_theme_font_size_override("font_size", 9)
|
||||
binding.add_theme_color_override("font_color", Color("a0b2c4"))
|
||||
@ -70,6 +71,10 @@ func _ready() -> void:
|
||||
_refresh()
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
var valid_drop := get_viewport().gui_is_dragging() and _can_drop_data(Vector2.ZERO, get_viewport().gui_get_drag_data())
|
||||
if valid_drop != _drop_highlight:
|
||||
_drop_highlight = valid_drop
|
||||
_apply_slot_style(_inventory.get_item(slot_kind, slot_index) if _inventory != null else null)
|
||||
_update_salvage_visibility()
|
||||
if slot_kind == "weapon" and _has_missile():
|
||||
queue_redraw()
|
||||
@ -126,6 +131,12 @@ func _apply_slot_style(item: ItemInstance) -> void:
|
||||
style.set_border_width_all(2)
|
||||
if overlay_slot:
|
||||
style.bg_color.a = 0.86 if item != null else 0.48
|
||||
if _drop_highlight:
|
||||
style.border_color = Color("75ffd0")
|
||||
style.set_border_width_all(3)
|
||||
style.bg_color = Color(0.08, 0.3, 0.25, 0.85)
|
||||
style.shadow_color = Color(0.2, 1.0, 0.72, 0.3)
|
||||
style.shadow_size = 5
|
||||
add_theme_stylebox_override("panel", style)
|
||||
|
||||
func _delete_item() -> void:
|
||||
|
||||
@ -1,5 +1,33 @@
|
||||
extends "res://scripts/ui/captain_skill_button.gd"
|
||||
|
||||
var _skill_notification := false
|
||||
var _pulse_time := 0.0
|
||||
var _pulse_frames: Dictionary = {}
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if name != "OpenCaptainSkills":
|
||||
return
|
||||
var available := GameState.captain.learning_points > 0
|
||||
if available and _pulse_frames.is_empty():
|
||||
for state in ["normal", "hover", "pressed"]:
|
||||
_pulse_frames[state] = get_theme_stylebox(state).duplicate()
|
||||
if available:
|
||||
_pulse_time += delta
|
||||
var pulse := 0.5 + 0.5 * sin(_pulse_time * TAU / 1.6)
|
||||
for state in _pulse_frames:
|
||||
var frame := get_theme_stylebox(state) as StyleBoxFlat
|
||||
frame.border_color = Color("b9a0ef").lerp(Color("ffe2a1"), pulse)
|
||||
frame.shadow_color = Color(0.72, 0.5, 1.0, 0.25 + pulse * 0.4)
|
||||
frame.shadow_size = int(7 + pulse * 9)
|
||||
tooltip_text = "Umiejętności kapitana · punkty do wydania: %d" % GameState.captain.learning_points
|
||||
elif _skill_notification:
|
||||
for state in _pulse_frames:
|
||||
add_theme_stylebox_override(state, _pulse_frames[state].duplicate())
|
||||
_pulse_frames.clear()
|
||||
_pulse_time = 0.0
|
||||
tooltip_text = "Umiejętności kapitana"
|
||||
_skill_notification = available
|
||||
|
||||
func configure_navigation(artwork: Texture2D, accent: Color, caption: String) -> void:
|
||||
configure(artwork, accent)
|
||||
custom_minimum_size = Vector2(64, 80)
|
||||
|
||||
@ -70,9 +70,21 @@ func _ready() -> void:
|
||||
assert(int(GameState.captain.minerals.get("iron", 0)) == before + 1)
|
||||
ore.call("_physics_process", 1.0)
|
||||
assert(int(GameState.captain.minerals.get("iron", 0)) == before + 1)
|
||||
# Best duplicate wins; restoration is capped, not additive.
|
||||
# Duplicate extensions stay in cargo; replacing and moving equipped items is allowed.
|
||||
recycler.passive_value = 40
|
||||
equip("recycler", 3, 10)
|
||||
var duplicate := GENERATOR.generate_for_definition(load("res://resources/items/recycler.tres"), 1)
|
||||
assert(inventory.add_item(duplicate))
|
||||
var cargo_index := inventory.cargo.find(duplicate)
|
||||
assert(not inventory.can_move_item("cargo", cargo_index, "extension", 3))
|
||||
assert(not inventory.move_item("cargo", cargo_index, "extension", 3))
|
||||
assert(inventory.extension_slots[3] == null and inventory.cargo[cargo_index] == duplicate)
|
||||
assert(inventory.move_item("extension", 0, "extension", 3))
|
||||
assert(inventory.move_item("extension", 3, "extension", 0))
|
||||
assert(inventory.move_item("cargo", cargo_index, "extension", 0))
|
||||
assert(inventory.extension_slots[0] == duplicate and inventory.cargo[cargo_index] == recycler)
|
||||
assert(inventory.move_item("cargo", cargo_index, "extension", 0))
|
||||
assert(inventory.move_item("extension", 0, "extension", 1))
|
||||
assert(inventory.move_item("extension", 1, "extension", 0))
|
||||
ship.energy = ship.max_energy * 0.9
|
||||
ship.shields = ship.max_shields * 0.9
|
||||
ship.recycle_asteroid_fragment()
|
||||
|
||||
36
tests/levels/enemy_repair_notification_test.gd
Normal file
36
tests/levels/enemy_repair_notification_test.gd
Normal file
@ -0,0 +1,36 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
GameState.start_new_run()
|
||||
var enemy := preload("res://scenes/game/enemy_ship.tscn").instantiate()
|
||||
add_child(enemy)
|
||||
enemy.set_physics_process(false)
|
||||
enemy.max_hull = 100.0
|
||||
enemy.max_shields = 200.0
|
||||
enemy.hull = 9.0
|
||||
enemy.shields = 0.0
|
||||
assert(enemy.is_retreating())
|
||||
enemy.regenerate_defenses(0.5)
|
||||
assert(is_equal_approx(enemy.hull, 9.5) and is_equal_approx(enemy.shields, 1.0))
|
||||
assert(enemy.is_retreating())
|
||||
enemy.regenerate_defenses(0.5)
|
||||
assert(is_equal_approx(enemy.hull, 10.0) and not enemy.is_retreating())
|
||||
enemy.regenerate_defenses(1000.0)
|
||||
assert(enemy.hull == enemy.max_hull and enemy.shields == enemy.max_shields)
|
||||
enemy.hull = 0.0
|
||||
enemy.regenerate_defenses(10.0)
|
||||
assert(enemy.hull == 0.0)
|
||||
var button := Button.new()
|
||||
button.set_script(preload("res://scripts/ui/navigation_button.gd"))
|
||||
button.configure_navigation(null, Color("b9a0ef"), "KAPITAN")
|
||||
button.name = "OpenCaptainSkills"
|
||||
add_child(button)
|
||||
var original := (button.get_theme_stylebox("normal") as StyleBoxFlat).shadow_size
|
||||
GameState.captain.learning_points = 1
|
||||
button._process(0.4)
|
||||
assert((button.get_theme_stylebox("normal") as StyleBoxFlat).shadow_size > original)
|
||||
GameState.captain.learning_points = 0
|
||||
button._process(0.1)
|
||||
assert((button.get_theme_stylebox("normal") as StyleBoxFlat).shadow_size == original)
|
||||
print("ENEMY_REPAIR_NOTIFICATION_TEST: PASS 1%/s, retreat threshold, caps, dead enemy, notification reset")
|
||||
get_tree().quit()
|
||||
1
tests/levels/enemy_repair_notification_test.gd.uid
Normal file
1
tests/levels/enemy_repair_notification_test.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://c8qee631np6rq
|
||||
4
tests/levels/enemy_repair_notification_test.tscn
Normal file
4
tests/levels/enemy_repair_notification_test.tscn
Normal file
@ -0,0 +1,4 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
[ext_resource type="Script" path="res://tests/levels/enemy_repair_notification_test.gd" id="1"]
|
||||
[node name="EnemyRepairNotificationTest" type="Node"]
|
||||
script = ExtResource("1")
|
||||
@ -36,8 +36,6 @@ func _ready() -> void:
|
||||
for poi in pois(game):
|
||||
if poi.get("kind") in ["asteroid", "wreck"]:
|
||||
# Test reward retention independently of the debris renderer.
|
||||
if poi.get("kind") == "asteroid":
|
||||
poi.set("_asteroid_sprite", null)
|
||||
assert(poi.call("claim"))
|
||||
var minerals_before := GameState.captain.minerals.duplicate()
|
||||
var seed_before := GameState.system_seed
|
||||
@ -59,8 +57,11 @@ func _ready() -> void:
|
||||
for poi in after:
|
||||
var original: Dictionary = before[poi.get("poi_id")]
|
||||
assert(original["kind"] == poi.get("kind"))
|
||||
assert(original["position"] == poi.position)
|
||||
assert(original["mineral"] == poi.get("_mineral"))
|
||||
if poi.get("kind") == "asteroid":
|
||||
assert(original["position"] != poi.position)
|
||||
elif int(str(poi.get("poi_id")).get_slice(":", 1)) < 1000:
|
||||
assert(original["position"] == poi.position)
|
||||
assert(GameState.asteroid_retry_seeds.has(seed_before))
|
||||
assert(GameState.captain.minerals == minerals_before)
|
||||
assert(GameState.captain.get_skill_rank("miner") == rank)
|
||||
assert(GameState.system_seed == seed_before)
|
||||
@ -70,5 +71,5 @@ func _ready() -> void:
|
||||
get_tree().current_scene = self
|
||||
restarted.queue_free()
|
||||
await get_tree().process_frame
|
||||
print("EXPLORATION_DEATH_RESTART_TEST: PASS (actual death and scene reload at miner ranks 0/1/2/3, exhausted boards restored, stable positions/minerals, explorer snapshot, rewards/equipment retained)")
|
||||
print("EXPLORATION_DEATH_RESTART_TEST: PASS (actual death and scene reload at miner ranks 0/1/2/3, exhausted boards restored, rerolled asteroid positions/minerals, explorer snapshot, rewards/equipment retained)")
|
||||
get_tree().quit()
|
||||
|
||||
@ -15,6 +15,8 @@ func _ready() -> void:
|
||||
for skill in skills.IDS:
|
||||
if not bool(skills.DATA[skill]["active"]):
|
||||
GameState.captain.skills[skill] = 1
|
||||
if "--skill-points" in OS.get_cmdline_user_args():
|
||||
GameState.captain.learning_points = 1
|
||||
GameState.captain.emit_changed()
|
||||
inventory.inventory_changed.emit()
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
|
||||
@ -93,6 +93,13 @@ func _ready() -> void:
|
||||
motion(origin + Vector2(24, 6))
|
||||
await get_tree().process_frame
|
||||
var target: Control = hud.get("_sidebar").get_node("Scroll/Contents/ShipEquipmentPanel/Contents/EquipmentLayout/Module2")
|
||||
await get_tree().process_frame
|
||||
assert(target.get("_drop_highlight"))
|
||||
assert(not target.get_parent().get_node("Module1").get("_drop_highlight"))
|
||||
assert(not target.get_parent().get_node("Extension1").get("_drop_highlight"))
|
||||
var sidebar: Control = hud.get("_sidebar")
|
||||
assert(not sidebar.get_node("Scroll").get_v_scroll_bar().visible)
|
||||
assert(sidebar.position.y + sidebar.size.y * sidebar.scale.y <= 720.0)
|
||||
var destination := target.get_global_rect().get_center()
|
||||
motion(destination)
|
||||
await get_tree().process_frame
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user