16 Commits

Author SHA1 Message Date
0eb188f72e More work on Ractor communication. Need to have a proper think about how to keep it DRY and KISS :) 2026-04-22 23:12:30 -05:00
c8bb271d82 Added downloads dialog for seeing progress and for pausing or cancelling operation 2026-04-22 23:11:29 -05:00
f1d6bbe390 Added some more paths 2026-04-22 23:10:07 -05:00
56bb6a59e2 Tweak layout so that player account info is always in the top right but the 'battleview/friends' container can be hidden 2026-04-22 08:32:25 -05:00
9d0a99de6e Update gems 2026-04-21 12:57:36 -05:00
e97e5cc862 Gems, workers, networking, oh my! 2026-04-21 12:57:05 -05:00
dc9e737587 Define ROOT_PATH to fix using absolute paths for assets 2026-04-20 12:25:19 -05:00
30776857a2 Bunch of stuff 2026-04-18 21:58:58 -05:00
43f8af42ec Flesh out about dialog a bit more, added tooltips to server browser server list item elements 2026-04-18 12:30:08 -05:00
33de050cd2 Fixed capitalization error in app name 2026-04-17 22:14:23 -05:00
6da57fef70 Tweak theme, stub about dialog 2026-04-17 18:45:43 -05:00
36610295cd Split off games and server browser into pages, theme tweaks. 2026-04-17 17:43:05 -05:00
95187f37f4 Added support for round avatars, added nine slice images, added rounded corners to a lot of stuff, tweaked theme. 2026-04-16 23:39:16 -05:00
4c283ee4d5 Re-add icons 2026-04-16 20:27:02 -05:00
5c3926e203 Tweak tweak 2026-04-16 11:10:33 -05:00
7d6fce2969 Moved THEME into its own file, added safe_get_image to prevent crash if image is not present, stubbed worker and some other bits for later. 2026-04-16 10:14:53 -05:00
151 changed files with 1379 additions and 263 deletions

4
.gitignore vendored
View File

