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 0000000..97100c7 Binary files /dev/null and b/media/ui_icons/export.png differ diff --git a/media/ui_icons/import.png b/media/ui_icons/import.png new file mode 100644 index 0000000..1a1278f Binary files /dev/null and b/media/ui_icons/import.png differ 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