moonpxi-nimo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +15 -0
  5. data/Rakefile +47 -0
  6. data/VERSION +1 -0
  7. data/examples/bouncy.rb +101 -0
  8. data/examples/eventy.rb +19 -0
  9. data/examples/images/dg_classm32.png +0 -0
  10. data/examples/images/dg_dungeon32.png +0 -0
  11. data/examples/images/jeeklabs.png +0 -0
  12. data/examples/images/tcheco.png +0 -0
  13. data/examples/imagey.rb +82 -0
  14. data/examples/screeny.rb +80 -0
  15. data/examples/simplest.rb +23 -0
  16. data/examples/sounds/KDE-Sys-Log-Out.ogg +0 -0
  17. data/examples/sounds/k3b_error1.wav +0 -0
  18. data/examples/soundy.rb +40 -0
  19. data/examples/spritey.rb +90 -0
  20. data/lib/nimo.rb +35 -0
  21. data/lib/nimo/behavior/deflector.rb +39 -0
  22. data/lib/nimo/behavior/moveable.rb +33 -0
  23. data/lib/nimo/behavior/projectile.rb +23 -0
  24. data/lib/nimo/behavior/with_velocity.rb +28 -0
  25. data/lib/nimo/game_object.rb +58 -0
  26. data/lib/nimo/game_window.rb +59 -0
  27. data/lib/nimo/listeners/event_listener.rb +27 -0
  28. data/lib/nimo/listeners/input_listener.rb +70 -0
  29. data/lib/nimo/representations/image_representation.rb +18 -0
  30. data/lib/nimo/representations/object_representation.rb +67 -0
  31. data/lib/nimo/representations/quad_representation.rb +23 -0
  32. data/lib/nimo/representations/sprite_representation.rb +78 -0
  33. data/lib/nimo/representations/text_representation.rb +27 -0
  34. data/lib/nimo/screen.rb +103 -0
  35. data/lib/nimo/utils/game.rb +63 -0
  36. data/lib/nimo/utils/intersection.rb +30 -0
  37. data/lib/nimo/utils/object_extension.rb +17 -0
  38. data/lib/nimo/utils/resources.rb +51 -0
  39. data/nimo.gemspec +117 -0
  40. data/spec/nimo/behavior/deflector_spec.rb +51 -0
  41. data/spec/nimo/behavior/moveable_spec.rb +81 -0
  42. data/spec/nimo/behavior/projectile_spec.rb +34 -0
  43. data/spec/nimo/behavior/with_velocity_spec.rb +18 -0
  44. data/spec/nimo/game_object_spec.rb +169 -0
  45. data/spec/nimo/game_window_spec.rb +88 -0
  46. data/spec/nimo/listeners/event_listener_spec.rb +34 -0
  47. data/spec/nimo/listeners/input_listener_spec.rb +87 -0
  48. data/spec/nimo/representations/image_representation_spec.rb +41 -0
  49. data/spec/nimo/representations/object_representation_spec.rb +74 -0
  50. data/spec/nimo/representations/sprite_representation_spec.rb +73 -0
  51. data/spec/nimo/representations/text_representation_spec.rb +21 -0
  52. data/spec/nimo/screen_spec.rb +114 -0
  53. data/spec/nimo/utils/game_spec.rb +44 -0
  54. data/spec/nimo/utils/object_extension_spec.rb +21 -0
  55. data/spec/nimo/utils/resources_spec.rb +59 -0
  56. data/spec/spec_helper.rb +9 -0
  57. metadata +133 -0
