56 Commits

Author SHA1 Message Date
f60aadac72 Fix missing wip causing crash 2024-12-09 20:08:12 -06:00
628212b5e3 Replace usages of label with para, WIP new Editor (v3) 2024-06-26 19:13:46 -05:00
86c1f24337 Migrate to Ocran, update release date 2024-01-19 17:52:06 -06:00
3aa63b4ad1 Adjust version 2024-01-18 18:12:05 -06:00
abdb86440f Added support for moving children, worked around bug in Ruby's Integer('08') parser, misc. tweaks. 2024-01-18 17:38:53 -06:00
b8cb9b9ab8 Update gems, bump version 2024-01-18 14:29:09 -06:00
c4622fa563 Make action adjust its indention when renamed 2024-01-04 17:48:58 -06:00
d49938855d Update gems 2024-01-04 17:31:41 -06:00
6554140acf Improvements to action shifting 2024-01-04 17:31:23 -06:00
43510faaa2 Made subbase actions indented, added buttons to shift actions up or down (a hair buggy but functional) 2024-01-02 18:49:17 -06:00
3350f4f063 Fix configs not sorting as downcased 2023-12-17 20:51:00 -06:00
49133bb49d update gems 2023-12-11 20:13:04 -06:00
8aaed6bc8d Fix importing action presets not correctly preventing duplicate names 2023-12-11 20:12:39 -06:00
4bdc12ce46 Refactored ROOT_PATH to be a subfolder in the current user's home directory (configs and settings will not need to be manually moved between updates) 2023-11-18 15:24:07 -06:00
f98edc1744 Fixed game clock colon 'blinking' in the middle of the second 2023-11-18 15:22:43 -06:00
9940837065 Implemented Centerstage field rendering 2023-11-18 14:33:32 -06:00
a8ebaebe4c Updated practice clock to use a zero exclusive display (matches offical clock better), tweaked sound timings a bit to match, disabled color changing on clock to match offical clock, misc. tweaks. 2023-11-17 17:09:44 -06:00
08bbd2c638 Updated gems 2023-11-15 18:00:23 -06:00
08fdd1de1b Updated randomizer 2023-11-15 18:00:17 -06:00
ffe946ae15 UX improvements of dual screen/remote game clock (buttons are dynamically enabled/disabled depending if the clock is running or not) 2023-11-13 11:41:08 -06:00
d94490110e UX improvements to monoscreen game clock (buttons are dynamically enabled/disabled depending if the clock is running or not), stubbed centerstage field (empty atm), fixed error when disconnecting from clocknet 2023-11-13 11:21:51 -06:00
1145ac646d Bump version 2023-02-06 18:07:44 -06:00
be1c1f4989 Make field planner request recalulate on mouse move 2023-02-06 15:10:30 -06:00
caa2000e91 Bump version 2023-02-06 14:35:08 -06:00
7bcc32097f Replicate improvements from Editor in Preset editor 2023-02-06 14:22:06 -06:00
2d2a4e0733 Make presets editor use Dialog::ActionDialog for editing action preset instead of TAC::Dialog::NamePromptDialog- Enables editing preset comment, action preset comments are now shown like in the Editor. 2023-02-04 21:03:14 -06:00
d61785c98a Actually fix highlight animator not triggering repaint 2023-02-02 10:06:00 -06:00
07fdf0055e Fixed highlight animator not rendering correctly due to a missing repaint request 2023-02-01 16:36:31 -06:00
2a8a2d1bbf Bump version 2023-02-01 15:50:31 -06:00
48b4d6ddcf Update gems, sync rakefile 2023-02-01 15:50:14 -06:00
0cd4475a8f WIP: Improved search page, editor now pulses the point of interest to draw focus, fixes for Editor not highlighting or scrolling into view items from a search 2023-02-01 15:46:54 -06:00
acc0792d17 WIP: Fix crash when deleting a group, implemented scroll_into_view- adding/editing an item it will be snapped into view. 2023-02-01 10:51:57 -06:00
6f211adf28 WIP: Cloning and importing presets no longer regenerates lists 2023-01-31 15:57:25 -06:00
3d29ff46e6 WIP: More work on optimizing Editor 2023-01-31 14:31:10 -06:00
27d0b7314f WIP: Refactoring Editor to no longer rebuild lists from scratch when changing most anything 2023-01-31 10:14:24 -06:00
308575dc63 Refactor TACNET Client a bit 2023-01-30 16:16:02 -06:00
94cd822b0c Fix up game clock a bit more so that it correctly requests repaints when remotely controlled, fixed layout issues with Drive Team Rotation Generator, update README 2023-01-30 15:52:21 -06:00
c312f4839d Fixed indentation, fixed calling .strip on nil in TACNET Client#read 2023-01-30 08:42:46 -06:00
3a8f4e5860 Added used software section to home page 2023-01-30 08:38:26 -06:00
7e09031257 Fixed Boot animation not requesting repaint every frame 2023-01-30 08:37:56 -06:00
905ced174b Fixed when adding action from preset: editing comment before adding was not preserved, and not defaulting to enabled 2023-01-30 08:37:30 -06:00
510a24644b Make titlebar TACNET_PRIMARY (blue) when connected, log tacnet read error. 2023-01-26 10:25:42 -06:00
570d965669 VariableDialog now reports an error if attempting to name a variable to one that already exists 2023-01-22 09:53:03 -06:00
60356fc7fa Added 'Open Folder' button on configurations page, replaced target icon with 'Generate' text for generate roster button on team rotation generator page, make group/action get highlighted correctly when selected from search. 2023-01-22 09:39:27 -06:00
41f5710b4a FieldPlanner: Fixed wrong inch to metric maths, fixed node counter and total distance labels overlapping, added toolip when hovering over the field showing the X/Y coordinate in 'unit' with the origin at field center. 2023-01-21 22:37:19 -06:00
1e21c64a18 Fixed using File.exists? instead of File.exist?, updated Randomizer to PowerPlay (with ducks), misc. tweaking. 2023-01-19 12:33:51 -06:00
451568003a Remove no longer needed clipboard and ffi gems, use new CyberarmEngine::GuiState#needs_repaint? method to only paint when needed- should avoid wasting CPU/GPU time (gain a bit more battery life?), make TACNET connection error visually change UI and also trigger a TACNET dialog when not on the TACNET page 2023-01-05 08:42:29 -06:00
cce1c2c341 Bump version 2022-10-23 18:42:46 -05:00
d5cf1cb6a2 Slight formatting change 2022-10-23 18:42:25 -05:00
a96cc7c604 Fixed actions not getting sorted when an action is updated 2022-10-23 17:44:28 -05:00
c2e527653d Added Alt+G/A/V shortcuts to add groups, actions, and variables
respectively.
2022-10-23 17:36:32 -05:00
66b0eb3d1e Updated Field/Robot/Simulator to use parent container's z index to remove need for Gosu.flush and causing Field to be drawn on top of tooltip's and menus, Added Power Play field, replaced font, enabled static option for text to improve rendering appearance, misc. bug fixes and tweaks. 2022-10-23 17:30:51 -05:00
655b418d70 Updates to support latest version of CyberarmEngine 2022-09-20 17:17:57 -05:00
a8fc2dccde A few tweaks 2022-04-27 14:31:01 -05:00
0d6fb8a657 Added comment 'state' for show a comment above the robot (TODO: make it non-scaled and actually hover above robot) 2021-10-23 12:40:13 -05:00
15d8e2ff62 Added delay state to simulator 2021-10-23 12:13:23 -05:00
56 changed files with 2016 additions and 935 deletions

View File

@@ -1,11 +1,8 @@
source "https://rubygems.org"
gem "cyberarm_engine"
gem "gosu_notifications"
gem "ffi"
gem "clipboard"
group :packaging do
gem "ocra"
gem "releasy"
end
gem "ocran"
# gem "releasy", path: "../releasy"
end

View File

@@ -1,37 +1,18 @@
GEM
remote: https://rubygems.org/
specs:
clipboard (1.3.6)
cri (2.1.0)
cyberarm_engine (0.19.1)
clipboard (~> 1.3.5)
excon (~> 0.78.0)
cyberarm_engine (0.24.4)
gosu (~> 1.1)
gosu_more_drawables (~> 0.3)
excon (0.78.1)
ffi (1.15.4-x64-mingw32)
gosu (1.2.0)
gosu_more_drawables (0.3.1)
gosu_notifications (0.1.0)
ocra (1.3.11)
rake (13.0.6)
releasy (0.2.3)
bundler (>= 1.2.1)
cri (~> 2.1.0)
ocra (~> 1.3.0)
rake (>= 0.9.2.2)
gosu (1.4.6)
ocran (1.3.15)
PLATFORMS
x64-mingw32
x64-mingw-ucrt
x86_64-linux
DEPENDENCIES
clipboard
cyberarm_engine
ffi
gosu_notifications
ocra
releasy
ocran
BUNDLED WITH
2.2.28
2.5.14

View File

@@ -1 +1,42 @@
# TimeCrafters Configuration Tool for Desktop
A desktop app for editing, either locally or remotely, JSON configuration files on the Robot Controller/Rev Control Hub.
![Screenshot of Editor](https://raw.githubusercontent.com/TimeCrafters/timecrafters_configuration_tool_desktop/master/media/screenshots/screenshot_editor.png)
## Features
* TACNET - **T**imeCrafters **A**uxiliary **C**onfiguration **NET**work.
* Enables syncing configurations between devices.
* Multiple Configurations
* Create multiple configurations for specific robots/projects.
* Presets
* Save Groups or Actions as Presets to quickly add pre-configurated Groups and Actions.
* Search
* Search through the active configurations Groups, Actions, Variables and Presets.
* Built-in practice clock
* Supports multiple screens or network remote control.
* Supports official clock sounds (must be added manually.)
* Built-in jukebox.
* Simulator
* Create a simple 2D simulation of your robot's path.
* Field Planner
* Estimate distances on the field using imperial or metric units.
## Usage
* Download from [Releases](https://github.com/TimeCrafters/timecrafters_configuration_tool_desktop/releases/latest)
## Developing
* Install [Ruby](https://ruby-lang.org)
* Run `bundle install`
* Run `bundle exec ruby timecrafters_configuration_tool.rb`
## Contributing
* Clone this repo and create a new branch for your feature/patch.
* Author your changes
* Commit your changes and push to your fork
* Open a pull request

View File

@@ -18,12 +18,12 @@ Releasy::Project.new do
"media/music/.gitkeep",
"media/particles/.gitkeep"
]
exclude_encoding # Applications that don't use advanced encoding (e.g. Japanese characters) can save build size with this.
# exclude_encoding # Applications that don't use advanced encoding (e.g. Japanese characters) can save build size with this.
verbose
add_build :windows_folder do
icon "media/icon.ico"
executable_type :console # Assuming you don't want it to run with a console window.
executable_type :windows # Assuming you don't want it to run with a console window.
add_package :exe # Windows self-extracting archive.
end
end

View File

@@ -2,6 +2,8 @@ module TAC
class Backend
attr_reader :config, :settings, :tacnet
def initialize
create_directories
load_settings
load_config(@settings.config) if @settings.config && File.exist?("#{TAC::CONFIGS_PATH}/#{@settings.config}.json")
@tacnet = TACNET.new
@@ -42,13 +44,13 @@ module TAC
end
def move_config(old_name, new_name)
if not File.exists?("#{TAC::CONFIGS_PATH}/#{old_name}.json") or
if not File.exist?("#{TAC::CONFIGS_PATH}/#{old_name}.json") or
File.directory?("#{TAC::CONFIGS_PATH}/#{old_name}.json")
# move_config: Can not move config file "#{old_name}" does not exist!
return false
end
if File.exists?("#{TAC::CONFIGS_PATH}/#{new_name}.json") &&
if File.exist?("#{TAC::CONFIGS_PATH}/#{new_name}.json") &&
!File.directory?("#{TAC::CONFIGS_PATH}/#{old_name}.json")
# move_config: Config file "#{new_name}" already exist!
return false
@@ -61,7 +63,7 @@ module TAC
end
def delete_config(config_name)
FileUtils.rm("#{TAC::CONFIGS_PATH}/#{config_name}.json") if File.exists?("#{TAC::CONFIGS_PATH}/#{config_name}.json")
FileUtils.rm("#{TAC::CONFIGS_PATH}/#{config_name}.json") if File.exist?("#{TAC::CONFIGS_PATH}/#{config_name}.json")
end
@@ -101,7 +103,7 @@ module TAC
end
def refresh_tacnet_status
$window.current_state.refresh_tacnet_status
CyberarmEngine::Window.instance.current_state.refresh_tacnet_status
end
@@ -113,6 +115,12 @@ module TAC
@settings_changed
end
def create_directories
FileUtils.mkdir_p(TAC::ROOT_PATH) unless File.exist?(TAC::ROOT_PATH)
FileUtils.mkdir_p(TAC::CONFIGS_PATH) unless File.exist?(TAC::CONFIGS_PATH)
# FileUtils.mkdir_p(TAC::SETTINGS_PATH) unless File.exist?(TAC::SETTINGS_PATH)
end
def load_settings
if File.exist?(TAC::SETTINGS_PATH)
@settings = TAC::Settings.new

View File

@@ -5,51 +5,32 @@ module TAC
background Gosu::Color.new(0xaa_000000)
@title = @options[:title] ? @options[:title] : "#{self.class}"
@window_width, @window_height = window.width, window.height
@previous_state = window.previous_state
@dialog_root = stack width: 0.25, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY] do
@dialog_root = stack(width: 0.25, h_align: :center, v_align: :center, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]) do
# Title bar
@titlebar = flow width: 1.0 do
@titlebar = flow(width: 1.0, height: 36) do
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
# title
flow width: 0.9 do
label @title, text_size: THEME_SUBHEADING_TEXT_SIZE, width: 1.0, text_align: :center, text_border: true, text_border_color: 0xff_222222, text_border_size: 1
end
para @title, text_size: THEME_SUBHEADING_TEXT_SIZE, font: TAC::THEME_BOLD_FONT, fill: true, text_align: :center, text_border: true, text_border_color: 0xaa_222222, text_border_size: 1
# Buttons
flow width: 0.0999 do
button get_image("#{TAC::ROOT_PATH}/media/icons/cross.png"), image_width: 1.0, **THEME_DANGER_BUTTON do
close
end
button get_image("#{TAC::MEDIA_PATH}/icons/cross.png"), image_height: 1.0, **THEME_DANGER_BUTTON do
close
end
end
# Dialog body
@dialog_content = stack width: 1.0 do
@dialog_content = stack(width: 1.0, scroll: true) do
end
end
@dialog_content.clear do
build
end
@root_container.recalculate
@root_container.recalculate
@root_container.recalculate
center_dialog
end
def build
end
def center_dialog
@dialog_root.style.x = window.width / 2 - @dialog_root.width / 2
@dialog_root.style.y = window.height / 2 - @dialog_root.height / 2
end
def name_filter(text)
text.match(/[A-Za-z0-9._\- ]/) ? text : ""
end
@@ -76,11 +57,12 @@ module TAC
def _deep_dive_interactive_elements(element, list)
element.children.each do |child|
if child.visible? && child.is_a?(CyberarmEngine::Element::EditLine) ||
child.is_a?(CyberarmEngine::Element::EditBox) ||
child.is_a?(CyberarmEngine::Element::CheckBox) ||
child.is_a?(CyberarmEngine::Element::ToggleButton) ||
child.is_a?(CyberarmEngine::Element::ListBox)
if child.visible? && child.enabled? &&
child.is_a?(CyberarmEngine::Element::EditLine) ||
child.is_a?(CyberarmEngine::Element::EditBox) ||
child.is_a?(CyberarmEngine::Element::CheckBox) ||
child.is_a?(CyberarmEngine::Element::ToggleButton) ||
child.is_a?(CyberarmEngine::Element::ListBox)
list << child
elsif child.visible? && child.is_a?(CyberarmEngine::Element::Container)
@@ -90,24 +72,12 @@ module TAC
end
def draw
@previous_state.draw
previous_state&.draw
Gosu.flush
super
end
def update
super
if window.width != @window_width or window.height != @window_height
request_recalculate
@window_width, @window_height = window.width, window.height
end
center_dialog
end
def button_down(id)
super
@@ -122,7 +92,7 @@ module TAC
end
def close
$window.pop_state
pop_state
end
end
end
end

View File

@@ -4,15 +4,15 @@ module TAC
def build
background Gosu::Color::GRAY
label "Name", width: 1.0, text_align: :center
@name_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
para "Name", width: 1.0, text_align: :center
@name_error = para "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
@name_error.hide
@name = edit_line @options[:action] ? @options[:action].name : "", filter: method(:name_filter), width: 1.0, autofocus: true
@name = edit_line @options[:action] ? @options[:action].name : "", filter: method(:name_filter), width: 1.0, autofocus: true, focus: true
@name.subscribe(:changed) do |sender, value|
valid?
end
label "Comment", width: 1.0, text_align: :center
para "Comment", width: 1.0, text_align: :center
@comment = edit_line @options[:action] ? @options[:action].comment : "", width: 1.0
flow width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do

View File

@@ -3,7 +3,7 @@ module TAC
class AlertDialog < Dialog
def build
background Gosu::Color::GRAY
label @options[:message]
para @options[:message]
button "Close", width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
try_commit

View File

