adventure_rl 0.0.1.pre.ld42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +47 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +31 -0
  8. data/Rakefile +11 -0
  9. data/adventure_rl.gemspec +48 -0
  10. data/bin/console +7 -0
  11. data/bin/mkaudio +196 -0
  12. data/bin/mkclip +223 -0
  13. data/bin/rdoc +9 -0
  14. data/bin/setup +8 -0
  15. data/bin/vimall +5 -0
  16. data/doc/Mask.md +183 -0
  17. data/doc/Point.md +95 -0
  18. data/doc/Window.md +139 -0
  19. data/lib/AdventureRL/Animation.rb +63 -0
  20. data/lib/AdventureRL/Audio.rb +75 -0
  21. data/lib/AdventureRL/AudioPlayer.rb +65 -0
  22. data/lib/AdventureRL/Button.rb +51 -0
  23. data/lib/AdventureRL/Clip.rb +91 -0
  24. data/lib/AdventureRL/ClipPlayer.rb +187 -0
  25. data/lib/AdventureRL/Deltatime.rb +51 -0
  26. data/lib/AdventureRL/EventHandlers/Buttons.rb +225 -0
  27. data/lib/AdventureRL/EventHandlers/EventHandler.rb +62 -0
  28. data/lib/AdventureRL/EventHandlers/MouseButtons.rb +142 -0
  29. data/lib/AdventureRL/Events/Event.rb +69 -0
  30. data/lib/AdventureRL/Events/Mouse.rb +60 -0
  31. data/lib/AdventureRL/FileGroup.rb +100 -0
  32. data/lib/AdventureRL/FileGroupPlayer.rb +226 -0
  33. data/lib/AdventureRL/Helpers/Error.rb +68 -0
  34. data/lib/AdventureRL/Helpers/MethodHelper.rb +20 -0
  35. data/lib/AdventureRL/Helpers/PipeMethods.rb +26 -0
  36. data/lib/AdventureRL/Image.rb +77 -0
  37. data/lib/AdventureRL/Layer.rb +273 -0
  38. data/lib/AdventureRL/Mask.rb +462 -0
  39. data/lib/AdventureRL/Menu.rb +92 -0
  40. data/lib/AdventureRL/Modifiers/Gravity.rb +60 -0
  41. data/lib/AdventureRL/Modifiers/Inventory.rb +104 -0
  42. data/lib/AdventureRL/Modifiers/Pusher.rb +61 -0
  43. data/lib/AdventureRL/Modifiers/Solid.rb +302 -0
  44. data/lib/AdventureRL/Modifiers/Velocity.rb +163 -0
  45. data/lib/AdventureRL/Point.rb +188 -0
  46. data/lib/AdventureRL/Quadtree.rb +237 -0
  47. data/lib/AdventureRL/Rectangle.rb +62 -0
  48. data/lib/AdventureRL/Settings.rb +80 -0
  49. data/lib/AdventureRL/SolidsManager.rb +170 -0
  50. data/lib/AdventureRL/Textbox.rb +195 -0
  51. data/lib/AdventureRL/TimingHandler.rb +225 -0
  52. data/lib/AdventureRL/Window.rb +152 -0
  53. data/lib/AdventureRL/misc/extensions.rb +80 -0
  54. data/lib/AdventureRL/misc/require_files.rb +45 -0
  55. data/lib/AdventureRL/version.rb +3 -0
  56. data/lib/adventure_rl.rb +22 -0
  57. data/lib/default_settings.yml +20 -0
  58. data/vimrc +4 -0
  59. metadata +237 -0
