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