@@ -11,7 +11,7 @@ module TAC
@titlebar.style.default[:background] = [ color, darken(color, 50) ]
background Gosu::Color::GRAY
label @options[:message]
para @options[:message]
flow width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
button "Cancel", width: 0.5 do
@@ -22,17 +22,17 @@ module TAC
try_commit(true)
end
end
end
def try_commit(force = false)
if !@dangerous
close
def try_commit(force = false)
if !@dangerous
close
@options[:callback_method].call
elsif @dangerous && force
close
@options[:callback_method].call
elsif @dangerous && force
close
@options[:callback_method].call
end
@options[:callback_method].call
end
end
end

View File

@@ -5,10 +5,10 @@ module TAC
def build
background Gosu::Color::GRAY
label "Name", width: 1.0, text_align: :center
@name_error = label "", color: TAC::Palette::TACNET_CONNECTION_ERROR
para "Name", width: 1.0, text_align: :center
@name_error = para "", color: TAC::Palette::TACNET_CONNECTION_ERROR
@name_error.hide
@name = edit_line @options[:renaming] ? @options[:renaming].name : "", filter: method(:name_filter), width: 1.0, autofocus: true
@name = edit_line @options[:renaming] ? @options[:renaming].name : "", filter: method(:name_filter), width: 1.0, autofocus: true, focus: true
@name.subscribe(:changed) do |sender, value|
valid?

View File

@@ -6,9 +6,9 @@ module TAC
@titlebar.style.default[:background] = [ Palette::TACNET_PRIMARY, Palette::TACNET_SECONDARY ]
background Gosu::Color::GRAY
label @options[:message], width: 1.0
para @options[:message], width: 1.0
@sound = Gosu::Sample.new("#{TAC::ROOT_PATH}/media/error_alarm.ogg").play(1, 1, true)
@sound = Gosu::Sample.new("#{TAC::MEDIA_PATH}/error_alarm.ogg").play(1, 1, true)
button "Close", width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
try_commit

View File

@@ -3,14 +3,14 @@ module TAC
class TACNETStatusDialog < Dialog
def build
background Gosu::Color::GRAY
@message_label = label $window.backend.tacnet.full_status
@message_label = para CyberarmEngine::Window.instance.backend.tacnet.full_status
button "Close", width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
try_commit
end
@timer = CyberarmEngine::Timer.new(1000.0) do
@message_label.value = $window.backend.tacnet.full_status
@message_label.value = CyberarmEngine::Window.instance.backend.tacnet.full_status
end
end

View File

@@ -6,16 +6,16 @@ module TAC
@type = @options[:variable].type if @options[:variable]
label "Name", width: 1.0, text_align: :center
@name_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
para "Name", width: 1.0, text_align: :center
@name_error = para "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
@name_error.hide
@name = edit_line @options[:variable] ? @options[:variable].name : "", filter: method(:name_filter), width: 1.0, autofocus: true
@name = edit_line @options[:variable] ? @options[:variable].name : "", filter: method(:name_filter), width: 1.0, autofocus: true, focus: true
@name.subscribe(:changed) do |sender, value|
valid?
end
label "Type", width: 1.0, text_align: :center
@type_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
para "Type", width: 1.0, text_align: :center
@type_error = para "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
@type_error.hide
@var_type = list_box items: [:float, :double, :integer, :long, :string, :boolean], choose: @type ? @type : :double, width: 1.0 do |item|
@@ -35,8 +35,8 @@ module TAC
@type ||= @var_type.value.to_sym
@value_container = stack width: 1.0 do
label "Value", width: 1.0, text_align: :center
@value_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
para "Value", width: 1.0, text_align: :center
@value_error = para "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
@value_error.hide
@value = edit_line @options[:variable] ? @options[:variable].value : "", width: 1.0
@value_boolean = check_box "Boolean", checked: @options[:variable] ? @options[:variable].value == "true" : false
@@ -79,11 +79,22 @@ module TAC
def valid?
valid = true
name = @name.value.strip
if @name.value.strip.empty?
if name.empty?
@name_error.value = "Error: Name cannot be blank or only whitespace."
@name_error.show
valid = false
### Don't error if renaming variable to itself
elsif @options[:variable] && @options[:variable].name == name
@name_error.value = ""
@name_error.hide
elsif @options[:list].find { |variable| variable.name == name }
@name_error.value = "Error: Name is not unique!"
@name_error.show
valid = false
end
if not @type

View File

@@ -8,7 +8,7 @@ module TAC
def initialize
@title = CyberarmEngine::Text.new("FIRST TECH CHALLENGE", size: TITLE_SIZE, text_shadow: true, y: 10, color: Gosu::Color::GRAY)
@title.x = $window.width / 2 - @title.width / 2
@title.x = CyberarmEngine::Window.instance.width / 2 - @title.width / 2
@text = CyberarmEngine::Text.new(":1234567890", size: CLOCK_SIZE, text_border: true, border_size: 2, border_color: Gosu::Color::GRAY)
@text.width # trigger font-eager loading
@@ -28,7 +28,7 @@ module TAC
end
def update
@title.x = $window.width / 2 - @title.width / 2
@title.x = CyberarmEngine::Window.instance.width / 2 - @title.width / 2
if @controller
@text.color = @controller.display_color
@@ -38,8 +38,8 @@ module TAC
@text.text = "0:00"
end
@text.x = $window.width / 2 - @text.textobject.text_width("0:00") / 2
@text.y = $window.height / 2 - @text.height / 2
@text.x = CyberarmEngine::Window.instance.width / 2 - @text.textobject.text_width("0:00") / 2
@text.y = CyberarmEngine::Window.instance.height / 2 - @text.height / 2
@controller&.update
end
@@ -57,13 +57,12 @@ module TAC
end
def clock_time(time_left)
minutes = ((time_left + 0.5) / 60.0).floor
minutes = ((time_left + 1.0) / 60.0).floor
seconds = time_left.round % 60
seconds = "0#{seconds}" if seconds < 10
seconds = format("%02d", time_left.ceil % 60)
return "#{minutes}:#{seconds}" if time_left.round.even?
return "#{minutes}<c=999999>:</c>#{seconds}" if time_left.round.odd?
return "#{minutes}:#{seconds}" if time_left.ceil.even?
return "#{minutes}<c=999999>:</c>#{seconds}" if time_left.ceil.odd?
end
end
end

View File

@@ -33,8 +33,8 @@ module TAC
create_event(:change_countdown, 33.0, "0:08"),
create_event(:change_display, 33.0, :countdown),
create_event(:start_countdown, 33.0),
create_event(:play_sound, 34.5, :teleop_pickup_controllers),
create_event(:change_color, 37.0, :red),
create_event(:play_sound, 35.0, :teleop_pickup_controllers),
create_event(:change_color, 38.0, :red),
create_event(:play_sound, 38.0, :teleop_countdown),
create_event(:stop_countdown, 41.0),
].freeze
@@ -44,7 +44,7 @@ module TAC
create_event(:change_clock, 131.0, "0:30"),
create_event(:start_clock, 131.0),
create_event(:play_sound, 131.0, :end_game),
create_event(:play_sound, 158.0, :autonomous_countdown),
# create_event(:play_sound, 158.0, :autonomous_countdown), # Not played here anymore
create_event(:play_sound, 161.0, :end_match),
create_event(:stop_clock, 161.0),
].freeze
@@ -128,7 +128,7 @@ module TAC
elsif @countdown_running
return @countdown_time
else
return 60 * 2 + 30
return 0 # Clock defaults to showing "0:00" if there is no clock or countdown
end
end
end

View File

@@ -20,7 +20,7 @@ module TAC
end
def start_clock(mode)
return if @clock.active? || $window.current_state.is_a?(Randomizer)
return if @clock.active? || CyberarmEngine::Window.instance.current_state.is_a?(Randomizer)
@clock.controller = case mode
when :full_match
@@ -45,7 +45,7 @@ module TAC
def set_clock_title(string)
@clock.title.text = string.to_s
@clock.title.x = $window.width / 2 - @clock.title.width / 2
@clock.title.x = CyberarmEngine::Window.instance.width / 2 - @clock.title.width / 2
end
def get_clock_title(string)

View File

@@ -41,6 +41,12 @@ module TAC
Gosu::Color.rgb(150, 0, 0)
end
### --- ###
# OVERRIDE: offical CenterStage game clock no longer has colors
### --- ###
out = Gosu::Color::WHITE
@display_color = out
end
@@ -52,30 +58,30 @@ module TAC
return minutes + seconds
end
def play_sound(sound)
def play_sound(sound)
path = nil
case sound
when :autonomous_countdown
path = "media/sounds/3-2-1.wav"
path = "sounds/3-2-1.wav"
when :autonomous_start
path = "media/sounds/charge.wav"
path = "sounds/charge.wav"
when :autonomous_ended
path = "media/sounds/endauto.wav"
path = "sounds/endauto.wav"
when :teleop_pickup_controllers
path = "media/sounds/Pick_Up_Controllers.wav"
path = "sounds/Pick_Up_Controllers.wav"
when :abort_match
path = "media/sounds/fogblast.wav"
path = "sounds/fogblast.wav"
when :teleop_countdown
path = "media/sounds/3-2-1.wav"
path = "sounds/3-2-1.wav"
when :teleop_started
path = "media/sounds/firebell.wav"
path = "sounds/firebell.wav"
when :end_game
path = "media/sounds/factwhistle.wav"
path = "sounds/factwhistle.wav"
when :end_match
path = "media/sounds/endmatch.wav"
path = "sounds/endmatch.wav"
end
path = "#{ROOT_PATH}/#{path}"
path = "#{MEDIA_PATH}/#{path}"
if path && File.exist?(path) && !File.directory?(path)
Jukebox::SAMPLES[path] = Gosu::Sample.new(path) unless Jukebox::SAMPLES[path].is_a?(Gosu::Sample)

View File

@@ -1,11 +1,11 @@
module TAC
class PracticeGameClock
class Jukebox
MUSIC = Dir.glob(ROOT_PATH + "/media/music/*.*").freeze
MUSIC = Dir.glob(MEDIA_PATH + "/music/*.*").freeze
SAMPLES = {}
if File.exist?(ROOT_PATH + "/media/sounds/skystone")
BEEPS_AND_BOOPS = Dir.glob(ROOT_PATH + "/media/sounds/skystone/*.*").freeze
if File.exist?(MEDIA_PATH + "/sounds/skystone")
BEEPS_AND_BOOPS = Dir.glob(MEDIA_PATH + "/sounds/skystone/*.*").freeze
end
attr_reader :volume, :now_playing

View File

@@ -129,7 +129,7 @@ module TAC
end
return message.strip
return message.to_s.strip
end
def puts(message)

View File

@@ -225,7 +225,7 @@ module TAC
def handle_shutdown(packet)
unless @host_is_a_connection
# RemoteControl.server.close
# $window.close
# CyberarmEngine::Window.instance.close
Gosu::Song.current_song&.stop
exit
end

View File

@@ -8,7 +8,7 @@ module TAC
@z = -2
@particles = []
@image_options = Dir.glob("#{ROOT_PATH}/media/particles/*.*")
@image_options = Dir.glob("#{MEDIA_PATH}/particles/*.*")
@last_spawned = 0
@clock_active = false
end
@@ -18,7 +18,7 @@ module TAC
end
def update
@particles.each { |part| part.update($window.dt) }
@particles.each { |part| part.update(CyberarmEngine::Window.instance.dt) }
@particles.delete_if { |part| part.die? }
spawn_particles
@@ -27,28 +27,28 @@ module TAC
def spawn_particles
# !clock_active? &&
if @particles.count < @max_particles && Gosu.milliseconds - @last_spawned >= @interval
screen_midpoint = CyberarmEngine::Vector.new($window.width / 2, $window.height / 2)
screen_midpoint = CyberarmEngine::Vector.new(CyberarmEngine::Window.instance.width / 2, CyberarmEngine::Window.instance.height / 2)
scale = rand(0.25..1.0)
image_name = @image_options.sample
return unless image_name
image = $window.current_state.get_image(image_name)
image = CyberarmEngine::Window.instance.current_state.get_image(image_name)
position = CyberarmEngine::Vector.new(0, 0)
r = rand
if r < 0.25 # LEFT
position.x = -image.width * scale
position.y = rand(0..($window.height - image.height * scale))
position.y = rand(0..(CyberarmEngine::Window.instance.height - image.height * scale))
elsif r < 0.5 # RIGHT
position.x = $window.width + (image.width * scale)
position.y = rand(0..($window.height - image.height * scale))
position.x = CyberarmEngine::Window.instance.width + (image.width * scale)
position.y = rand(0..(CyberarmEngine::Window.instance.height - image.height * scale))
elsif r < 0.75 # TOP
position.x = rand(0..($window.width - image.width * scale))
position.x = rand(0..(CyberarmEngine::Window.instance.width - image.width * scale))
position.y = -image.height * scale
else #BOTTOM
position.x = rand(0..($window.width - image.width * scale))
position.y = $window.height + image.height * scale
position.x = rand(0..(CyberarmEngine::Window.instance.width - image.width * scale))
position.y = CyberarmEngine::Window.instance.height + image.height * scale
end
position.x ||= 0
@@ -71,6 +71,10 @@ module TAC
end
end
def particle_count
@particles.size
end
def clock_active!
@clock_active = true
@particles.each(&:clock_active!)

View File

@@ -15,27 +15,25 @@ module TAC
case @roll
when 1, 4
# Blue: Right
# Red: Left
#Blue and Red: Left
@ducks << Ducky.new(window: window, alliance: :blue, slot: 3, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :red, slot: 1, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :blue, slot: 1, speed: 1010, die_size: @size, label: "Left")
@ducks << Ducky.new(window: window, alliance: :red, slot: 1, speed: 1010, die_size: @size, label: "Left")
when 2, 5
#Blue and Red: Center
@ducks << Ducky.new(window: window, alliance: :blue, slot: 2, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :red, slot: 2, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :blue, slot: 2, speed: 1010, die_size: @size, label: "Center")
@ducks << Ducky.new(window: window, alliance: :red, slot: 2, speed: 1010, die_size: @size, label: "Center")
when 3, 6
# Blue: Left
# Red: Right
#Blue and Red: Right
@ducks << Ducky.new(window: window, alliance: :blue, slot: 1, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :red, slot: 3, speed: 512, die_size: @size)
@ducks << Ducky.new(window: window, alliance: :blue, slot: 3, speed: 1010, die_size: @size, label: "Right")
@ducks << Ducky.new(window: window, alliance: :red, slot: 3, speed: 1010, die_size: @size, label: "Right")
end
end
def draw
window.previous_state.draw
previous_state.draw
Gosu.flush
@@ -56,7 +54,7 @@ module TAC
end
def update
window.previous_state&.update_non_gui
previous_state&.update_non_gui
@ducks.each { |o| o.update(@size) }
@@ -113,14 +111,17 @@ module TAC
SIZE = 0.20
HALF_SIZE = SIZE * 0.5
def initialize(window:, alliance:, slot:, speed:, die_size:)
def initialize(window:, alliance:, slot:, speed:, die_size:, label:)
@window = window
@alliance = alliance
@slot = slot
@speed = speed
@die_size = die_size
@label = label
@image = @window.get_image("#{ROOT_PATH}/media/openclipart_ducky.png")
@image = @window.get_image("#{MEDIA_PATH}/openclipart_ducky.png")
@debug_text = Gosu::Font.new(28)
@label_text = CyberarmEngine::Text.new(@label, static: true, size: 28, alignment: :center)
if @alliance == :blue
@position = CyberarmEngine::Vector.new(@window.width, die_size)
@@ -138,6 +139,10 @@ module TAC
duck_scale = (size * (SIZE + HALF_SIZE)) / @image.width
duck_scale_x = @alliance == :blue ? -duck_scale : duck_scale
@image.draw_rot(slot_position(size), size * SIZE + float_y(size), 1, 0, 0.5, 0.5, duck_scale_x, duck_scale)
@label_text.x = slot_position(size) + (@alliance == :blue ? -170 : 110)
@label_text.y = float_y(size) + 52
@label_text.draw
end
end

View File