@@ -6,7 +6,3 @@ data/logs/*
!data/logs/.gitkeep
_*.*
*.log
media/icons/*
!media/icons/app.*
!media/icons/default_icon.png
!media/icons/w3dhub.png

18
Gemfile
View File

@@ -1,3 +1,21 @@
source "https://gem.coop"
# "standard lib" gems
gem "base64"
gem "rexml"
gem "logger"
gem "json"
# "game" library gem
gem "cyberarm_engine"
gem "sdl2-bindings"
# networking libs
gem "async"
gem "async-http"
gem "async-websocket"
# misc. libs
gem "digest-crc"
gem "ircparser"
gem "rubyzip"

View File

@@ -1,20 +1,125 @@
GEM
remote: https://gem.coop/
specs:
async (2.39.0)
console (~> 1.29)
fiber-annotation
io-event (~> 1.11)
metrics (~> 0.12)
traces (~> 0.18)
async-http (0.95.0)
async (>= 2.10.2)
async-pool (~> 0.11)
io-endpoint (~> 0.14)
io-stream (~> 0.6)
metrics (~> 0.12)
protocol-http (~> 0.62)
protocol-http1 (~> 0.39)
protocol-http2 (~> 0.26)
protocol-url (~> 0.2)
traces (~> 0.10)
async-pool (0.11.2)
async (>= 2.0)
async-websocket (0.30.0)
async-http (~> 0.76)
protocol-http (~> 0.34)
protocol-rack (~> 0.7)
protocol-websocket (~> 0.17)
base64 (0.3.0)
console (1.34.3)
fiber-annotation
fiber-local (~> 1.1)
json
cyberarm_engine (0.25.1)
gosu (~> 1.1)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
ffi (1.17.4-x86_64-linux-gnu)
fiber-annotation (0.2.0)
fiber-local (1.1.0)
fiber-storage
fiber-storage (1.0.1)
gosu (1.4.6)
io-endpoint (0.17.2)
io-event (1.15.1)
io-stream (0.11.1)
ircparser (1.0.0)
json (2.19.4)
logger (1.7.0)
metrics (0.15.0)
protocol-hpack (1.5.1)
protocol-http (0.62.2)
protocol-http1 (0.39.0)
protocol-http (~> 0.62)
protocol-http2 (0.26.0)
protocol-hpack (~> 1.4)
protocol-http (~> 0.62)
protocol-rack (0.22.1)
io-stream (>= 0.10)
protocol-http (~> 0.58)
rack (>= 1.0)
protocol-url (0.4.0)
protocol-websocket (0.21.0)
protocol-http (~> 0.2)
rack (3.2.6)
rake (13.4.2)
rexml (3.4.4)
rubyzip (3.2.2)
sdl2-bindings (0.2.3)
ffi (~> 1.15)
traces (0.18.2)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
async
async-http
async-websocket
base64
cyberarm_engine
digest-crc
ircparser
json
logger
rexml
rubyzip
sdl2-bindings
CHECKSUMS
cyberarm_engine (0.25.1)
gosu (1.4.6)
async (2.39.0) sha256=df18730073f2bbb45788077dfa20cb365ecc1b9453969f44de6796b5191a00aa
async-http (0.95.0) sha256=08128cab255a48e41d1e856bfa9cbda001f1c57c88bafa3fa3a545ad795754aa
async-pool (0.11.2) sha256=0a43a17b02b04d9c451b7d12fafa9a50e55dc6dd00d4369aca00433f16a7e3ed
async-websocket (0.30.0) sha256=55739954528ad8f87f7792d0452e1268d1ef2aa5b3719f79400a05a1a6202cdf
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
console (1.34.3) sha256=869fbd74697efc4c606f102d2812b0b008e4e7fd738a91c591e8577140ec0dcc
cyberarm_engine (0.25.1) sha256=6f851a2ed0a32bdd25fc619172b546a8e26eefeb13a62c509f226337170d0efe
digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03
fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06
fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1
gosu (1.4.6) sha256=80c7fa48cf61f4960ef067fdd1c000d98b4adc8e11faeaa486543d00908a5c15
io-endpoint (0.17.2) sha256=3feaf766c116b35839c11fac68b6aaadc47887bb488902a57bf8e1d288fb3338
io-event (1.15.1) sha256=c644cdcf48254015d63f558bf4492f35471f5bb204a42180ea49752be59b30cc
io-stream (0.11.1) sha256=fa5f551fcff99581c1757b9d1cee2c37b124f07d2ca4f40b756a05ab9bd21b87
ircparser (1.0.0) sha256=0d4598f691172e2ad078207c50ba62c0d3428ae841836b0a682e6cd01d62878d
json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
metrics (0.15.0) sha256=61ded5bac95118e995b1bc9ed4a5f19bc9814928a312a85b200abbdac9039072
protocol-hpack (1.5.1) sha256=6feca238b8078da1cd295677d6f306c6001af92d75fe0643d33e6956cbc3ad91
protocol-http (0.62.2) sha256=e1c1f2f56029c1af8c4e2b8a67d0d096c76620f3afd8d99d1dcd2f6b8ffa773b
protocol-http1 (0.39.0) sha256=e49b3f4cda6f5d94c76a323d2b7f6977cba3ebd082d2da437039594da77ad8eb
protocol-http2 (0.26.0) sha256=bac89cd78082b241ccd0cf7246f5160e4bb0c9c975fb4bf7deef5f88cc317486
protocol-rack (0.22.1) sha256=1185d245927ef9849a603700d6991ca353bc89724fbf98efa4a4333ed62a9fc3
protocol-url (0.4.0) sha256=64d4c03b6b51ad815ac6fdaf77a1d91e5baf9220d26becb846c5459dacdea9e1
protocol-websocket (0.21.0) sha256=6e2ccc2adf7de1895b0f6548fdfacf5f9735c0d4deb56cd2bac1ae38a1952e93
rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
rubyzip (3.2.2) sha256=c0ed99385f0625415c8f05bcae33fe649ed2952894a95ff8b08f26ca57ea5b3c
sdl2-bindings (0.2.3) sha256=878c0755a4bea1aed0fa85c01596effd1c2b7d1878141ceec5bef808038bd309
traces (0.18.2) sha256=80f1649cb4daace1d7174b81f3b3b7427af0b93047759ba349960cb8f315e214
BUNDLED WITH
4.0.8

View File

@@ -1,7 +1,15 @@
Copyright (c) 2021 cyberarm
Copyright (c) 2021-2026 cyberarm
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

30
lib/attribution.rb Normal file
View File

@@ -0,0 +1,30 @@
module W3DHubLauncher
module Attribution
Item = Data.define(:name, :description, :url, :license, :license_url)
SPECIAL_THANKS = [
Item.new("W3D Hub", "Creating awesome games and mods for free", "https://w3dhub.com", "", ""),
Item.new("Yukihiro \"matz\" Matsumoto", "Creating the Ruby language", "https://matz.rubyist.net", "", ""),
Item.new("Samuel \"ioquatix\" Williams", "Creating and maintaining the async libraries for Ruby", "https://www.codeotaku.com", "", ""),
Item.new("Kenney", "Creating awesome game assets and releasing them completely for free\nThe launcher uses their UI Icon pack", "https://kenney.nl", "CC0", "https://creativecommons.org/publicdomain/zero/1.0/"),
]
LIBRARIES = [
Item.new("Ruby", "Programming language. A Programmer's Best Friend", "https://ruby-lang.org", "BSD 2-Clause", "https://www.ruby-lang.org/en/about/license.txt"),
Item.new("gosu", "Light-weight game library", "https://libgosu.org", "MIT", "https://github.com/gosu/gosu/blob/master/COPYING"),
Item.new("SDL2", "Simple DirectMedia Layer", "https://libsdl.org", "MIT", "https://github.com/libsdl-org/SDL/blob/SDL2/LICENSE.txt"),
Item.new("MojoAL", "OpenAL sound library implementation in a single C file", "https://icculus.org/mojoAL/", "MIT", "https://github.com/icculus/mojoAL/blob/main/LICENSE.txt"),
Item.new("async", "Asynchronous event-driven reactor for Ruby", "https://github.com/socketry/async", "MIT", "https://github.com/socketry/async/blob/main/license.md"),
Item.new("async-http", "Asynchronous http(s) client/server for Ruby", "https://github.com/socketry/async-http", "MIT", "https://github.com/socketry/async-http/blob/main/license.md"),
Item.new("async-websocket", "Asynchronous websockets for Ruby", "https://github.com/socketry/async-websocket", "MIT", "https://github.com/socketry/async-websocket/blob/main/license.md"),
Item.new("rubyzip", "Ruby library for reading and writing zip files", "https://github.com/rubyzip/rubyzip", "BSD 2-Clause", "https://github.com/rubyzip/rubyzip/blob/main/LICENSE.md"),
Item.new("digest-crc", "Ruby library for reading and writing zip files", "https://github.com/postmodern/digest-crc", "MIT", "https://github.com/postmodern/digest-crc/blob/main/LICENSE.txt"),
Item.new("ircparser", "Ruby parser for the IRCv3 message format", "https://codeberg.org/sadiepowell/ircparser-ruby", "MIT", "https://codeberg.org/sadiepowell/ircparser-ruby#license"),
Item.new("base64", "Ruby library for encoding and decoding base64", "https://github.com/ruby/base64", "BSD 2-Clause", "https://github.com/ruby/base64/blob/master/COPYING"),
Item.new("rexml", "Ruby library for parsing XML", "https://github.com/ruby/rexml", "BSD 2-Clause", "https://github.com/ruby/rexml/blob/master/LICENSE.txt"),
Item.new("logger", "Ruby logging library", "https://github.com/ruby/logger", "BSD 2-Clause", "https://github.com/ruby/logger/blob/master/COPYING"),
]
end
end

11
lib/constants.rb Normal file
View File

@@ -0,0 +1,11 @@
module W3DHubLauncher
DIRECTORY_NAME = "w3d-hub-linux-launcher".freeze
ROOT_PATH = Dir.pwd
CONFIG_PATH = "#{Dir.home}/.config/#{DIRECTORY_NAME}".freeze
CACHE_PATH = "#{Dir.home}/.cache/#{DIRECTORY_NAME}".freeze
DEFAULT_PACKAGE_CACHE_PATH = "#{CACHE_PATH}/packages".freeze
DEFAULT_APPLICATIONS_PATH = "#{Dir.home}/.local/share/#{DIRECTORY_NAME}/applications".freeze
USER_AGENT = "#{NAME} v#{VERSION}".freeze
end

9
lib/dialog.rb Normal file
View File

@@ -0,0 +1,9 @@
module W3DHubLauncher
class Dialog < CyberarmEngine::Dialog
def setup
theme(THEME)
background 0xee_222222
end
end
end

52
lib/dialogs/about.rb Normal file
View File

@@ -0,0 +1,52 @@
module W3DHubLauncher
class Dialog
class About < W3DHubLauncher::Dialog
def setup
super
stack(width: 1.0, max_width: 600, height: 1.0, max_height: 600, h_align: :center, v_align: :center, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_BLACK) do
flow(width: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
banner "About", width: 1.0, text_align: :center
end
stack(width: 1.0, fill: true, scroll: true, padding_left: PADDING, padding_right: PADDING) do
title NAME
tagline format("v%s (%s)", VERSION, VERSION_NAME), margin_left: PADDING
para "© 2021 - #{Time.now.year} cyberarm", margin_left: PADDING
link "MIT licence", margin_left: PADDING
title "Special Thanks", margin_top: LARGE_PADDING
W3DHubLauncher::Attribution::SPECIAL_THANKS.each do |item|
present_item(item)
end
title "Software / Libraries", margin_top: LARGE_PADDING
W3DHubLauncher::Attribution::LIBRARIES.each do |item|
present_item(item)
end
end
flow(width: 1.0, padding: PADDING, background_nine_slice: NINE_SLICE_ROUNDED_BOTTOM, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
flow(fill: true)
button "Close" do
pop_state
end
flow(fill: true)
end
end
end
def present_item(item)
unless item.url.empty?
link item.name, tip: item.url, margin_left: PADDING, font: FONT_BOLD
else
tagline item.name, margin_left: PADDING
end
para item.description, margin_left: LARGE_PADDING
link item.license, tip: item.license_url, margin_left: PADDING + LARGE_PADDING unless item.license.empty?
end
end
end
end

38
lib/dialogs/downloads.rb Normal file
View File

@@ -0,0 +1,38 @@
module W3DHubLauncher
class Dialog
class Downloads < W3DHubLauncher::Dialog
def setup
super
stack(width: 1.0, max_width: 600, height: 1.0, max_height: 600, h_align: :center, v_align: :center, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_BLACK) do
flow(width: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
banner "Downloads", width: 1.0, text_align: :center
end
stack(width: 1.0, fill: true, scroll: true, padding_left: PADDING, padding_right: PADDING) do
50.times do
stack(width: 1.0, margin_top: HALF_PADDING, margin_bottom: HALF_PADDING) do
flow(width: 1.0) do
para "PACKAGE_NAME.mix"
flow(fill: true)
para ["Downloading...", "Pending...", "Done.", "Patching...", "Unpacking..."].sample
end
progress(width: 1.0, fraction: rand)
end
end
end
flow(width: 1.0, padding: PADDING, background_nine_slice: NINE_SLICE_ROUNDED_BOTTOM, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
flow(fill: true)
button "Close" do
pop_state
end
flow(fill: true)
end
end
end
end
end
end

25
lib/gui_ext.rb Normal file
View File

@@ -0,0 +1,25 @@
module W3DHubLauncher
module GuiExt
BLACK_IMAGE = Gosu.render(64, 64, retro: true) { Gosu.draw_rect(0, 0, 32, 32, Gosu::Color::BLACK) }
WHITE_IMAGE = Gosu.render(64, 64, retro: true) { Gosu.draw_rect(0, 0, 32, 32, Gosu::Color::WHITE) }
def safe_get_image(path, retro: false)
return get_image(path, retro: retro) if File.exist?(path)
path = "#{ROOT_PATH}/media/default.png"
return get_image(path, retro: retro) if File.exist?(path)
WHITE_IMAGE
end
def rounded_avatar(image)
circle = get_image("#{ROOT_PATH}/media/ui/circle.png")
scale = [(circle.width.to_f / image.width).abs, (circle.width.to_f / image.height).abs].min
Gosu.render(circle.width, circle.height) do
image.draw_rot(circle.width / 2, circle.height / 2, 0, 0, 0.5, 0.5, scale, scale)
circle.draw(0, 0, 1, 1, 1, 0xff_ffffff, :multiply)
end
end
end
end

View File

@@ -0,0 +1,68 @@
module W3DHubLauncher
module Page
module Boot
class InitialSetup < CyberarmEngine::Page
include GuiExt
def setup
stack(width: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: 8, background_nine_slice_color: 0x11_ffffff, padding: HALF_PADDING) do
banner "Welcome to #{NAME}", width: 1.0, text_align: :center
tagline "Your gateway to the world of W3D Hub games.", width: 1.0, text_align: :center
end
stack(width: 1.0, fill: true, padding: PADDING) do
stack(width: 1.0, fill: true, scroll: true) do
title "Initial Setup"
caption "Please confirm launcher's default settings and make any desired tweaks.", font: FONT_REGULAR
flow(width: 1.0, height: 40, margin_top: HALF_PADDING) do
tagline "Launcher package cache directory", height: 1.0, text_v_align: :center
edit_line DEFAULT_PACKAGE_CACHE_PATH, fill: true
button "Browse..."
end
inscription "Location where the launcher will download application packages."
flow(width: 1.0, height: 40, margin_top: HALF_PADDING) do
tagline "Application installation directory", height: 1.0, text_v_align: :center
edit_line DEFAULT_APPLICATIONS_PATH, fill: true
button "Browse..."
end
inscription "Location where the launcher will install new applications."
flow(width: 1.0, height: 40, margin_top: HALF_PADDING) do
tagline "Wine context", height: 1.0, text_v_align: :center
edit_line "", fill: true
button "Browse..."
end
inscription "Location of wine context to use. Leave blank to use default."
flow(width: 1.0, height: 40, margin_top: HALF_PADDING) do
tagline "Wine command", height: 1.0, text_v_align: :center
edit_line "wine", fill: true
button "Browse..."
end
inscription "Path to wine executable. Use `wine` for system installed wine."
end
flow(width: 1.0, padding_top: PADDING) do
flow(fill: true)
button "Accept" do |btn|
btn.enabled = false
Worker::Api.update_settings({}) do |result|
if result.okay?
page(Page::Boot::StartUp)
else
btn.enabled = true
end
puts "NOW!"
end
end
end
end
end
end
end
end
end

View File

@@ -0,0 +1,82 @@
module W3DHubLauncher
module Page
module Boot
class StartUp < CyberarmEngine::Page
include GuiExt
def setup
stack(width: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: 8, background_nine_slice_color: 0x11_ffffff, padding: HALF_PADDING) do
banner "Establishing Battlefield Control", width: 1.0, text_align: :center
tagline "Please stand by...", width: 1.0, text_align: :center
end
stack(width: 1.0, fill: true, padding: PADDING) do
stack(width: 1.0, fill: true, scroll: true) do
a = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/checkmark.png"), height: 1.0, v_align: :center
tagline "DNS resolution", height: 1.0, text_v_align: :center
end
after(500) do
a.show
end
b = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/information.png"), height: 1.0, v_align: :center, color: 0xff_ff8800
tagline "Upstream backend", height: 1.0, text_v_align: :center
caption "Notice: Unreachable. Accounts will be unavailable.", height: 1.0, text_v_align: :center, color: 0xff_ff8800
end
after(1000) do
b.show
end
c = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/checkmark.png"), height: 1.0, v_align: :center
tagline "Alternate backend", height: 1.0, text_v_align: :center
end
after(1500) do
c.show
end
d = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/information.png"), height: 1.0, v_align: :center, color: 0xff_ff8800
tagline "Refresh account session", height: 1.0, text_v_align: :center
caption "Notice: Upstream backend unavailable, session not refreshed.", height: 1.0, text_v_align: :center, color: 0xff_ff8800
end
after(2000) do
d.show
end
e = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/checkmark.png"), height: 1.0, v_align: :center
tagline "Fetch game servers", height: 1.0, text_v_align: :center
end
after(2500) do
e.show
end
f = flow(width: 1.0, height: HALF_PADDING + PADDING, visible: false) do
image safe_get_image("#{ROOT_PATH}/media/icons/cross.png"), height: 1.0, v_align: :center, color: 0xff_ff0000
tagline "Fetch applications", height: 1.0, text_v_align: :center
caption "Fatal: Failed to retrieve applications list and no local cache exists. Cannot continue.", height: 1.0, text_v_align: :center, color: 0xff_ff0000
end
after(3000) do
f.show
@progress_bar.type = :linear
@progress_bar.value = 0.0
end
after(3500) do
parent.page_host.clear do
banner "Battlefield control established".upcase, width: 1.0, height: 1.0, text_v_align: :center, text_align: :center
end
end
after(3600) do
pop_state
push_state(States::Interface)
end
end
flow(width: 1.0, padding_top: PADDING) do
@progress_bar = progress width: 1.0, type: :marquee
end
end
end
end
end
end
end

38
lib/pages/boot/terms.rb Normal file
View File

@@ -0,0 +1,38 @@
module W3DHubLauncher
module Page
module Boot
class Terms < CyberarmEngine::Page
include GuiExt
TERMS_AND_CONDITIONS = <<~TERMS
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
TERMS
def setup
flow(width: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: 8, background_nine_slice_color: 0x11_ffffff, padding: HALF_PADDING) do
banner "Terms and Conditions", width: 1.0, text_align: :center
end
stack(width: 1.0, fill: true, padding: PADDING) do
stack(width: 1.0, fill: true, scroll: true, padding_bottom: PADDING) do
tagline TERMS_AND_CONDITIONS, font: FONT_REGULAR
end
flow(width: 1.0) do
flow(fill: true)
button "Decline", margin_right: PADDING do
window.close
end
button "Accept" do
page(Page::Boot::InitialSetup)
end
end
end
end
end
end
end
end

82
lib/pages/games.rb Normal file
View File

@@ -0,0 +1,82 @@
module W3DHubLauncher
module Page
class Games < CyberarmEngine::Page
include GuiExt
def setup
# game bar container
flow(width: 1.0, height: 60) do
widget(width: 220, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, hover: { background_nine_slice_color: ALPHA_BLACK }, active: { background_nine_slice_color: ALPHA_GRAY }) do
flow(width: 1.0, height: 40, margin_left: PADDING, v_align: :center, h_align: :center) do
image safe_get_image("#{ROOT_PATH}/media/icons/menuGrid.png"), height: 40, color: 0xff_bbbbbb
link "ALL GAMES", text_size: 24, font: FONT_BLACK, height: 1.0, text_v_align: :center
end
end
flow(fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, margin_left: PADDING) do
image safe_get_image("#{ROOT_PATH}/data/cache/apb.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0x88_5e5c64, border_thickness_bottom: 3, border_color_bottom: 0xff_3584e4, tip: "Red Alert: A Path Beyond"
image safe_get_image("#{ROOT_PATH}/data/cache/ren.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Command & Conquer: Renegade"
image safe_get_image("#{ROOT_PATH}/data/cache/tsr.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Tiberian Sun: Reborn"
image safe_get_image("#{ROOT_PATH}/data/cache/woa.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Battle for Dune: War of Assassins"
end
end
# game content container
flow(width: 1.0, fill: true, margin_top: LARGE_PADDING) do
# game info container
stack(width: 340, height: 1.0) do
# logo
image safe_get_image("/run/media/cyberarm/Storage/W3DHub/Launcher/package-cache/games/apb/logo.png.package"), width: 1.0, max_height: 124
# web links
stack(width: 1.0, fill: true, padding: 0, padding_top: LARGE_PADDING) do
link "Modifications", text_size: 24
link "Bug Tracker", text_size: 24
link "Player Statistics", text_size: 24
end
# launching ta game
caption "Game Version"
list_box items: [ "Release", "Open Testing" ], width: 1.0, margin_bottom: PADDING
flow(width: 1.0, height: 60) do
button "PLAY", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME
button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), image_height: 1.0, background_nine_slice: NINE_SLICE_SQUARE, **CTA_BUTTON_THEME
button safe_get_image("#{ROOT_PATH}/media/icons/gear.png"), image_height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME
end
inscription "Version: 3.9.2.15", margin_top: PADDING
end
# game events and news container
stack(fill: true, height: 1.0, margin_left: LARGE_PADDING, scroll: true) do
flow(width: 1.0, height: 1.0, max_height: 380, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
image safe_get_image("#{ROOT_PATH}/media/background.png"), fill: true, aspect_ratio: 16.0 / 9.0
stack(fill: true, height: 1.0, margin_left: PADDING) do
caption "Upcoming Event".upcase, color: 0xff_22aa11
title "Red Alert: A Path Beyond Game Night"
tagline "July 11, 2028"
flow(fill: true)
button "Read More", margin_left: PADDING, margin_right: LARGE_PADDING, margin_bottom: PADDING, width: 1.0
end
end
# news container
flow(width: 1.0, margin_top: PADDING) do
9.times do
stack(width: 1.0 / 3, min_width: 345, height: 345, aspect_ratio: 1, margin_left: HALF_PADDING, margin_right: HALF_PADDING, margin_bottom: PADDING) do
stack(width: 1.0, fill: true, background_image: safe_get_image("#{ROOT_PATH}/media/background.png"), background_image_mode: :fill)
stack(width: 1.0, height: 1.0 / 3, padding: PADDING, v_align: :bottom, background_nine_slice: NINE_SLICE_ROUNDED_BOTTOM, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, border_thickness_top: 1, border_color_top: Gosu::Color::BLACK) do
caption "NEWS", color: 0x88_ffffff
tagline "A News Item Post A News Item Post"
end
end
end
end
end
end
end
end
end
end

View File

@@ -0,0 +1,71 @@
module W3DHubLauncher
module Page
class ServerBrowser < CyberarmEngine::Page
include GuiExt
def setup
# game bar container
flow(width: 1.0, height: 60) do
flow(width: 220, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do
flow(width: 1.0, height: 40, margin_left: PADDING, v_align: :center, h_align: :center) do
image safe_get_image("#{ROOT_PATH}/media/icons/menuGrid.png"), height: 40, color: 0xff_bbbbbb
link "ALL SERVERS", text_size: 24, font: FONT_BLACK, height: 1.0, text_v_align: :center
end
end
flow(fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, margin_left: PADDING) do
image safe_get_image("#{ROOT_PATH}/data/cache/apb.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0x88_5e5c64, border_thickness_bottom: 3, border_color_bottom: 0xff_3584e4, tip: "Red Alert: A Path Beyond"
image safe_get_image("#{ROOT_PATH}/data/cache/ren.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Command & Conquer: Renegade"
image safe_get_image("#{ROOT_PATH}/data/cache/tsr.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Tiberian Sun: Reborn"
image safe_get_image("#{ROOT_PATH}/data/cache/woa.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Battle for Dune: War of Assassins"
end
end
# game content container
stack(width: 1.0, fill: true, margin_top: LARGE_PADDING, scroll: true) do
10.times do
widget(width: 1.0, height: 48, padding_top: HALF_PADDING, padding_bottom: HALF_PADDING, margin_bottom: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, hover: { background_nine_slice_color: 0xff_5e5c64 } , active: { background_nine_slice_color: 0xaa_5e5c64 }) do
# app icon container
image(safe_get_image("#{ROOT_PATH}/data/cache/apb.png"), tip: "Red Alert: A Path Beyond", width: 48, height: 1.0, margin_left: HALF_PADDING)
# friend name and status container
stack(fill: true, height: 1.0, margin_left: HALF_PADDING) do
stack(v_align: :center) do
# server name
server_name = ["Really Long Server Name Goes Here", "[US][W3D Hub] APB Game Night", "[US][W3D Hub] APB Co-op 3.7", "Really Long Server Name Goes Here Really Long Server Name Goes Here"].sample
caption server_name, text_wrap: :none, tip: server_name
# server info
a = ["North America", "South America", "Europe", "Asia", "Antarctica", "Arctica", "Oceania"]
inscription "#{a.sample} • 13:52 / #{rand > 0.5 ? '∞' : '30:00'}", text_wrap: :none, margin_top: -HALF_PADDING
end
end
# server map
stack(width: 256, height: 1.0, margin_left: HALF_PADDING) do
stack(width: 1.0, fill: true, v_align: :center) do
server_map = ["RA_Under", "C&C Superduple Long Map Name Goes Here", "RA_NorthByNorthWest", "RA_HostileWatersParadox", "RA_PacificThreat"].sample
caption server_map, tip: server_map, text_wrap: :none, width: 1.0, text_align: :center
inscription "map", text_wrap: :none, width: 1.0, text_align: :center, margin_top: -HALF_PADDING
end
end
# server player count
stack(width: 96, height: 1.0, margin_left: HALF_PADDING) do
stack(width: 1.0, fill: true, v_align: :center) do
caption format("%d / %d", rand(60), rand(60..127)), width: 1.0, text_align: :center
inscription "players", text_wrap: :none, width: 1.0, text_align: :center, margin_top: -HALF_PADDING
end
end
# server ping
flow(width: 96, height: 1.0, margin_left: HALF_PADDING, margin_right: HALF_PADDING) do
stack(fill: true, height: 1.0, v_align: :center) do
caption rand > 0.85 ? "?" : format("%d ms", rand(16..360)), width: 1.0, text_align: :center
inscription "ping", text_wrap: :none, width: 1.0, text_align: :center, margin_top: -HALF_PADDING
end
stack(width: 8, height: rand(0.25..1.0), v_align: :center, min_height: 8, background_nine_slice: NINE_SLICE_ROUNDED_SMALL, background_nine_slice_from_edge: NINE_SLICE_EDGE_SMALL, background_nine_slice_color: [0xff_26a269, 0xff_e5a50a, 0xff_a51d2d, 0xff_3d3846].sample)
end
end
end
end
end
end
end
end

9
lib/state.rb Normal file
View File

@@ -0,0 +1,9 @@
module W3DHubLauncher
class State < CyberarmEngine::GuiState
include W3DHubLauncher::GuiExt
def setup
theme(THEME)
end
end
end

35
lib/states/boot.rb Normal file
View File

@@ -0,0 +1,35 @@
module W3DHubLauncher
class States
class Boot < W3DHubLauncher::State
def setup
super
# root container - background image
stack(width: 1.0, height: 1.0, background_image: safe_get_image("/run/media/cyberarm/Storage/W3DHub/Launcher/package-cache/games/apb/background.png.package"), background_image_mode: :fill) do
# root container - background image tint
flow(width: 1.0, height: 1.0, background: 0xaa_000000) do
# content container
stack(fill: true, height: 1.0, margin: PADDING, margin_right: PADDING) do
# header bar container
flow(width: 1.0, height: 80, margin_bottom: PADDING) do |c|
# logo image
image(safe_get_image("#{ROOT_PATH}/media/logo.png"), height: 1.0)
stack(fill: true, height: 1.0) do
stack(fill: true)
title NAME
stack(fill: true)
end
end
@page_host = stack(width: 1.0, fill: true, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: 8, background_nine_slice_color: ALPHA_GRAY) do
end
end
end
end
page(Page::Boot::Terms)
end
end
end
end

139
lib/states/interface.rb Normal file
View File

@@ -0,0 +1,139 @@
module W3DHubLauncher
class States
class Interface < W3DHubLauncher::State
def setup
super
# root container - background image
stack(width: 1.0, height: 1.0, background_image: safe_get_image("/run/media/cyberarm/Storage/W3DHub/Launcher/package-cache/games/apb/background.png.package"), background_image_mode: :fill) do
# root container - background image tint
stack(width: 1.0, height: 1.0, background: ALPHA_BLACK) do
# content container
stack(width: 1.0, fill: true, margin: PADDING) do
# header bar container
flow(width: 1.0, height: 80, margin_bottom: PADDING) do |c|
# logo + menu button
button(safe_get_image("#{ROOT_PATH}/media/logo.png"), image_height: 1.0, background: 0, border_color: 0, hover: { background: 0 }, active: { background: 0, color: 0xff_ffffff }) do |btn|
menu(parent: btn) do
menu_item("Settings")
menu_item("About") do
dialog(Dialog::About)
end
menu_item("Exit") do
window.close
end
end.show
end
# navigation bar container
stack(fill: true, height: 1.0) do
# stack(fill: true)
# navigation container
flow(width: 1.0) do
link("GAMES", text_v_align: :center, font: FONT_BLACK, margin_left: PADDING) { page(Page::Games) }
link("SERVERS", text_v_align: :center, font: FONT_BLACK, margin_left: PADDING) { page(Page::ServerBrowser) }
stack(fill: true)
image safe_get_image("#{ROOT_PATH}/media/icons/import.png"), height: 40, color: 0xff_bbbbbb, tip: "Downloads" do |img|
dialog(Dialog::Downloads)
end
image safe_get_image("#{ROOT_PATH}/media/icons/information.png"), height: 40, color: 0xff_bbbbbb, tip: "Notifications"
end
# application task status bar container
stack(width: 1.0, fill: true, margin_left: PADDING) do
flow(width: 1.0) do
para "Updating Red Alert: A Path Beyond (Release)"
stack(fill: true)
para "Fetching manifests..."
end
progress(width: 1.0, fraction: 0.75)
end
end
# self account container
flow(width: 300, height: 80, margin_left: LARGE_PADDING) do
# self avatar container
stack(width: 80, height: 1.0, background_image: rounded_avatar(safe_get_image("#{ROOT_PATH}/media/default.png"))) do
# self online state container
stack(width: 20, height: 20, v_align: :bottom, h_align: :right, background_image: safe_get_image("#{ROOT_PATH}/media/ui/circle_small.png"), background_image_color: 0xff_26a269)
end
stack(fill: true, height: 1.0, margin_left: HALF_PADDING) do
flow(fill: true)
# self name
caption "cyberarm", font: FONT_BLACK, text_wrap: :none
# self set online state
link "Online ▼", text_size: 18 do |l|
menu(parent: l) do
menu_item("Online")
menu_item("Do Not Disturb")
menu_item("Away")
menu_item("Invisible")
menu_item("Sign Out")
end.show
end
flow(fill: true)
end
end
end
# layout container
flow(width: 1.0, fill: true) do
# page host container
@page_host = stack(fill: true, height: 1.0) do
end
# battleview/friends container
@battleview_container = stack(width: 300, height: 1.0, margin_left: LARGE_PADDING, visible: true) do
# friend management container
flow(width: 1.0, height: 60) do
flow(width: 1.0, v_align: :center) do
button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), image_height: 1.0
button safe_get_image("#{ROOT_PATH}/media/icons/gear.png"), image_height: 1.0, margin_left: HALF_PADDING
edit_line "", margin_left: HALF_PADDING, fill: true, height: 1.0
end
end
# friends/clanmates list container
stack(width: 1.0, fill: true, margin_top: LARGE_PADDING, scroll: true) do
50.times do |i|
# friend container
widget(width: 1.0, height: 48, padding_top: HALF_PADDING, padding_bottom: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, hover: { background_nine_slice_color: ALPHA_GRAY }, active: { background_nine_slice_color: ALPHA_BLACK }) do |w|
w.subscribe(:clicked_left_mouse_button) do
puts "HELLO THERE"
end
# friend avatar container
stack(width: 48, height: 1.0, margin_left: HALF_PADDING, background_image: rounded_avatar(safe_get_image("#{ROOT_PATH}/media/default.png"))) do
stack(width: 12, height: 12, v_align: :bottom, h_align: :right, background_image: safe_get_image("#{ROOT_PATH}/media/ui/circle_small.png"), background_image_color: 0xff_26a269)
end
# friend name and status container
stack(fill: true, height: 1.0, margin_left: HALF_PADDING, margin_right: HALF_PADDING) do
stack(v_align: :center) do
caption ["Silverlight", "PXD2000", "Alstar", "SteelGhost", "FRAYDO"].sample, text_wrap: :none
inscription "RA_Under • 13:52", text_wrap: :none, margin_top: -HALF_PADDING
end
end
# friend active application container
stack(width: 48, height: 1.0, margin_right: HALF_PADDING, background_image: safe_get_image("#{ROOT_PATH}/media/logo.png"))
end
end
end
end
end
end
end
end
page(Page::Games)
end
def button_up(id)
super
@battleview_container.toggle if id == Gosu::KB_F8
end
end
end
end

152
lib/theme.rb Normal file
View File

@@ -0,0 +1,152 @@
module W3DHubLauncher
LARGE_PADDING = 40
PADDING = 20
HALF_PADDING = 10
ALPHA_BLACK = 0x88_000000
ALPHA_GRAY = 0x88_5e5c64
FONT_LIGHT = "#{ROOT_PATH}/media/fonts/NotoSans-Light.ttf"
FONT_REGULAR = "#{ROOT_PATH}/media/fonts/NotoSans-Regular.ttf"
FONT_BOLD = "#{ROOT_PATH}/media/fonts/NotoSans-Bold.ttf"
FONT_BLACK = "#{ROOT_PATH}/media/fonts/NotoSans-Black.ttf"
FONT_MONO = "#{ROOT_PATH}/media/fonts/NotoSansMono-Regular.ttf"
NINE_SLICE_EDGE = 8
NINE_SLICE_EDGE_SMALL = 4
NINE_SLICE_EDGE_TINY = 2
NINE_SLICE_ROUNDED = "#{ROOT_PATH}/media/ui/rounded.png"
NINE_SLICE_ROUNDED_SMALL = "#{ROOT_PATH}/media/ui/rounded_small.png"
NINE_SLICE_ROUNDED_TINY = "#{ROOT_PATH}/media/ui/rounded_small.png"
NINE_SLICE_ROUNDED_LEFT = "#{ROOT_PATH}/media/ui/rounded_left.png"
NINE_SLICE_ROUNDED_RIGHT = "#{ROOT_PATH}/media/ui/rounded_right.png"
NINE_SLICE_ROUNDED_TOP = "#{ROOT_PATH}/media/ui/rounded_top.png"
NINE_SLICE_ROUNDED_BOTTOM = "#{ROOT_PATH}/media/ui/rounded_bottom.png"
NINE_SLICE_SQUARE = "#{ROOT_PATH}/media/ui/square.png"
CTA_BUTTON_THEME = {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_1c71d8,
hover: {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_3584e4
},
active: {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_1a5fb4
}
}
THEME = {
TextBlock: {
text_static: true,
font: FONT_REGULAR,
text_shadow: true,
text_shadow_color: 0x44_000000
},
Link: {
font: FONT_REGULAR,
color: 0xff_bbbbbb,
hover: {
color: 0xff_ffffff
},
active: {
color: 0xff_888888
}
},
Inscription: {
text_size: 18
},
Para: {
text_size: 20
},
Caption: {
font: FONT_BOLD
},
Tagline: {
font: FONT_BOLD
},
Title: {
font: FONT_BOLD
},
Banner: {
font: FONT_BLACK
},
Button: {
font: FONT_BOLD,
text_shadow: false,
color: 0xff_ffffff,
background: 0,#x88_5e5c64,
background_nine_slice: NINE_SLICE_ROUNDED,
background_nine_slice_from_edge: NINE_SLICE_EDGE,
background_nine_slice_mode: :stretched,
background_nine_slice_color: 0x88_5e5c64,
border_thickness: 0,
hover: {
color: 0xcc_ffffff,
background: 0,
background_nine_slice_color: 0xff_5e5c64
},
active: {
color: 0x88_ffffff,
background: 0,
background_nine_slice_color: 0xaa_5e5c64
}
},
EditLine: {
font: FONT_REGULAR
},
ListBox: {
text_align: :left,
text_size: 24,
font: FONT_REGULAR,
padding_left: HALF_PADDING
# background: 0xaa_000000,
# border_color: 0xff_000000,
# hover: {
# background: 0xaa_222222
# },
# active: {
# background: 0xaa_444444
# }
},
Menu: {
border_thickness: 1,
border_color: 0xff_000000
},
MenuItem: {
text_size: 24,
text_align: :left,
font: FONT_REGULAR,
background_nine_slice: NINE_SLICE_SQUARE,
background: 0xee_000000,
border_color: 0xaa_000000,
hover: {
background: 0xee_222222
},
active: {
background: 0xee_444444
}
},
Progress: {
height: NINE_SLICE_EDGE_TINY * 2,
background: 0,
background_nine_slice: NINE_SLICE_ROUNDED_TINY,
background_nine_slice_from_edge: NINE_SLICE_EDGE_TINY,
background_nine_slice_mode: :stretched,
background_nine_slice_color: 0x88_5e5c64,
fraction_background: 0xff_1a5fb4,
border_thickness: 0,
},
ToolTip: {
delay: 500,
text_size: 24,
background: 0,#x88_5e5c64,
background_nine_slice: NINE_SLICE_ROUNDED,
background_nine_slice_from_edge: NINE_SLICE_EDGE,
background_nine_slice_mode: :stretched,
background_nine_slice_color: 0xcc_000000,
border_thickness: 0
}
}
end

5
lib/version.rb Normal file
View File

@@ -0,0 +1,5 @@
module W3DHubLauncher
NAME = "Cyberarm's W3D Hub Linux Launcher".freeze
VERSION = "2.0.0".freeze
VERSION_NAME = "alpha".freeze # TODO: Come up with a short, fun, release name
end

View File

@@ -2,260 +2,14 @@ module W3DHubLauncher
class Window < CyberarmEngine::Window
def setup
self.show_cursor = true
self.caption = "Cyberarm's W3D Hub Linux Launcher | v2.0.0 alpha"
self.caption = format("%s | v%s (%s)", NAME, VERSION, VERSION_NAME) # "Cyberarm's W3D Hub Linux Launcher | v2.0.0 alpha"
push_state(Interface)
end
push_state(States::Boot)
# push_state(States::Interface)
end
class Interface < CyberarmEngine::GuiState
LARGE_PADDING = 40
PADDING = 20
HALF_PADDING = 10
ALPHA_BLACK = 0x88_000000
FONT_LIGHT = "./media/fonts/NotoSans-Light.ttf"
FONT_REGULAR = "./media/fonts/NotoSans-Regular.ttf"
FONT_BOLD = "./media/fonts/NotoSans-Bold.ttf"
FONT_BLACK = "./media/fonts/NotoSans-Black.ttf"
FONT_MONO = "./media/fonts/NotoSansMono-Regular.ttf"
CTA_BUTTON_THEME = {
background: 0xff_1a5fb4
}
THEME = {
TextBlock: {
text_static: true,
font: FONT_REGULAR,
text_shadow: true,
text_shadow_color: 0x44_000000
},
Link: {
font: FONT_REGULAR,
color: 0xff_bbbbbb,
hover: {
color: 0xff_ffffff
},
active: {
color: 0xff_888888
}
},
Button: {
font: FONT_BOLD,
text_shadow: false,
background: 0x88_5e5c64,
border_thickness: 1,
border_color: 0xff_000000
},
EditLine: {
font: FONT_REGULAR
},
ListBox: {
text_align: :left,
text_size: 24,
font: FONT_REGULAR,
# background: 0xaa_000000,
# border_color: 0xff_000000,
# hover: {
# background: 0xaa_222222
# },
# active: {
# background: 0xaa_444444
# }
},
Menu: {
border_thickness: 1,
border_color: 0xff_000000
},
MenuItem: {
text_size: 24,
text_align: :left,
font: FONT_REGULAR,
background: 0xee_000000,
border_color: 0xaa_000000,
hover: {
background: 0xee_222222
},
active: {
background: 0xee_444444
}
}
}
def setup
theme(THEME)
# root container - background image
stack(width: 1.0, height: 1.0, background_image: get_image("/run/media/cyberarm/Storage/W3DHub/Launcher/package-cache/games/apb/background.png.package"), background_image_mode: :fill) do
# root container - background image tint
flow(width: 1.0, height: 1.0, background: ALPHA_BLACK) do
# content container
stack(fill: true, height: 1.0, margin: PADDING, margin_right: LARGE_PADDING) do
# header bar container
flow(width: 1.0, height: 80, margin_bottom: PADDING) do |c|
# logo + menu button
button(get_image("./media/logo.png"), image_height: 1.0, background: 0, border_color: 0, hover: { background: 0 }, active: { background: 0, color: 0xff_ffffff }) do |btn|
menu(parent: btn) do
menu_item("Settings")
menu_item("About")
menu_item("Exit") do
window.close
end
end.show
end
stack(fill: true, height: 1.0) do
stack(fill: true)
flow(width: 1.0) do
link "GAMES", text_v_align: :center, font: FONT_BLACK, margin_left: PADDING
link "SERVERS", text_v_align: :center, font: FONT_BLACK, margin_left: PADDING
stack(fill: true)
image get_image("./media/icons/import.png"), height: 40, color: 0xff_bbbbbb
image get_image("./media/icons/information.png"), height: 40, color: 0xff_bbbbbb
end
stack(fill: true)
end
end
# page content container
stack(width: 1.0, fill: true) do
# game bar container
flow(width: 1.0, height: 60) do
flow(width: 220, height: 1.0, background: ALPHA_BLACK) do
flow(width: 1.0, height: 40, margin_left: PADDING, v_align: :center, h_align: :center) do
image get_image("./media/icons/menuGrid.png"), height: 40, color: 0xff_bbbbbb
link "ALL GAMES", text_size: 24, font: FONT_BLACK, height: 1.0, text_v_align: :center
end
end
flow(fill: true, height: 1.0, background: ALPHA_BLACK, margin_left: PADDING) do
image get_image("./data/cache/apb.png"), height: 1.0, padding: HALF_PADDING, background:0x88_5e5c64, border_thickness_bottom: 3, border_color_bottom: 0xff_3584e4, tip: "Red Alert: A Path Beyond"
image get_image("./data/cache/ren.png"), height: 1.0, padding: HALF_PADDING, tip: "Command & Conquer: Renegade"
image get_image("./data/cache/tsr.png"), height: 1.0, padding: HALF_PADDING, tip: "Tiberian Sun: Reborn"
image get_image("./data/cache/woa.png"), height: 1.0, padding: HALF_PADDING, tip: "Battle for Dune: War of Assassins"
end
end
# game content container
flow(width: 1.0, fill: true, margin_top: PADDING) do
# game info container
stack(width: 340, height: 1.0) do
# logo
image get_image("/run/media/cyberarm/Storage/W3DHub/Launcher/package-cache/games/apb/logo.png.package"), width: 1.0, max_height: 124
# web links
stack(width: 1.0, fill: true, padding: 0, padding_top: LARGE_PADDING) do
link "Modifications", text_size: 24
link "Bug Tracker", text_size: 24
link "Player Statistics", text_size: 24
end
# launching ta game
caption "Game Version"
list_box items: [ "Release", "Open Testing" ], width: 1.0, margin_bottom: PADDING
flow(width: 1.0, height: 60) do
button "PLAY", fill: true, height: 1.0, **CTA_BUTTON_THEME
button get_image("./media/icons/singleplayer.png"), image_height: 1.0, **CTA_BUTTON_THEME
button get_image("./media/icons/gear.png"), image_height: 1.0, **CTA_BUTTON_THEME
end
inscription "Version: 3.9.2.15", margin_top: PADDING
end
# game events and news container
stack(fill: true, height: 1.0, margin_left: LARGE_PADDING, scroll: true) do
flow(width: 1.0, height: 1.0, max_height: 380, background: ALPHA_BLACK, border_thickness: 1, border_color: Gosu::Color::BLACK) do
image get_image("./media/background.png"), fill: true, aspect_ratio: 16.0 / 9.0
stack(fill: true, height: 1.0, margin_left: PADDING) do
caption "Upcoming Event", color: 0xff_22aa11, font: FONT_BOLD
title "Red Alert: A Path Beyond Game Night", font: FONT_BOLD
tagline "July 11, 2028"
flow(fill: true)
button "Read More", margin_left: PADDING, margin_right: LARGE_PADDING, margin_bottom: PADDING, width: 1.0
end
end
# news container
flow(width: 1.0, margin_top: PADDING) do
9.times do
stack(width: 1.0 / 3, height: 345, aspect_ratio: 1, margin_left: HALF_PADDING, margin_right: HALF_PADDING, margin_bottom: PADDING, background: ALPHA_BLACK, background_image: get_image("./media/background.png"), background_image_mode: :fill, border_thickness: 1, border_color: Gosu::Color::BLACK) do
stack(width: 1.0, height: 1.0 / 3, padding: PADDING, v_align: :bottom, background: 0xdd_000000, border_thickness_top: 1, border_color_top: Gosu::Color::BLACK) do
caption "NEWS", color: 0x88_ffffff, font: FONT_BOLD
tagline "A News Item Post A News Item Post", font: FONT_BOLD
end
end
end
end
end
end
end
end
# battleview/friends container
stack(width: 300, height: 1.0, margin: PADDING, margin_left: 0) do
# self account container
flow(width: 1.0, height: 80) do
# self avatar container
stack(width: 80, height: 1.0, background_image: get_image("./media/default.png")) do
# self online state container
stack(width: 20, height: 20, v_align: :bottom, h_align: :right, background_image: get_image("./media/icons/contrast.png"), background_image_color: 0xff_26a269)
end
stack(fill: true, height: 1.0, margin_left: HALF_PADDING) do
flow(fill: true)
# self name
caption "moonsense715test", font: FONT_BLACK, text_wrap: :none
# self set online state
link "Online ▼", text_size: 18 do |l|
menu(parent: l) do
menu_item("Online")
menu_item("Do Not Disturb")
menu_item("Away")
menu_item("Invisible")
menu_item("Sign Out")
end.show
end
flow(fill: true)
end
end
# friend management container
flow(width: 1.0, height: 60, margin_top: PADDING) do
flow(width: 1.0) do # FIXME: , v_align: :center
button get_image("./media/icons/singleplayer.png"), image_height: 1.0
button get_image("./media/icons/gear.png"), image_height: 1.0, margin_left: HALF_PADDING
edit_line "", margin_left: HALF_PADDING, fill: true, height: 1.0
end
end
# Q
stack(width: 1.0, fill: true, margin_top: PADDING, scroll: true) do
50.times do |i|
# friend container
flow(width: 1.0, height: 48, margin_bottom: HALF_PADDING, hover: { background: 0x44_000000 }) do
# friend avatar container
stack(width: 48, height: 1.0, background_image: get_image("./media/default.png")) do
stack(width: 12, height: 12, v_align: :bottom, h_align: :right, background_image: get_image("./media/icons/contrast.png"), background_image_color: 0xff_26a269)
end
# friend name and status container
stack(fill: true, height: 1.0, margin_left: HALF_PADDING, margin_right: HALF_PADDING) do
stack(v_align: :center) do
caption "moonsense#{715 * i % 1000 }test", font: FONT_BOLD, text_wrap: :none, text_size: 20
inscription "RA_Under • 13:52", text_wrap: :none, text_size: 14, margin_top: -HALF_PADDING
end
end
# friend active application container
stack(width: 48, height: 1.0, background_image: get_image("./media/logo.png"))
end
end
end
end
end
end
def needs_redraw?
states.any?(&:needs_repaint?)
end
end
end

59
lib/worker.rb Normal file
View File

@@ -0,0 +1,59 @@
module W3DHubLauncher
class Worker
Response = Data.define(:status, :request_id, :data)
def initialize
@threads = []
@requests = []
@settings = # Settings.new
# next available request_id to assign incoming requests
@request_id = 0
# listen for requests from frontend
listener = Thread.new { listen }
# connect to and monitor GSH web service
@threads << Thread.new { game_server_hub_websocket }
# connect to and monitor Backend web service
@threads << Thread.new { backend_websocket }
@w3dhub_api = W3DHubLauncher::W3DHubApi.new
listener.join
end
def listen
loop do
query = Ractor.receive
pp query
case query.type
when Request::FETCH_URL
when Request::DOWNLOAD_URL
when Request::W3DHUB_API_CALL
Async do
result = @w3dhub_api.send(query.data[:call], *(query.data[:arguments] || []))
response = Response.new(result.okay? ? Request::STATUS_COMPLETE : Request::STATUS_ERROR, query.request_id, result)
Ractor.main.send(response)
end
when Request::LAUNCHER_UPDATE_SETTINGS
result = CyberarmEngine::Result.new
FileUtils.mkdir_p(W3DHubLauncher::CONFIG_PATH) # FAILABLE!
File.write("#{W3DHubLauncher::CONFIG_PATH}/settings.json", query.data) # FAILABLE!
result.data = query.data
response = Response.new(result.okay? ? Request::STATUS_COMPLETE : Request::STATUS_ERROR, query.request_id, result)
Ractor.main.send(response)
else
raise "UNKNOWN REQUEST"
end
end
end
def game_server_hub_websocket
end
def backend_websocket
end
end
end

74
lib/worker/api.rb Normal file
View File

@@ -0,0 +1,74 @@
# Helper for launcher frontend to safely communicate with ractor (prevent deadlocks and concurrent access errors)
module W3DHubLauncher
class Worker
class Api
# downloads requested resource, returns raw string
def self.fetch_url
end
# downloads requested resource, periodically reporting progress until completion, returning path for file on disk
def self.download_url
end
# returns user account data
#
# automatically handles signing in / refreshing token (DOES NOT remove account data if failed to refresh token due to network timeout)
def self.account
end
# returns launcher settings
def self.settings
end
# write updated launcher settings
def self.update_settings(settings, &block)
Worker::Request.new(Request::LAUNCHER_UPDATE_SETTINGS, settings.to_json, &block)
end
# returns list of available applications
#
# if updated list is requested, return cached version immediately and then the updated list later.
def self.applications
end
# returns current list of servers as reported from GSH / cache
def self.servers
end
# returns news for application
def self.news
end
# request installation of application
#
# periodically reports progress until completion
def self.install_application
end
# request update of application
#
# periodically reports progress until completion
def self.update_application
end
# request repair of application
#
# periodically reports progress until completion
def self.repair_application
end
# request relocation of application
#
# periodically reports progress until completion
def self.move_application
end
# request removal of application
#
# periodically reports progress until completion
def self.uninstall_application
end
end
end
end

65
lib/worker/request.rb Normal file
View File

@@ -0,0 +1,65 @@
module W3DHubLauncher
class Worker
class Request
Query = Data.define(:type, :request_id, :data)
FETCH_URL = 0
DOWNLOAD_URL = 1
W3DHUB_API_CALL = 10
LAUNCHER_SETTINGS = 1000
LAUNCHER_UPDATE_SETTINGS = 1001
STATUS_ERROR = -1 # request has failed
STATUS_PENDING = 0 # request has not yet started
STATUS_OK = 1 # request completed successfully
STATUS_COMPLETE = STATUS_OK
STATUS_IN_PROGRESS = 2 # request is in progress
STATUS_BUSY = STATUS_IN_PROGRESS
# NOT "Thread"/Ractor safe
@request_id = 0
@requests = []
# NOT "Thread"/Ractor safe. Only call from main ractor
# returns next available request id, and auto increments by 1
def self.request_id
@request_id += 1
end
# NOT "Thread"/Ractor safe.
# returns an array of pending requests
def self.requests
@requests
end
attr_reader :type, :data, :request_id
def initialize(type, data, request_id: Request.request_id, &block)
@type = type.freeze
@data = data.freeze
@status = STATUS_PENDING
@request_id = request_id
@callback = block # only called on error or success
enqueue(@type, @request_id, @data)
end
def enqueue(type, id, data)
Request.requests << self
W3DHubLauncher::WORKER.send(Query.new(type, id, data))
end
# event from Worker received
def handle_event(event, data)
pp [event, data]
case event
when STATUS_ERROR, STATUS_COMPLETE
@callback&.call(data)
Request.requests.delete(self)
end
end
end
end
end

4
lib/worker/task.rb Normal file
View File

@@ -0,0 +1,4 @@
module W3DHubLauncher
class Task
end
end

View File

View File

View File

109
lib/worker/w3dhub_api.rb Normal file
View File

@@ -0,0 +1,109 @@
module W3DHubLauncher
class W3DHubApi
API_TIMEOUT = 30 # seconds
API_CONNECT_TIMEOUT = 10 # seconds
PRIMARY_W3DHUB_API_ENDPOINT = "https://secure.w3dhub.com".freeze
ALTERNATIVE_W3DHUB_API_ENDPOINT = "https://backend.w3d.cyberarm.dev".freeze
def initialize
@access_token = nil
end
def headers(form_encoded: false)
end
# return raw response to requester
def fetch(url, method: :get, body: nil, headers: headers())
result = CyberarmEngine::Result.new
Sync do |task|
task.with_timeout(API_TIMEOUT) do
Async::HTTP::Internet.send(method, url, headers, body) do |response|
result.data = response.read
rescue StandardError => e
result.error = e
end
rescue Async::TimeoutError
result.error = e
end
end
result
end
# write response to file, periodically reporting progress to requester
def download(url, path:, method: :get, body: nil, headers: headers(), &block)
result = CyberarmEngine::Result.new
Sync do |task|
task.with_timeout(API_TIMEOUT) do
Async::HTTP::Internet.send(method, url, headers, body) do |response|
if response.success?
content_length = response.headers["content-length"] || 0
total_downloaded_bytes = 0
File.open(path, "wb") do |file|
response.each do |chunk|
file.write(chunk)
downloaded_bytes = chunk.length
total_downloaded_bytes += downloaded_bytes
block&.call(downloaded_bytes, total_downloaded_bytes, content_length)
end
end
result.data = true
end
rescue StandardError => e
result.error = e
end
rescue Async::TimeoutError
result.error = e
end
end
result
end
def user_login()
result = CyberarmEngine::Result.new
end
def refresh_user_login()
result = CyberarmEngine::Result.new
end
def fetch_user_details()
result = CyberarmEngine::Result.new
end
def fetch_applications
result = CyberarmEngine::Result.new
end
def fetch_news()
result = CyberarmEngine::Result.new
end
def fetch_events()
result = CyberarmEngine::Result.new
end
def fetch_manifest()
result = CyberarmEngine::Result.new
end
def fetch_manifests()
result = CyberarmEngine::Result.new
end
def fetch_package_details()
result = CyberarmEngine::Result.new
end
def fetch_package()
download()
end
end
end

BIN
media/icons/arrowDown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/arrowLeft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/arrowRight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/arrowUp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/audioOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/audioOn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/button1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/button2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/button3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/buttonA.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/buttonB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/buttonL.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonL1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonL2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonR1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonR2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonStart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/buttonX.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/buttonY.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/checkmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/contrast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/downLeft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/downRight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/exclamation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/exitLeft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/exitRight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/export.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/fastForward.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/gamepad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/gamepad1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/gamepad2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/gamepad3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/gamepad4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/import.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/information.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/joystick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/joystickUp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/larger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

14
media/icons/license.txt Normal file
View File

@@ -0,0 +1,14 @@
###############################################################################
Game icon pack by Kenney Vleugels (www.kenney.nl)
------------------------------
License (CC0)
http://creativecommons.org/publicdomain/zero/1.0/
You may use these graphics in personal and commercial projects.
Credit (Kenney or www.kenney.nl) would be nice but is not mandatory.
###############################################################################

BIN
media/icons/locked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/medal1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/medal2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
media/icons/menuGrid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/menuList.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/mouse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/movie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/multiplayer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/musicOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/musicOn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/phone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
media/icons/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More