Randoloot/scripts/planets/surface_world.gd
2026-10-26 21:06:32 +01:00

203 lines
9.8 KiB
GDScript

extends Node2D
const GENERATOR_ATLAS_PATH := preload("res://scripts/planets/surface_details.gd").ATLAS_PATH
const BUILDING_SCALE := preload("res://scripts/planets/surface_scenery.gd").BUILDING_SCALE
const GENERATOR := preload("res://scripts/planets/surface_generator.gd")
var data: Dictionary
var terrain: ImageTexture
var tree_canopy: Node2D
var _nature: Array[AtlasTexture] = []
var _tree: Texture2D
var _poi_sprites: Dictionary = {}
var _poi_labels: Dictionary = {}
var _event_nodes: Array[Dictionary] = []
func _ready() -> void:
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
terrain = GENERATOR.terrain_texture(data)
var ground := Sprite2D.new()
ground.name = "Terrain"
ground.texture = data.height_texture
ground.scale = Vector2.ONE * GENERATOR.WORLD_SIZE / ground.texture.get_width()
ground.z_index = -5
var material := ShaderMaterial.new()
material.shader = preload("res://resources/effects/planet_terrain.gdshader")
material.set_shader_parameter("height_map",data.height_texture)
material.set_shader_parameter("grass_detail",preload("res://assets/planets/grass.png"))
for kind in ["soil","rock","sand","moss"]:
material.set_shader_parameter(kind+"_detail",load("res://assets/planets/terrain/%s.png" % kind))
material.set_shader_parameter("biome_kind",int(data.landscape.index))
for key in ["low","high","sand","rock"]:
material.set_shader_parameter(key+"_color",data.landscape[key])
material.set_shader_parameter("climate",float(data.profile.temperature))
material.set_shader_parameter("seed_offset",float(posmod(int(data.seed),10000)))
ground.material = material
add_child(ground)
var atlas: Texture2D = load(GENERATOR_ATLAS_PATH)
var atlas_scale := Vector2(atlas.get_size()) / 1280.0
# The painted sheet has uneven transparent gutters; avoid clipping canopies.
var rows := [Vector2(0,360), Vector2(360,280), Vector2(640,310), Vector2(950,330)]
for index in 16:
var tile := AtlasTexture.new()
tile.atlas = atlas
var band: Vector2 = rows[index / 4]
var region := Rect2(float(index % 4)*320.0, band.x, 320.0, band.y)
if index == 12:
region = Rect2(0,950,365,330)
elif index == 13:
region = Rect2(370,950,270,330)
tile.region = Rect2(region.position*atlas_scale, region.size*atlas_scale)
tile.filter_clip = true
_nature.append(tile)
_tree = load("res://assets/planets/tree.png")
var mountains := Node2D.new()
mountains.name = "Mountains"
mountains.set_script(preload("res://scripts/planets/surface_mountains.gd"))
mountains.data = data
mountains.z_index = -1
add_child(mountains)
tree_canopy = Node2D.new()
tree_canopy.set_script(preload("res://scripts/planets/surface_tree_canopy.gd"))
tree_canopy.world = self
add_child(tree_canopy)
for poi in data.pois:
var sprite := Sprite2D.new()
if poi.kind in ["deposit", "deep_deposit"]:
sprite.set_script(preload("res://scripts/planets/surface_deposit.gd"))
sprite.configure(data,poi)
else:
if poi.kind == "cave":
sprite.set_script(preload("res://scripts/planets/surface_cave.gd"))
sprite.configure(data,poi)
else:
var textures := {"ruins":"ruins", "warehouse":"props/warehouse", "village":"landmarks/abandoned_hamlet", "crew":"props/engineer_camp", "hermit":"props/hermit_cartographer_grounded", "time_engineer":"props/time_engineer_grounded"}
sprite.texture = load("res://assets/planets/%s.png" % textures[poi.kind])
sprite.position = poi.position
if sprite.texture != null and poi.kind != "cave":
sprite.scale = Vector2.ONE * 125.0*BUILDING_SCALE / maxf(sprite.texture.get_width(), sprite.texture.get_height())
sprite.z_index = 10
if poi.kind == "crew" and sprite.texture != null:
sprite.scale = Vector2.ONE * 170.0 / maxf(sprite.texture.get_width(),sprite.texture.get_height())
add_child(sprite)
if poi.kind in ["hermit","time_engineer"]:
var grounding := ShaderMaterial.new()
grounding.shader = preload("res://resources/effects/hermit_grounding.gdshader")
var soil: Color = (data.landscape.low as Color).lerp(data.landscape.high,0.35)
grounding.set_shader_parameter("ground_tint",soil)
sprite.material = grounding
var contact := Node2D.new()
contact.name = "GroundContact"
contact.set_script(preload("res://scripts/planets/hermit_ground_contact.gd"))
contact.soil = soil.darkened(0.2)
contact.scale = Vector2.ONE/sprite.scale.x
contact.show_behind_parent = true
sprite.add_child(contact)
_poi_sprites[poi.id] = sprite
var label := Label.new()
label.position = poi.position + Vector2(-120, 46)
label.size.x = 240
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.add_theme_font_size_override("font_size", 13)
label.add_theme_color_override("font_color", Color("f0d5a1"))
label.add_theme_color_override("font_shadow_color", Color("061310"))
label.add_theme_constant_override("shadow_outline_size", 5)
label.z_index = 15
add_child(label)
_poi_labels[poi.id] = label
for landmark in data.get("landmarks", []):
var prop := Sprite2D.new()
prop.name = str(landmark.kind).to_pascal_case()
prop.texture = load("res://assets/planets/landmarks/%s.png" % landmark.kind)
prop.position = landmark.position
prop.rotation = landmark.rotation
prop.scale = Vector2.ONE * float(landmark.size) / maxf(prop.texture.get_width(), prop.texture.get_height())
prop.z_index = -2
add_child(prop)
for structure in data.structures:
var prop := Sprite2D.new()
prop.name = str(structure.kind).to_pascal_case()
prop.texture = load("res://assets/planets/props/%s.png" % structure.kind)
prop.position = structure.position
prop.rotation = structure.rotation
prop.scale = Vector2.ONE * float(structure.size) / maxf(prop.texture.get_width(),prop.texture.get_height())
prop.z_index = 4
add_child(prop)
for index in data.events.size():
var event: Dictionary = data.events[index]
var marker := Label.new()
marker.name = "EventMarker%d" % index
marker.text = "?"
marker.position = event.position+Vector2(-22,-28)
marker.size = Vector2(44,55)
marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
marker.add_theme_font_size_override("font_size",40)
marker.add_theme_color_override("font_color",Color("f8d685"))
marker.add_theme_color_override("font_outline_color",Color("17211b"))
marker.add_theme_constant_override("outline_size",7)
marker.z_index = 25
add_child(marker)
_event_nodes.append({"marker":marker})
refresh_pois(Vector2.ZERO)
func refresh_pois(lander_position: Vector2) -> void:
for index in data.events.size():
var event: Dictionary = data.events[index]
if lander_position.distance_to(event.position) < 600:
event.discovered = true
var nodes: Dictionary = _event_nodes[index]
nodes.marker.visible = not event.complete
for poi in data.pois:
if lander_position.distance_to(poi.position) < 650:
poi.discovered = true
var sprite: Sprite2D = _poi_sprites[poi.id]
sprite.visible = bool(poi.discovered)
sprite.modulate = Color(0.5, 0.55, 0.5) if poi.complete else Color.WHITE
var label: Label = _poi_labels[poi.id]
label.visible = bool(poi.discovered)
if poi.kind == "deep_deposit":
var built: bool = GameState.captain.expedition.mining_stations.has(str(data.profile.id))
if built and not bool(sprite.get_meta("outpost", false)):
sprite.texture = load("res://assets/planets/props/deuterium_outpost.png")
sprite.material = null
sprite.rotation = 0
sprite.scale = Vector2.ONE * 185.0 / maxf(sprite.texture.get_width(), sprite.texture.get_height())
sprite.set_meta("outpost", true)
var network := preload("res://scripts/planets/mining_network.gd")
var richness: int = network.station_richness(GameState.captain.expedition.mining_stations[str(data.profile.id)]) if built else network.deposit_richness(data.profile, int(data.profile.get("system_level", GameState.system_level)))
label.text = ("%s · %d/100" % ["Placówka górnicza" if built else "Głębokie złoże deuteru", richness])
if lander_position.distance_to(poi.position) <= 150: label.text += "\n[E] Placówka" if built else "\n[E] Buduj"
continue
if poi.kind == "time_engineer":
label.text = "Inżynier chronalny" + ("\n[E] Maksymalizuj addon" if lander_position.distance_to(poi.position) <= 150 else "")
continue
if poi.kind == "hermit":
var progress: Resource = GameState.captain.expedition
var map := preload("res://scripts/planets/distant_expeditions.gd").visit_offer(progress, data.profile, int(data.profile.get("system_level", GameState.system_level)))
var owned: bool = progress.hermit_purchases.has(str(map.id))
label.text = "Pustelnik / Kartograf" + (" · mapa kupiona" if owned else ("\n[E] Kup mapę" if lander_position.distance_to(poi.position) <= 150 else ""))
continue
if poi.kind == "crew":
label.text = "Obóz specjalisty" + (" · zatrudniono" if poi.complete else ("\n[E] Porozmawiaj" if lander_position.distance_to(poi.position) <= 150 else ""))
continue
var title: String = preload("res://scripts/planets/poi_rules.gd").NAMES[poi.kind]
label.text = title + (" · odwiedzono" if poi.complete else ("\n[E] Zbierz znaleziska" if lander_position.distance_to(poi.position) <= 150 else ""))
func _draw() -> void:
if terrain == null:
return
for detail in data.get("details", []):
var shade := float(detail.shade)
_draw_prop(_nature[int(detail.variant)], detail.position, detail.size, detail.rotation, Color(shade,shade,shade))
for rock in data.rocks:
_draw_prop(_nature[int(rock.get("variant",4))],rock.position,rock.size,rock.rotation,Color.WHITE)
# Small landing beacon.
draw_circle(Vector2.ZERO, 50, Color(0.5, 0.85, 0.8, 0.12))
draw_arc(Vector2.ZERO, 48, 0, TAU, 48, Color("8cd9c2"), 2, true)
func _draw_prop(texture: Texture2D, point: Vector2, width: float, angle: float, color: Color) -> void:
draw_set_transform(point, angle)
var size := Vector2(width, width*float(texture.get_height())/float(texture.get_width()))
draw_texture_rect(texture, Rect2(-size*0.5, size), false, color)
draw_set_transform(Vector2.ZERO)