red_bird 0.1.1

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +23 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +47 -0
  7. data/Rakefile +10 -0
  8. data/bin/setup +8 -0
  9. data/ext/red_bird/bird.c +15 -0
  10. data/ext/red_bird/bird.h +10 -0
  11. data/ext/red_bird/color.c +95 -0
  12. data/ext/red_bird/color.h +27 -0
  13. data/ext/red_bird/dynamic_sprite.c +163 -0
  14. data/ext/red_bird/dynamic_sprite.h +30 -0
  15. data/ext/red_bird/engine.c +354 -0
  16. data/ext/red_bird/engine.h +40 -0
  17. data/ext/red_bird/extconf.rb +9 -0
  18. data/ext/red_bird/font.c +94 -0
  19. data/ext/red_bird/font.h +26 -0
  20. data/ext/red_bird/input_device.c +100 -0
  21. data/ext/red_bird/input_device.h +15 -0
  22. data/ext/red_bird/keycode.c +42 -0
  23. data/ext/red_bird/keycode.h +12 -0
  24. data/ext/red_bird/loader.c +154 -0
  25. data/ext/red_bird/loader.h +54 -0
  26. data/ext/red_bird/main.c +38 -0
  27. data/ext/red_bird/main.h +12 -0
  28. data/ext/red_bird/palette.c +132 -0
  29. data/ext/red_bird/palette.h +23 -0
  30. data/ext/red_bird/rect.c +257 -0
  31. data/ext/red_bird/rect.h +20 -0
  32. data/ext/red_bird/render.c +130 -0
  33. data/ext/red_bird/render.h +25 -0
  34. data/ext/red_bird/sprite.c +130 -0
  35. data/ext/red_bird/sprite.h +27 -0
  36. data/ext/red_bird/text.c +212 -0
  37. data/ext/red_bird/text.h +31 -0
  38. data/ext/red_bird/texture.c +157 -0
  39. data/ext/red_bird/texture.h +33 -0
  40. data/ext/red_bird/texture_imp.cpp +49 -0
  41. data/ext/red_bird/texture_imp.hpp +29 -0
  42. data/ext/red_bird/timer.c +134 -0
  43. data/ext/red_bird/timer.h +25 -0
  44. data/lib/red_bird.rb +15 -0
  45. data/lib/red_bird/animation.rb +133 -0
  46. data/lib/red_bird/camera.rb +61 -0
  47. data/lib/red_bird/controller.rb +44 -0
  48. data/lib/red_bird/dynamic_sprite.rb +38 -0
  49. data/lib/red_bird/engine.rb +81 -0
  50. data/lib/red_bird/entity.rb +74 -0
  51. data/lib/red_bird/entity_collision.rb +31 -0
  52. data/lib/red_bird/input_device.rb +86 -0
  53. data/lib/red_bird/palette.rb +23 -0
  54. data/lib/red_bird/relative_entity.rb +95 -0
  55. data/lib/red_bird/sprite.rb +40 -0
  56. data/lib/red_bird/stage.rb +60 -0
  57. data/lib/red_bird/tile_map.rb +118 -0
  58. data/lib/red_bird/tile_set.rb +56 -0
  59. data/lib/red_bird/uibox.rb +143 -0
  60. data/lib/red_bird/version.rb +3 -0
  61. data/lib/red_bird/vertical_menu.rb +110 -0
  62. data/red_bird.gemspec +37 -0
  63. metadata +149 -0
