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