549 lines
26 KiB
GDScript
549 lines
26 KiB
GDScript
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.")
|