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.")