@@ -80,7 +80,10 @@ module TAC
@jukebox_volume = 1.0
@jukebox_sound_effects = true
@locked_buttons_randomizer_visible = []
@locked_buttons_clock_active = []
@randomizer_visible = false
@clock_updated_at = -1000
RemoteControl.connection.proxy_object.register(:track_changed, method(:track_changed))
RemoteControl.connection.proxy_object.register(:volume_changed, method(:volume_changed))
@@ -94,25 +97,25 @@ module TAC
flow width: 1.0, height: 1.0 do
stack width: 0.5 do
title "Match", width: 1.0, text_align: :center
button "Start Match", width: 1.0, text_size: 48, margin_bottom: 50 do
@start_match_btn = button "Start Match", width: 1.0, text_size: 48, margin_bottom: 50 do
start_clock(:full_match)
end
title "Practice", width: 1.0, text_align: :center
button "Autonomous", width: 1.0 do
@autonomous_btn = button "Autonomous", width: 1.0 do
start_clock(:autonomous)
end
button "TeleOp with Countdown", width: 1.0 do
@teleop_with_countdown_btn = button "TeleOp with Countdown", width: 1.0 do
start_clock(:full_teleop)
end
button "TeleOp", width: 1.0 do
@teleop_btn = button "TeleOp", width: 1.0 do
start_clock(:teleop_only)
end
button "TeleOp Endgame", width: 1.0, margin_bottom: 50 do
@teleop_endgame_btn = button "TeleOp Endgame", width: 1.0, margin_bottom: 50 do
start_clock(:endgame_only)
end
button "Abort Match", width: 1.0 do
@abort_match_btn = button "Abort Match", width: 1.0 do
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_abort_clock)
end
@@ -146,48 +149,48 @@ module TAC
end
flow width: 1.0 do
button get_image("#{ROOT_PATH}/media/icons/previous.png") do
button get_image("#{MEDIA_PATH}/icons/previous.png") do
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_previous_track)
end
button get_image("#{ROOT_PATH}/media/icons/right.png") do |button|
button get_image("#{MEDIA_PATH}/icons/right.png") do |button|
if @jukebox_playing
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_pause)
button.value = get_image("#{ROOT_PATH}/media/icons/right.png")
button.value = get_image("#{MEDIA_PATH}/icons/right.png")
@jukebox_playing = false
else
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_play)
button.value = get_image("#{ROOT_PATH}/media/icons/pause.png")
button.value = get_image("#{MEDIA_PATH}/icons/pause.png")
@jukebox_playing = true
end
end
button get_image("#{ROOT_PATH}/media/icons/stop.png") do
button get_image("#{MEDIA_PATH}/icons/stop.png") do
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_stop)
end
button get_image("#{ROOT_PATH}/media/icons/next.png") do
button get_image("#{MEDIA_PATH}/icons/next.png") do
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_next_track)
end
button get_image("#{ROOT_PATH}/media/icons/minus.png"), margin_left: 20 do
button get_image("#{MEDIA_PATH}/icons/minus.png"), margin_left: 20 do
@jukebox_volume -= 0.1
@jukebox_volume = 0.1 if @jukebox_volume < 0.1
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_set_volume(@jukebox_volume))
end
button get_image("#{ROOT_PATH}/media/icons/plus.png") do
button get_image("#{MEDIA_PATH}/icons/plus.png") do
@jukebox_volume += 0.1
@jukebox_volume = 1.0 if @jukebox_volume > 1.0
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_jukebox_set_volume(@jukebox_volume))
end
button get_image("#{ROOT_PATH}/media/icons/musicOn.png"), margin_left: 20, tip: "Toggle Sound Effects" do |button|
button get_image("#{MEDIA_PATH}/icons/musicOn.png"), margin_left: 20, tip: "Toggle Sound Effects" do |button|
if @jukebox_sound_effects
button.value = get_image("#{ROOT_PATH}/media/icons/musicOff.png")
button.value = get_image("#{MEDIA_PATH}/icons/musicOff.png")
@jukebox_sound_effects = false
else
button.value = get_image("#{ROOT_PATH}/media/icons/musicOn.png")
button.value = get_image("#{MEDIA_PATH}/icons/musicOn.png")
@jukebox_sound_effects = true
end
@@ -196,12 +199,12 @@ module TAC
end
button "Open Music Library", width: 1.0 do
path = "#{ROOT_PATH}/media/music"
path = "#{MEDIA_PATH}/music"
if RUBY_PLATFORM.match(/ming|msys|cygwin/)
system("explorer \"#{path.gsub("/", "\\")}\"")
elsif RUBY_PLATFORM.match(/linux/)
system("xdg-open \"#{ROOT_PATH}/media/music\"")
system("xdg-open \"#{MEDIA_PATH}/music\"")
else
# TODO.
end
@@ -219,7 +222,7 @@ module TAC
@randomizer_label = title "Not Visible"
end
button "Randomizer", width: 1.0, **TAC::THEME_DANGER_BUTTON do
@randomizer_btn = button "Randomizer", width: 1.0, **TAC::THEME_DANGER_BUTTON do
@randomizer_visible = !@randomizer_visible
RemoteControl.connection.puts(ClockNet::PacketHandler.packet_randomizer_visible(@randomizer_visible))
@@ -227,6 +230,24 @@ module TAC
end
end
end
@locked_buttons_clock_active.push(
@start_match_btn,
@autonomous_btn,
@teleop_with_countdown_btn,
@teleop_btn,
@teleop_endgame_btn,
@randomizer_btn
)
@locked_buttons_randomizer_visible.push(
@start_match_btn,
@autonomous_btn,
@teleop_with_countdown_btn,
@teleop_btn,
@teleop_endgame_btn,
@abort_match_btn
)
end
def update
@@ -236,6 +257,8 @@ module TAC
o.call
end
manage_button_enablement
return if RemoteControl.connection.connected?
# We've lost connection, unset window's connection object
@@ -258,6 +281,8 @@ module TAC
end
def clock_changed(string)
@clock_updated_at = Gosu.milliseconds if @clock_label.value != string
@clock_label.value = string
end
@@ -265,6 +290,22 @@ module TAC
@randomizer_label.value = "Visible" if boolean
@randomizer_label.value = "Not Visible" unless boolean
end
def manage_button_enablement
if @randomizer_visible
@locked_buttons_randomizer_visible.each do |btn|
btn.enabled = false
end
elsif Gosu.milliseconds - @clock_updated_at <= 1_250
@locked_buttons_clock_active.each do |btn|
btn.enabled = false
end
else
(@locked_buttons_clock_active + @locked_buttons_randomizer_visible).uniq.each do |btn|
btn.enabled = true
end
end
end
end
end
end

View File

@@ -2,7 +2,7 @@ module TAC
class PracticeGameClock
THEME = {
TextBlock: {
font: "Canterell",
font: "NotoSans-Bold",
color: Gosu::Color.new(0xee_ffffff)
},
Button: {
@@ -23,7 +23,7 @@ module TAC
},
ToggleButton: {
width: 18,
checkmark_image: "#{File.expand_path("..", __dir__)}/media/icons/checkmark.png",
checkmark_image: "#{MEDIA_PATH}/icons/checkmark.png",
}
}
end

View File