@@ -0,0 +1,188 @@
1
+ module AdventureRL
2
+ class Point
3
+ # This array will be filled with any created Points.
4
+ # Just so they won't get garbage collected
5
+ # <em>(not sure how garbage collection works)</em>.
6
+ POINTS = []
7
+
8
+ # Initialize with two arguments:
9
+ # <tt>x</tt>:: x position
10
+ # <tt>y</tt>:: y position
11
+ # <tt>args = {}</tt>:: Optional hash with extra options.
12
+ # Currently, the only valid hash key is <tt>:assign_to</tt>,
13
+ # to assign this Point to an object upon initialization.
14
+ def initialize x, y, args = {}
15
+ POINTS << self
16
+ @position = {
17
+ x: x,
18
+ y: y
19
+ }
20
+ @assigned_to = []
21
+ assign_to args[:assign_to] if (args[:assign_to])
22
+ @layer = nil
23
+ @real_point = nil
24
+ end
25
+
26
+ def assign_to object
27
+ Helpers::PipeMethods.pipe_methods_from object, to: self
28
+ @assigned_to << object
29
+ end
30
+
31
+ # Returns all objects this Point was assigned to.
32
+ def get_assigned
33
+ return @assigned_to
34
+ end
35
+
36
+ # Returns true if the Point has been
37
+ # assigned to the passed <tt>object</tt>.
38
+ def assigned_to? object
39
+ return @assigned_to.include? object
40
+ end
41
+
42
+ # Returns self.
43
+ def get_point
44
+ return self
45
+ end
46
+
47
+ def has_point?
48
+ return true
49
+ end
50
+
51
+ def x
52
+ return get_position :x
53
+ end
54
+
55
+ def y
56
+ return get_position :y
57
+ end
58
+
59
+ def get_position target = :all
60
+ target = target.to_sym
61
+ return @position if (target == :all)
62
+ return @position[target] if (@position.keys.include?(target))
63
+ return nil
64
+ end
65
+
66
+ # Set the new position with the given arguments.
67
+ # <tt>args</tt> may be:
68
+ # Two integers, representing the <tt>x</tt> and <tt>y</tt> axes, respectively.
69
+ # A hash containing one or both of the keys <tt>:x</tt> and <tt>:y</tt>.
70
+ def set_position *args
71
+ @real_point = nil
72
+ new_position = parse_position *args
73
+ @position[:x] = new_position[:x] if (new_position.key? :x)
74
+ @position[:y] = new_position[:y] if (new_position.key? :y)
75
+ return get_position
76
+ end
77
+ alias_method :move_to, :set_position
78
+
79
+ # Move the Point relative to the given arguments.
80
+ # <tt>args</tt> may be:
81
+ # Two integers, representing the <tt>x</tt> and <tt>y</tt> axes, respectively.
82
+ # A hash containing one or both of the keys <tt>:x</tt> and <tt>:y</tt>.
83
+ def move_by *args
84
+ @real_point = nil
85
+ incremental_position = parse_position *args
86
+ @position[:x] += incremental_position[:x] if (incremental_position.key? :x)
87
+ @position[:y] += incremental_position[:y] if (incremental_position.key? :y)
88
+ return get_position
89
+ end
90
+
91
+ def collides_with? other
92
+ return collides_with_mask? other if (defined? other.has_mask?)
93
+ return collides_with_point? other if (defined? other.has_point?)
94
+ return collides_with_hash? other if (other.is_a?(Hash))
95
+ end
96
+
97
+ def collides_with_mask? mask
98
+ return mask.collides_with_point? self
99
+ end
100
+
101
+ def collides_with_point? point
102
+ return get_real_position == point.get_real_position
103
+ end
104
+
105
+ def collides_with_hash? hash
106
+ if (hash.keys.include_all?(:x, :y))
107
+ point = Point.new hash[:x], hash[:y]
108
+ return collides_with_point? point
109
+ end
110
+ return nil
111
+ end
112
+
113
+ def keys
114
+ sorted_keys = [:x, :y]
115
+ return @position.keys.sort do |axis|
116
+ next sorted_keys.index axis
117
+ end
118
+ end
119
+
120
+ def values
121
+ return @position.sort_by_keys(keys).values
122
+ end
123
+
124
+ # Set the parent Layer.
125
+ def set_layer layer
126
+ error(
127
+ "Passed argument `layer' must be an instance of `Layer', but got",
128
+ "`#{layer.inspect}:#{layer.class.name}'."
129
+ ) unless (layer.is_a? Layer)
130
+ @layer = layer
131
+ end
132
+
133
+ # Returns the parent Layer.
134
+ def get_layer
135
+ return @layer
136
+ end
137
+
138
+ # Returns true if this Point has a parent Layer.
139
+ def has_layer?
140
+ return !!@layer
141
+ end
142
+
143
+ # Returns a new Point with the real window position of this Point.
144
+ def get_real_point
145
+ return self unless (has_layer?)
146
+ return @real_point if (@real_point)
147
+ layer_point = get_layer.get_real_corner :left, :top
148
+ @real_point = Point.new(
149
+ (layer_point.x + x),
150
+ (layer_point.y + y)
151
+ )
152
+ return @real_point
153
+ end
154
+
155
+ # Returns the real window position of this Point as a Hash.
156
+ def get_real_position
157
+ return get_real_point.get_position
158
+ end
159
+
160
+ private
161
+
162
+ def parse_position *args
163
+ position = {}
164
+ args[0] = args[0].get_position if (args[0].is_a? Point)
165
+ case args.size
166
+ when 2
167
+ position[:x] = args[0]
168
+ position[:y] = args[1]
169
+ when 1
170
+ Helpers::Error.error(
171
+ "Ambiguous argument `#{args[0]}' for Point##{__method__}"
172
+ ) unless (args[0].is_a?(Hash))
173
+ Helpers::Error.error(
174
+ "Hash must include either :x, :y, or both keys for Point##{__method__}"
175
+ ) unless (args[0].keys.include_any?(:x, :y))
176
+ position[:x] = args[0][:x] if (args[0][:x])
177
+ position[:y] = args[0][:y] if (args[0][:y])
178
+ else
179
+ Helpers::Error.error(
180
+ "Invalid amount of arguments for Point##{__method__}.",
181
+ "Pass either two arguments representing the x and y axes, respectively, or",
182
+ "pass a single hash with the keys :x and :y with their respective axes values."
183
+ )
184
+ end
185
+ return position
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,237 @@
1
+ module AdventureRL
2
+ class Quadtree < Mask
3
+ include Helpers::MethodHelper
4
+
5
+ DEFAULT_SETTINGS = Settings.new(
6
+ max_objects: 4,
7
+ position: {
8
+ x: 0,
9
+ y: 0
10
+ },
11
+ size: {
12
+ width: 960,
13
+ height: 540
14
+ },
15
+ origin: {
16
+ x: :left,
17
+ y: :top
18
+ }
19
+ )
20
+
21
+ def self.get_default_settings
22
+ window = Window.get_window
23
+ return Settings.new(
24
+ position: (window ? window.get_position : DEFAULT_SETTINGS.get(:window, :position) || DEFAULT_SETTINGS[:position]),
25
+ size: (window ? window.get_size : DEFAULT_SETTINGS.get(:window, :size) || DEFAULT_SETTINGS[:size]),
26
+ origin: (window ? window.get_origin : DEFAULT_SETTINGS.get(:window, :origin) || DEFAULT_SETTINGS[:origin]),
27
+ )
28
+ end
29
+
30
+ def initialize settings = {}
31
+ @settings = DEFAULT_SETTINGS.merge(Quadtree.get_default_settings).merge(settings)
32
+ super @settings
33
+ @max_objects = @settings.get :max_objects
34
+ @quadtrees = {
35
+ top_left: nil,
36
+ top_right: nil,
37
+ bottom_left: nil,
38
+ bottom_right: nil
39
+ }
40
+ @objects = []
41
+ add_object [@settings.get(:objects)].flatten.compact
42
+ end
43
+
44
+ # Add the given Mask <tt>object</tt>(s) into the Quadtree,
45
+ # and split into smaller quadtrees if necessary.
46
+ def add_object object
47
+ objects = [object].flatten
48
+ objects.each do |obj|
49
+ validate_object_has_mask_or_point obj
50
+ add_object_to_quadtree obj
51
+ end
52
+ end
53
+ alias_method :add, :add_object
54
+
55
+ def add_object_to_quadtree object
56
+ return false unless (collides_with? object)
57
+ return false if (@objects.include? object)
58
+
59
+ if (@objects.size < @max_objects)
60
+ @objects << object
61
+ return true
62
+ end
63
+
64
+ split_quadtrees unless (has_quadtrees?)
65
+
66
+ return get_quadtrees.map do |quadtree|
67
+ next quadtree.add_object_to_quadtree(object)
68
+ end .any?
69
+ end
70
+
71
+ # Returns <tt>true</tt> if the given <tt>object</tt>
72
+ # collides with any other object and <tt>false</tt> if not.
73
+ def collides? object
74
+ validate_object_has_mask_or_point object
75
+ return collides_for?(object)
76
+ end
77
+
78
+ def collides_for? object
79
+ return false unless (collides_with? object)
80
+ return (
81
+ @objects.any? do |obj|
82
+ next obj != object && obj.collides_with?(object)
83
+ end ||
84
+ get_quadtrees.any? do |quadtree|
85
+ next quadtree.collides_for?(object)
86
+ end
87
+ )
88
+ end
89
+
90
+ # Returns all objects, that collide with <tt>object</tt>.
91
+ def get_colliding_objects object
92
+ validate_object_has_mask_or_point object
93
+ return get_colliding_objects_for(object)
94
+ end
95
+
96
+ def get_colliding_objects_for object
97
+ colliding_objects = []
98
+ return colliding_objects unless (collides_with? object)
99
+ colliding_objects.concat(@objects.select do |obj|
100
+ next obj != object && obj.collides_with?(object)
101
+ end)
102
+ get_quadtrees.each do |quadtree|
103
+ colliding_objects.concat quadtree.get_colliding_objects_for(object)
104
+ end
105
+ return colliding_objects
106
+ end
107
+
108
+ # Reset this and all child Quadtrees.
109
+ # Removes all stored objects.
110
+ def reset
111
+ @objects.clear
112
+ get_quadtrees.each &:reset
113
+ end
114
+
115
+ # Remove and (try to) re-add the given <tt>object</tt>(s) (single or multiple).
116
+ def reset_object object
117
+ objects = [object].flatten
118
+ objects.each do |obj|
119
+ @objects.delete obj
120
+ add_object_to_quadtree obj
121
+ end
122
+ end
123
+
124
+ # Remove the given <tt>object</tt>(s) (single or multiple) from the Quadtree (or any children).
125
+ def remove_object object
126
+ objects = [object].flatten
127
+ objects.each do |obj|
128
+ @objects.delete obj
129
+ get_quadtrees.each do |quadtree|
130
+ quadtree.remove_object obj
131
+ end
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ def validate_object_has_mask_or_point object
138
+ object.has_point? rescue error( # NOTE: #has_point? method must be available for both Point and Mask
139
+ "Expected an instance of Mask/Point or an object that has a Mask/Point, but got",
140
+ "`#{object.inspect}:#{object.class.name}'."
141
+ )
142
+ end
143
+
144
+ # Returns all the children Quadtrees.
145
+ def get_quadtrees
146
+ return @quadtrees.values.compact
147
+ end
148
+
149
+ # Returns <tt>true</tt> if this Quadtree has already been split
150
+ # and has children Quadtrees.
151
+ def has_quadtrees?
152
+ return @quadtrees.values.all?
153
+ end
154
+
155
+ def split_quadtrees
156
+ @quadtrees = @quadtrees.keys.map do |corner|
157
+ new_quadtree = get_split_quadtree_for_corner corner
158
+ next [corner, new_quadtree] if (new_quadtree)
159
+ next nil
160
+ end .compact.to_h
161
+ #move_objects_to_quadtrees if (@objects.any?) # NOTE: Doing this will break stuff.
162
+ end
163
+
164
+ def get_split_quadtree_for_corner corner
165
+ method_name = "get_split_quadtree_#{corner.to_s}".to_sym
166
+ error(
167
+ "Method `#{method_name.to_s}' doesn't exist for `#{self.inspect}:#{self.class}'."
168
+ ) unless (method_exists? method_name)
169
+ return method(method_name).call
170
+ end
171
+
172
+ def get_split_quadtree_top_left
173
+ return Quadtree.new(Settings.new(
174
+ position: get_position,
175
+ size: get_size.map do |side, size|
176
+ next [side, (size.to_f * 0.5).round]
177
+ end .to_h,
178
+ origin: get_origin,
179
+ max_objects: @max_objects
180
+ ))
181
+ end
182
+
183
+ def get_split_quadtree_top_right
184
+ return Quadtree.new(Settings.new(
185
+ position: get_position.map do |axis, pos|
186
+ next [axis, pos + (get_size(:width).to_f * 0.5).round] if (axis == :x)
187
+ next [axis, pos]
188
+ end .to_h,
189
+ size: get_size.map do |side, size|
190
+ next [side, (size.to_f * 0.5).round]
191
+ end .to_h,
192
+ origin: get_origin,
193
+ max_objects: @max_objects
194
+ ))
195
+ end
196
+
197
+ def get_split_quadtree_bottom_left
198
+ return Quadtree.new(Settings.new(
199
+ position: get_position.map do |axis, pos|
200
+ next [axis, pos + (get_size(:height).to_f * 0.5).round] if (axis == :y)
201
+ next [axis, pos]
202
+ end .to_h,
203
+ size: get_size.map do |side, size|
204
+ next [side, (size.to_f * 0.5).round]
205
+ end .to_h,
206
+ origin: get_origin,
207
+ max_objects: @max_objects
208
+ ))
209
+ end
210
+
211
+ def get_split_quadtree_bottom_right
212
+ return Quadtree.new(Settings.new(
213
+ position: get_position.map do |axis, pos|
214
+ next [axis, pos + (get_size(:width).to_f * 0.5).round] if (axis == :x)
215
+ next [axis, pos + (get_size(:height).to_f * 0.5).round] if (axis == :y)
216
+ end .to_h,
217
+ size: get_size.map do |side, size|
218
+ next [side, (size.to_f * 0.5).round]
219
+ end .to_h,
220
+ origin: get_origin,
221
+ max_objects: @max_objects
222
+ ))
223
+ end
224
+
225
+ # NOTE: Shouldn't be used, breaks stuff currently.
226
+ # Life is easier without this method.
227
+ def move_objects_to_quadtrees
228
+ return if (@objects.empty?)
229
+ @objects.each do |object|
230
+ get_quadtrees.detect do |quadtree|
231
+ next quadtree.add_object_to_quadtree(object)
232
+ end
233
+ end
234
+ @objects.clear
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,62 @@
1
+ module AdventureRL
2
+ class Rectangle < Mask
3
+ include Helpers::Error
4
+
5
+ # Default settings for Rectangle.
6
+ # <tt>settings</tt> passed to #new take precedence.
7
+ DEFAULT_SETTINGS = Settings.new(
8
+ color: 0xff_ffffff,
9
+ z_index: 0,
10
+ position: {
11
+ x: 0,
12
+ y: 0
13
+ },
14
+ size: {
15
+ width: 128,
16
+ height: 128
17
+ },
18
+ origin: {
19
+ x: :left,
20
+ y: :top
21
+ }
22
+ )
23
+
24
+ # Initialize with a Settings object <tt>settings</tt>.
25
+ def initialize settings = {}
26
+ @settings = DEFAULT_SETTINGS.merge settings
27
+ super @settings
28
+ @color = nil
29
+ @color_temporary = nil
30
+ @color_original = @settings.get :color
31
+ @z_index = @settings.get :z_index
32
+ end
33
+
34
+ def set_color color
35
+ @color = color
36
+ end
37
+
38
+ # Set the color only for the next frame.
39
+ def set_temporary_color color
40
+ @color_temporary = color
41
+ end
42
+
43
+ def get_color
44
+ return @color_temporary || @color || @color_original
45
+ end
46
+
47
+ def reset_color
48
+ @color = nil
49
+ end
50
+
51
+ def draw
52
+ corner = get_corner :left, :top
53
+ Gosu.draw_rect(
54
+ corner.x, corner.y,
55
+ get_size(:width), get_size(:height),
56
+ get_color,
57
+ @z_index
58
+ )
59
+ @color_temporary = nil
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,80 @@
1
+ module AdventureRL
2
+ class Settings
3
+ include Helpers::Error
4
+ attr_reader :content
5
+
6
+ # Initialize Settings with either a string representing
7
+ # a path to a YAML file, or a hash with your settings.
8
+ def initialize arg
9
+ if ([String, Pathname].include? arg.class)
10
+ @file = Pathname.new arg
11
+ validate_file_exists @file
12
+ @content = get_file_content(@file).keys_to_sym
13
+ elsif (arg.is_a? Hash)
14
+ @content = arg.keys_to_sym
15
+ elsif (arg.is_a? Settings)
16
+ @content = arg.get
17
+ end
18
+ end
19
+
20
+ # Returns the settings, following the structure of the passed <tt>keys</tt>.
21
+ # Similar to <tt>Hash#dig</tt>
22
+ def get *keys
23
+ current_content = @content
24
+ keys.each do |key|
25
+ key = key.to_sym if (key.is_a? String)
26
+ if (current_content.is_a?(Hash) && !current_content[key].nil?)
27
+ current_content = current_content[key]
28
+ else
29
+ current_content = nil
30
+ break
31
+ end
32
+ end
33
+ return current_content
34
+ end
35
+
36
+ # Merge self Settings content with other_settings Settings content or Hash.
37
+ # Can pass unlimited optional arguments as keys.
38
+ # If <tt>keys</tt> are given, then it will only merge the content
39
+ # from the keys <tt>keys</tt> for both Settings instances.
40
+ # Returns a new Settings object where the values of the <tt>keys</tt> keys
41
+ # are its settings content.
42
+ def merge other_settings, *keys
43
+ merged_settings = nil
44
+ if (other_settings.is_a? Settings)
45
+ merged_settings = Settings.new get(*keys).merge(other_settings.get(*keys))
46
+ elsif (other_settings.is_a? Hash)
47
+ merged_settings = Settings.new get(*keys).merge(other_settings)
48
+ else
49
+ error(
50
+ "Argument needs to be an instance of `AdventureRL::Settings' or a Hash",
51
+ "but got a `#{other_settings.class.name}'"
52
+ )
53
+ end
54
+ return merged_settings
55
+ end
56
+
57
+ def each
58
+ return get.each
59
+ end
60
+
61
+ private
62
+
63
+ def validate_file_exists file = @file
64
+ error_no_file file unless (file_exists? file)
65
+ end
66
+
67
+ def get_file_content file = @file
68
+ file = Pathname.new file unless (file.is_a? Pathname)
69
+ begin
70
+ return YAML.load_file(file.to_path) || {}
71
+ rescue
72
+ begin
73
+ return JSON.parse(file.read, symbolize_names: true)
74
+ rescue
75
+ error "Couldn't load settings file: '#{file.to_path}'", 'Is it a valid YAML or JSON file?'
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end