class_name FrontEnd extends Control const ORANGE := Color("f18b28") var ui: GameUI var loading := false var status: Label var progress: ProgressBar var preview: SubViewport var actor: ImportedOperator var elapsed := 0.0 func caption(value: String, at: Vector2, font_size: int, color: Color = Color("cfcbc3")) -> Label: var item := Label.new() item.text = value item.position = at item.add_theme_font_size_override("font_size", font_size) item.add_theme_color_override("font_color", color) add_child(item) return item func _ready() -> void: set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) if DisplayServer.get_name() == "headless": set_process(false) return mouse_filter = Control.MOUSE_FILTER_STOP resized.connect(queue_redraw) var backdrop := SubViewportContainer.new() backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) backdrop.stretch = true backdrop.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(backdrop) var viewport := SubViewport.new() preview = viewport viewport.own_world_3d = true viewport.msaa_3d = get_viewport().msaa_3d viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS backdrop.add_child(viewport) var environment := WorldEnvironment.new() var env := Environment.new() env.background_mode = Environment.BG_COLOR env.background_color = Color("090807") env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR env.ambient_light_color = Color("7c858e") env.ambient_light_energy = 0.16 env.tonemap_mode = Environment.TONE_MAPPER_FILMIC environment.environment = env viewport.add_child(environment) var camera := Camera3D.new() viewport.add_child(camera) camera.position = Vector3(0, 1.38, -1.65) camera.look_at(Vector3(0.18, 1.35, 0)) camera.fov = 43 camera.current = true actor = ImportedOperator.new() viewport.add_child(actor) actor.setup("gas_mask") actor.rotation.y = -0.17 actor.preview("pro_rifle__idle", 0.2) actor.weapon_mount.add_child(WeaponModels.build(0)) for index in 2: var light := OmniLight3D.new() light.position = Vector3(-0.7, 2.7, -0.4) if index == 0 else Vector3(1.1, 1.9, 0.7) light.light_color = Color("ffbe70") if index == 0 else Color("b67b3d") light.light_energy = 4.0 if index == 0 else 3.0 light.omni_range = 5 viewport.add_child(light) var veil := TextureRect.new() var gradient := Gradient.new() gradient.offsets = PackedFloat32Array([0, 0.25, 0.48, 0.75, 1]) gradient.colors = PackedColorArray([Color(0,0,0,0.88), Color(0,0,0,0.55), Color(0,0,0,0), Color(0,0,0,0.1), Color(0,0,0,0.65)]) var texture := GradientTexture2D.new() texture.gradient = gradient texture.fill_from = Vector2(0, 0) texture.fill_to = Vector2(1, 0) veil.texture = texture veil.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(veil) veil.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) var haze := ColorRect.new() haze.mouse_filter = Control.MOUSE_FILTER_IGNORE var shader := Shader.new() shader.code = """shader_type canvas_item; float noise(vec2 p) { return fract(sin(dot(p,vec2(12.9898,78.233))) * 43758.5453); } void fragment() { vec2 p=UV*vec2(20.,12.); float mist=sin(p.x*.7+TIME*.13)*sin(p.y*.6-TIME*.1); float glow=exp(-length((UV-vec2(.7,.4))*vec2(2.,1.))*3.); vec2 sparks=UV*vec2(150.,80.)+vec2(TIME*.6,TIME*1.2); float dotlight=step(.994,noise(floor(sparks)))*(1.-smoothstep(.02,.13,length(fract(sparks)-.5))); COLOR=vec4(vec3(.52,.28,.10)+vec3(dotlight),glow*(.06+mist*.025)+dotlight*.65); }""" var haze_material := ShaderMaterial.new() haze_material.shader = shader haze.material = haze_material add_child(haze) haze.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) var layout := Control.new() layout.name = "Layout" layout.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(layout) # Design coordinates remain proportional on every window size. resized.connect(func(): layout.scale = size / Vector2(1600, 900)) layout.scale = size / Vector2(1600, 900) var logo := caption("BLOCKLINE", Vector2(60, 264), 48, Color("eeeae2")) logo.reparent(layout, false) var bars := caption("III", Vector2(348, 259), 58, ORANGE) bars.reparent(layout, false) var subtitle := caption("RELAY DISTRICT / LOCAL OPERATIONS", Vector2(62, 322), 12) subtitle.reparent(layout, false) var strip := ColorRect.new() strip.color = Color("a9ada6") strip.position = Vector2(1106, 57) strip.size = Vector2(420, 24) layout.add_child(strip) var roster := caption("1 SPIELER / 8 MAX.", Vector2(1114, 58), 15, Color("181917")) roster.reparent(layout, false) var nameplate := caption((str(ui.game.nickname) if ui != null else "OPERATOR") + "\n\nLOKALES NETZWERK", Vector2(1130, 97), 17, ORANGE) nameplate.reparent(layout, false) if loading: status = Label.new() progress = ProgressBar.new() var heading := caption("EINSATZ WIRD VORBEREITET", Vector2(62, 420), 22, ORANGE) heading.reparent(layout, false) layout.add_child(status) status.position = Vector2(62, 465) status.size = Vector2(480, 90) status.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART status.add_theme_font_size_override("font_size", 17) layout.add_child(progress) progress.position = Vector2(62, 575) progress.size = Vector2(430, 6) progress.show_percentage = false var fill := StyleBoxFlat.new() fill.bg_color = ORANGE progress.add_theme_stylebox_override("fill", fill) var track := StyleBoxFlat.new() track.bg_color = Color("34302a") progress.add_theme_stylebox_override("background", track) var count := caption("0 %", Vector2(62, 595), 14, ORANGE) count.reparent(layout, false) progress.value_changed.connect(func(value: float): count.text = "%02d %% / %d VON %d SCHRITTEN" % [int(progress.ratio * 100), int(value), int(progress.max_value)]) else: var names := ["PLAY", "MULTIPLAYER", "LOADOUT", "OPERATOR", "SETTINGS", "QUIT"] var actions: Array[Callable] = [ui.play_menu, ui.multiplayer_menu, ui.loadout_menu, ui.operator_menu, ui.settings_menu, func(): get_tree().quit()] for index in names.size(): var button := Button.new() button.text = names[index] button.position = Vector2(62, 397 + index * 35) button.size = Vector2(240, 32) button.alignment = HORIZONTAL_ALIGNMENT_LEFT button.add_theme_font_size_override("font_size", 20) for state: String in ["normal", "hover", "pressed", "focus"]: var style := StyleBoxFlat.new() style.bg_color = Color(0,0,0,0) if state == "normal" else ORANGE style.content_margin_left = 9 button.add_theme_stylebox_override(state, style) button.add_theme_color_override("font_hover_color", Color.BLACK) button.add_theme_color_override("font_focus_color", Color.BLACK) button.pressed.connect(actions[index]) button.mouse_entered.connect(button.grab_focus) layout.add_child(button) if index == 0: button.grab_focus() var footer := caption("RESSOURCEN LADEN / BITTE WARTEN" if loading else "ENTER AUSWÄHLEN / MAUS NAVIGATION", Vector2(62, 854), 13) footer.reparent(layout, false) var party := caption("LOKALER EINSATZ\nPARTY: OFFEN", Vector2(62, 736), 15) party.reparent(layout, false) var build := caption("BLOCKLINE / RELAY DISTRICT LAN • 2–8", Vector2(1070, 854), 12) build.reparent(layout, false) func _process(dt: float) -> void: preview.render_target_update_mode = SubViewport.UPDATE_ALWAYS if is_visible_in_tree() else SubViewport.UPDATE_DISABLED if not is_visible_in_tree(): return elapsed += dt if is_instance_valid(actor): actor.preview("pro_rifle__idle", elapsed) queue_redraw() func _draw() -> void: draw_line(Vector2(0, size.y * 0.93), Vector2(size.x, size.y * 0.93), Color(0.6,0.5,0.35,0.28))