@@ -9,14 +9,15 @@ module TAC
window.show_cursor = !@remote_control_mode
@escape_counter = 0
@background_image = get_image("#{ROOT_PATH}/media/background.png")
# Preload duck image since Gosu and windows threads don't get along with OpenGL (image is blank if loaded in a threaded context)
get_image("#{ROOT_PATH}/media/openclipart_ducky.png")
@background_image = get_image("#{MEDIA_PATH}/background.png")
# Preload duck image since Gosu and Windows threads don't get along with OpenGL (image is blank if loaded in a threaded context)
get_image("#{MEDIA_PATH}/openclipart_ducky.png")
@menu_background = 0xaa004000
@mouse = Mouse.new(window)
@clock = Clock.new
@clock.controller = nil
@last_clock_display_value = @clock.value
@last_clock_title_value = @clock.title.text
@particle_emitters = [
PracticeGameClock::ParticleEmitter.new
@@ -24,6 +25,10 @@ module TAC
@last_clock_state = @clock.active?
@locked_buttons_randomizer_visible = []
@locked_buttons_clock_active = []
@randomizer_visible = false
theme(THEME)
@menu_container = flow width: 1.0 do
@@ -31,28 +36,45 @@ module TAC
background @menu_background
title "Match", width: 1.0, text_align: :center
button "Start Match", width: 1.0, margin_bottom: 50 do
@start_match_btn = button "Start Match", width: 1.0, margin_bottom: 50 do |btn|
@locked_buttons_clock_active << btn
@locked_buttons_randomizer_visible << btn
@clock_proxy.start_clock(:full_match)
end
title "Practice", width: 1.0, text_align: :center
button "Autonomous", width: 1.0 do
@autonomous_btn = button "Autonomous", width: 1.0 do |btn|
@locked_buttons_clock_active << btn
@locked_buttons_randomizer_visible << btn
@clock_proxy.start_clock(:autonomous)
end
button "TeleOp with Countdown", width: 1.0 do
@teleop_with_countdown_btn = button "TeleOp with Countdown", width: 1.0 do |btn|
@locked_buttons_clock_active << btn
@locked_buttons_randomizer_visible << btn
@clock_proxy.start_clock(:full_teleop)
end
button "TeleOp", width: 1.0 do
@teleop_btn = button "TeleOp", width: 1.0 do |btn|
@locked_buttons_clock_active << btn
@locked_buttons_randomizer_visible << btn
@clock_proxy.start_clock(:teleop_only)
end
button "TeleOp Endgame", width: 1.0 do
@teleop_endgame_btn = button "TeleOp Endgame", width: 1.0 do |btn|
@locked_buttons_clock_active << btn
@locked_buttons_randomizer_visible << btn
@clock_proxy.start_clock(:endgame_only)
end
button "Abort Match", width: 1.0, margin_top: 50 do
@abort_match_btn = button "Abort Match", width: 1.0, margin_top: 50 do |btn|
@locked_buttons_randomizer_visible << btn
@clock_proxy.abort_clock
end
@@ -72,73 +94,73 @@ module TAC
stack width: 0.4, padding_left: 50 do
background @menu_background
flow do
label "♫ Now playing:"
@current_song_label = label "♫ ♫ ♫"
flow(width: 1.0) do
para "♫ Now playing:"
@current_song_label = para "♫ ♫ ♫"
end
flow do
label "Volume:"
@current_volume_label = label "100%"
flow(width: 1.0) do
para "Volume:"
@current_volume_label = para "100%"
end
flow do
button get_image("#{ROOT_PATH}/media/icons/previous.png") do
flow(width: 1.0) do
button get_image("#{MEDIA_PATH}/icons/previous.png") do
@jukebox.previous_track
end
button get_image("#{ROOT_PATH}/media/icons/pause.png") do |button|
button get_image("#{MEDIA_PATH}/icons/pause.png") do |button|
if @jukebox.song && @jukebox.song.paused?
button.value = get_image("#{ROOT_PATH}/media/icons/right.png")
button.value = get_image("#{MEDIA_PATH}/icons/right.png")
@jukebox.play
elsif !@jukebox.song
button.value = get_image("#{ROOT_PATH}/media/icons/right.png")
button.value = get_image("#{MEDIA_PATH}/icons/right.png")
@jukebox.play
else
button.value = get_image("#{ROOT_PATH}/media/icons/pause.png")
button.value = get_image("#{MEDIA_PATH}/icons/pause.png")
@jukebox.pause
end
end
button get_image("#{ROOT_PATH}/media/icons/stop.png") do
button get_image("#{MEDIA_PATH}/icons/stop.png") do
@jukebox.stop
end
button get_image("#{ROOT_PATH}/media/icons/next.png") do
button get_image("#{MEDIA_PATH}/icons/next.png") do
@jukebox.next_track
end
button get_image("#{ROOT_PATH}/media/icons/minus.png"), margin_left: 20 do
button get_image("#{MEDIA_PATH}/icons/minus.png"), margin_left: 20 do
@jukebox.set_volume(@jukebox.volume - 0.1)
end
button get_image("#{ROOT_PATH}/media/icons/plus.png") do
button get_image("#{MEDIA_PATH}/icons/plus.png") do
@jukebox.set_volume(@jukebox.volume + 0.1)
end
button "Open Music Library", margin_left: 50 do
if RUBY_PLATFORM.match(/ming|msys|cygwin/)
system("explorer #{ROOT_PATH}/media/music")
system("explorer #{MEDIA_PATH}/music")
elsif RUBY_PLATFORM.match(/linux/)
system("xdg-open #{ROOT_PATH}/media/music")
system("xdg-open #{MEDIA_PATH}/music")
else
# TODO.
end
end
button get_image("#{ROOT_PATH}/media/icons/musicOn.png"), margin_left: 50, tip: "Toggle Sound Effects" do |button|
button get_image("#{MEDIA_PATH}/icons/musicOn.png"), margin_left: 50, tip: "Toggle Sound Effects" do |button|
boolean = @jukebox.set_sfx(!@jukebox.play_sfx?)
if boolean
button.value = get_image("#{ROOT_PATH}/media/icons/musicOn.png")
button.value = get_image("#{MEDIA_PATH}/icons/musicOn.png")
else
button.value = get_image("#{ROOT_PATH}/media/icons/musicOff.png")
button.value = get_image("#{MEDIA_PATH}/icons/musicOff.png")
end
end
end
stack width: 1.0 do
button "Randomizer", width: 1.0, **TAC::THEME_DANGER_BUTTON do
stack(width: 1.0) do
@randomizer_btn = button "Randomizer", width: 1.0, **TAC::THEME_DANGER_BUTTON do |btn|
unless @clock.active?
push_state(Randomizer)
end
@@ -147,6 +169,24 @@ module TAC
end
end
@locked_buttons_clock_active.push(
@start_match_btn,
@autonomous_btn,
@teleop_with_countdown_btn,
@teleop_btn,
@teleop_endgame_btn,
@randomizer_btn
)
@locked_buttons_randomizer_visible.push(
@start_match_btn,
@autonomous_btn,
@teleop_with_countdown_btn,
@teleop_btn,
@teleop_endgame_btn,
@abort_match_btn
)
@jukebox = Jukebox.new(@clock)
@clock_proxy = ClockProxy.new(@clock, @jukebox)
@@ -190,16 +230,26 @@ module TAC
@menu_container.hide if @menu_container.visible?
window.show_cursor = false
end
manage_button_enablement
end
if @clock.value != @last_clock_display_value
@last_clock_display_value = @clock.value
request_repaint
if @remote_control_mode && @server.active_client
@server.active_client.puts(ClockNet::PacketHandler.packet_clock_time(@last_clock_display_value))
end
end
if @clock.title.text != @last_clock_title_value
@last_clock_title_value = @clock.title.text
request_repaint
end
if @last_track_name != @jukebox.current_track
track_changed(@jukebox.current_track)
end
@@ -213,6 +263,14 @@ module TAC
@last_clock_state = @clock.active?
end
def needs_repaint?
if @particle_emitters && @particle_emitters.map(&:particle_count).sum.positive?
@needs_repaint = true
else
super
end
end
def update_non_gui
if @remote_control_mode
while (o = RemoteControl.server.proxy_object.queue.shift)
@@ -224,6 +282,22 @@ module TAC
@jukebox.update
end
def manage_button_enablement
if @randomizer_visible
@locked_buttons_randomizer_visible.each do |btn|
btn.enabled = false
end
elsif @clock.active?
@locked_buttons_clock_active.each do |btn|
btn.enabled = false
end
else
(@locked_buttons_clock_active + @locked_buttons_randomizer_visible).uniq.each do |btn|
btn.enabled = true
end
end
end
def button_down(id)
super
@@ -257,6 +331,8 @@ module TAC
end
def randomizer_changed(boolean)
@randomizer_visible = boolean
if boolean
push_state(Randomizer) unless @clock.active?
else

View File

@@ -5,7 +5,7 @@ module TAC
header_bar("Manage Configurations")
menu_bar.clear do
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_height: 1.0, tip: "Add configuration" do
button get_image("#{TAC::MEDIA_PATH}/icons/plus.png"), image_height: 1.0, tip: "Add configuration" do
push_state(Dialog::NamePromptDialog, title: "Config Name", callback_method: proc { |name|
window.backend.write_new_config(name)
@@ -13,15 +13,27 @@ module TAC
populate_configs
})
end
button "Open Folder", tip: "Open folder containing configurations", height: 1.0 do
if RUBY_PLATFORM =~ /mingw/
system("explorer \"#{TAC::CONFIGS_PATH.gsub("/", "\\")}\"")
elsif RUBY_PLATFORM =~ /darwin/
system("open \"#{TAC::CONFIGS_PATH}\"")
else
system("xdg-open \"#{TAC::CONFIGS_PATH}\"")
end
end
end
status_bar.clear do
label "Current Configuration: "
@config_label = label window.backend.settings.config
flow(width: 1.0, max_width: 720, h_align: :center) do
para "Current Configuration: "
@config_label = para window.backend.settings.config
end
end
body.clear do
@configs_list = stack width: 1.0, height: 1.0, scroll: true do
@configs_list = stack width: 1.0, height: 1.0, margin_top: 36, max_width: 720, h_align: :center, scroll: true do
end
end
@@ -33,13 +45,14 @@ module TAC
@config_files_list = @config_files.map { |file| Dialog::NamePromptDialog::NameStub.new(File.basename(file, ".json")) }
@configs_list.clear do
@config_files.each_with_index do |config_file, i|
flow width: 1.0, **THEME_ITEM_CONTAINER_PADDING do
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
@config_files.sort_by { |f| [f.downcase] }.each_with_index do |config_file, i|
flow width: 1.0, height: 36, **THEME_ITEM_CONTAINER_PADDING do
name = File.basename(config_file, ".json")
button "#{name}", width: 0.94 do
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR unless name == window.backend.settings.config
background THEME_HIGHLIGHTED_COLOR if name == window.backend.settings.config
button "#{name}", fill: true, text_size: THEME_ICON_SIZE - 3 do
change_config(name)
if window.backend.tacnet.connected?
@@ -47,7 +60,7 @@ module TAC
end
end
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Rename configuration" do
button get_image("#{TAC::MEDIA_PATH}/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Rename configuration" do
push_state(Dialog::NamePromptDialog, title: "Rename Config", renaming: @config_files_list.find { |c| c.name == name }, list: @config_files_list, accept_label: "Update", callback_method: proc { |old_name, new_name|
if not File.exist?("#{TAC::CONFIGS_PATH}/#{new_name}.json")
FileUtils.mv(
@@ -66,7 +79,7 @@ module TAC
})
end
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, **THEME_DANGER_BUTTON, tip: "Delete configuration" do
button get_image("#{TAC::MEDIA_PATH}/icons/trashcan.png"), image_width: THEME_ICON_SIZE, **THEME_DANGER_BUTTON, tip: "Delete configuration" do
push_state(Dialog::ConfirmDialog, title: "Delete Config?", dangerous: true, callback_method: proc {
File.delete("#{TAC::CONFIGS_PATH}/#{name}.json")
@@ -92,6 +105,8 @@ module TAC
window.backend.load_config(name)
@config_label.value = name.to_s
populate_configs
end
end
end

View File

@@ -7,22 +7,25 @@ module TAC
header_bar("Drive Team Rotation Generator")
@roster ||= [
"Alexander",
"Aubrey",
"Cayden",
"Connor",
"Ben",
"Dan",
"Gabe",
"Spencer",
"Olivia"
"Sodi"
]
@roles ||= [
"Coach",
"Driver A",
"Driver B"
"Driver B",
"Human"
]
menu_bar.clear do
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_height: 1.0, tip: "Export rotation as Comma-Seperated Values" do
button get_image("#{TAC::MEDIA_PATH}/icons/save.png"), image_height: 1.0, tip: "Export rotation as Comma-Seperated Values" do
export_rotation
@status_bar.clear do
@@ -30,19 +33,19 @@ module TAC
end
end
button get_image("#{TAC::ROOT_PATH}/media/icons/target.png"), margin_right: 10, image_height: 1.0, tip: "Generate rotation" do
button "Generate", margin_right: 10, height: 1.0, tip: "Generate rotation" do
populate_rotation
end
end
body.clear do
flow(margin_left: 20, width: 1.0, height: 1.0) do
stack(width: 0.25) do
stack(width: 0.25, height: 1.0) do
title "Roles", width: 1.0, margin_bottom: 4, text_align: :center
flow(width: 1.0, margin_bottom: 20) do
@role_name = edit_line "", placeholder: "Add role", width: 0.9
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: 0.1, tip: "Add role" do
flow(width: 1.0, height: 32, margin_bottom: 20) do
@role_name = edit_line "", placeholder: "Add role", fill: true, height: 1.0
button get_image("#{TAC::MEDIA_PATH}/icons/plus.png"), image_height: 1.0, tip: "Add role" do
if @role_name.value.strip.length.positive?
@roles.push(@role_name.value.strip)
@role_name.value = ""
@@ -52,16 +55,16 @@ module TAC
end
end
@roles_container = stack(width: 1.0, height: 0.835, scroll: true) do
@roles_container = stack(width: 1.0, fill: true, scroll: true) do
end
end
stack(margin_left: 20, width: 0.25, height: 1.0) do
title "Roster", width: 1.0, margin_bottom: 4, text_align: :center
flow(width: 1.0, margin_bottom: 20) do
@roster_name = edit_line "", placeholder: "Add name", width: 0.9
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: 0.1, tip: "Add name" do
flow(width: 1.0, height: 32, margin_bottom: 20) do
@roster_name = edit_line "", placeholder: "Add name", height: 1.0, fill: true
button get_image("#{TAC::MEDIA_PATH}/icons/plus.png"), image_height: 1.0, tip: "Add name" do
if @roster_name.value.strip.length.positive?
@roster.push(@roster_name.value.strip)
@roster_name.value = ""
@@ -71,14 +74,14 @@ module TAC
end
end
@roster_container = stack(width: 1.0, height: 0.835, scroll: true) do
@roster_container = stack(width: 1.0, fill: true, scroll: true) do
end
end
stack(margin_left: 20, margin_right: 20, width: 0.5, height: 1.0) do
stack(margin_left: 20, margin_right: 20, fill: true, height: 1.0) do
title "Rotation", width: 1.0, margin_bottom: 4, text_align: :center
@rotation_container = stack(width: 1.0, height: 0.835, scroll: true) do
@rotation_container = stack(width: 1.0, fill: true, scroll: true) do
end
end
end
@@ -92,11 +95,11 @@ module TAC
def populate_roles
@roles_container.clear do
@roles.each_with_index do |name, i|
flow(width: 1.0, padding: 2) do
flow(width: 1.0, height: 32, padding: 2) do
background i.even? ? 0xff_007000 : 0xff_006000
tagline name, width: 0.9
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: 0.1, tip: "Remove role", **THEME_DANGER_BUTTON do
tagline name, fill: true
button get_image("#{TAC::MEDIA_PATH}/icons/trashcan.png"), image_height: 1.0, tip: "Remove role", **THEME_DANGER_BUTTON do
@roles.delete(name)
populate_roles
end
@@ -108,11 +111,11 @@ module TAC
def populate_roster
@roster_container.clear do
@roster.each_with_index do |name, i|
flow(width: 1.0, padding: 2) do
flow(width: 1.0, height: 32, padding: 2) do
background i.even? ? 0xff_007000 : 0xff_006000
tagline name, width: 0.9
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: 0.1, tip: "Remove name", **THEME_DANGER_BUTTON do
tagline name, fill: true
button get_image("#{TAC::MEDIA_PATH}/icons/trashcan.png"), image_height: 1.0, tip: "Remove name", **THEME_DANGER_BUTTON do
@roster.delete(name)
populate_roster
end
@@ -125,13 +128,11 @@ module TAC
@rotation = Generator.new(roster: @roster, team_size: @roles.count)
@rotation_container.clear do
fraction = (1.0 / @rotation.team_size) - 0.02
flow(width: 1.0, padding: 2) do
flow(width: 1.0, height: 32, padding: 2) do
background Gosu::Color::BLACK
@roles.each do |role|
tagline "<b>#{role}</b>", width: fraction
tagline "<b>#{role}</b>", fill: true
end
end
@@ -139,11 +140,11 @@ module TAC
teams = @rotation.teams.shuffle if @shuffle_teams&.value
teams.each_with_index do |team, i|
flow(width: 1.0, padding: 2) do
flow(width: 1.0, height: 32, padding: 2) do
background i.even? ? 0xff_007000 : 0xff_006000
team.each do |player|
tagline player, width: fraction
tagline player, fill: true
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,15 @@ module TAC
button "Reset", text_size: THEME_HEADING_TEXT_SIZE, **THEME_DANGER_BUTTON do
@nodes.clear
measure_path
refresh_panel
end
list_box items: ["CENTERSTAGE", "Power Play", "Freight Frenzy", "Ultimate Goal", "Skystone"], width: 200, height: 1.0 do |item|
season = item.downcase.gsub(" ", "_").to_sym
@field = TAC::Simulator::Field.new(container: @field_container, season: season, simulation: nil)
end
end
end
@@ -43,7 +50,7 @@ module TAC
tagline "Nodes:"
@nodes_count_label = tagline "0"
tagline "Total Distance:"
tagline "Total Distance:", margin_left: 20
@total_distance_label = tagline "0"
@units_label = tagline "Inches"
@@ -61,7 +68,7 @@ module TAC
end
end
@field = TAC::Simulator::Field.new(container: @field_container, season: :freight_frenzy, simulation: nil)
@field = TAC::Simulator::Field.new(container: @field_container, season: :centerstage, simulation: nil)
@nodes ||= []
@unit = :inches
@total_distance = 0
@@ -72,6 +79,9 @@ module TAC
@node_radius = 6
@segment_thickness = 2
@font = CyberarmEngine::Text.new(font: THEME_BOLD_FONT, size: 18, border: true, static: true)
@last_mouse_position = CyberarmEngine::Vector.new(window.mouse_x, window.mouse_y)
measure_path
refresh_panel
end
@@ -82,11 +92,35 @@ module TAC
@field.draw
display_path
if @field_container.hit?(window.mouse_x, window.mouse_y)
x = (window.mouse_x - @field_container.x) / @field.scale - 72
y = (window.mouse_y - @field_container.y) / @field.scale - 72
@font.text = "X: #{inches_to_unit(x).round(2)} Y: #{inches_to_unit(y).round(2)} (#{@unit.to_s})"
@font.x = window.mouse_x + 6
@font.y = window.mouse_y - (@font.height / 2.0 + 24)
@font.z = 100_001
Gosu.draw_rect(
window.mouse_x,
@font.y - 6,
@font.width + 12,
@font.height + 12,
0xaa_000000,
100_000
)
@font.draw
end
end
def update
super
current_state.request_repaint if window.mouse_x != @last_mouse_position.x || window.mouse_y != @last_mouse_position.y
@last_mouse_position = CyberarmEngine::Vector.new(window.mouse_x, window.mouse_y)
@field.update
measure_path
@@ -133,7 +167,7 @@ module TAC
Gosu.draw_circle(
current_node.x * @field.scale + @field_container.x,
current_node.y * @field.scale + @field_container.y,
@node_radius, 7, mouse_near ? @node_hover_color : @node_color, 10
@node_radius, 7, mouse_near ? @node_hover_color : @node_color, @field_container.z + 1
)
next if i.zero?
@@ -153,7 +187,8 @@ module TAC
(@field_container.y + last_node.y * @field.scale) - distance,
@segment_thickness,
distance,
@segment_color
@segment_color,
@field_container.z + 1
)
end
@@ -184,6 +219,10 @@ module TAC
@total_distance_label.value = "#{inches_to_unit(@total_distance).round(2)}"
@units_label.value = @unit.to_s.capitalize
status_bar.recalculate
status_bar.recalculate
status_bar.recalculate
# @points_container.clear do
# v1 = @nodes.first
# break unless v1
@@ -214,11 +253,11 @@ module TAC
when :feet
inches / 12.0
when :millimeters
inches / 0.254
inches * 25.4
when :centimeters
inches / 2.54
inches * 2.54
when :meters
inches / 25.4
inches * 0.0254
end
end
end

View File

@@ -24,7 +24,7 @@ module TAC
# Spawn game clock window
$clock_pid = Process.spawn(
RbConfig.ruby,
"#{ROOT_PATH}/timecrafters_configuration_tool.rb",
"#{MEDIA_PATH}/../timecrafters_configuration_tool.rb",
"--game-clock-remote-display"
)

View File

@@ -6,37 +6,71 @@ module TAC
body.clear do
stack(width: 1.0, height: 1.0) do
label TAC::NAME, width: 1.0, text_size: 48, text_align: :center
para TAC::NAME, width: 1.0, text_size: 48, text_align: :center
stack(width: 1.0, height: 8) do
background 0xff_006000
end
if window.backend.settings.config.empty?
label "TODO: Introduction"
label "Get Started", text_size: 28
para "TODO: Introduction"
para "Get Started", text_size: 28
button "1. Create a configuration" do
page(TAC::Pages::Configurations)
end
label "2. Add a group"
label "3. Add an action"
label "4. Add a variable"
label "5. Profit?"
para "2. Add a group"
para "3. Add an action"
para "4. Add a variable"
para "5. Profit?"
else
label "Display config stats or something?"
para "Display config stats or something?"
config = window.backend.config
groups = config.groups
actions = config.groups.map { |g| g.actions }.flatten
variables = actions.map { |a| a.variables }.flatten
label "Total groups: #{groups.size}"
label "Total actions: #{actions.size}"
label "Total variables: #{variables.size}"
para "Total groups: #{groups.size}"
para "Total actions: #{actions.size}"
para "Total variables: #{variables.size}"
end
stack(width: 1.0, fill: true, scroll: true, margin_top: 32) do
heading, items = Gosu::LICENSES.split("\n\n")
title heading
items.split("\n").each do |item|
name, website, license, license_website = item.split(",").map(&:strip)
flow(width: 1.0, height: 28) do
tagline "#{name} - "
button "Website", height: 1.0, tip: website do
open_url(website)
end
end
flow(width: 1.0, height: 22, margin_bottom: 20) do
para "#{license} - "
button "License Website", height: 1.0, text_size: 16, tip: license_website do
open_url(license_website)
end
end
end
end
end
end
end
def open_url(url)
case RUBY_PLATFORM
when /mingw/ # windows
system("start #{url}")
when /linux/
system("xdg-open #{url}")
when /darwin/ # macos
system("open #{url}")
end
end
end
end
end

View File

@@ -11,90 +11,205 @@ module TAC
body.clear do
flow(width: 1.0, height: 1.0) do
@group_presets = stack(width: 0.49995, height: 1.0, scroll: true, border_thickness_right: 1, border_color: [0, Gosu::Color::BLACK, 0, 0]) do
@group_presets = stack(fill: true, height: 1.0, scroll: true, padding_left: 2, padding_top: 2, padding_right: 2, border_thickness_right: 1, border_color: Gosu::Color::BLACK) do
end
@action_presets = stack(width: 0.49995, height: 1.0, scroll: true) do
@action_presets = stack(fill: true, height: 1.0, scroll: true, padding_left: 2, padding_top: 2, padding_right: 2) do
end
end
end
@scroll_into_view_list = []
@highlight_item_container = nil
@highlight_from_color = Gosu::Color.rgba(0, 0, 0, 0)
@highlight_to_color = Gosu::Color.rgba(THEME_HIGHLIGHTED_COLOR.red, THEME_HIGHLIGHTED_COLOR.green, THEME_HIGHLIGHTED_COLOR.blue, 200)
@highlight_animator = CyberarmEngine::Animator.new(
start_time: Gosu.milliseconds - 1,
duration: 0,
from: Gosu::Color.rgba(0, 0, 0, 0),
to: THEME_HIGHLIGHTED_COLOR
)
populate_group_presets
populate_action_presets
end
def draw
super
unless @highlight_animator.complete?
item = @highlight_item_container
Gosu.draw_rect(
item.x, item.y,
item.width, item.height,
@highlight_animator.color_transition,
item.z + 1
)
end
end
def update
super
current_state.request_repaint unless @highlight_animator.complete?
while (hash = @scroll_into_view_list.shift)
list_container = hash[:list]
item_container = hash[:item]
return unless list_container
return unless item_container
unless (item_container.y + item_container.height).between?(list_container.y, list_container.y + list_container.height)
list_container.scroll_top = (item_container.y + item_container.height) - (list_container.y + list_container.height)
list_container.recalculate
end
@highlight_item_container = item_container
@highlight_animator = CyberarmEngine::Animator.new(
start_time: Gosu.milliseconds,
duration: 750,
from: @highlight_from_color,
to: @highlight_to_color,
tween: :ease_in_out_back
)
end
end
def update_list_children(list)
list.children.sort_by! { |i| i.style.tag.downcase }
list.children.each_with_index do |child, i|
child.style.default[:background] = i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
child.root.gui_state.request_recalculate
end
end
def scroll_into_view(item)
list_container = nil
item_container = nil
if item.is_a?(TAC::Config::Group)
list_container = @group_presets
elsif item.is_a?(TAC::Config::Action)
list_container = @action_presets
else
raise "Unsupported item type: #{item.class}"
end
item_container = find_element_by_tag(list_container, item.name)
@scroll_into_view_list << { list: list_container, item: item_container }
end
def add_group_container(group)
index = window.backend.config.presets.groups.index(group)
flow width: 1.0, height: 36, **THEME_ITEM_CONTAINER_PADDING, tag: group.name do |container|
background group == @active_group ? THEME_HIGHLIGHTED_COLOR : (index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR)
@active_group_container = container if group == @active_group
button group.name, fill: true, text_size: THEME_ICON_SIZE - 3, tag: "label" do
page(TAC::Pages::Editor, { group: group, group_is_preset: true })
end
button get_image("#{TAC::MEDIA_PATH}/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit group" do
push_state(
Dialog::NamePromptDialog,
title: "Rename Group Preset",
renaming: group,
list: window.backend.config.presets.groups,
callback_method: method(:update_group_preset)
)
end
button get_image("#{TAC::MEDIA_PATH}/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete group", **THEME_DANGER_BUTTON do
push_state(
Dialog::ConfirmDialog,
title: "Are you sure?",
message: "Delete group preset and all of its actions and variables?",
callback_method: proc { delete_group_preset(group) }
)
end
end
end
def add_action_container(action)
index = window.backend.config.presets.actions.index(action)
stack width: 1.0, height: action.comment.empty? ? 36 : 72, **THEME_ITEM_CONTAINER_PADDING, tag: action.name do |container|
background action == @active_action ? THEME_HIGHLIGHTED_COLOR : (index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR)
@active_action_container = container if action == @active_action
flow width: 1.0, height: 36 do
button action.name, fill: true, text_size: THEME_ICON_SIZE - 3, tag: "label" do
page(TAC::Pages::Editor, { action: action, action_is_preset: true })
end
button get_image("#{TAC::MEDIA_PATH}/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit action" do
push_state(
Dialog::ActionDialog,
title: "Edit Action Preset",
action: action,
list: window.backend.config.presets.actions,
callback_method: method(:update_action_preset)
)
end
button get_image("#{TAC::MEDIA_PATH}/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete action", **THEME_DANGER_BUTTON do
push_state(
Dialog::ConfirmDialog,
title: "Are you sure?",
message: "Delete action preset and all of its actions and variables?",
callback_method: proc { delete_action_preset(action) }
)
end
end
stack(width: 1.0, fill: true, scroll: true, visible: !action.comment.empty?, tag: "comment_container") do
caption action.comment.to_s, width: 1.0, text_wrap: :word_wrap, text_border: true, text_border_size: 1, text_border_color: 0xaa_000000, tag: "comment"
end
end
end
def populate_group_presets
@group_presets.clear do
window.backend.config.presets.groups.each_with_index do |group, i|
flow(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button group.name, width: 0.895 do
page(TAC::Pages::Editor,{ group: group, group_is_preset: true })
end
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit group preset" do
push_state(
Dialog::NamePromptDialog,
title: "Rename Group Preset",
renaming: group,
list: window.backend.config.presets.groups,
callback_method: method(:update_group_preset)
)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete group preset", **THEME_DANGER_BUTTON do
push_state(
Dialog::ConfirmDialog,
title: "Are you sure?",
message: "Delete group preset and all of its actions and variables?",
callback_method: proc { delete_group_preset(group) }
)
end
end
window.backend.config.presets.groups.each do |group|
add_group_container(group)
end
end
end
def populate_action_presets
@action_presets.clear do
window.backend.config.presets.actions.each_with_index do |action, i|
flow(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button action.name, width: 0.895 do
page(TAC::Pages::Editor,{ action: action, action_is_preset: true })
end
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit action preset" do
push_state(
Dialog::NamePromptDialog,
title: "Rename Action Preset",
renaming: action,
list: window.backend.config.presets.actions,
callback_method: method(:update_action_preset)
)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete action preset", **THEME_DANGER_BUTTON do
push_state(
Dialog::ConfirmDialog,
title: "Are you sure?",
message: "Delete action preset and all of its actions and variables?",
callback_method: proc { delete_action_preset(action) }
)
end
end
window.backend.config.presets.actions.each do |action|
add_action_container(action)
end
end
end
def update_group_preset(group, name)
old_name = group.name
group.name = name
window.backend.config.presets.groups.sort_by! { |g| g.name.downcase }
window.backend.config_changed!
populate_group_presets
group_container = find_element_by_tag(@group_presets, old_name)
para = find_element_by_tag(group_container, "label")
label.value = name
group_container.style.tag = name
update_list_children(@group_presets)
scroll_into_view(group)
end
def delete_group_preset(group)
@@ -102,15 +217,42 @@ module TAC
window.backend.config.presets.groups.sort_by! { |g| g.name.downcase }
window.backend.config_changed!
populate_group_presets
# Remove deleted action from list
container = find_element_by_tag(@group_presets, group.name)
@group_presets.remove(container)
update_list_children(@group_presets)
end
def update_action_preset(action, name)
def update_action_preset(action, name, comment)
old_name = action.name
action.name = name
action.comment = comment
window.backend.config.presets.actions.sort_by! { |a| a.name.downcase }
window.backend.config_changed!
populate_action_presets
action_container = find_element_by_tag(@action_presets, old_name)
para = find_element_by_tag(action_container, "label")
comment_container = find_element_by_tag(action_container, "comment_container")
comment_label = find_element_by_tag(action_container, "comment")
label.value = name
if comment.empty?
action_container.style.height = 36
comment_container.hide
comment_label.value = ""
else
action_container.style.height = 72
comment_container.show
comment_label.value = comment.to_s
end
action_container.style.tag = name
update_list_children(@action_presets)
scroll_into_view(action)
end
def delete_action_preset(action)
@@ -118,8 +260,12 @@ module TAC
window.backend.config.presets.actions.sort_by! { |a| a.name.downcase }
window.backend.config_changed!
populate_action_presets
# Remove deleted action from list
container = find_element_by_tag(@action_presets, action.name)
@action_presets.remove(container)
update_list_children(@action_presets)
end
end
end
end
end

View File

@@ -5,10 +5,10 @@ module TAC
header_bar("Search")
menu_bar.clear do
search = edit_line "", width: 0.9, height: 1.0
button get_image("#{TAC::ROOT_PATH}/media/icons/zoom.png"), image_height: 1.0 do
search = edit_line "", fill: true, height: 1.0
button get_image("#{TAC::MEDIA_PATH}/icons/zoom.png"), image_height: 1.0 do
unless search.value.strip.empty?
search_results = search_config(search.value.downcase.strip)
search_results = search_config(search.value.strip)
status_bar.clear do
if search_results.results.size.zero?
@@ -22,7 +22,7 @@ module TAC
shared_index = 0
flow(width: 1.0, height: 1.0) do
stack(width: 0.495, height: 1.0, scroll: true) do
stack(fill: true, height: 1.0, scroll: true, padding: 20, padding_right: 10) do
if search_results.groups.size.positive?
title "Groups"
@@ -44,7 +44,9 @@ module TAC
search_results.actions.each do |result|
stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button result.highlight(result.action.name), width: 1.0 do
tip = "Group: #{result.group.name}"
button result.highlight(result.action.name), width: 1.0, tip: tip do
page(TAC::Pages::Editor, { group: result.group, action: result.action, is_search: true })
end
@@ -63,7 +65,9 @@ module TAC
search_results.variables.each do |result|
stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button "#{result.highlight(result.variable.name)} [#{result.highlight(result.variable.value)}]", width: 1.0 do
tip = "Group: #{result.group.name}, Action: #{result.action.name}"
button "#{result.highlight(result.variable.name)} [#{result.variable.type}: #{result.highlight(result.variable.value)}]", width: 1.0, tip: tip do
page(TAC::Pages::Editor, { group: result.group, action: result.action, variable: result.variable, is_search: true })
end
end
@@ -73,13 +77,14 @@ module TAC
end
end
stack(width: 0.495, height: 1.0, scroll: true) do
stack(fill: true, height: 1.0, scroll: true, padding: 20, padding_left: 10) do
if search_results.group_presets.size.positive?
title "Group Presets"
search_results.group_presets.each do |result|
stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button result.highlight(result.group.name), width: 1.0 do
page(TAC::Pages::Editor, { group: result.group, group_is_preset: true, is_search: true })
end
@@ -89,14 +94,15 @@ module TAC
end
end
if search_results.action_presets.size.positive?
title "Action Presets"
search_results.action_presets.each do |result|
stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button result.highlight(result.action.name), width: 1.0 do
tip = result.group ? "Group: #{result.group.name}" : nil
button result.highlight(result.action.name), width: 1.0, tip: tip do
if result.group.nil?
page(TAC::Pages::Editor, { action: result.action, action_is_preset: true, is_search: true })
else
@@ -119,7 +125,9 @@ module TAC
search_results.variables_from_presets.each do |result|
stack(width: 1.0, **THEME_ITEM_CONTAINER_PADDING) do
background shared_index.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
button "#{result.highlight(result.variable.name)} [#{result.highlight(result.variable.value)}]", width: 1.0 do
tip = result.group ? "Group: #{result.group.name}, Action: #{result.action.name}" : "Action: #{result.action.name}"
button "#{result.highlight(result.variable.name)} [#{result.variable.type}: #{result.highlight(result.variable.value)}]", width: 1.0, tip: tip do
if result.group.nil?
page(TAC::Pages::Editor, { action: result.action, variable: result.variable, action_is_preset: true, is_search: true })
else
@@ -152,7 +160,7 @@ module TAC
def search_groups(query, search_results)
window.backend.config.groups.each do |group|
if group.name.downcase.include?(query)
if group.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, query: query, is_group: true, is_from_name: true)
search_results.results << result
end
@@ -162,12 +170,12 @@ module TAC
def search_actions(query, search_results)
window.backend.config.groups.each do |group|
group.actions.each do |action|
if action.name.downcase.include?(query)
if action.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_name: true)
search_results.results << result
end
if action.comment.downcase.include?(query)
if action.comment.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_comment: true)
search_results.results << result
end
@@ -179,12 +187,12 @@ module TAC
window.backend.config.groups.each do |group|
group.actions.each do |action|
action.variables.each do |variable|
if variable.name.downcase.include?(query)
if variable.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_name: true)
search_results.results << result
end
if variable.value.downcase.include?(query)
if variable.value.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_value: true)
search_results.results << result
end
@@ -195,29 +203,29 @@ module TAC
def search_presets(query, search_results)
window.backend.config.presets.groups.each do |group|
if group.name.downcase.include?(query)
if group.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, query: query, is_group: true, is_from_name: true, is_preset: true)
search_results.results << result
end
group.actions.each do |action|
if action.name.downcase.include?(query)
if action.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_name: true, is_preset: true)
search_results.results << result
end
if action.comment.downcase.include?(query)
if action.comment.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_comment: true, is_preset: true)
search_results.results << result
end
action.variables.each do |variable|
if variable.name.downcase.include?(query)
if variable.name.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_name: true, is_preset: true)
search_results.results << result
end
if variable.value.downcase.include?(query)
if variable.value.downcase.include?(query.downcase)
result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_value: true, is_preset: true)
search_results.results << result
end
@@ -226,27 +234,27 @@ module TAC
end
window.backend.config.presets.actions.each do |action|
if action.name.downcase.include?(query)
result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_name: true, is_preset: true)
if action.name.downcase.include?(query.downcase)
result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_name: true, is_preset: true)
search_results.results << result
end
if action.comment.downcase.include?(query.downcase)
result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_comment: true, is_preset: true)
search_results.results << result
end
action.variables.each do |variable|
if variable.name.downcase.include?(query.downcase)
result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_name: true, is_preset: true)
search_results.results << result
end
if action.comment.downcase.include?(query)
result = SearchResult.new(group: nil, action: action, query: query, is_action: true, is_from_comment: true, is_preset: true)
if variable.value.downcase.include?(query.downcase)
result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_value: true, is_preset: true)
search_results.results << result
end
action.variables.each do |variable|
if variable.name.downcase.include?(query)
result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_name: true, is_preset: true)
search_results.results << result
end
if variable.value.downcase.include?(query)
result = SearchResult.new(group: nil, action: action, variable: variable, is_variable: true, query: query, is_from_value: true, is_preset: true)
search_results.results << result
end
end
end
end
end
@@ -332,7 +340,8 @@ module TAC
end
def highlight(string)
string.gsub(/#{@query}/i, "<b><c=ff00ff>#{@query}</c></b>")
match = string.match(/#{@query}/i)
string.gsub(/#{@query}/i, "<b><c=ff00ff>#{match}</c></b>")
end
end
end

View File

@@ -6,30 +6,30 @@ module TAC
header_bar("Simulator")
menu_bar.clear do
button get_image("#{TAC::ROOT_PATH}/media/icons/right.png"), tip: "Run Simulation", image_height: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/right.png"), tip: "Run Simulation", image_height: 1.0 do
save_source
begin
@simulation = TAC::Simulator::Simulation.new(source_code: @source_code.value, field_container: @field_container)
@simulation.start
rescue SyntaxError, NameError, NoMethodError, TypeError, ArgumentError => e
rescue SyntaxError, NameError, NoMethodError, TypeError, ArgumentError, StandardError => e
puts e.backtrace.reverse.join("\n")
puts e
push_state(Dialog::AlertDialog, title: "#{e.class}", message: e)
end
end
button get_image("#{TAC::ROOT_PATH}/media/icons/stop.png"), tip: "Stop Simulation", image_height: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/stop.png"), tip: "Stop Simulation", image_height: 1.0 do
@simulation.robots.each { |robot| robot.queue.clear } if @simulation
end
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), tip: "Save", image_height: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/save.png"), tip: "Save", image_height: 1.0 do
save_source
end
end
status_bar.clear do
@simulation_status = label ""
@simulation_status = para ""
end
body.clear do
@@ -49,7 +49,7 @@ robot.forward 100
robot.turn -90
robot.forward 100"
source_code = File.read(SOURCE_FILE_PATH) if File.exists?(SOURCE_FILE_PATH)
source_code = File.read(SOURCE_FILE_PATH) if File.exist?(SOURCE_FILE_PATH)
@source_code = edit_box source_code, width: 1.0, height: 1.0
end
@@ -77,6 +77,7 @@ robot.forward 100"
@simulation.update
unless @simulation.robots.all? { |robot| robot.queue.empty? } # Only update clock if simulation is running
current_state.request_repaint
@simulation_status.value = "Time: #{(@simulation.simulation_time).round(1)} seconds"
end
end

View File

@@ -6,9 +6,9 @@ module TAC
menu_bar.clear do
@connect_menu = flow(width: 1.0, height: 1.0) do
label "Hostname", text_size: 28
para "Hostname", text_size: 28
hostname = edit_line window.backend.settings.hostname, width: 0.33, height: 1.0, text_size: 28
label "Port", text_size: 28
para "Port", text_size: 28
port = edit_line window.backend.settings.port, width: 0.33, height: 1.0, text_size: 28
button "Connect", height: 1.0, text_size: 28 do
if hostname.value != window.backend.settings.hostname || port.value.to_i != window.backend.settings.port
@@ -30,12 +30,12 @@ module TAC
end
status_bar.clear do
@tacnet_icon = image "#{TAC::ROOT_PATH}/media/icons/signal3.png", height: 26
@status_label = label "TACNET: Not Connected", text_size: 26
@tacnet_icon = image "#{TAC::MEDIA_PATH}/icons/signal3.png", height: 26
@status_label = para "TACNET: Not Connected", text_size: 26
end
body.clear do
@full_status_label = label ""
@full_status_label = para ""
end
end

View File

@@ -12,14 +12,14 @@ module TAC
@scale = 1
@size = 0
@field_size = 144 # inches [1 pixel = 1 inch]
@z = @container.z + 1
@blue = Gosu::Color.new(0xff_004080)
@red = Gosu::Color.new(0xff_800000)
@soft_orange = Gosu::Color.rgb(255, 175, 0)
end
def draw
Gosu.flush
Gosu.clip_to(@position.x, @position.y, @size, @size) do
Gosu.translate(@position.x, @position.y) do
draw_field
@@ -34,88 +34,90 @@ module TAC
end
def draw_field
Gosu.draw_rect(0, 0, @field_size * @scale, @field_size * @scale, Gosu::Color::GRAY)
Gosu.draw_rect(0, 0, @field_size * @scale, @field_size * @scale, Gosu::Color::GRAY, @z)
6.times do |i| # Tile lines across
next if i == 0
Gosu.draw_rect((@field_size * @scale) / 6 * i, 0, 1, @field_size * @scale, Gosu::Color::BLACK)
Gosu.draw_rect((@field_size * @scale) / 6 * i, 0, 1, @field_size * @scale, Gosu::Color::BLACK, @z)
end
6.times do |i| # Tile lines down
next if i == 0
Gosu.draw_rect(0, (@field_size * @scale) / 6 * i, @field_size * @scale, 1, Gosu::Color::BLACK)
Gosu.draw_rect(0, (@field_size * @scale) / 6 * i, @field_size * @scale, 1, Gosu::Color::BLACK, @z)
end
end
def draw_field_skystone
# blue bridge
Gosu.draw_rect(0, @field_size / 2 - 2, 48, 1, @blue)
Gosu.draw_rect(0, @field_size / 2 + 1, 48, 1, @blue)
Gosu.draw_rect(0, @field_size / 2 - 2, 48, 1, @blue, @z)
Gosu.draw_rect(0, @field_size / 2 + 1, 48, 1, @blue, @z)
# mid bridge
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 - 9.25, 48, 18.5, Gosu::Color.new(0xff_222222))
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 - 2, 48, 1, Gosu::Color::YELLOW)
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 + 1, 48, 1, Gosu::Color::YELLOW)
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 - 9.25, 48, 18.5, Gosu::Color.new(0xff_222222), @z)
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 - 2, 48, 1, @soft_orange, @z)
Gosu.draw_rect(@field_size / 2 - 24, @field_size / 2 + 1, 48, 1, @soft_orange, @z)
# blue bridge
Gosu.draw_rect(@field_size - 48, @field_size / 2 - 2, 48, 1, @red)
Gosu.draw_rect(@field_size - 48, @field_size / 2 + 1, 48, 1, @red)
Gosu.draw_rect(@field_size - 48, @field_size / 2 - 2, 48, 1, @red, @z)
Gosu.draw_rect(@field_size - 48, @field_size / 2 + 1, 48, 1, @red, @z)
# blue build site
Gosu.draw_quad(
24 - 2, 0, @blue,
24, 0, @blue,
0, 24 - 2, @blue,
0, 24, @blue
)
0, 24, @blue,
@z
)
# red build site
Gosu.draw_quad(
@field_size - (24 - 2), 0, @red,
@field_size - (24 - 0), 0, @red,
@field_size, 24 - 2, @red,
@field_size, 24, @red
)
@field_size, 24, @red,
@z
)
# blue depot
Gosu.draw_rect(@field_size - 24, @field_size - 24, 24, 2, @blue)
Gosu.draw_rect(@field_size - 24, @field_size - 24, 2, 24, @blue)
Gosu.draw_rect(@field_size - 24, @field_size - 24, 24, 2, @blue, @z)
Gosu.draw_rect(@field_size - 24, @field_size - 24, 2, 24, @blue, @z)
# red depot
Gosu.draw_rect(-1, @field_size - 24, 24, 2, @red)
Gosu.draw_rect(22, @field_size - 24, 2, 24, @red)
Gosu.draw_rect(-1, @field_size - 24, 24, 2, @red, @z)
Gosu.draw_rect(22, @field_size - 24, 2, 24, @red, @z)
# blue foundation
Gosu.draw_rect(48, 4, 18.5, 34.5, @blue)
Gosu.draw_rect(48, 4, 18.5, 34.5, @blue, @z)
# red foundation
Gosu.draw_rect(@field_size - (48 + 18.5), 4, 18.5, 34.5, @red)
Gosu.draw_rect(@field_size - (48 + 18.5), 4, 18.5, 34.5, @red, @z)
# stones
6.times do |i|
Gosu.draw_rect(48, @field_size - 8 * i - 8, 4, 8, Gosu::Color::YELLOW)
Gosu.draw_rect(48, @field_size - 8 * i - 8, 4, 8, @soft_orange, @z)
end
6.times do |i|
Gosu.draw_rect(@field_size - (48 + 4), @field_size - 8 * i - 8, 4, 8, Gosu::Color::YELLOW)
Gosu.draw_rect(@field_size - (48 + 4), @field_size - 8 * i - 8, 4, 8, @soft_orange, @z)
end
end
def draw_field_ultimate_goal
# middle line
Gosu.draw_rect(0, @field_size / 2 - 13, @field_size, 2, Gosu::Color::WHITE)
Gosu.draw_rect(0, @field_size / 2 - 13, @field_size, 2, Gosu::Color::WHITE, @z)
# phantom center line to indict half field for remote season field
Gosu.draw_rect(@field_size / 2 - (0.5 + 24), 0, 1, @field_size, 0x88_448844)
Gosu.draw_rect(@field_size / 2 - (0.5 + 24), 0, 1, @field_size, 0x88_448844, @z)
# blue starting lines
Gosu.draw_rect(24 - 1, @field_size - 24, 2, 24, @blue)
Gosu.draw_rect(48 - 1, @field_size - 24, 2, 24, @blue)
Gosu.draw_rect(24 - 1, @field_size - 24, 2, 24, @blue, @z)
Gosu.draw_rect(48 - 1, @field_size - 24, 2, 24, @blue, @z)
# blue wobbly wobs
Gosu.draw_circle(24, @field_size - 24, 4, 32, @blue)
Gosu.draw_circle(48, @field_size - 24, 4, 32, @blue)
Gosu.draw_circle(24, @field_size - 24, 4, 32, @blue, @z)
Gosu.draw_circle(48, @field_size - 24, 4, 32, @blue, @z)
# blue starter stack
Gosu.draw_rect(36 - 1, @field_size - 50, 2, 2, @blue)
Gosu.draw_rect(36 - 1, @field_size - 50, 2, 2, @blue, @z)
# blue target zones
# A
@@ -132,15 +134,15 @@ module TAC
end
# red starting lines
Gosu.draw_rect(@field_size - 24 - 1, @field_size - 24, 2, 24, @red)
Gosu.draw_rect(@field_size - 48 - 1, @field_size - 24, 2, 24, @red)
Gosu.draw_rect(@field_size - 24 - 1, @field_size - 24, 2, 24, @red, @z)
Gosu.draw_rect(@field_size - 48 - 1, @field_size - 24, 2, 24, @red, @z)
# red wobbly wobs
Gosu.draw_circle(@field_size - 24, @field_size - 24, 4, 32, @red)
Gosu.draw_circle(@field_size - 48, @field_size - 24, 4, 32, @red)
Gosu.draw_circle(@field_size - 24, @field_size - 24, 4, 32, @red, @z)
Gosu.draw_circle(@field_size - 48, @field_size - 24, 4, 32, @red, @z)
# red starter stack
Gosu.draw_rect(@field_size - 37, @field_size - 50, 2, 2, @red)
Gosu.draw_rect(@field_size - 37, @field_size - 50, 2, 2, @red, @z)
# red target zones
# A
@@ -161,91 +163,88 @@ module TAC
def draw_field_freight_frenzy
# blue ZONE
Gosu.draw_rect(24, @field_size - 24, 2, 24, @blue)
Gosu.draw_rect(24, @field_size - 24, 24, 2, @blue)
Gosu.draw_rect(48 - 2, @field_size - 24, 2, 24, @blue)
Gosu.draw_rect(24, @field_size - 24, 2, 24, @blue, @z)
Gosu.draw_rect(24, @field_size - 24, 24, 2, @blue, @z)
Gosu.draw_rect(48 - 2, @field_size - 24, 2, 24, @blue, @z)
# blue barcode 1
Gosu.draw_rect(36 - 1, @field_size - 24 - 4, 2, 2, @blue)
Gosu.draw_rect(36 - 1, @field_size - 36 - 1, 2, 2, @blue)
Gosu.draw_rect(36 - 1, @field_size - 48 + 2, 2, 2, @blue)
Gosu.draw_rect(36 - 1, @field_size - 24 - 4, 2, 2, @blue, @z)
Gosu.draw_rect(36 - 1, @field_size - 36 - 1, 2, 2, @blue, @z)
Gosu.draw_rect(36 - 1, @field_size - 48 + 2, 2, 2, @blue, @z)
# blue barcode 2
Gosu.draw_rect(36 - 1, 48 + 2, 2, 2, @blue)
Gosu.draw_rect(36 - 1, 60 - 1, 2, 2, @blue)
Gosu.draw_rect(36 - 1, 72 - 4, 2, 2, @blue)
Gosu.draw_rect(36 - 1, 48 + 2, 2, 2, @blue, @z)
Gosu.draw_rect(36 - 1, 60 - 1, 2, 2, @blue, @z)
Gosu.draw_rect(36 - 1, 72 - 4, 2, 2, @blue, @z)
# blue wobble goal
Gosu.draw_circle(48, 84, 9, 32, @blue)
Gosu.draw_circle(48, 84, 9, 32, @blue, @z)
# blue shared wobble goal
Gosu.draw_circle(@field_size / 2, 24, 9, 32, @blue)
Gosu.draw_circle(@field_size / 2, 24, 9, 32, @blue, @z)
# red ZONE
Gosu.draw_rect(@field_size - 24 - 2, @field_size - 24, 2, 24, @red)
Gosu.draw_rect(@field_size - 48, @field_size - 24, 24, 2, @red)
Gosu.draw_rect(@field_size - 48, @field_size - 24, 2, 24, @red)
Gosu.draw_rect(@field_size - 24 - 2, @field_size - 24, 2, 24, @red, @z)
Gosu.draw_rect(@field_size - 48, @field_size - 24, 24, 2, @red, @z)
Gosu.draw_rect(@field_size - 48, @field_size - 24, 2, 24, @red, @z)
# red barcode 1
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 24 - 4, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 36 - 1, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 48 + 2, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 24 - 4, 2, 2, @red, @z)
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 36 - 1, 2, 2, @red, @z)
Gosu.draw_rect(@field_size - 36 - 1, @field_size - 48 + 2, 2, 2, @red, @z)
# red barcode 2
Gosu.draw_rect(@field_size - 36 - 1, 48 + 2, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, 60 - 1, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, 72 - 4, 2, 2, @red)
Gosu.draw_rect(@field_size - 36 - 1, 48 + 2, 2, 2, @red, @z)
Gosu.draw_rect(@field_size - 36 - 1, 60 - 1, 2, 2, @red, @z)
Gosu.draw_rect(@field_size - 36 - 1, 72 - 4, 2, 2, @red, @z)
# red wobble goal
Gosu.draw_circle(@field_size - 48, 84, 9, 32, @red)
Gosu.draw_circle(@field_size - 48, 84, 9, 32, @red, @z)
# red shared wobble goal
Gosu.clip_to(@field_size / 2, 0, 10, 48) do
Gosu.draw_circle(@field_size / 2, 24, 9, 32, @red)
end
# Gosu.clip_to(@field_size / 2, 0, 10, 48) do
Gosu.draw_circle(@field_size / 2, 24, 9, 32, @red, @z)
# end
# white corner left
faint_white = Gosu::Color.rgb(240, 240, 240)
Gosu.draw_rect(0, 46 - 2, 46, 2, faint_white)
Gosu.draw_rect(46 - 2, 0, 2, 46, faint_white)
Gosu.draw_rect(0, 46 - 2, 46, 2, faint_white, @z)
Gosu.draw_rect(46 - 2, 0, 2, 46, faint_white, @z)
# white corner right
Gosu.draw_rect(@field_size - 46, 46 - 2, 46, 2, faint_white)
Gosu.draw_rect(@field_size - 46, 0, 2, 46, faint_white)
Gosu.draw_rect(@field_size - 46, 46 - 2, 46, 2, faint_white, @z)
Gosu.draw_rect(@field_size - 46, 0, 2, 46, faint_white, @z)
# cross bars
bar_gray = Gosu::Color.rgb(50, 50, 50)
# MAIN
Gosu.draw_rect(13.75, 48 - 2, @field_size - 13.75 * 2, 1, bar_gray)
Gosu.draw_rect(13.75, 48 + 1, @field_size - 13.75 * 2, 1, bar_gray)
Gosu.draw_rect(13.75, 48 - 2, 1, 4, Gosu::Color::BLACK)
Gosu.draw_rect(@field_size - 13.75 - 1, 48 - 2, 1, 4, Gosu::Color::BLACK)
Gosu.draw_rect(13.75, 48 - 2, @field_size - 13.75 * 2, 1, bar_gray, @z)
Gosu.draw_rect(13.75, 48 + 1, @field_size - 13.75 * 2, 1, bar_gray, @z)
Gosu.draw_rect(13.75, 48 - 2, 1, 4, Gosu::Color::BLACK, @z)
Gosu.draw_rect(@field_size - 13.75 - 1, 48 - 2, 1, 4, Gosu::Color::BLACK, @z)
# BLUE
Gosu.draw_rect(48 - 2, 13.75, 1, 48 - 13.75 - 2, bar_gray)
Gosu.draw_rect(48 + 1, 13.75, 1, 48 - 13.75 - 2, bar_gray)
Gosu.draw_rect(48 - 2, 13.75, 4, 1, Gosu::Color::BLACK)
Gosu.draw_rect(48 - 2, 48 - 3, 4, 1, Gosu::Color::BLACK)
Gosu.draw_rect(48 - 2, 13.75, 1, 48 - 13.75 - 2, bar_gray, @z)
Gosu.draw_rect(48 + 1, 13.75, 1, 48 - 13.75 - 2, bar_gray, @z)
Gosu.draw_rect(48 - 2, 13.75, 4, 1, Gosu::Color::BLACK, @z)
Gosu.draw_rect(48 - 2, 48 - 3, 4, 1, Gosu::Color::BLACK, @z)
# RED
Gosu.draw_rect(@field_size - 48 - 2, 13.75, 1, 48 - 13.75 - 2, bar_gray)
Gosu.draw_rect(@field_size - 48 + 1, 13.75, 1, 48 - 13.75 - 2, bar_gray)
Gosu.draw_rect(@field_size - 48 - 2, 13.75, 4, 1, Gosu::Color::BLACK)
Gosu.draw_rect(@field_size - 48 - 2, 48 - 3, 4, 1, Gosu::Color::BLACK)
Gosu.draw_rect(@field_size - 48 - 2, 13.75, 1, 48 - 13.75 - 2, bar_gray, @z)
Gosu.draw_rect(@field_size - 48 + 1, 13.75, 1, 48 - 13.75 - 2, bar_gray, @z)
Gosu.draw_rect(@field_size - 48 - 2, 13.75, 4, 1, Gosu::Color::BLACK, @z)
Gosu.draw_rect(@field_size - 48 - 2, 48 - 3, 4, 1, Gosu::Color::BLACK, @z)
# Duck Delivery
Gosu.draw_circle(2, @field_size - 2, 9, 16, Gosu::Color.rgb(75, 75, 75))
Gosu.draw_circle(@field_size - 2, @field_size - 2, 9, 16, Gosu::Color.rgb(75, 75, 75))
# packages
soft_orange = Gosu::Color.rgb(255, 175, 0)
7.times do |y|
7.times do |x|
if x.even?
Gosu.draw_rect(x * 3 + 1, y * 3 + 1, 2, 2, soft_orange)
Gosu.draw_rect(x * 3 + 1, y * 3 + 1, 2, 2, @soft_orange, @z)
else
Gosu.draw_circle(x * 3 + 2, y * 3 + 2, 1, 16, faint_white)
Gosu.draw_circle(x * 3 + 2, y * 3 + 2, 1, 16, faint_white, @z)
end
end
end
@@ -253,24 +252,200 @@ module TAC
7.times do |y|
7.times do |x|
if x.even?
Gosu.draw_rect((@field_size - 4) - x * 3 + 1, y * 3 + 1, 2, 2, soft_orange)
Gosu.draw_rect((@field_size - 4) - x * 3 + 1, y * 3 + 1, 2, 2, @soft_orange, @z)
else
Gosu.draw_circle((@field_size - 4) - x * 3 + 2, y * 3 + 2, 1, 16, faint_white)
Gosu.draw_circle((@field_size - 4) - x * 3 + 2, y * 3 + 2, 1, 16, faint_white, @z)
end
end
end
Gosu.draw_rect(0, 60 - 1, 2, 2, soft_orange)
Gosu.draw_rect(0, 108 - 1, 2, 2, soft_orange)
Gosu.draw_rect(@field_size - 2, 60 - 1, 2, 2, soft_orange)
Gosu.draw_rect(@field_size - 2, 108 - 1, 2, 2, soft_orange)
Gosu.draw_rect(0, 60 - 1, 2, 2, @soft_orange, @z)
Gosu.draw_rect(0, 108 - 1, 2, 2, @soft_orange, @z)
Gosu.draw_rect(@field_size - 2, 60 - 1, 2, 2, @soft_orange, @z)
Gosu.draw_rect(@field_size - 2, 108 - 1, 2, 2, @soft_orange, @z)
end
def draw_field_power_play
# pole junctions (Drawn before ground junctions to be lazy- ground junctions will cover non-existant poles)
5.times do |y|
5.times do |x|
Gosu.draw_circle(24 + (x * 24), 24 + (y * 24), 0.5, 16, @soft_orange, @z)
end
end
# ground junction
3.times do |y|
3.times do |x|
Gosu.draw_circle(24 + (x * 48), 24 + (y * 48), 3, 16, Gosu::Color::BLACK, @z)
end
end
# Field cones
2.times do |y|
2.times do |x|
Gosu.draw_circle(36 + (x * 72), 36 + (y * 72), 2, 16, x.zero? ? @blue : @red, @z)
end
end
# alliance LINEs
2.times do |y|
2.times do |x|
Gosu.draw_rect(59 + (x * 24), y * (144 - 23.5), 2, 23.5, x.zero? ? @blue : @red, @z)
end
end
# alliance LINE cones
2.times do |y|
2.times do |x|
Gosu.draw_circle(60 + (x * 24), y * (144 - 4) + 2, 2, 16, x.zero? ? @blue : @red, @z)
end
end
# Corner TAPE
4.times do |i|
Gosu.rotate(i * 90.0, 72, 72) do
Gosu.draw_quad(
24 - 2, 0, i.even? ? @red : @blue,
24, 0, i.even? ? @red : @blue,
0, 24 - 2, i.even? ? @red : @blue,
0, 24, i.even? ? @red : @blue,
@z
)
end
end
# Triangle TAPE
2.times do |i|
Gosu.rotate(i * 180.0, 72, 72) do
Gosu.draw_quad(
0, 72 - 10.5, i.odd? ? @red : @blue,
10.5, 72, i.odd? ? @red : @blue,
8.5, 72, i.odd? ? @red : @blue,
0, 72 - 8.5, i.odd? ? @red : @blue,
@z
)
Gosu.draw_quad(
0, 72 + 10.5, i.odd? ? @red : @blue,
10.5, 72, i.odd? ? @red : @blue,
8.5, 72, i.odd? ? @red : @blue,
0, 72 + 8.5, i.odd? ? @red : @blue,
@z
)
end
end
end
###########################
### --- CENTERSTAGE --- ###
###########################
def draw_field_centerstage
# Corner TAPE
2.times do |i|
Gosu.rotate((i + 2) * 90.0, 72, 72) do
Gosu.draw_quad(
24 - 2, 0, i.odd? ? @red : @blue,
24, 0, i.odd? ? @red : @blue,
0, 24 - 2, i.odd? ? @red : @blue,
0, 24, i.odd? ? @red : @blue,
@z
)
end
end
# Backstage TAPE
# BLUE
Gosu.draw_rect(0, 22, 58.5, 2, @blue, @z)
Gosu.draw_quad(
72 - 2, 0, @blue,
72, 0, @blue,
58.5 - 2, 24, @blue,
58.5, 24, @blue,
@z
)
# RED
Gosu.draw_rect(@field_size, 22, -58.5, 2, @red, @z)
Gosu.draw_quad(
@field_size - (72 - 2), 0, @red,
@field_size - 72, 0, @red,
@field_size - (58.5 - 2), 24, @red,
@field_size - 58.5, 24, @red,
@z
)
# Backstage BACKDROP
Gosu.draw_rect(24, 0, 24, 11.25, 0xff_252525, @z)
Gosu.draw_rect(@field_size - 48, 0, 24, 11.25, 0xff_252525, @z)
# Pixel TAPE
7.times do |i|
next if i == 3 # skip 4th iteration; empty slot
# TAPE
Gosu.draw_rect(24 + 11.5 + (12 * i), @field_size - 6, 1, 6, 0xff_dddddd, @z)
# Pixel
Gosu.rotate(30, 24 + 12 + (12 * i), @field_size - 1.75) do
Gosu.draw_circle(24 + 12 + (12 * i), @field_size - 1.75, 2.25, 6, 0xff_ffffff, @z)
end
end
# Spike marks TAPE
# BLUE
2.times do |r|
2.times do |i|
Gosu.rotate(r * 180, @field_size / 2, @field_size / 2 + 12) do
c = r.even? ? @blue : @red
Gosu.translate(0, i * 48) do
Gosu.draw_rect(35.5, @field_size / 2 - 1.5, 12, 1, c, @z)
Gosu.draw_rect(47.5 - 1, @field_size / 2 - 18, 1, 12, c, @z)
Gosu.draw_rect(35.5, @field_size / 2 - 23.5, 12, 1, c, @z)
# --- mark
Gosu.draw_rect(35.5 + 6, @field_size / 2 - 1.5, 0.125, 1, 0xff_000000, @z)
Gosu.draw_rect(47.5 - 1, @field_size / 2 - 18 + 6, 1, 0.125, 0xff_000000, @z)
Gosu.draw_rect(35.5 + 6, @field_size / 2 - 23.5, 0.125, 1, 0xff_000000, @z)
end
end
end
end
# Trusses
Gosu.draw_rect(0, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
Gosu.draw_rect(23, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
Gosu.draw_rect(47, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
Gosu.draw_rect(@field_size / 2 + 24 + -1, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
Gosu.draw_rect(@field_size / 2 + 24 + 23, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
Gosu.draw_rect(@field_size / 2 + 24 + 46, @field_size / 2 - 2, 2, 28, 0xff_656565, @z)
# Crossbeams
# BLUE
Gosu.draw_rect(0, @field_size / 2 + 12 + 2, 26, 2, @blue, @z)
Gosu.draw_rect(24, @field_size / 2 + 12 - 1, 24, 2, @blue, @z)
# YELLOW
# --- Blue
Gosu.draw_rect(0, @field_size / 2 + 2, 25, 1, @soft_orange, @z)
Gosu.draw_rect(23, @field_size / 2 + 21, 26, 1, @soft_orange, @z)
# --- Middle
Gosu.draw_rect(24 + 24, @field_size / 2 + 2, 48, 1, @soft_orange, @z)
Gosu.draw_rect(24 + 24, @field_size / 2 + 12 - 0.5, 48, 1, @soft_orange, @z)
Gosu.draw_rect(24 + 24, @field_size / 2 + 12 - 0.5, 48, 1, @soft_orange, @z)
# --- --- parallel beams
Gosu.draw_rect(24 + 28, @field_size / 2 + 2, 1, 10, @soft_orange, @z)
Gosu.draw_rect(@field_size / 2, @field_size / 2 + 2, 1, 10, @soft_orange, @z)
Gosu.draw_rect(24 + 72 - 4, @field_size / 2 + 2, 1, 10, @soft_orange, @z)
# --- Red
Gosu.draw_rect(47 + 72, @field_size / 2 + 2, 25, 1, @soft_orange, @z)
Gosu.draw_rect(23 + 72, @field_size / 2 + 21, 26, 1, @soft_orange, @z)
# RED
Gosu.draw_rect(24 + 72, @field_size / 2 + 12 - 1, 24, 2, @red, @z)
Gosu.draw_rect(46 + 72, @field_size / 2 + 12 + 2, 26, 2, @red, @z)
end
def draw_tile_box(color)
Gosu.draw_rect(0, 0, 24, 2, color)
Gosu.draw_rect(22, 2, 2, 22, color)
Gosu.draw_rect(0, 22, 22, 2, color)
Gosu.draw_rect(0, 2, 2, 22, color)
Gosu.draw_rect(0, 0, 24, 2, color, @z)
Gosu.draw_rect(22, 2, 2, 22, color, @z)
Gosu.draw_rect(0, 22, 22, 2, color, @z)
Gosu.draw_rect(0, 2, 2, 22, color, @z)
end
def update

View File

@@ -1,44 +1,54 @@
module TAC
class Simulator
class Robot
attr_accessor :position, :angle
attr_reader :alliance, :width, :depth
def initialize(alliance:, width:, depth:)
FONT = Gosu::Font.new(11)
attr_accessor :position, :angle, :comment
attr_reader :alliance, :width, :depth, :z
def initialize(alliance:, width:, depth:, container:)
@alliance = alliance
@width, @depth = width, depth
@width = width
@depth = depth
@container = container
@position = CyberarmEngine::Vector.new
@angle = 0
@z = @container.z + 1
@queue = []
@unit = :ticks
@ticks_per_revolution = 240
@gear_ratio = 1
@comment = ""
end
def draw
Gosu.translate(@width / 2, @depth / 2) do
Gosu.rotate(@angle, @position.x, @position.y) do
Gosu.draw_rect(@position.x - @width / 2, @position.y - @depth / 2, @width, @depth, Gosu::Color::BLACK)
Gosu.draw_rect(@position.x - @width / 2 + 1, @position.y - @depth / 2 + 1, @width - 2, @depth - 2, Gosu::Color.new(0xff_808022))
Gosu.draw_rect(@position.x - @width / 2, @position.y - @depth / 2, @width, @depth, Gosu::Color::BLACK, @z)
Gosu.draw_rect(@position.x - @width / 2 + 1, @position.y - @depth / 2 + 1, @width - 2, @depth - 2, Gosu::Color.new(0xff_808022), @z)
if @alliance == :blue
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, TAC::Palette::BLUE_ALLIANCE)
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, TAC::Palette::BLUE_ALLIANCE, @z)
elsif @alliance == :red
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, TAC::Palette::RED_ALLIANCE)
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, TAC::Palette::RED_ALLIANCE, @z)
else
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, @alliance)
Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, @alliance, @z)
end
Gosu.draw_circle(@position.x, @position.y - @depth * 0.25, 2, 3, TAC::Palette::TIMECRAFTERS_TERTIARY)
Gosu.draw_circle(@position.x, @position.y - @depth * 0.25, 2, 3, TAC::Palette::TIMECRAFTERS_TERTIARY, @z)
end
FONT.draw_text(@comment, 2.2, 2.2, @z, 1, 1, Gosu::Color::BLACK)
FONT.draw_text(@comment, 2, 2, @z)
end
end
def update(dt)
@angle %= 360.0
if state = @queue.first
if (state = @queue.first)
state.update(dt)
if state.complete?
@@ -87,6 +97,14 @@ module TAC
@queue << Turn.new(robot: self, relative_angle: relative_angle, power: power)
end
def delay(time_in_seconds)
@queue << Delay.new(robot: self, time_in_seconds: time_in_seconds)
end
def comment(comment)
@queue << Comment.new(robot: self, comment: comment)
end
def speed
@ticks_per_revolution / @gear_ratio
end
@@ -95,7 +113,7 @@ module TAC
@queue
end
class State
class State
def start
end
@@ -130,9 +148,10 @@ class State
def draw
Gosu.draw_line(
@robot.position.x + @robot.width / 2, @robot.position.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY,
@goal.x + @robot.width / 2, @goal.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY
@goal.x + @robot.width / 2, @goal.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY,
@robot.z
)
Gosu.draw_rect(@goal.x + (@robot.width / 2 - 1), @goal.y + (@robot.depth / 2 - 1), 2, 2, Gosu::Color::RED)
Gosu.draw_rect(@goal.x + (@robot.width / 2 - 1), @goal.y + (@robot.depth / 2 - 1), 2, 2, Gosu::Color::RED, @robot.z)
end
def update(dt)
@@ -176,9 +195,10 @@ class State
def draw
Gosu.draw_line(
@robot.position.x + @robot.width / 2, @robot.position.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY,
@goal.x + @robot.width / 2, @goal.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY
@goal.x + @robot.width / 2, @goal.y + @robot.depth / 2, TAC::Palette::TIMECRAFTERS_TERTIARY,
@robot.z
)
Gosu.draw_rect(@goal.x + (@robot.width / 2 - 1), @goal.y + (@robot.depth / 2 - 1), 2, 2, Gosu::Color::RED)
Gosu.draw_rect(@goal.x + (@robot.width / 2 - 1), @goal.y + (@robot.depth / 2 - 1), 2, 2, Gosu::Color::RED, @robot.z)
end
def update(dt)
@@ -230,7 +250,8 @@ class State
fraction,
360,
1,
TAC::Palette::TIMECRAFTERS_TERTIARY
TAC::Palette::TIMECRAFTERS_TERTIARY,
@robot.z
)
end
@@ -239,7 +260,8 @@ class State
@robot.position.y + @robot.depth / 2 + Gosu.offset_y(@target_angle, @robot.width > @robot.depth ? @robot.width : @robot.depth),
1,
9,
Gosu::Color::RED
Gosu::Color::RED,
@robot.z
)
# Gosu.draw_arc(@position.x, @position.y, 6, 1.0, 32, 2, @alliance)
end
@@ -259,6 +281,58 @@ class State
@last_angle = @robot.angle
end
end
class Delay < State
def initialize(robot:, time_in_seconds:)
@robot = robot
@time_in_seconds = time_in_seconds
@accumulator = 0.0
end
def start
@complete = false
end
def draw
fraction = @accumulator / @time_in_seconds.to_f
Gosu.draw_arc(
@robot.position.x + @robot.width / 2,
@robot.position.y + @robot.depth / 2,
@robot.width > @robot.depth ? @robot.width : @robot.depth,
1 - fraction,
360,
1,
TAC::Palette::TIMECRAFTERS_TERTIARY,
@robot.z
)
@complete = fraction >= 1
end
def update(dt)
@accumulator += dt
end
end
class Comment < State
def initialize(robot:, comment:)
@robot = robot
@comment = comment
end
def start
@robot.comment = @comment
@complete = true
end
def draw
end
def update(dt)
end
end
end
end
end

