From 3266ac85e008044060002030b49e7a4fe3e462cc Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Wed, 10 Nov 2021 17:55:50 -0600 Subject: [PATCH] Refactored to use pages --- lib/page.rb | 55 ++++ lib/pages/community.rb | 64 ++++ lib/pages/download_manager.rb | 64 ++++ lib/pages/games.rb | 157 ++++++++++ lib/pages/login.rb | 30 ++ lib/pages/server_browser.rb | 214 +++++++++++++ lib/pages/settings.rb | 42 +++ lib/renegade_player.rb | 14 + lib/renegade_server.rb | 23 ++ lib/states/interface.rb | 544 ++++------------------------------ media/ui_icons/export.png | Bin 0 -> 15268 bytes media/ui_icons/import.png | Bin 0 -> 15285 bytes w3dhub.rb | 8 + 13 files changed, 736 insertions(+), 479 deletions(-) create mode 100644 lib/page.rb create mode 100644 lib/pages/community.rb create mode 100644 lib/pages/download_manager.rb create mode 100644 lib/pages/games.rb create mode 100644 lib/pages/login.rb create mode 100644 lib/pages/server_browser.rb create mode 100644 lib/pages/settings.rb create mode 100644 lib/renegade_player.rb create mode 100644 lib/renegade_server.rb create mode 100644 media/ui_icons/export.png create mode 100644 media/ui_icons/import.png diff --git a/lib/page.rb b/lib/page.rb new file mode 100644 index 0000000..25410ec --- /dev/null +++ b/lib/page.rb @@ -0,0 +1,55 @@ +class W3DHub + class Page + include CyberarmEngine::DSL + include CyberarmEngine::Common + + attr_reader :menu_bar, :status_bar, :body + + def initialize(host:) + @host = host + # @header_bar_label = host.header_bar_label + # @menu_bar = host.menu_bar + # @status_bar = host.status_bar + @body = host.body + + @options = {} + end + + def main_thread_queue + @host.main_thread_queue + end + + def options=(options) + @options = options + end + + def page(klass, options = {}) + @host.page(klass, options) + end + + # def header_bar(text) + # @header_bar_label.value = text + # end + + def setup + end + + def focus + end + + def blur + end + + def draw + end + + def update + end + + def button_down(id) + end + + def button_up(id) + end + end +end \ No newline at end of file diff --git a/lib/pages/community.rb b/lib/pages/community.rb new file mode 100644 index 0000000..8e6f8e8 --- /dev/null +++ b/lib/pages/community.rb @@ -0,0 +1,64 @@ +class W3DHub + class Pages + class Community < Page + def setup + body.clear do + stack(width: 1.0, height: 1.0, padding: 8) do + stack(width: 1.0, height: 0.15) do + tagline "Welcome to the W3D Hub Launcher" + para "The W3D Hub launcher is a one-stop shop for your W3D gamings needs, providing game downloads and automatic updating, an intregrated server browser, centralized management of in-game options and many other features." + end + + flow(width: 1.0, height: 0.1, margin_top: 24) do + flow(width: 0.375, height: 1.0) do + end + + flow(width: 0.25, height: 1.0) do + image "#{GAME_ROOT_PATH}/media/icons/apb.png", height: 1.0 + image "#{GAME_ROOT_PATH}/media/icons/ren.png", height: 1.0, margin_left: 32 + image "#{GAME_ROOT_PATH}/media/icons/tsr.png", height: 1.0, margin_left: 32 + image "#{GAME_ROOT_PATH}/media/icons/w3dhub.png", height: 1.0, margin_left: 32 + end + + flow(width: 0.375, height: 1.0) do + end + end + + stack(width: 1.0, height: 0.6, scroll: true) do + tagline "Latest Updates" + para "Beta 12", margin_left: 16 + para "- Server Browser: Added detailed information for selection server", margin_left: 32 + + para "Beta 11.6", margin_left: 16, margin_top: 16 + para "- Localisation: Added Korean translations (unknown author)", margin_left: 32 + para "- Localisation: Added Spanish translations (thanks to Silverlight and URKA)", margin_left: 32 + para "- Localisation: Added Spanish translations (thanks to darkyuri-cz)", margin_left: 32 + + para "Beta 11.0", margin_left: 16, margin_top: 16 + para "- Localisation: Added partial Chinese (Simplified) translations and Polish (thanks to DoDoCat and TrollekPL on the W3D Hub forums for providing translations)", margin_left: 32 + para "- Performance: Reduced CPU and GPU usage during game installs and updates", margin_left: 32 + para "- Settings: Added new setting menu for the launcher - click on the [gear] icon in the titlebar. Incluudes:", margin_left: 32 + para "- Manually choose language, rather than using default based on OS", margin_left: 48 + para "- Choose package cache folder location", margin_left: 48 + para "- Choose default folder into which games are installed", margin_left: 48 + para "- Server Browser: Now receives push notifications so it shows changes to maps, player counts, etc. as soon as they are available", margin_left: 32 + para "- Server Browser: Now lists servers with players in above empty ones", margin_left: 32 + para "- Server Browser: Game filter options are now saved", margin_left: 32 + end + + stack(width: 1.0, height: 0.15) do + tagline "Help & Support" + flow(width: 1.0) do + para "For help and support using this launcher or playing any W3D Hub game visit the" + link("W3D Hub forums", text_size: 16) { Launchy.open("https://w3dhub.com/forum/") } + para "or join us in" + link("[discord]#tech-support", text_size: 16) { Launchy.open("https://w3dhub.com/forum/") } + para "on the W3D Hub Discord server" + end + end + end + end + end + end + end +end diff --git a/lib/pages/download_manager.rb b/lib/pages/download_manager.rb new file mode 100644 index 0000000..91d8a95 --- /dev/null +++ b/lib/pages/download_manager.rb @@ -0,0 +1,64 @@ +class W3DHub + class Pages + class DownloadManager < Page + def setup + body.clear do + stack(width: 1.0, height: 1.0) do + flow(width: 1.0, height: 0.1, padding: 8) do + background 0xff_252550 + flow(width: 0.70, height: 1.0) do + image "#{GAME_ROOT_PATH}/media/icons/apb.png", height: 1.0 + stack(margin_left: 8) do + tagline "Red Alert: A Path Beyond" + inscription "Version: 0.3.23 (release)" + end + end + + flow(width: 0.30, height: 1.0) do + stack(width: 0.499, height: 1.0) do + para "Download Speed", width: 1.0, text_align: :center + inscription "10 MB/s", width: 1.0, text_align: :center + end + + stack(width: 0.5, height: 1.0) do + para "Downloaded", width: 1.0, text_align: :center + inscription "325.8 MB / 1.39 GB", width: 1.0, text_align: :center + end + end + end + + # UPDATES + stack(width: 1.0, height: 0.1) do + background 0x44_000000 + inscription "Something something updates...", width: 1.0, text_align: :center, margin_top: 18 + end + + # Available to download + stack(width: 1.0, height: 0.8, padding: 8, scroll: true) do + [W3DHub::Game.games + W3DHub::Game.games].flatten.each_with_index do |game, i| + flow(width: 1.0, height: 64, padding: 8) do + background 0xff_333333 if i.odd? + + flow(width: 0.7, height: 1.0) do + image game.icon, width: 0.1, margin_right: 8 + + stack(width: 0.9, height: 1.0) do + title game.name + inscription "This is a brief description of the game", width: 1.0 + end + end + + flow(width: 0.3, height: 1.0) do + list_box items: ["Release", "1.7 Beta"] + + button "Install" + end + end + end + end + end + end + end + end + end +end diff --git a/lib/pages/games.rb b/lib/pages/games.rb new file mode 100644 index 0000000..19d655f --- /dev/null +++ b/lib/pages/games.rb @@ -0,0 +1,157 @@ +class W3DHub + class Pages + class Games < Page + def setup + @@game_news ||= {} + @focused_game ||= W3DHub::Game.games.first + + body.clear do + # Games List + @games_list_container = stack(width: 0.15, height: 1.0) do + end + + # Game Menu + @game_page_container = stack(width: 0.85, height: 1.0) do + end + end + + populate_game_page(W3DHub::Game.games.first) + populate_games_list + end + + def populate_games_list + @games_list_container.clear do + background 0xff_121920 + + W3DHub::Game.games.each do |game| + selected = game == @focused_game + + game_button = stack(width: 1.0, border_thickness_left: 4, border_color_left: selected ? 0xff_00acff : 0x00_000000, hover: { background: 0xff_444444 }) do + background game.background_color if selected + + image game.icon, height: 48 + inscription game.name + end + + def game_button.hit_element?(x, y) + self if hit?(x, y) + end + + game_button.subscribe(:clicked_left_mouse_button) do |e| + populate_game_page(game) + populate_games_list + end + end + end + end + + def populate_game_page(game) + @focused_game = game + + @game_page_container.clear do + background game.background_color + + # Release channel + flow(width: 1.0, height: 0.03) do + # background 0xff_444411 + + inscription "Release" + end + + # Game Stuff + flow(width: 1.0, height: 0.89) do + # background 0xff_9999ff + + # Gane options + stack(width: 0.25, height: 1.0, padding: 8) do + # background 0xff_550055 + + game.menu_items.each do |item| + flow(width: 1.0, height: 22, margin_bottom: 8) do + image item.image, width: 0.11 + link item.label, text_size: 18 + end + end + end + + # Game News + @game_news_container = flow(width: 0.75, height: 1.0, padding: 8, scroll: true) do + # background 0xff_005500 + end + end + + # Play buttons + flow(width: 1.0, height: 0.08) do + # background 0xff_551100 + + game.play_items.each do |item| + button "#{item.label}", margin_left: 24 do + item.block&.call(game) + end + end + end + end + + unless @@game_news[game.slot] + Thread.new do + fetch_game_news(game) + main_thread_queue << proc { populate_game_news(game) } + end + + @game_news_container.clear do + title "Fetching News...", padding: 8 + end + else + populate_game_news(game) + end + end + + # FIXME: Do actual gui update on main thread + def fetch_game_news(game) + feed_uri = Excon.get( + game.news_feed, + headers: { + "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", + "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Encoding" => "deflate", + "Accept-Language" => "en-US,en;q=0.5", + "Host" => "w3dhub.com", + "DNT" => "1" + } + ) + + @@game_news[game.slot] = RSS::Parser.parse(feed_uri.body) if feed_uri.status == 200 + end + + def populate_game_news(game) + return unless @focused_game == game + + if (feed = @@game_news[game.slot]) + @game_news_container.clear do + feed.items.sort_by { |i| i.pubDate }.reverse[0..9].each do |item| + flow(width: 0.5, height: 128, margin: 4) do + # background 0x88_000000 + + image game.icon, width: 0.4 + + stack(width: 0.6, height: 1.0) do + stack(width: 1.0, height: 112) do + para "#{item.title}" + inscription "#{Sanitize.fragment(item.description[0...180]).strip}" + end + + flow(width: 1.0) do + inscription item.pubDate.strftime("%Y-%m-%d"), width: 0.5 + link "Read More", width: 0.5, text_align: :right, text_size: 14 do + Launchy.open(item.link) + end + end + end + end + end + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/pages/login.rb b/lib/pages/login.rb new file mode 100644 index 0000000..38a5faa --- /dev/null +++ b/lib/pages/login.rb @@ -0,0 +1,30 @@ +class W3DHub + class Pages + class Login < Page + def setup + body.clear do + stack(width: 1.0, height: 1.0, padding: 32) do + background 0xff_252535 + + para "Login using your W3D Hub forum account" + + flow(width: 1.0) do + tagline "Username", width: 0.25, text_align: :right, focus: true + edit_line "" + end + + flow(width: 1.0) do + tagline "Password", width: 0.25, text_align: :right + edit_line "", type: :password + end + + flow(width: 1.0) do + tagline "", width: 0.25 + button "Log In" + end + end + end + end + end + end +end diff --git a/lib/pages/server_browser.rb b/lib/pages/server_browser.rb new file mode 100644 index 0000000..5107e52 --- /dev/null +++ b/lib/pages/server_browser.rb @@ -0,0 +1,214 @@ +class W3DHub + class Pages + class ServerBrowser < Page + def setup + body.clear do + stack(width: 1.0, height: 1.0, padding: 8) do + stack(width: 1.0, height: 0.04) do + inscription "Filters" + end + + flow(width: 1.0, height: 0.06) do + flow(width: 0.75, height: 1.0) do + image "#{GAME_ROOT_PATH}/media/icons/ren.png", height: 1.0 do |img| + if img.style.color == 0xff_444444 + img.style.color = 0xff_ffffff + img.style.default[:color] = 0xff_ffffff + else + img.style.color = 0xff_444444 + img.style.default[:color] = 0xff_444444 + end + end + image "#{GAME_ROOT_PATH}/media/icons/ecw.png", height: 1.0, margin_left: 32 do |img| + if img.style.color == 0xff_444444 + img.style.color = 0xff_ffffff + img.style.default[:color] = 0xff_ffffff + else + img.style.color = 0xff_444444 + img.style.default[:color] = 0xff_444444 + end end + image "#{GAME_ROOT_PATH}/media/icons/ia.png", height: 1.0, margin_left: 32 do |img| + if img.style.color == 0xff_444444 + img.style.color = 0xff_ffffff + img.style.default[:color] = 0xff_ffffff + else + img.style.color = 0xff_444444 + img.style.default[:color] = 0xff_444444 + end end + image "#{GAME_ROOT_PATH}/media/icons/apb.png", height: 1.0, margin_left: 32 do |img| + if img.style.color == 0xff_444444 + img.style.color = 0xff_ffffff + img.style.default[:color] = 0xff_ffffff + else + img.style.color = 0xff_444444 + img.style.default[:color] = 0xff_444444 + end end + image "#{GAME_ROOT_PATH}/media/icons/tsr.png", height: 1.0, margin_left: 32, margin_right: 32 do |img| + if img.style.color == 0xff_444444 + img.style.color = 0xff_ffffff + img.style.default[:color] = 0xff_ffffff + else + img.style.color = 0xff_444444 + img.style.default[:color] = 0xff_444444 + end end + para "Region" + list_box items: ["Any", "North America", "Europe"], width: 0.25 + end + + flow(width: 0.249, height: 1.0) do + inscription "Nickname:" + inscription "Cyberarm" + image "#{GAME_ROOT_PATH}/media/ui_icons/wrench.png", height: 16 + end + end + + flow(width: 1.0, height: 0.9, margin_top: 16) do + stack(width: 0.62, height: 1.0) do + # Icon + # Hostname + # Current Map + # Players + # Ping + flow(width: 1.0, height: 0.05) do + stack(width: 0.08) do + end + + stack(width: 0.50, height: 1.0) do + para "Hostname", text_wrap: :none, width: 1.0 + end + + flow(width: 0.24, height: 1.0) do + para "Current Map", text_wrap: :none, width: 1.0 + end + + flow(width: 0.11, height: 1.0) do + para "Players", text_wrap: :none, width: 1.0 + end + + stack(width: 0.06) do + para "Ping", text_wrap: :none, width: 1.0 + end + end + + stack(width: 1.0, height: 0.95, scroll: true) do + 15.times do |i| + server_container = flow(width: 1.0, height: 48, hover: { background: 0xff_555566 }, active: { background: 0xff_555588 }) do + background 0xff_333333 if i.odd? + + image "#{GAME_ROOT_PATH}/media/icons/ren.png", width: 0.08, padding: 4 + + stack(width: 0.45, height: 1.0) do + inscription "[W3DHub] GAME SERVER" + + flow(width: 1.0, height: 1.0) do + inscription "Release", margin_right: 64, text_size: 14 + inscription "North America", text_size: 14 + end + end + + flow(width: 0.30, height: 1.0) do + inscription "C&C_Vile_Facility_D3.mix" + end + + flow(width: 0.1, height: 1.0) do + inscription "127/127" + end + + image "#{GAME_ROOT_PATH}/media/ui_icons/signal3.png", width: 0.05, color: 0xff_008000 + end + + def server_container.hit_element?(x, y) + self if hit?(x, y) + end + + server_container.subscribe(:clicked_left_mouse_button) do + populate_server_info(nil) + end + end + end + end + + @game_server_info_container = stack(width: 0.38, height: 1.0) do + para "No server selected", width: 1.0, text_align: :center + end + end + end + end + end + + def populate_server_info(server) + @game_server_info_container.clear do + stack(width: 1.0, height: 1.0, padding: 8) do + stack(width: 1.0, height: 0.3) do + flow(width: 1.0, height: 0.2) do + image "#{GAME_ROOT_PATH}/media/icons/ia.png", height: 24 + tagline "[W3D Hub] GAME SERVER" + end + + stack(width: 1.0, height: 0.25) do + button "Join Server" + end + + stack(width: 1.0, height: 0.55, margin_top: 16) do + flow(width: 1.0, height: 0.33) do + inscription "Game", width: 0.4 + inscription "GAME (branch)", width: 0.6 + end + + flow(width: 1.0, height: 0.33) do + inscription "Map", width: 0.4 + inscription "C&C_Islands.mix", width: 0.6 + end + + flow(width: 1.0, height: 0.33) do + inscription "Max Players", width: 0.4 + inscription "127", width: 0.6 + end + end + end + + flow(width: 1.0, height: 0.05) do + stack(width: 0.5, height: 1.0) do + para "GDI", width: 1.0, text_align: :center + end + + stack(width: 0.5, height: 1.0) do + para "Nod", width: 1.0, text_align: :center + end + end + + flow(width: 1.0, height: 0.65, scroll: true) do + stack(width: 0.5) do + 15.times do |i| + flow(width: 1.0, height: 18) do + stack(width: 0.6, height: 1.0) do + inscription "Player Name #{i}", text_size: 14 + end + + stack(width: 0.4, height: 1.0) do + inscription "#{rand(1000..100000)}", text_size: 14, width: 1.0, text_align: :right + end + end + end + end + + stack(width: 0.5, border_thickness_left: 2, border_color_left: 0xff_000000) do + 45.times do |i| + flow(width: 1.0, height: 18) do + stack(width: 0.6, height: 1.0) do + inscription "Player Name #{i}", text_size: 14 + end + + stack(width: 0.4, height: 1.0) do + inscription "#{rand(1000..100000)}", text_size: 14, width: 1.0, text_align: :right + end + end + end + end + end + end + end + end + end + end +end diff --git a/lib/pages/settings.rb b/lib/pages/settings.rb new file mode 100644 index 0000000..dd791f6 --- /dev/null +++ b/lib/pages/settings.rb @@ -0,0 +1,42 @@ +class W3DHub + class Pages + class Settings < Page + def setup + body.clear do + stack(width: 1.0, height: 1.0, padding: 64) do + para "Language" + para "Select the UI language you'd like to use in the W3D Hub Launcher. You should restart the launcher after changing this setting before the ui will update", width: 1.0 + list_box items: ["English", "French", "German"], width: 1.0 + + para "Folder Paths", margin_top: 32 + stack(width: 1.0, height: 0.35) do + flow(width: 1.0, height: 0.5) do + para "App Install Folder", width: 0.249 + + stack(width: 0.75, height: 1.0) do + edit_line "C:\\Program Files (x86)\\W3D Hub", width: 1.0 + inscription "The folder into which new games and apps will be installed by the launcher" + end + end + + flow(width: 1.0, height: 0.5) do + para "Package Cache Folder", width: 0.249 + + stack(width: 0.75, height: 1.0) do + edit_line "C:\\Program Data\\W3D Hub\\Launcher\\package-cache", width: 1.0 + inscription "A folder which will be used to cache downloaded packages used to install games and apps" + end + end + end + + para "Diagnostics" + check_box "Enable Automatic Error Reporting", text_size: 16 + inscription "If this is enabled the launcher will automatically report errors to the development team, along with basic information about your machine, such as operating system.", width: 1.0 + + button "Save", margin_top: 32 + end + end + end + end + end +end diff --git a/lib/renegade_player.rb b/lib/renegade_player.rb new file mode 100644 index 0000000..8d8b7b3 --- /dev/null +++ b/lib/renegade_player.rb @@ -0,0 +1,14 @@ +class W3DHub + class RenegadePlayer + attr_accessor :name, :team, :score, :kills, :deaths, :ping + + def initialize(name, team, score, kills, deaths, ping) + @name = name + @team = team + @score = score + @kills = kills + @deaths = deaths + @ping = ping + end + end +end diff --git a/lib/renegade_server.rb b/lib/renegade_server.rb new file mode 100644 index 0000000..35a53df --- /dev/null +++ b/lib/renegade_server.rb @@ -0,0 +1,23 @@ +class W3DHub + class RenegadeServer + attr_accessor :country, :country_code, :time_left, :ip, :host_port, :hostname, :map_name, + :website, :player_count, :max_players, :password, :players + + def initialize(country, country_code, time_left, ip, host_port, hostname, map_name, + website, player_count, max_players, password, players) + @country = country + @country_code = country_code + @time_left = time_left + @ip = ip + @host_port = host_port + @hostname = hostname + @map_name = map_name + @website = website + @player_count = player_count + @max_players = max_players + @password = password + + @players = players + end + end +end diff --git a/lib/states/interface.rb b/lib/states/interface.rb index edc0b46..4373d73 100644 --- a/lib/states/interface.rb +++ b/lib/states/interface.rb @@ -1,11 +1,13 @@ class W3DHub class States class Interface < CyberarmEngine::GuiState + attr_reader :main_thread_queue + def setup window.show_cursor = true - @active_page = nil - @focused_game = W3DHub::Game.games.first + @page = nil + @pages = {} @main_thread_queue = [] @@ -20,6 +22,11 @@ class W3DHub text_shadow_size: 1, text_shadow_color: 0x88_000000, }, + EditLine: { + border_thickness: 2, + border_color: Gosu::Color::WHITE, + hover: { color: Gosu::Color::WHITE } + }, Link: { color: 0xff_cdcdcd, hover: { @@ -46,8 +53,6 @@ class W3DHub } }) - @game_news = {} - stack(width: 1.0, height: 1.0, border_thickness: 1, border_color: 0xff_aaaaaa) do background 0xff_252525 @@ -63,9 +68,32 @@ class W3DHub stack(width: 0.75, height: 1.0) do title "W3D Hub Launcher", height: 0.5 flow(width: 1.0, height: 0.5) do - button get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), tip: "W3D Hub Launcher Settings", image_height: 1.0, padding_left: 4, padding_top: 4, padding_right: 4, padding_bottom: 4, margin_left: 32 do - page(:settings) + button( + get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), + tip: "W3D Hub Launcher Settings", + image_height: 1.0, + padding_left: 4, + padding_top: 4, + padding_right: 4, + padding_bottom: 4, + margin_left: 32 + ) do + page(W3DHub::Pages::Settings) end + + button( + get_image("#{GAME_ROOT_PATH}/media/ui_icons/import.png"), + tip: "Download Manager", + image_height: 1.0, + padding_left: 4, + padding_top: 4, + padding_right: 4, + padding_bottom: 4, + margin_left: 4 + ) do + page(W3DHub::Pages::DownloadManager) + end + inscription "Version 0.14.0.0", margin_left: 16 end end @@ -78,7 +106,7 @@ class W3DHub tagline "Cyberarm" flow(width: 1.0) do - link("Logout", text_size: 14) { page(:login) } + link("Logout", text_size: 14) { page(W3DHub::Pages::Login) } link "Profile", text_size: 14 end end @@ -95,15 +123,15 @@ class W3DHub flow(width: 0.55, height: 1.0) do link "Games" do - page(:games) + page(W3DHub::Pages::Games) end link "Server Browser", margin_left: 18 do - page(:server_browser) + page(W3DHub::Pages::ServerBrowser) end link "Community", margin_left: 18 do - page(:community) + page(W3DHub::Pages::Community) end end @@ -117,496 +145,54 @@ class W3DHub end end - page(:games) + page(W3DHub::Pages::Games) + end + + def draw + super + + @page&.draw end def update super + @page&.update + while(block = @main_thread_queue.shift) block&.call end end - def page(page) - return if page == @active_page + def button_down(id) + super - send(:"#{page}_page") - - @active_page = page + @page&.button_down(id) end - def games_page - @content_container.clear do - # Games List - @games_list_container = stack(width: 0.15, height: 1.0) do - end + def button_up(id) + super - # Game Menu - @game_page_container = stack(width: 0.85, height: 1.0) do - end - end - - populate_game_page(W3DHub::Game.games.first) - populate_games_list + @page&.button_up(id) end - def server_browser_page - @content_container.clear do - stack(width: 1.0, height: 1.0, padding: 8) do - stack(width: 1.0, height: 0.04) do - inscription "Filters" - end - - flow(width: 1.0, height: 0.06) do - flow(width: 0.75, height: 1.0) do - image "#{GAME_ROOT_PATH}/media/icons/ren.png", height: 1.0 do |img| - if img.style.color == 0xff_444444 - img.style.color = 0xff_ffffff - img.style.default[:color] = 0xff_ffffff - else - img.style.color = 0xff_444444 - img.style.default[:color] = 0xff_444444 - end - end - image "#{GAME_ROOT_PATH}/media/icons/ecw.png", height: 1.0, margin_left: 32 do |img| - if img.style.color == 0xff_444444 - img.style.color = 0xff_ffffff - img.style.default[:color] = 0xff_ffffff - else - img.style.color = 0xff_444444 - img.style.default[:color] = 0xff_444444 - end end - image "#{GAME_ROOT_PATH}/media/icons/ia.png", height: 1.0, margin_left: 32 do |img| - if img.style.color == 0xff_444444 - img.style.color = 0xff_ffffff - img.style.default[:color] = 0xff_ffffff - else - img.style.color = 0xff_444444 - img.style.default[:color] = 0xff_444444 - end end - image "#{GAME_ROOT_PATH}/media/icons/apb.png", height: 1.0, margin_left: 32 do |img| - if img.style.color == 0xff_444444 - img.style.color = 0xff_ffffff - img.style.default[:color] = 0xff_ffffff - else - img.style.color = 0xff_444444 - img.style.default[:color] = 0xff_444444 - end end - image "#{GAME_ROOT_PATH}/media/icons/tsr.png", height: 1.0, margin_left: 32, margin_right: 32 do |img| - if img.style.color == 0xff_444444 - img.style.color = 0xff_ffffff - img.style.default[:color] = 0xff_ffffff - else - img.style.color = 0xff_444444 - img.style.default[:color] = 0xff_444444 - end end - para "Region" - list_box items: ["Any", "North America", "Europe"], width: 0.25 - end - - flow(width: 0.249, height: 1.0) do - inscription "Nickname:" - inscription "Cyberarm" - image "#{GAME_ROOT_PATH}/media/ui_icons/wrench.png", height: 16 - end - end - - flow(width: 1.0, height: 0.9, margin_top: 16) do - stack(width: 0.62, height: 1.0) do - # Icon - # Hostname - # Current Map - # Players - # Ping - flow(width: 1.0, height: 0.05) do - stack(width: 0.08) do - end - - stack(width: 0.50, height: 1.0) do - para "Hostname", text_wrap: :none, width: 1.0 - end - - flow(width: 0.24, height: 1.0) do - para "Current Map", text_wrap: :none, width: 1.0 - end - - flow(width: 0.11, height: 1.0) do - para "Players", text_wrap: :none, width: 1.0 - end - - stack(width: 0.06) do - para "Ping", text_wrap: :none, width: 1.0 - end - end - - stack(width: 1.0, height: 0.95, scroll: true) do - 15.times do |i| - server_container = flow(width: 1.0, height: 48, hover: { background: 0xff_555566 }, active: { background: 0xff_555588 }) do - background 0xff_333333 if i.odd? - - image "#{GAME_ROOT_PATH}/media/icons/ren.png", width: 0.08, padding: 4 - - stack(width: 0.45, height: 1.0) do - inscription "[W3DHub] GAME SERVER" - - flow(width: 1.0, height: 1.0) do - inscription "Release", margin_right: 64, text_size: 14 - inscription "North America", text_size: 14 - end - end - - flow(width: 0.30, height: 1.0) do - inscription "C&C_Vile_Facility_D3.mix" - end - - flow(width: 0.1, height: 1.0) do - inscription "127/127" - end - - image "#{GAME_ROOT_PATH}/media/ui_icons/signal3.png", width: 0.05, color: 0xff_008000 - end - - def server_container.hit_element?(x, y) - self if hit?(x, y) - end - - server_container.subscribe(:clicked_left_mouse_button) do - populate_server_info(nil) - end - end - end - end - - @game_server_info_container = stack(width: 0.38, height: 1.0) do - para "No server selected", width: 1.0, text_align: :center - end - end - end - end + def body + @content_container end - def community_page - @content_container.clear do - stack(width: 1.0, height: 1.0, padding: 8) do - stack(width: 1.0, height: 0.15) do - tagline "Welcome to the W3D Hub Launcher" - para "The W3D Hub launcher is a one-stop shop for your W3D gamings needs, providing game downloads and automatic updating, an intregrated server browser, centralized management of in-game options and many other features." - end + def page(klass, options = {}) + # @menu_bar.clear + # @status_bar.clear + body.clear - flow(width: 1.0, height: 0.1, margin_top: 24) do - flow(width: 0.375, height: 1.0) do - end + @page.blur if @page - flow(width: 0.25, height: 1.0) do - image "#{GAME_ROOT_PATH}/media/icons/apb.png", height: 1.0 - image "#{GAME_ROOT_PATH}/media/icons/ren.png", height: 1.0, margin_left: 32 - image "#{GAME_ROOT_PATH}/media/icons/tsr.png", height: 1.0, margin_left: 32 - image "#{GAME_ROOT_PATH}/media/icons/w3dhub.png", height: 1.0, margin_left: 32 - end + @pages[klass] = klass.new(host: self) unless @pages[klass] + @page = @pages[klass] - flow(width: 0.375, height: 1.0) do - end - end - - stack(width: 1.0, height: 0.6, scroll: true) do - tagline "Latest Updates" - para "Beta 12", margin_left: 16 - para "- Server Browser: Added detailed information for selection server", margin_left: 32 - - para "Beta 11.6", margin_left: 16, margin_top: 16 - para "- Localisation: Added Korean translations (unknown author)", margin_left: 32 - para "- Localisation: Added Spanish translations (thanks to Silverlight and URKA)", margin_left: 32 - para "- Localisation: Added Spanish translations (thanks to darkyuri-cz)", margin_left: 32 - - para "Beta 11.0", margin_left: 16, margin_top: 16 - para "- Localisation: Added partial Chinese (Simplified) translations and Polish (thanks to DoDoCat and TrollekPL on the W3D Hub forums for providing translations)", margin_left: 32 - para "- Performance: Reduced CPU and GPU usage during game installs and updates", margin_left: 32 - para "- Settings: Added new setting menu for the launcher - click on the [gear] icon in the titlebar. Incluudes:", margin_left: 32 - para "- Manually choose language, rather than using default based on OS", margin_left: 48 - para "- Choose package cache folder location", margin_left: 48 - para "- Choose default folder into which games are installed", margin_left: 48 - para "- Server Browser: Now receives push notifications so it shows changes to maps, player counts, etc. as soon as they are available", margin_left: 32 - para "- Server Browser: Now lists servers with players in above empty ones", margin_left: 32 - para "- Server Browser: Game filter options are now saved", margin_left: 32 - end - - stack(width: 1.0, height: 0.15) do - tagline "Help & Support" - flow(width: 1.0) do - para "For help and support using this launcher or playing any W3D Hub game visit the" - link("W3D Hub forums", text_size: 16) { Launchy.open("https://w3dhub.com/forum/") } - para "or join us in" - link("[discord]#tech-support", text_size: 16) { Launchy.open("https://w3dhub.com/forum/") } - para "on the W3D Hub Discord server" - end - end - end - end - end - - def login_page - @content_container.clear do - stack(width: 1.0, height: 1.0, padding: 32) do - background 0xff_252535 - - para "Login using your W3D Hub forum account" - - flow(width: 1.0) do - tagline "Username", width: 0.25, text_align: :right, focus: true - edit_line "" - end - - flow(width: 1.0) do - tagline "Password", width: 0.25, text_align: :right - edit_line "", type: :password - end - - flow(width: 1.0) do - tagline "", width: 0.25 - button "Log In" - end - end - end - end - - def settings_page - @content_container.clear do - stack(width: 1.0, height: 1.0, padding: 64) do - para "Language" - para "Select the UI language you'd like to use in the W3D Hub Launcher. You should restart the launcher after changing this setting before the ui will update", width: 1.0 - list_box items: ["English", "French", "German"], width: 1.0 - - para "Folder Paths", margin_top: 32 - stack(width: 1.0, height: 0.35) do - flow(width: 1.0, height: 0.5) do - para "App Install Folder", width: 0.249 - - stack(width: 0.75, height: 1.0) do - edit_line "C:\\Program Files (x86)\\W3D Hub", width: 1.0 - inscription "The folder into which new games and apps will be installed by the launcher" - end - end - - flow(width: 1.0, height: 0.5) do - para "Package Cache Folder", width: 0.249 - - stack(width: 0.75, height: 1.0) do - edit_line "C:\\Program Data\\W3D Hub\\Launcher\\package-cache", width: 1.0 - inscription "A folder which will be used to cache downloaded packages used to install games and apps" - end - end - end - - para "Diagnostics" - check_box "Enable Automatic Error Reporting", text_size: 16 - inscription "If this is enabled the launcher will automatically report errors to the development team, along with basic information about your machine, such as operating system.", width: 1.0 - - button "Save", margin_top: 32 - end - end - end - - def populate_games_list - @games_list_container.clear do - background 0xff_121920 - - W3DHub::Game.games.each do |game| - selected = game == @focused_game - - stack(width: 1.0, border_thickness_left: 4, border_color_left: selected ? 0xff_00acff : 0x00_000000) do - background game.background_color if selected - - image game.icon, height: 48 - inscription game.name - end.subscribe(:clicked_left_mouse_button) do |e| - populate_game_page(game) - populate_games_list - end - end - end - end - - def populate_game_page(game) - @focused_game = game - - @game_page_container.clear do - background game.background_color - - # Release channel - flow(width: 1.0, height: 0.03) do - # background 0xff_444411 - - inscription "Release" - end - - # Game Stuff - flow(width: 1.0, height: 0.89) do - # background 0xff_9999ff - - # Gane options - stack(width: 0.25, height: 1.0, padding: 8) do - # background 0xff_550055 - - game.menu_items.each do |item| - flow(width: 1.0, height: 22, margin_bottom: 8) do - image item.image, width: 0.11 - link item.label, text_size: 18 - end - end - end - - # Game News - @game_news_container = flow(width: 0.75, height: 1.0, padding: 8, scroll: true) do - # background 0xff_005500 - end - end - - # Play buttons - flow(width: 1.0, height: 0.08) do - # background 0xff_551100 - - game.play_items.each do |item| - button "#{item.label}", margin_left: 24 do - item.block&.call(game) - end - end - end - end - - unless @game_news[game.slot] - Thread.new do - fetch_game_news(game) - @main_thread_queue << proc { populate_game_news(game) } - end - - @game_news_container.clear do - title "Fetching News...", padding: 8 - end - else - populate_game_news(game) - end - end - - # FIXME: Do actual gui update on main thread - def fetch_game_news(game) - feed_uri = Excon.get( - game.news_feed, - headers: { - "User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", - "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Encoding" => "deflate", - "Accept-Language" => "en-US,en;q=0.5", - "Host" => "w3dhub.com", - "DNT" => "1" - } - ) - - @game_news[game.slot] = RSS::Parser.parse(feed_uri.body) if feed_uri.status == 200 - end - - def populate_game_news(game) - return unless @focused_game == game - - if (feed = @game_news[game.slot]) - @game_news_container.clear do - feed.items.sort_by { |i| i.pubDate }.reverse[0..9].each do |item| - flow(width: 0.5, height: 128, margin: 4) do - # background 0x88_000000 - - image game.icon, width: 0.4 - - stack(width: 0.6, height: 1.0) do - stack(width: 1.0, height: 112) do - para "#{item.title}" - inscription "#{Sanitize.fragment(item.description[0...180]).strip}" - end - - flow(width: 1.0) do - inscription item.pubDate.strftime("%Y-%m-%d"), width: 0.5 - link "Read More", width: 0.5, text_align: :right, text_size: 14 do - Launchy.open(item.link) - end - end - end - end - end - end - end - end - - def populate_server_info(server) - @game_server_info_container.clear do - stack(width: 1.0, height: 1.0, padding: 8) do - stack(width: 1.0, height: 0.3) do - flow(width: 1.0, height: 0.2) do - image "#{GAME_ROOT_PATH}/media/icons/ia.png", height: 24 - tagline "[W3D Hub] GAME SERVER" - end - - stack(width: 1.0, height: 0.25) do - button "Join Server" - end - - stack(width: 1.0, height: 0.55, margin_top: 16) do - flow(width: 1.0, height: 0.33) do - inscription "Game", width: 0.4 - inscription "GAME (branch)", width: 0.6 - end - - flow(width: 1.0, height: 0.33) do - inscription "Map", width: 0.4 - inscription "C&C_Islands.mix", width: 0.6 - end - - flow(width: 1.0, height: 0.33) do - inscription "Max Players", width: 0.4 - inscription "127", width: 0.6 - end - end - end - - flow(width: 1.0, height: 0.05) do - stack(width: 0.5, height: 1.0) do - para "GDI", width: 1.0, text_align: :center - end - - stack(width: 0.5, height: 1.0) do - para "Nod", width: 1.0, text_align: :center - end - end - - flow(width: 1.0, height: 0.65) do - stack(width: 0.5, height: 1.0, scroll: true) do - 15.times do |i| - flow(width: 1.0, height: 18) do - stack(width: 0.6, height: 1.0) do - inscription "Player Name #{i}", text_size: 14 - end - - stack(width: 0.4, height: 1.0) do - inscription "#{rand(1000..100000)}", text_size: 14, width: 1.0, text_align: :right - end - end - end - end - - stack(width: 0.5, height: 1.0, scroll: true, border_thickness_left: 2, border_color_left: 0xff_000000) do - 45.times do |i| - flow(width: 1.0, height: 18) do - stack(width: 0.6, height: 1.0) do - inscription "Player Name #{i}", text_size: 14 - end - - stack(width: 0.4, height: 1.0) do - inscription "#{rand(1000..100000)}", text_size: 14, width: 1.0, text_align: :right - end - end - end - end - end - end - end + @page.options = options + @page.setup + @page.focus end end end diff --git a/media/ui_icons/export.png b/media/ui_icons/export.png new file mode 100644 index 0000000000000000000000000000000000000000..97100c763c26c23d3061bfe0fd150b1276a4c591 GIT binary patch literal 15268 zcmeI3du$X%9LM)4L8xL{6blAqa|Ba`>+as8?QQOkUhi7k$d%gGP~;)o+iAP$-R-fv z*Sj`=^zJWYru8iI)st5IIYpbIat?r3dUGQITvQi`IcH#d3P;J2v%Oq~LMhevmJz^`fHrWGnh-Fc7x zW1!+M&Y~#$9;u^K>+~(<{c^BM5aljV6$^%;nxY(wV`0JH2Q<11^hhD6@%N988EHv$ z8kgIAtS{^Vy;9Sl0@?>#I{brue!FN~>?(D{cxVs=nn1^bfso3_oW`Ut4`aQVG15ti z*5@?3^#SQlUn}jA6+qjnY$iWz=I9!G6=$>BIc^bcW;qMPv5eVn;yB(~!*kVi`eSsJ z!pNbB-F%z3F+CjopVQc@Xim>R z#>Wd(zaoV-Nebe7;+!1%m}M zRjm!=R#tF8wPPR*m^Prw5ycOne14%Rs9HlmTv8g*1dJN`K}aiTCHk{I;fNAQf)@P@ z2!J3=Q6UAc;9A1HvL>s&auI;p?xGkqsk;aZy{Bjzh)G4#^qy2&vZhj7?SO^MdlVpO zveF^T0avPATQjq#Js!GpxfBxRsQQqD$yeuUCT9e^f(Bf$rme7=O{}@Y%<&d8&srC< z9M7_dNlKaL;gdzFdtjUgG^pjREb4(M^F81h6a`I~kPE6E>*MqB%^_72LVnQfb-}e) zNs`EOcA*xCHQgp#m&InXiB`d6w{&%x>@{Z2!d2JUtX6w+kz>=xS$k!FM6aQ=wFs@P z4R#C1y1mtAPb0@QSZnKREpCs^YN_{Bb9P&G#uiB(ZwnNuABc^L984bXu%M_wKQ&Hc z=GbKKgLDlf&mAv~Uut?j?&9+qmztXoG%$2lXZIp-{f+*0#O8lqpONbp7% z0thR82mcS|0v5<5pc@>fNmAj_89>I@*R9{H?O@&5)=Wo0n&8G{w+G&4yx`YwY?ms) z&2=!z8!pq9n^Y=8A1Ea#u%{C%-*M^1$B6cV(3s<%cT`hWNmX$ZRqocJf&$z<@I+uP zp_Jj+jN;DzULn*2L>H5RmTAaOaWML>1*EZ?mTDclmznw-zxNK&&v(m8P|#dbQ0M_n zIMm}{a*eq$9ofGCyhjem3cSpK%i>@PH6vO2-LIE6q$DKkfpak9+Vb_C`oRxGlKL7m z3qSB_1Afw}Xm@_-mt0+*If%I_(dxuR$$(v!|i6$A1Bm8_LiD||eIPlinP(Qs@TWe?_U z4suJn&Bmx583d()jR`I+B2++dp){~D!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_K zP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4u zaG^A?F~NmJgbD~Qlm<2?xUh&&0l|gRz{Ug@77;2SxKJ9{nBc-9LIngDN&_1cTv$Y? zfZ#%DU}J&{iwG4ETqq4}OmJZlp#p*nrGbqJE-WHcKyaZnura}fMT80nE|dl~Cb+PO zPyxY((!j>I#8sMm*%E}{8=1r#;3g`%$Agx}{WD#B9K zg|!sL@1>{;`MD=PZ=@*0aI@Fl5&Qnq5669<)|J(Lb9vacujJg&eehr9rWyN6+6@c5 z+Osn%4G*&w^FD0Yc}eX5dqZOToWL8`%?DrqEb;E5M-H@{zn3a6qbE)B)(uUaT4q4w z)9?Qp={x#D3;WBHZ!KQhGVxT781`4YADBP-cIc}ezm=80T)uAQx%I0y+*rA>E;6tDgyqV=Bg5N= zuYUZ;(?6cMcJoptK6}qJgORRF&%glVEt}`u;dSCW>op}daJRen()KpjgX>*O3mh+{hBiuMZ+EV@;qLa> z-L_YdP)e*Q1`{kqz(^n=F)9!VV#1pNV(^O)NDOF11px!85`4iRAL{JAPOo=NP53yQ zT<_`3|M}0(Z=RXkFZd_&0WAM;rW_6l?C#C~ESv z#;1td{XscJId{r!?RvX^F)u0+iy$chV2MX!P)$*;dGVMat^qn70G)EQ-gNK8mnK@4 z>P;&he%2rJf{@(2P6aF0Eol?itr4A)X`Z{(73ZNr1n2@CkA$NdAFnqhe0dlf&5Vgo zNc1)JCXX>7-R@sTdlePX4vWJqvR00+by_%w-N|utX)DXw7>;GEPBX{x_FA5+p;MR1 zT?!+YDh2sgUsGy0_+Py#r0X%BVS0LcEIl@hqINQz)9GYbD`T~qp~9^7Ms*=>j%t-j zleCWyXrd~|bXkeghOZD%y7YRJ$rvberREihWd@3BDRz(|6Bl9(XJMHFNq+z1(;|_) znVQ}TvNOv&pw`wK157K>lrB{SP+pnu7Bs!F8%`;OXbeV;-5{#xwUWe)PpnG~CqPRg z1HvEzQ#43{%R84?NYNE7q!a*{=`M&-mxBdZ7(E5kKwK`6X7nV}5;c`vY8NbI-m3yZ zSJXB|3A>Z!x-5P7wAV|&v{H^rN{?3UVsh2ln#mdgpP&ObtZ6%}W;1JTvvR!6%Cq)4 zEXT7fVvNV{$>YBYpgSz9p*ZLR17TJ~y1Z zMV2L=tqnLq5C~?`QCA1&XBW)@Neq~QP*+pyU~2+S)|Qy$$n;UxK1J*@YA9tbL2G-X z)5ftLXN}d{#Bq)Gx`sNN$Lp}$8oV`}(@~SQMN&uG0#)t?Qj@Ag65Bf_s2VVKO}#0- zZ8GOUss$!uK;OHG4nM+?DK>^362i+=z2VQnSt(4B4sXlr%)Y zRF&b0E`$+Q#tQx)%y}%3n}BX`m}XgnTc;Np-?uC4%UN4BOr!txjwCbK;7G~)%) zII-QD02kNABu=<=TlS`s8OA_KL7p`oTltPkFFZz12t-G0_nfVov`VN7n`lZ4ZB1a|k63M`jdu7DGa`6G(0*4K3Y}o8n@ORSU}_7cJR3axK&KH+t<|lE?=YH6rM4 zIU;ldCKl~PW&A)TVXS};Wx!qIaM&C! zCbu~^W0@QW;dY-4&r;3UA#iG`q}-monH>c)k)Bj$vKWvDs6?$KTjApwd@^J*kA@@5 zD6=u2aFCtS6E;Te$RH>UY)o)r5upNt3#EaL2`(%mR6uZ{G_Wzjg++u42riTcHYT{R zh)@B+h0?&r1Q!+&Dj>K}8rYcN!XiQi1Q$vJ8xveuM5ut^LTO-Qf(wfX6%bq~4Qxzs zVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n z;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A? zF~NmJgbD~Qlm<3_EUwb*%a$Mt->~d~uTomJ+Rwn(Fln*5)lX6Dswk>&GetcdhTnH6 zs*9zlyQ?XRf1jetl1^|JFPs@nUjuJ%`jCLRk+ z*jK!8^-$lm@R?~JmTj9tN6)_U&!f)nffYyIQ>=TaP1QDU*Kw_+ucTypQIUzBKYsic zH2zrs>C&si|6U)yIc3IldfYhQ{JxSceN-wQ`1JCxFZTZO+tLe}4&&d99XI&(}73YzK45XFnf;mZupaV zU}KJ6`~3Dsb@9}Vv%9}paN+irc;sYoFfJXsQd@TMVD+lGKMzm3IdkC`<)=1Rc<;=r z+<*A|{-ygW)?WjsEO!@4%ga)e3Y~o3yK&&wlr#c3Lj%X={-KHPZI9lj_iXa~IH&*h zirEk9ukO14%#6JUYp(vWzT?P8#RiHLw*(dw{oJD>iW(j+rp(2E9jIO_R~RqWwlpsB J9cozJ{~yetv9SOE literal 0 HcmV?d00001 diff --git a/w3dhub.rb b/w3dhub.rb index ebd867a..9c554c1 100644 --- a/w3dhub.rb +++ b/w3dhub.rb @@ -19,6 +19,14 @@ require_relative "lib/games/interim_apex" require_relative "lib/games/ra_a_path_beyond" require_relative "lib/games/ts_reborn" +require_relative "lib/page" +require_relative "lib/pages/games" +require_relative "lib/pages/server_browser" +require_relative "lib/pages/community" +require_relative "lib/pages/login" +require_relative "lib/pages/settings" +require_relative "lib/pages/download_manager" + W3DHub::Game.load_games W3DHub::Window.new(width: 980, height: 720, borderless: false).show