Randoloot/scripts/planets/surface_screen.gd
2026-10-27 22:20:01 +01:00

549 lines
26 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Control
signal finished(success: bool)
const UI := preload("res://scripts/planets/expedition_ui.gd")
const PROFILES := preload("res://scripts/planets/planet_profiles.gd")
const EVENTS := preload("res://scripts/planets/surface_events.gd")
const GENERATOR := preload("res://scripts/planets/surface_generator.gd")
const RULES := preload("res://scripts/planets/poi_rules.gd")
const POI_CARD := preload("res://scripts/planets/poi_presentation.gd")
var captain: Resource
var progress: Resource
var world: Node2D
var lander: Sprite2D
var camera: Camera2D
var weather: Node2D
var flight_effects: Node2D
var _hp: ProgressBar
var _cargo: Label
var _threat: Label
var _status: Label
var _dialog: PanelContainer
var _choices: VBoxContainer
var _poi: Dictionary = {}
var _engineer_item: ItemInstance
var _engineer_addon := 0
var _rng := RandomNumberGenerator.new()
var _motion := preload("res://scripts/planets/lander_motion.gd").new()
var flight_ship: Node
var planet_texture: Texture2D
var _telemetry: PanelContainer
var _ending := false
var _hud_refresh_time := 0.0
var _previous_position := Vector2.ZERO
var _active_event: Dictionary = {}
var _event_armed := true
const SURFACE_RENDER_SCALE := 1.5
const SURFACE_CAMERA_ZOOM := 0.8
var _surface_viewport: SubViewport
func _ready() -> void:
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
offset_top = preload("res://scripts/ui/resource_bar.gd").HEIGHT
mouse_filter = Control.MOUSE_FILTER_STOP
_rng.seed = progress.visit_seed ^ 90317
if planet_texture == null:
var planets: Array[Texture2D] = PROFILES.earthlike_textures()
planet_texture = planets[posmod(int(progress.active_profile.seed),planets.size())]
if is_instance_valid(flight_ship):
_motion.configure(flight_ship)
var container := TextureRect.new()
container.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
container.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
container.stretch_mode = TextureRect.STRETCH_SCALE
container.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
container.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(container)
var viewport := SubViewport.new()
_surface_viewport = viewport
viewport.size = Vector2i(size * SURFACE_RENDER_SCALE)
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
viewport.world_2d = World2D.new()
container.add_child(viewport)
container.texture = viewport.get_texture()
world = Node2D.new()
world.set_script(preload("res://scripts/planets/surface_world.gd"))
world.data = GENERATOR.generate(int(progress.active_profile.seed), progress.active_profile)
for poi in world.data.pois:
if poi.kind == "crew" and progress.recruited_planets.has(str(progress.active_profile.id)): poi.complete = true
viewport.add_child(world)
lander = Sprite2D.new()
lander.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_ON
lander.texture = preload("res://assets/planets/lander.png")
lander.scale = Vector2.ONE * 100.0 / lander.texture.get_width()
lander.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
var sprite_material := ShaderMaterial.new()
sprite_material.shader = preload("res://resources/effects/lander_detail.gdshader")
lander.material = sprite_material
lander.z_index = 30
viewport.add_child(lander)
lander.reset_physics_interpolation()
var engine := Node2D.new()
engine.name = "EngineVisual"
engine.set_script(preload("res://scripts/planets/lander_engine_visual.gd"))
engine.source = self
engine.scale = Vector2.ONE / lander.scale
engine.z_index = -1
lander.add_child(engine)
var audio := AudioStreamPlayer.new()
audio.name = "EngineAudio"
audio.set_script(preload("res://scripts/game/ship_engine_audio.gd"))
audio.thrust_source = self
add_child(audio)
camera = Camera2D.new()
camera.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF
camera.process_callback = Camera2D.CAMERA2D_PROCESS_IDLE
camera.zoom = Vector2.ONE * SURFACE_CAMERA_ZOOM * SURFACE_RENDER_SCALE
camera.position_smoothing_enabled = true
camera.position_smoothing_speed = 6
camera.limit_left = -1920
camera.limit_top = -1920
camera.limit_right = 1920
camera.limit_bottom = 1920
viewport.add_child(camera)
var atmosphere := Node2D.new()
atmosphere.name = "SurfaceAtmosphere"
atmosphere.set_script(preload("res://scripts/planets/surface_clouds.gd"))
atmosphere.seed_value = progress.visit_seed
viewport.add_child(atmosphere)
weather = Node2D.new()
weather.set_script(preload("res://scripts/planets/surface_weather.gd"))
weather.source = self
viewport.add_child(weather)
flight_effects = Node2D.new()
flight_effects.set_script(preload("res://scripts/planets/surface_flight_effects.gd"))
flight_effects.source = self
flight_effects.weather = weather
viewport.add_child(flight_effects)
var nature_audio := Node.new()
nature_audio.name = "SurfaceAudio"
nature_audio.set_script(preload("res://scripts/planets/surface_audio.gd"))
nature_audio.source = self
add_child(nature_audio)
resized.connect(_resize_surface_viewport)
_telemetry = PanelContainer.new()
_telemetry.set_script(preload("res://scripts/planets/surface_telemetry.gd"))
_telemetry.source = self
add_child(_telemetry)
_hp = _telemetry.health
_status = _telemetry.status
_cargo = _telemetry.cargo
_threat = _telemetry.threat
var back := POI_CARD.button(self, "POWRÓT NA STATEK [L]", func(): _return(true))
back.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
back.position = Vector2(size.x-285, 24)
back.size.x = 260
var help := UI.panel(self)
help.name = "SurfaceControls"
help.custom_minimum_size.x = 350
help.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
help.position = Vector2(24, size.y-84)
POI_CARD.panel_style(help)
POI_CARD.text(help, "W — ciąg · S — hamuj / wstecz · A/D — obrót\nQ/R — ruch boczny · TAB — dopalanie · E — odwiedź POI", 13)
var map_frame := UI.panel(self, Vector2(195,195))
map_frame.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
map_frame.position = size-Vector2(220,220)
var minimap := Control.new()
minimap.set_script(preload("res://scripts/planets/surface_minimap.gd"))
minimap.world = world
minimap.lander = lander
map_frame.add_child(minimap)
_dialog = UI.panel(self)
_dialog.set_anchors_and_offsets_preset(Control.PRESET_TOP_LEFT)
var scroll := ScrollContainer.new()
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
_dialog.add_child(scroll)
_choices = VBoxContainer.new()
_choices.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_choices.add_theme_constant_override("separation", 10)
scroll.add_child(_choices)
_choices.minimum_size_changed.connect(_layout_dialog.call_deferred)
_layout_dialog()
_dialog.hide()
_update_hud()
func _physics_process(delta: float) -> void:
if _ending:
return
_previous_position = lander.position
if _dialog.visible:
_motion.velocity = Vector2.ZERO
_motion.angular_velocity = 0
return
_motion.step(delta, Input.get_axis("ship_turn_left","ship_turn_right"),
Input.is_action_pressed("ship_thrust"), Input.is_action_pressed("ship_brake"),
float(Input.is_physical_key_pressed(KEY_R))-float(Input.is_physical_key_pressed(KEY_Q)), Input.is_action_pressed("ship_mini_boost"))
lander.rotation = _motion.rotation
var next := lander.position + _motion.velocity * delta
var bounded := next.clamp(Vector2(-1780,-1780),Vector2(1780,1780))
if next.x != bounded.x:
_motion.velocity.x = 0
if next.y != bounded.y:
_motion.velocity.y = 0
lander.position = bounded
func _process(delta: float) -> void:
if _ending:
return
# Camera and sprite must follow the same interpolated pose between physics ticks.
camera.position = _previous_position.lerp(lander.position, Engine.get_physics_interpolation_fraction())
_hud_refresh_time += delta
if _hud_refresh_time >= 0.1:
_hud_refresh_time = 0
world.refresh_pois(lander.position)
_update_hud()
_check_events()
func _input(event: InputEvent) -> void:
if not event is InputEventKey or not event.pressed or event.echo or _ending:
return
if event.is_action("ship_mini_boost"):
get_viewport().set_input_as_handled()
return
if event.keycode == KEY_L:
_return(true)
elif event.keycode == KEY_ESCAPE:
if _dialog.visible:
_dialog.hide()
else:
_return(true)
elif event.keycode == KEY_E and _dialog.visible:
if _poi.is_empty() or str(_poi.get("kind","")) not in RULES.KINDS: return
if not bool(_poi.get("complete",false)): _attempt()
else: _dialog.hide()
elif event.keycode == KEY_E and not _dialog.visible:
for encounter in world.data.events:
if not encounter.complete and lander.position.distance_to(encounter.position) <= 180:
_event_armed = false
_show_event(encounter)
get_viewport().set_input_as_handled()
return
for poi in world.data.pois:
if (not poi.complete or (RULES.has_bonus(poi,progress.active_profile) and not bool(poi.get("bonus_complete",false)))) and lander.position.distance_to(poi.position) <= 150:
_poi = poi
_show_poi()
break
else:
return
get_viewport().set_input_as_handled()
func _show_poi(message := "") -> void:
_active_event = {}
for child in _choices.get_children():
_choices.remove_child(child)
child.queue_free()
_dialog.show()
POI_CARD.panel_style(_dialog)
_choices.add_theme_constant_override("separation",8)
_layout_dialog()
if _poi.kind == "time_engineer":
preload("res://scripts/planets/time_engineer_panel.gd").build(self,message)
return
if _poi.kind == "hermit":
_show_hermit(message)
return
if _poi.kind in ["deep_deposit", "crew"]:
_show_colony_poi(message)
return
POI_CARD.header(_choices,str(_poi.kind),0,bool(_poi.get("complete",false)))
if not bool(_poi.get("complete",false)):
_show_forecast(false)
POI_CARD.button(_choices,"Podejmij eksplorację [E]",_attempt,true).name = "ExploreButton"
POI_CARD.button(_choices,"Wróć do wyprawy",func(): _dialog.hide())
return
var result: Dictionary = _poi.get("loot_result",{})
if not result.is_empty(): _show_findings(result)
var bonus_result: Dictionary = _poi.get("bonus_result",{})
if not bonus_result.is_empty(): _show_findings(bonus_result)
elif RULES.has_bonus(_poi,progress.active_profile) and not bool(_poi.get("bonus_complete",false)):
POI_CARD.text(_choices,"DODATKOWE ZNALEZISKA",13,POI_CARD.GOLD)
_show_forecast(true)
POI_CARD.button(_choices,"Przeszukaj głębiej",func(): _attempt(true),true).name = "BonusExploreButton"
POI_CARD.text(_choices,"Znaleziska są w ładowni wyprawy. Zachowasz je po bezpiecznym powrocie na statek.",12,POI_CARD.MUTED)
POI_CARD.button(_choices,"Kontynuuj wyprawę [E]",func(): _dialog.hide())
func _show_forecast(risky: bool) -> void:
var plan := RULES.preview(progress.active_profile,captain,_poi,progress.visit_seed,risky)
POI_CARD.text(_choices,"PRZEWIDYWANA NAGRODA",13,POI_CARD.GOLD)
if not plan.materials.is_empty(): POI_CARD.text(_choices,UI.material_text(plan.materials),16)
if int(plan.modules) > 0:
var levels: Vector2i = plan.module_levels
POI_CARD.text(_choices,"Moduły: %d · poziom %d–%d" % [plan.modules,levels.x,levels.y],16)
var bounds: Vector2i = plan.damage
POI_CARD.text(_choices,"Uszkodzenia lądownika: %d–%d HP" % [bounds.x,bounds.y],16,Color("e9b18a"))
if float(bounds.y) >= progress.lander_hp:
POI_CARD.text(_choices,"Lądownik może zostać zniszczony. Utracisz cały łup tej wyprawy.",14,Color("f08370"))
func _show_findings(result: Dictionary) -> void:
POI_CARD.text(_choices,"ZDOBYTO",13,POI_CARD.GOLD)
if not result.materials.is_empty(): POI_CARD.text(_choices,UI.material_text(result.materials),15)
for item in result.modules:
var row := HBoxContainer.new()
row.name = "LootModuleRow"
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_theme_constant_override("separation",10)
_choices.add_child(row)
var icon := TextureRect.new()
icon.texture = item.definition.icon
icon.custom_minimum_size = Vector2(36,36)
icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
icon.size_flags_vertical = Control.SIZE_SHRINK_CENTER
row.add_child(icon)
var name_label := POI_CARD.text(row,"%s · poziom %d" % [item.definition.display_name,item.item_level],14,item.get_rarity_color())
name_label.name = "LootModuleName"
name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
name_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
name_label.tooltip_text = item.get_tooltip_text()
POI_CARD.text(_choices,"Uszkodzenia lądownika: −%d HP" % int(result.get("damage",0)),13,Color("e9b18a"))
func _attempt(risky: bool = false) -> void:
if _ending or _poi.is_empty(): return
if _poi.has("position") and lander.position.distance_to(_poi.position) > 150: return
var result: Dictionary = progress.resolve_poi(captain, _poi, _rng, risky)
if result.is_empty():
return
var rarity := 0
for item in result.modules: rarity = maxi(rarity,item.rarity)
preload("res://scripts/items/loot_reveal_effect.gd").spawn(lander.get_parent(),lander.position,Color("f1cd86"),rarity,true)
_update_hud()
world.refresh_pois(lander.position)
if progress.lander_hp <= 0:
await _destroy_lander()
else: _show_poi()
func _destroy_lander() -> void:
if _ending:
return
_ending = true
_status.text = "LĄDOWNIK ZNISZCZONY — utracono łup wyprawy"
_dialog.hide()
var explosion := Node2D.new()
explosion.set_script(preload("res://scripts/game/player_destruction_effect.gd"))
explosion.configure(lander,Vector2.ZERO,camera)
lander.get_parent().add_child(explosion)
lander.hide()
await explosion.finished
finished.emit(false)
func _update_hud() -> void:
_hp.value = progress.lander_hp
if not _ending:
_status.text = "Lądownik %d / 100 HP · Energia %d%%" % [ceili(progress.lander_hp),roundi(_motion.energy)] + ("\nPogoda: " + weather.display_name() if is_instance_valid(weather) else "")
_threat.text = ""
_threat.hide()
var loot_data: Dictionary = progress.cargo.duplicate()
if progress.cargo_parts > 0: loot_data.parts = progress.cargo_parts
if progress.cargo_missiles > 0: loot_data.missile = progress.cargo_missiles
var loot := UI.material_text(loot_data)
if progress.cargo_credits > 0:
loot += (" · " if not loot.is_empty() else "") + "%d kr." % progress.cargo_credits
_cargo.text = "Łup: " + (loot if not loot.is_empty() else "brak") + " · Moduły: %d" % progress.cargo_modules.size()
_cargo.tooltip_text = _cargo.text
func _return(success: bool) -> void:
if _ending:
return
_ending = true
finished.emit(success)
func engine_thrust_active() -> bool:
return not _ending and not _dialog.visible and (_motion.boost_active or (Input.is_action_pressed("ship_thrust") and not Input.is_action_pressed("ship_brake")))
func _check_events() -> void:
for event in world.data.events:
if event.complete:
continue
var distance: float = lander.position.distance_to(event.position)
if distance > 260:
_event_armed = true
# Encounters, like ordinary POIs, open only after pressing E.
func _event_text(text: String, color := Color("d5e4e8"), font_size := 15) -> Label:
var label := POI_CARD.text(_choices,text,font_size,color)
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
return label
func _show_event(event: Dictionary, result: Dictionary = {}) -> void:
_poi = {}
_active_event = event
EVENTS.prepare(event,captain,progress)
for child in _choices.get_children():
_choices.remove_child(child)
child.queue_free()
_choices.get_parent().set("scroll_vertical",0)
_dialog.show()
POI_CARD.panel_style(_dialog)
_layout_dialog()
_event_text(str(EVENTS.TITLES.get(event.kind,"Spotkanie")).to_upper(),UI.CYAN,22)
_event_text(str(EVENTS.STORIES.get(event.kind,"")))
if not result.is_empty():
_event_text(str(result.get("message",result.get("reason",""))),Color("efca8a"))
if not event.complete:
_event_text("Kredyty: %d · części: %d\nWydatki pobierane są od razu. Nowe nagrody zachowasz po bezpiecznym powrocie." % [EVENTS.stock(captain,progress,"credits"),EVENTS.stock(captain,progress,"parts")],Color("9bc4c7"),13)
if event.kind == "scan_cache":
_event_text(EVENTS.scan_text(event),Color("efca8a"))
if event.kind == "service" and progress.lander_hp >= 100:
_event_text("Kadłub jest w pełni sprawny. Naprawa nie jest potrzebna.",Color("efca8a"))
if event.kind == "trader":
_event_text("Możesz wybrać tylko jedną ofertę.",Color("efca8a"))
if event.kind in ["prototype","rescue","evacuation"]:
var limits: Vector2i = EVENTS.damage_range(event,progress)
_event_text("Przewidywane uszkodzenia lądownika: 0–%d HP" % limits.y,Color("efca8a"))
if event.kind in ["blind_cache","safe_bet"]:
_event_text("Tajemniczy łup: 50% części, 35% minerały, 10% rakiety, 5% moduł. Wartość może być niższa od opłaty.",Color("9bc4c7"),13)
for option in EVENTS.options(event,captain,progress):
var action: String = option.id
var text: String = option.label
if not option.cost.is_empty():
text += "\nKoszt: "+EVENTS.reward_text(option.cost)
if not option.reward.is_empty():
text += "\n"+("Nagroda za sukces: " if event.kind in ["prototype","rescue","evacuation"] else "Otrzymasz: ")+EVENTS.reward_text(option.reward)
var button := POI_CARD.button(_choices,text,func(): _event_action(action))
button.add_theme_font_size_override("font_size",12)
button.clip_text = true
button.tooltip_text = str(option.get("tooltip",text))
button.disabled = not EVENTS.can_pay(captain,progress,option.cost)
if event.kind in ["prototype","rescue","evacuation"] and action != "stop" and EVENTS.chance(event,captain,progress) <= 0.0:
button.disabled = true
button.tooltip_text += "\nTen etap jest niedostępny z obecnym wyposażeniem."
if button.disabled:
button.tooltip_text += "\nSprawdź wymagane zasoby."
POI_CARD.button(_choices,"Może później",func(): _dialog.hide())
else:
_event_text("Spotkanie zakończone. Nagrody w ładowni wyprawy przepadną po zniszczeniu lądownika.",Color("9bc4c7"),13)
POI_CARD.button(_choices,"Kontynuuj wyprawę",func(): _dialog.hide())
func _event_action(action: String) -> void:
if _ending:
return
var result: Dictionary = EVENTS.resolve(_active_event,action,captain,progress)
_show_event(_active_event,result)
world.refresh_pois(lander.position)
_update_hud()
if progress.lander_hp <= 0:
await _destroy_lander()
func _layout_dialog() -> void:
if not is_instance_valid(_dialog):
return
var view := size
var width := minf(450,maxf(280,view.x-32))
var height := minf(minf(600,maxf(180,view.y-48)),maxf(240,_choices.get_combined_minimum_size().y+20))
_dialog.size = Vector2(width,height)
_dialog.position = Vector2(view.x-width-20,maxf(16,(view.y-height)*0.5))
func _resize_surface_viewport() -> void:
_layout_dialog()
if is_instance_valid(_surface_viewport):
_surface_viewport.size = Vector2i(Vector2(maxf(1,size.x),maxf(1,size.y))*SURFACE_RENDER_SCALE)
func _show_colony_poi(message: String) -> void:
var network := preload("res://scripts/planets/mining_network.gd")
var is_deposit: bool = _poi.kind == "deep_deposit"
var art := TextureRect.new()
var art_path := "res://assets/planets/props/engineer_camp.png"
if is_deposit:
art_path = "res://assets/planets/props/deuterium_outpost.png" if progress.mining_stations.has(str(progress.active_profile.id)) else "res://assets/planets/deuterium_deposit.png"
art.texture = load(art_path)
art.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
art.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
art.custom_minimum_size.y = 170
_choices.add_child(art)
POI_CARD.text(_choices, "GŁĘBOKIE ZŁOŻE / DEUTER" if is_deposit else "OBÓZ SPECJALISTY / REKRUTACJA", 15, POI_CARD.GOLD)
if not message.is_empty(): POI_CARD.text(_choices, message, 13, POI_CARD.GOLD)
if is_deposit:
var id := str(progress.active_profile.id)
var richness: int = network.deposit_richness(progress.active_profile, int(progress.active_profile.get("system_level", GameState.system_level)))
if progress.mining_stations.has(id):
richness = network.station_richness(progress.mining_stations[id])
POI_CARD.text(_choices, "Zasobność: %d / 100 · złoże niewyczerpywalne" % richness, 14, POI_CARD.GOLD)
POI_CARD.text(_choices, "Z załogantem: %.1f–%.1f deuteru/min (zarządzanie 1–100)." % [richness * 1.01, richness * 2.0], 12)
if progress.mining_stations.has(id):
var station: Dictionary = progress.mining_stations[id]
POI_CARD.text(_choices, "Placówka zbudowana · %.1f deuteru/min" % network.station_rate(progress, station), 13)
POI_CARD.text(_choices, "Przydziel załoganta w centrum dowodzenia fregaty. Deuter trafia automatycznie do magazynu, również gdy jesteś poza tą planetą.", 12)
else:
var cost: int = network.build_cost(progress.active_profile, int(progress.active_profile.get("system_level", GameState.system_level)))
POI_CARD.text(_choices, "Budowa kosztuje zasobność × 10 części. Do wydobycia potrzebny jest załogant.", 13)
POI_CARD.text(_choices, "Wydobycie = zasobność × (1 + zarządzanie / 100). Bez załoganta: 0. Części: %d / %d" % [int(captain.parts), cost], 12)
var build := POI_CARD.button(_choices, "ZBUDUJ PLACÓWKĘ · %d CZĘŚCI" % cost, _build_outpost, true)
build.name = "BuildMiningOutpost"
build.disabled = captain.parts < cost
else:
POI_CARD.text(_choices, "Specjalista z obozu szuka pracy. Obejrzyj umiejętności i zdecyduj — możesz wrócić później. Koszt zatrudnienia: 1000 kredytów × zarządzanie.", 12)
var candidate: Dictionary = network.recruit(progress.active_profile)
var hire_cost: int = network.recruitment_cost(candidate)
POI_CARD.text(_choices, "Rekrutacja: %d kredytów · posiadasz: %d" % [hire_cost, captain.credits], 13, POI_CARD.GOLD)
POI_CARD.text(_choices, "%s\nZarządzanie: %d / 100\nPremia do wydobycia: +%d%%" % [candidate.name, candidate.management, candidate.management], 14)
if progress.recruited_planets.has(str(progress.active_profile.id)):
POI_CARD.text(_choices, "Specjalista został już zatrudniony.", 12)
else:
var cabin: String = network.free_cabin(progress)
POI_CARD.text(_choices, "Brak wolnej, zbudowanej kajuty. Przygotuj pokój na fregacie." if cabin.is_empty() else "Zakwaterowanie: Kajuta %02d" % int(cabin.trim_prefix("cabin_")), 12)
var hire := POI_CARD.button(_choices, "ZATRUDNIJ · %d KREDYTÓW" % hire_cost, _hire_crew, true)
hire.name = "HireCrew"
hire.disabled = cabin.is_empty() or captain.credits < hire_cost
POI_CARD.button(_choices, "Wróć do wyprawy", func(): _dialog.hide())
func _build_outpost() -> void:
if _ending or not progress.active or _poi.kind != "deep_deposit": return
var network := preload("res://scripts/planets/mining_network.gd")
if network.build(progress, captain, progress.active_profile, GameState.system_level):
world.refresh_pois(lander.position)
_show_poi("Placówka gotowa. Przydziel operatora w centrum dowodzenia.")
else: _show_poi("Nie można zbudować placówki. Sprawdź liczbę części.")
func _maximize_addon() -> void:
if _ending or not progress.active or _poi.kind != "time_engineer" or not is_instance_valid(flight_ship): return
if lander.position.distance_to(_poi.position) > 150: return
var service := preload("res://scripts/planets/time_engineer.gd")
if service.maximize(progress,flight_ship.get_node("ShipInventory"),_engineer_item,_engineer_addon):
captain.emit_changed()
_show_poi("Wybrany addon ma teraz maksymalną wartość dla obecnego poziomu przedmiotu.")
else:
_show_poi("Brakuje kryształów, addon jest już maksymalny lub przedmiot jest niedostępny.")
func _show_hermit(message: String) -> void:
var expeditions := preload("res://scripts/planets/distant_expeditions.gd")
var profile: Dictionary = progress.active_profile
var map: Dictionary = expeditions.visit_offer(progress, profile, int(profile.get("system_level", GameState.system_level)))
var art := TextureRect.new()
art.texture = load("res://assets/planets/props/hermit_cartographer.png")
art.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
art.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
art.custom_minimum_size.y = 175
_choices.add_child(art)
POI_CARD.text(_choices, "PUSTELNIK / KARTOGRAF", 18, Color("e9b4fc"))
POI_CARD.text(_choices, "W zaciszu chaty pustelnik przechowuje współrzędne miejsc poza znanymi szlakami. Oferuje ci jedną ze swoich map.", 13)
if not message.is_empty(): POI_CARD.text(_choices, message, 13, POI_CARD.GOLD)
POI_CARD.text(_choices, "%s · poziom wyprawy %d" % [map.name, map.level], 15, POI_CARD.GOLD)
POI_CARD.text(_choices, "Mapa: %d kredytów\nPaliwo na lot: %d deuteru\nPosiadasz: %d kredytów" % [expeditions.map_cost(map),expeditions.fuel_cost(map),captain.credits], 13)
if progress.hermit_purchases.has(str(map.id)):
POI_CARD.text(_choices, "Mapa z tej wizyty została kupiona. Wróć po kolejnym lądowaniu po nową ofertę.", 13)
else:
var buy := POI_CARD.button(_choices, "KUP MAPĘ · %d KREDYTÓW" % expeditions.map_cost(map), _buy_distant_map, true)
buy.name = "BuyDistantMap"
buy.disabled = captain.credits < expeditions.map_cost(map)
POI_CARD.button(_choices, "Wróć do wyprawy", func(): _dialog.hide())
func _buy_distant_map() -> void:
if _ending or not progress.active or _poi.kind != "hermit": return
var profile: Dictionary = progress.active_profile
if preload("res://scripts/planets/distant_expeditions.gd").buy(progress,captain,profile,int(profile.get("system_level",GameState.system_level))):
world.refresh_pois(lander.position)
_show_poi("Sektor dodany do listy wypraw na frachtowcu. Mapa pozwala na jeden lot.")
else:
_show_poi("Mapa została już kupiona lub brakuje kredytów.")
func _hire_crew() -> void:
if _ending or not progress.active or _poi.kind != "crew": return
if preload("res://scripts/planets/mining_network.gd").hire(progress, captain, progress.active_profile):
_poi.complete = true
world.refresh_pois(lander.position)
_show_poi("Załogant zajął wolną kajutę na fregacie. Przydziel go do stacji w centrum dowodzenia.")
else: _show_poi("Zatrudnienie wymaga wolnej, zbudowanej kajuty i kredytów: 1000 × zarządzanie.")