View File

@@ -8,7 +8,7 @@ module TAC
@field_container = field_container
@robots = []
@field = Field.new(simulation: self, season: :freight_frenzy, container: @field_container)
@field = Field.new(simulation: self, season: :centerstage, container: @field_container)
@show_paths = false
@last_milliseconds = Gosu.milliseconds
@@ -29,7 +29,7 @@ module TAC
def update
@accumulator += (Gosu.milliseconds - @last_milliseconds) / 1000.0
while(@accumulator > @simulation_step)
while @accumulator > @simulation_step
@field.update
@robots.each { |robot| robot.update(@simulation_step) }
@@ -41,7 +41,7 @@ module TAC
end
def create_robot(alliance:, width:, depth:)
robot = Simulator::Robot.new(alliance: alliance, width: width, depth: depth)
robot = Simulator::Robot.new(alliance: alliance, width: width, depth: depth, container: @field_container)
@robots << robot
return robot

View File

@@ -8,12 +8,12 @@ module TAC
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY, TAC::Palette::TIMECRAFTERS_TERTIARY, TAC::Palette::TIMECRAFTERS_PRIMARY]
end
@title_font = CyberarmEngine::Text.new(TAC::NAME, z: 100, size: 72, border: true, border_size: 3, font: THEME[:Label][:font])
@logo = Gosu::Image.new("#{TAC::ROOT_PATH}/media/logo.png")
@title_font = CyberarmEngine::Text.new(TAC::NAME, z: 100, size: 72, border: true, border_size: 2, border_color: 0xff_000000, font: THEME[:TextBlock][:font], static: true)
@logo = Gosu::Image.new("#{TAC::MEDIA_PATH}/logo.png")
@title_animator = CyberarmEngine::Animator.new(start_time: 0, duration: 750, from: 0.0, to: 1.0, tween: :swing_from_to)
@logo_animator = CyberarmEngine::Animator.new(start_time: 750, duration: 1_000, from: 0.0, to: 1.0, tween: :swing_to)
@transition_animator = CyberarmEngine::Animator.new(start_time: 2_250, duration: 750, from: 0, to: 255, tween: :ease_out)
@title_animator = CyberarmEngine::Animator.new(start_time: Gosu.milliseconds + 0, duration: 750, from: 0.0, to: 1.0, tween: :swing_from_to)
@logo_animator = CyberarmEngine::Animator.new(start_time: Gosu.milliseconds + 750, duration: 1_000, from: 0.0, to: 1.0, tween: :swing_to)
@transition_animator = CyberarmEngine::Animator.new(start_time: Gosu.milliseconds + 2_250, duration: 750, from: 0, to: 255, tween: :ease_out)
@transition_color = Gosu::Color.new(0x00_111111)
@next_state = Editor
@@ -30,17 +30,23 @@ module TAC
def update
super
request_repaint
@title_font.x = window.width / 2 - @title_font.width / 2
@title_font.y = (window.height / 2 - (@logo.height / 2 + @title_font.height)) * @title_animator.transition
@transition_color.alpha = @transition_animator.transition
push_state(@next_state) if @transition_color.alpha >= 255
if @transition_color.alpha >= 255
pop_state
push_state(@next_state)
end
end
def button_up(id)
def button_down(id)
super
pop_state
push_state(@next_state)
end
end

