summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--docs/docs.css2
-rw-r--r--dragon/args.rb17
-rw-r--r--dragon/assert.rb92
-rw-r--r--dragon/console.rb101
-rw-r--r--dragon/console_menu.rb103
-rw-r--r--dragon/console_prompt.rb15
-rw-r--r--dragon/grid.rb39
-rw-r--r--dragon/inputs.rb6
-rw-r--r--dragon/kernel_docs.rb2
-rw-r--r--dragon/log.rb34
-rw-r--r--dragon/numeric.rb48
-rw-r--r--dragon/numeric_docs.rb240
-rw-r--r--dragon/outputs_docs.rb8
-rw-r--r--dragon/readme_docs.rb17
-rw-r--r--dragon/string.rb7
-rw-r--r--dragon/tests.rb13
-rw-r--r--dragon/trace.rb3
-rw-r--r--samples/00_beginner_ruby_primer/app/main.rb4
19 files changed, 656 insertions, 101 deletions
diff --git a/.gitignore b/.gitignore
index 642c1d1..dd17806 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
-*DS_Store* \ No newline at end of file
+*DS_Store*
+/docs/source.txt
+/docs/docs.txt
+/docs/docs.html
+/docs/search_results.txt
diff --git a/docs/docs.css b/docs/docs.css
index 7be4b7c..25cc677 100644
--- a/docs/docs.css
+++ b/docs/docs.css
@@ -84,7 +84,7 @@ hr:before {
pre {
border: solid 1px silver;
padding: 10px;
- font-size: 16px;
+ font-size: 14px;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
diff --git a/dragon/args.rb b/dragon/args.rb
index 2c4c604..38a28b7 100644
--- a/dragon/args.rb
+++ b/dragon/args.rb
@@ -41,7 +41,7 @@ module GTK
# @return [OpenEntity]
attr_accessor :state
- # Gives you access to the top level DragonRuby runtime.
+ # Gives you access to the top level DragonRuby runtime.
#
# @return [Runtime]
attr_accessor :runtime
@@ -49,6 +49,8 @@ module GTK
attr_accessor :passes
+ attr_accessor :wizards
+
def initialize runtime, recording
@inputs = Inputs.new
@outputs = Outputs.new args: self
@@ -57,12 +59,14 @@ module GTK
@state.tick_count = -1
@runtime = runtime
@recording = recording
- @grid = Grid.new runtime.ffi_draw
+ @grid = Grid.new runtime
@render_targets = {}
@all_tests = []
@geometry = GTK::Geometry
+ @wizards = Wizards.new
end
+
# The number of ticks since the start of the game.
#
# @return [Integer]
@@ -101,15 +105,6 @@ module GTK
end
def render_target name
- if @state.tick_count == 0
- log_important <<-S
-* WARNING:
-~render_target~ with name ~#{name}~ was created
-on ~args.state.tick_count == 0~. You cannot create ~render_targets~ on the
-first frame and need to wait until ~args.state.tick_count >= 1~.
-S
- end
-
name = name.to_s
if !@render_targets[name]
@render_targets[name] = Outputs.new(args: self, target: name, background_color_override: [255, 255, 255, 0])
diff --git a/dragon/assert.rb b/dragon/assert.rb
index 0eb8c2a..0c26f29 100644
--- a/dragon/assert.rb
+++ b/dragon/assert.rb
@@ -4,13 +4,57 @@
# assert.rb has been released under MIT (*only this file*).
module GTK
+=begin
+This is a tiny assertion api for the unit testing portion of Game Toolkit.
+
+@example
+
+1. Create a file called tests.rb under mygame.
+2. Any method that begins with the word test_ will be considered a test.
+
+def test_this_works args, assert
+ assert.equal! 1, 1
+end
+
+3. To run a test, save the file while the game is running.
+
+@example
+
+To add an assertion open up this class and write:
+
+class Assert
+ def custom_assertion actual, expected, message = nil
+ # this tell Game Toolkit that an assertion was performed (so that the test isn't marked inconclusive).
+ @assertion_performed = true
+
+ # perform your custom logic here and rais an exception to denote a failure.
+
+ raise "Some Error. #{message}."
+ end
+end
+=end
class Assert
attr :assertion_performed
+=begin
+Us this if you are throwing your own exceptions and you want to mark the tests as ran (so that it wont be marked as inconclusive).
+=end
def ok!
@assertion_performed = true
end
+=begin
+Assert if a value is a thruthy value. All assert method take an optional final parameter that is the message to display to the user.
+
+@example
+
+def test_does_this_work args, assert
+ some_result = Person.new
+ assert.true! some_result
+ # OR
+ assert.true! some_result, "Person was not created."
+end
+=end
def true! value, message = nil
@assertion_performed = true
if !value
@@ -20,6 +64,16 @@ module GTK
nil
end
+=begin
+Assert if a value is a falsey value.
+
+@example
+
+def test_does_this_work args, assert
+ some_result = nil
+ assert.false! some_result
+end
+=end
def false! value, message = nil
@assertion_performed = true
if value
@@ -29,16 +83,39 @@ module GTK
nil
end
+=begin
+Assert if two values are equal.
+
+@example
+
+def test_does_this_work args, assert
+ a = 1
+ b = 1
+ assert.equal! a, b
+end
+=end
def equal! actual, expected, message = nil
@assertion_performed = true
if actual != expected
actual_string = "#{actual}#{actual.nil? ? " (nil) " : " " }".strip
- message = "actual: #{actual_string} did not equal expected: #{expected}.\n#{message}"
+ message = "actual:\n#{actual_string} did not equal\nexpected:\n#{expected}.\n#{message}"
raise message
end
nil
end
+=begin
+Assert if a value is explicitly nil (not false).
+
+@example
+
+def test_does_this_work args, assert
+ a = nil
+ b = false
+ assert.nil! a # this will pass
+ assert.nil! b # this will throw an exception.
+end
+=end
def nil! value, message = nil
@assertion_performed = true
if !value.nil?
@@ -47,18 +124,5 @@ module GTK
end
nil
end
-
- def raises! exception_class, message = nil
- @assertion_performed = true
- begin
- yield
- rescue exception_class
- return # Success
- rescue Exception => e
- raise "Expected #{exception_class.name} to be raised but instead #{e.class.name} was raised\n#{message}"
- else
- raise "Expected #{exception_class.name} to be raised but nothing was raised\n#{message}"
- end
- end
end
end
diff --git a/dragon/console.rb b/dragon/console.rb
index acacfd2..c90755a 100644
--- a/dragon/console.rb
+++ b/dragon/console.rb
@@ -13,11 +13,12 @@ module GTK
:last_command_errored, :last_command, :error_color, :shown_at,
:header_color, :archived_log, :last_log_lines, :last_log_lines_count,
:suppress_left_arrow_behavior, :command_set_at,
- :toast_ids,
- :font_style
+ :toast_ids, :bottom,
+ :font_style, :menu
def initialize
@font_style = FontStyle.new(font: 'font.ttf', size_enum: -1, line_height: 1.1)
+ @menu = Menu.new self
@disabled = false
@log_offset = 0
@visible = false
@@ -26,6 +27,7 @@ module GTK
@log = [ 'Console ready.' ]
@max_log_lines = 1000 # I guess...?
@max_history = 1000 # I guess...?
+ @log_invocation_count = 0
@command_history = []
@command_history_index = -1
@nonhistory_input = ''
@@ -76,6 +78,7 @@ module GTK
end
def addsprite obj
+ @log_invocation_count += 1
obj[:id] ||= "id_#{obj[:path]}_#{Time.now.to_i}".to_sym
if @last_line_log_index &&
@@ -103,6 +106,7 @@ module GTK
def addtext obj
@last_log_lines_count ||= 1
+ @log_invocation_count += 1
str = obj.to_s
@@ -450,7 +454,7 @@ S
def write_primitive_and_return_offset(args, left, y, str, archived: false)
if str.is_a?(Hash)
padding = 10
- args.outputs.reserved << [left + 10, y - padding * 1.66, str[:w], str[:h], str[:path]].sprite
+ args.outputs.reserved << [left + 10, y + 5, str[:w], str[:h], str[:path]].sprite
return str[:h] + padding
else
write_line args, left, y, str, archived: archived
@@ -465,25 +469,25 @@ S
args.outputs.reserved << font_style.label(x: left.shift_right(10), y: y, text: str, color: color)
end
+ def should_tick?
+ return false if !@toggled_at
+ return false if slide_progress == 0
+ return false if @disabled
+ return visible?
+ end
+
def render args
return if !@toggled_at
+ return if slide_progress == 0
- if visible?
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip)
- else
- percent = @toggled_at.global_ease(@animation_duration, :flip, :quint)
- end
-
- return if percent == 0
-
- bottom = top - (h * percent)
- args.outputs.reserved << [left, bottom, w, h, *@background_color.mult_alpha(percent)].solid
- args.outputs.reserved << [right.shift_left(110), bottom.shift_up(630), 100, 100, @logo, 0, (80.0 * percent).to_i].sprite
+ @bottom = top - (h * slide_progress)
+ args.outputs.reserved << [left, @bottom, w, h, *@background_color.mult_alpha(slide_progress)].solid
+ args.outputs.reserved << [right.shift_left(110), @bottom.shift_up(630), 100, 100, @logo, 0, (80.0 * slide_progress).to_i].sprite
- y = bottom + 2 # just give us a little padding at the bottom.
+ y = @bottom + 2 # just give us a little padding at the bottom.
prompt.render args, x: left.shift_right(10), y: y
y += line_height_px * 1.5
- args.outputs.reserved << line(y: y, color: @text_color.mult_alpha(percent))
+ args.outputs.reserved << line(y: y, color: @text_color.mult_alpha(slide_progress))
y += line_height_px.to_f / 2.0
((@log.size - @log_offset) - 1).downto(0) do |idx|
@@ -492,8 +496,8 @@ S
break if y > top
end
- # past log seperator
- args.outputs.reserved << line(y: y + line_height_px.half, color: @text_color.mult_alpha(0.25 * percent))
+ # past log separator
+ args.outputs.reserved << line(y: y + line_height_px.half, color: @text_color.mult_alpha(0.25 * slide_progress))
y += line_height_px
@@ -504,10 +508,19 @@ S
end
render_log_offset args
- render_help args, top if percent == 1
end
- def render_help args, top
+ def tick_help args
+ tick_help_debounce args
+ alpha_rate = 20
+ @render_help_target_alpha ||= 255
+ @render_help_current_alpha ||= 255
+ @render_help_target_alpha += 4 if @render_help_current_alpha == @render_help_target_alpha
+ @render_help_current_alpha = (@render_help_current_alpha.towards @render_help_target_alpha, 20)
+
+ @render_help_target_alpha = @render_help_target_alpha.clamp(-255, 255)
+ @render_help_current_alpha = @render_help_current_alpha.clamp(-255, 255)
+
[
"* Prompt Commands: ",
"You can type any of the following ",
@@ -525,8 +538,33 @@ S
].each_with_index do |s, i|
args.outputs.reserved << [args.grid.right - 10,
top - 100 - line_height_px * i * 0.8,
- s, -3, 2, 180, 180, 180].label
+ s, -3, 2, 180, 180, 180, (@render_help_current_alpha.clamp 0, 255)].label
+ end
+ end
+
+ def tick_help_debounce args
+ hide_log_alpha = -255
+ if hidden?
+ @render_help_current_alpha = -255
+ end
+
+ if prompt.last_input_str_changed
+ @render_help_target_alpha = hide_log_alpha
end
+
+ if args.inputs.mouse.moved
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ if args.inputs.mouse.wheel
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ if @render_help_last_log_invocation_count != @log_invocation_count
+ @render_help_target_alpha = hide_log_alpha
+ end
+
+ @render_help_last_log_invocation_count = @log_invocation_count
end
def render_log_offset args
@@ -582,9 +620,18 @@ S
begin
return if @disabled
render args
- calc args
process_inputs args
+ return unless should_tick?
+ calc args
+ tick_help args
+ prompt.tick
+ menu.tick args
rescue Exception => e
+ begin
+ puts "#{e}"
+ puts "* FATAL: The GTK::Console console threw an unhandled exception and has been reset. You should report this exception (along with reproduction steps) to DragonRuby."
+ rescue
+ end
@disabled = true
$stdout.puts e
$stdout.puts "* FATAL: The GTK::Console console threw an unhandled exception and has been reset. You should report this exception (along with reproduction steps) to DragonRuby."
@@ -722,5 +769,15 @@ S
@prompt.clear
:console_silent_eval
end
+
+ def slide_progress
+ return 0 if !@toggled_at
+ if visible?
+ @slide_progress = @toggled_at.global_ease(@animation_duration, :flip, :quint, :flip)
+ else
+ @slide_progress = @toggled_at.global_ease(@animation_duration, :flip, :quint)
+ end
+ @slide_progress
+ end
end
end
diff --git a/dragon/console_menu.rb b/dragon/console_menu.rb
new file mode 100644
index 0000000..e8ce973
--- /dev/null
+++ b/dragon/console_menu.rb
@@ -0,0 +1,103 @@
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# console_menu.rb has been released under MIT (*only this file*).
+
+module GTK
+ class Console
+ class Menu
+ def initialize console
+ @console = console
+ end
+
+ def record_clicked
+ $recording.start 100
+ end
+
+ def replay_clicked
+ $replay.start 'replay.txt'
+ end
+
+ def reset_clicked
+ $gtk.reset
+ end
+
+ def scroll_up_clicked
+ @console.scroll_up_half
+ end
+
+ def scroll_down_clicked
+ @console.scroll_down_half
+ end
+
+ def close_clicked
+ @console.hide
+ end
+
+ def tick args
+ return unless @console.visible?
+
+ # defaults
+ @buttons = [
+ (button id: :record, row: 0, col: 15, text: "record", method: :record_clicked),
+ (button id: :replay, row: 0, col: 16, text: "replay", method: :replay_clicked),
+ (button id: :reset, row: 0, col: 17, text: "reset", method: :reset_clicked),
+ (button id: :scroll_up, row: 0, col: 18, text: "scroll up", method: :scroll_up_clicked),
+ (button id: :scroll_down, row: 0, col: 19, text: "scroll down", method: :scroll_down_clicked),
+ (button id: :close, row: 0, col: 20, text: "close", method: :close_clicked),
+ ]
+
+ # render
+ args.outputs.reserved << @buttons.map { |b| b[:primitives] }
+
+ # inputs
+ if args.inputs.mouse.click
+ clicked = @buttons.find { |b| args.inputs.mouse.inside_rect? b[:rect] }
+ if clicked
+ send clicked[:method]
+ end
+ end
+ end
+
+ def rect_for_layout row, col
+ col_width = 50
+ row_height = 50
+ col_margin = 5
+ row_margin = 5
+ x = (col_margin + (col * col_width) + (col * col_margin))
+ y = (row_margin + (row * row_height) + (row * row_margin) + row_height).from_top
+ { x: x, y: y, w: row_height, h: col_width }
+ end
+
+ def button args
+ id, row, col, text, method = args[:id], args[:row], args[:col], args[:text], args[:method]
+
+ font_height = @console.font_style.line_height_px.half
+ {
+ id: id,
+ rect: (rect_for_layout row, col),
+ method: method
+ }.let do |entity|
+ primitives = []
+ primitives << entity[:rect].merge(a: 80).solid
+ primitives << entity[:rect].merge(r: 255, g: 255, b: 255).border
+ primitives << text.wrapped_lines(5)
+ .map_with_index do |l, i|
+ [
+ entity[:rect][:x] + entity[:rect][:w].half,
+ entity[:rect][:y] + entity[:rect][:h].half + font_height - (i * (font_height + 2)),
+ l, -3, 1, 255, 255, 255
+ ]
+ end.labels
+
+ entity.merge(primitives: primitives)
+ end
+ end
+
+ def serialize
+ {
+ not_supported: "#{self}"
+ }
+ end
+ end
+ end
+end
diff --git a/dragon/console_prompt.rb b/dragon/console_prompt.rb
index dd454f7..aea5df8 100644
--- a/dragon/console_prompt.rb
+++ b/dragon/console_prompt.rb
@@ -8,7 +8,7 @@
module GTK
class Console
class Prompt
- attr_accessor :current_input_str, :font_style, :console_text_width
+ attr_accessor :current_input_str, :font_style, :console_text_width, :last_input_str, :last_input_str_changed
def initialize(font_style:, text_color:, console_text_width:)
@prompt = '-> '
@@ -24,6 +24,7 @@ module GTK
def <<(str)
@current_input_str << str
+ @current_input_changed_at = Kernel.global_tick_count
reset_autocomplete
end
@@ -114,6 +115,18 @@ S
args.outputs.reserved << font_style.label(x: x - 2, y: y + 3, text: (" " * (@prompt.length + current_input_str.length)) + "|", color: @cursor_color)
end
+ def tick
+ if (@current_input_changed_at) &&
+ (@current_input_changed_at < Kernel.global_tick_count) &&
+ (@last_input_str != @current_input_str)
+ @last_input_str_changed = true
+ @last_input_str = "#{@current_input_str}"
+ @current_input_changed_at = nil
+ else
+ @last_input_str_changed = false
+ end
+ end
+
private
def last_period_index
diff --git a/dragon/grid.rb b/dragon/grid.rb
index e5c21a3..c2a6a43 100644
--- a/dragon/grid.rb
+++ b/dragon/grid.rb
@@ -60,8 +60,9 @@ module GTK
attr_accessor :left_margin, :bottom_margin
- def initialize ffi_draw
- @ffi_draw = ffi_draw
+ def initialize runtime
+ @runtime = runtime
+ @ffi_draw = runtime.ffi_draw
origin_bottom_left!
end
@@ -109,18 +110,18 @@ module GTK
return if @name == :bottom_left
@name = :bottom_left
@origin_x = 0.0
- @origin_y = $gtk.logical_height
+ @origin_y = @runtime.logical_height
@left = 0.0
- @right = $gtk.logical_width
- @top = $gtk.logical_height
+ @right = @runtime.logical_width
+ @top = @runtime.logical_height
@bottom = 0.0
@left_margin = 0.0
@bottom_margin = 0.0
- @center_x = $gtk.logical_width.half
- @center_y = $gtk.logical_height.half
- @rect = [@left, @bottom, $gtk.logical_width, $gtk.logical_height].rect
+ @center_x = @runtime.logical_width.half
+ @center_y = @runtime.logical_height.half
+ @rect = [@left, @bottom, @runtime.logical_width, @runtime.logical_height].rect
@center = [@center_x, @center_y].point
- @ffi_draw.set_gtk_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
+ @ffi_draw.set_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
end
# Sets the rendering coordinate system to have its origin in the center.
@@ -130,24 +131,24 @@ module GTK
def origin_center!
return if @name == :center
@name = :center
- @origin_x = $gtk.logical_width.half
- @origin_y = $gtk.logical_height.half
- @left = -$gtk.logical_width.half
- @right = $gtk.logical_width.half
- @top = $gtk.logical_height.half
- @bottom = -$gtk.logical_height.half
+ @origin_x = @runtime.logical_width.half
+ @origin_y = @runtime.logical_height.half
+ @left = [email protected]_width.half
+ @right = @runtime.logical_width.half
+ @top = @runtime.logical_height.half
+ @bottom = [email protected]_height.half
@center_x = 0.0
@center_y = 0.0
- @rect = [@left, @bottom, $gtk.logical_width, $gtk.logical_height].rect
+ @rect = [@left, @bottom, @runtime.logical_width, @runtime.logical_height].rect
@center = [@center_x, @center_y].point
- @ffi_draw.set_gtk_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
+ @ffi_draw.set_grid @origin_x, @origin_y, SCREEN_Y_DIRECTION
end
# The logical width used for rendering.
#
# @return [Float]
def w
- $gtk.logical_width
+ @runtime.logical_width
end
# Half the logical width used for rendering.
@@ -161,7 +162,7 @@ module GTK
#
# @return [Float]
def h
- $gtk.logical_height
+ @runtime.logical_height
end
# Half the logical height used for rendering.
diff --git a/dragon/inputs.rb b/dragon/inputs.rb
index fc6d8ef..a4ef40f 100644
--- a/dragon/inputs.rb
+++ b/dragon/inputs.rb
@@ -295,7 +295,7 @@ S
return self.send m
rescue Exception => e
- log_important "#{e}}"
+ log_important "#{e}"
end
raise <<-S
@@ -498,6 +498,10 @@ module GTK
[@x, @y].point
end
+ def inside_rect? rect
+ point.inside_rect? rect
+ end
+
alias_method :position, :point
def clear
diff --git a/dragon/kernel_docs.rb b/dragon/kernel_docs.rb
index 1f4977b..00eadf6 100644
--- a/dragon/kernel_docs.rb
+++ b/dragon/kernel_docs.rb
@@ -53,6 +53,8 @@ S
final_string += k.docs_all
end
+ final_string += "\n" + (($gtk.read_file "docs/source.txt") || "")
+
html_parse_result = (__docs_to_html__ final_string)
$gtk.write_file 'docs/docs.txt', "#{final_string}"
diff --git a/dragon/log.rb b/dragon/log.rb
index 60246e6..b2ee0e1 100644
--- a/dragon/log.rb
+++ b/dragon/log.rb
@@ -81,6 +81,30 @@ module GTK
"
end
+ def self.puts_error *args
+ args ||= []
+ title = args[0]
+ additional = args[1..-1] || []
+ additional = "" if additional.length == 0
+ if !title.multiline? && join_lines(additional).multiline?
+ message = headline "ERROR: #{title}" do
+ dynamic_block do
+ additional
+ end
+ end
+ elsif title.multiline?
+ message = headline "ERROR: " do
+ dynamic_block do
+ args
+ end
+ end
+ else
+ message = "* ERROR: #{title} #{additional}".strip
+ end
+
+ self.puts message
+ end
+
def self.puts_info *args
args ||= []
title = args[0]
@@ -110,14 +134,14 @@ module GTK
@once ||= {}
return if @once[id]
@once[id] = id
+ if !$gtk.cli_arguments[:replay] && !$gtk.cli_arguments[:record]
+ $gtk.notify!("Open the DragonRuby Console by pressing [`] [~] [²] [^] [º] or [§]. [Message ID: #{id}].")
+ end
write_to_log_and_puts ""
write_to_log_and_puts "#{message.strip}"
write_to_log_and_puts ""
write_to_log_and_puts "[Message ID: #{id}]"
write_to_log_and_puts ""
- return if $gtk.cli_arguments[:replay]
- return if $gtk.cli_arguments[:record]
- $gtk.notify!("One time notification occurred. [Message ID: #{id}] (Open console for more info.)")
end
def self.puts_once_info *ids, message
@@ -222,6 +246,10 @@ class Object
log_with_color XTERM_COLOR[:bright_white], *args
end
+ def log_error *args
+ GTK::Log.puts_error(*args)
+ end
+
def log_info *args
GTK::Log.puts_info(*args)
end
diff --git a/dragon/numeric.rb b/dragon/numeric.rb
index c2c461f..6d8b84d 100644
--- a/dragon/numeric.rb
+++ b/dragon/numeric.rb
@@ -28,12 +28,6 @@ class Numeric
clamp(0, 255).to_i
end
- # For a given number, the elapsed frames since that number is returned.
- # `Kernel.tick_count` is used to determine how many frames have elapsed.
- # An override numeric value can be passed in which will be used instead
- # of `Kernel.tick_count`.
- #
- # @gtk
def elapsed_time tick_count_override = nil
(tick_count_override || Kernel.tick_count) - self
end
@@ -51,25 +45,42 @@ class Numeric
# moment in time.
#
# @gtk
- def elapsed? offset, tick_count_override = nil
- (self + offset) < (tick_count_override || Kernel.tick_count)
+ def elapsed? offset = 0, tick_count_override = Kernel.tick_count
+ (self + offset) < tick_count_override
end
- # This is helpful for determining the index of frame-by-frame sprite animation.
- # The numeric value `self` represents the moment the animation started. `frame_index`
- # takes three additional parameters: how many frames exist in the sprite animation;
- # how long to hold each animation for; and whether the animation should repeat.
- #
- # @gtk
- def frame_index frame_count, hold_for, repeat, tick_count_override = nil
+ def frame_index *opts
+ frame_count_or_hash, hold_for, repeat, tick_count_override = opts
+ if frame_count_or_hash.is_a? Hash
+ frame_count = frame_count_or_hash[:count]
+ hold_for = frame_count_or_hash[:hold_for]
+ repeat = frame_count_or_hash[:repeat]
+ tick_count_override = frame_count_or_hash[:tick_count_override]
+ else
+ frame_count = frame_count_or_hash
+ end
+
+ tick_count_override ||= Kernel.tick_count
animation_frame_count = frame_count
animation_frame_hold_time = hold_for
animation_length = animation_frame_hold_time * animation_frame_count
- if !repeat && self.+(animation_length) < (tick_count_override || Kernel.tick_count).-(1)
+ return nil if Kernel.tick_count < self
+
+ if !repeat && (self + animation_length) < (tick_count_override - 1)
return nil
else
return self.elapsed_time.-(1).idiv(animation_frame_hold_time) % animation_frame_count
end
+ rescue Exception => e
+ raise <<-S
+* ERROR:
+#{opts}
+#{e}
+S
+ end
+
+ def zero?
+ self == 0
end
def zero
@@ -479,6 +490,11 @@ S
def serialize
self
end
+
+ def from_top
+ return 720 - self unless $gtk
+ $gtk.args.grid.h - self
+ end
end
class Fixnum
diff --git a/dragon/numeric_docs.rb b/dragon/numeric_docs.rb
new file mode 100644
index 0000000..fb8a94d
--- /dev/null
+++ b/dragon/numeric_docs.rb
@@ -0,0 +1,240 @@
+# coding: utf-8
+# Copyright 2019 DragonRuby LLC
+# MIT License
+# numeric_docs.rb has been released under MIT (*only this file*).
+
+module NumericDocs
+ def docs_method_sort_order
+ [
+ :docs_frame_index,
+ :docs_elapsed_time,
+ :docs_elapsed?,
+ :docs_new?
+ ]
+ end
+
+ def docs_frame_index
+ <<-S
+* DOCS: ~Numeric#frame_index~
+
+This function is helpful for determining the index of frame-by-frame
+ sprite animation. The numeric value ~self~ represents the moment the
+ animation started.
+
+~frame_index~ takes three additional parameters:
+
+- How many frames exist in the sprite animation.
+- How long to hold each animation for.
+- Whether the animation should repeat.
+
+~frame_index~ will return ~nil~ if the time for the animation is out
+of bounds of the parameter specification.
+
+Example using variables:
+
+#+begin_src ruby
+ def tick args
+ start_looping_at = 0
+ number_of_sprites = 6
+ number_of_frames_to_show_each_sprite = 4
+ does_sprite_loop = true
+
+ sprite_index =
+ start_looping_at.frame_index number_of_sprites,
+ number_of_frames_to_show_each_sprite,
+ does_sprite_loop
+
+ sprite_index ||= 0
+
+ args.outputs.sprites << [
+ 640 - 50,
+ 360 - 50,
+ 100,
+ 100,
+ "sprites/dragon-\#{sprite_index}.png"
+ ]
+ end
+#+end_src
+
+Example using named parameters:
+
+#+begin_src ruby
+ def tick args
+ start_looping_at = 0
+
+ sprite_index =
+ start_looping_at.frame_index count: 6,
+ hold_for: 4,
+ repeat: true,
+ tick_count_override: args.state.tick_count
+
+ sprite_index ||= 0
+
+ args.outputs.sprites << [
+ 640 - 50,
+ 360 - 50,
+ 100,
+ 100,
+ "sprites/dragon-\#{sprite_index}.png"
+ ]
+ end
+#+end_src
+
+S
+ end
+
+ def docs_new?
+ <<-S
+* DOCS: ~Numeric#created?~
+Returns true if ~Numeric#elapsed_time == 0~. Essentially communicating that
+number is equal to the current frame.
+
+Example usage:
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 60 }
+ end
+
+ boxes_to_spawn_this_frame = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].new? }
+
+ boxes_to_spawn_this_frame.each { |b| puts "box \#{b} was new? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_spawn_this_frame
+ end
+#+end_src
+S
+ end
+
+ def docs_elapsed?
+ <<-S
+* DOCS: ~Numeric#elapsed?~
+Returns true if ~Numeric#elapsed_time~ is greater than the number. An optional parameter can be
+passed into ~elapsed?~ which is added to the number before evaluating whether ~elapsed?~ is true.
+
+Example usage (no optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :green,
+ destroy_at: args.state.tick_count + 60 }
+ args.state.box_queue << { name: :blue,
+ destroy_at: args.state.tick_count + 120 }
+ end
+
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:destroy_at].elapsed? }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: \#{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box \#{b} was elapsed? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+Example usage (with optional parameter):
+
+#+begin_src ruby
+ def tick args
+ args.state.box_queue ||= []
+
+ if args.state.box_queue.empty?
+ args.state.box_queue << { name: :red,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :green,
+ create_at: args.state.tick_count + 120,
+ lifespan: 60 }
+ args.state.box_queue << { name: :blue,
+ create_at: args.state.tick_count + 120,
+ lifespan: 120 }
+ end
+
+ # lifespan is passed in as a parameter to ~elapsed?~
+ boxes_to_destroy = args.state
+ .box_queue
+ .find_all { |b| b[:create_at].elapsed? b[:lifespan] }
+
+ if !boxes_to_destroy.empty?
+ puts "boxes to destroy count: \#{boxes_to_destroy.length}"
+ end
+
+ boxes_to_destroy.each { |b| puts "box \#{b} was elapsed? on \#{args.state.tick_count}." }
+
+ args.state.box_queue -= boxes_to_destroy
+ end
+#+end_src
+
+S
+ end
+
+ def docs_elapsed_time
+ <<-S
+* DOCS: ~Numeric#elapsed_time~
+For a given number, the elapsed frames since that number is returned.
+`Kernel.tick_count` is used to determine how many frames have elapsed.
+An optional numeric argument can be passed in which will be used instead
+of `Kernel.tick_count`.
+
+Here is an example of how elapsed_time can be used.
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.tick_count
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if args.state.last_click_at.elapsed_time > 120
+ args.outputs.labels << [10, 710, "It has been over 2 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+And here is an example where the override parameter is passed in:
+
+#+begin_src ruby
+ def tick args
+ args.state.last_click_at ||= 0
+
+ # create a state variable that tracks time at half the speed of args.state.tick_count
+ args.state.simulation_tick = args.state.tick_count.idiv 2
+
+ # record when a mouse click occurs
+ if args.inputs.mouse.click
+ args.state.last_click_at = args.state.simulation_tick
+ end
+
+ # Use Numeric#elapsed_time to determine how long it's been
+ if (args.state.last_click_at.elapsed_time args.state.simulation_tick) > 120
+ args.outputs.labels << [10, 710, "It has been over 4 seconds since the mouse was clicked."]
+ end
+ end
+#+end_src
+
+S
+ end
+end
+
+class Numeric
+ extend Docs
+ extend NumericDocs
+end
diff --git a/dragon/outputs_docs.rb b/dragon/outputs_docs.rb
index 1e8eecc..16c97ec 100644
--- a/dragon/outputs_docs.rb
+++ b/dragon/outputs_docs.rb
@@ -4,6 +4,14 @@
# outputs_docs.rb has been released under MIT (*only this file*).
module OutputsDocs
+ def docs_method_sort_order
+ [
+ :docs_class,
+ :docs_solids,
+ :docs_borders
+ ]
+ end
+
def docs_class
<<-S
* DOCS: ~GTK::Outputs~
diff --git a/dragon/readme_docs.rb b/dragon/readme_docs.rb
index 870a5a1..39fc283 100644
--- a/dragon/readme_docs.rb
+++ b/dragon/readme_docs.rb
@@ -509,7 +509,22 @@ It's a hard pill to swallow, but forget blindly accepted best
practices and try to figure out the underlying motivation for a
specific approach to game development. Collaborate with us.
-*** Release Often And Quickly
+*** Continuity of Design
+
+There is a programming idiom in software called "the pit of
+success". The term normalizes up front pain as a necessity in the
+(hopes that the investment will yield dividends "when you become
+successful"). This results in more "Enterprise TM" code upfront, and
+makes it more difficult to get started when you are new to programming.
+
+DragonRuby's philosophy is to provide a spectrum across the "make it
+fast" vs "make it right" spectrum and provide incremental, intuitive
+transitions between points on that spectrum. This is captured in how
+render primitives can be represented as tuples/arrays, hashes, open
+structs/entities, and then finally classes (as opposed to forcing devs
+to use classes upfront).
+
+*** Release Often And Soon
The biggest mistake game devs make is spending too much time in
isolation building their game. Release something, however small, and
diff --git a/dragon/string.rb b/dragon/string.rb
index 8d2d9ba..5fa273c 100644
--- a/dragon/string.rb
+++ b/dragon/string.rb
@@ -38,6 +38,7 @@ S
self[0..-2]
end
+ # @gtk
def wrapped_lines length
self.each_line.map do |l|
l = l.rstrip
@@ -50,20 +51,22 @@ S
end.flatten
end
+ # @gtk
def wrap length
wrapped_lines(length).join.rstrip
end
+ # @gtk
def multiline?
include? "\n"
end
- def indent_lines amount
+ def indent_lines amount, char = " "
self.each_line.each_with_index.map do |l, i|
if i == 0
l
else
- " " * amount + l
+ char * amount + l
end
end.join
end
diff --git a/dragon/tests.rb b/dragon/tests.rb
index 5cda2fe..7cbba09 100644
--- a/dragon/tests.rb
+++ b/dragon/tests.rb
@@ -16,7 +16,6 @@ module GTK
def run_test m
args = Args.new $gtk, nil
assert = Assert.new
- setup(args) if respond_to?(:setup)
begin
log_test_running m
send(m, args, assert)
@@ -32,7 +31,6 @@ module GTK
mark_test_failed m, e
end
end
- teardown if respond_to?(:teardown)
end
def test_methods_focused
@@ -43,6 +41,7 @@ module GTK
Object.methods.find_all { |m| m.start_with? "test_" }
end
+ # @gtk
def start
log "* TEST: gtk.test.start has been invoked."
if test_methods_focused.length != 0
@@ -73,10 +72,6 @@ module GTK
def log_inconclusive m
self.inconclusive << {m: m}
log "Inconclusive."
- log_once :assertion_ok_note, <<-S
-NOTE FOR INCONCLUSIVE TESTS: No assertion was performed in the test.
-Add assert.ok! at the end of the test if you are using your own assertions.
-S
end
def log_passed m
@@ -125,6 +120,12 @@ S
log "#{self.passed.length} test(s) passed."
self.passed.each { |h| log "**** :#{h[:m]}" }
log "*** Inconclusive"
+ if self.inconclusive.length > 0
+ log_once :assertion_ok_note, <<-S
+NOTE FOR INCONCLUSIVE TESTS: No assertion was performed in the test.
+Add assert.ok! at the end of the test if you are using your own assertions.
+S
+ end
log "#{self.inconclusive.length} test(s) inconclusive."
self.inconclusive.each { |h| log "**** :#{h[:m]}" }
log "*** Failed"
diff --git a/dragon/trace.rb b/dragon/trace.rb
index c3b4bbd..04067bf 100644
--- a/dragon/trace.rb
+++ b/dragon/trace.rb
@@ -65,7 +65,7 @@ module GTK
@traced_classes.clear
$trace_enabled = false
if !$gtk.production
- $gtk.write_file 'logs/trace.txt', "Add trace!(SOMEOBJECT) to the top of `tick` and this file will be populated with invocation information.\n"
+ $gtk.write_file 'logs/trace.txt', "Add trace!(SOMEOBJECT) to the top of ~tick~ and this file will be populated with invocation information.\n"
end
end
@@ -95,6 +95,7 @@ module GTK
$trace_puts.clear
end
+ # @gtk
def self.trace! instance = nil
$trace_history ||= []
$trace_enabled = true
diff --git a/samples/00_beginner_ruby_primer/app/main.rb b/samples/00_beginner_ruby_primer/app/main.rb
index 33c6dbf..6822cf3 100644
--- a/samples/00_beginner_ruby_primer/app/main.rb
+++ b/samples/00_beginner_ruby_primer/app/main.rb
@@ -76,8 +76,8 @@ end
def tick_reset_button
return unless state.hello_dragonruby_confirmed
- $gtk.reserved_primitives << state.reset_button.background
- $gtk.reserved_primitives << state.reset_button.label
+ $gtk.args.outputs.reserved << state.reset_button.background
+ $gtk.args.outputs.reserved << state.reset_button.label
if inputs.mouse.click && inputs.mouse.click.point.inside_rect?(state.reset_button.background)
restart_tutorial
end