@@ -0,0 +1,18 @@
1
+ module Nimo
2
+
3
+ class ImageRepresentation < ObjectRepresentation
4
+ represent :image
5
+
6
+ def load(resources, params)
7
+ raise "Must provide :image param for image loading" unless params.has_key?(:image)
8
+
9
+ @image = resources.image(params[:image])
10
+ @image = @image[params[:index]] if params.has_key?(:index)
11
+ end
12
+
13
+ def draw
14
+ @image.draw(@game_object.x, @game_object.y, 0)
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,67 @@
1
+ module Nimo
2
+
3
+ # Nimo::GameObject's view. It holds actions to be executed on every update or when a key is pressed.
4
+ #
5
+ class ObjectRepresentation
6
+ include InputListener, EventListener
7
+
8
+ attr_reader :game_object
9
+ attr_accessor :game_window
10
+
11
+ def initialize(game_window, game_object)
12
+ @game_window, @game_object = game_window, game_object
13
+ @always_actions = []
14
+ end
15
+
16
+ # Hook to load data (i.e. images and fonts) from the game window.
17
+ def load(resources, params)
18
+ raise "Load should be overriden by representation"
19
+ end
20
+
21
+ # Register and action that always execute on a game update.
22
+ def always(&action)
23
+ @always_actions << action
24
+ self
25
+ end
26
+
27
+ # Register an action that will execute when the game object sends a notification. Overrides EventListener to
28
+ # add default registration to game_object.
29
+ #
30
+ def listen_to(event_type, &action)
31
+ super
32
+ game_object.register_listener(event_type, self)
33
+ end
34
+
35
+ # Register an observer to be invoked every update, after all actions runned. This could be useful when a more complex behavior
36
+ # is required from the representation, and there is a need to inspect the game object to change some state.
37
+ #
38
+ def with_observer(&observer)
39
+ @observer = observer
40
+ end
41
+
42
+ def update
43
+ @always_actions.each { |action| @game_object.instance_eval(&action) }
44
+ process_inputs(@game_window)
45
+ @observer.call(self, game_object) unless @observer.nil?
46
+ end
47
+
48
+ # Should be overriden by childs.
49
+ def draw
50
+ raise "Draw should be overriden by representation"
51
+ end
52
+
53
+ def act_upon
54
+ @game_object
55
+ end
56
+
57
+ private
58
+
59
+ # Register itself as a object representation with the shortcut name.
60
+ #
61
+ def self.represent(representation_name)
62
+ Screen.register_representation(representation_name, self)
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,23 @@
1
+ module Nimo
2
+
3
+ class QuadRepresentation < ObjectRepresentation
4
+ represent :quad
5
+ attr_accessor :color
6
+
7
+ def load(resources, params)
8
+ raise "Must provide :color param for quad loading" unless params.has_key?(:color)
9
+
10
+ @color = params[:color]
11
+ end
12
+
13
+ def draw
14
+ @game_window.draw_quad(
15
+ @game_object.x, @game_object.y, @color,
16
+ @game_object.x + @game_object.width, @game_object.y, @color,
17
+ @game_object.x, @game_object.y + @game_object.height, @color,
18
+ @game_object.x + @game_object.width, @game_object.y + @game_object.height, @color)
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,78 @@
1
+ module Nimo
2
+
3
+ class SpriteRepresentation < ObjectRepresentation
4
+ represent :sprite
5
+ attr_accessor :flip
6
+
7
+ def initialize(game_window, game_object)
8
+ super(game_window, game_object)
9
+ @animations = {}
10
+ @current_animation = nil
11
+ @next_animation = nil
12
+ unflip
13
+ end
14
+
15
+ def load(resources, params)
16
+ raise "Must provide :image param for sprite loading" unless params.has_key?(:image)
17
+ @sprite_tiles = resources.image(params[:image])
18
+ end
19
+
20
+ def flip
21
+ @drawing = lambda { |sprite| sprite.draw(@game_object.x + @game_object.width, @game_object.y, 0, -1) }
22
+ end
23
+
24
+ def unflip
25
+ @drawing = lambda { |sprite| sprite.draw(@game_object.x, @game_object.y, 0) }
26
+ end
27
+
28
+ def change_to(animation_name)
29
+ @next_animation = animation_name
30
+ end
31
+
32
+ def draw
33
+ if @current_animation.name != @next_animation && @animations.has_key?(@next_animation)
34
+ @current_animation = @animations[@next_animation]
35
+ @current_animation.reset_animation
36
+ end
37
+ @drawing.call(@sprite_tiles[@current_animation.frame_index])
38
+ end
39
+
40
+ # Adds a new animation to the sprite. The first animation will be set as the current.
41
+ # A list of options can be specified to customise the behavior:
42
+ # - :timeout (default is 0.1): time to wait between frames
43
+ # - :loop (default is true): true if the animation should start from the beginning, after the last element,
44
+ # otherwise it will draw the last frame
45
+ def with_animation(name, frame_indexes, options = {})
46
+ @animations[name] = Animation.new(name, frame_indexes, options)
47
+ @current_animation = @animations[name] if @current_animation.nil?
48
+ self
49
+ end
50
+ end
51
+
52
+ class Animation
53
+ attr_reader :name
54
+
55
+ def initialize(name, frame_indexes, options)
56
+ @name = name
57
+ @frame_indexes = frame_indexes
58
+ @options = {:timeout => 0.1, :loop => true}.merge(options)
59
+ reset_animation
60
+ end
61
+
62
+ def reset_animation
63
+ @current_index = 0
64
+ @time_since_last_draw = Time.now
65
+ end
66
+
67
+ def frame_index
68
+ if (Time.now - @time_since_last_draw) > @options[:timeout]
69
+ @time_since_last_draw = Time.now
70
+ @current_index += 1
71
+ if @current_index == @frame_indexes.size
72
+ @current_index = @options[:loop] ? 0 : @frame_indexes.size - 1
73
+ end
74
+ end
75
+ @frame_indexes[@current_index]
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,27 @@
1
+ module Nimo
2
+
3
+ class TextRepresentation < ObjectRepresentation
4
+ represent :text
5
+ attr_accessor :color
6
+
7
+ def load(resources, params)
8
+ validate(params)
9
+
10
+ @font = resources.font(params[:font])
11
+ @color = params[:color]
12
+ @text = params[:text] # TODO Or should it get it from the game_object?
13
+ end
14
+
15
+ def draw
16
+ @font.draw(@text, @game_object.x, @game_object.y, 0, 1, 1, @color)
17
+ end
18
+
19
+ private
20
+
21
+ def validate(params)
22
+ [:font, :color, :text].each { |param| raise "Must provide :#{param} param for font loading" unless params.has_key?(param) }
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,103 @@
1
+ require "forwardable"
2
+
3
+ module Nimo
4
+
5
+ # Represent a game's screen, holding representations of domain objects for updates and draws.
6
+ # TODO: Add info on how to add 'quad', 'image' (whatever) methods
7
+ #
8
+ class Screen
9
+ include InputListener, EventListener
10
+ extend Forwardable
11
+
12
+ attr_reader :id
13
+
14
+ def_delegators :@game_window, :go_to, :open_menu, :close_menu
15
+ def_delegators :@resources, :images, :sounds, :fonts
16
+
17
+ def initialize(id, game_window, resources)
18
+ @id, @game_window, @resources = id, game_window, resources
19
+
20
+ @representations = []
21
+ @events = {}
22
+ @timers = []
23
+ end
24
+
25
+ # Register a representation to be called as a method.
26
+ # Example:
27
+ # Screen.register_representation(:blah, Blah)
28
+ # a_screen.blah :for => obj, :with => {:attr => 42} do
29
+ # # Initialising representation
30
+ # end
31
+ #
32
+ def self.register_representation(representation_name, representation_class)
33
+ define_method representation_name do |options, &blk|
34
+ representation = add(representation_class, options)
35
+ representation.instance_eval &blk if blk
36
+ end
37
+ end
38
+
39
+ # Add a representation to the screen. The options params is used to specify a GameObject and params. It returns the
40
+ # constructed representation.
41
+ # Examples of usage:
42
+ # screen.add(SomeRepresentation, :for => object, :with => { :attr => "something"}) # will construct SomeRepresentation with the supplied object and :with params
43
+ # screen.add(SomeRepresentation, :with => { :attr => "something"}) # will construct SomeRepresentation with a vanilla Nimo::GameObject
44
+ #
45
+ def add(representation_class, options)
46
+ params = options.has_key?(:with) ? options[:with] : {}
47
+ game_object = options.has_key?(:for) ? options[:for] : Nimo::GameObject.new(params)
48
+
49
+ representation = representation_class.new(@game_window, game_object)
50
+ representation.load(@resources, params)
51
+
52
+ @representations << representation
53
+ representation
54
+ end
55
+
56
+ # Defines an <tt>action</tt> to be executed after some <tt>seconds</tt>.
57
+ #
58
+ def timer_for(seconds, &action)
59
+ @timers << Timer.new(Time.now.to_f, seconds, action)
60
+ end
61
+
62
+ # :section: Gosu::Window hooks
63
+
64
+ # Updates all representations.
65
+ #
66
+ def update
67
+ process_timers
68
+ process_inputs(@game_window)
69
+ @representations.each { |representation| representation.update }
70
+ end
71
+
72
+ # Draws all representations.
73
+ #
74
+ def draw
75
+ @representations.each { |representation| representation.draw }
76
+ end
77
+
78
+ private
79
+
80
+ def process_timers
81
+ @timers.find_all { |timer| timer.time_is_up? }.each do |timer|
82
+ timer.run_action
83
+ @timers.delete(timer)
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ class Timer
90
+ def initialize(start_time, delay_in_seconds, action)
91
+ @start_time, @delay_in_seconds, @action = start_time, delay_in_seconds, action
92
+ end
93
+
94
+ def time_is_up?
95
+ (Time.now.to_f - @start_time) > @delay_in_seconds
96
+ end
97
+
98
+ def run_action
99
+ @action.call
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,63 @@
1
+ module Nimo
2
+
3
+ # Helper method to create and show a new GameWindow with a construction block.
4
+ #
5
+ # Example, from simplest.rb:
6
+ #
7
+ # Nimo::Game("Simplest", 640, 480) do
8
+ # screen :game do
9
+ # quad :with => { :width => 20, :height => 20, :color => Gosu::white } do
10
+ # # Code ommited.
11
+ # end
12
+ # when_key(Gosu::Button::KbEscape) { exit }
13
+ # end
14
+ # end
15
+ #
16
+ def self.Game(title, width, height, &blk)
17
+ window = Nimo::GameWindow.new(title, width, height)
18
+ GameBuilder.new(window).instance_eval(&blk) if block_given?
19
+ window.show
20
+ return window
21
+ end
22
+
23
+ class GameBuilder
24
+ def initialize(game_window)
25
+ @game_window = game_window
26
+ @resources = Nimo::Resources.new(@game_window)
27
+ end
28
+
29
+ # Load images that can be referenced by a tag.
30
+ # Example of <tt>image_definitions</tt>:
31
+ # images :some_tag => { :filename => "path_to_image.png" } # Load path_to_image.png to be used by the tag :some_tag
32
+ # images :tile_tag => { :filename => "path_to_tile.png", :tile_dimension => [32, 50] } # Load path_to_tile.png as a tile of width 32 and height 50
33
+ #
34
+ def images(image_definitions)
35
+ @resources.load_images(image_definitions)
36
+ end
37
+
38
+ # Load fonts that can be referenced by a tag.
39
+ # Example of <tt>font_definitions</tt>:
40
+ # fonts :some_tag => { :type => "font_type", :size => 20 } # Load font of type 'font_type'
41
+ #
42
+ def fonts(font_definitions)
43
+ @resources.load_fonts(font_definitions)
44
+ end
45
+
46
+ # Load sounds that can be referenced by a tag.
47
+ # Example of <tt>sound_definitions</tt>:
48
+ # sounds :some_tag => { :filename => "some_file.wav", :size => 20 }
49
+ #
50
+ def sounds(sound_definitions)
51
+ @resources.load_sounds(sound_definitions)
52
+ end
53
+
54
+ # Register a new screen with the <tt>name</tt>, using the supplied block as the Screen constructor.
55
+ #
56
+ def screen(screen_id, &blk)
57
+ screen = Nimo::Screen.new(screen_id, @game_window, @resources)
58
+ screen.instance_eval(&blk) if block_given?
59
+ @game_window.add_screen(screen_id, screen)
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,30 @@
1
+ module Nimo
2
+ class Intersection
3
+
4
+ attr_reader :x, :y, :width, :height
5
+
6
+ def initialize(x, y, width, height)
7
+ @x = x
8
+ @y = y
9
+ @width = width
10
+ @height = height
11
+ end
12
+
13
+ def self.between(obj1, obj2)
14
+ x = [obj1.x, obj2.x].max
15
+ y = [obj1.y, obj2.y].max
16
+ width = [obj1.x + obj1.width, obj2.x + obj2.width].min - x
17
+ height = [obj1.y + obj1.height, obj2.y + obj2.height].min - y
18
+ return Intersection.new(x, y, width, height)
19
+ end
20
+
21
+ def collistion_side_for(obj)
22
+ if @width >= @height
23
+ return obj.center.y > (@y + (@height/2)) ? :top : :bottom
24
+ else
25
+ return obj.center.x > (@x + (@width/2)) ? :left : :right
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ require "ostruct"
2
+
3
+ class Object
4
+
5
+ # Creates a new object from a hash. The hash keys will became attribute names, and
6
+ # the respective values will be the attribute's value.
7
+ #
8
+ # Example:
9
+ # obj = Object.from_hash({:a => 1, :b => 42})
10
+ # puts obj.a # 1
11
+ # puts obj.b # 42
12
+ #
13
+ def self.from_hash(hash)
14
+ OpenStruct.new(hash)
15
+ end
16
+
17
+ end
@@ -0,0 +1,51 @@
1
+ module Nimo
2
+
3
+ # Resource loader.
4
+ #
5
+ class Resources
6
+
7
+ attr_reader :images, :fonts, :sounds
8
+
9
+ def initialize(game_window)
10
+ @game_window = game_window
11
+ @images = {}
12
+ @fonts = {}
13
+ @sounds = {}
14
+ end
15
+
16
+ def load_images(image_definitions)
17
+ image_definitions.each { |tag, definition| @images[tag] ||= create_image_from(definition) }
18
+ end
19
+
20
+ def load_fonts(font_definitions)
21
+ font_definitions.each { |tag, definition| @fonts[tag] ||= Gosu::Font.new(@game_window, definition[:type], definition[:size]) }
22
+ end
23
+
24
+ def load_sounds(sound_definitions)
25
+ sound_definitions.each { |tag, definition| @sounds[tag] ||= Gosu::Song.new(@game_window, definition[:filename]) }
26
+ end
27
+
28
+ def method_missing(meth, *args, &blk)
29
+ super unless [:image, :sound, :font].include?(meth)
30
+ validate_and_return_resource(meth, args.shift)
31
+ end
32
+
33
+ private
34
+
35
+ def create_image_from(definition)
36
+ if definition.has_key?(:tile_dimension)
37
+ width = definition[:tile_dimension][0]
38
+ height = definition[:tile_dimension][1]
39
+ return Gosu::Image.load_tiles(@game_window, definition[:filename], width, height, false)
40
+ end
41
+ return Gosu::Image.new(@game_window, definition[:filename], 0)
42
+ end
43
+
44
+ def validate_and_return_resource(resource_type, tag)
45
+ resource_list = self.send("#{resource_type.to_s}s")
46
+ raise "No #{resource_type.to_s} resource named '#{tag}'" unless resource_list.has_key?(tag)
47
+ resource_list[tag]
48
+ end
49
+
50
+ end
51
+ end