View File

@@ -8,6 +8,8 @@ class Editor < CyberarmEngine::GuiState
@window_width = 0
@window_height = 0
@last_tacnet_status = nil
@pages = {}
@page = nil
@@ -34,18 +36,18 @@ class Editor < CyberarmEngine::GuiState
@header_bar = flow(width: 1.0, height: 36) do
background 0xff_006000
@header_bar_label = label TAC::NAME, width: 1.0, text_align: :center, text_size: 32
@header_bar_label = para TAC::NAME, fill: true, text_align: :center, text_size: 32, font: TAC::THEME_BOLD_FONT, margin_left: BORDERLESS ? 36 * 3 : 0
@window_controls = flow(x: window.width - 36 * 2, y: 0, height: 1.0) do
button get_image("#{TAC::ROOT_PATH}/media/icons/minus.png"), tip: "Minimize", image_height: 1.0 do
@window_controls = flow(width: 36 * 3, height: 1.0) do
button get_image("#{TAC::MEDIA_PATH}/icons/minus.png"), tip: "Minimize", image_height: 1.0 do
window.minimize if window.respond_to?(:minimize)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/larger.png"), tip: "Maximize", image_height: 1.0 do |btn|
button get_image("#{TAC::MEDIA_PATH}/icons/larger.png"), tip: "Maximize", image_height: 1.0 do |btn|
window.maximize if window.respond_to?(:maximize)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/cross.png"), tip: "Exit", image_height: 1.0, **TAC::THEME_DANGER_BUTTON do
button get_image("#{TAC::MEDIA_PATH}/icons/cross.png"), tip: "Exit", image_height: 1.0, **TAC::THEME_DANGER_BUTTON do
window.close
end
end
@@ -55,27 +57,27 @@ class Editor < CyberarmEngine::GuiState
@navigation = stack(width: 64, height: 1.0, scroll: true) do
background 0xff_333333
button get_image("#{TAC::ROOT_PATH}/media/icons/home.png"), margin: 4, tip: "Home", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/home.png"), margin: 4, tip: "Home", image_width: 1.0 do
page(TAC::Pages::Home)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/menuList.png"), margin: 4, tip: "Editor", image_width: 1.0 do
page(TAC::Pages::Editor)
button get_image("#{TAC::MEDIA_PATH}/icons/menuList.png"), margin: 4, tip: "Editor", image_width: 1.0 do
page(TAC::Pages::EditorV3)
end
@tacnet_button = button get_image("#{TAC::ROOT_PATH}/media/icons/signal3.png"), margin: 4, tip: "TACNET", image_width: 1.0 do
@tacnet_button = button get_image("#{TAC::MEDIA_PATH}/icons/signal3.png"), margin: 4, tip: "TACNET", image_width: 1.0 do
page(TAC::Pages::TACNET)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), margin: 4, tip: "Configurations", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/gear.png"), margin: 4, tip: "Configurations", image_width: 1.0 do
page(TAC::Pages::Configurations)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/menuGrid.png"), margin: 4, tip: "Presets", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/menuGrid.png"), margin: 4, tip: "Presets", image_width: 1.0 do
page(TAC::Pages::Presets)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/zoom.png"), margin: 4, tip: "Search", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/zoom.png"), margin: 4, tip: "Search", image_width: 1.0 do
page(TAC::Pages::Search)
end
@@ -84,28 +86,28 @@ class Editor < CyberarmEngine::GuiState
para "Tools", width: 1.0, text_align: :center
end
button get_image("#{TAC::ROOT_PATH}/media/icons/right.png"), margin: 4, tip: "Simulator", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/right.png"), margin: 4, tip: "Simulator", image_width: 1.0 do
page(TAC::Pages::Simulator)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/joystickLeft.png"), margin: 4, tip: "Field Planner", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/joystickLeft.png"), margin: 4, tip: "Field Planner", image_width: 1.0 do
page(TAC::Pages::FieldPlanner)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/massiveMultiplayer.png"), margin: 4, tip: "Drive Team Rotation Generator", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/massiveMultiplayer.png"), margin: 4, tip: "Drive Team Rotation Generator", image_width: 1.0 do
page(TAC::Pages::DriveTeamRotationGenerator)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/custom_stopWatch.png"), margin: 4, tip: "Game Clock", image_width: 1.0 do
button get_image("#{TAC::MEDIA_PATH}/icons/custom_stopWatch.png"), margin: 4, tip: "Game Clock", image_width: 1.0 do
page(TAC::Pages::GameClock)
end
button get_image("#{TAC::ROOT_PATH}/media/icons/power.png"), margin: 4, tip: "Exit", image_width: 1.0, **TAC::THEME_DANGER_BUTTON do
button get_image("#{TAC::MEDIA_PATH}/icons/power.png"), margin: 4, tip: "Exit", image_width: 1.0, **TAC::THEME_DANGER_BUTTON do
window.close
end
end
@content = stack(width: window.width - @navigation.style.width, height: 1.0) do
@content = stack(fill: true, height: 1.0) do
@chrome = stack(width: 1.0, height: 96) do
@menu_bar = flow(width: 1.0, height: 48, padding: 8) do
background 0xff_008000
@@ -128,9 +130,9 @@ class Editor < CyberarmEngine::GuiState
end
def draw
super
@page&.draw
@page.draw if @page
super
end
def update
@@ -138,15 +140,35 @@ class Editor < CyberarmEngine::GuiState
@page.update if @page
case window.backend.tacnet.status
when :not_connected
@tacnet_button.style.color = Gosu::Color::WHITE
when :connecting
@tacnet_button.style.color = TAC::Palette::TACNET_CONNECTING
when :connected
@tacnet_button.style.color = TAC::Palette::TACNET_CONNECTED
when :connection_error
@tacnet_button.style.color = TAC::Palette::TACNET_CONNECTION_ERROR
if @last_tacnet_status != window.backend.tacnet.status
@last_tacnet_status = window.backend.tacnet.status
case window.backend.tacnet.status
when :not_connected
@tacnet_button.style.color = Gosu::Color::WHITE
@header_bar.style.background = 0xff_006000
when :connected
@tacnet_button.style.color = Gosu::Color::WHITE
@header_bar.style.background = TAC::Palette::TACNET_PRIMARY
when :connecting
@tacnet_button.style.color = TAC::Palette::TACNET_CONNECTING
@header_bar.style.background = TAC::Palette::TACNET_CONNECTING
when :connection_error
@tacnet_button.style.color = TAC::Palette::TACNET_CONNECTION_ERROR
@header_bar.style.background = TAC::Palette::TACNET_CONNECTION_ERROR
unless @page.is_a?(TAC::Pages::TACNET)
push_state(TAC::Dialog::TACNETDialog, title: "TACNET Connection Error", message: window.backend.tacnet.full_status)
end
end
@tacnet_button.style.default[:color] = @tacnet_button.style.color
@header_bar.style.default[:background] = @header_bar.style.background
@tacnet_button.recalculate
@header_bar.recalculate
request_repaint
end
window.width = Gosu.available_width / 2 if window.width < Gosu.available_width / 2
@@ -211,4 +233,4 @@ class Editor < CyberarmEngine::GuiState
TAC::Pages::Search
].include?(klass)
end
end
end