@@ -0,0 +1,44 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ # A Controller receives commands from a {RedBird::InputDevice} and executes
4
+ # codes associated with those commands. Every {RedBird::InputDevice} instance
5
+ # has a variable called +@controller+ that must point to an instance of this
6
+ # class.
7
+ #
8
+ # The purpose of this class is to allow you to change the effect of user
9
+ # input by merely changing the currently active controller; you would do so,
10
+ # for example, when the user pauses the game, or when the user's character
11
+ # enters a vehicle.
12
+ #
13
+ # @see RedBird::InputDevice
14
+ # @author Frederico Linhares
15
+ class Controller
16
+ def initialize
17
+ @commands = {}
18
+
19
+ # By default, quit game when window is closed or another signal to end
20
+ # the game is given.
21
+ self.add_command(:quit_game) do
22
+ Engine::quit_game
23
+ end
24
+ end
25
+
26
+ # Use this method to create an association between a command and a code
27
+ # block.
28
+ #
29
+ # @param command [Symbol]
30
+ # @param function
31
+ # @author Frederico Linhares
32
+ def add_command(command, &function)
33
+ @commands[command] = function
34
+ end
35
+
36
+ # Execute a code block associated with a command.
37
+ #
38
+ # @param command [Symbol]
39
+ # @author Frederico Linhares
40
+ def call_command(command)
41
+ @commands[command].call unless @commands[command].nil?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ # A sprite is rectangular part of an texture.
4
+ class DynamicSprite
5
+ # Create an array of sprites and return it. The array has homogeneous
6
+ # sprites that do not overlap and have no gaps between them.
7
+ #
8
+ # To create a grid starting from the position x 20, y 20, containing three
9
+ # columns and two rows, each sprite, having 100x80 pixels:
10
+ #
11
+ # RedBird::Texture.grid sprite, 20, 20, 3, 2, 100, 80
12
+ #
13
+ # @param texture [RedBird::Texture] a texture that will be used as the
14
+ # source for all sprites.
15
+ # @param init_x [Integer] initial x coordinate for the grid.
16
+ # @param init_y [Integer] initial y coordinate for the grid.
17
+ # @param num_hor_sprites [Integer] number of columns the grid will have.
18
+ # @param num_ver_sprites [Integer] number of rows the grid will have.
19
+ # @param width [Integer] width of every sprite.
20
+ # @param height [Integer] height of every sprite.
21
+ # @param mods [Hash]
22
+ # @return [Array]
23
+ # @author Frederico Linhares
24
+ def self.grid(texture, init_x, init_y, num_hor_sprites, num_ver_sprites,
25
+ width, height, mods)
26
+ sprites = []
27
+ num_ver_sprites.times do |v|
28
+ num_hor_sprites.times do |h|
29
+ sprites << new(
30
+ texture, init_x + h * width, init_y + v * height, width, height,
31
+ mods)
32
+ end
33
+ end
34
+
35
+ return sprites
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ module Engine
4
+ # Maximum frames per second. The Engine will try to maintain the game
5
+ # running at the frame rate defined by this variable.
6
+ @@max_fps = 30
7
+
8
+ # Set frames per second.
9
+ #
10
+ # @param fps [Integer]
11
+ # @author Frederico Linhares
12
+ def self.fps=(fps)
13
+ @@max_fps = fps
14
+ end
15
+
16
+ # Get frames per second.
17
+ #
18
+ # @return [Integer]frames per second.
19
+ def self.fps
20
+ return @@max_fps
21
+ end
22
+
23
+ # Exit the current stage and end the {Engine#run} loop.
24
+ #
25
+ # @author Frederico Linhares
26
+ def self.quit_game
27
+ @@quit_game = true
28
+ @@quit_stage = true
29
+ end
30
+
31
+ # Exit the current stage. The code block sent to {Engine#run} will be
32
+ # called again to get the next stage.
33
+ #
34
+ # @author Frederico Linhares
35
+ def self.quit_stage
36
+ @@quit_stage = true
37
+ end
38
+
39
+ # This method initializes the engine, executes the main loop, and returns
40
+ # when the game is over. This function calls the code block to get the
41
+ # first stage of the game and call it again every time a stage end to get
42
+ # the next one.
43
+ #
44
+ # @param global_data [Object] any object that you want to share between the
45
+ # game. This method sends this object back to the code block received.
46
+ # @yield [global_data] give +global_data+ to the block and expect it to
47
+ # return the next stage.
48
+ def self.run(global_data, &stage_manager)
49
+ @@quit_game = false
50
+
51
+ self.load do
52
+ while not @@quit_game do
53
+ @@quit_stage = false
54
+
55
+ timer = Timer.new(@@max_fps)
56
+ clear_color = Color.new(0, 0, 0, 0)
57
+ current_stage = stage_manager.call(global_data)
58
+
59
+ # The best moment to cleanup memory is before the engine loads a new
60
+ # stage.
61
+ GC.start
62
+
63
+ while not @@quit_stage do
64
+ timer.tick
65
+
66
+ current_stage.input_device.exec_events(current_stage.entities)
67
+
68
+ current_stage.pre_tick
69
+ current_stage.entities.each { |e| e.tick }
70
+ current_stage.post_tick
71
+
72
+ Render.set_color(clear_color)
73
+ Render.clear_screen
74
+ current_stage.entities.each { |e| e.render }
75
+ Render.update_screen
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,74 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ # An entity is an object that is managed by the Engine; it has a size, a
4
+ # position, ticks, and is renderable. {RedBird::Engine.run} expects every
5
+ # single entity that it receives from {RedBird::Stage} to have these methods
6
+ # and attributes.
7
+ #
8
+ # @author Frederico Linhares
9
+ class Entity
10
+ # Position
11
+ #
12
+ # @return [Integer]
13
+ attr_reader :pos_x, :pos_y
14
+ # Size
15
+ #
16
+ # @return [Integer]
17
+ attr_reader :width, :height
18
+ # Entities with higher priorit are called first when the
19
+ # {RedBird::Engine.run} calls {#tick} and {#render}.
20
+ #
21
+ # @return [Integer]
22
+ attr_reader :priority
23
+
24
+ # The {RedBird::Engine.run} calls this method every single frame. Overwrite
25
+ # it if you want your entity to do something every frame.
26
+ #
27
+ # @author Frederico Linhares
28
+ def tick
29
+ # By default do nothing
30
+ end
31
+
32
+ # The {RedBird::Engine.run} calls this method whenever the cursor is over
33
+ # this entity. Overwrite this function if you want your entity to react to a
34
+ # cursor hover.
35
+ #
36
+ # @param x [Integer] cursor position.
37
+ # @param y [Integer] cursor position.
38
+ # @author Frederico Linhares
39
+ def cursor_hover(x, y)
40
+ # By default do nothing
41
+ end
42
+
43
+ # The {RedBird::Engine.run} calls this method whenever there is a click over
44
+ # this entity. Overwrite this function if you want your entity to react to a
45
+ # click.
46
+ #
47
+ # @param x [Integer] cursor position.
48
+ # @param y [Integer] cursor position.
49
+ # @author Frederico Linhares
50
+ def cursor_down(x, y)
51
+ # By default do nothing
52
+ end
53
+
54
+ # The {RedBird::Engine.run} calls this method whenever there is a cursor
55
+ # release over this entity. Overwrite this function if you want your entity
56
+ # to react to a cursor release.
57
+ #
58
+ # @param x [Integer] cursor position.
59
+ # @param y [Integer] cursor position.
60
+ # @author Frederico Linhares
61
+ def cursor_up(x, y)
62
+ # By default do nothing
63
+ end
64
+
65
+ # The {RedBird::Engine.run} calls this method to render this entity into
66
+ # the scree. Overwrite this method if you want to change how the entity
67
+ # renders into the screen.
68
+ #
69
+ # @author Frederico Linhares
70
+ def render
71
+ self.current_sprite.render_to_screen(self.pos_x, self.pos_y)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,31 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ # This class does collision tests between entities.
4
+ class EntityCollision
5
+ # @param entities [Array<RedBird::Entity>] array containing the entities to
6
+ # test for collusion.
7
+ # @author Frederico Linhares
8
+ def initialize(entities)
9
+ @entities = entities
10
+ end
11
+
12
+ # Loop over entities in the array and test if they collide. In the case of
13
+ # collision, it calls the method +collide+ of the entity and sends the
14
+ # entity it collided to as a single argument.
15
+ #
16
+ # @author Frederico Linhares
17
+ def call
18
+ # Calculate entities rote
19
+ @entities.each_with_index do |e1, i|
20
+ @entities.drop(i + 1).each do |e2|
21
+ if e1.collision_box.collide? e2.collision_box then
22
+ e1.collide(e2)
23
+ e2.collide(e1)
24
+ # If an object responds to 'collision_box' then it is tested.
25
+ end if e1.respond_to?('collision_box')
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,86 @@
1
+ # SPDX-License-Identifier: MIT
2
+ module RedBird
3
+ # The InputDevice receives input from the keyboard, joystick, and mouse;
4
+ # then, it translates the input into a meaningful command name and sends it
5
+ # to a Controller. For example, you can use it to translate the 'space' key
6
+ # from the keyboard and an 'x' button from a controller into a +:jump+
7
+ # command.
8
+ #
9
+ # This class has an internal Hash that translates inputs into commands. For
10
+ # every input, it looks if there is a key associated with that input; it
11
+ # sends a command if such key exists; otherwise, it does nothing.
12
+ #
13
+ # @see RedBird::Controller
14
+ # @author Frederico Linhares
15
+ class InputDevice
16
+ # Defines a {RedBird::Controller} to receives commands from the input.
17
+ attr_writer :controller
18
+
19
+ # @param controller [RedBird::Controller]
20
+ # @author Frederico Linhares
21
+ def initialize(controller)
22
+ @controller = controller
23
+ @keydown_inputs = {}
24
+ @keyup_inputs = {}
25
+ end
26
+
27
+ # Associates a keydown even to a command_name.
28
+ #
29
+ # @param keycode [Integer] use constants under {RedBird::Keycode}.
30
+ # @param command_name [Symbol]
31
+ # @author Frederico Linhares
32
+ def add_keydown(keycode, command_name)
33
+ @keydown_inputs[keycode] = command_name
34
+ end
35
+
36
+ # Associates a keyup even to a command_name.
37
+ #
38
+ # @param keycode [Integer] use constants under {RedBird::Keycode}.
39
+ # @param command_name [Symbol]
40
+ # @author Frederico Linhares
41
+ def add_keyup(keycode, command_name)
42
+ @keyup_inputs[keycode] = command_name
43
+ end
44
+
45
+ # Gets all events that are in the queue, translate then into commands, and
46
+ # send then to the current controller.
47
+ #
48
+ # @param entities [Array<RedBird::Entity>] entities that can receive events
49
+ # from mouse.
50
+ # @author Frederico Linhares
51
+ def exec_events(entities)
52
+ get_events do |type, data|
53
+ case type
54
+ when :key_down then
55
+ @controller.call_command(@keydown_inputs[data])
56
+ when :key_up then
57
+ @controller.call_command(@keyup_inputs[data])
58
+ when :mouse_motion then
59
+ entity = cursor_hover_entity(entities, data[:x], data[:y])
60
+ entity.cursor_hover(data[:x], data[:y]) unless entity.nil?
61
+ when :mouse_button_down then
62
+ entity = cursor_hover_entity(entities, data[:x], data[:y])
63
+ entity.cursor_down(data[:x], data[:y]) unless entity.nil?
64
+ when :mouse_button_up then
65
+ entity = cursor_hover_entity(entities, data[:x], data[:y])
66
+ entity.cursor_up(data[:x], data[:y]) unless entity.nil?
67
+ when :quit_game then
68
+ Engine::quit_game
69
+ end
70
+ end
71
+ end
72
+
73
+ protected
74
+ # This function is called when user move the mouse to se if mouse is hover
75
+ # an entity.
76
+ def cursor_hover_entity(entities, x, y)
77
+ entities.each do |e|
78
+ if(x >= e.pos_x and x <= e.pos_x + e.width and
79
+ y >= e.pos_y and y <= e.pos_y + e.height) then
80
+ return e
81
+ end
82
+ end
83
+ return nil
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,23 @@
1
+ # SPDX-License-Identifier: MIT
2
+ require 'yaml'
3
+
4
+ module RedBird
5
+ class Palette
6
+ # Load information from a YAML file and uses it to create an instance of this
7
+ # class
8
+ #
9
+ # @param tile_file [String] path to the yaml file.
10
+ # @return [RedBird::Palette]
11
+ # @author Frederico Linhares
12
+ def self.from_yml(palette_file)
13
+ data = YAML.load_file(palette_file)
14
+
15
+ # Convert data from YAML into Color instances.
16
+ colors = data.map do |i|
17
+ Color.new(i[0], i[1], i[2], i[3])
18
+ end
19
+
20
+ return self.new(colors)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,95 @@
1
+ # SPDX-License-Identifier: MIT
2
+ require_relative 'entity'
3
+
4
+ module RedBird
5
+ # The {RedBird::Entity} has an absolute position on the screen. Use this
6
+ # class if you want an entity that has a position relative to a scenario
7
+ # ({RedBird::TileMap}).
8
+ #
9
+ # @author Frederico Linhares
10
+ class RelativeEntity < Entity
11
+ # @return [RedBird::Rect]
12
+ attr_reader :collision_box
13
+
14
+ # @param relative_pos_x [Integer] position in the scenario
15
+ # @param relative_pos_y [Integer] position in the scenario
16
+ # @param width [Integer]
17
+ # @param height [Integer]
18
+ # @author Frederico Linhares
19
+ def initialize(relative_pos_x, relative_pos_y, width, height)
20
+ super()
21
+
22
+ @priority = 1
23
+
24
+ @collision_box = RedBird::Rect.new(
25
+ relative_pos_x, relative_pos_y, width, height)
26
+ end
27
+
28
+ # Define which scenario to use as a reference for every
29
+ # {RedBird::RelativeEntity}.
30
+ #
31
+ # @param scenario [RedBird::TileMap]
32
+ # @author Frederico Linhares
33
+ def self.scenario=(scenario)
34
+ @@scenario = scenario
35
+ end
36
+
37
+ # @return [RedBird::TileMap]
38
+ # @author Frederico Linhares
39
+ def self.scenario
40
+ return @@scenario
41
+ end
42
+
43
+ # @return [Integer] position in the scenario
44
+ # @author Frederico Linhares
45
+ def relative_pos_x
46
+ return @collision_box.x
47
+ end
48
+
49
+ # @return [Integer] position in the scenario
50
+ # @author Frederico Linhares
51
+ def relative_pos_y
52
+ return @collision_box.y
53
+ end
54
+
55
+ # @param x [Integer] position in the scenario
56
+ # @author Frederico Linhares
57
+ def relative_pos_x=(x)
58
+ @collision_box.x = x
59
+ end
60
+
61
+ # @param y [Integer] position in the scenario
62
+ # @author Frederico Linhares
63
+ def relative_pos_y=(y)
64
+ @collision_box.y = y
65
+ end
66
+
67
+ # Gets absolute position (position in the screen).
68
+ #
69
+ # @return [Float] position in the screen
70
+ # @author Frederico Linhares
71
+ def pos_x
72
+ @collision_box.x - @@scenario.hor_offset + @@scenario.pos_x
73
+ end
74
+
75
+ # Gets absolute position (position in the screen).
76
+ #
77
+ # @return [Float] position in the screen
78
+ # @author Frederico Linhares
79
+ def pos_y
80
+ @collision_box.y - @@scenario.ver_offset + @@scenario.pos_y
81
+ end
82
+
83
+ # @return [Integer]
84
+ # @author Frederico Linhares
85
+ def width
86
+ @collision_box.width
87
+ end
88
+
89
+ # @return [Integer]
90
+ # @author Frederico Linhares
91
+ def height
92
+ @collision_box.height
93
+ end
94
+ end
95
+ end