mirror of
https://github.com/TimeCrafters/timecrafters_configuration_tool_desktop.git
synced 2025-12-16 13:52:34 +00:00
Compare commits
44 Commits
v0.1.0_ind
...
v0.4.0_bet
| Author | SHA1 | Date | |
|---|---|---|---|
| 606032c81d | |||
| 6185728ea7 | |||
| 02b16bab33 | |||
| 0facd003f0 | |||
| 93c6430028 | |||
| 3ee8881d43 | |||
| 81a827f0ac | |||
| 9b53bd8a70 | |||
| 2969e3df9e | |||
| 64d4f08410 | |||
| c24a9d9673 | |||
| 27bae38291 | |||
| 5fd494bb47 | |||
| 75d6de0b00 | |||
| 3cc4c204a7 | |||
| 9999026969 | |||
| 4b1fdd9baf | |||
| b625f53b6e | |||
| 8934462c46 | |||
| e27f1d1dca | |||
| ab55e8db5b | |||
| 988ef506ce | |||
| d340e94d46 | |||
| 9f54d21932 | |||
| 4e9d3c0759 | |||
| 8659bdfe4a | |||
| 22ab122604 | |||
| a1aa9a3396 | |||
| 8a61b6263b | |||
| 4adcff577a | |||
| d36211cb5e | |||
| 691cafb697 | |||
| fe480202ac | |||
| 28c159cc10 | |||
| af074ad57f | |||
| 5d267746dc | |||
| df0d0df223 | |||
| b811a83c60 | |||
| 9dc3caca0f | |||
| 08ada79e5b | |||
| d3fdc2d7dd | |||
| 1f37be3604 | |||
| f85426780b | |||
| 5e996019a9 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
pkg/*
|
||||
data/**/*.json
|
||||
data/settings.json
|
||||
data/simulator.rb
|
||||
data/simulator.rb
|
||||
data/*.csv
|
||||
8
.rubocop.yml
Normal file
8
.rubocop.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
Style/StringLiterals:
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 40
|
||||
|
||||
Style/EmptyMethod:
|
||||
EnforcedStyle: expanded
|
||||
9
Gemfile
9
Gemfile
@@ -1,3 +1,10 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "cyberarm_engine"
|
||||
gem "cyberarm_engine"
|
||||
gem "gosu_notifications"
|
||||
gem "ffi"
|
||||
gem "clipboard"
|
||||
|
||||
group :packaging do
|
||||
gem "ocra"
|
||||
end
|
||||
@@ -1 +1 @@
|
||||
# TimeCrafters Action Configurator for Desktop
|
||||
# TimeCrafters Configuration Tool for Desktop
|
||||
|
||||
19
Rakefile
Executable file
19
Rakefile
Executable file
@@ -0,0 +1,19 @@
|
||||
require "releasy"
|
||||
require 'bundler/setup' # Releasy requires that your application uses bundler.
|
||||
require_relative "lib/version"
|
||||
|
||||
Releasy::Project.new do
|
||||
name TAC::NAME
|
||||
version TAC::VERSION
|
||||
|
||||
executable "timecrafters_configuration_tool.rb"
|
||||
files ["lib/**/*.*", "media/**/*.*", "data"]
|
||||
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.
|
||||
add_package :exe # Windows self-extracting archive.
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,12 @@ module TAC
|
||||
@config.configuration.updated_at = Time.now
|
||||
@config.configuration.revision += 1
|
||||
@config_changed = true
|
||||
|
||||
save_config
|
||||
|
||||
if @tacnet.connected?
|
||||
upload_config(@config.name)
|
||||
end
|
||||
end
|
||||
|
||||
def config_changed?
|
||||
@@ -26,24 +32,49 @@ module TAC
|
||||
end
|
||||
end
|
||||
|
||||
def save_config(name)
|
||||
json = @config.to_json
|
||||
def save_config(name = nil, json = nil)
|
||||
name = @config.name unless name
|
||||
json = @config.to_json unless name && json
|
||||
|
||||
File.open("#{TAC::CONFIGS_PATH}/#{name}.json", "w") { |f| f.write json }
|
||||
|
||||
@config_changed = false
|
||||
end
|
||||
|
||||
def upload_config
|
||||
if @config && @tacnet.connected?
|
||||
json = @config.to_json
|
||||
@tacnet.puts(TAC::TACNET::PacketHandler.packet_upload_config(json))
|
||||
def move_config(old_name, new_name)
|
||||
if not File.exists?("#{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") &&
|
||||
!File.directory?("#{TAC::CONFIGS_PATH}/#{old_name}.json")
|
||||
# move_config: Config file "#{new_name}" already exist!
|
||||
return false
|
||||
end
|
||||
|
||||
return FileUtils.mv(
|
||||
"#{TAC::CONFIGS_PATH}/#{old_name}.json",
|
||||
"#{TAC::CONFIGS_PATH}/#{new_name}.json"
|
||||
)
|
||||
end
|
||||
|
||||
def delete_config(config_name)
|
||||
FileUtils.rm("#{TAC::CONFIGS_PATH}/#{config_name}.json") if File.exists?("#{TAC::CONFIGS_PATH}/#{config_name}.json")
|
||||
end
|
||||
|
||||
|
||||
def upload_config(config_name)
|
||||
if @tacnet.connected?
|
||||
json = Config.new(config_name).to_json
|
||||
@tacnet.puts( TAC::TACNET::PacketHandler.packet_upload_config(config_name, json) )
|
||||
end
|
||||
end
|
||||
|
||||
def download_config
|
||||
if @config && @tacnet.connected?
|
||||
@tacnet.puts(TAC::TACNET::PacketHandler.packet_download_config)
|
||||
def download_config(config_name)
|
||||
if @tacnet.connected?
|
||||
@tacnet.puts( TAC::TACNET::PacketHandler.packet_download_config(config_name) )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -106,7 +137,7 @@ module TAC
|
||||
data: {
|
||||
hostname: TACNET::DEFAULT_HOSTNAME,
|
||||
port: TACNET::DEFAULT_PORT,
|
||||
config: nil,
|
||||
config: "",
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module TAC
|
||||
class Config
|
||||
attr_reader :configuration, :groups, :presets
|
||||
attr_reader :name, :configuration, :groups, :presets
|
||||
def initialize(name)
|
||||
@name = name
|
||||
@configuration = nil
|
||||
@groups = nil
|
||||
@presets = nil
|
||||
@@ -109,23 +110,24 @@ module TAC
|
||||
end
|
||||
|
||||
class Action
|
||||
attr_accessor :name, :enabled
|
||||
attr_accessor :name, :comment, :enabled
|
||||
attr_reader :variables
|
||||
def initialize(name:, enabled:, variables:)
|
||||
@name, @enabled = name, enabled
|
||||
def initialize(name:, comment:, enabled:, variables:)
|
||||
@name, @comment, @enabled = name, comment, enabled
|
||||
@variables = variables
|
||||
end
|
||||
|
||||
def to_json(*args)
|
||||
{
|
||||
name: @name,
|
||||
comment: @comment,
|
||||
enabled: @enabled,
|
||||
variables: @variables
|
||||
}.to_json(*args)
|
||||
end
|
||||
|
||||
def self.from_json(hash)
|
||||
Action.new(name: hash[:name], enabled: hash[:enabled], variables: hash[:variables].map { |h| Variable.from_json(h) })
|
||||
Action.new(name: hash[:name], comment: hash[:comment], enabled: hash[:enabled], variables: hash[:variables].map { |h| Variable.from_json(h) })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -138,13 +140,48 @@ module TAC
|
||||
def to_json(*args)
|
||||
{
|
||||
name: @name,
|
||||
type: @type,
|
||||
value: @value
|
||||
value: "#{Variable.encode_type(@type)}x#{@value}"
|
||||
}.to_json(*args)
|
||||
end
|
||||
|
||||
def self.from_json(hash)
|
||||
Variable.new(name: hash[:name], type: hash[:type].to_sym, value: hash[:value])
|
||||
type, value = hash[:value].split("x", 2)
|
||||
type = Variable.decode_type(type)
|
||||
Variable.new(name: hash[:name], type: type, value: value)
|
||||
end
|
||||
|
||||
def self.encode_type(symbol)
|
||||
symbol.to_s.chars.first.upcase
|
||||
end
|
||||
|
||||
def self.decode_type(character)
|
||||
case character.upcase
|
||||
when "I"
|
||||
:integer
|
||||
when "F"
|
||||
:float
|
||||
when "D"
|
||||
:double
|
||||
when "L"
|
||||
:long
|
||||
when "S"
|
||||
:string
|
||||
when "B"
|
||||
:boolean
|
||||
end
|
||||
end
|
||||
|
||||
def self.decode_value(type, string)
|
||||
case type
|
||||
when "I", "L", :integer, :long
|
||||
Integer(string)
|
||||
when "F", "D", :float, :double
|
||||
Float(string)
|
||||
when "S", :string
|
||||
string
|
||||
when "B", :boolean
|
||||
string.downcase == "true"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,36 +2,43 @@ module TAC
|
||||
class Dialog < CyberarmEngine::GuiState
|
||||
def setup
|
||||
theme(THEME)
|
||||
background Gosu::Color.new(0x88_000000)
|
||||
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: 250, height: 400, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY] do
|
||||
@dialog_root = stack width: 0.25, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY] do
|
||||
# Title bar
|
||||
flow width: 1.0, height: 0.1 do
|
||||
@titlebar = flow width: 1.0 do
|
||||
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
|
||||
|
||||
# title
|
||||
flow width: 0.855 do
|
||||
label @title, text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
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
|
||||
|
||||
# Buttons
|
||||
flow width: 0.145 do
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/cross.png"), image_width: 24, **THEME_DANGER_BUTTON do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
# Dialog body
|
||||
stack width: 1.0, height: 0.9 do
|
||||
build
|
||||
@dialog_content = stack width: 1.0 do
|
||||
end
|
||||
end
|
||||
|
||||
@dialog_content.clear do
|
||||
build
|
||||
end
|
||||
|
||||
@root_container.recalculate
|
||||
@root_container.recalculate
|
||||
@root_container.recalculate
|
||||
|
||||
center_dialog
|
||||
end
|
||||
|
||||
@@ -39,8 +46,47 @@ module TAC
|
||||
end
|
||||
|
||||
def center_dialog
|
||||
@dialog_root.style.x = window.width / 2 - @dialog_root.style.width / 2
|
||||
@dialog_root.style.y = window.height / 2 - @dialog_root.style.height / 2
|
||||
@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
|
||||
|
||||
def try_commit
|
||||
end
|
||||
|
||||
def focus_next_element
|
||||
elements = []
|
||||
|
||||
_deep_dive_interactive_elements(@dialog_content, elements)
|
||||
|
||||
element_index = elements.find_index(self.focused)
|
||||
|
||||
if element_index && elements.size.positive?
|
||||
element = elements[element_index + 1]
|
||||
element ||= elements.first
|
||||
|
||||
if element
|
||||
request_focus(element)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
list << child
|
||||
elsif child.visible? && child.is_a?(CyberarmEngine::Element::Container)
|
||||
_deep_dive_interactive_elements(child, list)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def draw
|
||||
@@ -54,10 +100,25 @@ module TAC
|
||||
super
|
||||
|
||||
if window.width != @window_width or window.height != @window_height
|
||||
center_dialog
|
||||
request_recalculate
|
||||
|
||||
@window_width, @window_height = window.width, window.height
|
||||
end
|
||||
|
||||
center_dialog
|
||||
end
|
||||
|
||||
def button_down(id)
|
||||
super
|
||||
|
||||
case id
|
||||
when Gosu::KB_ENTER, Gosu::KB_RETURN
|
||||
try_commit
|
||||
when Gosu::KB_ESCAPE
|
||||
close
|
||||
when Gosu::KB_TAB
|
||||
focus_next_element
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
|
||||
69
lib/dialogs/action_dialog.rb
Normal file
69
lib/dialogs/action_dialog.rb
Normal file
@@ -0,0 +1,69 @@
|
||||
module TAC
|
||||
class Dialog
|
||||
class ActionDialog < Dialog
|
||||
def build
|
||||
background Gosu::Color::GRAY
|
||||
|
||||
label "Name", width: 1.0, text_align: :center
|
||||
@name_error = label "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.subscribe(:changed) do |sender, value|
|
||||
valid?
|
||||
end
|
||||
|
||||
label "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
|
||||
button "Cancel", width: 0.5 do
|
||||
close
|
||||
end
|
||||
|
||||
button @options[:action] ? @options[:accept_label] ? @options[:accept_label] : "Update" : "Add", width: 0.5 do |b|
|
||||
try_commit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
if valid?
|
||||
if @options[:action]
|
||||
@options[:callback_method].call(@options[:action], @name.value.strip, @comment.value.strip)
|
||||
else
|
||||
@options[:callback_method].call(@name.value.strip, @comment.value.strip)
|
||||
end
|
||||
|
||||
close
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
valid = true
|
||||
name = @name.value.strip
|
||||
|
||||
if name.empty?
|
||||
@name_error.value = "Error: Name cannot be blank or only whitespace."
|
||||
@name_error.show
|
||||
valid = false
|
||||
|
||||
### TODO: Handle case when renaming a cloned Action
|
||||
elsif !@options[:cloning] && @options[:action] && @options[:action].name == name
|
||||
@name_error.value = ""
|
||||
@name_error.hide
|
||||
|
||||
elsif @options[:list].find { |action| action.name == name}
|
||||
@name_error.value = "Error: Name is not unique!"
|
||||
@name_error.show
|
||||
valid = false
|
||||
|
||||
else
|
||||
@name_error.value = ""
|
||||
@name_error.hide
|
||||
end
|
||||
|
||||
return valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,10 +5,14 @@ module TAC
|
||||
background Gosu::Color::GRAY
|
||||
label @options[:message]
|
||||
|
||||
button "Close", width: 1.0 do
|
||||
close
|
||||
button "Close", width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
|
||||
try_commit
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
close
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,14 +2,34 @@ module TAC
|
||||
class Dialog
|
||||
class ConfirmDialog < Dialog
|
||||
def build
|
||||
@dangerous = @options[:dangerous]
|
||||
@dangerous ||= false
|
||||
|
||||
color = @dangerous ? Palette::DANGEROUS : Palette::ALERT
|
||||
|
||||
|
||||
@dialog_root.style.border_color = [ color, darken(color, 50) ]
|
||||
@titlebar.style.background = [ color, darken(color, 50) ]
|
||||
|
||||
background Gosu::Color::GRAY
|
||||
label @options[:message]
|
||||
|
||||
flow width: 1.0 do
|
||||
button "Cancel", width: 0.475 do
|
||||
flow width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
|
||||
button "Cancel", width: 0.5 do
|
||||
close
|
||||
end
|
||||
button "Okay", width: 0.475 do
|
||||
|
||||
button "Proceed", width: 0.5, **TAC::THEME_DANGER_BUTTON do
|
||||
try_commit(true)
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit(force = false)
|
||||
if !@dangerous
|
||||
close
|
||||
|
||||
@options[:callback_method].call
|
||||
elsif @dangerous && force
|
||||
close
|
||||
|
||||
@options[:callback_method].call
|
||||
|
||||
@@ -5,47 +5,58 @@ module TAC
|
||||
|
||||
def build
|
||||
background Gosu::Color::GRAY
|
||||
flow width: 1.0 do
|
||||
label "Name", width: 0.25
|
||||
@name = edit_line @options[:renaming] ? @options[:renaming].name : "", filter: method(:filter), width: 0.70
|
||||
end
|
||||
label "Name", width: 1.0, text_align: :center
|
||||
@name_error = label "", 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.subscribe(:changed) do |sender, value|
|
||||
valid?
|
||||
end
|
||||
|
||||
flow width: 1.0 do
|
||||
button "Cancel", width: 0.475 do
|
||||
flow width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
|
||||
button "Cancel", width: 0.5 do
|
||||
close
|
||||
end
|
||||
|
||||
accept_label = @options[:renaming] ? "Update" : "Add"
|
||||
accept_label = @options[:accept_label] if @options[:accept_label]
|
||||
|
||||
button accept_label, width: 0.475 do
|
||||
unless valid?
|
||||
else
|
||||
if @options[:renaming]
|
||||
@options[:callback_method].call(@options[:renaming], @name.value.strip)
|
||||
else
|
||||
@options[:callback_method].call(@name.value.strip)
|
||||
end
|
||||
|
||||
close
|
||||
end
|
||||
button accept_label, width: 0.5 do
|
||||
try_commit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
if valid?
|
||||
if @options[:renaming]
|
||||
@options[:callback_method].call(@options[:renaming], @name.value.strip)
|
||||
else
|
||||
@options[:callback_method].call(@name.value.strip)
|
||||
end
|
||||
|
||||
close
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
name = @name.value.strip
|
||||
|
||||
if @name.value.strip.empty?
|
||||
@name_error.value = "Name cannot be blank.\nName cannot only be whitespace."
|
||||
@name_error.value = "Name cannot be blank. Name cannot only be whitespace."
|
||||
@name_error.show
|
||||
|
||||
return false
|
||||
elsif @options[:list] && @options[:list].find { |i| i.name == @name.value.strip }
|
||||
|
||||
### TODO: Handle case when renaming a cloned Group
|
||||
# elsif @options[:renaming] && @options[:renaming].name == name
|
||||
# @name_error.value = ""
|
||||
# @name_error.hide
|
||||
|
||||
# return true
|
||||
|
||||
elsif @options[:list] && @options[:list].find { |i| i.name == name }
|
||||
@name_error.value = "Name is not unique!"
|
||||
@name_error.show
|
||||
|
||||
@@ -57,10 +68,6 @@ module TAC
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def filter(text)
|
||||
text.match(/[A-Za-z0-9._\- ]/) ? text : ""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
26
lib/dialogs/pick_preset_dialog.rb
Normal file
26
lib/dialogs/pick_preset_dialog.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module TAC
|
||||
class Dialog
|
||||
class PickPresetDialog < Dialog
|
||||
def build
|
||||
@limit = @options[:limit]
|
||||
|
||||
list = window.backend.config.presets.groups if @limit == :groups
|
||||
list = window.backend.config.presets.actions if @limit == :actions
|
||||
|
||||
background Gosu::Color::GRAY
|
||||
|
||||
stack(width: 1.0, height: 512, scroll: true) do
|
||||
list.each do |item|
|
||||
button item.name, width: 1.0 do
|
||||
close
|
||||
@options[:callback_method].call(item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
29
lib/dialogs/tacnet_dialog.rb
Normal file
29
lib/dialogs/tacnet_dialog.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
module TAC
|
||||
class Dialog
|
||||
class TACNETDialog < Dialog
|
||||
def build
|
||||
@dialog_root.style.border_color = [ Palette::TACNET_PRIMARY, Palette::TACNET_SECONDARY ]
|
||||
@titlebar.style.background = [ Palette::TACNET_PRIMARY, Palette::TACNET_SECONDARY ]
|
||||
|
||||
background Gosu::Color::GRAY
|
||||
label @options[:message], width: 1.0
|
||||
|
||||
@sound = Gosu::Sample.new("#{TAC::ROOT_PATH}/media/error_alarm.ogg").play(1, 1, true)
|
||||
|
||||
button "Close", width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
|
||||
try_commit
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
close
|
||||
end
|
||||
|
||||
def close
|
||||
super
|
||||
|
||||
@sound.stop
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
lib/dialogs/tacnet_status_dialog.rb
Normal file
28
lib/dialogs/tacnet_status_dialog.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module TAC
|
||||
class Dialog
|
||||
class TACNETStatusDialog < Dialog
|
||||
def build
|
||||
background Gosu::Color::GRAY
|
||||
@message_label = label $window.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
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
close
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@timer.update
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,55 +6,82 @@ module TAC
|
||||
|
||||
@type = @options[:variable].type if @options[:variable]
|
||||
|
||||
label "Name"
|
||||
label "Name", width: 1.0, text_align: :center
|
||||
@name_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
|
||||
@name_error.hide
|
||||
@name = edit_line @options[:variable] ? @options[:variable].name : ""
|
||||
@name = edit_line @options[:variable] ? @options[:variable].name : "", filter: method(:name_filter), width: 1.0, autofocus: true
|
||||
@name.subscribe(:changed) do |sender, value|
|
||||
valid?
|
||||
end
|
||||
|
||||
label "Type"
|
||||
label "Type", width: 1.0, text_align: :center
|
||||
@type_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
|
||||
@type_error.hide
|
||||
# TODO: Add dropdown menus to CyberarmEngine
|
||||
flow width: 1.0 do
|
||||
[:float, :double, :integer, :long, :string, :boolean].each do |btn|
|
||||
button btn do
|
||||
@type = btn
|
||||
@value_container.show
|
||||
end
|
||||
|
||||
@var_type = list_box items: [:float, :double, :integer, :long, :string, :boolean], choose: @type ? @type : :double, width: 1.0 do |item|
|
||||
@type = item.value.to_sym
|
||||
|
||||
if @type == :boolean
|
||||
@value.hide
|
||||
@value_boolean.show
|
||||
else
|
||||
@value.show
|
||||
@value_boolean.hide
|
||||
end
|
||||
|
||||
valid?
|
||||
end
|
||||
|
||||
@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
|
||||
@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
|
||||
|
||||
@value.subscribe(:changed) do |sender, value|
|
||||
valid?
|
||||
end
|
||||
|
||||
unless @options[:variable] && @options[:variable].type == :boolean
|
||||
@value_boolean.hide
|
||||
else
|
||||
@value.hide
|
||||
end
|
||||
end
|
||||
|
||||
@value_container = stack width: 1.0 do
|
||||
label "Value"
|
||||
@value_error = label "Error", color: TAC::Palette::TACNET_CONNECTION_ERROR
|
||||
@value_error.hide
|
||||
@value = edit_line @options[:variable] ? @options[:variable].value : ""
|
||||
end
|
||||
|
||||
flow width: 1.0 do
|
||||
button "Cancel", width: 0.475 do
|
||||
flow width: 1.0, margin_top: THEME_DIALOG_BUTTON_PADDING do
|
||||
button "Cancel", width: 0.5 do
|
||||
close
|
||||
end
|
||||
|
||||
button @options[:variable] ? "Update" : "Add", width: 0.475 do |b|
|
||||
if valid?
|
||||
if @options[:variable]
|
||||
@options[:callback_method].call(@options[:variable], @name.value.strip, @type, @value.value.strip)
|
||||
else
|
||||
@options[:callback_method].call(@name.value.strip, @type, @value.value.strip)
|
||||
end
|
||||
|
||||
close
|
||||
end
|
||||
button @options[:variable] ? "Update" : "Add", width: 0.5 do |b|
|
||||
try_commit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def try_commit
|
||||
if valid?
|
||||
value = @type == :boolean ? @value_boolean.value.to_s : @value.value.strip
|
||||
|
||||
if @options[:variable]
|
||||
@options[:callback_method].call(@options[:variable], @name.value.strip, @type, value)
|
||||
else
|
||||
@options[:callback_method].call(@name.value.strip, @type, value)
|
||||
end
|
||||
|
||||
close
|
||||
end
|
||||
end
|
||||
|
||||
def valid?
|
||||
valid = true
|
||||
|
||||
if @name.value.strip.empty?
|
||||
@name_error.value = "Error: Name cannot be blank\n or only whitespace."
|
||||
@name_error.value = "Error: Name cannot be blank or only whitespace."
|
||||
@name_error.show
|
||||
valid = false
|
||||
else
|
||||
@@ -73,22 +100,24 @@ module TAC
|
||||
|
||||
if [:integer, :float, :double, :long].include?(@type)
|
||||
if @value.value.strip.empty?
|
||||
@value_error.value = "Error: Value cannot be blank\n or only whitespace."
|
||||
@value_error.value = "Error: Numeric value cannot be blank or only whitespace."
|
||||
@value_error.show
|
||||
valid = false
|
||||
|
||||
elsif [:integer, :long].include?(@type)
|
||||
begin
|
||||
Integer(@value.value.strip)
|
||||
rescue
|
||||
@value_error.value = "Error: Invalid value,\nexpected whole number."
|
||||
@value_error.value = "Error: Invalid value, expected whole number."
|
||||
@value_error.show
|
||||
valid = false
|
||||
end
|
||||
|
||||
elsif [:float, :double].include?(@type)
|
||||
begin
|
||||
Float(@value.value.strip)
|
||||
rescue
|
||||
@value_error.value = "Error: Invalid value,\nexpected decimal number."
|
||||
@value_error.value = "Error: Invalid value, expected decimal number."
|
||||
@value_error.show
|
||||
valid = false
|
||||
end
|
||||
@@ -96,18 +125,18 @@ module TAC
|
||||
@value_error.value = ""
|
||||
@value_error.hide
|
||||
end
|
||||
|
||||
elsif @type == :string
|
||||
if @value.value.strip.empty?
|
||||
@value_error.value = "Error: Value cannot be blank\n or only whitespace."
|
||||
@value_error.value = "Error: Value cannot be blank or only whitespace."
|
||||
@value_error.show
|
||||
valid = false
|
||||
end
|
||||
|
||||
elsif @type == :boolean
|
||||
@value_error.value = "Error: Boolean not yet supported."
|
||||
@value_error.show
|
||||
valid = false
|
||||
|
||||
else
|
||||
@value_error.value = "Error: Type not set."
|
||||
@value_error.value = "Error: Type not set or type #{@type.inspect} is not valid."
|
||||
@value_error.show
|
||||
valid = false
|
||||
end
|
||||
@@ -116,4 +145,4 @@ module TAC
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
51
lib/page.rb
Normal file
51
lib/page.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
module TAC
|
||||
class Page
|
||||
include CyberarmEngine::DSL
|
||||
include CyberarmEngine::Common
|
||||
|
||||
attr_reader :menu_bar, :status_bar, :body
|
||||
|
||||
def initialize(host:)
|
||||
@host = host
|
||||
@header_bar_label = host.header_bar_label
|
||||
@menu_bar = host.menu_bar
|
||||
@status_bar = host.status_bar
|
||||
@body = host.body
|
||||
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def options=(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def page(klass, options = {})
|
||||
@host.page(klass, options)
|
||||
end
|
||||
|
||||
def header_bar(text)
|
||||
@header_bar_label.value = text
|
||||
end
|
||||
|
||||
def setup
|
||||
end
|
||||
|
||||
def focus
|
||||
end
|
||||
|
||||
def blur
|
||||
end
|
||||
|
||||
def draw
|
||||
end
|
||||
|
||||
def update
|
||||
end
|
||||
|
||||
def button_down(id)
|
||||
end
|
||||
|
||||
def button_up(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,44 +1,27 @@
|
||||
module TAC
|
||||
class States
|
||||
class ManageConfigurations < CyberarmEngine::GuiState
|
||||
class Pages
|
||||
class Configurations < Page
|
||||
def setup
|
||||
theme(THEME)
|
||||
stack width: 1.0, height: 0.1 do
|
||||
background THEME_HEADER_BACKGROUND
|
||||
label "#{TAC::NAME} ― Manage Configurations", bold: true, text_size: THEME_HEADING_TEXT_SIZE
|
||||
flow do
|
||||
button "Close" do
|
||||
if window.backend.settings.config
|
||||
window.backend.load_config(window.backend.settings.config)
|
||||
header_bar("Manage Configurations")
|
||||
|
||||
pop_state
|
||||
menu_bar.clear do
|
||||
button get_image("#{TAC::ROOT_PATH}/media/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)
|
||||
|
||||
window.current_state.refresh_config
|
||||
else
|
||||
push_state(Dialog::AlertDialog, title: "No Config Loaded", message: "A config must be loaded.")
|
||||
end
|
||||
end
|
||||
|
||||
label "Current Configuration: "
|
||||
@config_label = label window.backend.settings.config
|
||||
change_config(name)
|
||||
populate_configs
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
stack width: 1.0, height: 0.9 do
|
||||
background THEME_CONTENT_BACKGROUND
|
||||
flow do
|
||||
label "Configurations", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: 18, tip: "Add configuration" do
|
||||
push_state(Dialog::NamePromptDialog, title: "Config Name", callback_method: proc { |name|
|
||||
window.backend.write_new_config(name)
|
||||
status_bar.clear do
|
||||
label "Current Configuration: "
|
||||
@config_label = label window.backend.settings.config
|
||||
end
|
||||
|
||||
change_config(name)
|
||||
populate_configs
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@configs_list = stack width: 1.0 do
|
||||
body.clear do
|
||||
@configs_list = stack width: 1.0, height: 1.0, scroll: true do
|
||||
end
|
||||
end
|
||||
|
||||
@@ -58,6 +41,10 @@ module TAC
|
||||
|
||||
button "#{name}", width: 0.94 do
|
||||
change_config(name)
|
||||
|
||||
if window.backend.tacnet.connected?
|
||||
window.backend.tacnet.puts(TAC::TACNET::PacketHandler.packet_select_config(name))
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Rename configuration" do
|
||||
@@ -74,17 +61,21 @@ module TAC
|
||||
|
||||
populate_configs
|
||||
else
|
||||
push_state(Dialog::AlertDialog, title: "Config Rename Failed", message: "File already exists at\n#{TAC::CONFIGS_PATH}/#{new_name}.json}")
|
||||
push_state(Dialog::AlertDialog, title: "Config Rename Failed", message: "File already exists at #{TAC::CONFIGS_PATH}/#{new_name}.json}")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, **THEME_DANGER_BUTTON, tip: "Delete configuration" do
|
||||
push_state(Dialog::ConfirmDialog, title: "Delete Config?", callback_method: proc {
|
||||
push_state(Dialog::ConfirmDialog, title: "Delete Config?", dangerous: true, callback_method: proc {
|
||||
File.delete("#{TAC::CONFIGS_PATH}/#{name}.json")
|
||||
|
||||
if window.backend.settings.config == name
|
||||
change_config(nil)
|
||||
change_config("")
|
||||
end
|
||||
|
||||
if window.backend.tacnet.connected?
|
||||
window.backend.tacnet.puts(TAC::TACNET::PacketHandler.packet_delete_config(name))
|
||||
end
|
||||
|
||||
populate_configs
|
||||
@@ -98,6 +89,7 @@ module TAC
|
||||
def change_config(name)
|
||||
window.backend.settings.config = name
|
||||
window.backend.save_settings
|
||||
window.backend.load_config(name)
|
||||
|
||||
@config_label.value = name.to_s
|
||||
end
|
||||
204
lib/pages/drive_team_rotation_generator.rb
Normal file
204
lib/pages/drive_team_rotation_generator.rb
Normal file
@@ -0,0 +1,204 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class DriveTeamRotationGenerator < Page
|
||||
FILENAME = "#{TAC::ROOT_PATH}/data/drive_team_rotation.csv"
|
||||
|
||||
def setup
|
||||
header_bar("Drive Team Rotation Generator")
|
||||
|
||||
@roster ||= [
|
||||
"Alexander",
|
||||
"Aubrey",
|
||||
"Cayden",
|
||||
"Gabe",
|
||||
"Spencer",
|
||||
"Olivia"
|
||||
]
|
||||
|
||||
@roles ||= [
|
||||
"Coach",
|
||||
"Driver A",
|
||||
"Driver B"
|
||||
]
|
||||
|
||||
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
|
||||
export_rotation
|
||||
|
||||
@status_bar.clear do
|
||||
tagline "Saved to: #{FILENAME}"
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/target.png"), margin_right: 10, image_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
|
||||
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 do
|
||||
if @role_name.value.strip.length.positive?
|
||||
@roles.push(@role_name.value.strip)
|
||||
@role_name.value = ""
|
||||
|
||||
populate_roles
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@roles_container = stack(width: 1.0, height: 0.835, 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 do
|
||||
if @roster_name.value.strip.length.positive?
|
||||
@roster.push(@roster_name.value.strip)
|
||||
@roster_name.value = ""
|
||||
|
||||
populate_roster
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@roster_container = stack(width: 1.0, height: 0.835, scroll: true) do
|
||||
end
|
||||
end
|
||||
|
||||
stack(margin_left: 20, margin_right: 20, width: 0.5, 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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
populate_roles
|
||||
populate_roster
|
||||
populate_rotation
|
||||
end
|
||||
|
||||
def populate_roles
|
||||
@roles_container.clear do
|
||||
@roles.each_with_index do |name, i|
|
||||
flow(width: 1.0, padding: 2) do
|
||||
background i.even? ? 0xff_007000 : 0xff_006000
|
||||
|
||||
tagline name, width: 0.9
|
||||
button "<b>X</b>", width: 0.1, text_size: 18, **THEME_DANGER_BUTTON do
|
||||
@roles.delete(name)
|
||||
populate_roles
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def populate_roster
|
||||
@roster_container.clear do
|
||||
@roster.each_with_index do |name, i|
|
||||
flow(width: 1.0, padding: 2) do
|
||||
background i.even? ? 0xff_007000 : 0xff_006000
|
||||
|
||||
tagline name, width: 0.9
|
||||
button "<b>X</b>", width: 0.1, text_size: 18, **THEME_DANGER_BUTTON do
|
||||
@roster.delete(name)
|
||||
populate_roster
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def populate_rotation
|
||||
@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
|
||||
background Gosu::Color::BLACK
|
||||
|
||||
@roles.each do |role|
|
||||
tagline "<b>#{role}</b>", width: fraction
|
||||
end
|
||||
end
|
||||
|
||||
teams = @rotation.teams unless @shuffle_teams&.value
|
||||
teams = @rotation.teams.shuffle if @shuffle_teams&.value
|
||||
|
||||
teams.each_with_index do |team, i|
|
||||
flow(width: 1.0, padding: 2) do
|
||||
background i.even? ? 0xff_007000 : 0xff_006000
|
||||
|
||||
team.each do |player|
|
||||
tagline player, width: fraction
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def export_rotation
|
||||
return unless @rotation
|
||||
|
||||
buff = "#{@roles.join(',')}\n"
|
||||
|
||||
@rotation.teams.each do |team|
|
||||
buff += "#{team.join(",")}\n"
|
||||
end
|
||||
|
||||
buff.strip
|
||||
|
||||
File.write(FILENAME, buff)
|
||||
end
|
||||
end
|
||||
|
||||
class Generator
|
||||
attr_reader :roster, :team_size, :rounds, :teams, :schedule
|
||||
|
||||
def initialize(roster:, team_size: 4, rounds: 6)
|
||||
@roster = roster.clone
|
||||
@roster.freeze
|
||||
@team_size = team_size
|
||||
@rounds = rounds
|
||||
|
||||
@teams = []
|
||||
@schedule = []
|
||||
|
||||
generate
|
||||
end
|
||||
|
||||
def generate
|
||||
generate_teams
|
||||
generate_round_robin
|
||||
end
|
||||
|
||||
def generate_teams
|
||||
return unless @roster.size >= @team_size
|
||||
|
||||
list = @roster.dup
|
||||
|
||||
list.size.times do
|
||||
list.rotate!
|
||||
|
||||
@teams << list[0..@team_size - 1]
|
||||
end
|
||||
end
|
||||
|
||||
def generate_round_robin
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
420
lib/pages/editor.rb
Normal file
420
lib/pages/editor.rb
Normal file
@@ -0,0 +1,420 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class Editor < Page
|
||||
def setup
|
||||
header_bar("Editor")
|
||||
@active_group = nil
|
||||
@active_action = nil
|
||||
|
||||
menu_bar.clear do
|
||||
if @options[:group_is_preset]
|
||||
title "Editing group preset: #{@options[:group].name}"
|
||||
elsif @options[:action_is_preset]
|
||||
title "Editing action preset: #{@options[:action].name}"
|
||||
else
|
||||
title "Editing configuration: #{window.backend.config.name}"
|
||||
end
|
||||
end
|
||||
|
||||
status_bar.clear do
|
||||
flow(width: 0.3333) do
|
||||
label "Active group:", margin_right: 20
|
||||
@active_group_label = label ""
|
||||
end
|
||||
|
||||
flow(width: 0.3333) do
|
||||
label "Active action:", margin_right: 20
|
||||
@active_action_label = label ""
|
||||
end
|
||||
end
|
||||
|
||||
body.clear do
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
stack width: 0.33333, height: 1.0, border_thickness_right: 1, border_color: [0, Gosu::Color::BLACK, 0, 0] do
|
||||
@groups_menu = flow(width: 1.0) do
|
||||
label "Groups", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add group" do
|
||||
push_state(TAC::Dialog::NamePromptDialog, title: "Create Group", list: window.backend.config.groups, callback_method: method(:create_group))
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/button2.png"), image_width: THEME_ICON_SIZE, tip: "Clone currently selected group" do
|
||||
if @active_group
|
||||
push_state(Dialog::NamePromptDialog, title: "Clone Group", renaming: @active_group, accept_label: "Clone", list: window.backend.config.groups, callback_method: proc { |group, name|
|
||||
clone = TAC::Config::Group.from_json( JSON.parse( @active_group.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
window.backend.config.groups << clone
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
})
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to clone group, no group selected.")
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, tip: "Save group as preset" do
|
||||
if @active_group
|
||||
push_state(Dialog::NamePromptDialog, title: "Save Group Preset", renaming: @active_group, accept_label: "Save", list: window.backend.config.presets.groups, callback_method: proc { |group, name|
|
||||
clone = TAC::Config::Group.from_json( JSON.parse( @active_group.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
window.backend.config.presets.groups << clone
|
||||
window.backend.config.presets.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
window.toast("Saved Group Preset", "Saved preset: #{name}")
|
||||
})
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create group preset, no group selected.")
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/import.png"), image_width: THEME_ICON_SIZE, tip: "Import group from preset" do
|
||||
push_state(Dialog::PickPresetDialog, title: "Pick Group Preset", limit: :groups, callback_method: proc { |preset|
|
||||
push_state(Dialog::NamePromptDialog, title: "Name Group", renaming: preset, accept_label: "Add", list: window.backend.config.groups, callback_method: proc { |group, name|
|
||||
clone = TAC::Config::Group.from_json( JSON.parse( group.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
window.backend.config.groups << clone
|
||||
window.backend.config.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
})
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@groups_list = stack width: 1.0, scroll: true do
|
||||
end
|
||||
end
|
||||
|
||||
stack width: 0.33333, height: 1.0, border_thickness_right: 1, border_color: [0, Gosu::Color::BLACK, 0, 0] do
|
||||
@actions_menu = flow(width: 1.0) do
|
||||
label "Actions", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add action" do
|
||||
if @active_group
|
||||
push_state(TAC::Dialog::ActionDialog, title: "Create Action", list: @active_group.actions, callback_method: method(:create_action))
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create action, no group selected.")
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/button2.png"), image_width: THEME_ICON_SIZE, tip: "Clone currently selected action" do
|
||||
if @active_group && @active_action
|
||||
push_state(Dialog::ActionDialog, title: "Clone Action", action: @active_action, cloning: true, accept_label: "Clone", list: @active_group.actions, callback_method: proc { |action, name, comment|
|
||||
clone = TAC::Config::Action.from_json( JSON.parse( @active_action.to_json, symbolize_names: true ))
|
||||
clone.name = name
|
||||
clone.comment = comment
|
||||
@active_group.actions << clone
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
})
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to clone action, no action selected.")
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, tip: "Save action as preset" do
|
||||
if @active_action
|
||||
push_state(Dialog::NamePromptDialog, title: "Save Action Preset", renaming: @active_action, accept_label: "Save", list: window.backend.config.presets.actions, callback_method: proc { |action, name|
|
||||
clone = TAC::Config::Action.from_json( JSON.parse( @active_action.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
window.backend.config.presets.actions << clone
|
||||
window.backend.config.presets.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
window.toast("Saved Action Preset", "Saved preset: #{name}")
|
||||
})
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create action preset, no action selected.")
|
||||
end
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/import.png"), image_width: THEME_ICON_SIZE, tip: "Import action from preset" do
|
||||
if @active_group
|
||||
push_state(Dialog::PickPresetDialog, title: "Pick Action Preset", limit: :actions, callback_method: proc { |preset|
|
||||
push_state(Dialog::ActionDialog, title: "Name Action", action: preset, accept_label: "Add", list: @active_group.actions, callback_method: proc { |action, name|
|
||||
clone = TAC::Config::Action.from_json( JSON.parse( action.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
@active_group.actions << clone
|
||||
@active_group.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
})
|
||||
})
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to import action preset, no group selected.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@actions_list = stack width: 1.0, scroll: true do
|
||||
end
|
||||
end
|
||||
|
||||
stack width: 0.331, height: 1.0 do
|
||||
@variables_menu = flow(width: 1.0) do
|
||||
label "Variables", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add variable" do
|
||||
if @active_action
|
||||
push_state(TAC::Dialog::VariableDialog, title: "Create Variable", callback_method: method(:create_variable))
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create variable, no action selected.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@variables_list = stack width: 1.0, scroll: true do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
populate_groups_list
|
||||
|
||||
if @options[:group_is_preset]
|
||||
@active_group = @options[:group]
|
||||
@active_group_label.value = @active_group.name
|
||||
populate_actions_list(@active_group)
|
||||
|
||||
@groups_menu.hide
|
||||
|
||||
elsif @options[:action_is_preset]
|
||||
@active_action = @options[:action]
|
||||
@active_action_label.value = @active_action.name
|
||||
|
||||
populate_variables_list(@options[:action])
|
||||
|
||||
@groups_menu.hide
|
||||
@actions_menu.hide
|
||||
|
||||
else
|
||||
if @options[:group]
|
||||
@active_group = @options[:group]
|
||||
@active_group_label.value = @active_group.name
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
|
||||
if @options[:action]
|
||||
@active_action = @options[:action]
|
||||
@active_action_label.value = @active_action.name
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
|
||||
if @options[:variable]
|
||||
# Scroll into view?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
body.root.subscribe(:window_size_changed) do
|
||||
set_list_heights
|
||||
end
|
||||
end
|
||||
|
||||
def set_list_heights
|
||||
@groups_list.style.height = body.height - @groups_menu.height
|
||||
@actions_list.style.height = body.height - @actions_menu.height
|
||||
@variables_list.style.height = body.height - @variables_menu.height
|
||||
end
|
||||
|
||||
def create_group(name)
|
||||
window.backend.config.groups << TAC::Config::Group.new(name: name, actions: [])
|
||||
window.backend.config.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def update_group(group, name)
|
||||
group.name = name
|
||||
window.backend.config.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def delete_group(group)
|
||||
window.backend.config.groups.delete(group)
|
||||
window.backend.config.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
@active_group = nil
|
||||
@active_group_label.value = ""
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
@actions_list.clear
|
||||
@variables_list.clear
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def create_action(name, comment)
|
||||
@active_group.actions << TAC::Config::Action.new(name: name, comment: comment, enabled: true, variables: [])
|
||||
@active_group.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def update_action(action, name, comment)
|
||||
action.name = name
|
||||
action.comment = comment
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def delete_action(action)
|
||||
@active_group.actions.delete(action)
|
||||
@active_group.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
@variables_list.clear
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def create_variable(name, type, value)
|
||||
@active_action.variables << TAC::Config::Variable.new(name: name, type: type, value: value)
|
||||
@active_action.variables.sort_by! { |v| v.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def update_variable(variable, name, type, value)
|
||||
variable.name = name
|
||||
variable.type = type
|
||||
variable.value = value
|
||||
|
||||
@active_action.variables.sort_by! { |v| v.name.downcase }
|
||||
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def delete_variable(variable)
|
||||
@active_action.variables.delete(variable)
|
||||
@active_action.variables.sort_by! { |v| v.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def populate_groups_list
|
||||
@groups_list.scroll_top = 0
|
||||
|
||||
groups = []
|
||||
|
||||
unless @options[:group_is_preset] || @options[:action_is_preset]
|
||||
groups = window.backend.config.groups
|
||||
end
|
||||
|
||||
@groups_list.clear do
|
||||
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.8 do
|
||||
@active_group = group
|
||||
@active_group_label.value = group.name
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
|
||||
populate_actions_list(group)
|
||||
@variables_list.clear
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit group" do
|
||||
push_state(Dialog::NamePromptDialog, title: "Rename Group", renaming: group, list: window.backend.config.groups, callback_method: method(:update_group))
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete group", **THEME_DANGER_BUTTON do
|
||||
push_state(Dialog::ConfirmDialog, dangerous: true, title: "Are you sure?", message: "Delete group and all of its actions and variables?", callback_method: proc { delete_group(group) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_list_heights
|
||||
end
|
||||
|
||||
def populate_actions_list(group)
|
||||
@actions_list.scroll_top = 0
|
||||
|
||||
actions = group.actions
|
||||
|
||||
@actions_list.clear do
|
||||
actions.each_with_index do |action, i|
|
||||
stack width: 1.0, **THEME_ITEM_CONTAINER_PADDING do
|
||||
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
|
||||
|
||||
flow width: 1.0 do
|
||||
button action.name, width: 0.72 do
|
||||
@active_action = action
|
||||
@active_action_label.value = action.name
|
||||
|
||||
populate_variables_list(action)
|
||||
end
|
||||
|
||||
action_enabled_toggle = toggle_button tip: "Enable action", checked: action.enabled
|
||||
action_enabled_toggle.subscribe(:changed) do |sender, value|
|
||||
action.enabled = value
|
||||
window.backend.config_changed!
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit action" do
|
||||
push_state(Dialog::ActionDialog, title: "Rename Action", action: action, list: @active_group.actions, callback_method: method(:update_action))
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete action", **THEME_DANGER_BUTTON do
|
||||
push_state(Dialog::ConfirmDialog, dangerous: true, title: "Are you sure?", message: "Delete action and all of its variables?", callback_method: proc { delete_action(action) })
|
||||
end
|
||||
end
|
||||
|
||||
caption "#{action.comment}", width: 1.0, text_wrap: :word_wrap unless action.comment.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_list_heights
|
||||
end
|
||||
|
||||
def populate_variables_list(action)
|
||||
@variables_list.scroll_top = 0
|
||||
|
||||
variables = action.variables
|
||||
|
||||
@variables_list.clear do
|
||||
variables.each_with_index do |variable, i|
|
||||
stack width: 1.0, **THEME_ITEM_CONTAINER_PADDING do
|
||||
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
|
||||
|
||||
flow(width: 1.0) do
|
||||
button "#{variable.name}", width: 0.89, tip: "Edit variable" do
|
||||
push_state(Dialog::VariableDialog, title: "Edit Variable", variable: variable, callback_method: method(:update_variable))
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete variable", **THEME_DANGER_BUTTON do
|
||||
push_state(Dialog::ConfirmDialog, title: "Are you sure?", message: "Delete variable?", callback_method: proc { delete_variable(variable) })
|
||||
end
|
||||
end
|
||||
|
||||
caption "Type: #{variable.type}"
|
||||
caption "Value: #{variable.value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_list_heights
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
226
lib/pages/field_planner.rb
Normal file
226
lib/pages/field_planner.rb
Normal file
@@ -0,0 +1,226 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class FieldPlanner < Page
|
||||
def setup
|
||||
header_bar("Field Planner")
|
||||
|
||||
menu_bar.clear do
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
button "Inches", text_size: THEME_HEADING_TEXT_SIZE do
|
||||
@unit = :inches
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
button "Feet", text_size: THEME_HEADING_TEXT_SIZE do
|
||||
@unit = :feet
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
button "Millimeters", text_size: THEME_HEADING_TEXT_SIZE do
|
||||
@unit = :millimeters
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
button "Centimeters", text_size: THEME_HEADING_TEXT_SIZE do
|
||||
@unit = :centimeters
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
button "Meters", text_size: THEME_HEADING_TEXT_SIZE do
|
||||
@unit = :meters
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
button "Reset", text_size: THEME_HEADING_TEXT_SIZE, **THEME_DANGER_BUTTON do
|
||||
@nodes.clear
|
||||
refresh_panel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
status_bar.clear do
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
tagline "Nodes:"
|
||||
@nodes_count_label = tagline "0"
|
||||
|
||||
tagline "Total Distance:"
|
||||
@total_distance_label = tagline "0"
|
||||
|
||||
@units_label = tagline "Inches"
|
||||
end
|
||||
end
|
||||
|
||||
body.clear do
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
@field_container = stack width: 0.5, height: 1.0 do
|
||||
background 0xff_111111
|
||||
end
|
||||
|
||||
@points_container = stack width: 0.5, height: 1.0 do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@field = TAC::Simulator::Field.new(container: @field_container, season: :freight_frenzy, simulation: nil)
|
||||
@nodes ||= []
|
||||
@unit = :inches
|
||||
@total_distance = 0
|
||||
|
||||
@node_color = 0xff_00f000
|
||||
@node_hover_color = Gosu::Color::YELLOW
|
||||
@segment_color = 0xaa_00f000
|
||||
@node_radius = 6
|
||||
@segment_thickness = 2
|
||||
|
||||
measure_path
|
||||
refresh_panel
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
@field.draw
|
||||
|
||||
display_path
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@field.update
|
||||
|
||||
measure_path
|
||||
end
|
||||
|
||||
def button_down(id)
|
||||
super
|
||||
|
||||
if @field_container.hit?(window.mouse_x, window.mouse_y)
|
||||
x = (window.mouse_x - @field_container.x) / @field.scale
|
||||
y = (window.mouse_y - @field_container.y) / @field.scale
|
||||
|
||||
case id
|
||||
when Gosu::MS_LEFT # Add Node
|
||||
@nodes << CyberarmEngine::Vector.new(x, y, 0)
|
||||
|
||||
measure_path
|
||||
|
||||
refresh_panel
|
||||
|
||||
when Gosu::MS_RIGHT # Delete Node
|
||||
result = @nodes.find do |node|
|
||||
Gosu.distance(node.x, node.y, x, y) <= @node_radius * 0.25
|
||||
end
|
||||
|
||||
@nodes.delete(result) if result
|
||||
|
||||
measure_path
|
||||
|
||||
refresh_panel
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def display_path
|
||||
x = (window.mouse_x - @field_container.x) / @field.scale
|
||||
y = (window.mouse_y - @field_container.y) / @field.scale
|
||||
|
||||
last_node = @nodes.first
|
||||
|
||||
@nodes.each_with_index do |current_node, i|
|
||||
mouse_near = Gosu.distance(current_node.x, current_node.y, x, y) <= @node_radius * 0.25
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
next if i.zero?
|
||||
|
||||
angle = Gosu.angle(
|
||||
last_node.x * @field.scale,
|
||||
last_node.y * @field.scale,
|
||||
current_node.x * @field.scale,
|
||||
current_node.y * @field.scale
|
||||
)
|
||||
|
||||
distance = Gosu.distance(last_node.x, last_node.y, current_node.x, current_node.y) * @field.scale
|
||||
|
||||
Gosu.rotate(angle, last_node.x * @field.scale + @field_container.x, last_node.y * @field.scale + @field_container.y) do
|
||||
Gosu.draw_rect(
|
||||
(@field_container.x + last_node.x * @field.scale) - (@segment_thickness / 2.0),
|
||||
(@field_container.y + last_node.y * @field.scale) - distance,
|
||||
@segment_thickness,
|
||||
distance,
|
||||
@segment_color
|
||||
)
|
||||
end
|
||||
|
||||
last_node = current_node
|
||||
end
|
||||
end
|
||||
|
||||
def measure_path
|
||||
@total_distance = 0
|
||||
|
||||
v1 = @nodes.first
|
||||
@nodes.each_with_index do |v2, i|
|
||||
next if i.zero?
|
||||
|
||||
@total_distance += Gosu.distance(
|
||||
v1.x + @field_container.x,
|
||||
v1.y + @field_container.y,
|
||||
v2.x + @field_container.x,
|
||||
v2.y + @field_container.y
|
||||
)
|
||||
|
||||
v1 = v2
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_panel
|
||||
@nodes_count_label.value = "#{@nodes.count}"
|
||||
@total_distance_label.value = "#{inches_to_unit(@total_distance).round(2)}"
|
||||
@units_label.value = @unit.to_s.capitalize
|
||||
|
||||
# @points_container.clear do
|
||||
# v1 = @nodes.first
|
||||
# break unless v1
|
||||
|
||||
# para "Vector #{inches_to_unit(v1.x).round}:#{inches_to_unit(v1.y).round} - 0 #{@unit.to_s.capitalize}"
|
||||
|
||||
# @nodes.each_with_index do |v2, i|
|
||||
# next if i.zero?
|
||||
|
||||
# distance = Gosu.distance(
|
||||
# v1.x + @field_container.x,
|
||||
# v1.y + @field_container.y,
|
||||
# v2.x + @field_container.x,
|
||||
# v2.y + @field_container.y
|
||||
# )
|
||||
|
||||
# para "Vector #{inches_to_unit(v1.x).round}:#{inches_to_unit(v1.y).round} - #{inches_to_unit(distance).round(2)} #{@unit.to_s.capitalize}"
|
||||
|
||||
# v1 = v2
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
def inches_to_unit(inches)
|
||||
case @unit
|
||||
when :inches
|
||||
inches
|
||||
when :feet
|
||||
inches / 12.0
|
||||
when :millimeters
|
||||
inches / 0.254
|
||||
when :centimeters
|
||||
inches / 2.54
|
||||
when :meters
|
||||
inches / 25.4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
42
lib/pages/home.rb
Normal file
42
lib/pages/home.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class Home < Page
|
||||
def setup
|
||||
header_bar(TAC::NAME)
|
||||
|
||||
body.clear do
|
||||
stack(width: 1.0, height: 1.0) do
|
||||
label 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
|
||||
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?"
|
||||
else
|
||||
label "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}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
125
lib/pages/presets.rb
Normal file
125
lib/pages/presets.rb
Normal file
@@ -0,0 +1,125 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class Presets < Page
|
||||
def setup
|
||||
header_bar("Manage Presets")
|
||||
|
||||
status_bar.clear do
|
||||
tagline "Group Presets", width: 0.495
|
||||
tagline "Action Presets", width: 0.495
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@action_presets = stack(width: 0.49995, height: 1.0, scroll: true) do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
populate_group_presets
|
||||
populate_action_presets
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_group_preset(group, name)
|
||||
group.name = name
|
||||
window.backend.config.presets.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_group_presets
|
||||
end
|
||||
|
||||
def delete_group_preset(group)
|
||||
window.backend.config.presets.groups.delete(group)
|
||||
window.backend.config.presets.groups.sort_by! { |g| g.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_group_presets
|
||||
end
|
||||
|
||||
def update_action_preset(action, name)
|
||||
action.name = name
|
||||
window.backend.config.presets.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_action_presets
|
||||
end
|
||||
|
||||
def delete_action_preset(action)
|
||||
window.backend.config.presets.actions.delete(action)
|
||||
window.backend.config.presets.actions.sort_by! { |a| a.name.downcase }
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_action_presets
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
340
lib/pages/search.rb
Normal file
340
lib/pages/search.rb
Normal file
@@ -0,0 +1,340 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class Search < Page
|
||||
def setup
|
||||
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
|
||||
unless search.value.strip.empty?
|
||||
search_results = search_config(search.value.downcase.strip)
|
||||
|
||||
status_bar.clear do
|
||||
if search_results.results.size.zero?
|
||||
subtitle "No results for: \"#{search.value.strip}\""
|
||||
else
|
||||
subtitle "Search results for: \"#{search.value.strip}\""
|
||||
end
|
||||
end
|
||||
|
||||
body.clear do
|
||||
shared_index = 0
|
||||
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
stack(width: 0.495, height: 1.0, scroll: true) do
|
||||
if search_results.groups.size.positive?
|
||||
title "Groups"
|
||||
|
||||
search_results.groups.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, is_search: true })
|
||||
end
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
end
|
||||
end
|
||||
|
||||
if search_results.actions.size.positive?
|
||||
title "Actions"
|
||||
|
||||
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
|
||||
page(TAC::Pages::Editor, { group: result.group, action: result.action, is_search: true })
|
||||
end
|
||||
|
||||
if result.from_comment?
|
||||
para result.highlight(result.action.comment), width: 1.0
|
||||
end
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
end
|
||||
end
|
||||
|
||||
if search_results.variables.size.positive?
|
||||
title "Variables"
|
||||
|
||||
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
|
||||
page(TAC::Pages::Editor, { group: result.group, action: result.action, variable: result.variable, is_search: true })
|
||||
end
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
stack(width: 0.495, height: 1.0, scroll: true) 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
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
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
|
||||
if result.group.nil?
|
||||
page(TAC::Pages::Editor, { action: result.action, action_is_preset: true, is_search: true })
|
||||
else
|
||||
page(TAC::Pages::Editor, { group: result.group, action: result.action, group_is_preset: true, is_search: true })
|
||||
end
|
||||
end
|
||||
|
||||
if result.from_comment?
|
||||
para result.highlight(result.action.comment), width: 1.0
|
||||
end
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
end
|
||||
end
|
||||
|
||||
if search_results.variables_from_presets.size.positive?
|
||||
title "Variables from Presets"
|
||||
|
||||
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
|
||||
if result.group.nil?
|
||||
page(TAC::Pages::Editor, { action: result.action, variable: result.variable, action_is_preset: true, is_search: true })
|
||||
else
|
||||
page(TAC::Pages::Editor, { group: result.group, action: result.action, variable: result.variable, group_is_preset: true, is_search: true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_config(query)
|
||||
search_results = SearchResults.new
|
||||
|
||||
search_groups(query, search_results)
|
||||
search_actions(query, search_results)
|
||||
search_variables(query, search_results)
|
||||
search_presets(query, search_results)
|
||||
|
||||
return search_results
|
||||
end
|
||||
|
||||
def search_groups(query, search_results)
|
||||
window.backend.config.groups.each do |group|
|
||||
if group.name.downcase.include?(query)
|
||||
result = SearchResult.new(group: group, query: query, is_group: true, is_from_name: true)
|
||||
search_results.results << result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_actions(query, search_results)
|
||||
window.backend.config.groups.each do |group|
|
||||
group.actions.each do |action|
|
||||
if action.name.downcase.include?(query)
|
||||
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)
|
||||
result = SearchResult.new(group: group, action: action, query: query, is_action: true, is_from_comment: true)
|
||||
search_results.results << result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_variables(query, search_results)
|
||||
window.backend.config.groups.each do |group|
|
||||
group.actions.each do |action|
|
||||
action.variables.each do |variable|
|
||||
if variable.name.downcase.include?(query)
|
||||
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)
|
||||
result = SearchResult.new(group: group, action: action, variable: variable, is_variable: true, query: query, is_from_value: true)
|
||||
search_results.results << result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def search_presets(query, search_results)
|
||||
window.backend.config.presets.groups.each do |group|
|
||||
if group.name.downcase.include?(query)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
end
|
||||
end
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
class SearchResults
|
||||
attr_reader :results
|
||||
|
||||
def initialize
|
||||
@results = []
|
||||
end
|
||||
|
||||
def groups
|
||||
@results.select { |result| result.group? && !result.preset? }
|
||||
end
|
||||
|
||||
def actions
|
||||
@results.select { |result| result.action? && !result.preset? }
|
||||
end
|
||||
|
||||
def variables
|
||||
@results.select { |result| result.variable? && !result.preset? }
|
||||
end
|
||||
|
||||
def group_presets
|
||||
@results.select { |result| result.group? && result.preset? }
|
||||
end
|
||||
|
||||
def action_presets
|
||||
@results.select { |result| result.action? && result.preset? }
|
||||
end
|
||||
|
||||
def variables_from_presets
|
||||
@results.select { |result| result.variable? && result.preset? }
|
||||
end
|
||||
end
|
||||
|
||||
class SearchResult
|
||||
attr_reader :group, :action, :variable, :query
|
||||
|
||||
def initialize(query:, group:, action: nil, variable: nil,
|
||||
is_group: false, is_action: false, is_variable: false,
|
||||
is_from_name: false, is_from_value: false, is_from_comment: false, is_preset: false)
|
||||
@group = group
|
||||
@action = action
|
||||
@variable = variable
|
||||
@query = query
|
||||
|
||||
@is_group = is_group
|
||||
@is_action = is_action
|
||||
@is_variable = is_variable
|
||||
|
||||
@is_from_name = is_from_name
|
||||
@is_from_value = is_from_value
|
||||
@is_from_comment = is_from_comment
|
||||
@is_preset = is_preset
|
||||
end
|
||||
|
||||
def group?
|
||||
@is_group
|
||||
end
|
||||
|
||||
def action?
|
||||
@is_action
|
||||
end
|
||||
|
||||
def variable?
|
||||
@is_variable
|
||||
end
|
||||
|
||||
def from_name?
|
||||
@is_from_name
|
||||
end
|
||||
|
||||
def from_value?
|
||||
@is_from_value
|
||||
end
|
||||
|
||||
def from_comment?
|
||||
@is_from_comment
|
||||
end
|
||||
|
||||
def preset?
|
||||
@is_preset
|
||||
end
|
||||
|
||||
def highlight(string)
|
||||
string.gsub(/#{@query}/i, "<b><c=ff00ff>#{@query}</c></b>")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
85
lib/pages/simulator.rb
Normal file
85
lib/pages/simulator.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class Simulator < Page
|
||||
SOURCE_FILE_PATH = "#{TAC::ROOT_PATH}/data/simulator.rb"
|
||||
def setup
|
||||
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
|
||||
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
|
||||
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
|
||||
@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
|
||||
save_source
|
||||
end
|
||||
end
|
||||
|
||||
status_bar.clear do
|
||||
@simulation_status = label ""
|
||||
end
|
||||
|
||||
body.clear do
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
@field_container = stack width: 0.4, height: 1.0 do
|
||||
background 0xff_111111
|
||||
end
|
||||
|
||||
stack(width: 0.6, height: 1.0) do
|
||||
source_code =
|
||||
"robot = create_robot(alliance: :blue, width: 18, depth: 18)
|
||||
robot.backward 100
|
||||
robot.turn 90
|
||||
robot.forward 100
|
||||
robot.turn -90
|
||||
robot.forward 100
|
||||
robot.turn -90
|
||||
robot.forward 100"
|
||||
|
||||
source_code = File.read(SOURCE_FILE_PATH) if File.exists?(SOURCE_FILE_PATH)
|
||||
|
||||
@source_code = edit_box source_code, width: 1.0, height: 1.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def save_source
|
||||
File.open(SOURCE_FILE_PATH, "w") { |f| f.write @source_code.value }
|
||||
@simulation_status.value = "Saved source to #{SOURCE_FILE_PATH}"
|
||||
end
|
||||
|
||||
def blur
|
||||
@simulation.robots.each { |robot| robot.queue.clear } if @simulation
|
||||
save_source
|
||||
end
|
||||
|
||||
def draw
|
||||
@simulation.draw if @simulation
|
||||
end
|
||||
|
||||
def update
|
||||
return unless @simulation
|
||||
|
||||
@simulation.update
|
||||
|
||||
unless @simulation.robots.all? { |robot| robot.queue.empty? } # Only update clock if simulation is running
|
||||
@simulation_status.value = "Time: #{(@simulation.simulation_time).round(1)} seconds"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
76
lib/pages/tacnet.rb
Normal file
76
lib/pages/tacnet.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
module TAC
|
||||
class Pages
|
||||
class TACNET < Page
|
||||
def setup
|
||||
header_bar("TimeCrafters Auxiliary Configuration Network")
|
||||
|
||||
menu_bar.clear do
|
||||
@connect_menu = flow(width: 1.0, height: 1.0) do
|
||||
label "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
|
||||
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
|
||||
window.backend.settings_changed!
|
||||
end
|
||||
|
||||
window.backend.settings.hostname = hostname.value
|
||||
window.backend.settings.port = port.value.to_i
|
||||
|
||||
window.backend.tacnet.connect(hostname.value, port.value.to_i)
|
||||
end
|
||||
end
|
||||
|
||||
@disconnect_menu = flow(width: 1.0, height: 1.0) do
|
||||
button "Disconnect", height: 1.0, text_size: 28 do
|
||||
window.backend.tacnet.close
|
||||
end
|
||||
end
|
||||
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
|
||||
end
|
||||
|
||||
body.clear do
|
||||
@full_status_label = label ""
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
case window.backend.tacnet.status
|
||||
when :connected
|
||||
sent = "#{window.backend.tacnet.client.packets_sent}".rjust(4, '0')
|
||||
received = "#{window.backend.tacnet.client.packets_received}".rjust(4, '0')
|
||||
@status_label.value = "TACNET: Connected Pkt Sent: #{sent} Pkt Received: #{received}"
|
||||
@full_status_label.value = window.backend.tacnet.full_status
|
||||
@tacnet_icon.style.color = TAC::Palette::TACNET_CONNECTED
|
||||
@connect_menu.hide
|
||||
@disconnect_menu.show
|
||||
|
||||
when :connecting
|
||||
@status_label.value = "TACNET: Connecting..."
|
||||
@tacnet_icon.style.color = TAC::Palette::TACNET_CONNECTING
|
||||
@connect_menu.hide
|
||||
@disconnect_menu.show
|
||||
|
||||
when :connection_error
|
||||
@status_label.value = "TACNET: Connection Error"
|
||||
@full_status_label.value = window.backend.tacnet.full_status
|
||||
@tacnet_icon.style.color = TAC::Palette::TACNET_CONNECTION_ERROR
|
||||
@connect_menu.show
|
||||
@disconnect_menu.hide
|
||||
|
||||
when :not_connected
|
||||
@status_label.value = "TACNET: Not Connected"
|
||||
@full_status_label.value = ""
|
||||
@tacnet_icon.style.color = 0xff_ffffff
|
||||
@connect_menu.show
|
||||
@disconnect_menu.hide
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,8 +12,8 @@ module TAC
|
||||
BLUE_ALLIANCE = Gosu::Color.new(0xff_000080)
|
||||
RED_ALLIANCE = Gosu::Color.new(0xff_800000)
|
||||
|
||||
TACNET_PRIMARY = Gosu::Color.new(0xff_003f7f)
|
||||
TACNET_SECONDARY = Gosu::Color.new(0xff_007f7f)
|
||||
TACNET_PRIMARY = Gosu::Color.new(0xff000080)
|
||||
TACNET_SECONDARY = Gosu::Color.new(0xff000060)
|
||||
|
||||
GROUPS_PRIMARY = Gosu::Color.new(0xff_444444)
|
||||
GROUPS_SECONDARY = Gosu::Color.new(0xff_444444)
|
||||
@@ -26,5 +26,8 @@ module TAC
|
||||
|
||||
EDITOR_PRIMARY = Gosu::Color.new(0xff_446688)
|
||||
EDITOR_SECONDARY = Gosu::Color.new(0xff_224466)
|
||||
|
||||
ALERT = TACNET_CONNECTING
|
||||
DANGEROUS = TACNET_CONNECTION_ERROR
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,8 @@
|
||||
module TAC
|
||||
class Simulator
|
||||
class Field
|
||||
attr_reader :scale
|
||||
|
||||
def initialize(container:, season:, simulation:)
|
||||
@container = container
|
||||
@season = season
|
||||
@@ -9,21 +11,23 @@ module TAC
|
||||
@position = CyberarmEngine::Vector.new
|
||||
@scale = 1
|
||||
@size = 0
|
||||
@field_size = 144 # inches [1 pxel = 1 inch]
|
||||
@field_size = 144 # inches [1 pixel = 1 inch]
|
||||
|
||||
@blue = Gosu::Color.new(0xff_004080)
|
||||
@red = Gosu::Color.new(0xff_800000)
|
||||
end
|
||||
|
||||
def draw
|
||||
Gosu.flush
|
||||
|
||||
Gosu.clip_to(@position.x, @position.y, @size, @size) do
|
||||
Gosu.translate(@position.x, @position.y) do
|
||||
draw_field
|
||||
Gosu.scale(@scale) do
|
||||
self.send(:"draw_field_#{@season}")
|
||||
|
||||
@simulation.robots.each(&:draw)
|
||||
@simulation.robots.each { |robot| robot.queue.first.draw if robot.queue.first && @simulation.show_paths }
|
||||
@simulation&.robots&.each(&:draw)
|
||||
@simulation&.robots&.each { |robot| robot.queue.first.draw if robot.queue.first && @simulation.show_paths }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -94,9 +98,185 @@ module TAC
|
||||
end
|
||||
end
|
||||
|
||||
def draw_field_ultimate_goal
|
||||
# middle line
|
||||
Gosu.draw_rect(0, @field_size / 2 - 13, @field_size, 2, Gosu::Color::WHITE)
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
# blue wobbly wobs
|
||||
Gosu.draw_circle(24, @field_size - 24, 4, 32, @blue)
|
||||
Gosu.draw_circle(48, @field_size - 24, 4, 32, @blue)
|
||||
|
||||
# blue starter stack
|
||||
Gosu.draw_rect(36 - 1, @field_size - 50, 2, 2, @blue)
|
||||
|
||||
# blue target zones
|
||||
# A
|
||||
draw_tile_box(@blue)
|
||||
|
||||
# B
|
||||
Gosu.translate(24, 24) do
|
||||
draw_tile_box(@blue)
|
||||
end
|
||||
|
||||
# C
|
||||
Gosu.translate(0, 48) do
|
||||
draw_tile_box(@blue)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# red starter stack
|
||||
Gosu.draw_rect(@field_size - 37, @field_size - 50, 2, 2, @red)
|
||||
|
||||
# red target zones
|
||||
# A
|
||||
Gosu.translate(@field_size - 24, 0) do
|
||||
draw_tile_box(@red)
|
||||
end
|
||||
|
||||
# B
|
||||
Gosu.translate(@field_size - 48, 24) do
|
||||
draw_tile_box(@red)
|
||||
end
|
||||
|
||||
# C
|
||||
Gosu.translate(@field_size - 24, 48) do
|
||||
draw_tile_box(@red)
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# blue wobble goal
|
||||
Gosu.draw_circle(48, 84, 9, 32, @blue)
|
||||
|
||||
# blue shared wobble goal
|
||||
Gosu.draw_circle(@field_size / 2, 24, 9, 32, @blue)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# red wobble goal
|
||||
Gosu.draw_circle(@field_size - 48, 84, 9, 32, @red)
|
||||
|
||||
# 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
|
||||
|
||||
# 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)
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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)
|
||||
else
|
||||
Gosu.draw_circle(x * 3 + 2, y * 3 + 2, 1, 16, faint_white)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
else
|
||||
Gosu.draw_circle((@field_size - 4) - x * 3 + 2, y * 3 + 2, 1, 16, faint_white)
|
||||
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)
|
||||
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)
|
||||
end
|
||||
|
||||
def update
|
||||
@position.x, @position.y = @container.x, @container.y
|
||||
@size = @container.width
|
||||
@position.x = @container.x
|
||||
@position.y = @container.y
|
||||
@size = [@container.width, @container.height].min
|
||||
@scale = @size.to_f / @field_size
|
||||
end
|
||||
end
|
||||
|
||||
@@ -75,6 +75,14 @@ module TAC
|
||||
@queue << Move.new(robot: self, distance: -distance, power: power)
|
||||
end
|
||||
|
||||
def strafe_right(distance, power = 1.0)
|
||||
@queue << Strafe.new(robot: self, distance: distance, power: power)
|
||||
end
|
||||
|
||||
def strafe_left(distance, power = 1.0)
|
||||
@queue << Strafe.new(robot: self, distance: -distance, power: power)
|
||||
end
|
||||
|
||||
def turn(relative_angle, power = 1.0)
|
||||
@queue << Turn.new(robot: self, relative_angle: relative_angle, power: power)
|
||||
end
|
||||
@@ -143,6 +151,52 @@ class State
|
||||
end
|
||||
end
|
||||
|
||||
class Strafe < State
|
||||
def initialize(robot:, distance:, power:)
|
||||
@robot = robot
|
||||
@distance = distance
|
||||
@power = power.clamp(-1.0, 1.0)
|
||||
end
|
||||
|
||||
def start
|
||||
@starting_position = @robot.position.clone
|
||||
@goal = @starting_position.clone
|
||||
if @distance.positive?
|
||||
@goal.x += Math.cos((@robot.angle + 90).gosu_to_radians) * @distance
|
||||
@goal.y += Math.sin((@robot.angle + 90).gosu_to_radians) * @distance
|
||||
else
|
||||
@goal.x += Math.cos((@robot.angle - 90).gosu_to_radians) * @distance
|
||||
@goal.y += Math.sin((@robot.angle - 90).gosu_to_radians) * @distance
|
||||
end
|
||||
|
||||
@complete = false
|
||||
@allowable_error = 1.0
|
||||
end
|
||||
|
||||
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
|
||||
)
|
||||
Gosu.draw_rect(@goal.x + (@robot.width / 2 - 1), @goal.y + (@robot.depth / 2 - 1), 2, 2, Gosu::Color::RED)
|
||||
end
|
||||
|
||||
def update(dt)
|
||||
speed = (@distance > 0 ? @power * dt : -@power * dt) * @robot.speed
|
||||
|
||||
if @robot.position.distance(@goal) <= @allowable_error
|
||||
@complete = true
|
||||
@robot.position = @goal
|
||||
else
|
||||
if speed > 0
|
||||
@robot.position -= (@robot.position - @goal).normalized * speed
|
||||
else
|
||||
@robot.position += (@robot.position - @goal).normalized * speed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Turn < State
|
||||
def initialize(robot:, relative_angle:, power:)
|
||||
@robot = robot
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
module TAC
|
||||
class Simulator
|
||||
class Simulation
|
||||
attr_reader :robots, :show_paths
|
||||
attr_reader :robots, :show_paths, :simulation_time
|
||||
|
||||
def initialize(source_code:, field_container:)
|
||||
@source_code = source_code
|
||||
@field_container = field_container
|
||||
|
||||
@robots = []
|
||||
@field = Field.new(simulation: self, season: :skystone, container: @field_container)
|
||||
@field = Field.new(simulation: self, season: :freight_frenzy, container: @field_container)
|
||||
@show_paths = false
|
||||
|
||||
@last_milliseconds = Gosu.milliseconds
|
||||
@simulation_step = 1.0 / 60.0
|
||||
@accumulator = 0.0
|
||||
@simulation_time = 0.0
|
||||
end
|
||||
|
||||
def start
|
||||
@@ -23,8 +27,15 @@ module TAC
|
||||
end
|
||||
|
||||
def update
|
||||
@field.update
|
||||
@robots.each { |robot| robot.update((Gosu.milliseconds - @last_milliseconds) / 1000.0) }
|
||||
@accumulator += (Gosu.milliseconds - @last_milliseconds) / 1000.0
|
||||
|
||||
while(@accumulator > @simulation_step)
|
||||
@field.update
|
||||
@robots.each { |robot| robot.update(@simulation_step) }
|
||||
|
||||
@accumulator -= @simulation_step
|
||||
@simulation_time += @simulation_step
|
||||
end
|
||||
|
||||
@last_milliseconds = Gosu.milliseconds
|
||||
end
|
||||
|
||||
@@ -6,18 +6,22 @@ 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, shadow: true, shadow_size: 3, font: THEME[:Label][:font])
|
||||
@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")
|
||||
|
||||
@animator = CyberarmEngine::Animator.new(start_time: 0, duration: 3_000, from: 0, to: 255)
|
||||
@transition_color = Gosu::Color.new(0x00_000000)
|
||||
@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)
|
||||
@transition_color = Gosu::Color.new(0x00_111111)
|
||||
|
||||
@next_state = Editor
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
@title_font.draw
|
||||
@logo.draw(window.width / 2 - @logo.width / 2, window.height / 2 - @logo.height / 2, 99)
|
||||
@logo.draw_rot(window.width / 2, window.height / 2, 99, 0, 0.5, 0.5, @logo_animator.transition, @logo_animator.transition)
|
||||
Gosu.draw_rect(0, 0, window.width, window.height, @transition_color, 10_00)
|
||||
end
|
||||
|
||||
@@ -25,17 +29,17 @@ module TAC
|
||||
super
|
||||
|
||||
@title_font.x = window.width / 2 - @title_font.width / 2
|
||||
@title_font.y = window.height / 2 - (@logo.height / 2 + @title_font.height)
|
||||
@title_font.y = (window.height / 2 - (@logo.height / 2 + @title_font.height)) * @title_animator.transition
|
||||
|
||||
@transition_color.alpha = @animator.transition(0, 255, :sine)
|
||||
@transition_color.alpha = @transition_animator.transition
|
||||
|
||||
push_state(Editor) if @transition_color.alpha >= 255
|
||||
push_state(@next_state) if @transition_color.alpha >= 255
|
||||
end
|
||||
|
||||
def button_up(id)
|
||||
super
|
||||
|
||||
push_state(Editor)
|
||||
push_state(@next_state)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,379 +1,194 @@
|
||||
module TAC
|
||||
class States
|
||||
class Editor < CyberarmEngine::GuiState
|
||||
def setup
|
||||
@active_group = nil
|
||||
@active_action = nil
|
||||
class Editor < CyberarmEngine::GuiState
|
||||
include CyberarmEngine::Theme # get access to deep_merge method
|
||||
attr_reader :header_bar, :header_bar_label, :navigation, :content, :menu_bar, :status_bar, :body
|
||||
|
||||
theme(THEME)
|
||||
def setup
|
||||
@window_width = 0
|
||||
@window_height = 0
|
||||
|
||||
stack width: 1.0, height: 1.0 do
|
||||
stack width: 1.0, height: 0.1, border_thickness: 1, border_color: [0, 0, Gosu::Color::BLACK, 0] do
|
||||
background THEME_HEADER_BACKGROUND
|
||||
@pages = {}
|
||||
@page = nil
|
||||
|
||||
flow width: 1.0, height: 1.0 do
|
||||
stack width: 0.60 do
|
||||
label TAC::NAME, bold: true, text_size: THEME_HEADING_TEXT_SIZE
|
||||
flow width: 1.0 do
|
||||
flow width: 0.3 do
|
||||
label "Group: "
|
||||
@active_group_label = label ""
|
||||
end
|
||||
# TODO: Use these colors for buttons
|
||||
_theme = {
|
||||
Button: {
|
||||
background: 0xff_006000,
|
||||
border_color: 0x88_111111,
|
||||
hover: {
|
||||
color: 0xff_ffffff,
|
||||
background: 0xff_00d000,
|
||||
border_color: 0x88_111111
|
||||
},
|
||||
active: {
|
||||
color: 0xff_ffffff,
|
||||
background: 0xff_004000,
|
||||
border_color: 0x88_111111
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flow width: 0.3 do
|
||||
label "Action: "
|
||||
@active_action_label = label ""
|
||||
end
|
||||
theme(deep_merge(TAC::THEME, _theme))
|
||||
|
||||
flow width: 0.395 do
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/right.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Simulator" do
|
||||
push_state(Simulator)
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/menuList.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Manage presets" do
|
||||
push_state(ManagePresets)
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/wrench.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Manage configurations" do
|
||||
push_state(ManageConfigurations)
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Save config and settings to disk" do
|
||||
window.backend.save_config(window.backend.settings.config)
|
||||
window.backend.save_settings
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/export.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Upload local config to remote, if connected." do
|
||||
window.backend.upload_config(window.backend.settings.config)
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/import.png"), image_width: THEME_ICON_SIZE, margin_left: 10, tip: "Download remote config, if connected." do
|
||||
window.backend.download_config(window.backend.settings.config)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@header_bar = flow(width: 1.0, height: 36) do
|
||||
background 0xff_006000
|
||||
|
||||
flow width: 0.399 do
|
||||
stack width: 0.5 do
|
||||
label "TACNET v#{TACNET::Packet::PROTOCOL_VERSION}", color: TAC::Palette::TACNET_PRIMARY, text_shadow: true, text_shadow_size: 1, text_shadow_color: Gosu::Color::BLACK
|
||||
flow width: 1.0 do
|
||||
@tacnet_hostname = edit_line "#{window.backend.settings.hostname}", width: 0.5, margin_right: 0
|
||||
@tacnet_hostname.subscribe(:changed) do |caller, value|
|
||||
window.backend.settings.hostname = value
|
||||
window.backend.settings_changed!
|
||||
end
|
||||
@header_bar_label = label TAC::NAME, width: 1.0, text_align: :center, text_size: 32
|
||||
|
||||
label ":", margin: 0, padding: 0, padding_top: 3
|
||||
|
||||
@tacnet_port = edit_line "#{window.backend.settings.port}", width: 0.2, margin_left: 0
|
||||
@tacnet_port.subscribe(:changed) do |caller, value|
|
||||
window.backend.settings.port = Integer(value)
|
||||
window.backend.settings_changed!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
stack width: 0.499 do
|
||||
@tacnet_status = label "Not Connected", background: TAC::Palette::TACNET_NOT_CONNECTED, width: 1.0, padding: 5, margin_top: 2, border_thickness: 1, border_color: Gosu::Color::GRAY
|
||||
flow width: 1.0 do
|
||||
@tacnet_connection_button = button "Connect", width: 0.475 do
|
||||
case window.backend.tacnet.status
|
||||
when :connected, :connecting
|
||||
window.backend.tacnet.close
|
||||
when :not_connected, :connection_error
|
||||
window.backend.tacnet.connect(@tacnet_hostname.value, @tacnet_port.value)
|
||||
end
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/information.png"), image_width: THEME_ICON_SIZE, width: 0.475 do
|
||||
push_state(Dialog::AlertDialog, title: "TACNET Status", message: window.backend.tacnet.full_status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@content = flow width: 1.0, height: 0.9 do
|
||||
background THEME_CONTENT_BACKGROUND
|
||||
stack width: 0.333, height: 1.0, border_thickness: 1, border_color: [0, Gosu::Color::BLACK, 0, 0] do
|
||||
flow do
|
||||
label "Groups", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add group" do
|
||||
push_state(TAC::Dialog::NamePromptDialog, title: "Create Group", list: window.backend.config.groups, callback_method: method(:create_group))
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/button2.png"), image_width: THEME_ICON_SIZE, tip: "Clone currently selected group" do
|
||||
if @active_group
|
||||
push_state(Dialog::NamePromptDialog, title: "Clone Group", renaming: @active_group, accept_label: "Clone", list: window.backend.config.groups, callback_method: proc { |group, name|
|
||||
clone = TAC::Config::Group.from_json( JSON.parse( @active_group.to_json, symbolize_names: true ))
|
||||
clone.name = "#{name}"
|
||||
window.backend.config.groups << clone
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
})
|
||||
end
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, tip: "Save group as preset" do
|
||||
if @active_group
|
||||
push_state(Dialog::NamePromptDialog, title: "Save Group Preset", renaming: @active_group, accept_label: "Save", list: window.backend.config.presets.actions, callback_method: proc { |action, name|
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@groups_list = stack width: 1.0 do
|
||||
end
|
||||
end
|
||||
stack width: 0.333, height: 1.0, border_thickness: 1, border_color: [0, Gosu::Color::BLACK, 0, 0] do
|
||||
flow do
|
||||
label "Actions", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add action" do
|
||||
if @active_group
|
||||
push_state(TAC::Dialog::NamePromptDialog, title: "Create Action", list: @active_group.actions, callback_method: method(:create_action))
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create action,\nno group selected.")
|
||||
end
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/button2.png"), image_width: THEME_ICON_SIZE, tip: "Clone currently selected action" do
|
||||
if @active_group && @active_action
|
||||
push_state(Dialog::NamePromptDialog, title: "Clone Action", renaming: @active_action, accept_label: "Clone", list: @active_group.actions, callback_method: proc { |action, name|
|
||||
clone = TAC::Config::Action.from_json( JSON.parse( @active_action.to_json, symbolize_names: true ))
|
||||
clone.name = name
|
||||
@active_group.actions << clone
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
})
|
||||
end
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, tip: "Save action as preset" do
|
||||
if @active_action
|
||||
push_state(Dialog::NamePromptDialog, title: "Save Action Preset", renaming: @active_action, accept_label: "Save", list: window.backend.config.presets.actions, callback_method: proc { |action, name|
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@actions_list = stack width: 1.0 do
|
||||
end
|
||||
end
|
||||
stack width: 0.331, height: 1.0 do
|
||||
flow do
|
||||
label "Variables", text_size: THEME_SUBHEADING_TEXT_SIZE
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/plus.png"), image_width: THEME_ICON_SIZE, tip: "Add variable" do
|
||||
if @active_action
|
||||
push_state(TAC::Dialog::VariableDialog, title: "Create Variable", callback_method: method(:create_variable))
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create variable,\nno action selected.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@variables_list = stack width: 1.0 do
|
||||
end
|
||||
end
|
||||
end
|
||||
@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.minimize if window.respond_to?(:minimize)
|
||||
end
|
||||
|
||||
if window.backend.settings.config == nil || window.backend.config == nil
|
||||
push_state(ManageConfigurations)
|
||||
else
|
||||
populate_groups_list
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/larger.png"), tip: "Maximize", image_height: 1.0 do |btn|
|
||||
window.maximize if window.respond_to?(:maximize)
|
||||
end
|
||||
|
||||
@tacnet_status_monitor = CyberarmEngine::Timer.new(250) do
|
||||
case window.backend.tacnet.status
|
||||
when :connected
|
||||
@tacnet_status.value = "Connected"
|
||||
@tacnet_status.background = TAC::Palette::TACNET_CONNECTED
|
||||
|
||||
@tacnet_connection_button.value = "Disconnect"
|
||||
when :connecting
|
||||
@tacnet_status.value = "Connecting..."
|
||||
@tacnet_status.background = TAC::Palette::TACNET_CONNECTING
|
||||
|
||||
@tacnet_connection_button.value = "Disconnect"
|
||||
when :connection_error
|
||||
@tacnet_status.value = "Connection Error"
|
||||
@tacnet_status.background = TAC::Palette::TACNET_CONNECTION_ERROR
|
||||
|
||||
@tacnet_connection_button.value = "Connect"
|
||||
when :not_connected
|
||||
@tacnet_status.value = "Not Connected"
|
||||
@tacnet_status.background = TAC::Palette::TACNET_NOT_CONNECTED
|
||||
|
||||
@tacnet_connection_button.value = "Connect"
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/cross.png"), tip: "Exit", image_height: 1.0, **TAC::THEME_DANGER_BUTTON do
|
||||
window.close
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@tacnet_status_monitor.update
|
||||
end
|
||||
|
||||
def create_group(name)
|
||||
window.backend.config.groups << TAC::Config::Group.new(name: name, actions: [])
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def update_group(group, name)
|
||||
group.name = name
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def delete_group(group)
|
||||
window.backend.config.groups.delete(group)
|
||||
window.backend.config_changed!
|
||||
|
||||
@active_group = nil
|
||||
@active_group_label.value = ""
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
@actions_list.clear
|
||||
@variables_list.clear
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
|
||||
def create_action(name)
|
||||
@active_group.actions << TAC::Config::Action.new(name: name, enabled: true, variables: [])
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def update_action(action, name)
|
||||
action.name = name
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def delete_action(action)
|
||||
@active_group.actions.delete(action)
|
||||
window.backend.config_changed!
|
||||
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
@variables_list.clear
|
||||
|
||||
populate_actions_list(@active_group)
|
||||
end
|
||||
|
||||
def create_variable(name, type, value)
|
||||
@active_action.variables << TAC::Config::Variable.new(name: name, type: type, value: value)
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def update_variable(variable, name, type, value)
|
||||
variable.name = name
|
||||
variable.type = type
|
||||
variable.value = value
|
||||
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def delete_variable(variable)
|
||||
@active_action.variables.delete(variable)
|
||||
window.backend.config_changed!
|
||||
|
||||
populate_variables_list(@active_action)
|
||||
end
|
||||
|
||||
def populate_groups_list
|
||||
groups = window.backend.config.groups
|
||||
|
||||
@groups_list.clear do
|
||||
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.855 do
|
||||
@active_group = group
|
||||
@active_group_label.value = group.name
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
|
||||
populate_actions_list(group)
|
||||
@variables_list.clear
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit group" do
|
||||
push_state(Dialog::NamePromptDialog, title: "Rename Group", renaming: group, list: window.backend.config.groups, callback_method: method(:update_group))
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/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 and all\nof its actions and variables?", callback_method: proc { delete_group(group) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def populate_actions_list(group)
|
||||
actions = group.actions
|
||||
|
||||
@actions_list.clear do
|
||||
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.8 do
|
||||
@active_action = action
|
||||
@active_action_label.value = action.name
|
||||
|
||||
populate_variables_list(action)
|
||||
end
|
||||
|
||||
toggle_button tip: "Enable action"
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/gear.png"), image_width: THEME_ICON_SIZE, tip: "Edit action" do
|
||||
push_state(Dialog::NamePromptDialog, title: "Rename Action", renaming: action, list: @active_group.actions, callback_method: method(:update_action))
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/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 and all\nof its variables?", callback_method: proc { delete_action(action) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def populate_variables_list(action)
|
||||
variables = action.variables
|
||||
|
||||
@variables_list.clear do
|
||||
variables.each_with_index do |variable, i|
|
||||
flow width: 1.0, **THEME_ITEM_CONTAINER_PADDING do
|
||||
background i.even? ? THEME_EVEN_COLOR : THEME_ODD_COLOR
|
||||
|
||||
button "#{variable.name} [Type: #{variable.type}, Value: #{variable.value}]", width: 0.925, tip: "Edit variable" do
|
||||
push_state(Dialog::VariableDialog, title: "Edit Variable", variable: variable, callback_method: method(:update_variable))
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/trashcan.png"), image_width: THEME_ICON_SIZE, tip: "Delete variable", **THEME_DANGER_BUTTON do
|
||||
push_state(Dialog::ConfirmDialog, title: "Are you sure?", message: "Delete variable?", callback_method: proc { delete_variable(variable) })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_config
|
||||
@active_group = nil
|
||||
@active_group_label.value = ""
|
||||
@active_action = nil
|
||||
@active_action_label.value = ""
|
||||
|
||||
@groups_list.clear
|
||||
@actions_list.clear
|
||||
@variables_list.clear
|
||||
|
||||
populate_groups_list
|
||||
end
|
||||
end
|
||||
|
||||
@container = flow(width: 1.0, height: 1.0) do
|
||||
@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
|
||||
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)
|
||||
end
|
||||
|
||||
@tacnet_button = button get_image("#{TAC::ROOT_PATH}/media/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/right.png"), margin: 4, tip: "Simulator", image_width: 1.0 do
|
||||
page(TAC::Pages::Simulator)
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/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
|
||||
page(TAC::Pages::Presets)
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/zoom.png"), margin: 4, tip: "Search", image_width: 1.0 do
|
||||
page(TAC::Pages::Search)
|
||||
end
|
||||
|
||||
button get_image("#{TAC::ROOT_PATH}/media/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
|
||||
page(TAC::Pages::DriveTeamRotationGenerator)
|
||||
end
|
||||
end
|
||||
|
||||
@content = stack(width: window.width - @navigation.style.width, 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
|
||||
end
|
||||
|
||||
@status_bar = flow(width: 1.0, height: 96 - 48, padding: 2) do
|
||||
background 0xff_006000
|
||||
end
|
||||
end
|
||||
|
||||
@body = stack(width: 1.0, height: 1.0) do
|
||||
background 0xff_707070
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@window_controls.hide unless BORDERLESS
|
||||
|
||||
page(TAC::Pages::Home)
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
@page.draw if @page
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@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
|
||||
end
|
||||
|
||||
window.width = Gosu.available_width / 2 if window.width < Gosu.available_width / 2
|
||||
window.height = Gosu.available_height / 2 if window.height < Gosu.available_height / 2
|
||||
|
||||
if window.width != @window_width || window.height != @window_height
|
||||
@window_width = window.width
|
||||
@window_height = window.height
|
||||
|
||||
recalc
|
||||
end
|
||||
end
|
||||
|
||||
def button_down(id)
|
||||
super
|
||||
|
||||
@page&.button_down(id)
|
||||
end
|
||||
|
||||
def button_up(id)
|
||||
super
|
||||
|
||||
@page&.button_up(id)
|
||||
end
|
||||
|
||||
def recalc
|
||||
@window_controls.style.x = window.width - @window_controls.width
|
||||
@container.style.height = window.height - @header_bar.height
|
||||
@content.style.width = window.width - @navigation.width
|
||||
@body.style.height = window.height - (@chrome.height + @header_bar.height)
|
||||
|
||||
|
||||
request_recalculate
|
||||
end
|
||||
|
||||
def page(klass, options = {})
|
||||
@menu_bar.clear
|
||||
@status_bar.clear
|
||||
@body.clear
|
||||
|
||||
if window.backend.settings.config.empty?
|
||||
if [TAC::Pages::Home, TAC::Pages::TACNET, TAC::Pages::Simulator, TAC::Pages::Configurations].include?(klass)
|
||||
else
|
||||
push_state(TAC::Dialog::AlertDialog, title: "No Config Loaded", message: "A config must be loaded.")
|
||||
page(TAC::Pages::Configurations)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
@page.blur if @page
|
||||
|
||||
@pages[klass] = klass.new(host: self) unless @pages[klass]
|
||||
@page = @pages[klass]
|
||||
|
||||
@page.options = options
|
||||
@page.setup
|
||||
@page.focus
|
||||
end
|
||||
end
|
||||
@@ -1,45 +0,0 @@
|
||||
module TAC
|
||||
class States
|
||||
class ManagePresets < CyberarmEngine::GuiState
|
||||
def setup
|
||||
theme(THEME)
|
||||
|
||||
stack width: 1.0, height: 0.1 do
|
||||
background THEME_HEADER_BACKGROUND
|
||||
label "#{TAC::NAME} ― Manage Presets", bold: true, text_size: THEME_HEADING_TEXT_SIZE
|
||||
button "Close" do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
flow width: 1.0, height: 0.9 do
|
||||
stack width: 0.33, height: 1.0 do
|
||||
background TAC::Palette::GROUPS_PRIMARY
|
||||
|
||||
label "Group Presets"
|
||||
# TAC::Storage.group_presets.each do |preset|
|
||||
%w{ Hello World How Are You }.each do |preset|
|
||||
button preset, width:1.0
|
||||
end
|
||||
|
||||
label "Action Presets"
|
||||
# TAC::Storage.action_presets.each do |preset|
|
||||
%w{ Hello World How Are You }.each do |preset|
|
||||
button preset, width:1.0
|
||||
end
|
||||
end
|
||||
|
||||
stack width: 0.6698, height: 1.0 do
|
||||
background TAC::Palette::EDITOR_PRIMARY
|
||||
|
||||
label "Editor"
|
||||
|
||||
@editor = stack width: 1.0, height: 1.0, margin: 10 do
|
||||
background TAC::Palette::EDITOR_SECONDARY
|
||||
label "HELLO WORLD"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,81 +0,0 @@
|
||||
module TAC
|
||||
class States
|
||||
class Simulator < CyberarmEngine::GuiState
|
||||
def setup
|
||||
theme(THEME)
|
||||
|
||||
stack width: 1.0, height: 0.1 do
|
||||
background THEME_HEADER_BACKGROUND
|
||||
label "#{TAC::NAME} ― Simulator", bold: true, text_size: THEME_HEADING_TEXT_SIZE
|
||||
button "Close" do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
flow width: 1.0, height: 0.9 do
|
||||
@field_container = stack width: 0.4, height: 1.0 do
|
||||
background Gosu::Color.new(0xff_333333)..Gosu::Color::BLACK
|
||||
end
|
||||
|
||||
stack width: 0.6, height: 1.0 do
|
||||
background Gosu::Color.new(0x88_ff8800)
|
||||
|
||||
flow width: 1.0, height: 0.05 do
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/right.png"), image_width: THEME_ICON_SIZE, width: 0.49, tip: "Run simulation" do
|
||||
begin
|
||||
@simulation_start_time = Gosu.milliseconds
|
||||
@simulation = TAC::Simulator::Simulation.new(source_code: @source_code.value, field_container: @field_container)
|
||||
@simulation.start
|
||||
rescue SyntaxError, NameError, NoMethodError, TypeError, ArgumentError => 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"), image_width: THEME_ICON_SIZE, width: 0.49, tip: "Stop simulation" do
|
||||
@simulation.robots.each { |robot| robot.queue.clear } if @simulation
|
||||
end
|
||||
button get_image("#{TAC::ROOT_PATH}/media/icons/save.png"), image_width: THEME_ICON_SIZE, width: 0.49, tip: "Save source code" do
|
||||
File.open("#{TAC::ROOT_PATH}/data/simulator.rb", "w") { |f| f.write @source_code.value }
|
||||
end
|
||||
|
||||
@simulation_status = label ""
|
||||
end
|
||||
|
||||
stack width: 1.0, height: 0.95 do
|
||||
source_code = ""
|
||||
if File.exist?("#{TAC::ROOT_PATH}/data/simulator.rb")
|
||||
source_code = File.read("#{TAC::ROOT_PATH}/data/simulator.rb")
|
||||
else
|
||||
source_code =
|
||||
"robot = create_robot(alliance: :blue, width: 18, depth: 18)
|
||||
robot.backward 100
|
||||
robot.turn 90
|
||||
robot.forward 100
|
||||
robot.turn -90
|
||||
robot.forward 100
|
||||
robot.turn -90
|
||||
robot.forward 100"
|
||||
end
|
||||
@source_code = edit_box source_code, width: 1.0, height: 1.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
Gosu.flush
|
||||
@simulation.draw if @simulation
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@simulation.update if @simulation
|
||||
@simulation_status.value = "Time: #{((Gosu.milliseconds - @simulation_start_time) / 1000.0).round(1)} seconds" if @simulation_start_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -41,15 +41,15 @@ module TAC
|
||||
net_stats += "<b>Data Received:</b> #{client.data_received} bytes\n"
|
||||
|
||||
"<b>Status:</b> #{_status}\n\n#{net_stats}"
|
||||
elsif @connection && @connection.client && @connection.client.socket_error?
|
||||
"<b>Status:</b> #{_status}\n\n#{@connection.client.last_socket_error.to_s.chars.each_slice(32).to_a.map { |c| c.join }.join("\n")}"
|
||||
elsif @connection&.client && @connection.client.socket_error?
|
||||
"<b>Status:</b> #{_status}\n\n#{@connection.client.last_socket_error.to_s}"
|
||||
else
|
||||
"<b>Status:</b> #{_status}"
|
||||
end
|
||||
end
|
||||
|
||||
def connected?
|
||||
@connection && @connection.connected?
|
||||
@connection&.connected?
|
||||
end
|
||||
|
||||
def close
|
||||
@@ -60,7 +60,7 @@ module TAC
|
||||
end
|
||||
|
||||
def client
|
||||
@connection.client if connected?
|
||||
@connection.client
|
||||
end
|
||||
|
||||
def puts(packet)
|
||||
|
||||
@@ -2,14 +2,21 @@ module TAC
|
||||
class TACNET
|
||||
class Packet
|
||||
PROTOCOL_VERSION = 1
|
||||
PROTOCOL_HEADER_SEPERATOR = "|"
|
||||
PROTOCOL_SEPERATOR = "|"
|
||||
PROTOCOL_HEARTBEAT = "heartbeat"
|
||||
|
||||
PACKET_TYPES = {
|
||||
handshake: 0,
|
||||
heartbeat: 1,
|
||||
download_config: 2,
|
||||
upload_config: 3,
|
||||
error: 2,
|
||||
|
||||
download_config: 10,
|
||||
upload_config: 11,
|
||||
list_configs: 12,
|
||||
select_config: 13,
|
||||
add_config: 14,
|
||||
update_config: 15,
|
||||
delete_config: 16,
|
||||
|
||||
add_group: 20,
|
||||
update_group: 21,
|
||||
@@ -28,7 +35,7 @@ module TAC
|
||||
slice = message.split("|", 4)
|
||||
|
||||
if slice.size < 4
|
||||
warn "Failed to split packet along first 4 " + PROTOCOL_HEADER_SEPERATOR + ". Raw return: " + slice.to_s
|
||||
warn "Failed to split packet along first 4 " + PROTOCOL_SEPERATOR + ". Raw return: " + slice.to_s
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -71,11 +78,11 @@ module TAC
|
||||
def encode_header
|
||||
string = ""
|
||||
string += protocol_version.to_s
|
||||
string += PROTOCOL_HEADER_SEPERATOR
|
||||
string += PROTOCOL_SEPERATOR
|
||||
string += PACKET_TYPES[type].to_s
|
||||
string += PROTOCOL_HEADER_SEPERATOR
|
||||
string += PROTOCOL_SEPERATOR
|
||||
string += content_length.to_s
|
||||
string += PROTOCOL_HEADER_SEPERATOR
|
||||
string += PROTOCOL_SEPERATOR
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ module TAC
|
||||
packet = Packet.from_stream(message)
|
||||
|
||||
if packet
|
||||
log.i(TAG, "Received packet of type: #{packet.type}")
|
||||
hand_off(packet)
|
||||
else
|
||||
log.d(TAG, "Rejected raw packet: #{message}")
|
||||
@@ -22,10 +23,23 @@ module TAC
|
||||
handle_handshake(packet)
|
||||
when :heartbeat
|
||||
handle_heartbeat(packet)
|
||||
when :error
|
||||
handle_error(packet)
|
||||
|
||||
when :download_config
|
||||
handle_download_config(packet)
|
||||
when :upload_config
|
||||
handle_upload_config(packet)
|
||||
when :list_configs
|
||||
handle_list_configs(packet)
|
||||
when :select_config
|
||||
handle_select_config(packet)
|
||||
when :add_config
|
||||
handle_add_config(packet)
|
||||
when :update_config # rename config/file
|
||||
handle_update_config(packet)
|
||||
when :delete_config
|
||||
handle_delete_config(packet)
|
||||
else
|
||||
log.d(TAG, "No hand off available for packet type: #{packet.type}")
|
||||
end
|
||||
@@ -41,52 +55,134 @@ module TAC
|
||||
def handle_heartbeat(packet)
|
||||
end
|
||||
|
||||
# TODO: Handle errors
|
||||
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)
|
||||
else
|
||||
log.e(TAG, "Remote error: #{title}: #{message}")
|
||||
end
|
||||
end
|
||||
|
||||
def handle_upload_config(packet)
|
||||
begin
|
||||
data = JSON.parse(packet.body, symbolize_names: true)
|
||||
config_name, json = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2)
|
||||
data = JSON.parse(json, symbolize_names: true)
|
||||
|
||||
if @host_is_a_connection
|
||||
if data.is_a?(Array)
|
||||
# OLDEST CONFIG, upgrade?
|
||||
$window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config to old.")
|
||||
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 }
|
||||
|
||||
elsif data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION
|
||||
$window.push_state(TAC::Dialog::ConfirmDialog, title: "Replace Config", message: "Replace local config\nwith remote config?", callback_method: proc {
|
||||
File.open("#{TAC::ROOT_PATH}/data/config.json", "w") { |f| f.write packet.body }
|
||||
|
||||
$window.backend.update_config
|
||||
})
|
||||
|
||||
elsif data.is_a?(Hash) && data.dig(:config, :spec_version) < TAC::CONFIG_SPEC_VERSION
|
||||
# OLD CONFIG, Upgrade?
|
||||
$window.push_state(TAC::Dialog::ConfirmDialog, title: "Upgrade Config", message: "Remote config is an older\nspec version.\nTry to upgrade?", callback_method: proc {})
|
||||
|
||||
elsif data.is_a?(Hash) && data.dig(:config, :spec_version) > TAC::CONFIG_SPEC_VERSION
|
||||
# NEWER CONFIG, Error Out
|
||||
$window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Client outdated, check for\nupdates.\nSupported config spec:\nv#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}")
|
||||
|
||||
else
|
||||
# CONFIG is unknown
|
||||
$window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config is not supported.")
|
||||
if $window.backend.config&.name == config_name
|
||||
$window.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)}")
|
||||
end
|
||||
|
||||
rescue JSON::ParserError => e
|
||||
log.e(TAG, "JSON parsing error: #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
def handle_download_config(packet)
|
||||
config_name = packet.body
|
||||
log.i(TAG, config_name)
|
||||
pkt = nil
|
||||
|
||||
if File.exist?("#{TAC::CONFIGS_PATH}/#{config_name}.json")
|
||||
pkt = PacketHandler.packet_upload_config(config_name, Config.new(config_name).to_json)
|
||||
else
|
||||
pkt = PacketHandler.packet_error("Remote config not found", "The requested config #{config_name} does not exist over here.")
|
||||
end
|
||||
|
||||
if @host_is_a_connection
|
||||
json = JSON.dump($window.backend.config)
|
||||
$window.backend.tacnet.puts(PacketHandler.packet_upload_config(json))
|
||||
$window.backend.tacnet.puts(pkt)
|
||||
else
|
||||
$server.active_client.puts(pkt)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_list_configs(packet)
|
||||
if @host_is_a_connection # Download new or updated configs
|
||||
list = packet.body.split(Packet::PROTOCOL_SEPERATOR).map { |part| part.split(",") }
|
||||
|
||||
remote_configs = list.map { |l| l.first }
|
||||
local_configs = Dir.glob("#{TAC::CONFIGS_PATH}/*.json").map { |f| File.basename(f, ".json") }
|
||||
_diff = local_configs - remote_configs
|
||||
|
||||
list.each do |name, revision|
|
||||
revision = Integer(revision)
|
||||
path = "#{TAC::CONFIGS_PATH}/#{name}.json"
|
||||
|
||||
if File.exist?(path)
|
||||
config = Config.new(name)
|
||||
|
||||
if config.configuration.revision < revision
|
||||
$window.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 )) )
|
||||
end
|
||||
|
||||
else
|
||||
$window.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 )) )
|
||||
end
|
||||
else
|
||||
if $server.active_client && $server.active_client.connected?
|
||||
json = File.read(TAC::CONFIGS_PATH)
|
||||
$server.active_client.puts(PacketHandler.packet_upload_config(json))
|
||||
$server.active_client.puts(PacketHandler.packet_list_configs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
def handle_add_config(packet)
|
||||
config_name = packet.body
|
||||
|
||||
if $window.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)
|
||||
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)
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_delete_config(packet)
|
||||
config_name = packet.body
|
||||
|
||||
$window.backend.delete_config(config_name)
|
||||
end
|
||||
|
||||
def self.packet_handshake(client_uuid)
|
||||
Packet.create(Packet::PACKET_TYPES[:handshake], client_uuid)
|
||||
end
|
||||
@@ -95,15 +191,41 @@ module TAC
|
||||
Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_HEARTBEAT)
|
||||
end
|
||||
|
||||
def self.packet_download_config
|
||||
Packet.create(Packet::PACKET_TYPES[:download_config], "")
|
||||
def self.packet_error(error_code, message)
|
||||
Packet.create(Packet::PACKET_TYPES[:error], error_code.to_s, message.to_s)
|
||||
end
|
||||
|
||||
def self.packet_upload_config(string)
|
||||
string = string.gsub("\n", " ")
|
||||
def self.packet_download_config(config_name)
|
||||
Packet.create(Packet::PACKET_TYPES[:download_config], "#{config_name}")
|
||||
end
|
||||
|
||||
def self.packet_upload_config(config_name, json)
|
||||
string = "#{config_name}#{Packet::PROTOCOL_SEPERATOR}#{json.gsub("\n", " ")}"
|
||||
|
||||
Packet.create(Packet::PACKET_TYPES[:upload_config], string)
|
||||
end
|
||||
|
||||
def self.packet_list_configs
|
||||
files = Dir.glob("#{TAC::CONFIGS_PATH}/*.json")
|
||||
list = files.map do |file|
|
||||
name = File.basename(file, ".json")
|
||||
config = Config.new(name)
|
||||
"#{name},#{config.configuration.revision}"
|
||||
end.join(Packet::PROTOCOL_SEPERATOR)
|
||||
|
||||
Packet.create(
|
||||
Packet::PACKET_TYPES[:list_configs],
|
||||
list
|
||||
)
|
||||
end
|
||||
|
||||
def self.packet_select_config(config_name)
|
||||
Packet.create(Packet::PACKET_TYPES[:select_config], config_name)
|
||||
end
|
||||
|
||||
def self.packet_delete_config(config_name)
|
||||
Packet.create(Packet::PACKET_TYPES[:delete_config], config_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,10 +71,9 @@ module TAC
|
||||
@active_client = client
|
||||
# TODO: Backup local config
|
||||
# SEND CONFIG
|
||||
config = File.read(TAC::CONFIGS_PATH)
|
||||
|
||||
@active_client.puts(PacketHandler.packet_handshake(@active_client.uuid))
|
||||
@active_client.puts(PacketHandler.packet_upload_config(config))
|
||||
@active_client.puts(PacketHandler.packet_list_configs)
|
||||
|
||||
log.i(TAG, "Client connected!")
|
||||
|
||||
@@ -136,4 +135,4 @@ module TAC
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
19
lib/theme.rb
19
lib/theme.rb
@@ -1,11 +1,13 @@
|
||||
module TAC
|
||||
THEME_FONT = "#{TAC::ROOT_PATH}/media/fonts/DejaVuSansCondensed.ttf"
|
||||
THEME = {
|
||||
Label: {
|
||||
font: "#{TAC::ROOT_PATH}/media/fonts/DejaVuSansCondensed.ttf",
|
||||
text_size: 18,
|
||||
font: THEME_FONT,
|
||||
text_size: 22,
|
||||
color: Gosu::Color.new(0xee_ffffff),
|
||||
},
|
||||
Button: {
|
||||
text_size: 22,
|
||||
background: TAC::Palette::TIMECRAFTERS_PRIMARY,
|
||||
border_thickness: 1,
|
||||
border_color: Gosu::Color.new(0xff_111111),
|
||||
@@ -17,7 +19,7 @@ module TAC
|
||||
}
|
||||
},
|
||||
EditLine: {
|
||||
caret_color: Gosu::Color.new(0xff_434343),
|
||||
caret_color: Gosu::Color.new(0xff_88ef90),
|
||||
},
|
||||
ToggleButton: {
|
||||
width: 18,
|
||||
@@ -36,7 +38,8 @@ module TAC
|
||||
}
|
||||
}
|
||||
|
||||
THEME_ICON_SIZE = 18
|
||||
THEME_DIALOG_BUTTON_PADDING = 24
|
||||
THEME_ICON_SIZE = 22
|
||||
THEME_HEADING_TEXT_SIZE = 32
|
||||
THEME_SUBHEADING_TEXT_SIZE = 28
|
||||
THEME_ITEM_PADDING = 8
|
||||
@@ -46,11 +49,15 @@ module TAC
|
||||
padding_top: THEME_ITEM_PADDING,
|
||||
padding_bottom: THEME_ITEM_PADDING
|
||||
}
|
||||
THEME_EVEN_COLOR = Gosu::Color.new(0xff_606060)
|
||||
THEME_ODD_COLOR = Gosu::Color.new(0xff_202020)
|
||||
THEME_EVEN_COLOR = Gosu::Color.new(0xff_202020)
|
||||
THEME_ODD_COLOR = Gosu::Color.new(0xff_606060)
|
||||
THEME_CONTENT_BACKGROUND = Gosu::Color.new(0x88_007f3f)
|
||||
THEME_HEADER_BACKGROUND = [
|
||||
TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_PRIMARY,
|
||||
TAC::Palette::TIMECRAFTERS_SECONDARY, TAC::Palette::TIMECRAFTERS_SECONDARY,
|
||||
]
|
||||
THEME_NOTIFICATION_EDGE_COLOR = Gosu::Color.new(0xff_008000)
|
||||
THEME_NOTIFICATION_BACKGROUND = Gosu::Color.new(0xff_102010)
|
||||
THEME_NOTIFICATION_TITLE_COLOR = Gosu::Color::WHITE
|
||||
THEME_NOTIFICATION_TAGLINE_COLOR = Gosu::Color::WHITE
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
module TAC
|
||||
NAME = "TimeCrafters Configuration Tool"
|
||||
VERSION = "0.1.0"
|
||||
RELEASE_NAME = "IN-DEV"
|
||||
VERSION = "0.4.0"
|
||||
RELEASE_NAME = "Beta"
|
||||
end
|
||||
@@ -1,23 +1,71 @@
|
||||
module TAC
|
||||
class Window < CyberarmEngine::Window
|
||||
attr_reader :backend
|
||||
attr_reader :backend, :notification_manager
|
||||
def initialize(**args)
|
||||
super(**args)
|
||||
|
||||
self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME})"
|
||||
@backend = Backend.new
|
||||
@notification_manager = GosuNotifications::NotificationManager.new(window: self, edge: :bottom)
|
||||
|
||||
push_state(TAC::States::Boot)
|
||||
end
|
||||
|
||||
def draw
|
||||
super
|
||||
|
||||
Gosu.flush
|
||||
@notification_manager.draw
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
|
||||
@notification_manager.update
|
||||
end
|
||||
|
||||
def needs_cursor?
|
||||
true
|
||||
end
|
||||
|
||||
def toast(title, message = nil)
|
||||
@notification_manager.create_notification(
|
||||
priority: GosuNotifications::Notification::PRIORITY_HIGH,
|
||||
title: title,
|
||||
|
||||
tagline: message ? message : "",
|
||||
edge_color: THEME_NOTIFICATION_EDGE_COLOR,
|
||||
background_color: THEME_NOTIFICATION_BACKGROUND,
|
||||
title_color: THEME_NOTIFICATION_TITLE_COLOR,
|
||||
tagline_color: THEME_NOTIFICATION_TAGLINE_COLOR
|
||||
)
|
||||
end
|
||||
|
||||
def hit_test(x, y)
|
||||
return 0 unless BORDERLESS
|
||||
|
||||
if y <= 4
|
||||
return 2 if x <= 4
|
||||
return 4 if x >= width - 4
|
||||
return 3
|
||||
end
|
||||
|
||||
if y >= height - 4
|
||||
return 8 if x <= 4
|
||||
return 6 if x >= width - 4
|
||||
return 7
|
||||
end
|
||||
|
||||
return 1 if y <= 36 && x <= width - (36 * 3 + 4 * 6)
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
def close
|
||||
if @backend.config_changed?
|
||||
push_state(Dialog::ConfirmDialog, title: "Unsaved Config", message: "Config has unsaved changes\nthat will be lost if\nyou continue!", callback_method: proc { cleanup_and_close })
|
||||
push_state(Dialog::ConfirmDialog, title: "Unsaved Config", message: "Config has unsaved changes that will be lost if you continue!", callback_method: proc { cleanup_and_close })
|
||||
elsif @backend.settings_changed?
|
||||
push_state(Dialog::ConfirmDialog, title: "Unsaved Settings", message: "Settings has unsaved changes\nthat will be lost if\nyou continue!", callback_method: proc { cleanup_and_close })
|
||||
push_state(Dialog::ConfirmDialog, title: "Unsaved Settings", message: "Settings has unsaved changes that will be lost if you continue!", callback_method: proc { cleanup_and_close })
|
||||
else
|
||||
cleanup_and_close
|
||||
end
|
||||
@@ -31,4 +79,4 @@ module TAC
|
||||
close!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BIN
media/error_alarm.ogg
Normal file
BIN
media/error_alarm.ogg
Normal file
Binary file not shown.
BIN
media/icon.ico
Normal file
BIN
media/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -4,6 +4,8 @@ require "securerandom"
|
||||
|
||||
require_relative "lib/tac"
|
||||
require_relative "lib/logger"
|
||||
require_relative "lib/settings"
|
||||
require_relative "lib/config"
|
||||
|
||||
require_relative "lib/tacnet"
|
||||
require_relative "lib/tacnet/packet"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
require_relative "../cyberarm_engine/lib/cyberarm_engine"
|
||||
begin
|
||||
require_relative "../cyberarm_engine/lib/cyberarm_engine"
|
||||
rescue LoadError
|
||||
require "cyberarm_engine"
|
||||
end
|
||||
require "gosu_notifications"
|
||||
require "socket"
|
||||
require "securerandom"
|
||||
require "json"
|
||||
@@ -13,9 +18,16 @@ require_relative "lib/config"
|
||||
require_relative "lib/settings"
|
||||
require_relative "lib/states/boot"
|
||||
require_relative "lib/states/editor"
|
||||
require_relative "lib/states/simulator"
|
||||
require_relative "lib/states/manage_presets"
|
||||
require_relative "lib/states/manage_configurations"
|
||||
require_relative "lib/page"
|
||||
require_relative "lib/pages/home"
|
||||
require_relative "lib/pages/editor"
|
||||
require_relative "lib/pages/tacnet"
|
||||
require_relative "lib/pages/simulator"
|
||||
require_relative "lib/pages/configurations"
|
||||
require_relative "lib/pages/presets"
|
||||
require_relative "lib/pages/search"
|
||||
require_relative "lib/pages/field_planner"
|
||||
require_relative "lib/pages/drive_team_rotation_generator"
|
||||
require_relative "lib/simulator/robot"
|
||||
require_relative "lib/simulator/field"
|
||||
require_relative "lib/simulator/simulation"
|
||||
@@ -25,7 +37,11 @@ require_relative "lib/dialog"
|
||||
require_relative "lib/dialogs/alert_dialog"
|
||||
require_relative "lib/dialogs/confirm_dialog"
|
||||
require_relative "lib/dialogs/name_prompt_dialog"
|
||||
require_relative "lib/dialogs/action_dialog"
|
||||
require_relative "lib/dialogs/variable_dialog"
|
||||
require_relative "lib/dialogs/tacnet_dialog"
|
||||
require_relative "lib/dialogs/tacnet_status_dialog"
|
||||
require_relative "lib/dialogs/pick_preset_dialog"
|
||||
require_relative "lib/tacnet"
|
||||
require_relative "lib/tacnet/packet"
|
||||
require_relative "lib/tacnet/packet_handler"
|
||||
@@ -35,4 +51,9 @@ require_relative "lib/tacnet/server"
|
||||
|
||||
# Thread.abort_on_exception = true
|
||||
|
||||
TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true).show
|
||||
USE_REDESIGN = ARGV.include?("--redesign")
|
||||
BORDERLESS = ARGV.include?("--borderless")
|
||||
|
||||
if not defined?(Ocra)
|
||||
TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true, borderless: BORDERLESS).show
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user