View File

@@ -1,7 +1,13 @@
module TAC
ROOT_PATH = File.expand_path("../..", __FILE__)
if ARGV.join.include?("--dev")
ROOT_PATH = File.expand_path("../..", __FILE__)
else
ROOT_PATH = "#{Dir.home}/TimeCrafters_Configuration_Tool"
end
CONFIGS_PATH = "#{ROOT_PATH}/data/configs"
SETTINGS_PATH = "#{ROOT_PATH}/data/settings.json"
MEDIA_PATH = "#{File.expand_path("../..", __FILE__)}/media"
CONFIG_SPEC_VERSION = 2
end

View File

@@ -1,6 +1,6 @@
module TAC
class TACNET
DEFAULT_HOSTNAME = "192.168.49.1"
DEFAULT_HOSTNAME = "192.168.49.1".freeze
DEFAULT_PORT = 8962
SYNC_INTERVAL = 250 # ms
@@ -30,7 +30,7 @@ module TAC
end
def full_status
_status = status.to_s.split("_").map { |c| c.capitalize }.join(" ")
_status = status.to_s.split("_").map(&:capitalize).join(" ")
if connected?
net_stats = ""
@@ -42,7 +42,7 @@ module TAC
"<b>Status:</b> #{_status}\n\n#{net_stats}"
elsif @connection&.client && @connection.client.socket_error?
"<b>Status:</b> #{_status}\n\n#{@connection.client.last_socket_error.to_s}"
"<b>Status:</b> #{_status}\n\n#{@connection.client.last_socket_error}"
else
"<b>Status:</b> #{_status}"
end
@@ -53,10 +53,10 @@ module TAC
end
def close
if connected?
@connection.close
@connection = nil
end
return unless connected?
@connection.close
@connection = nil
end
def client
@@ -75,4 +75,4 @@ module TAC
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
end
end
end
end

