summaryrefslogtreecommitdiffhomepage
path: root/dragon
diff options
context:
space:
mode:
authorAmir Rajan <[email protected]>2021-08-07 00:18:49 -0500
committerAmir Rajan <[email protected]>2021-08-07 00:23:49 -0500
commita1b8552ae442d1fa22df1e569652eb99750dcebf (patch)
treec097d7d69144a97cc0426cb61baa330877644e93 /dragon
parent17d6f2a45d07932fb2ac354d4f8996c420eddc27 (diff)
downloaddragonruby-game-toolkit-contrib-a1b8552ae442d1fa22df1e569652eb99750dcebf.tar.gz
dragonruby-game-toolkit-contrib-a1b8552ae442d1fa22df1e569652eb99750dcebf.zip
Sync core oss engine files.
Diffstat (limited to 'dragon')
-rw-r--r--dragon/args.rb51
-rw-r--r--dragon/assert.rb4
-rw-r--r--dragon/attr_gtk.rb12
-rw-r--r--dragon/attr_sprite.rb3
-rw-r--r--dragon/autocomplete.rb19
-rw-r--r--dragon/benchmark.rb112
-rw-r--r--dragon/config.rb2
-rw-r--r--dragon/console.rb49
-rw-r--r--dragon/console_color.rb5
-rw-r--r--dragon/console_font_style.rb3
-rw-r--r--dragon/console_menu.rb8
-rw-r--r--dragon/console_prompt.rb15
-rw-r--r--dragon/controller_config.rb396
-rw-r--r--dragon/directional_input_helper_methods.rb6
-rw-r--r--dragon/docs.rb22
-rw-r--r--dragon/draw.rb234
-rw-r--r--dragon/framerate.rb1
-rw-r--r--dragon/framerate_diagnostics.rb13
-rw-r--r--dragon/geometry.rb68
-rw-r--r--dragon/grid.rb8
-rw-r--r--dragon/help.rb72
-rw-r--r--dragon/hotload.rb44
-rw-r--r--dragon/inputs.rb60
-rw-r--r--dragon/ios_wizard.rb328
-rw-r--r--dragon/itch_wizard.rb60
-rw-r--r--dragon/keys.rb18
-rw-r--r--dragon/layout.rb182
-rw-r--r--dragon/log.rb5
-rw-r--r--dragon/metadata.rb41
-rw-r--r--dragon/numeric.rb117
-rw-r--r--dragon/outputs_docs.rb2
-rw-r--r--dragon/readme_docs.rb87
-rw-r--r--dragon/runtime_docs.rb32
-rw-r--r--dragon/string.rb24
-rw-r--r--dragon/tests.rb2
-rw-r--r--dragon/wizards.rb35
36 files changed, 1101 insertions, 1039 deletions
diff --git a/dragon/args.rb b/dragon/args.rb
index 2b11008..9cf6e33 100644
--- a/dragon/args.rb
+++ b/dragon/args.rb
@@ -10,58 +10,21 @@ module GTK
class Args
include ArgsDeprecated
include Serialize
-
- # Contains information related to input devices and input events.
- #
- # @return [Inputs]
attr_accessor :inputs
-
- # Contains the means to interact with output devices such as the screen.
- #
- # @return [Outputs]
attr_accessor :outputs
-
- # Contains the means to interact with the audio mixer.
- #
- # @return [Hash]
attr_accessor :audio
-
- # Contains display size information to assist in positioning things on the screen.
- #
- # @return [Grid]
attr_accessor :grid
-
- # Provides access to game play recording facilities within Game Toolkit.
- #
- # @return [Recording]
attr_accessor :recording
-
- # Gives you access to geometry related functions.
- #
- # @return [Geometry]
attr_accessor :geometry
-
attr_accessor :fn
-
- # This is where you'll put state associated with your video game.
- #
- # @return [OpenEntity]
attr_accessor :state
-
- # Gives you access to the top level DragonRuby runtime.
- #
- # @return [Runtime]
+ attr_accessor :temp_state
attr_accessor :runtime
alias_method :gtk, :runtime
-
attr_accessor :passes
-
attr_accessor :wizards
-
attr_accessor :layout
-
attr_accessor :easing
-
attr_accessor :string
def initialize runtime, recording
@@ -70,6 +33,7 @@ module GTK
@audio = {}
@passes = []
@state = OpenEntity.new
+ @temp_state = OpenEntity.new
@state.tick_count = -1
@runtime = runtime
@recording = recording
@@ -99,11 +63,12 @@ module GTK
def serialize
{
- state: state.as_hash,
- inputs: inputs.serialize,
- passes: passes.serialize,
- outputs: outputs.serialize,
- grid: grid.serialize
+ state: state.as_hash,
+ temp_state: temp_state.as_hash,
+ inputs: inputs.serialize,
+ passes: passes.serialize,
+ outputs: outputs.serialize,
+ grid: grid.serialize
}
end
diff --git a/dragon/assert.rb b/dragon/assert.rb
index 5ba344d..974846a 100644
--- a/dragon/assert.rb
+++ b/dragon/assert.rb
@@ -98,7 +98,7 @@ end
@assertion_performed = true
if actual != expected
actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip
- message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}.\n#{message}"
+ message = "actual:\n#{actual_string}\n\ndid not equal\n\nexpected:\n#{expected}\n#{message}"
raise message
end
nil
@@ -108,7 +108,7 @@ end
@assertion_performed = true
if actual == expected
actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip
- message = "actual:\n#{actual_string}\n\nequaled\n\nexpected:\n#{expected}.\n#{message}"
+ message = "actual:\n#{actual_string}\n\nequaled\n\nexpected:\n#{expected}\n#{message}"
raise message
end
nil
diff --git a/dragon/attr_gtk.rb b/dragon/attr_gtk.rb
index 2f2ccc5..e53e1b9 100644
--- a/dragon/attr_gtk.rb
+++ b/dragon/attr_gtk.rb
@@ -19,6 +19,10 @@ module AttrGTK
args.state
end
+ def temp_state
+ args.temp_state
+ end
+
def inputs
args.inputs
end
@@ -46,4 +50,12 @@ module AttrGTK
def layout
args.layout
end
+
+ def new_entity entity_type, init_hash = nil, &block
+ args.state.new_entity entity_type, init_hash, &block
+ end
+
+ def new_entity_strict entity_type, init_hash = nil, &block
+ args.state.new_entity_strict entity_type, init_hash, &block
+ end
end
diff --git a/dragon/attr_sprite.rb b/dragon/attr_sprite.rb
index 69ddd8c..754429d 100644
--- a/dragon/attr_sprite.rb
+++ b/dragon/attr_sprite.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# attr_sprite.rb has been released under MIT (*only this file*).
@@ -37,7 +38,7 @@ module AttrSprite
attr_accessor :x, :y, :w, :h, :path, :angle, :a, :r, :g, :b, :tile_x,
:tile_y, :tile_w, :tile_h, :flip_horizontally,
:flip_vertically, :angle_anchor_x, :angle_anchor_y, :id,
- :source_x, :source_y, :source_w, :source_h
+ :source_x, :source_y, :source_w, :source_h, :blendmode_enum
def primitive_marker
:sprite
diff --git a/dragon/autocomplete.rb b/dragon/autocomplete.rb
index 490d6c9..ce29d0a 100644
--- a/dragon/autocomplete.rb
+++ b/dragon/autocomplete.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# autocomplete.rb has been released under MIT (*only this file*).
@@ -24,7 +25,7 @@ module GTK
sub_index = index - previous_line[:sum]
word = (cursor_line[:line][0..sub_index - 1]).strip
token = (word.split " ")[-1]
- dots = (token.split ".")
+ dots = (token.split ".").flat_map { |s| s.split "[" }.flat_map { |s| s.split "]" }.flat_map { |s| s.split "(" }.flat_map { |s| s.split ")" }
dot = dots[-1]
end
@@ -45,6 +46,10 @@ module GTK
ignores ||= []
ignores = [ignores].flatten
keys = keys.map { |k| k.to_s }
+ keys = keys.reject { |k| k.include? '"' }
+ .reject { |k| k.start_with? "'" }
+ .reject { |k| k.include? "," }
+ .reject { |k| k.start_with? "#" }
others = ["def", "end"] +
[ :entity_keys_by_ref,
:entity_name,
@@ -102,6 +107,10 @@ module GTK
return autocomplete_filter_methods lookup_result.call if lookup_result
+ if dot[0].upcase == dot[0] && (Object.const_defined? dot.to_sym)
+ return (Object.const_get dot.to_sym).autocomplete_methods
+ end
+
start_collecting = false
dots_after_state = dots.find_all do |s|
if s == "state"
@@ -117,10 +126,16 @@ module GTK
target = target.as_hash[k.to_sym] if target.respond_to? :as_hash
end
- return autocomplete_filter_methods target.as_hash.keys
+ if target.respond_to? :as_hash
+ return autocomplete_filter_methods target.as_hash.keys
+ else
+ return autocomplete_filter_methods target.autocomplete_methods
+ end
end
+ text = text.each_line.reject { |l| l.strip.start_with? "#" }.join "\n"
+ text = text.each_line.map { |l| l.split("#").first }.join "\n"
text.gsub!("[", " ")
text.gsub!("]", " ")
text.gsub!("(", " ")
diff --git a/dragon/benchmark.rb b/dragon/benchmark.rb
new file mode 100644
index 0000000..d7e8945
--- /dev/null
+++ b/dragon/benchmark.rb
@@ -0,0 +1,112 @@
+# coding: utf-8
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# benchmark.rb has been released under MIT (*only this file*).
+
+module GTK
+ class Runtime
+ module Benchmark
+ def benchmark_single iterations, name, proc
+ log <<-S
+** Invoking :#{name}...
+S
+ time_start = Time.now
+ idx = 0
+ r = nil
+ while idx < iterations
+ r = proc.call
+ idx += 1
+ end
+
+ result = (Time.now - time_start).round 3
+
+ { name: name,
+ time: result,
+ time_ms: (result * 1000).to_i }
+ end
+
+ def benchmark opts = {}
+ iterations = opts.iterations
+
+ log <<-S
+* BENCHMARK: Started
+** Caller: #{(caller || []).first}
+** Iterations: #{iterations}
+S
+ procs = opts.find_all { |k, v| v.respond_to? :call }
+
+ times = procs.map do |(name, proc)|
+ benchmark_single iterations, name, proc
+ end.sort_by { |r| r.time }
+
+ first_place = times.first
+ second_place = times.second || first_place
+
+ times = times.map do |candidate|
+ average_time = first_place.time
+ .add(candidate.time)
+ .abs
+ .fdiv(2)
+
+ difference_percentage = 0
+ if average_time == 0
+ difference_percentage = 0
+ else
+ difference_percentage = first_place.time
+ .subtract(candidate.time)
+ .abs
+ .fdiv(average_time)
+ .imult(100)
+ end
+
+ difference_time = ((first_place.time - candidate.time) * 1000).round
+ candidate.merge(difference_percentage: difference_percentage,
+ difference_time: difference_time)
+ end
+
+ too_small_to_measure = false
+ if (first_place.time + second_place.time) == 0
+ too_small_to_measure = true
+ difference_percentage = 0
+ log <<-S
+* BENCHMARK: Average time for experiments were too small. Increase the number of iterations.
+S
+ else
+ difference_percentage = (((first_place.time - second_place.time).abs.fdiv((first_place.time + second_place.time).abs.fdiv(2))) * 100).round
+ end
+
+ difference_time = first_place.time.-(second_place.time).*(1000).abs.round
+
+ r = {
+ iterations: iterations,
+ first_place: first_place,
+ second_place: second_place,
+ difference_time: difference_time,
+ difference_percentage: difference_percentage,
+ times: times,
+ too_small_to_measure: too_small_to_measure
+ }
+
+ log_message = []
+ only_one_result = first_place.name == second_place.name
+
+ if only_one_result
+ log <<-S
+* BENCHMARK: #{r.first_place.name} completed in #{r.first_place.time_ms}ms."
+S
+ else
+ log <<-S
+* BENCHMARK: #{r.message}
+** Fastest: #{r.first_place.name.inspect}
+** Second: #{r.second_place.name.inspect}
+** Margin: #{r.difference_percentage}% (#{r.difference_time.abs}ms) #{r.first_place.time_ms}ms vs #{r.second_place.time_ms}ms.
+** Times:
+#{r.times.map { |t| "*** #{t.name}: #{t.time_ms}ms (#{t.difference_percentage}% #{t.difference_time.abs}ms)." }.join("\n")}
+S
+ end
+
+ r
+ end
+ end
+ end
+end
diff --git a/dragon/config.rb b/dragon/config.rb
index a2887dc..de57085 100644
--- a/dragon/config.rb
+++ b/dragon/config.rb
@@ -127,7 +127,7 @@ module GTK
def previous_part
if @current_part > 0
- # remove the binding that we previously had here so it can be reused.
+ # remove the binding that we previous had here so it can be reused.
bindstr = @bindings[@current_part - 1]
@bindings[@current_part - 1] = nil
@used_bindings[bindstr] = nil
diff --git a/dragon/console.rb b/dragon/console.rb
index c30c311..b3a2c2a 100644
--- a/dragon/console.rb
+++ b/dragon/console.rb
@@ -1,10 +1,10 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# console.rb has been released under MIT (*only this file*).
# Contributors outside of DragonRuby who also hold Copyright:
# - Kevin Fischer: https://github.com/kfischer-okarin
-# - Austin Meyer: https://github.com/Niyy
module GTK
class Console
@@ -20,7 +20,7 @@ module GTK
:font_style, :menu
def initialize
- @font_style = FontStyle.new(font: 'font.ttf', size_enum: -1, line_height: 1.1)
+ @font_style = FontStyle.new(font: 'font.ttf', size_enum: -1.5, line_height: 1.1)
@menu = Menu.new self
@disabled = false
@log_offset = 0
@@ -40,6 +40,8 @@ module GTK
@text_color = Color.new [255, 255, 255]
@error_color = Color.new [200, 50, 50]
@header_color = Color.new [100, 200, 220]
+ @code_color = Color.new [210, 168, 255]
+ @comment_color = Color.new [0, 200, 100]
@animation_duration = 1.seconds
@shown_at = -1
load_history
@@ -314,6 +316,8 @@ S
if cmd == 'quit' || cmd == ':wq' || cmd == ':q!' || cmd == ':q' || cmd == ':wqa'
$gtk.request_quit
+ elsif cmd.start_with? ':'
+ send ((cmd.gsub '-', '_').gsub ':', '')
else
puts "-> #{cmd}"
begin
@@ -322,13 +326,19 @@ S
if $results.nil?
puts "=> nil"
elsif $results == :console_silent_eval
+ # do nothing since the console is silent
else
puts "=> #{$results}"
end
@last_command_errored = false
rescue Exception => e
try_search_docs e
- puts "* EXCEPTION: #{e}"
+ # if an exception is thrown and the bactrace includes something helpful, then show it
+ if (e.backtrace || []).first && (e.backtrace.first.include? "(eval)")
+ puts "* EXCEPTION: #{e}"
+ else
+ puts "* EXCEPTION: #{e}\n#{e.__backtrace_to_org__}"
+ end
end
end
end
@@ -555,6 +565,11 @@ S
end
render_log_offset args
+
+ args.outputs.reserved << { x: 10.from_right, y: @bottom + 10,
+ text: "Press CTRL+g or ESCAPE to clear the prompt.",
+ vertical_alignment_enum: 0,
+ alignment_enum: 2, r: 80, g: 80, b: 80 }.label!
end
def render_log_offset args
@@ -577,7 +592,7 @@ S
end
def include_subdued_markers? text
- include_any_words? text, subdued_markers
+ (text.start_with? "* INFO: ") && (include_any_words? text, subdued_markers)
end
def include_any_words? text, words
@@ -723,8 +738,32 @@ S
(log_entry.start_with? "**** ")
end
+ def code? log_entry
+ (just_symbol? log_entry) || (codeblock_marker? log_entry)
+ end
+
+ def just_symbol? log_entry
+ scrubbed = log_entry.gsub("*", "").strip
+ (scrubbed.start_with? ":") && (!scrubbed.include? " ") && (!scrubbed.include? "=>")
+ end
+
+ def code_comment? log_entry
+ return true if log_entry.strip.start_with?("# ")
+ return false
+ end
+
+ def codeblock_marker? log_entry
+ return true if log_entry.strip.start_with?("#+begin_src")
+ return true if log_entry.strip.start_with?("#+end_src")
+ return false
+ end
+
def color_for_log_entry(log_entry)
- if include_row_marker? log_entry
+ if code? log_entry
+ @code_color
+ elsif code_comment? log_entry
+ @comment_color
+ elsif include_row_marker? log_entry
@text_color
elsif include_error_marker? log_entry
@error_color
diff --git a/dragon/console_color.rb b/dragon/console_color.rb
index f5b164d..1b19c22 100644
--- a/dragon/console_color.rb
+++ b/dragon/console_color.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# console_color.rb has been released under MIT (*only this file*).
@@ -22,6 +23,10 @@ module GTK
@color
end
+ def to_s
+ "GTK::Console::Color #{to_h}"
+ end
+
def to_h
{ r: @color[0], g: @color[1], b: @color[2], a: @color[3] }
end
diff --git a/dragon/console_font_style.rb b/dragon/console_font_style.rb
index 8efab4f..0bb7cc4 100644
--- a/dragon/console_font_style.rb
+++ b/dragon/console_font_style.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# console_font_style.rb has been released under MIT (*only this file*).
@@ -33,7 +34,7 @@ module GTK
size_enum: size_enum,
alignment_enum: alignment_enum,
**color.to_h,
- }.label
+ }.label!
end
end
end
diff --git a/dragon/console_menu.rb b/dragon/console_menu.rb
index d10ceeb..0f21614 100644
--- a/dragon/console_menu.rb
+++ b/dragon/console_menu.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# console_menu.rb has been released under MIT (*only this file*).
@@ -51,7 +52,7 @@ module GTK
def itch_wizard_clicked
@console.scroll_to_bottom
- $wizards.itch.start
+ $wizards.itch.restart
end
def docs_clicked
@@ -76,6 +77,7 @@ module GTK
@buttons = [
(button id: :record, row: 0, col: 9, text: "record gameplay", method: :record_clicked),
(button id: :replay, row: 0, col: 10, text: "start replay", method: :replay_clicked),
+ *custom_buttons
]
elsif @menu_shown == :hidden
@buttons = [
@@ -134,8 +136,8 @@ module GTK
method: method
}.let do |entity|
primitives = []
- primitives << entity[:rect].merge(a: 164).solid
- primitives << entity[:rect].merge(r: 255, g: 255, b: 255).border
+ primitives << entity[:rect].solid!(a: 164)
+ primitives << entity[:rect].border!(r: 255, g: 255, b: 255)
primitives << text.wrapped_lines(5)
.map_with_index do |l, i|
[
diff --git a/dragon/console_prompt.rb b/dragon/console_prompt.rb
index d4c4026..97ea2f9 100644
--- a/dragon/console_prompt.rb
+++ b/dragon/console_prompt.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# console_prompt.rb has been released under MIT (*only this file*).
@@ -71,14 +72,14 @@ module GTK
return if @cursor_position.zero?
new_pos = @cursor_position - 1
- (is_word_boundary? @current_input_str[new_pos]) ?
+ (is_word_boundary? @current_input_str[new_pos]) ?
(new_pos -= 1 until !(is_word_boundary? @current_input_str[new_pos - 1]) || new_pos.zero?):
(new_pos -= 1 until (is_word_boundary? @current_input_str[new_pos - 1]) || new_pos.zero?)
@cursor_position = new_pos
update_cursor_position_px
end
-
+
def move_cursor_right
@cursor_position += 1 if @cursor_position < current_input_str.length
update_cursor_position_px
@@ -91,7 +92,7 @@ module GTK
(is_word_boundary? @current_input_str[new_pos]) ?
(new_pos += 1 until !(is_word_boundary? @current_input_str[new_pos]) || (new_pos.equal? str_len)):
(new_pos += 1 until (is_word_boundary? @current_input_str[new_pos]) || (new_pos.equal? str_len))
-
+
@cursor_position = new_pos
update_cursor_position_px
end
@@ -157,11 +158,11 @@ S
# partition the original list of items into a string to be printed
items.each_slice(columns).each_with_index do |cells, i|
- pretty_print_row_separator string_width, cell_width, column_width, columns
+ pretty_print_row_seperator string_width, cell_width, column_width, columns
pretty_print_row cells, string_width, cell_width, column_width, columns
end
- pretty_print_row_separator string_width, cell_width, column_width, columns
+ pretty_print_row_seperator string_width, cell_width, column_width, columns
end
end
@@ -193,12 +194,12 @@ S
args.outputs.reserved << (@cursor_color.to_h.merge x: x + @cursor_position_px + 0.5,
y: y + 5,
x2: x + @cursor_position_px + 0.5,
- y2: y + @font_style.letter_size.y + 5)
+ y2: y + @font_style.letter_size.y + 4)
args.outputs.reserved << (@cursor_color.to_h.merge x: x + @cursor_position_px + 1,
y: y + 5,
x2: x + @cursor_position_px + 1,
- y2: y + @font_style.letter_size.y + 5)
+ y2: y + @font_style.letter_size.y + 4)
# debugging rectangle for string
# args.outputs.reserved << (@cursor_color.to_h.merge x: x,
diff --git a/dragon/controller_config.rb b/dragon/controller_config.rb
deleted file mode 100644
index c315ab5..0000000
--- a/dragon/controller_config.rb
+++ /dev/null
@@ -1,396 +0,0 @@
-# Copyright 2019 DragonRuby LLC
-# MIT License
-# controller_config.rb has been released under MIT (*only this file*).
-
-# !!! FIXME: add console command to forget custom binding(s)
-# !!! FIXME: add console command to forget replace existing binding(s)
-# !!! FIXME: add console command go into play_around mode to make sure controller isn't wonky.
-
-module GTK
- class ControllerConfig
- def initialize runtime
- @runtime = runtime
- @raw_joysticks = {} # things that aren't game controllers to try to configure.
- @target = nil
- @animation_duration = (1.5).seconds
- @toggled_at = 0
- @fading = 0
- @current_part = 0
- @part_alpha = 0
- @part_alpha_increment = 10
- @joystick_state = {}
- @playing_around = false
- @used_bindings = {}
- @bindings = []
- @parts = [
- [ 919, 282, 'A button', 'a' ],
- [ 960, 323, 'B button', 'b' ],
- [ 878, 323, 'X button', 'x' ],
- [ 919, 365, 'Y button', 'y' ],
- [ 433, 246, 'left stick left', '-leftx' ],
- [ 497, 246, 'left stick right', '+leftx' ],
- [ 466, 283, 'left stick up', '-lefty' ],
- [ 466, 218, 'left stick down', '+lefty' ],
- [ 466, 246, 'left stick button', 'leftstick' ],
- [ 741, 246, 'right stick left', '-rightx' ],
- [ 802, 246, 'right stick right', '+rightx' ],
- [ 773, 283, 'right stick up', '-righty' ],
- [ 773, 218, 'right stick down', '+righty' ],
- [ 772, 246, 'right stick button', 'rightstick' ],
- [ 263, 465, 'left shoulder button', 'leftshoulder' ],
- [ 263, 503, 'left trigger', 'lefttrigger' ],
- [ 977, 465, 'right shoulder button', 'rightshoulder' ],
- [ 977, 503, 'right trigger', 'righttrigger' ],
- [ 318, 365, 'D-pad up', 'dpup' ],
- [ 360, 322, 'D-pad right', 'dpright' ],
- [ 318, 280, 'D-pad down', 'dpdown' ],
- [ 275, 322, 'D-pad left', 'dpleft' ],
- [ 570, 402, 'select/back button', 'back'],
- [ 619, 448, 'guide/home button', 'guide' ],
- [ 669, 402, 'start button', 'start' ],
- ]
- end
-
- def rawjoystick_connected jid, joystickname, guid
- return if jid < 0
- @raw_joysticks[jid] = { name: joystickname, guid: guid }
- end
-
- def rawjoystick_disconnected jid
- return if jid < 0
- if @raw_joysticks[jid] != nil
- @raw_joysticks.delete(jid)
- @runtime.ffi_misc.close_raw_joystick(jid)
- # Fade out the config screen if we were literally configuring this controller right now.
- if [email protected]? && @target[0] == jid
- @target[0] = nil
- @toggled_at = Kernel.global_tick_count
- @fading = -1
- end
- end
- end
-
- def build_binding_string
- bindingstr = ''
- skip = false
-
- if skip ; skip = false ; next ; end
-
- binding = @bindings[i]
- next if binding.nil?
-
- part = @parts[i][3]
-
- # clean up string:
- # if axis uses -a0 for negative and +a0 for positive, just make it "leftx:a0" instead of "-leftx:-a0,+leftx:+a0"
- # if axis uses +a0 for negative and -a0 for positive, just make it "leftx:a0~" instead of "-leftx:+a0,+leftx:-a0"
- if part == '-leftx' || part == '-lefty' || part == '-rightx' || part == '-righty'
- nextbinding = @bindings[i+1]
- if binding.start_with?('-a') && nextbinding.start_with?('+a') && binding[2..-1] == nextbinding[2..-1]
- skip = true
- part = part[1..-1]
- binding = binding[1..-1]
- elsif binding.start_with?('+a') && nextbinding.start_with?('-a') && binding[2..-1] == nextbinding[2..-1]
- skip = true
- part = part[1..-1]
- binding = "#{binding[1..-1]}~"
- end
- end
-
- bindingstr += "#{!bindingstr.empty? ? ',' : ''}#{part}:#{binding}"
- end
-
- details = @target[1]
-
- # !!! FIXME: no String.delete in mRuby?!?! Maybe so when upgrading.
- #name = details[:name].delete(',')
- # !!! FIXME: ...no regexp either... :/
- #name = details[:name].gsub(/,/, ' ') # !!! FIXME: will SDL let you escape these instead?
- unescaped = details[:name]
- name = ''
- for i in 0..unescaped.length-1
- ch = unescaped[i]
- name += (ch == ',') ? ' ' : ch
- end
- return "#{details[:guid]},#{name},platform:#{@runtime.platform},#{bindingstr}"
- end
-
- def move_to_different_part part
- if !@joystick_state[:axes].nil?
- @joystick_state[:axes].each { |i| i[:farthestval] = i[:startingval] if !i.nil? }
- end
- @current_part = part
- end
-
- def previous_part
- if @current_part > 0
- # remove the binding that we previously had here so it can be reused.
- bindstr = @bindings[@current_part - 1]
- @bindings[@current_part - 1] = nil
- @used_bindings[bindstr] = nil
- move_to_different_part @current_part - 1
- end
- end
-
- def next_part
- if @current_part < (@parts.length - 1)
- move_to_different_part @current_part + 1
- else
- @playing_around = true
- end
- end
-
- def set_binding bindstr
- return false if !@used_bindings[bindstr].nil?
- @used_bindings[bindstr] = @current_part
- @bindings[@current_part] = bindstr
- return true
- end
-
- # Called when a lowlevel joystick moves an axis.
- def rawjoystick_axis jid, axis, value
- return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
-
- @joystick_state[:axes] ||= []
- @joystick_state[:axes][axis] ||= {
- moving: false,
- startingval: 0,
- currentval: 0,
- farthestval: 0
- }
-
- # this is the logic from SDL's controllermap.c, more or less, since this is hard to get right from scratch.
- state = @joystick_state[:axes][axis]
- state[:currentval] = value
- if !state[:moving]
- state[:moving] = true
- state[:startingval] = value
- state[:farthestval] = value
- end
-
- current_distance = (value - state[:startingval]).abs
- farthest_distance = (state[:farthestval] - state[:startingval]).abs
- if current_distance > farthest_distance
- state[:farthestval] = value
- farthest_distance = (state[:farthestval] - state[:startingval]).abs
- end
-
- # If we've gone out far enough and started to come back, let's bind this axis
- if (farthest_distance >= 16000) && (current_distance <= 10000)
- next_part if set_binding("#{(state[:farthestval] < 0) ? '-' : '+'}a#{axis}")
- end
- end
-
- # Called when a lowlevel joystick moves a hat.
- def rawjoystick_hat jid, hat, value
- return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
-
- @joystick_state[:hats] ||= []
- @joystick_state[:hats][hat] = value
-
- return if value == 0 # 0 == centered, skip it
- next_part if set_binding("h#{hat}.#{value}")
- end
-
- # Called when a lowlevel joystick moves a button.
- def rawjoystick_button jid, button, pressed
- return if @target.nil? || jid != @target[0] || @fading != 0 # skip if not currently considering this joystick.
-
- @joystick_state[:buttons] ||= []
- @joystick_state[:buttons][button] = pressed
-
- return if !pressed
- next_part if set_binding("b#{button}")
- end
-
- def calc_fading
- if @fading == 0
- return 255
- elsif @fading > 0 # fading in
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip)
- if percent >= 1.0
- percent = 1.0
- @fading = 0
- end
- else # fading out
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint)
- if percent <= 0.0
- percent = 0.0
- @fading = 0
- end
- end
-
- return (percent * 255.0).to_i
- end
-
- def render_basics args, msg, fade=255
- joystickname = @target[1][:name]
- args.outputs.primitives << [0, 0, GAME_WIDTH, GAME_HEIGHT, 255, 255, 255, fade].solid
- args.outputs.primitives << [0, 0, GAME_WIDTH, GAME_HEIGHT, 'dragonruby-controller.png', 0, fade, 255, 255, 255].sprite
- args.outputs.primitives << [GAME_WIDTH / 2, 700, joystickname, 2, 1, 0, 0, 0, fade].label
- args.outputs.primitives << [GAME_WIDTH / 2, 650, msg, 0, 1, 0, 0, 0, 255].label if !msg.empty?
- end
-
- def render_part_highlight args, part, alpha=255
- partsize = 41
- args.outputs.primitives << [part[0], part[1], partsize, partsize, 255, 0, 0, alpha].border
- args.outputs.primitives << [part[0]-1, part[1]-1, partsize+2, partsize+2, 255, 0, 0, alpha].border
- args.outputs.primitives << [part[0]-2, part[1]-2, partsize+4, partsize+4, 255, 0, 0, alpha].border
- end
-
- def choose_target
- if @target.nil?
- while !@raw_joysticks.empty?
- t = @raw_joysticks.shift # see if there's a joystick waiting on us.
- next if t[0] < 0 # just in case.
- next if t[1][:guid].nil? # did we already handle this guid? Dump it.
- @target = t
- break
- end
- return false if @target.nil? # nothing to configure at the moment.
- @toggled_at = Kernel.global_tick_count
- @fading = 1
- @current_part = 0
- @part_alpha = 0
- @part_alpha_increment = 10
- @joystick_state = {}
- @used_bindings = {}
- @playing_around = false
- @bindings = []
- end
- return true
- end
-
- def render_part_highlight_from_bindstr args, bindstr, alpha=255
- partidx = @used_bindings[bindstr]
- return if partidx.nil?
- render_part_highlight args, @parts[partidx], alpha
- end
-
- def play_around args
- return false if !@playing_around
-
- if args.inputs.keyboard.key_down.escape
- @current_part = 0
- @part_alpha = 0
- @part_alpha_increment = 10
- @used_bindings = {}
- @playing_around = false
- @bindings = []
- elsif args.inputs.keyboard.key_down.space
- jid = @target[0]
- bindingstr = build_binding_string
- #puts("new controller binding: '#{bindingstr}'")
- @runtime.ffi_misc.add_controller_config bindingstr
- @runtime.ffi_misc.convert_rawjoystick_to_controller jid
- @target[0] = -1 # Conversion closes the raw joystick.
-
- # Handle any other pending joysticks that have the same GUID (so if you plug in four of the same model, we're already done!)
- guid = @target[1][:guid]
- @raw_joysticks.each { |jid, details|
- if details[:guid] == guid
- @runtime.ffi_misc.convert_rawjoystick_to_controller jid
- details[:guid] = nil
- end
- }
-
- # Done with this guy.
- @playing_around = false
- @toggled_at = Kernel.global_tick_count
- @fading = -1
- return false
- end
-
- render_basics args, 'Now play around with the controller, and make sure it feels right!'
- args.outputs.primitives << [GAME_WIDTH / 2, 90, '[ESCAPE]: Reconfigure, [SPACE]: Save this configuration', 0, 1, 0, 0, 0, 255].label
-
- axes = @joystick_state[:axes]
- if !axes.nil?
- for i in 0..axes.length-1
- next if axes[i].nil?
- value = axes[i][:currentval]
- next if value.nil? || (value.abs < 16000)
- render_part_highlight_from_bindstr args, "#{value < 0 ? '-' : '+'}a#{i}"
- end
- end
-
- hats = @joystick_state[:hats]
- if !hats.nil?
- for i in 0..hats.length-1
- value = hats[i]
- next if value.nil? || (value == 0)
- render_part_highlight_from_bindstr args, "h#{i}.#{value}"
- end
- end
-
- buttons = @joystick_state[:buttons]
- if !buttons.nil?
- for i in 0..buttons.length-1
- value = buttons[i]
- next if value.nil? || !value
- render_part_highlight_from_bindstr args, "b#{i}"
- end
- end
-
- return true
- end
-
- def should_tick?
- return true if @play_around
- return true if @target
- return false
- end
-
- def tick args
- return true if play_around args
- return false if !choose_target
-
- jid = @target[0]
-
- if @fading == 0
- # Cancel config?
- if args.inputs.keyboard.key_down.escape
- # !!! FIXME: prompt to ignore this joystick forever or just this run
- @toggled_at = Kernel.global_tick_count
- @fading = -1
- end
- end
-
- if @fading == 0
- if args.inputs.keyboard.key_down.backspace
- previous_part
- elsif args.inputs.keyboard.key_down.space
- next_part
- end
- end
-
- fade = calc_fading
- if (@fading < 0) && (fade == 0)
- @runtime.ffi_misc.close_raw_joystick(jid) if jid >= 0
- @target = nil # done with this controller
- return false
- end
-
- render_basics args, (@fading >= 0) ? "We don't recognize this controller, so tell us about it!" : '', fade
-
- return true if fade < 255 # all done for now
-
- part = @parts[@current_part]
- args.outputs.primitives << [GAME_WIDTH / 2, 575, "Please press the #{part[2]}.", 0, 1, 0, 0, 0, 255].label
- render_part_highlight args, part, @part_alpha
- args.outputs.primitives << [GAME_WIDTH / 2, 90, '[ESCAPE]: Ignore controller, [BACKSPACE]: Go back one button, [SPACE]: Skip this button', 0, 1, 0, 0, 0, 255].label
-
- @part_alpha += @part_alpha_increment
- if (@part_alpha_increment > 0) && (@part_alpha >= 255)
- @part_alpha = 255
- @part_alpha_increment = -10
- elsif (@part_alpha_increment < 0) && (@part_alpha <= 0)
- @part_alpha = 0
- @part_alpha_increment = 10
- end
-
- return true
- end
- end
-end
diff --git a/dragon/directional_input_helper_methods.rb b/dragon/directional_input_helper_methods.rb
index 44913ba..0a15c70 100644
--- a/dragon/directional_input_helper_methods.rb
+++ b/dragon/directional_input_helper_methods.rb
@@ -70,6 +70,12 @@ S
end
end
+ def directional_angle
+ return nil unless directional_vector
+
+ Math.atan2(up_down, left_right).to_degrees
+ end
+
def method_missing m, *args
# combine the key with ctrl_
if m.to_s.start_with?("ctrl_")
diff --git a/dragon/docs.rb b/dragon/docs.rb
index e6f6e2d..cd39483 100644
--- a/dragon/docs.rb
+++ b/dragon/docs.rb
@@ -46,7 +46,7 @@ module DocsOrganizer
unsorted.each do |k|
puts <<-S
* WARNING: #{klass.name} is not included in DocsOrganizer::class_sort_order. Please place this
-module in it's correct topological order.
+module in its correct topological order.
S
end
@@ -253,8 +253,9 @@ S
parse_log = []
html_start_to_toc_start = <<-S
-<html>
+<html lang="en">
<head>
+ <meta charset="utf-8">
<title>DragonRuby Game Toolkit Documentation</title>
<link href="docs.css?ver=#{Time.now.to_i}" rel="stylesheet" type="text/css" media="all">
</head>
@@ -342,6 +343,11 @@ S
__docs_append_true_line__ true_lines, current_true_line, parse_log
__docs_append_true_line__ true_lines, l, parse_log
current_true_line = ""
+ elsif l.start_with? "***** "
+ parse_log << "- Header detected."
+ __docs_append_true_line__ true_lines, current_true_line, parse_log
+ __docs_append_true_line__ true_lines, l, parse_log
+ current_true_line = ""
else
current_true_line += l.rstrip + " "
end
@@ -373,6 +379,9 @@ S
text = text.gsub("]", "-")
text = text.gsub(":", "-")
text = text.gsub(" ", "-")
+ text = text.gsub(".", "-")
+ text = text.gsub(",", "-")
+ text = text.gsub("?", "-")
text
end
@@ -434,6 +443,15 @@ S
link_id = text_to_id.call l
# toc_html += "<ul><ul><ul><li><a href='##{link_id}'>#{formatted_html}</a></li></ul></ul></ul>"
content_html += "<h4>#{__docs_line_to_html__ l, parse_log}</h4>\n"
+ elsif l.start_with? "**** "
+ parse_log << "- H5 detected."
+ content_html += close_list_if_needed.call inside_ul, inside_ol
+ inside_ol = false
+ inside_ul = false
+ formatted_html = __docs_line_to_html__ l, parse_log
+ link_id = text_to_id.call l
+ # toc_html += "<ul><ul><ul><li><a href='##{link_id}'>#{formatted_html}</a></li></ul></ul></ul>"
+ content_html += "<h5>#{__docs_line_to_html__ l, parse_log}</h5>\n"
elsif l.strip.length == 0 && !inside_pre
# do nothing
elsif l.start_with? "#+begin_src"
diff --git a/dragon/draw.rb b/dragon/draw.rb
index 128624d..7136994 100644
--- a/dragon/draw.rb
+++ b/dragon/draw.rb
@@ -1,4 +1,4 @@
-# Contributors outside of DragonRuby who also hold Copyright: Nick Sandberg
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# draw.rb has been released under MIT (*only this file*).
@@ -6,197 +6,44 @@
module GTK
class Runtime
module Draw
-
- def execute_draw_order pass
- # Don't change this draw order unless you understand
- # the implications.
- render_solids pass
- render_static_solids pass
- render_sprites pass
- render_static_sprites pass
- render_primitives pass
- render_static_primitives pass
- render_labels pass
- render_static_labels pass
- render_lines pass
- render_static_lines pass
- render_borders pass
- render_static_borders pass
- end
-
def primitives pass
if $top_level.respond_to? :primitives_override
return $top_level.tick_render @args, pass
end
- execute_draw_order pass
+ fn.each_send pass.solids, self, :draw_solid
+ fn.each_send pass.static_solids, self, :draw_solid
+ fn.each_send pass.sprites, self, :draw_sprite
+ fn.each_send pass.static_sprites, self, :draw_sprite
+ fn.each_send pass.primitives, self, :draw_primitive
+ fn.each_send pass.static_primitives, self, :draw_primitive
+ fn.each_send pass.labels, self, :draw_label
+ fn.each_send pass.static_labels, self, :draw_label
+ fn.each_send pass.lines, self, :draw_line
+ fn.each_send pass.static_lines, self, :draw_line
+ fn.each_send pass.borders, self, :draw_border
+ fn.each_send pass.static_borders, self, :draw_border
if !$gtk.production
- # pass.debug.each { |r| draw_primitive r }
- idx = 0
- length = pass.debug.length
- while idx < length
- draw_primitive (pass.debug.at idx)
- idx += 1
- end
-
- # pass.static_debug.each { |r| draw_primitive r }
- idx = 0
- length = pass.static_debug.length
- while idx < length
- draw_primitive (pass.static_debug.at idx)
- idx += 1
- end
+ fn.each_send pass.debug, self, :draw_primitive
+ fn.each_send pass.static_debug, self, :draw_primitive
end
- # pass.reserved.each { |r| draw_primitive r }
- idx = 0
- length = pass.reserved.length
- while idx < length
- draw_primitive (pass.reserved.at idx)
- idx += 1
- end
-
- # pass.static_reserved.each { |r| draw_primitive r }
- idx = 0
- length = pass.static_reserved.length
- while idx < length
- draw_primitive (pass.static_reserved.at idx)
- idx += 1
- end
+ fn.each_send pass.reserved, self, :draw_primitive
+ fn.each_send pass.static_reserved, self, :draw_primitive
rescue Exception => e
pause!
pretty_print_exception_and_export! e
end
-
- def render_solids pass
- # pass.solids.each { |s| draw_solid s }
- # while loops are faster than each with block
- idx = 0
- length = pass.solids.length
- while idx < pass.solids.length
- draw_solid (pass.solids.at idx) # accessing an array using .value instead of [] is faster
- idx += 1
- end
- end
-
- def render_static_solids pass
- # pass.static_solids.each { |s| draw_solid s }
- idx = 0
- length = pass.static_solids.length
- while idx < length
- draw_solid (pass.static_solids.at idx)
- idx += 1
- end
- end
-
- def render_sprites pass
- # pass.sprites.each { |s| draw_sprite s }
- idx = 0
- length = pass.sprites.length
- while idx < length
- draw_sprite (pass.sprites.at idx)
- idx += 1
- end
- end
-
- def render_static_sprites pass
- # pass.static_sprites.each { |s| draw_sprite s }
- idx = 0
- length = pass.static_sprites.length
- while idx < length
- draw_sprite (pass.static_sprites.at idx)
- idx += 1
- end
- end
-
- def render_primitives pass
- # pass.primitives.each { |p| draw_primitive p }
- idx = 0
- length = pass.primitives.length
- while idx < length
- draw_primitive (pass.primitives.at idx)
- idx += 1
- end
- end
-
- def render_static_primitives pass
- # pass.static_primitives.each { |p| draw_primitive p }
- idx = 0
- length = pass.static_primitives.length
- while idx < length
- draw_primitive (pass.static_primitives.at idx)
- idx += 1
- end
- end
-
- def render_labels pass
- # pass.labels.each { |l| draw_label l }
- idx = 0
- length = pass.labels.length
- while idx < length
- draw_label (pass.labels.at idx)
- idx += 1
- end
- end
-
- def render_static_labels pass
- # pass.static_labels.each { |l| draw_label l }
- idx = 0
- length = pass.static_labels.length
- while idx < length
- draw_label (pass.static_labels.at idx)
- idx += 1
- end
- end
-
- def render_lines pass
- # pass.lines.each { |l| draw_line l }
- idx = 0
- length = pass.lines.length
- while idx < length
- draw_line (pass.lines.at idx)
- idx += 1
- end
- end
-
- def render_static_lines pass
- # pass.static_lines.each { |l| draw_line l }
- idx = 0
- length = pass.static_lines.length
- while idx < pass.static_lines.length
- draw_line (pass.static_lines.at idx)
- idx += 1
- end
- end
-
- def render_borders pass
- # pass.borders.each { |b| draw_border b }
- idx = 0
- length = pass.borders.length
- while idx < length
- draw_border (pass.borders.at idx)
- idx += 1
- end
- end
-
- def render_static_borders pass
- # pass.static_borders.each { |b| draw_border b }
- idx = 0
- length = pass.static_borders.length
- while idx < length
- draw_border (pass.static_borders.at idx)
- idx += 1
- end
- end
-
def draw_solid s
return unless s
if s.respond_to? :draw_override
s.draw_override @ffi_draw
else
- @ffi_draw.draw_solid s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a
+ @ffi_draw.draw_solid_2 s.x, s.y, s.w, s.h,
+ s.r, s.g, s.b, s.a,
+ (s.blendmode_enum || 1)
end
rescue Exception => e
raise_conversion_for_rendering_failed s, e, :solid
@@ -207,14 +54,15 @@ module GTK
if s.respond_to? :draw_override
s.draw_override @ffi_draw
else
- @ffi_draw.draw_sprite_3 s.x, s.y, s.w, s.h,
- s.path.s_or_default,
+ @ffi_draw.draw_sprite_4 s.x, s.y, s.w, s.h,
+ (s.path || '').to_s,
s.angle,
s.a, s.r, s.g, s.b,
s.tile_x, s.tile_y, s.tile_w, s.tile_h,
!!s.flip_horizontally, !!s.flip_vertically,
s.angle_anchor_x, s.angle_anchor_y,
- s.source_x, s.source_y, s.source_w, s.source_h
+ s.source_x, s.source_y, s.source_w, s.source_h,
+ (s.blendmode_enum || 1)
end
rescue Exception => e
raise_conversion_for_rendering_failed s, e, :sprite
@@ -225,7 +73,7 @@ module GTK
if s.respond_to? :draw_override
s.draw_override @ffi_draw
else
- @ffi_draw.draw_screenshot s.path.s_or_default,
+ @ffi_draw.draw_screenshot (s.path || '').to_s,
s.x, s.y, s.w, s.h,
s.angle,
s.a, s.r, s.g, s.b,
@@ -243,10 +91,13 @@ module GTK
if l.respond_to? :draw_override
l.draw_override @ffi_draw
else
- @ffi_draw.draw_label l.x, l.y, l.text.s_or_default,
- l.size_enum, l.alignment_enum,
- l.r, l.g, l.b, l.a,
- l.font.s_or_default(nil)
+ @ffi_draw.draw_label_3 l.x, l.y,
+ (l.text || '').to_s,
+ l.size_enum, l.alignment_enum,
+ l.r, l.g, l.b, l.a,
+ l.font,
+ (l.vertical_alignment_enum || 2),
+ (l.blendmode_enum || 1)
end
rescue Exception => e
raise_conversion_for_rendering_failed l, e, :label
@@ -257,7 +108,21 @@ module GTK
if l.respond_to? :draw_override
l.draw_override @ffi_draw
else
- @ffi_draw.draw_line l.x, l.y, l.x2, l.y2, l.r, l.g, l.b, l.a
+ if l.x2
+ @ffi_draw.draw_line_2 l.x, l.y, l.x2, l.y2,
+ l.r, l.g, l.b, l.a,
+ (l.blendmode_enum || 1)
+ else
+ w = l.w || 0
+ w = 1 if w == 0
+ h = l.h || 0
+ h = 1 if h == 0
+ @ffi_draw.draw_line_2 l.x, l.y,
+ l.x + w - 1,
+ l.y + h - 1,
+ l.r, l.g, l.b, l.a,
+ (l.blendmode_enum || 1)
+ end
end
rescue Exception => e
raise_conversion_for_rendering_failed l, e, :line
@@ -268,7 +133,9 @@ module GTK
if s.respond_to? :draw_override
s.draw_override @ffi_draw
else
- @ffi_draw.draw_border s.x, s.y, s.w, s.h, s.r, s.g, s.b, s.a
+ @ffi_draw.draw_border_2 s.x, s.y, s.w, s.h,
+ s.r, s.g, s.b, s.a,
+ (s.blendmode_enum || 1)
end
rescue Exception => e
raise_conversion_for_rendering_failed s, e, :border
@@ -288,7 +155,6 @@ module GTK
pause!
pretty_print_exception_and_export! e
end
-
end
end
end
diff --git a/dragon/framerate.rb b/dragon/framerate.rb
index 2550443..a9504a6 100644
--- a/dragon/framerate.rb
+++ b/dragon/framerate.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# framerate.rb has been released under MIT (*only this file*).
diff --git a/dragon/framerate_diagnostics.rb b/dragon/framerate_diagnostics.rb
index 4586472..6985011 100644
--- a/dragon/framerate_diagnostics.rb
+++ b/dragon/framerate_diagnostics.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# framerate_diagnostics.rb has been released under MIT (*only this file*).
@@ -115,7 +116,7 @@ If this warning is getting annoying put the following in your tick method:
def framerate_diagnostics_primitives
[
- { x: 0, y: 93.from_top, w: 500, h: 93, a: 128 }.solid,
+ { x: 0, y: 93.from_top, w: 500, h: 93, a: 128 }.solid!,
{
x: 5,
y: 5.from_top,
@@ -124,7 +125,7 @@ If this warning is getting annoying put the following in your tick method:
g: 255,
b: 255,
size_enum: -2
- }.label,
+ }.label!,
{
x: 5,
y: 20.from_top,
@@ -133,7 +134,7 @@ If this warning is getting annoying put the following in your tick method:
g: 255,
b: 255,
size_enum: -2
- }.label,
+ }.label!,
{
x: 5,
y: 35.from_top,
@@ -142,7 +143,7 @@ If this warning is getting annoying put the following in your tick method:
g: 255,
b: 255,
size_enum: -2
- }.label,
+ }.label!,
{
x: 5,
y: 50.from_top,
@@ -151,7 +152,7 @@ If this warning is getting annoying put the following in your tick method:
g: 255,
b: 255,
size_enum: -2
- }.label,
+ }.label!,
{
x: 5,
y: 65.from_top,
@@ -160,7 +161,7 @@ If this warning is getting annoying put the following in your tick method:
g: 255,
b: 255,
size_enum: -2
- }.label,
+ }.label!,
]
end
diff --git a/dragon/geometry.rb b/dragon/geometry.rb
index c2974db..6bceea8 100644
--- a/dragon/geometry.rb
+++ b/dragon/geometry.rb
@@ -5,6 +5,22 @@
module GTK
module Geometry
+ def self.rotate_point point, angle, around = nil
+ s = Math.sin a.to_radians
+ c = Math.cos a.to_radians
+ px = point.x
+ py = point.y
+ cx = 0
+ cy = 0
+ if around
+ cx = around.x
+ cy = around.y
+ end
+
+ point.merge(x: ((px - cx) * c - (py - cy) * s) + cx,
+ y: ((px - cx) * s + (py - cy) * c) + cy)
+ end
+
# Returns f(t) for a cubic Bezier curve.
def self.cubic_bezier t, a, b, c, d
s = 1 - t
@@ -86,7 +102,7 @@ module GTK
rescue Exception => e
raise e, <<-S
* ERROR:
-center_inside_rect for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+center_inside_rect for self #{self} and other_rect #{other_rect}.\n#{e}.
S
end
@@ -103,7 +119,7 @@ S
rescue Exception => e
raise e, <<-S
* ERROR:
-center_inside_rect_x for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+center_inside_rect_x for self #{self} and other_rect #{other_rect}.\n#{e}.
S
end
@@ -120,7 +136,7 @@ S
rescue Exception => e
raise e, <<-S
* ERROR:
-center_inside_rect_y for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
+center_inside_rect_y for self #{self} and other_rect #{other_rect}.\n#{e}.
S
end
@@ -265,10 +281,10 @@ S
# @gtk
def self.intersect_rect? rect_one, rect_two, tolerance = 0.1
- return false if rect_one.right - tolerance < rect_two.left + tolerance
- return false if rect_one.left + tolerance > rect_two.right - tolerance
- return false if rect_one.top - tolerance < rect_two.bottom + tolerance
- return false if rect_one.bottom + tolerance > rect_two.top - tolerance
+ return false if ((rect_one.x + rect_one.w) - tolerance) < (rect_two.x + tolerance)
+ return false if (rect_one.x + tolerance) > ((rect_two.x + rect_two.w) - tolerance)
+ return false if ((rect_one.y + rect_one.h) - tolerance) < (rect_two.y + tolerance)
+ return false if (rect_one.y + tolerance) > ((rect_two.y + rect_two.h) - tolerance)
return true
rescue Exception => e
context_help_rect_one = (rect_one.__help_contract_implementation contract_intersect_rect?)[:not_implemented_methods]
@@ -296,6 +312,7 @@ S
- rect_one: #{rect_one}
- rect_two: #{rect_two}
#{context_help}
+\n#{e}
S
end
@@ -306,14 +323,14 @@ S
y = y.shift_down(size * anchor_y)
[x, y, size, size]
rescue Exception => e
- raise e, ":to_square failed for size: #{size} x: #{x} y: #{y} anchor_x: #{anchor_x} anchor_y: #{anchor_y}."
+ raise e, ":to_square failed for size: #{size} x: #{x} y: #{y} anchor_x: #{anchor_x} anchor_y: #{anchor_y}.\n#{e}"
end
# @gtk
def self.distance point_one, point_two
Math.sqrt((point_two.x - point_one.x)**2 + (point_two.y - point_one.y)**2)
rescue Exception => e
- raise e, ":distance failed for point_one: #{point_one} point_two #{point_two}."
+ raise e, ":distance failed for point_one: #{point_one} point_two #{point_two}.\n#{e}"
end
# @gtk
@@ -322,31 +339,34 @@ S
d_x = end_point.x - start_point.x
Math::PI.+(Math.atan2(d_y, d_x)).to_degrees
rescue Exception => e
- raise e, ":angle_from failed for start_point: #{start_point} end_point: #{end_point}."
+ raise e, ":angle_from failed for start_point: #{start_point} end_point: #{end_point}.\n#{e}"
end
# @gtk
def self.angle_to start_point, end_point
angle_from end_point, start_point
rescue Exception => e
- raise e, ":angle_to failed for start_point: #{start_point} end_point: #{end_point}."
+ raise e, ":angle_to failed for start_point: #{start_point} end_point: #{end_point}.\n#{e}"
end
# @gtk
def self.point_inside_circle? point, circle_center_point, radius
(point.x - circle_center_point.x) ** 2 + (point.y - circle_center_point.y) ** 2 < radius ** 2
rescue Exception => e
- raise e, ":point_inside_circle? failed for point: #{point} circle_center_point: #{circle_center_point} radius: #{radius}"
+ raise e, ":point_inside_circle? failed for point: #{point} circle_center_point: #{circle_center_point} radius: #{radius}.\n#{e}"
end
# @gtk
def self.inside_rect? inner_rect, outer_rect, tolerance = 0.0
+ return nil if !inner_rect
+ return nil if !outer_rect
+
inner_rect.x + tolerance >= outer_rect.x - tolerance &&
- inner_rect.right - tolerance <= outer_rect.right + tolerance &&
+ (inner_rect.x + inner_rect.w) - tolerance <= (outer_rect.x + outer_rect.w) + tolerance &&
inner_rect.y + tolerance >= outer_rect.y - tolerance &&
- inner_rect.top - tolerance <= outer_rect.top + tolerance
+ (inner_rect.y + inner_rect.h) - tolerance <= (outer_rect.y + outer_rect.h) + tolerance
rescue Exception => e
- raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}."
+ raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}.\n#{e}"
end
# @gtk
@@ -381,7 +401,7 @@ S
return rect
end
rescue Exception => e
- raise e, ":scale_rect_extended failed for rect: #{rect} percentage_x: #{percentage_x} percentage_y: #{percentage_y} anchors_x: #{anchor_x} anchor_y: #{anchor_y}."
+ raise e, ":scale_rect_extended failed for rect: #{rect} percentage_x: #{percentage_x} percentage_y: #{percentage_y} anchors_x: #{anchor_x} anchor_y: #{anchor_y}.\n#{e}"
end
# @gtk
@@ -395,7 +415,21 @@ S
anchor_x: anchor_x,
anchor_y: anchor_y
rescue Exception => e
- raise e, ":scale_rect failed for rect: #{rect} percentage: #{percentage} anchors [#{anchor_x} (x), #{anchor_y} (y)]."
+ raise e, ":scale_rect failed for rect: #{rect} percentage: #{percentage} anchors [#{anchor_x} (x), #{anchor_y} (y)].\n#{e}"
+ end
+
+ def self.rect_to_line rect
+ l = rect.to_hash.line
+ l.merge(x2: l.x + l.w - 1,
+ y2: l.y + l.h)
+ end
+
+ def self.rect_center_point rect
+ { x: rect.x + rect.w.half, y: rect.y + rect.h.half }
+ end
+
+ def rect_center_point
+ Geometry.rect_center_point self
end
end # module Geometry
end # module GTK
diff --git a/dragon/grid.rb b/dragon/grid.rb
index c2a6a43..2c808fc 100644
--- a/dragon/grid.rb
+++ b/dragon/grid.rb
@@ -185,5 +185,13 @@ module GTK
def bottom_right
[@right, @bottom].point
end
+
+ def x
+ 0
+ end
+
+ def y
+ 0
+ end
end
end
diff --git a/dragon/help.rb b/dragon/help.rb
deleted file mode 100644
index 9644347..0000000
--- a/dragon/help.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# coding: utf-8
-# Copyright 2019 DragonRuby LLC
-# MIT License
-# help.rb has been released under MIT (*only this file*).
-
-module GTK
- class Help
- def self.primitive_contract primitive_name
- if primitive_name == :label
- label_contract
- elsif primitive_name == :solid
- solid_border_contract
- elsif primitive_name == :border
- solid_border_contract
- elsif primitive_name == :sprite
- sprite_contract
- else
- help_text = "No contract found for primitive #{primitive_name}. The supported primitives are :label, :solid, :border, :sprite."
- end
- end
-
- def self.label_contract
- <<-S
-* :label (if :primitive_marker returns :label)
-** :x, :y, :text
-** :size_enum
-default: 0
-negative value means smaller text
-positive value means larger text
-** :alignment_enum default: 0
-default: 0
-0: left aligned, 1: center aligned, 2: right aligned
-** :r, :g, :b, :a
-default: 0's for rgb and 255 for a
-** :font
-default nil
-path to ttf file
-S
- end
-
- def self.solid_border_contract
- <<-S
-* :solid, :border (if :primitive_marker returns :solid or :border)
-** :x, :y, :w, :h, :r, :g, :b, :a
-S
- end
-
- def self.label_contract
- <<-S
-* :line (if :primitive_marker returns :line)
-** :x, :y, :x2, :y2, :r, :g, :b, :a
-S
- end
-
- def self.sprite_contract
- <<-S
-* :sprite (if :primitive_marker returns :sprite)
-** :x, :y, :w, :h
-** :angle, :angle_anchor_x, :angle_anchor_y
-default for angle: 0 (0 to 360 degrees)
-default for angle_anchor_(x|y): 0 (decimal value between 0 and 1.0, 0.5 means center)
-** :r, :g, :b, :a
-** :tile_x, :tile_y
-default: 0, x, y offset for sprite to crop at
-** :tile_w, :tile_h
-default: -1, width and height of crop (-1 means full width and height)
-** :flip_horizontally, :flip_vertically
-default: falsey value
-S
- end
- end
-end
diff --git a/dragon/hotload.rb b/dragon/hotload.rb
index 1cb062a..1196e2b 100644
--- a/dragon/hotload.rb
+++ b/dragon/hotload.rb
@@ -10,7 +10,14 @@ module GTK
def hotload_init
@hotload_if_needed = 0
@mailbox_if_needed = 0
+
+ # schema for file_mtimes
+ # { FILE_PATH: { current: (Time as Fixnum),
+ # last: (Time as Fixnum) },
+ # FILE_PATH: { current: (Time as Fixnum),
+ # last: (Time as Fixnum) } }
@file_mtimes = { }
+
@suppress_mailbox = true
files_to_reload.each { |f| init_mtimes f }
init_mtimes 'app/mailbox.rb'
@@ -48,6 +55,7 @@ module GTK
'dragon/symbol.rb',
'dragon/numeric_deprecated.rb',
'dragon/numeric.rb',
+ 'dragon/hash_deprecated.rb',
'dragon/hash.rb',
'dragon/outputs_deprecated.rb',
'dragon/array_docs.rb',
@@ -80,6 +88,7 @@ module GTK
'dragon/trace.rb',
'dragon/readme_docs.rb',
'dragon/hotload_client.rb',
+ 'dragon/wizards.rb',
'dragon/ios_wizard.rb',
'dragon/itch_wizard.rb',
] + core_files_to_reload + @required_files
@@ -98,10 +107,8 @@ module GTK
end
def init_mtimes file
- current_key = "current_#{file}".to_sym
- last_key = "last_#{file}".to_sym
- @file_mtimes[current_key] ||= @ffi_file.mtime(file)
- @file_mtimes[last_key] ||= @ffi_file.mtime(file)
+ @file_mtimes[file] ||= { current: @ffi_file.mtime(file),
+ last: @ffi_file.mtime(file) }
end
def hotload_source_files
@@ -133,25 +140,36 @@ module GTK
end
def hotload_if_needed
+ return if Kernel.tick_count < 0
hotload_source_files
check_mailbox
end
def on_load_succeeded file
- @rcb_sender.files_reloaded << file
- @rcb_sender.reloaded_files << file
+ self.files_reloaded << file
+ self.reloaded_files << file
Trace.untrace_classes!
end
+ def reset_all_mtimes
+ @file_mtimes.each do |file, _|
+ @file_mtimes[file].current = @ffi_file.mtime(file)
+ @file_mtimes[file].last = @file_mtimes[file].current
+ end
+
+ files_to_reload.each do |file, _|
+ @file_mtimes[file] ||= {}
+ @file_mtimes[file].current = @ffi_file.mtime(file)
+ @file_mtimes[file].last = @file_mtimes[file].current
+ end
+ end
+
def reload_if_needed file, force = false
- current_key = "current_#{file}".to_sym
- last_key = "last_#{file}".to_sym
- @file_mtimes[current_key] ||= nil
- @file_mtimes[last_key] ||= nil
- @file_mtimes[current_key] = @ffi_file.mtime(file)
- return if !force && @file_mtimes[last_key] == @file_mtimes[current_key]
+ @file_mtimes[file] ||= { current: @ffi_file.mtime(file), last: @ffi_file.mtime(file) }
+ @file_mtimes[file].current = @ffi_file.mtime(file)
+ return if !force && @file_mtimes[file].current == @file_mtimes[file].last
on_load_succeeded file if reload_ruby_file file
- @file_mtimes[last_key] = @file_mtimes[current_key]
+ @file_mtimes[file].last = @file_mtimes[file].current
end
end
end
diff --git a/dragon/inputs.rb b/dragon/inputs.rb
index 0ae4f86..163fc16 100644
--- a/dragon/inputs.rb
+++ b/dragon/inputs.rb
@@ -191,6 +191,30 @@ module GTK
}
end
+ def self.method_to_key_hash
+ return @method_to_key_hash if @method_to_key_hash
+ @method_to_key_hash = {}
+ string_representation_overrides ||= {
+ backspace: '\b'
+ }
+ char_to_method_hash.each do |k, v|
+ v.each do |vi|
+ t = { char_or_raw_key: k }
+
+ if k.is_a? Numeric
+ t[:raw_key] = k
+ t[:string_representation] = "raw_key == #{k}"
+ else
+ t[:char] = k
+ t[:string_representation] = "\"#{k.strip}\""
+ end
+
+ @method_to_key_hash[vi] = t
+ end
+ end
+ @method_to_key_hash
+ end
+
def self.char_to_method char, int = nil
methods = char_to_method_hash[char] || char_to_method_hash[int]
methods ? methods.dup : [char.to_sym || int]
@@ -305,24 +329,32 @@ S
end
def method_missing m, *args
- begin
- define_singleton_method(m) do
- r = self.instance_variable_get("@#{m.without_ending_bang}".to_sym)
- clear_key m
- return r
+ if KeyboardKeys.method_to_key_hash[m.without_ending_bang]
+ begin
+ define_singleton_method(m) do
+ r = self.instance_variable_get("@#{m.without_ending_bang}".to_sym)
+ clear_key m
+ return r
+ end
+
+ return self.send m
+ rescue Exception => e
+ log_important "#{e}"
end
-
- return self.send m
- rescue Exception => e
- log_important "#{e}"
end
+ did_you_mean = KeyboardKeys.method_to_key_hash.find_all do |k, v|
+ k.to_s[0..1] == m.to_s[0..1]
+ end.map {|k, v| ":#{k} (#{v[:string_representation]})" }
+ did_you_mean_string = ""
+ did_you_mean_string = ". Did you mean #{did_you_mean.join ", "}?"
+
raise <<-S
* ERROR:
-There is no member on the keyboard called #{m}. Here is a to_s representation of what's available:
-
-#{KeyboardKeys.char_to_method_hash.map { |k, v| "[#{k} => #{v.join(",")}]" }.join(" ")}
+#{KeyboardKeys.method_to_key_hash.map { |k, v| "** :#{k} #{v.string_representation}" }.join("\n")}
+There is no key on the keyboard called :#{m}#{did_you_mean_string}.
+Full list of available keys =:points_up:=.
S
end
@@ -706,6 +738,10 @@ module GTK
(controller_one && controller_one.directional_vector)
end
+ def directional_angle
+ keyboard.directional_angle || (controller_one && controller_one.directional_angle)
+ end
+
# Returns a signal indicating right (`1`), left (`-1`), or neither ('0').
#
# @return [Integer]
diff --git a/dragon/ios_wizard.rb b/dragon/ios_wizard.rb
index c1c032d..a64cdc9 100644
--- a/dragon/ios_wizard.rb
+++ b/dragon/ios_wizard.rb
@@ -1,19 +1,11 @@
-# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# ios_wizard.rb has been released under MIT (*only this file*).
-class WizardException < Exception
- attr_accessor :console_primitives
-
- def initialize *console_primitives
- @console_primitives = console_primitives
- end
-end
-
-class IOSWizard
- include Metadata
+# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński
+class IOSWizard < Wizard
def initialize
@doctor_executed_at = 0
end
@@ -26,23 +18,45 @@ class IOSWizard
@steps ||= []
end
- def steps_development_build
+ def prerequisite_steps
[
:check_for_xcode,
:check_for_brew,
:check_for_certs,
- :check_for_device,
- :check_for_dev_profile,
+ ]
+ end
+
+ def app_metadata_retrieval_steps
+ [
:determine_team_identifier,
:determine_app_name,
:determine_app_id,
- :blow_away_temp,
+ ]
+ end
+
+ def steps_development_build
+ [
+ *prerequisite_steps,
+
+ :check_for_device,
+ :check_for_dev_profile,
+
+ *app_metadata_retrieval_steps,
+
+ :clear_tmp_directory,
:stage_app,
+
:development_write_info_plist,
+
:write_entitlements_plist,
:compile_icons,
- :create_payload_directory,
+ :clear_payload_directory,
+
+ :create_payload_directory_dev,
+
+ :create_payload,
:code_sign_payload,
+
:create_ipa,
:deploy
]
@@ -50,20 +64,27 @@ class IOSWizard
def steps_production_build
[
- :check_for_xcode,
- :check_for_brew,
- :check_for_certs,
+ *prerequisite_steps,
+
:check_for_distribution_profile,
- :determine_team_identifier,
- :determine_app_name,
- :determine_app_id,
- :blow_away_temp,
+ :determine_app_version,
+
+ *app_metadata_retrieval_steps,
+
+ :clear_tmp_directory,
:stage_app,
+
:production_write_info_plist,
+
:write_entitlements_plist,
:compile_icons,
- :create_payload_directory,
+ :clear_payload_directory,
+
+ :create_payload_directory_prod,
+
+ :create_payload,
:code_sign_payload,
+
:create_ipa,
:print_publish_help
]
@@ -84,31 +105,25 @@ class IOSWizard
sprite_path
end
- def start opts = {}
- @opts = opts
+ def start opts = nil
+ @opts = opts || {}
- unless $gtk.args.fn.eq_any? @opts[:env], :dev, :prod
+ if !(@opts.is_a? Hash) || !($gtk.args.fn.eq_any? @opts[:env], :dev, :prod)
raise WizardException.new(
- "* $wizards.ios.start needs to be provided an environment option.",
- "** For development builds type: $wizards.ios.start env: :dev",
- "** For production builds type: $wizards.ios.start env: :prod"
- )
+ "* $wizards.ios.start needs to be provided an environment option.",
+ "** For development builds type: $wizards.ios.start env: :dev",
+ "** For production builds type: $wizards.ios.start env: :prod"
+ )
end
@production_build = (@opts[:env] == :prod)
-
- @version = determine_app_version @opts
- log_info "I will be using version: '#{@version}'" if @production_build
-
@steps = steps_development_build
@steps = steps_production_build if @production_build
@certificate_name = nil
+ @app_version = opts[:version]
+ @app_version = "1.0" if @opts[:env] == :dev && !@app_version
init_wizard_status
- if @production_build
- log_info "Starting iOS Wizard so we can create a production build."
- else
- log_info "Starting iOS Wizard so we can deploy to your device."
- end
+ log_info "Starting iOS Wizard so we can deploy to your device."
@start_at = Kernel.global_tick_count
steps.each do |m|
log_info "Running step ~:#{m}~."
@@ -128,8 +143,10 @@ class IOSWizard
log "=" * $console.console_text_width
else
log_error e.to_s
+ log e.__backtrace_to_org__
end
+ init_wizard_status
$console.set_command "$wizards.ios.start env: :#{@opts[:env]}"
end
@@ -236,25 +253,69 @@ class IOSWizard
return "profiles/development.mobileprovision"
end
+ def ios_metadata_template
+ <<-S
+# ios_metadata.txt is used by the Pro version of DragonRuby Game Toolkit to create iOS apps.
+# Information about the Pro version can be found at: http://dragonruby.org/toolkit/game#purchase
+
+# teamid needs to be set to your assigned Team Id which can be found at https://developer.apple.com/account/#/membership/
+teamid=
+# appid needs to be set to your application identifier which can be found at https://developer.apple.com/account/resources/identifiers/list
+appid=
+# appname is the name you want to show up underneath the app icon on the device. Keep it under 10 characters.
+appname=
+S
+ end
+
+ def ios_metadata
+ contents = $gtk.read_file 'metadata/ios_metadata.txt'
+
+ if !contents
+ $gtk.write_file 'metadata/ios_metadata.txt', ios_metadata_template
+ contents = $gtk.read_file 'metadata/ios_metadata.txt'
+ end
+
+ kvps = contents.each_line
+ .reject { |l| l.strip.length == 0 || (l.strip.start_with? "#") }
+ .map do |l|
+ key, value = l.split("=")
+ [key.strip.to_sym, value.strip]
+ end.flatten
+ Hash[*kvps]
+ end
+
+ def game_metadata
+ contents = $gtk.read_file 'metadata/game_metadata.txt'
+
+ kvps = contents.each_line
+ .reject { |l| l.strip.length == 0 || (l.strip.start_with? "#") }
+ .map do |l|
+ key, value = l.split("=")
+ [key.strip.to_sym, value.strip]
+ end.flatten
+ Hash[*kvps]
+ end
+
+ def raise_ios_metadata_required
+ raise WizardException.new(
+ "* mygame/metadata/ios_metadata.txt needs to be filled out.",
+ "You need to update metadata/ios_metadata.txt with a valid teamid, appname, and appid.",
+ "Instructions for where the values should come from are within metadata/ios_metadata.txt."
+ )
+ end
+
def determine_team_identifier
- @team_name = (team_identifier_from_provisioning_profile @opts[:env])
- log_info "Team Identifier is: #{@team_name}"
+ @team_id = (ios_metadata.teamid || "")
+ raise_ios_metadata_required if @team_id.strip.length == 0
+ log_info "Team Identifer is: #{@team_id}"
end
def determine_app_name
- @app_name = (provisioning_profile_xml @opts[:env])[:children].first[:children].first[:children][1][:children].first[:data]
+ @app_name = (ios_metadata.appname || "")
+ raise_ios_metadata_required if @app_name.strip.length == 0
log_info "App name is: #{@app_name}."
end
- def determine_app_version opts
- version = @opts[:version]
- unless version
- version = get_metadata[:version]
- version = version.start_with?('#') ? '1.0' : version.split('=').last
- end
- version.to_s
- end
-
def provisioning_profile_xml environment
xml = $gtk.read_file (provisioning_profile_path environment)
scrubbed = xml.each_line.map do |l|
@@ -275,36 +336,9 @@ class IOSWizard
$gtk.parse_xml scrubbed
end
- def app_id_from_provisioning_profile environment
- application_identifier_index = (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children][0][:children][0][:data]
- (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children].each.with_index do |node, i|
- if node[:children] && node[:children][0] && node[:children][0][:data] == "application-identifier"
- application_identifier_index = i
- break
- end
- end
-
- app_id_with_team_identifier = (provisioning_profile_xml environment)[:children].first[:children].first[:children][13][:children][application_identifier_index + 1][:children].first[:data]
- team_identifier = team_identifier_from_provisioning_profile environment
- app_id_with_team_identifier.gsub "#{team_identifier}.", ""
- end
-
- def team_identifier_from_provisioning_profile environment
- team_identifier_index = (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children][0][:children][0][:data]
-
- (provisioning_profile_xml environment)[:children][0][:children][0][:children][13][:children].each.with_index do |node, i|
- if node[:children] && node[:children][0] && node[:children][0][:data] == "com.apple.developer.team-identifier"
- team_identifier_index = i
- break
- end
- end
-
- (provisioning_profile_xml environment)[:children].first[:children].first[:children][13][:children][team_identifier_index + 1][:children].first[:data]
- end
-
def determine_app_id
- @app_id = app_id_from_provisioning_profile @opts[:env]
-
+ @app_id = ios_metadata.appid
+ raise_ios_metadata_required if @app_id.strip.length == 0
log_info "App Identifier is set to : #{@app_id}"
end
@@ -323,7 +357,7 @@ class IOSWizard
end
end
- def blow_away_temp
+ def clear_tmp_directory
sh "rm -rf #{tmp_directory}"
end
@@ -377,7 +411,7 @@ class IOSWizard
"** 1. Open Xcode.",
"** 2. Log into your developer account. Xcode -> Preferences -> Accounts.",
{ w: 700, h: 98, path: get_reserved_sprite("login-xcode.png") },
- "** 3. After logging in, select Manage Certificates...",
+ "** 3. After loggin in, select Manage Certificates...",
{ w: 700, h: 115, path: get_reserved_sprite("manage-certificates.png") },
"** 4. Add a certificate for Apple Development.",
{ w: 700, h: 217, path: get_reserved_sprite("add-cert.png") },
@@ -464,7 +498,8 @@ XML
log_info "Creating Entitlements.plist"
- $gtk.write_file_root "tmp/ios/Entitlements.plist", entitlement_plist_string.gsub(":app_id", "#{@team_name}.#{@app_id}").strip
+ $gtk.write_file_root "tmp/ios/Entitlements.plist", entitlement_plist_string.gsub(":app_id", "#{@team_id}.#{@app_id}").strip
+ $gtk.write_file_root "tmp/ios/Entitlements.txt", entitlement_plist_string.gsub(":app_id", "#{@team_id}.#{@app_id}").strip
sh "/usr/bin/plutil -convert binary1 \"#{tmp_directory}/Entitlements.plist\""
sh "/usr/bin/plutil -convert xml1 \"#{tmp_directory}/Entitlements.plist\""
@@ -502,15 +537,15 @@ XML
<key>CFBundleExecutable</key>
<string>:app_name</string>
<key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
+ <string>:app_version</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>5.6</string>
+ <string>:app_version</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>5.6</string>
+ <string>:app_version</string>
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
@@ -659,13 +694,13 @@ XML
<key>CFBundleIdentifier</key>
<string>:app_id</string>
<key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
+ <string>:app_version</string>
<key>CFBundleName</key>
<string>:app_name</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>5.2</string>
+ <string>:app_version</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
@@ -673,7 +708,7 @@ XML
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
- <string>5.2</string>
+ <string>:app_version</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
@@ -759,6 +794,7 @@ XML
info_plist_string.gsub!(":app_id", @app_id)
$gtk.write_file_root "tmp/ios/#{@app_name}.app/Info.plist", info_plist_string.strip
+ $gtk.write_file_root "tmp/ios/Info.txt", info_plist_string.strip
@info_plist_written = true
end
@@ -812,13 +848,13 @@ XML
<key>CFBundleIdentifier</key>
<string>:app_id</string>
<key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
+ <string>:app_version</string>
<key>CFBundleName</key>
<string>:app_name</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>:version</string>
+ <string>:app_version</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
@@ -826,7 +862,7 @@ XML
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
- <string>:version</string>
+ <string>:app_version</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
@@ -910,9 +946,10 @@ XML
info_plist_string.gsub!(":app_name", @app_name)
info_plist_string.gsub!(":app_id", @app_id)
- info_plist_string.gsub!(":version", @version);
+ info_plist_string.gsub!(":app_version", @app_version)
$gtk.write_file_root "tmp/ios/#{@app_name}.app/Info.plist", info_plist_string.strip
+ $gtk.write_file_root "tmp/ios/Info.txt", info_plist_string.strip
@info_plist_written = true
end
@@ -934,28 +971,53 @@ XML
"#{relative_path}/#{$gtk.cli_arguments[:dragonruby]}"
end
- def write_ip_address
+ def embed_mobileprovision
+ sh %Q[cp #{@provisioning_profile_path} "#{app_path}/embedded.mobileprovision"]
+ sh %Q[/usr/bin/plutil -convert binary1 "#{app_path}/Info.plist"]
+ end
+
+ def clear_payload_directory
+ sh %Q[rm "#{@app_name}".ipa]
+ sh %Q[rm -rf "#{app_path}/app"]
+ sh %Q[rm -rf "#{app_path}/sounds"]
+ sh %Q[rm -rf "#{app_path}/sprites"]
+ sh %Q[rm -rf "#{app_path}/data"]
+ sh %Q[rm -rf "#{app_path}/fonts"]
+ end
+
+ def stage_app
+ sh %Q[cp -r "#{root_folder}/app/" "#{app_path}/app/"]
+ sh %Q[cp -r "#{root_folder}/sounds/" "#{app_path}/sounds/"]
+ sh %Q[cp -r "#{root_folder}/sprites/" "#{app_path}/sprites/"]
+ sh %Q[cp -r "#{root_folder}/data/" "#{app_path}/data/"]
+ sh %Q[cp -r "#{root_folder}/fonts/" "#{app_path}/fonts/"]
+ end
+
+ def create_payload
+ sh %Q[mkdir -p #{tmp_directory}/ipa_root/Payload]
+ sh %Q[cp -r "#{app_path}" "#{tmp_directory}/ipa_root/Payload"]
+ sh %Q[chmod -R 755 "#{tmp_directory}/ipa_root/Payload"]
+ end
+
+ def create_payload_directory_dev
+ # write dev machine's ip address for hotloading
$gtk.write_file "app/server_ip_address.txt", $gtk.ffi_misc.get_local_ip_address.strip
+
+ embed_mobileprovision
+ clear_payload_directory
+ stage_app
end
- def create_payload_directory
- sh "cp #{@provisioning_profile_path} \"#{app_path}/embedded.mobileprovision\""
- sh "/usr/bin/plutil -convert binary1 \"#{app_path}/Info.plist\""
- write_ip_address
- sh "rm \"#{@app_name}\".ipa"
- sh "rm -rf \"#{app_path}/app\""
- sh "rm -rf \"#{app_path}/sounds\""
- sh "rm -rf \"#{app_path}/sprites\""
- sh "rm -rf \"#{app_path}/data\""
- sh "rm -rf \"#{app_path}/fonts\""
- sh "cp -r \"#{root_folder}/app/\" \"#{app_path}/app/\""
- sh "cp -r \"#{root_folder}/sounds/\" \"#{app_path}/sounds/\""
- sh "cp -r \"#{root_folder}/sprites/\" \"#{app_path}/sprites/\""
- sh "cp -r \"#{root_folder}/data/\" \"#{app_path}/data/\""
- sh "cp -r \"#{root_folder}/fonts/\" \"#{app_path}/fonts/\""
- sh "mkdir -p #{tmp_directory}/ipa_root/Payload"
- sh "cp -r \"#{app_path}\" \"#{tmp_directory}/ipa_root/Payload\""
- sh "chmod -R 755 \"#{tmp_directory}/ipa_root/Payload\""
+ def create_payload_directory_prod
+ # production builds does not hotload ip address
+ sh %Q[rm "#{root_folder}/app/server_ip_address.txt"]
+
+ embed_mobileprovision
+ stage_app
+
+ # production build marker
+ sh %Q[mkdir -p "#{app_path}/metadata/"]
+ sh %Q[touch metadata/DRAGONRUBY_PRODUCTION_BUILD]
end
def create_ipa
@@ -988,10 +1050,22 @@ SCRIPT
end
def print_publish_help
- log_info "Go to https://appstoreconnect.apple.com/apps and create an App if you haven't already done so."
- log_info "Go to https://appleid.apple.com and create a 'Application Specific Password'."
- log_info "To upload your app, Download Transporter from the App Store https://apps.apple.com/us/app/transporter/id1450874784?mt=12."
- log_info "Your app is located at ./tmp/ios/#{@app_name}.ipa"
+ has_transporter = (sh "ls /Applications/Transporter.app").include? "Contents"
+ if !has_transporter
+ $gtk.openurl "https://apps.apple.com/us/app/transporter/id1450874784?mt=12"
+ $console.set_command "$wizards.ios.start env: :#{@opts[:env]}, version: \"#{@opts[:version]}\""
+ raise WizardException.new(
+ "* To upload your app, Download Transporter from the App Store https://apps.apple.com/us/app/transporter/id1450874784?mt=12."
+ )
+ else
+ sh "mkdir ./tmp/ios/intermediary_artifacts"
+ sh "mv \"#{tmp_directory}/#{@app_name}.app\" #{tmp_directory}/intermediary_artifacts/"
+ sh "mv \"#{tmp_directory}/do_zip.sh\" #{tmp_directory}/intermediary_artifacts"
+ sh "mv \"#{tmp_directory}/Entitlements.plist\" #{tmp_directory}/intermediary_artifacts"
+ sh "mv \"#{tmp_directory}/ipa_root\" #{tmp_directory}/intermediary_artifacts/"
+ sh "open /Applications/Transporter.app"
+ sh "open ./tmp/ios/"
+ end
end
def compile_icons
@@ -1012,4 +1086,24 @@ S
sh "cp -r \"#{root_folder}/native/\" \"#{app_path}/native/\""
sh "CODESIGN_ALLOCATE=\"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate\" /usr/bin/codesign -f -s \"#{@certificate_name}\" --entitlements #{tmp_directory}/Entitlements.plist \"#{tmp_directory}/#{@app_name}.app/native/ios-device/ext.dylib\""
end
+
+ def set_version version
+ @app_version = version
+ start env: @opts[:env], version: version
+ end
+
+ def app_version
+ log_info "Attempting to retrieve App Version from metadata/ios_metadata.txt."
+ ios_version_number = (ios_metadata.version || "").strip
+ if ios_version_number.length == 0
+ log_info "Not found. Attempting to retrieve App Version from metadata/game_metadata.txt."
+ ios_version_number = (game_metadata.version || "").strip
+ end
+ ios_version_number
+ end
+
+ def determine_app_version
+ @app_version = app_version
+ return if @app_version
+ end
end
diff --git a/dragon/itch_wizard.rb b/dragon/itch_wizard.rb
index c0a69de..e976dc9 100644
--- a/dragon/itch_wizard.rb
+++ b/dragon/itch_wizard.rb
@@ -1,24 +1,34 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# itch_wizard.rb has been released under MIT (*only this file*).
-class ItchWizard
- include Metadata
-
+class ItchWizard < Wizard
def steps
[
:check_metadata,
- :deploy
+ :deploy,
]
end
+ def write_blank_metadata
+ $gtk.write_file metadata_file_path, <<-S.strip
+#devid=myname
+#devtitle=My Name
+#gameid=mygame
+#gametitle=My Game
+#version=0.1
+#icon=metadata/icon.png
+S
+ end
+
def check_metadata
metadata_text = $gtk.read_file metadata_file_path
if !metadata_text
write_blank_metadata
end
- if metadata_text.each_line.to_a.length != 6
+ if metadata_text.strip.each_line.to_a.length < 6
write_blank_metadata
end
@@ -30,70 +40,67 @@ class ItchWizard
if metadata[:dev_id].start_with?("#") || !@dev_id
log "* PROMPT: Please provide your username for Itch."
- $console.set_command "$wizards.itch.set_dev_id \"your-itch-username\""
+ $console.set_command "$wizards.itch.set_dev_id \"#{metadata[:dev_id]}\""
return :need_dev_id
end
if metadata[:dev_title].start_with?("#") || !@dev_title
log "* PROMPT: Please provide developer's/company's name that you want displayed."
- $console.set_command "$wizards.itch.set_dev_title \"Your Name\""
+ $console.set_command "$wizards.itch.set_dev_title \"#{metadata[:dev_title]}\""
return :need_dev_title
end
if metadata[:game_id].start_with?("#") || !@game_id
log "* PROMPT: Please provide the id for you game. This is the id you specified when you set up a new game page on Itch."
- $console.set_command "$wizards.itch.set_game_id \"your-game-id\""
+ $console.set_command "$wizards.itch.set_game_id \"#{metadata[:game_id]}\""
return :need_game_id
end
if metadata[:game_title].start_with?("#") || !@game_title
log "* PROMPT: Please provide the display name for your game. (This can include spaces)"
- $console.set_command "$wizards.itch.set_game_title \"Your Game\""
+ $console.set_command "$wizards.itch.set_game_title \"#{metadata[:game_title]}\""
return :need_game_title
end
if metadata[:version].start_with?("#") || !@version
log "* PROMPT: Please provide the version for your game."
- $console.set_command "$wizards.itch.set_version \"1.0\""
+ $console.set_command "$wizards.itch.set_version \"#{metadata[:version]}\""
return :need_version
end
if metadata[:icon].start_with?("#") || !@icon
log "* PROMPT: Please provide icon path for your game."
- $console.set_command "$wizards.itch.set_icon \"icon.png\""
+ $console.set_command "$wizards.itch.set_icon \"#{metadata[:icon]}\""
return :need_icon
end
+ puts "here!! success!!!"
+
return :success
end
def set_dev_id value
@dev_id = value
- write_metadata
start
end
def set_dev_title value
@dev_title = value
- write_metadata
start
end
def set_game_id value
@game_id = value
- write_metadata
start
end
def set_game_title value
@game_title = value
- write_metadata
start
end
def set_version value
@version = value
- write_metadata
start
end
@@ -136,7 +143,7 @@ class ItchWizard
end
if @icon
- text += "icon=metadata/#{@icon}\n"
+ text += "icon=#{@icon}\n"
else
text += "#icon=metadata/icon.png\n"
end
@@ -154,10 +161,25 @@ class ItchWizard
def deploy
log_info "* Running dragonruby-publish: #{package_command}"
- results = $gtk.exec package_command
+ $gtk.openurl "http://itch.io/dashboard" if $gtk.platform == "Mac OS X"
+ if $gtk.platform? :mac
+ $gtk.exec "rm -rf ./builds"
+ end
+ results = $gtk.exec "#{package_command} --only-package"
+ puts File.expand_path("./builds")
+
log "#+begin_src"
log results
log "#+end_src"
+
+ if $gtk.platform? :mac
+ $gtk.exec "open ./builds/"
+ elsif $gtk.platform? :windows
+ $gtk.exec "powershell \"ii .\""
+ end
+
+ $gtk.openurl "https://itch.io/dashboard"
+
:success
end
@@ -168,7 +190,7 @@ class ItchWizard
steps.each do |m|
begin
log_info "Running Itch Wizard Step: ~$wizards.itch.#{m}~"
- result = (send m) || :success if @wizard_status[m][:result] != :success
+ result = (send m) || :success
@wizard_status[m][:result] = result
if result != :success
log_info "Exiting wizard. :#{result}"
diff --git a/dragon/keys.rb b/dragon/keys.rb
index c931aff..c49d98a 100644
--- a/dragon/keys.rb
+++ b/dragon/keys.rb
@@ -14,7 +14,7 @@ module GTK
:l1, :r1,
:l2, :r2,
:l3, :r3,
- :start, :select,
+ :start, :select, :home,
:directional_up, :directional_down, :directional_left, :directional_right
].freeze
@@ -22,6 +22,22 @@ module GTK
attr label
end
+ def back
+ @select
+ end
+
+ def back= value
+ @select = value
+ end
+
+ def guide
+ @home
+ end
+
+ def guide= value
+ @home = value
+ end
+
# Activate a key.
#
# @return [void]
diff --git a/dragon/layout.rb b/dragon/layout.rb
index 1b0f8d0..18ca8c7 100644
--- a/dragon/layout.rb
+++ b/dragon/layout.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# layout.rb has been released under MIT (*only this file*).
@@ -276,10 +277,18 @@ module GTK
device.grid_area.row_count
end
+ def row_max_index
+ row_count - 1
+ end
+
def col_count
device.grid_area.col_count
end
+ def col_max_index
+ col_count - 1
+ end
+
def gutter_height
device.grid_area.gutter
end
@@ -302,18 +311,124 @@ module GTK
def rect_defaults
{
- row: nil,
- col: nil,
- h: 1,
- w: 1,
- dx: 0,
- dy: 0,
- rect: :control_rect
+ row: nil,
+ col: nil,
+ h: 1,
+ w: 1,
+ dx: 0,
+ dx_ratio: 1,
+ dy: 0,
+ dy_ratio: 1,
+ dh_ratio: 1,
+ dw_ratio: 1,
+ merge: nil,
+ rect: :control_rect
}
end
- def rect opts
+ def row n
+ (rect row: n, col: 0, w: 0, h: 0).x
+ end
+
+ def row_from_bottom n
+ (rect row: row_count - n, col: 0, w: 0, h: 0).x
+ end
+
+ def col n
+ (rect row: 0, col: n, w: 0, h: 0).y
+ end
+
+ def col_from_right n
+ (rect row: 0, col: col_max_index - n, w: 0, h: 0).y
+ end
+
+ def w n
+ (rect row: 0, col: 0, w: n, h: 1).w
+ end
+
+ def h n
+ (rect row: 0, col: 0, w: 1, h: n).h
+ end
+
+ def rect_group opts
+ group = opts.group
+ r = opts.row || 0
+ r = row_max_index - opts.row_from_bottom if opts.row_from_bottom
+ c = opts.col || 0
+ c = col_max_index - opts.col_from_right if opts.col_from_right
+ drow = opts.drow || 0
+ dcol = opts.dcol || 0
+ w = opts.w || 0
+ h = opts.h || 0
+ merge = opts[:merge]
+
+ running_row = r
+ running_col = c
+
+ running_col = calc_col_offset(opts.col_offset) if opts.col_offset
+ running_row = calc_row_offset(opts.row_offset) if opts.row_offset
+
+ group.map do |i|
+ group_layout_opts = i.layout || {}
+ group_layout_opts = group_layout_opts.merge row: running_row,
+ col: running_col,
+ merge: merge,
+ w: w, h: h
+ result = (rect group_layout_opts).merge i
+
+ if (i.is_a? Hash) && (i.primitive_marker == :label)
+ if i.alignment_enum == 1
+ result.x += result.w.half
+ elsif i.alignment_enum == 2
+ result.x += result.w
+ end
+ end
+
+ running_row += drow
+ running_col += dcol
+ result
+ end
+ end
+
+ def calc_row_offset opts = {}
+ count = opts[:count] || opts[:length] || 0
+ h = opts.h || 1
+ (row_count - (count * h)) / 2.0
+ end
+
+ def calc_col_offset opts = {}
+ count = opts[:count] || opts[:length] || 0
+ w = opts.w || 1
+ (col_count - (count * w)) / 2.0
+ end
+
+ def point opts = {}
+ opts.w = 1
+ opts.h = 1
+ opts.row ||= 0
+ opts.col ||= 0
+ r = rect opts
+ r.x += r.w * opts.col_anchor if opts.col_anchor
+ r.y += r.h * opts.row_anchor if opts.row_anchor
+ r
+ end
+
+ def rect *all_opts
+ if all_opts.length == 1
+ opts = all_opts.first
+ else
+ opts = {}
+ all_opts.each do |o|
+ opts.merge! o
+ end
+ end
+
+ opts.row = row_max_index - opts.row_from_bottom if opts.row_from_bottom
+ opts.col = col_max_index - opts.col_from_right if opts.col_from_right
opts = rect_defaults.merge opts
+ opts.row ||= 0
+ opts.col ||= 0
+
result = send opts[:rect]
if opts[:row] && opts[:col] && opts[:w] && opts[:h]
col = rect_col opts[:col], opts[:w]
@@ -321,7 +436,9 @@ module GTK
result = control_rect.merge x: col.x,
y: row.y,
w: col.w,
- h: row.h
+ h: row.h,
+ center_x: col.center_x,
+ center_y: row.center_y
elsif opts[:row] && !opts[:col]
result = rect_row opts[:row], opts[:h]
elsif !opts[:row] && opts[:col]
@@ -359,13 +476,21 @@ module GTK
result[:h] += device.grid_area.gutter * 2
end
- result[:x] += opts[:dx] if opts[:dx]
- result[:y] += opts[:dy] if opts[:dy]
- result[:w] += opts[:dw] if opts[:dw]
- result[:h] += opts[:dh] if opts[:dh]
+ result[:x] += opts[:dx] if opts[:dx]
+ result[:x] *= opts[:dx_ratio] if opts[:dx_ratio]
+ result[:y] += opts[:dy] if opts[:dy]
+ result[:y] *= opts[:dy_ratio] if opts[:dy_ratio]
+ result[:w] += opts[:dw] if opts[:dw]
+ result[:w] *= opts[:dw_ratio] if opts[:dw_ratio]
+ result[:h] += opts[:dh] if opts[:dh]
+ result[:h] *= opts[:dh_ratio] if opts[:dh_ratio]
+ result.merge! opts[:merge] if opts[:merge]
result[:row] = opts[:row]
result[:col] = opts[:col]
+ result[:h] = result[:h].clamp 0
+ result[:w] = result[:w].clamp 0
+
if $gtk.args.grid.name == :center
result[:x] -= 640
result[:y] -= 360
@@ -400,7 +525,7 @@ module GTK
row_y = device.h - row_y - row_h
- result = control_rect.merge y: row_y, h: row_h
+ result = control_rect.merge y: row_y, h: row_h, center_y: (row_y + row_h.half)
@rect_cache[:row][index][h] = result
@rect_cache[:row][index][h]
end
@@ -423,7 +548,7 @@ module GTK
col_w = col_w.to_i
col_w -= 1 if col_w.odd?
- result = control_rect.merge x: col_x, w: col_w
+ result = control_rect.merge x: col_x, w: col_w, center_x: (col_x + col_w.half)
@rect_cache[:col][index][w] = result
@rect_cache[:col][index][w]
end
@@ -474,6 +599,26 @@ module GTK
@device
end
+ def debug_primitives opts = {}
+ @primitives ||= col_count.map_with_index do |col|
+ row_count.map_with_index do |row|
+ cell = rect row: row, col: col
+ center = Geometry.rect_center_point cell
+ [
+ cell.merge(opts).border,
+ cell.merge(opts)
+ .label!(x: center.x,
+ y: center.y,
+ text: "#{row},#{col}",
+ size_enum: -3,
+ vertical_alignment_enum: 1,
+ alignment_enum: 1)
+ ]
+ end
+ end
+ @primitives
+ end
+
def serialize
{
device: @device.serialize,
@@ -487,5 +632,12 @@ module GTK
def to_s
serialize.to_s
end
+
+ def reset
+ @primitives = nil
+ @rect_cache ||= {}
+ @rect_cache.clear
+ end
+
end
end
diff --git a/dragon/log.rb b/dragon/log.rb
index 7bf09fd..2e39217 100644
--- a/dragon/log.rb
+++ b/dragon/log.rb
@@ -129,6 +129,11 @@ module GTK
self.puts message
end
+ def self.reset
+ @once = {}
+ nil
+ end
+
def self.puts_once *ids, message
id = "#{ids}"
@once ||= {}
diff --git a/dragon/metadata.rb b/dragon/metadata.rb
deleted file mode 100644
index 88d9b60..0000000
--- a/dragon/metadata.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# Contributors outside of DragonRuby who also hold Copyright: Michał Dudziński
-# Copyright 2021 DragonRuby LLC
-# MIT License
-# metadata.rb has been released under MIT (*only this file*).
-
-module Metadata
- def metadata_file_path
- "metadata/game_metadata.txt"
- end
-
- def get_metadata
- metadata = $gtk.read_file metadata_file_path
-
- if !metadata
- write_blank_metadata
- metadata = $gtk.read_file metadata_file_path
- end
-
- dev_id, dev_title, game_id, game_title, version, icon = *metadata.each_line.to_a
-
- {
- dev_id: dev_id.strip,
- dev_title: dev_title.strip,
- game_id: game_id.strip,
- game_title: game_title.strip,
- version: version.strip,
- icon: icon.strip
- }
- end
-
- def write_blank_metadata
- $gtk.write_file metadata_file_path, <<-S.strip
-#devid=myname
-#devtitle=My Name
-#gameid=mygame
-#gametitle=My Game
-#version=0.1
-#icon=metadata/icon.png
-S
- end
-end
diff --git a/dragon/numeric.rb b/dragon/numeric.rb
index 3cf1314..27cf02e 100644
--- a/dragon/numeric.rb
+++ b/dragon/numeric.rb
@@ -13,6 +13,35 @@ class Numeric
alias_method :lte, :<=
alias_method :__original_eq_eq__, :== unless Numeric.instance_methods.include? :__original_eq_eq__
+ def to_layout_row opts = {}
+ $layout.rect(row: self,
+ col: opts.col || 0,
+ w: opts.w || 0,
+ h: opts.h || 0).y
+ end
+
+ def to_layout_col opts = {}
+ $layout.rect(row: 0,
+ col: self,
+ w: opts.w || 0,
+ h: opts.h || 0).x
+ end
+
+ def to_layout_w
+ $layout.rect(row: 0, col: 0, w: self, h: 1).w
+ end
+
+ def to_layout_h
+ $layout.rect(row: 0, col: 0, w: 1, h: self).h
+ end
+
+ def to_layout_row_from_bottom opts = {}
+ ($layout.row_max_index - self).to_layout_row opts
+ end
+
+ def to_layout_col_from_right opts = {}
+ ($layout.col_max_index - self).to_layout_col opts
+ end
# Converts a numeric value representing seconds into frames.
#
@@ -28,10 +57,26 @@ class Numeric
self / 2.0
end
+ def third
+ self / 3.0
+ end
+
+ def quarter
+ self / 4.0
+ end
+
def to_byte
clamp(0, 255).to_i
end
+ def clamp *opts
+ min = (opts.at 0)
+ max = (opts.at 1)
+ return min if min && self < min
+ return max if max && self > max
+ return self
+ end
+
def clamp_wrap min, max
max, min = min, max if min > max
return self if self >= min && self <= max
@@ -314,6 +359,18 @@ S
(self % n) == 0
end
+ def multiply n
+ self * n
+ end
+
+ def fmult n
+ self * n.to_f
+ end
+
+ def imult n
+ (self * n).to_i
+ end
+
def mult n
self * n
end
@@ -435,12 +492,6 @@ S
return gt other
end
- def == other
- return true if __original_eq_eq__ other
- return __original_eq_eq__ other.entity_id if other.is_a? OpenEntity
- return false
- end
-
# @gtk
def map
unless block_given?
@@ -527,29 +578,29 @@ S
end
def - other
- return nil unless other
- super
+ return self unless other
+ self - other
rescue Exception => e
__raise_arithmetic_exception__ other, :-, e
end
def + other
- return nil unless other
- super
+ return self unless other
+ self + other
rescue Exception => e
__raise_arithmetic_exception__ other, :+, e
end
def * other
- return nil unless other
- super
+ return self unless other
+ self * other
rescue Exception => e
__raise_arithmetic_exception__ other, :*, e
end
def / other
- return nil unless other
- super
+ return self unless other
+ self / other
rescue Exception => e
__raise_arithmetic_exception__ other, :/, e
end
@@ -579,6 +630,10 @@ S
def self.clamp n, min, max
n.clamp min, max
end
+
+ def mid? l, r
+ (between? l, r) || (between? r, l)
+ end
end
class Fixnum
@@ -605,39 +660,33 @@ class Fixnum
end
def + other
- return nil unless other
- super
+ return self unless other
+ self + other
rescue Exception => e
__raise_arithmetic_exception__ other, :+, e
end
def * other
- return nil unless other
- super
+ return self unless other
+ self * other
rescue Exception => e
__raise_arithmetic_exception__ other, :*, e
end
def / other
- return nil unless other
- super
+ return self unless other
+ self / other
rescue Exception => e
__raise_arithmetic_exception__ other, :/, e
end
def - other
- return nil unless other
- super
+ return self unless other
+ self - other
rescue Exception => e
__raise_arithmetic_exception__ other, :-, e
end
- def == other
- return true if __original_eq_eq__ other
- return __original_eq_eq__ other.entity_id if other.is_a? GTK::OpenEntity
- return false
- end
-
# Returns `-1` if the number is less than `0`. `+1` if the number
# is greater than `0`. Returns `0` if the number is equal to `0`.
#
@@ -694,28 +743,28 @@ class Float
alias_method :__original_divide__, :- unless Float.instance_methods.include? :__original_divide__
def - other
- return nil unless other
+ return self unless other
super
rescue Exception => e
__raise_arithmetic_exception__ other, :-, e
end
def + other
- return nil unless other
+ return self unless other
super
rescue Exception => e
__raise_arithmetic_exception__ other, :+, e
end
def * other
- return nil unless other
+ return self unless other
super
rescue Exception => e
__raise_arithmetic_exception__ other, :*, e
end
def / other
- return nil unless other
+ return self unless other
super
rescue Exception => e
__raise_arithmetic_exception__ other, :/, e
@@ -762,4 +811,8 @@ class Integer
def nan?
false
end
+
+ def center other
+ (self - other).abs.fdiv(2)
+ end
end
diff --git a/dragon/outputs_docs.rb b/dragon/outputs_docs.rb
index b2dc57d..db15989 100644
--- a/dragon/outputs_docs.rb
+++ b/dragon/outputs_docs.rb
@@ -150,7 +150,7 @@ Here is an example:
S
end
-
+
def docs_sprites
<<-S
* DOCS: ~GTK::Outputs#sprites~
diff --git a/dragon/readme_docs.rb b/dragon/readme_docs.rb
index 78e0320..541c3ce 100644
--- a/dragon/readme_docs.rb
+++ b/dragon/readme_docs.rb
@@ -36,8 +36,6 @@ to get fancy you can provide a ~lambda~ to filter documentation:
#+begin_src
docs_search { |entry| (entry.include? "Array") && (!entry.include? "Enumerable") }
#+end_src
-
-[[docs_search.gif]]
S
end
@@ -67,20 +65,25 @@ Reply with:
I am a Dragon Rider.
#+end_quote
-* Watch Some Intro Videos
+* Intro Videos
+
+Here are some videos to help you get the lay of the land.
-Each video is only 20 minutes and all of them will fit into a lunch
-break. So please watch them:
+** Quick Api Tour
1. Beginner Introduction to DragonRuby Game Toolkit: [[https://youtu.be/ixw7TJhU08E]]
-2. Intermediate Introduction to Ruby Syntax: [[https://youtu.be/HG-XRZ5Ppgc]]
-3. Intermediate Introduction to Arrays in Ruby: [[https://youtu.be/N72sEYFRqfo]]
-The second and third videos are not required if you are proficient
-with Ruby, but *definitely* watch the first one.
+** If You Are Completely New to Ruby and Programming
+
+1. Intermediate Introduction to Ruby Syntax: [[https://youtu.be/HG-XRZ5Ppgc]]
+2. Intermediate Introduction to Arrays in Ruby: [[https://youtu.be/N72sEYFRqfo]]
+3. You may also want to try this free course provided at [[http://dragonruby.school]].
+
+** If You Have Game Dev Experience
-You may also want to try this free course provided at
-[[http://dragonruby.school]].
+1. Building Tetris - Part 1: [[https://youtu.be/xZMwRSbC4rY]]
+2. Building Tetris - Part 2: [[https://youtu.be/C3LLzDUDgz4]]
+3. Low Res Game Jam Tutorial: [[https://youtu.be/pCI90ukKCME]]
* Getting Started Tutorial
@@ -482,10 +485,10 @@ overwhelms beginners who are new to the engine or programming in general.
DragonRuby's philosophy is to provide multiple options across the "make it
fast" vs "make it right" spectrum, with incremental/intuitive
transitions between the options provided. A concrete example of this philosophy
-would be render primitives: the spectrum of options allows renderable constructs take
-the form of tuples/arrays (easy to pickup, simple, and fast to code/prototype with),
+would be render primitives: the spectrum of options allows renderable constructs that
+take the form of tuples/arrays (easy to pickup, simple, and fast to code/prototype with),
hashes (a little more work, but gives you the ability to add additional properties),
-open and string entities (more work than hashes, but yields cleaner apis),
+open and strict entities (more work than hashes, but yields cleaner apis),
and finally - if you really need full power/flexibility in rendering - classes
(which take the most amount of code and programming knowledge to create).
@@ -656,11 +659,15 @@ You can represent a sprite as a ~Hash~:
flip_vertically: false,
flip_horizontally: false,
angle_anchor_x: 0.5,
- angle_anchor_y: 1.0
+ angle_anchor_y: 1.0,
+ blendmode_enum: 1
}
end
#+end_src
+The ~blendmode_enum~ value can be set to ~0~ (no blending), ~1~ (alpha blending),
+~2~ (additive blending), ~3~ (modulo blending), ~4~ (multiply blending).
+
You can represent a sprite as an ~object~:
#+begin_src ruby
@@ -670,7 +677,7 @@ You can represent a sprite as an ~object~:
:source_x, :source_y, :source_w, :source_h,
:tile_x, :tile_y, :tile_w, :tile_h,
:flip_horizontally, :flip_vertically,
- :angle_anchor_x, :angle_anchor_y
+ :angle_anchor_x, :angle_anchor_y, :blendmode_enum
def primitive_marker
:sprite
@@ -760,16 +767,17 @@ You can add additional metadata about your game within a label, which requires y
#+begin_src
def tick args
args.outputs.labels << {
- x: 200,
- y: 550,
- text: "dragonruby",
- size_enum: 2,
- alignment_enum: 1,
- r: 155,
- g: 50,
- b: 50,
- a: 255,
- font: "fonts/manaspc.ttf",
+ x: 200,
+ y: 550,
+ text: "dragonruby",
+ size_enum: 2,
+ alignment_enum: 1,
+ r: 155,
+ g: 50,
+ b: 50,
+ a: 255,
+ font: "fonts/manaspc.ttf",
+ vertical_alignment_enum: 0, # 0 is bottom, 1 is middle, 2 is top
# You can add any properties you like (this will be ignored/won't cause errors)
game_data_one: "Something",
game_data_two: {
@@ -801,6 +809,21 @@ You can get the render size of any string using ~args.gtk.calcstringbox~.
]
end
#+end_src
+
+** Rendering Labels With New Line Characters And Wrapping
+
+You can use a strategy like the following to create multiple labels from a String.
+
+#+begin_src ruby
+ def tick args
+ long_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elitteger dolor velit, ultricies vitae libero vel, aliquam imperdiet enim."
+ max_character_length = 30
+ long_strings_split = args.string.wrapped_lines long_string, max_character_length
+ args.outputs.labels << long_strings_split.map_with_index do |s, i|
+ { x: 10, y: 600 - (i * 20), text: s }
+ end
+ end
+#+end_src
S
end
@@ -1031,7 +1054,7 @@ The elevator pitch is:
DragonRuby is a Multilevel Cross-platform Runtime. The "multiple levels"
within the runtime allows us to target platforms no other Ruby can
target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo
-Switch, PS4, Xbox, and Scadia.
+Switch, PS4, Xbox, and Stadia.
**** What does Multilevel Cross-platform mean?
@@ -1232,13 +1255,11 @@ questions asked.
*** But still, you should offer a free version. So I can try it out and see if I like it.
-You can try our [web-based sandbox environment](). But it won't do the
-runtime justice. Or just come to our [Slack]() or [Discord]() channel
-and ask questions. We'd be happy to have a one on one video chat with
-you and show off all the cool stuff we're doing.
+You can try our web-based sandbox environment at [[http://fiddle.dragonruby.org]]. But it won't do the
+runtime justice. Or just come to our Discord Channel at [[http://discord.dragonruby.org]] and ask questions.
+We'd be happy to have a one on one video chat with you and show off all the cool stuff we're doing.
-Seriously just buy it. Get a refund if you don't like it. We make it
-stupid easy to do so.
+Seriously just buy it. Get a refund if you don't like it. We make it stupid easy to do so.
*** I still think you should do a free version. Think of all people who would give it a shot.
diff --git a/dragon/runtime_docs.rb b/dragon/runtime_docs.rb
index 90addff..6da72d0 100644
--- a/dragon/runtime_docs.rb
+++ b/dragon/runtime_docs.rb
@@ -119,18 +119,18 @@ The properties ~args.inputs.mouse.(click|down|previous_click|up)~ each return ~n
that has an ~x~, ~y~ properties along with helper functions to determine collision: ~inside_rect?~, ~inside_circle~.
*** ~.controller_one~, ~.controller_two~
Represents controllers connected to the usb ports.
-**** ~.up
+**** ~.up~
Returns ~true~ if ~up~ is pressed or held on the directional or left analog.
-**** ~.down
+**** ~.down~
Returns ~true~ if ~down~ is pressed or held on the directional or left analog.
-**** ~.left
+**** ~.left~
Returns ~true~ if ~left~ is pressed or held on the directional or left analog.
-**** ~.right
+**** ~.right~
Returns ~true~ if ~right~ is pressed or held on the directional or left analog.
-**** ~.left_right
+**** ~.left_right~
Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.controller_(one|two).left~ and ~args.inputs.controller_(one|two).right~.
-**** ~.up_down
-Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.controller_(one|two).down~ and ~args.inputs.controller_(one|two).up~.
+**** ~.up_down~
+Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.controller_(one|two).up~ and ~args.inputs.controller_(one|two).down~.
**** ~.(left_analog_x_raw|right_analog_x_raw)~
Returns the raw integer value for the analog's horizontal movement (~-32,000 to +32,000~).
**** ~.left_analog_y_raw|right_analog_y_raw)~
@@ -139,13 +139,13 @@ Returns the raw integer value for the analog's vertical movement (~-32,000 to +3
Returns a number between ~-1~ and ~1~ which represents the percentage the analog is moved horizontally as a ratio of the maximum horizontal movement.
**** ~.left_analog_y_perc|right_analog_y_perc)~
Returns a number between ~-1~ and ~1~ which represents the percentage the analog is moved vertically as a ratio of the maximum vertical movement.
-**** ~.directional_up)~
+**** ~.directional_up~
Returns ~true~ if ~up~ is pressed or held on the directional.
-**** ~.directional_down)~
+**** ~.directional_down~
Returns ~true~ if ~down~ is pressed or held on the directional.
-**** ~.directional_left)~
+**** ~.directional_left~
Returns ~true~ if ~left~ is pressed or held on the directional.
-**** ~.directional_right)~
+**** ~.directional_right~
Returns ~true~ if ~right~ is pressed or held on the directional.
**** ~.(a|b|x|y|l1|r1|l2|r2|l3|r3|start|select)~
Returns ~true~ if the specific button is pressed or held.
@@ -170,7 +170,7 @@ Returns ~true~ if ~right~ or ~d~ is pressed or held on the keyboard.
**** ~.left_right~
Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.keyboard.left~ and ~args.inputs.keyboard.right~.
**** ~.up_down~
-Returns ~-1~ (down), ~0~ (neutral), or ~+1~ (up) depending on results of ~args.inputs.keyboard.down~ and ~args.inputs.keyboard.up~.
+Returns ~-1~ (left), ~0~ (neutral), or ~+1~ (right) depending on results of ~args.inputs.keyboard.up~ and ~args.inputs.keyboard.up~.
**** keyboard properties
The following properties represent keys on the keyboard and are available on ~args.inputs.keyboard.KEY~, ~args.inputs.keyboard.key_down.KEY~, ~args.inputs.keyboard.key_held.KEY~, and ~args.inputs.keyboard.key_up.KEY~:
- ~alt~
@@ -301,6 +301,14 @@ Send a Primitive to this collection to render an unfilled rectangle to the scree
Send any Primitive to this collection which represents things you render to the screen for debugging purposes. Primitives in this collection will not be rendered in a production release of your game.
** ~args.geometry~
This property contains geometric functions. Functions can be invoked via ~args.geometry.FUNCTION~.
+
+Here are some general notes with regards to the arguments these geometric functions accept.
+
+1. ~Rectangles~ can be represented as an ~Array~ with four (or more) values ~[x, y, w, h]~, as a ~Hash~ ~{ x:, y:, w:, h: }~ or an object that responds to ~x~, ~y~, ~w~, and ~h~.
+2. ~Points~ can be represent as an ~Array~ with two (or more) values ~[x, y]~, as a ~Hash~ ~{ x:, y:}~ or an object that responds to ~x~, and ~y~.
+3. ~Lines~ can be represented as an ~Array~ with four (or more) values ~[x, y, x2, y2]~, as a ~Hash~ ~{ x:, y:, x2:, y2: }~ or an object that responds to ~x~, ~y~, ~x2~, and ~y2~.
+4. ~Angles~ are represented as degrees (not radians).
+
*** ~.inside_rect? rect_1, rect_2~
Returns ~true~ if ~rect_1~ is inside ~rect_2~.
*** ~.intersect_rect? rect_2, rect_2~
diff --git a/dragon/string.rb b/dragon/string.rb
index 62c151a..153ba27 100644
--- a/dragon/string.rb
+++ b/dragon/string.rb
@@ -29,6 +29,30 @@ S
end
end
+ def char_byte
+ return nil if self.length == 0
+ c = self.each_char.first.bytes
+ c = c.first if c.is_a? Enumerable
+ c
+ end
+
+ def insert_character_at index, char
+ t = each_char.to_a
+ t = (t.insert index, char)
+ t.join
+ end
+
+ def excluding_character_at index
+ t = each_char.to_a
+ t.delete_at index
+ t.join
+ end
+
+ def excluding_last_character
+ return "" if self.length <= 1
+ return excluding_character_at(self.length - 1)
+ end
+
def end_with_bang?
self[-1] == "!"
end
diff --git a/dragon/tests.rb b/dragon/tests.rb
index 7cbba09..ad1a780 100644
--- a/dragon/tests.rb
+++ b/dragon/tests.rb
@@ -132,7 +132,7 @@ S
log "#{self.failed.length} test(s) failed."
self.failed.each do |h|
log "**** Test name: :#{h[:m]}"
- log "#{h[:e].to_s.gsub("* ERROR:", "").strip}"
+ log "#{h[:e].to_s.gsub("* ERROR:", "").strip}\n#{h[:e].__backtrace_to_org__}"
end
end
end
diff --git a/dragon/wizards.rb b/dragon/wizards.rb
index 3501b80..b0c7ca0 100644
--- a/dragon/wizards.rb
+++ b/dragon/wizards.rb
@@ -1,7 +1,42 @@
+# coding: utf-8
# Copyright 2019 DragonRuby LLC
# MIT License
# wizards.rb has been released under MIT (*only this file*).
+class Wizard
+ def metadata_file_path
+ "metadata/game_metadata.txt"
+ end
+
+ def get_metadata
+ metadata = $gtk.read_file metadata_file_path
+
+ if !metadata
+ write_blank_metadata
+ metadata = $gtk.read_file metadata_file_path
+ end
+
+ dev_id, dev_title, game_id, game_title, version, icon = *metadata.each_line.to_a
+
+ {
+ dev_id: dev_id.strip.gsub("#", "").gsub("devid=", ""),
+ dev_title: dev_title.strip.gsub("#", "").gsub("devtitle=", ""),
+ game_id: game_id.strip.gsub("#", "").gsub("gameid=", ""),
+ game_title: game_title.strip.gsub("#", "").gsub("gametitle=", ""),
+ version: version.strip.gsub("#", "").gsub("version=", ""),
+ icon: icon.strip.gsub("#", "").gsub("icon=", "")
+ }
+ end
+end
+
+class WizardException < Exception
+ attr_accessor :console_primitives
+
+ def initialize *console_primitives
+ @console_primitives = console_primitives
+ end
+end
+
module GTK
class Wizards
attr_accessor :ios, :itch