From ef477cfdd509f2e2f28507852b4c9309c2ca318e Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Mon, 2 May 2022 19:09:50 -0500 Subject: [PATCH] Improvements --- .gitignore | 4 +- lib/application_manager.rb | 7 +- lib/gui_state_ext.rb | 52 ++++++++ lib/page.rb | 4 + lib/pages/games_redesign.rb | 50 +++++--- lib/pages/server_browser.rb | 49 ++++++-- lib/states/direct_connect_dialog.rb | 188 ++++++++++++++++++++++++++++ lib/states/prompt_dialog.rb | 2 +- lib/window.rb | 1 + locales/en.yml | 3 + media/textures/noise.png | Bin 0 -> 9867 bytes media/textures/noiseb.png | Bin 0 -> 14651 bytes media/textures/noisec.png | Bin 0 -> 13965 bytes media/textures/noised.png | Bin 0 -> 11662 bytes media/ui_icons/plus.png | Bin 0 -> 15215 bytes w3d_hub_linux_launcher.rb | 4 + 16 files changed, 333 insertions(+), 31 deletions(-) create mode 100644 lib/gui_state_ext.rb create mode 100644 lib/states/direct_connect_dialog.rb create mode 100644 media/textures/noise.png create mode 100644 media/textures/noiseb.png create mode 100644 media/textures/noisec.png create mode 100644 media/textures/noised.png create mode 100644 media/ui_icons/plus.png diff --git a/.gitignore b/.gitignore index c92b40f..d5105fe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ _*.* media/icons/* !media/icons/app.* !media/icons/default_icon.png -!media/icons/w3dhub.png \ No newline at end of file +!media/icons/w3dhub.png +media/banners/* +!media/banners/.gitkeep \ No newline at end of file diff --git a/lib/application_manager.rb b/lib/application_manager.rb index 7e5abb6..7416196 100644 --- a/lib/application_manager.rb +++ b/lib/application_manager.rb @@ -133,13 +133,16 @@ class W3DHub "open" end + # TODO: Change if this correct on Linux + user_data_path = "#{Dir.home}/Documents/W3D Hub/games/#{app_id}-#{channel}" + path = case type when :installation app_data[:install_directory] when :user_data - app_data[:install_directory] + user_data_path when :screenshots - app_data[:install_directory] + Dir.exist?("#{user_data_path}/Screenshots") ? "#{user_data_path}/Screenshots" : user_data_path else raise "Unknown folder type: #{type.inspect}" end diff --git a/lib/gui_state_ext.rb b/lib/gui_state_ext.rb new file mode 100644 index 0000000..2fa7c7d --- /dev/null +++ b/lib/gui_state_ext.rb @@ -0,0 +1,52 @@ +module CyberarmEngine + class GuiState < CyberarmEngine::GameState + def menu(host_element, items:, width: 200) + container = CyberarmEngine::Element::Stack.new( + parent: host_element.parent, + width: width, + theme: W3DHub::THEME, + border_color: 0xff_000000, + border_thickness: 1 + ) + + container.instance_variable_set(:"@__menu", host_element) + + container.define_singleton_method(:recalculate_menu) do + @x = @__menu.x + @y = @__menu.y + @__menu.height + + @y = @__menu.y - height if @y + height > window.height + end + + def container.recalculate + super + + recalculate_menu + end + + items.each do |item| + btn = CyberarmEngine::Element::Button.new( + item[:label], + { + parent: container, + width: 1.0, + text_align: :left, + theme: W3DHub::THEME, + border_thickness: 0, + margin: 0 + }, + proc do + item[:block]&.call + end + ) + container.add(btn) + end + + container.recalculate + container.recalculate + container.recalculate + + show_menu(container) + end + end +end diff --git a/lib/page.rb b/lib/page.rb index 13cd42d..0025336 100644 --- a/lib/page.rb +++ b/lib/page.rb @@ -47,5 +47,9 @@ class W3DHub def button_up(id) end + + def menu(host_element, items:) + @host.menu(host_element, items: items) + end end end \ No newline at end of file diff --git a/lib/pages/games_redesign.rb b/lib/pages/games_redesign.rb index b9565c1..3124091 100644 --- a/lib/pages/games_redesign.rb +++ b/lib/pages/games_redesign.rb @@ -18,7 +18,7 @@ class W3DHub end # Game Menu - @game_page_container = stack(width: 1.0, fill: true) do + @game_page_container = stack(width: 1.0, fill: true, background_image: "#{GAME_ROOT_PATH}/media/textures/noiseb.png", background_image_mode: :tiled) do # , background_image: "C:/Users/cyber/Downloads/vlcsnap-2022-04-24-22h24m15s854.png" end end @@ -46,9 +46,8 @@ class W3DHub image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{game.id}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{game.id}.png" : "#{GAME_ROOT_PATH}/media/icons/default_icon.png" image_color = Store.application_manager.installed?(game.id, game.channels.first.id) ? 0xff_ffffff : 0x66_ffffff - flow(width: 1.0, height: 1.0, margin: 8, background_image: image_path, background_image_color: image_color, background_image_mode: :fill) do - image "#{GAME_ROOT_PATH}/media/ui_icons/return.png", width: 24, margin_left: -6, margin_top: -6, color: 0xdd_ff8844 if Store.application_manager.updateable?(game.id, game.channels.first.id) - image "#{GAME_ROOT_PATH}/media/ui_icons/import.png", width: 24, margin_left: -4, margin_top: -6, color: 0xdd_ffffff unless Store.application_manager.installed?(game.id, game.channels.first.id) + flow(width: 1.0, height: 1.0, margin: 8, background_image: image_path, background_image_color: image_color, background_image_mode: :fill_height) do + image "#{GAME_ROOT_PATH}/media/ui_icons/import.png", width: 24, margin_left: -4, margin_top: -6, color: 0xff_ff8800 if Store.application_manager.updateable?(game.id, game.channels.first.id) end # inscription game.name, width: 1.0, text_align: :center, text_size: 14 @@ -91,23 +90,21 @@ class W3DHub image_path = "#{GAME_ROOT_PATH}/media/banners/#{game.id}.png" if File.exist?(image_path) - stack(width: 360-8, height: 200, margin: 8, background_image: image_path, background_image_mode: :fill_width) + image image_path, width: 1.0 else - stack(width: 360-8, height: 200, padding: 8) do - banner game.name unless File.exist?(image_path) - end + banner game.name unless File.exist?(image_path) end - stack(width: 1.0, fill: true, scroll: true) do + stack(width: 1.0, fill: true, scroll: true, margin_top: 32) do if Store.application_manager.installed?(game.id, channel.id) Hash.new.tap { |hash| - hash[I18n.t(:"games.game_settings")] = { icon: "gear", block: proc { Store.application_manager.settings(game.id, channel.id) } } - hash[I18n.t(:"games.wine_configuration")] = { icon: "gear", block: proc { Store.application_manager.wine_configuration(game.id, channel.id) } } if W3DHub.unix? - hash[I18n.t(:"games.game_modifications")] = { icon: "gear", enabled: true, block: proc { populate_game_modifications(game, channel) } } - if game.id != "ren" - hash[I18n.t(:"games.repair_installation")] = { icon: "wrench", block: proc { Store.application_manager.repair(game.id, channel.id) } } - hash[I18n.t(:"games.uninstall_game")] = { icon: "trashCan", block: proc { Store.application_manager.uninstall(game.id, channel.id) } } - end + # hash[I18n.t(:"games.game_settings")] = { icon: "gear", block: proc { Store.application_manager.settings(game.id, channel.id) } } + # hash[I18n.t(:"games.wine_configuration")] = { icon: "gear", block: proc { Store.application_manager.wine_configuration(game.id, channel.id) } } if W3DHub.unix? + # hash[I18n.t(:"games.game_modifications")] = { icon: "gear", enabled: true, block: proc { populate_game_modifications(game, channel) } } + # if game.id != "ren" + # hash[I18n.t(:"games.repair_installation")] = { icon: "wrench", block: proc { Store.application_manager.repair(game.id, channel.id) } } + # hash[I18n.t(:"games.uninstall_game")] = { icon: "trashCan", block: proc { Store.application_manager.uninstall(game.id, channel.id) } } + # end hash[I18n.t(:"games.install_folder")] = { icon: nil, block: proc { Store.application_manager.show_folder(game.id, channel.id, :installation) } } hash[I18n.t(:"games.user_data_folder")] = { icon: nil, block: proc { Store.application_manager.show_folder(game.id, channel.id, :user_data) } } hash[I18n.t(:"games.view_screenshots")] = { icon: nil, block: proc { Store.application_manager.show_folder(game.id, channel.id, :screenshots) } } @@ -134,6 +131,9 @@ class W3DHub if game.channels.count > 1 # Release channel + + inscription I18n.t(:"games.game_version"), width: 1.0, text_align: :center + flow(width: 1.0, height: 48) do # background 0xff_444411 list_box(width: 1.0, items: game.channels.map(&:name), choose: channel.name, enabled: game.channels.count > 1) do |value| @@ -161,6 +161,22 @@ class W3DHub Store.application_manager.run(game.id, channel.id) end + button get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), tip: I18n.t(:"games.game_options"), image_height: 32, margin_left: 0 do |btn| + items = [] + + items << { label: I18n.t(:"games.game_settings"), block: proc { Store.application_manager.settings(game.id, channel.id) } } + items << { label: I18n.t(:"games.wine_configuration"), block: proc { Store.application_manager.wine_configuration(game.id, channel.id) } } if W3DHub.unix? + items << { label: I18n.t(:"games.game_modifications"), block: proc { populate_game_modifications(game, channel) } } + if game.id != "ren" + items << { label: I18n.t(:"games.repair_installation"), block: proc { Store.application_manager.repair(game.id, channel.id) } } + items << { label: I18n.t(:"games.uninstall_game"), block: proc { Store.application_manager.uninstall(game.id, channel.id) } } + end + + # From gui_state_ext.rb + # TODO: Implement in engine proper + menu(btn, items: items) + end + else installing = Store.application_manager.task?(:installer, game.id, channel.id) @@ -277,7 +293,7 @@ class W3DHub # Detailed view news_blurb_container = stack(width: 1.0, height: 1.0, background: 0xaa_000000, padding: 4) do tagline "#{item.title}", width: 1.0 - inscription item.timestamp.strftime("%Y-%m-%d") + inscription "#{item.author} • #{item.timestamp.strftime("%Y-%m-%d")}" inscription item.blurb.gsub(/\n+/, "\n").strip[0..1024], fill: true button I18n.t(:"games.read_more"), width: 1.0, margin_top: 8, margin_bottom: 0, padding_top: 4, padding_bottom: 4 do diff --git a/lib/pages/server_browser.rb b/lib/pages/server_browser.rb index ee3173f..92e9c9e 100644 --- a/lib/pages/server_browser.rb +++ b/lib/pages/server_browser.rb @@ -62,11 +62,19 @@ class W3DHub # button get_image("#{GAME_ROOT_PATH}/media/ui_icons/return.png"), tip: I18n.t(:"server_browser.refresh"), image_height: 1.0, margin_left: 16, padding_left: 2, padding_right: 2, padding_top: 2, padding_bottom: 2 do # fetch_server_list # end + + flow(fill: true) + + button "Direct Connect", height: 1.0, padding_top: 4, padding_bottom: 4, enabled: false, tip: "Directly connect to a game server (under development)" do + push_state(W3DHub::States::DirectConnectDialog) + end end - flow(min_width: 372, width: 0.38, max_width: 512, height: 1.0) do - inscription "#{I18n.t(:"server_browser.nickname")}:", width: 0.32 - @nickname_label = inscription "#{Store.settings[:server_list_username]}", width: 0.6 + flow(min_width: 372, width: 0.38, max_width: 512, height: 1.0) do |container| + flow(fill: true) + + inscription "#{I18n.t(:"server_browser.nickname")}:" + @nickname_label = inscription "#{Store.settings[:server_list_username]}" image "#{GAME_ROOT_PATH}/media/ui_icons/wrench.png", height: 16, hover: { color: 0xaa_ffffff }, tip: I18n.t(:"server_browser.set_nickname") do # Prompt for player name prompt_for_nickname( @@ -74,6 +82,10 @@ class W3DHub @nickname_label.value = entry Store.settings[:server_list_username] = entry Store.settings.save_settings + + container.recalculate + container.recalculate + container.recalculate end ) end @@ -248,16 +260,21 @@ class W3DHub stack(width: 1.0, height: 1.0, padding: 8) do stack(width: 1.0, height: 220) do flow(width: 1.0, height: 0.2) do + flow(fill: true) + image game_icon(server), width: 0.05 - tagline server.status.name, width: 0.949, text_wrap: :none + tagline server.status.name, text_wrap: :none + + flow(fill: true) end - stack(width: 1.0, height: 0.2) do + flow(width: 1.0, height: 0.2) do game_installed = Store.application_manager.installed?(server.game, server.channel) game_updatable = Store.application_manager.updateable?(server.game, server.channel) style = server.channel != "release" ? TESTING_BUTTON : {} - button "#{I18n.t(:"server_browser.join_server")}", margin_left: 96, enabled: (game_installed && !game_updatable), **style do + flow(fill: true) + button "#{I18n.t(:"server_browser.join_server")}", enabled: (game_installed && !game_updatable), **style do # Check for nickname # prompt for nickname # !abort unless nickname set @@ -294,6 +311,13 @@ class W3DHub end end end + + if Store.developer_mode + list_box(items: (1..12).to_a.map(&:to_s), margin_left: 16, **TESTING_BUTTON) + button "Multijoin", tip: "Launch multiple clients with configured username_\#{number}", **TESTING_BUTTON, enabled: true + end + + flow(fill: true) end # Server Info @@ -329,17 +353,22 @@ class W3DHub # Game score and balance display flow(width: 1.0, height: 48, border_thickness_bottom: 2, border_color_bottom: 0x44_ffffff) do - stack(width: 0.4, height: 1.0) do + stack(fill: true, height: 1.0) do para "#{server.status.teams[0].name} (#{server.status.players.select { |pl| pl.team == 0 }.count})", width: 1.0, text_align: :center para formatted_score(game_balance[:team_0_score].to_i), width: 1.0, text_align: :center end stack(width: 0.2, height: 1.0) do - image game_balance[:icon], height: 0.5, margin_left: 20, tip: game_balance[:message], color: game_balance[:color] + flow(width: 1.0, height: 0.5) do + flow(fill: true) + image game_balance[:icon], height: 1.0, tip: game_balance[:message], color: game_balance[:color] + flow(fill: true) + end + para game_balance[:ratio].round(2).to_s, width: 1.0, text_align: :center end - stack(width: 0.4, height: 1.0) do + stack(fill: true, height: 1.0) do para "#{server.status.teams[1].name} (#{server.status.players.select { |pl| pl.team == 1 }.count})", width: 1.0, text_align: :center para formatted_score(game_balance[:team_1_score].to_i), width: 1.0, text_align: :center end @@ -347,7 +376,7 @@ class W3DHub # Team roster flow(width: 1.0, fill: true, scroll: true) do - stack(width: 0.499) do + stack(width: 0.5) do server.status.players.select { |ply| ply.team == 0 }.sort_by { |ply| ply.score }.reverse.each_with_index do |player, i| flow(width: 1.0, height: 18) do background 0xff_333333 if i.even? diff --git a/lib/states/direct_connect_dialog.rb b/lib/states/direct_connect_dialog.rb new file mode 100644 index 0000000..0494dc8 --- /dev/null +++ b/lib/states/direct_connect_dialog.rb @@ -0,0 +1,188 @@ +class W3DHub + class States + class DirectConnectDialog < CyberarmEngine::GuiState + def setup + window.show_cursor = true + + theme(W3DHub::THEME) + + background 0xee_444444 + + stack(width: 1.0, height: 1.0, margin: 128, background: 0xee_222222) do + # Title bar + flow(width: 1.0, height: 32, padding: 8) do + background 0x88_000000 + + image "#{GAME_ROOT_PATH}/media/ui_icons/export.png", width: 32, align: :center, color: 0xaa_ffffff + + tagline "#{I18n.t(:"server_browser.direct_connect")}", fill: true, text_align: :center + end + + stack(width: 1.0, fill: true, scroll: true) do + stack(width: 1.0, height: 60, margin_left: 8, margin_right: 8) do + para "Server profiles", text_align: :center, width: 1.0 + + flow(width: 1.0, fill: true) do + list = [""] # window.config.server_profiles.count.positive? ? window.config.server_profiles.map { |pf| pf.name }.insert(0, "") : [""] + + @server_profiles_list = list_box items: list, fill: true, height: 1.0 + @server_profiles_list.subscribe(:changed) do |list| + list.items.delete("") if list.value != "" + + profile = window.config.server_profiles.find { |pf| pf.name == list.value } + populate_from_server_profile(profile ? profile : window.config.settings) + + valid_for_multiplayer? + end + + button get_image("#{GAME_ROOT_PATH}/media/ui_icons/plus.png"), image_height: 1.0, tip: "Create new profile" do + push_state(ServerProfileForm, save_callback: method(:save_server_profile)) + end + + @server_delete_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/minus.png"), image_height: 1.0, tip: "Remove selected profile" do + push_state(ConfirmDialog, message: "Purge server profile") + end + + @server_edit_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), image_height: 1.0, tip: "Edit and save selected profile" do + push_state(ServerProfileForm, editing: window.config.server_profiles.find { |pf| pf.name == @server_profiles_list.value }, save_callback: method(:save_server_profile)) + end + end + end + + stack(width: 1.0, fill: true, margin_top: 8, padding: 8, border_color: 0xff_111111, border_thickness: 1) do + flow(width: 1.0, height: 60) do + stack(width: 0.5, height: 1.0) do + para "Nickname:" + @server_nickname = edit_line "", width: 1.0, fill: true + @server_nickname.subscribe(:changed) do |e| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + end + + stack(width: 0.5, height: 1.0) do + para "Server Password:" + @server_password = edit_line "", width: 1.0, fill: true, margin_left: 4, type: :password + @server_password.subscribe(:changed) do |e| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + end + end + + flow(width: 1.0, height: 60) do + stack(width: 0.5, height: 1.0) do + para "Server IP or Hostname:" + @server_hostname = edit_line "", width: 1.0, fill: true + @server_hostname.subscribe(:changed) do |e| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + end + + stack(width: 0.5, height: 1.0) do + para "Server Port:" + @server_port = edit_line "", width: 1.0, fill: true, margin_left: 4 + @server_port.subscribe(:changed) do |e| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + end + end + + stack(width: 1.0, height: 60) do + para "Game or Mod:" + + flow(width: 1.0, fill: true) do + list = [""] # window.config.games.count.positive? ? window.config.games.map { |g| g.title } : [""] + + @games_list = list_box items: list, fill: true, height: 1.0 + @games_list.subscribe(:changed) do |list| + list.items.delete("") if list.value != "" + + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + + button get_image("#{GAME_ROOT_PATH}/media/ui_icons/plus.png"), image_height: 1.0, tip: "Add game" do + push_state(GameForm, save_callback: method(:save_game)) + end + + @game_delete_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/minus.png"), image_height: 1.0, tip: "Remove selected game" do + push_state(ConfirmDialog, message: "Remove game?") + end + + @game_edit_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), image_height: 1.0, tip: "Edit selected game" do + push_state(GameForm, editing: window.config.games.find { |g| g.title == @games_list.value }, save_callback: method(:save_game)) + end + end + end + + stack(width: 1.0, height: 60) do + para "Launch arguments (Optional):" + @launch_arguments = edit_line "", width: 1.0, fill: true + @launch_arguments.subscribe(:changed) do |e| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + end + + stack(width: 1.0, height: 60) do + para "IRC Profile:" + + flow(width: 1.0, fill: true) do + @irc_profiles_list = list_box items: ["None"], fill: true, height: 1.0 + @irc_profiles_list.subscribe(:changed) do |list| + @changes_made = true if @server_profiles_list.value.length.positive? + + valid_for_multiplayer? + end + + button get_image("#{GAME_ROOT_PATH}/media/ui_icons/plus.png"), image_height: 1.0, tip: "Add IRC profile" do + push_state(IRCProfileForm, save_callback: method(:save_irc_profile)) + end + + @irc_delete_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/minus.png"), image_height: 1.0, tip: "Remove selected IRC profile" do + push_state(ConfirmDialog, message: "") + end + + @irc_edit_button = button get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), image_height: 1.0, tip: "Edit selected IRC profile" do + push_state(IRCProfileForm, editing: window.config.irc_profiles.find { |pf| pf.name == @irc_profiles_list.value }, save_callback: method(:save_irc_profile)) + end + end + end + end + end + + flow(width: 1.0, height: 40, padding: 8) do + button "Cancel", width: 0.25 do + pop_state + @options[:cancel_callback]&.call + end + + stack(fill: true) + + button "Connect", width: 0.25 do + pop_state + @options[:accept_callback]&.call + end + end + end + end + + def draw + previous_state&.draw + + Gosu.flush + + super + end + end + end +end diff --git a/lib/states/prompt_dialog.rb b/lib/states/prompt_dialog.rb index 7602732..f7d191e 100644 --- a/lib/states/prompt_dialog.rb +++ b/lib/states/prompt_dialog.rb @@ -17,7 +17,7 @@ class W3DHub image "#{GAME_ROOT_PATH}/media/ui_icons/question.png", width: 32, align: :center, color: 0xff_ff8800 - tagline "#{@options[:title]}", width: 0.9, text_align: :center + tagline "#{@options[:title]}", fill: true, text_align: :center end stack(width: 1.0, fill: true, padding: 16) do diff --git a/lib/window.rb b/lib/window.rb index 96df093..2907b6f 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -20,6 +20,7 @@ class W3DHub # push_state(W3DHub::States::DemoInputDelay) # push_state(W3DHub::States::Welcome) push_state(W3DHub::States::Boot) + # push_state(W3DHub::States::DirectConnectDialog) end def update diff --git a/locales/en.yml b/locales/en.yml index 14a8383..e07fa9a 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -26,6 +26,8 @@ en: settings: Settings games: game_settings: Game Settings + game_options: Game Options + game_version: Game Version wine_configuration: Wine Configuration game_modifications: Game Modifications repair_installation: Repair Installation @@ -38,6 +40,7 @@ en: channel: Channel version: Version server_browser: + direct_connect: Direct Connect refresh: Refresh join_server: Join Server game: Game diff --git a/media/textures/noise.png b/media/textures/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..4ae484caea9c2388f4fd1a3a2865729c0403c2ed GIT binary patch literal 9867 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 literal 0 HcmV?d00001 diff --git a/media/textures/noiseb.png b/media/textures/noiseb.png new file mode 100644 index 0000000000000000000000000000000000000000..8d2d104f0c5b98c7daa33ee269d25c9631434ccf GIT binary patch literal 14651 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 literal 0 HcmV?d00001 diff --git a/media/textures/noisec.png b/media/textures/noisec.png new file mode 100644 index 0000000000000000000000000000000000000000..ab662df95213ca0565e57bc699fdccd2135f6ad9 GIT binary patch literal 13965 zcmV;8Hgd^{P)I56&(DvqvCo+O z+n9a7*T4Stug`n;{_&50{P^>q|NP-t-F7dVgIJX>5X2e_sP3CH$OXVj>$S5*&}(j zhJBH3V>WN+G1uqUn;)C32C5)7y+wC@!fvts#~{15_cPMQFwB& zbvOrQY$L`q@)R=#QRW1Gxklf0&Mi`*7h_JI?tSZSUi#sj8@KN!@AfQOIUQ%-KtqWk>$rPv=;w#XtJg~^@r zk#0{-ExRVFr8$udDIgEzgIt>zcUur`-lmYxuK_$;Kt5j5IIf(;!5}?5ICLk>qze+YI9IvBa#PC*1_$@kegxmd2?D{ zL~g(KHv^Rya2RmO(V!q*)rm#ozMHZq%ieP?ylFb)e4LhJHC-|)B({$R8^{;1c*OQ@ z&dMrfu`&4`2QUeS3x4D$n-#%@_bt3dF*_yjnzClSM zXy27lOIw|PYjdP)7!GYeBK79svhj^U*gEgtOK}1Q(m~BSQP;4p^eDy9Zv$0OvfCVK zPE@4kl(^jI*Mv_M0NxnMKnQ?YQmi~kz`gQV`M#Sj>sD4SQlu7+0;(xS1R_!Uo@X!G z%DNes;qeO*UnjsT>|4q!lc!v1uzQA9O688(cO{aWr>JBvV19cQ^`dvO@4h-YL8`I~Ti;qO)2zAi ztyZifqI}*w(u!1LU&v$gEd^56K666Rqr{tR@@P3yxdcM&UTb0E(2Fzel{1kOirJHW zb6^NeX6sj1b;zcd?(cDcuPA&tfW`~K_a&GR(S>)yECOv({seC}vayW-knS2LE(J+R zlWB7i`j$#{6yqS*=BP0m>GCgy+sj_1g*v~|ZF`-5_MM!oY`x02hsl70C>5obTUts3 zf`dHWTV<_b5l^xiH+kDDKqjS&rx7$L2K9{=?==s2Ehf3eY+RlrJSb~T$}vFX=G{Lm z6-jgNzGGxgAVswz5cQ&K_aUttCyh|}zJoZ7drQ#GwSSzGV{NX>9i{{1uT@YxZ-}xK z!`Yi2npTR&Mb@U*t%>y64`p#nd?yPJ%p(c4^Fg2s9hrW)iNUcY=5tx9ihS&kCMSponhnZG?(`Y`^3~ zB$eni;-@6-Pfo!&=0Ui2pG_8Oea`@3u{pghWi8?&m6{m5wqBn+j3q6|-+P@*(vSVp zRUK1V%jxM0+;SM*Kx>x$?;i3O1zI&g!Isccp*C~*niO2_hO0arz-ip|j>~Lo`2VjC zMY=%ggyA3*yw~=^9hGV*wGSYQL0!>G#BB{mYD9^$$utC;k&sVtSUK&J)~YSd>iL|YUfZTq8F2gdlskuDxUH!R=rnHQCNHuka_s>u-Y#eJE5(-g++03g zDNl`Wn!2R)@(GL_f(WVx6z71#_J%w{SaP!4t|RFjsfa4wvhUV;od%X&>W`P8VUs0% zn_MG@P~Z*RY!RiPl7*3azQ7r7B~frMyv5R9Wjp*l(RI9ZW62IgdP|E?<&w+Dfzf6@jVyOjtQxs{m^a%)R5d;fF-83KW8 zS!H7)4ZJs6)V7#=nRUJR=EB?llxvGHyxapPjc5-^K&ZkiB{Usf#1SpvvBa{7giQ5t zXJdUv(xeYYYp~FxK$8D4dH3#n2A4LFRKa%9l0ogcokOT-ubNT2K4<*=A`L%U8f{~` zN%FP{ck?FN7(|S>%9Hz^hu@YiMeQDGhZ8U+#;VCA(zeM)HpfD=H|3B z!Me6R`+cyMi1?Bc#g1%;mw7CMai(l2cB- z?=A_YV2($TlTk~X&TjeEad5>ix&C}T?lld-yz7TYM3HlH%EwK51Q zACR`!vQUcUJZ*h41TumUs%tfnAjH0>V6ESxNTFXOloZPQC4}-f*xy=MxDJ`7 z4~O1$$aV=c=HC`^bysH;+4S9~gB&Z@LA!qWmJ)V*a9*P?1NGRf2vR#k^`{CT7)6Pk zcS_}5EtB?Mb7qT_i82^A49Y~(t>tTA2TfBb!}d_g9Ik=%?nAZEez^T|xSocV#1;*e zWzd21a(*yt&L_Wqetsj#*6sO(=F#@3qT-y85!%@AUX`%c(`^)>?9*nGfZ40SdN=@< z4kEUosMrEd2t{T|4u(f0wHg(}Dhje7#IvOrbM@2NwQz%8mIFPds0aKFKAaEYCy+AXO%jg@A^x9KPP7j`1 z!I2lX`d9}*jBWIo|bT+z4jpuO2rdyhida9gl&k3#I5NbP+9}3 zs~JP2h~SG%C3x;>+JYj5TsoMd?WY|!Xv3&=_^pq4N>4tw6x-Z&Y}~-1)yB3GnUm~4 z`MoEm?eiBoigHE8?Rhdi&jc2hk%Z=JxfcN{D^_M*l2$LnF;ownig$~AhAYRI6H%^i zp?p?kk+1iv0aYW*yI{H9PxIHnZKOC*k^YpW_a|x1;fN|ep~G4JU>~Ac>S>C*s8j;T zaT)WLfY6)MLmketRP^)#N`RCb^3iU>Fn0}Uxs1E5&>C3#SPoWJ%1=6DA`WY<0YuT} zz_b~cw-=g4)#OmBwf273%HEN0dho{Eg$cObtdr$G+JEpsSy_sf;8kzX@tpUa@hy4SX7XBcTg3PIfN`T}S&>;Ao~Y&K7k z?eY{(E?tvTRJXsu;1Erpr}ua+;7I{f0GM{Du+Jv9S@4c95r&~Igo`c?Ba#?E8N{=9 zUCXscsFz@J8jesB+iqsNajl29PMcm3P_?q}A<~@pK5(Jtb`Zl@8au5L%1`!wV_M78 zY0*?QAcqU45=QV24LG+$tCqqXc4HwC$9;~2b&~p6RolSz?g>}eES!Tiq>h2)H3r7C zLD;VM!4HuKeG2`YX>-fdBDVxnx@u$Ekti6sqJ+*T0zEw=MeLrUn0qO7vOLO)51g?GioS-_3hZ z;Cie4yEXhW2k|rhRu7F3RNOq4%IH zG$+J*cWBo1srLXORBJsq0rS&QQna)m#W=D?49S; znPwJ9OFil>4i0#cE#A?H;?>QTXfWFYyS`P;p0#Fd*C09=Yaw1RC znWNk}z?9e@C31kx*NELct${_BeAgz7_m|bqNu8{zR5<45gFNl_L6{j?3EFCdAsA1q z)ZUv8)zlzdlcnY2*1P;+*Jk+5SvV(~J#Byc*L1Ak(9Rx*-8#}^(^0FdBE|N|F`fX> z2m;l6^#>U*Ap-YuoyDirIu9qA@j!l~v}AB|foAEZo7$QsrsW3e>8-u%6af;+)xUs3 z`AtQ5MWK{~?^;jgM2|fxAveDDypo!(pey5g;8YU|G(+O@C-QkXg?HX>7xW(v-JNBhaZ8LdhuyKoEPfD#dMW&r?VXsu{-a{dmx=U<4}km7bl8 zv_jG!XzLo$-MH!f^UFxjQ^5pfKy`J?qrK;O4l_0(XjbCwRmTkLp8*-9ByVFeX5|s0 zLRf?c^n(122f6Yw1vLs(C7Z__-iwOc1fx*ZgsP+@YWLzU9J8f#Q(n`Bv~Vj2`$dY` zVrvLU0cxG16z&4i`oPWQA5!q^FNNykml1D+mo-*lCZX(oZtXdxUE(ELK+)z?9yy9u z-#7Ykk=Da=!rfa4#@M0DUOhK3B|?L(3v#6xX$gUXT$I0Y9_f-E?RR73;*Dc)+OQJ@px9@M)uN8u-LEX)nqz&_ttHe&@335Oc&Z8kEbx9i=r)1*{oQ|_0 z)6ijiw+}jR`&v39Ctjdp+5PX@$!*YWOsll=qUV=EN;ex|ST|m_TEl`q2AeQAgU;Mu z<=>J`sX{ZQq!ZbrKxlN!rXmu#w|<|)uRPKWGU&8X+m{hGFUM4yOF86)fVciGEHh5Y zC!;gOd-PbRy0agkOg@~!-<^4aHh8fQqAOPgOU zpE=yVSGtK*5xvjGNnz4$1Mv1!q+2DX^t4xLpomny@vc|%dLBTuW7JSV#8FTc57rPt zjxk{vy)`)JAQVH~dLG;{hG^{lJ>R~v-Zk5>u@*!j%RZN=35s2Rm-0B&C3o-Urx#9) z!9#-T?V{_}-gtiqf^(=yID$E#6qZkyex@;M=GKk5pqC3x_~g|!wDMI*jW40-5yQOQ>51F-xi&UbjWv+3z_s;1k zms2(ebHu$u2k43Da*lEWh9ozXqbu!ohUX~0mwwvj^&XMG_ux5`=ivaNYl8BALN*UJ z?}D&GxQ)Kc)!+>*nXAg|dxqx__&|FPC3WPqj+EB)1KM@Yl)1_&X-JJN)ogBcH5s=L zJ^X#IS=2@m6xjDykD{_<*mw?Zf8`X~)YiQ9J-Z;+-A5sAikTYhf0&zk1#e@ zQxo{UEnyHX{V7dLW_1&&*)@H7He!z3tP$CDR0fc~1lPvkBT#^$&7(>X*=-ST9*$pIu%Ys^`qW1M)(Rff z^cN=YSp0u!sqYqMo+up;jtHdA! zd8AaRC}{TiamcU9f0T`@XZ`P2DIA1eLSz$VqaYH5a)NgK5ToWJkf?4JIr|LCAqMB^ zMs+Mz9d$b6MY^aV2SnKCRyxv(aZ@y>p={CIvWvozHPBvyT!i41?SOKw%|*X;mdR00 z&`LzwwC?2r9RAz?GibuL4sD*fa6&O{xq@C10Lt4Hqxd=uG}*XD_}<^cA5P?qqMbEL zFZq)HkR{6GY;D%a(NfgrVX!J5(SlZUV)NMx(n?QtxEj`euLD5icQ9V=u=h%2DY817 ze%jl?3+(3`2f)$|nzEMHJk44*zwahod*u|Po-Xqhft!0mYe%+CDS}IML^$Rx`f41I z+~?kuQs8szYryP1#qT}X@3r;^jNh7i9Fs@QzOmh_szyLD2o&=xDrmEH&Bb{;N9CH6 zTC3ODgTCq{9HTu350-rUQ?V1&W|1I7Lv&jR>6t>NWaOsMr=+3aeW!CZ0So8av2(UM zN#W1jawrr5b0Vv6v12IgJhWuHNNiH`+AsfFuEqLo*c1nrs)lp`o> zI81Xh1#w1ek{Og+7g=md1JTMWhhdc!oQu${(`O_erG5QVmg-S--O1guR>k0&bikI) z24%tK+P#`g&AU(mx#2j-jEJ2f^(Qz;;gCVd!&$ZmO(3kgM$njnT5W!99hJxCV^qMMUht8n1?A?QHHo>u=-w0pxFq)#vwg^RQ=dude}GC%3@xo}M;F zMFQPa1sk*X(=9Y=+UUBtmGna9IyM`T3mUYXIh}&RJGuMdi8YZ_dL=`n7AHV_eFsU3l+ptG6?H2A%Wz+Bo(gV{g>M1> zQ4-}=`6XNL8blW2sf^IxxH6Mh>pqHfWeXP0*>;~uDYK`zIt;!bLT*>DN@!Pc*YDFs z_j@c`V6@_aocNGa#Ldg8_TC)Y>AQA+fdob9#Xk2Lxi)4pNC72%_Gu4NQ`6PAzZGmV z?`m~0%loRt+kgCuC`TF7k>Vbuvc7JM&R*WN3!HoyBgS6jApSmSC0(VcV$DL5X3@3kq`KIqWgf^n~caIdc?f-qjn zlb;GLpz|7X_spyIJ(9ICC2AJl_XMs1e(q<{lvHNl8X4^NArSJ(&5W*T#X!yZWy;1g z0_TEo^nj2-PrL;J(P48t$7-%NA9BVChjd8o*4CciYrD4%#>p^UstoF6O+`4vlL8!* zP7tR=7Gy-5)S;TEZW6r%$Y8u?#pVH7gqG9lT+MU4XR^4jtiF{*$)CKtdmPH_d+4P0 zzslV@ptR_~82b(vXhhdx^g#pxU8o#5DW=~hRZ~Nq{8kLRSGQ2*Bh}K^UbWUC7ejAN z$I5wTLc_5?&kgyEt`F4t6L=6o@hG6nC#c00NFo@p9IL57qLl@WN&9-4Fj6(O? zY65S#r;(SSYR^gsq64A3(cC_Jd5aW^aB;Hc;v$y5hDMb1!bpYn?xn}mdLoN@?PzjL zZhZ|wi+6e5yblT7V)s0GH{V7Br>G~%gLQ;k+U)Zd-Iys2)PhDHxzG9ljY7soa6n#b zUz^ey+t?B?DAPP!1>r6tg-*7%<`2X%cY71aEPWt~_aRyQ!~7z7p9PH%vd|o2Dds`|ZeK-nq&!l!wPdx#Q=l@s&zm1-v&YvTh}a+=#lVa(kCbCEUCP1kd$3bh>{j ze??S|cMW-a5z;N2Tw*B@1WWb*?SCHKvJAZ1Y)KYYzKI*b8|$D*-U06-P7?=IZ*wi{ zdZ0pia(k5sZ7!6m&o{RGqOgkedK^r|NkJU5+{2juOiO;>THB|H2F21%Nu*LvBaP$I z*XHLKJ#%3jPIv44K9;9i(bUpBf}qInlLEdjKL5Xfn{TEJ1hAe8rrL;*n?bxt>BEyE zb>B^Rk4H_2LZ`Iii6p6_bG9+G`>oqLxz~BV+$hai7pjTmyGIcy2)uF%e^_&}YJ&@s zi)_dyr=UA<&VdCUo^hjInri?dv_-#pWV}nW<&}TT@=)30Ytm1p_Rh+Bxjc!a4LKNd6PEeaO=?#q-h(t z-!YIJ-Si4&)%WxvXG;gT5gwotks|7lc#-tSZXA+sl+&*VLa@g6w%^t8$|#Xbxs*zg zqNt@AWoS}J2g<_Di6yrsO4zbn&PE9(ez?M95!Lc;^E-}FKGRf3&Uf5Eoi1(ITmMpt z-g`?#&7f8}qMqURijN_!nm_xl2-x>ZUV5B<+g!4D!SeJ0l!0wzAPsx<0EAo%X7<^4 zg>WZ*+knb~k)#?**c==LVe(kIdzvG$aL34^Nu)!V-NQh)WI|e}P~{LuZ%$`)-*G;A zx^nNjASije(b)*x8q*;zh4T~*8-(rdDXpvfqH_B%912gFz98$<36yV?XN2yv^b@jw zbQz=}B@|QMKp;`g@Ehwp3PE_}Lb=Ol5!PSv>>iE=aWrS-dA0lQ7m2M>$bzpRP$CfG z6`t{n8mFlNn%436-hGp!zcema;?x}M65vL>u}Zp|W}4QdVUD5dcpeGEKpn^wZa^)_ zLSE!N8=2w@Z;V4pAYH=*lr^G6SrvC7kHg^+E&px>;s4etN~Vw=ur!wzk{Ju%c%brg z4^RE~K&Fwl@0?G_yPzL8!e2ayv2~}%?FH}JSO!BR!9QfVQLU~Qz;#S%m43C1o#jA~ zZ%+jyK#dm1kK3GDo)Sf+bO6d2xyQnFh&olz5Snf_hEp}7s$9lUina`+&|One+_H{h zTW(EZ>bl%`FuMQ!F;b?7Y zc}QkX|N2|O*tlAe_sbu0hKixN3lxFNvUqSSdNywlN^A^wm?8*>9hA#2|I5EfBv=+1`U+ssebQ5%%DoP1luIRH-83hr zm^Muqr;%?Qg{neI99@H8_NOOc{~V>L+1k9$*c4SX<_H{Y!MF7wgcP&2I5a+h4AW!D z?GtR{05uj!+N4Ne71CV332eNI#OSwZ=O`&D7jOvDJlJwfDxoZMDsk`uFvk(vv7DTc zr$^i7B6DH99X>E$r{lDo!2_70davClh;HM`KCN4>rHq>%&BG~m_k5><(fxza9j7y@ zy4#pnClJ|^i?Cpu*sBN-{UvB>Sp;Sa_b}rkNm;fufy6i~=XiZB5-AHshRrK;pa6q& zKt!n3Ku<&M3#r|GEl0_qgD40#@0?(-Q*M75J>zT2$l=woAjGTuKnB4?8aX$^a!g39 z(Ipu6J-*StfD--b1Avn{!QNZE2ONdyMwR|LfhO=}tqfVlJ!SMK)2*9D_8emhopPao z^`xshYP*)M+we_2TaR;N@tpNGUv8S`77(R|#9*Zd(@ zb1jE{hggiZ*QT-N%FQXFIW}Sl6;ugy1d7RspKRVs(jbgAr<(>+yQt>5X=Uq7PPyNC z2uBhioU@hL)_)p7?+BW{Gq00x%PenCpI5f_S5-8pJB^|ghUS%xU^%x98Kj5f)KJfJ zZl9m82T#nsu}U|Nr%xc4Z>$m!TGen{7lG5WkgnA1^dp^3w#_M`Z-=pa-+K@)sQR}H z>C!#Wm@{%Zol!Cti8%&Dczzn@e`KT*(2gZ^p|1pw0V)XNS)e`bDeJO*T){79Axrai zOFQKgBGm{kS<(?SAA^{Se7nx5IL;-Nn^zv4yrSJnpER<3&H~>M16;LMrJ$QXHIkaMZLS?V<)2n1 zuWprr-CIM>hB6(?5!w>wq#T#x>}&H;93I2~lwN~5euWy=B1fwdh)NGq3n=gB5?)B8 ziDb_ZORx&!q6FP{KKA|gzm4PB$}t9Fgf?Wru&8NYyqhaJn1_dYNMz%04}N2@c=zoy zGSrfaQdJ=CgUViyU4mQg~&RK#|hKJ;B=??QU%xXWDmK zzVa8QPANUS`8n2I-oc3WCWUNX+(WN^*N{6Y2X9g0^?#b>evY`kwA+b4T_(@*8ugqM z=OG`52xue4DsPcq z&Oz7u3MIvLpxdCUF3OEcrg!~$CU8o?`Y~P18l<(OC3-=d{Dt7j-|fUPvU=C6vj9;< zG1xMejjSo13d5AdFy^W}@^I5i9j$0FnWU2vE)Jk?VO*;V@@Y!pL?yRSh~tv2f1ncI z+MYO?fU#fB4b70j>8`1O?#pYW=GOxu8`K)NrC6If8)uVpiB|@H*Hx>s!CE~S#EX(` z2CQvONWg=o9VHe;Q3B)d-m>lMwB;yr=kC#{Y3o;6MSeu;I+wJ`z)+9jr2+)ix@vv; z#&d2ZpyU*Id+qNW1xHD)s)`fgQ%@s^$Fw0r=@VA;_bHfb(0#s;UiMpqK!}z$o<&fC zHJ_Td^KS#-vmG){SfYeq-0ox@1I)J%P-MXcq`L+Jhk>3Yw}_}5-ga=?!{tL;WBL+k zdw-vB#=mqEFCx+nGU~Hng}mP$3ooPRtzHYACi7j}lJaz$Ci2%dx$7doJp&Q0-HTAx z(I~p->B!oY#mPh_Ig1{kesf8Ej4M~R%V|!EY$c);bnt>#lq~BpY(2;!t#G0_hqDGj zLAO%Dtz^twhuXc{1H!Ay*c?4y(YKf_zWp|ka%v#T+;<3*CIz8H3h1W@#^ZG$2@LQ4 z-UDe0arfw!A}P}vUCTOhr0A|;)ywy^uDMelZ%=~JH}0~2#;a`h&Y*QDdlS9%uARH< z9+I&a_xRk3Lj!O3X-f4lN#HaMK2<8OKp!slw?jusfGcslV4Ud}A2TJl08d)Yd4C~%G0yc}8j*x2VR0E{@uaiY|wM9T-`f6lw#kwKond*rj* zi~&u^sOE=WKx_Y!w{jO`rv~;T6wZQdaa@s0K`J7V+@OE}dh|V)*!b!U!d=qVahq?3 z&^>JWXzD64?C%UAKy^b0f<%dlRC4;q0fqpc+TCjze4)IJ4HJQ5xw*&3B6k~N04$oq z+wa~SSg~j;Cux~>Psu>BG3xNv@*+Zc$JrqfCHdZCUb<_}F{;#kM@H3%$StyP3IS8S z8ehkyPx@-W?K=U407}6)jK|xctuDAh zrKG-HQ&yIH&{cnAQ^$su9#!8Ob*9>*!Hfi#A3djm&Al}gc-r^d3}xLi3*}6gaNDS_x2h@FI1#(QC@vb zuA*8|ulmG$tUn!lPZxp-`n3GybUhH$->FmMaXz%4!(r67$kCmZB8G1GPai`^c<$J4#$_uTfPV?;E z=P+nrAcBb2ty$ZW6-9k`p`K_C8={D-m`l*qtNIBu4mk?xu9@F`>Ryq>xggFx%$l<` zDmSk{z}{OkLkW%qedJSy9yT=3H9CjiI3?m< zpFV&*$%qI@n=kE|Z<0nive>935Ml#s&q&!eFDmv5es6Q9u*9Kk0s?K__Rv9l745-^rKfThb0I{Oig!>X zr|i))Qe_T~H>2V4Gi?IrAffwMKrHCkdqNl4Rtk1Y22 zhK+wc6-+s8Wm*^%A$|zoUO5Gi5(UknttdDOxNQW_g6zPObzVSOkxqX}BouWqT6a*{z+C1VIC4tbw zcQb0wDmZ3YGbC{M!@=(Z7zk+tpk-GTuB^n=$)S`5{kJRE*75GSy$GqM70^Xbd*g7N z-kQ@9$E-6$R?dd}J}Cg~@5@GLNe53ex#$8jNR{%i|2tZXV$Ev%!ZqWH13 z)KDj-1Wwb+L>W6HG0c<*9<`MbU{HS@gYn742{?XvLc5SEf+8M7sKbhUdw@LTmpa`; zZ_pZNlG+5K+>h%_&h6zX14W76u{@2rm#N#h$Zv03hYUAp!vwpvu|A??e#*n}i#nGa z^&WF_WX_23wu{!o8J#%X1Gi~Ii34mdsL`maLo}k>&+ssfe1G}L zr3g`3kVVLcs|i>R^6hU0Gk{Hl5CZHK^Kaurvjms0co&=Ssshn#j&hAcK4NKJDWXhR z1hlaO2`*yDi*>BcB`OR56x8{r;CFxXz#QF=wIF3tcGo!6_SW)u%hk#m@vFh^33VZ_BQ$_@YkxJ--!lfCg_NqgFE<{Azw9z|bR zIsNxHD(c_5_}QO+(%s0@(7R`wN>P8;52DWK;!!7L&x5kNr+>$yv^~$L_$aM|m8j6s zla;gohOn|i4=n;y2%WS#`p@;xzo@6=NE+dc5=n1AitGjl)}qcldQgh3sFTx9T8-}+ z-Pw=WPCo#X{>*m;Mt~bg1CgZR9vZ@*T$xIn{z6KI>~EkD8ATBk-0|Q6@ei%%>||r* z^nL3|*Fm5=z52wRzWx{xwS2QJmuqS9o#Hs^Og*?Z23{}e@?)<=zBd`@19W& z>bU;Xow<{6k`|yHq5;W&wF3)EV~M6Uv9NsvcVkaI93Q##pYlCTVe!*fVU3@>2dfpu1(B@M?XQqeey~gncwYCXl8u}O2)Ct`($*OB-7%$@E}{` z=U43+>b|@ z^a2$T-KrM;l=$4YD*{-B3Knq@*8$l^)K>7sn~jP*@rdiHvV5jEs9c1-@#b!1$+6`* zfkprF+=8nKFEsKWFlNoiycZudO)W2F-Ujt)XAH;?eV!I~Rb2QR{{E49!zmh=l`H~w zlz(JiDtkqQcfCcCIp8ESdKXk)ZhZ-+&^C;Anl5@d9($BxaQf%{x1#a4=Vvan7z3=7 zydVJX)g6JnEiQv=D5=12u7Ef>eN3DH6Ynt0Y$sB&U&STK4gww>eV^r5fCL`out6TP zvhd%}2K2Gl`kw42%wfPW&mi~BOvuJEP3`yddlW4%6^_`YgSO~>Z~ z;3`ljJzEUeTljl!qxrpNV!?48d^hhao~?1r)RQc^HCf*!8c32EcOfeYIXldYHiuwZ zu)d6PpTCRD{<<>nWQRG0Cv{m&!K`2z!xGyzS3*n$eo&MKFcH~7wW&Z;Oh#=NpgyLV zMRSRg>H16%e%)eu*KRx{X~aR~+(KGotu;KZV1{jWmo?mTuEy>T=PD#_H~n1}&ZjKX z=7w_Cu4McvxSZ>^N4|_uC8fVu)Zx%ZNH%|eqh*4&tbNs;Wx^aW&PzK{O7iSCf<;qs zQb$Tn%?>Ov(X~J2ldCT(<3IlWs}f!CwZuMJq)dCRx?$H~I#J&z#gg%ZdayA1IWLJ4 ztXgZqS#H_IZe2^ovM4@@Zj~=(gC>h^z_c$sy_**Bx>@*182Y%LJEPrL56}3@b^R0< z-4#KbGts@${rxtYi;nh!B<2gE4UFrtF2rrnD&@V`Woh~gj&pFG>nq@4z3s*fyC+WY z0tAppgvv|ojQE>VHpzXjFAm~3#x;bzMgm&&P!f&%h5zg_)zi|aWaz<8H}qwvM=5T^>?I!}P`YC}C3+kVZVs*;hcOysDSrO9 zj#m?YwHDslT<(IG1G`m+jDorR?0Dj#4yprT_#Qs5jKETb04{^%#nD}F!LDV!DVJuY zX0!Ijap{+BJtB(Uuhy~}#QNV+CPE_2D@wjxT^`OO3ACr8+ByfRJ~x-JWAFAJU|Dv3?5d$~GIIyfs62RZ>Gp6-Ak7uI#72v( z_;&QvP;m;u7j2{u*J+6MtrG|3awI8qd(ZFyo)_F%t-UmU1|7!8Wcpvuu%44|TA`(~ zR))x!OB;P_txuZxG;0^r8%)zfrN8?P)9gN6q`^LO�b!s5x;sA@b#VHrU!< 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 WRx2Om)cw!12AJHlFsRe>iuoTQgbxA$ literal 0 HcmV?d00001 diff --git a/media/ui_icons/plus.png b/media/ui_icons/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5cf3712daf72669e839b850c657ddf934d5d76 GIT binary patch literal 15215 zcmeI3Z)h839LKL+*=A+BO+gtrZnxE0TynWf(l$quwoBTzZ0SlHY3ERTxqF(PP442| zWl1~4`VXCgjLOESLqWuW;08L7A$7T{QWPyX$xu$NCD z&9Be%{e7S3`8>bp$-T*;!GT@tnm0956t%9uFFXW4W$V3mHT;|U`A8IgJdos6y-arL`Tih$R0t`l1@?9V!)YArl6Xl{GHj9C`|&Bj)AzM2AEr4pJix8 z4lpC$h%1r`feEE=S_i|^15s&uQu4`6XRz6y6`(;9m?E7`CR9Vn1{m8{fU(ugGPEr* zCj(5kH6T428Kgs+4rs5_>yTVLN4NW&oY&*yxE(a_;@m9fVtJo~;{;E;z_ro&7ZYrT zkzbd`g`se7emMAFfSEAOl)$o?OvagUJ2gGdaz3Arb@43EJD|d0OsS@rb*RSHoJqka z3=BzEQl_G*wB;+tw6qyu7;B*7E5EK}syL8p-rY*tLMoYTeDNs2`7ot8}2%rwj) zP+D2d0gdQX3a~@K(9*gDpnPksDHvwY0l1_*q7@kR9000W(@K_#KB=^xutCcb3lbm+ zQw&IftGSlcgl1~SgjNS&vAZrtQyH(r!s@A;2C_<>G^;0_Qccnbe8A&ZU7crEUT2ayv{a#&xH@cWWSXn#x ze=yguKqdj*;4poP0guiUGQPEL>s}oOFO|0zIugnXH>S8f@HP`f$-1$Fh6p#;&)PR! zp{+EjT!uAJPEcb{S604N>Gj9ROaQg)xK|w2oRzJrZ(?ZUW=7OOcO0Gw%q5gFEYGO# zEKP`N9LPbo0Ikqanc`=yT}vqCo0e-W-^)V%RqwrDmV|LlPl{$xNs4j6rqsBfEj5)A7={mJAlT;hy1jn3vbi#2P|1SCXjp-F zsbQTExU_syWlzn_eRVUDo_uDm7?1}jyH;|o@bL^j8M4Jk!}2mJ9?bh3l$La#jZr%? z2ucGR6I@tCsDR)?X<%c53yTO95L_q?Y)o)r5upNt3#EaL2`(%mR6uZ{G_Wzjg++u4 z2riTcHYT{Rh)@B+h0?&r1Q!+&Dj>K}8rYcN!XiQi1Q$vJ8xveuM5ut^LTO-Qf(wfX z6%bq~4QxzsVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@ z5h@_KP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsOBp ztGV=JOQ6DESZ3f)DSLbW{sR6Cla~61A`~^c8}^=Qw9_@*sSbgenv)!MqHrEATNnT?EaY~xU4bgWH0 zbU5IC=X2l4A3C#rSLfngd2Y$KFz|4&k#1}Zcg?I_yS|};DT~wd+s<4$dFP|wX0LDk z{@Uin%csw^ymew5IDckxc4^zyCqGG^II{Fm|Kgs$e_qtG{Fb86hV>;MJHGx^%faE@ zrxu>Qy7%Jzsm=ShPyY4k?M3gp+uuy@`A+#k`yjOG=F5K`JA7gJma?$$1~Qk`Yg$*m z@*#MaUV63rz5Pv7XMehUY3%*q=dOQw`^~$*Tns%|oOxGMX%%*Ces<-?j>Db8$nij< za&>P0)NJp;;rSo0J%2R*_6)VbUT#-YQ$GH<6V<%_Hd2xU#h=nAbfJ? H*lYg*r*Dl$ literal 0 HcmV?d00001 diff --git a/w3d_hub_linux_launcher.rb b/w3d_hub_linux_launcher.rb index d1c8c30..faa896f 100644 --- a/w3d_hub_linux_launcher.rb +++ b/w3d_hub_linux_launcher.rb @@ -53,6 +53,9 @@ require "protocol/websocket/connection" I18n.load_path << Dir["#{W3DHub::GAME_ROOT_PATH}/locales/*.yml"] I18n.default_locale = :en +# GUI_DEBUG = true +require_relative "lib/gui_state_ext" + require_relative "lib/version" require_relative "lib/theme" require_relative "lib/common" @@ -82,6 +85,7 @@ require_relative "lib/states/welcome" require_relative "lib/states/message_dialog" require_relative "lib/states/prompt_dialog" require_relative "lib/states/confirm_dialog" +require_relative "lib/states/direct_connect_dialog" require_relative "lib/api" require_relative "lib/api/service_status"