Randoloot/scripts/planets/freighter_deck.gd
2026-10-18 14:46:34 +02:00

220 lines
9.7 KiB
GDScript

extends Control
signal section_selected(section: String)
signal section_hovered(section: String)
const DESIGN_SIZE := Vector2(1792,768)
const ROOMS := [
{"id":"command","title":"CENTRUM DOWODZENIA","status":"MOSTEK","rect":Rect2(25,165,315,435),"tag":Vector2(88,68),"active":false},
{"id":"workshop","title":"WARSZTAT","status":"OPERACYJNY","rect":Rect2(360,120,255,205),"tag":Vector2(360,58),"active":true},
{"id":"expansion_1","title":"SEKCJA 02","status":"W REMONCIE","rect":Rect2(632,120,200,205),"tag":Vector2(632,58),"active":false},
{"id":"expansion_2","title":"SEKCJA 03","status":"W REMONCIE","rect":Rect2(848,120,214,205),"tag":Vector2(848,58),"active":false},
{"id":"expansion_3","title":"SEKCJA 04","status":"W REMONCIE","rect":Rect2(1073,120,200,205),"tag":Vector2(1073,58),"active":false},
{"id":"cabin_1","title":"POKÓJ 01","status":"W REMONCIE","rect":Rect2(363,439,151,184),"tag":Vector2(363,667),"active":false},
{"id":"cabin_2","title":"POKÓJ 02","status":"W REMONCIE","rect":Rect2(528,439,155,184),"tag":Vector2(528,667),"active":false},
{"id":"cabin_3","title":"POKÓJ 03","status":"W REMONCIE","rect":Rect2(696,439,184,184),"tag":Vector2(696,667),"active":false},
{"id":"cabin_4","title":"POKÓJ 04","status":"W REMONCIE","rect":Rect2(894,439,159,184),"tag":Vector2(894,667),"active":false},
{"id":"cabin_5","title":"POKÓJ 05","status":"W REMONCIE","rect":Rect2(1068,439,204,184),"tag":Vector2(1068,667),"active":false},
{"id":"hangar","title":"HANGAR","status":"STATEK ZABEZPIECZONY","rect":Rect2(1305,115,435,530),"tag":Vector2(1335,42),"active":true}
]
var ship_texture: Texture2D
const ROOM_SCENE := preload("res://scenes/planets/freighter_room.tscn")
const ROOM_PLANS := {
"expansion_1":{"kind":"laboratory","title":"LABORATORIUM","description":"Stanowiska analityczne, aparatura i stół do doświadczeń."},
"expansion_2":{"kind":"alien_research","title":"BADANIA OBCYCH","description":"Izolowane próbki i komora do badań obcych organizmów."},
"expansion_3":{"kind":"module_storage","title":"SKŁAD MODUŁÓW","description":"Regały, pojemniki i stanowiska do przechowywania modułów."}
}
var room_states: Dictionary = {}
var room_nodes: Dictionary = {}
var ship_name := "STATEK GRACZA"
var selected := ""
var hovered := ""
var _tag_styles: Dictionary = {}
var _art: Texture2D
var _time := 0.0
var _frame_time := 0.0
var _art_rect := Rect2()
func _ready() -> void:
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
mouse_filter = Control.MOUSE_FILTER_STOP
clip_contents = true
_art = load("res://assets/freighter/interior_deck.png")
resized.connect(func(): _layout_rooms(); queue_redraw())
mouse_exited.connect(func(): _set_hover(""))
func room(id: String) -> Dictionary:
for section in ROOMS:
if section.id == id:
var data: Dictionary = section.duplicate()
if is_buildable(id):
data.title = plan(id).title
data.status = "ZBUDOWANO" if is_built(id) else "W REMONCIE"
data.active = is_built(id)
return data
return {}
func is_buildable(id: String) -> bool:
return ROOM_PLANS.has(id) or id in ["cabin_1","cabin_2","cabin_3","cabin_4","cabin_5"]
func plan(id: String) -> Dictionary:
if ROOM_PLANS.has(id): return ROOM_PLANS[id]
if id.begins_with("cabin_"):
return {"kind":"crew_cabin","title":"KAJUTA %02d" % int(id.trim_prefix("cabin_")),"description":"Pokój załogi z łóżkiem, biurkiem i miejscem na rzeczy osobiste."}
return {}
func is_built(id: String) -> bool:
return room_states.get(id,"") == str(plan(id).get("kind","invalid"))
func sync_rooms(states: Dictionary) -> void:
room_states = states
for section in ROOMS:
var id: String = section.id
if not is_buildable(id): continue
if not room_nodes.has(id):
var node := ROOM_SCENE.instantiate()
node.name = id
node.show_behind_parent = true
add_child(node)
room_nodes[id] = node
room_nodes[id].configure(id,plan(id).kind,section.rect,is_built(id))
_layout_rooms()
_tag_styles.clear()
queue_redraw()
func _layout_rooms() -> void:
var bounds := art_rect()
for id in room_nodes:
var area: Rect2 = room(id).rect
room_nodes[id].position = bounds.position + area.position / DESIGN_SIZE * bounds.size
room_nodes[id].size = area.size / DESIGN_SIZE * bounds.size
func art_rect() -> Rect2:
if _art == null:
return Rect2()
var factor := minf((size.x-24)/_art.get_width(),(size.y-12)/_art.get_height())
var extent := _art.get_size()*maxf(0.01,factor)
return Rect2((size-extent)*0.5,extent)
func design_to_local(point: Vector2) -> Vector2:
var bounds := art_rect()
return bounds.position+point/DESIGN_SIZE*bounds.size
func section_at(point: Vector2) -> String:
var bounds := art_rect()
if not bounds.has_point(point):
return ""
var at := (point-bounds.position)/bounds.size*DESIGN_SIZE
for section in ROOMS:
if (section.rect as Rect2).has_point(at):
return str(section.id)
return ""
func _set_hover(id: String) -> void:
if hovered == id:
return
hovered = id
mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if not id.is_empty() else Control.CURSOR_ARROW
var data := room(id)
tooltip_text = "%s · %s" % [data.title,data.status] if not data.is_empty() else ""
section_hovered.emit(id)
queue_redraw()
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
_set_hover(section_at(event.position))
elif event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
var id := section_at(event.position)
if not id.is_empty():
selected = id
section_selected.emit(id)
accept_event()
queue_redraw()
func _process(delta: float) -> void:
_time += delta
_frame_time += delta
if _frame_time >= 1.0/30.0:
_frame_time = 0
queue_redraw()
func _glow(at: Vector2, radius: float, color: Color, strength: float) -> void:
for layer in 4:
draw_circle(at,radius*(1.0-float(layer)*0.2),Color(color,strength*(0.025+layer*0.018)))
draw_circle(at,maxf(1.2,radius*0.12),Color(color,minf(1,strength*0.85)))
func _draw() -> void:
if _art == null:
return
_art_rect = art_rect()
# The native InteriorArt node stores the background in the editable scene.
draw_set_transform(_art_rect.position,0,_art_rect.size/DESIGN_SIZE)
for section_data in ROOMS:
var section: Dictionary = room(section_data.id)
var tint := Color("78d9d8") if section.active else Color("d1a15d")
if section.id == "command":
tint = Color("91b5c0")
if section.id == hovered or section.id == selected:
draw_rect(section.rect,Color(tint,0.045 if section.id == selected else 0.025))
draw_rect(section.rect,Color(tint,0.65),false,2.0,true)
_draw_ship()
# Powered floor strips follow the central corridor instead of flashing the whole room.
for index in 8:
var x := 355.0+float(index)*117.0
var pulse := 0.3+0.7*maxf(0,sin(_time*1.8-index*0.6))
draw_line(Vector2(x,393),Vector2(x+32,393),Color(0.32,0.85,0.88,0.12+pulse*0.30),2.0,true)
_glow(Vector2(x+16,393),10,Color("5ed8de"),pulse*0.65)
for index in 9:
var at := Vector2(368+index*99,340 if index < 4 else 438)
_glow(at,9,Color("efad55"),0.25+0.65*maxf(0,sin(_time*2.2+index)))
for at in [Vector2(1430,245),Vector2(1620,245),Vector2(1430,552),Vector2(1620,552)]:
_glow(at,20,Color("64dadd"),0.7+0.2*sin(_time*1.4))
_glow(Vector2(1680,390),23,Color("ed7659"),0.2+0.65*maxf(0,sin(_time*3.0)))
for index in 5:
_glow([Vector2(110,260),Vector2(95,330),Vector2(105,410),Vector2(140,480),Vector2(290,210)][index],8,Color("6be0e1"),0.3+0.6*maxf(0,sin(_time*2.7+index*2.0)))
# Occasional tiny welding sparks beside the workshop workbench.
var weld := fmod(_time,4.8)
if weld > 3.9 and weld < 4.3:
var origin := Vector2(512,185)
_glow(origin,24,Color("b5e9f6"),0.6+0.3*sin(_time*59))
for index in 7:
var ray := Vector2.from_angle(index*1.1+_time*5)*float(8+index*2)
draw_line(origin+ray*0.3,origin+ray,Color(0.95,0.73,0.35,0.7),1.2,true)
# Fine vapour drifting from a hangar service vent.
for index in 4:
var phase := fmod(_time*0.18+index*0.25,1.0)
var at := Vector2(1665+sin(phase*5+index)*9,569-phase*56)
draw_arc(at,6+phase*13,0.5,3.8,14,Color(0.62,0.75,0.76,(1-phase)*0.11),2.0,true)
for section in ROOMS:
_draw_tag(room(section.id))
draw_set_transform(Vector2.ZERO)
func _draw_ship() -> void:
if ship_texture == null:
return
var extent := ship_texture.get_size()*235.0/maxf(ship_texture.get_width(),ship_texture.get_height())
var ship_at := Vector2(1510,394)
var scale_value := _art_rect.size/DESIGN_SIZE
draw_set_transform(_art_rect.position+(ship_at+Vector2(7,8))*scale_value,-PI*0.5,scale_value)
draw_texture_rect(ship_texture,Rect2(-extent*0.5,extent),false,Color(0,0,0,0.45))
draw_set_transform(_art_rect.position+ship_at*scale_value,-PI*0.5,scale_value)
draw_texture_rect(ship_texture,Rect2(-extent*0.5,extent),false)
draw_set_transform(_art_rect.position,0,scale_value)
func _draw_tag(section: Dictionary) -> void:
var font := get_theme_default_font()
var point: Vector2 = section.tag
var width := 270.0 if section.id == "hangar" else (260.0 if section.id == "command" else (160.0 if str(section.id).begins_with("cabin") else (240.0 if section.id == "workshop" else 200.0)))
var color := Color("79d9d8") if section.active else Color("c9a06c")
if section.id == "command": color = Color("a1bcc4")
var box := _tag_styles.get(section.id) as StyleBoxFlat
if box == null:
box = StyleBoxFlat.new()
box.bg_color = Color(0.018,0.035,0.043,0.91)
box.border_color = Color(color,0.38)
box.set_border_width_all(1)
box.set_corner_radius_all(4)
_tag_styles[section.id] = box
draw_style_box(box,Rect2(point,Vector2(width,43)))
draw_circle(point+Vector2(10,13),2.8,Color(color,0.65+0.25*sin(_time*1.8)))
draw_string(font,point+Vector2(20,19),section.title,HORIZONTAL_ALIGNMENT_LEFT,width-25,13 if section.id == "expansion_2" else 16,color)
draw_string(font,point+Vector2(10,35),section.status,HORIZONTAL_ALIGNMENT_LEFT,width-15,13,Color("899a9b"))