View File

@@ -8,7 +8,9 @@ module TAC
attr_reader :uuid, :read_queue, :write_queue, :socket,
:packets_sent, :packets_received,
:data_sent, :data_received
attr_accessor :sync_interval, :last_socket_error, :socket_error
def initialize
@uuid = SecureRandom.uuid
@read_queue = []
@@ -20,8 +22,11 @@ module TAC
@socket_error = false
@bound = false
@packets_sent, @packets_received = 0, 0
@data_sent, @data_received = 0, 0
@packets_sent = 0
@packets_received = 0
@data_sent = 0
@data_received = 0
end
def uuid=(id)
@@ -39,17 +44,15 @@ module TAC
Thread.new do
while connected?
# Read from socket
while message_in = read
if message_in.empty?
break
else
log.i(TAG, "Read: " + message_in)
while (message_in = read)
break if message_in.empty?
@read_queue << message_in
log.i(TAG, "Read: #{message_in}")
@packets_received += 1
@data_received += message_in.length
end
@read_queue << message_in
@packets_received += 1
@data_received += message_in.length
end
sleep @sync_interval / 1000.0
@@ -59,12 +62,12 @@ module TAC
Thread.new do
while connected?
# Write to socket
while message_out = @write_queue.shift
while (message_out = @write_queue.shift)
write(message_out)
@packets_sent += 1
@data_sent += message_out.to_s.length
log.i(TAG, "Write: " + message_out.to_s)
log.i(TAG, "Write: #{message_out}")
end
sleep @sync_interval / 1000.0
@@ -82,7 +85,7 @@ module TAC
while message
puts(message)
log.i(TAG, "Writing to Queue: " + message)
log.i(TAG, "Writing to Queue: #{message}")
message = gets
end
@@ -101,32 +104,29 @@ module TAC
end
def closed?
@socket.closed? if @socket
@socket&.closed?
end
def write(message)
begin
@socket.puts("#{message}#{PACKET_TAIL}")
rescue => error
@last_socket_error = error
@socket_error = true
log.e(TAG, error.message)
close
end
@socket.puts("#{message}#{PACKET_TAIL}") if connected?
rescue => error
@last_socket_error = error
@socket_error = true
log.e(TAG, error.message)
close
end
def read
begin
message = @socket.gets
rescue => error
@last_socket_error = error
@socket_error = true
@socket&.gets&.strip if connected?
rescue => error
@last_socket_error = error
@socket_error = true
message = ""
end
log.e(TAG, error.message)
return message.strip
close
end
def puts(message)
@@ -138,11 +138,11 @@ module TAC
end
def encode(message)
return message
message
end
def decode(blob)
return blob
blob
end
def flush
@@ -151,8 +151,9 @@ module TAC
def close(reason = nil)
write(reason) if reason
@socket.close if @socket
@socket&.close
end
end
end
end
end

View File

@@ -47,7 +47,7 @@ module TAC
def handle_handshake(packet)
if @host_is_a_connection
$window.backend.tacnet.client.uuid = packet.body
CyberarmEngine::Window.instance.backend.tacnet.client.uuid = packet.body
end
end
@@ -59,7 +59,7 @@ module TAC
def handle_error(packet)
if @host_is_a_connection
title, message = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2)
$window.push_state(TAC::Dialog::TACNETDialog, title: title, message: message)
CyberarmEngine::Window.instance.push_state(TAC::Dialog::TACNETDialog, title: title, message: message)
else
log.e(TAG, "Remote error: #{title}: #{message}")
end
@@ -73,11 +73,11 @@ module TAC
if data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION
File.open("#{TAC::CONFIGS_PATH}/#{config_name}.json", "w") { |f| f.write json }
if $window.backend.config&.name == config_name
$window.backend.load_config(config_name)
if CyberarmEngine::Window.instance.backend.config&.name == config_name
CyberarmEngine::Window.instance.backend.load_config(config_name)
end
else
$window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Supported config spec: v#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}")
CyberarmEngine::Window.instance.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Supported config spec: v#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}")
end
rescue JSON::ParserError => e
@@ -97,7 +97,7 @@ module TAC
end
if @host_is_a_connection
$window.backend.tacnet.puts(pkt)
CyberarmEngine::Window.instance.backend.tacnet.puts(pkt)
else
$server.active_client.puts(pkt)
end
@@ -119,20 +119,20 @@ module TAC
config = Config.new(name)
if config.configuration.revision < revision
$window.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
CyberarmEngine::Window.instance.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
elsif config.configuration.revision > revision
$window.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
CyberarmEngine::Window.instance.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
end
else
$window.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
CyberarmEngine::Window.instance.backend.tacnet.puts( PacketHandler.packet_download_config(name) )
end
end
_diff.each do |name|
config = Config.new(name)
$window.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
CyberarmEngine::Window.instance.backend.tacnet.puts( PacketHandler.packet_upload_config(name, JSON.dump( config )) )
end
else
if $server.active_client && $server.active_client.connected?
@@ -144,43 +144,43 @@ module TAC
def handle_select_config(packet)
config_name = packet.body
$window.backend.settings.config = config_name
$window.backend.save_settings
$window.backend.load_config(config_name)
CyberarmEngine::Window.instance.backend.settings.config = config_name
CyberarmEngine::Window.instance.backend.save_settings
CyberarmEngine::Window.instance.backend.load_config(config_name)
end
def handle_add_config(packet)
config_name = packet.body
if $window.backend.configs_list.include?(config_name)
if CyberarmEngine::Window.instance.backend.configs_list.include?(config_name)
unless @host_is_a_connection
if $server.active_client&.connected?
$server.active_client.puts(PacketHandler.packet_error("Config already exists!", "A config with the name #{config_name} already exists over here."))
end
end
else
$window.backend.write_new_config(config_name)
CyberarmEngine::Window.instance.backend.write_new_config(config_name)
end
end
def handle_update_config(packet)
old_config_name, new_config_name = packet.body.split(PROTOCOL_SEPERATOR, 2)
if $window.backend.configs_list.include?(config_name)
if CyberarmEngine::Window.instance.backend.configs_list.include?(config_name)
unless @host_is_a_connection
if $server.active_client&.connected?
$server.active_client.puts(PacketHandler.packet_error("Config already exists!", "A config with the name #{config_name} already exists over here."))
end
end
else
$window.backend.move_config(old_config_name, new_config_name)
CyberarmEngine::Window.instance.backend.move_config(old_config_name, new_config_name)
end
end
def handle_delete_config(packet)
config_name = packet.body
$window.backend.delete_config(config_name)
CyberarmEngine::Window.instance.backend.delete_config(config_name)
end
def self.packet_handshake(client_uuid)

View File

@@ -1,12 +1,15 @@
module TAC
THEME_FONT = "#{TAC::ROOT_PATH}/media/fonts/DejaVuSansCondensed.ttf"
THEME_FONT = "#{TAC::MEDIA_PATH}/fonts/NotoSans-Bold.ttf"
THEME_BOLD_FONT = "#{TAC::MEDIA_PATH}/fonts/NotoSans-Black.ttf"
THEME = {
Label: {
TextBlock: {
text_static: true,
font: THEME_FONT,
text_size: 22,
color: Gosu::Color.new(0xee_ffffff),
},
Button: {
font: THEME_BOLD_FONT,
text_size: 22,
background: TAC::Palette::TIMECRAFTERS_PRIMARY,
border_thickness: 1,
@@ -20,10 +23,11 @@ module TAC
},
EditLine: {
caret_color: Gosu::Color.new(0xff_88ef90),
font: THEME_FONT
},
ToggleButton: {
width: 18,
checkmark_image: "#{TAC::ROOT_PATH}/media/icons/checkmark.png",
checkmark_image: "#{TAC::MEDIA_PATH}/icons/checkmark.png",
},
}
@@ -31,10 +35,10 @@ module TAC
color: Gosu::Color.new(0xff_ffffff),
background: Gosu::Color.new(0xff_800000),
hover: {
background: Gosu::Color.new(0xff_600000),
background: Gosu::Color.new(0xff_c00000),
},
active: {
background: Gosu::Color.new(0xff_c00000),
background: Gosu::Color.new(0xff_600000),
}
}
@@ -49,6 +53,7 @@ module TAC
padding_top: THEME_ITEM_PADDING,
padding_bottom: THEME_ITEM_PADDING
}
THEME_HIGHLIGHTED_COLOR = Gosu::Color.rgb(255, 175, 0) # Gosu::Color.new(0xff_f080f0)
THEME_EVEN_COLOR = Gosu::Color.new(0xff_202020)
THEME_ODD_COLOR = Gosu::Color.new(0xff_606060)
THEME_CONTENT_BACKGROUND = Gosu::Color.new(0x88_007f3f)
@@ -60,4 +65,4 @@ module TAC
THEME_NOTIFICATION_BACKGROUND = Gosu::Color.new(0xff_102010)
THEME_NOTIFICATION_TITLE_COLOR = Gosu::Color::WHITE
THEME_NOTIFICATION_TAGLINE_COLOR = Gosu::Color::WHITE
end
end

View File

@@ -1,5 +1,6 @@
module TAC
NAME = "TimeCrafters Configuration Tool"
VERSION = "0.5.1"
RELEASE_NAME = "Beta"
end
RELEASE_DATE = "2024-01-19" # ISO 8601 Date of version release
VERSION = "0.8.0"
RELEASE_NAME = "BETA"
end

View File

@@ -5,12 +5,14 @@ module TAC
def initialize(**args)
super(**args)
self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME})"
self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME}) [#{TAC::RELEASE_DATE}]"
@backend = Backend.new
@notification_manager = GosuNotifications::NotificationManager.new(window: self, edge: :bottom)
@notification_manager = CyberarmEngine::NotificationManager.new(window: self, edge: :bottom)
if ARGV.join.include?("--game-clock-remote-display")
push_state(PracticeGameClock::View, remote_control_mode: true)
elsif ARGV.join.include?("--intro")
push_state(CyberarmEngine::IntroState, forward: TAC::States::Boot)
else
push_state(TAC::States::Boot)
end
@@ -24,14 +26,18 @@ module TAC
end
def update
super
@notification_manager.update
super
end
def needs_redraw?
states.any?(&:needs_repaint?) || @notification_manager.instance_variable_get(:@drivers).size.positive?
end
def toast(title, message = nil)
@notification_manager.create_notification(
priority: GosuNotifications::Notification::PRIORITY_HIGH,
priority: CyberarmEngine::Notification::PRIORITY_HIGH,
title: title,
tagline: message ? message : "",

View File

@@ -1,187 +0,0 @@
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
Bitstream Vera Fonts Copyright
------------------------------
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:
The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".
This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream
Vera" names.
The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or Bitstream
Inc., respectively. For further information, contact: fonts at gnome dot
org.
Arev Fonts Copyright
------------------------------
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of the fonts accompanying this license ("Fonts") and
associated documentation files (the "Font Software"), to reproduce
and distribute the modifications to the Bitstream Vera Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to
the following conditions:
The above copyright and trademark notices and this permission notice
shall be included in all copies of one or more of the Font Software
typefaces.
The Font Software may be modified, altered, or added to, and in
particular the designs of glyphs or characters in the Fonts may be
modified and additional glyphs or characters may be added to the
Fonts, only if the fonts are renamed to names not containing either
the words "Tavmjong Bah" or the word "Arev".
This License becomes null and void to the extent applicable to Fonts
or Font Software that has been modified and is distributed under the
"Tavmjong Bah Arev" names.
The Font Software may be sold as part of a larger software package but
no copy of one or more of the Font Software typefaces may be sold by
itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the name of Tavmjong Bah shall not
be used in advertising or otherwise to promote the sale, use or other
dealings in this Font Software without prior written authorization
from Tavmjong Bah. For further information, contact: tavmjong @ free
. fr.
TeX Gyre DJV Math
-----------------
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
(on behalf of TeX users groups) are in public domain.
Letters imported from Euler Fraktur from AMSfonts are (c) American
Mathematical Society (see below).
Bitstream Vera Fonts Copyright
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of the fonts accompanying this license (“Fonts”) and associated
documentation
files (the “Font Software”), to reproduce and distribute the Font Software,
including without limitation the rights to use, copy, merge, publish,
distribute,
and/or sell copies of the Font Software, and to permit persons to whom
the Font Software is furnished to do so, subject to the following
conditions:
The above copyright and trademark notices and this permission notice
shall be
included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional
glyphs or characters may be added to the Fonts, only if the fonts are
renamed
to names not containing either the words “Bitstream” or the word “Vera”.
This License becomes null and void to the extent applicable to Fonts or
Font Software
that has been modified and is distributed under the “Bitstream Vera”
names.
The Font Software may be sold as part of a larger software package but
no copy
of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
FOUNDATION
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
INABILITY TO USE
THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of GNOME, the GNOME
Foundation,
and Bitstream Inc., shall not be used in advertising or otherwise to promote
the sale, use or other dealings in this Font Software without prior written
authorization from the GNOME Foundation or Bitstream Inc., respectively.
For further information, contact: fonts at gnome dot org.
AMSFonts (v. 2.2) copyright
The PostScript Type 1 implementation of the AMSFonts produced by and
previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
available for general use. This has been accomplished through the
cooperation
of a consortium of scientific publishers with Blue Sky Research and Y&Y.
Members of this consortium include:
Elsevier Science IBM Corporation Society for Industrial and Applied
Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
In order to assure the authenticity of these fonts, copyright will be
held by
the American Mathematical Society. This is not meant to restrict in any way
the legitimate use of the fonts, such as (but not limited to) electronic
distribution of documents containing these fonts, inclusion of these fonts
into other public domain or commercial font collections or computer
applications, use of the outline data to create derivative fonts and/or
faces, etc. However, the AMS does require that the AMS copyright notice be
removed from any derivative versions of the fonts which have been altered in
any way. In addition, to ensure the fidelity of TeX documents using Computer
Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
has requested that any alterations which yield different font metrics be
given a different name.
$Id$

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

93
media/fonts/OFL.txt Normal file
View File

@@ -0,0 +1,93 @@
Copyright 2015-2021 Google LLC. All Rights Reserved.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

View File

@@ -1,10 +1,9 @@
begin
raise LoadError if defined?(Ocra)
raise LoadError if defined?(Ocra) || defined?(Ocran)
require_relative "../cyberarm_engine/lib/cyberarm_engine"
rescue LoadError
require "cyberarm_engine"
end
require "gosu_notifications"
require "socket"
require "securerandom"
require "json"
@@ -74,7 +73,7 @@ require_relative "lib/game_clock/net/packet"
USE_REDESIGN = ARGV.include?("--redesign")
BORDERLESS = ARGV.include?("--borderless")
if not defined?(Ocra)
unless defined?(Ocra) || defined?(Ocran)
TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true, borderless: BORDERLESS).show
end