ippa-chingu 0.2.0 → 0.3.0

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.
@@ -1,41 +1,149 @@
1
1
  module Chingu
2
- module GameStateHelpers
2
+
3
+ module InputClient
4
+ def input=(input_map)
5
+ @input = input_map
6
+ @parent.add_input_client(self) if @parent
7
+ end
8
+
9
+ def input
10
+ @input
11
+ end
12
+ end
13
+
14
+ module InputDispatcher
15
+ def add_input_client(object)
16
+ @input_clients << object
17
+ end
18
+
19
+ def remove_input_client(object)
20
+ @input_clients.delete(object)
21
+ end
22
+
23
+ def dispatch_button_down(id, object)
24
+ return if object.nil? || object.input.nil?
25
+
26
+ object.input.each do |symbol, action|
27
+ if Input::SYMBOL_TO_CONSTANT[symbol] == id
28
+ dispatch_action(action, object)
29
+ end
30
+ end
31
+ end
32
+
33
+ def dispatch_button_up(id, object)
34
+ return if object.nil? || object.input.nil?
35
+
36
+ object.input.each do |symbol, action|
37
+ if symbol.to_s.include? "released_"
38
+ symbol = symbol.to_s.sub("released_", "").to_sym
39
+ if Input::SYMBOL_TO_CONSTANT[symbol] == id
40
+ dispatch_action(action, object)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ #
47
+ # Dispatches input-mapper for any given object
48
+ #
49
+ def dispatch_input_for(object, prefix = "holding_")
50
+ return if object.nil? || object.input.nil?
51
+
52
+ object.input.each do |symbol, action|
53
+ if symbol.to_s.include? prefix
54
+ symbol = symbol.to_s.sub(prefix, "").to_sym
55
+ if $window.button_down?(Input::SYMBOL_TO_CONSTANT[symbol])
56
+ dispatch_action(action, object)
57
+ end
58
+ end
59
+ end
60
+ end
3
61
 
4
62
  #
5
- # push_gamestate accepts either a class inherited from GameState or an object-instance from such a class.
63
+ # For a given object, dispatch "action".
64
+ # An action can be:
6
65
  #
7
- # push_gamestate(Intro) is the same as:
8
- # push_gamestate(Intro.new)
9
- #
10
- # The first line ends upp calling "new" to Intro before activating the newlycreated gamestate.
66
+ # * Symbol (:p, :space), translates into a method-call
67
+ # * Proc/Lambda, call() it
68
+ # * GameState-instance, push it on top of stack
69
+ # * GameState-inherited class, create a new instance, cache it and push it on top of stack
11
70
  #
12
- def push_gamestate(state)
13
- if state.is_a? Chingu::GameState
14
- $window.game_state_manager.push_state(state)
15
- elsif state.superclass == Chingu::GameState
16
- $window.game_state_manager.push_state(state.new)
71
+ def dispatch_action(action, object)
72
+ # puts "Dispatch Action: #{action} - Objects class: #{object.class.to_s}"
73
+ if action.is_a? Symbol
74
+ object.send(action)
75
+ elsif action.is_a? Proc
76
+ action.call
77
+ elsif action.is_a? Chingu::GameState
78
+ push_game_state(action)
79
+ elsif action.superclass == Chingu::GameState
80
+ push_game_state(action)
17
81
  end
18
82
  end
83
+ end
84
+
85
+ #
86
+ # push_game_state accepts either a class inherited from GameState or an object-instance from such a class.
87
+ #
88
+ # push_game_state(Intro):
89
+ # game state mananger will create a new Intro-object first time called and cache it.
90
+ #
91
+ # push_game_state(Intro.new):
92
+ # The first line ends up calling "new" to Intro before activating the newly created game state.
93
+ # Each time 'push_game_state(Intro.new)' is called a new Intro-object will be created.
94
+ # Usefull for stuff like: push_game_state(Level.new(:level_nr => 11))
95
+ #
96
+ module GameStateHelpers
97
+ def push_game_state(state, options = {})
98
+ $window.game_state_manager.push_state(state, options)
99
+ end
19
100
 
20
- def pop_gamestate
21
- $window.game_state_manager.pop_state
101
+ def pop_game_state(options = {})
102
+ $window.game_state_manager.pop_state(options)
22
103
  end
23
104
 
24
- def current_gamestate
25
- $window.game_state_manager.state
105
+ def current_game_state
106
+ $window.game_state_manager.current_state
26
107
  end
27
108
 
28
- def previous_gamestate
109
+ def previous_game_state
29
110
  $window.game_state_manager.previous_state
30
111
  end
112
+
113
+ def clear_game_states
114
+ $window.game_state_manager.clear_states
115
+ end
31
116
  end
32
117
 
118
+ #
119
+ # Various helper-methods to manipulate the screen
120
+ #
33
121
  module DrawHelpers
34
- def fill(color)
35
- $window.draw_quad(0, 0, color, $window.width, 0, color, $window.width, $window.width, color, 0, $window.height, color, 0, :default)
36
- end
122
+ #
123
+ # Fills whole window with color 'c'
124
+ #
125
+ def fill(c)
126
+ $window.draw_quad(0,0,c,$window.width,0,c,$window.width,$window.width,c,0,$window.height,c,0,:default)
127
+ end
128
+
129
+ #
130
+ # Fills a given Rect 'r' with color 'c'
131
+ #
132
+ def fill_rect(r, c)
133
+ $window.draw_quad(r.x,r.y,c, r.right,r.y,c, r.right,r.bottom,c, r.x,r.bottom,c,0,:default)
134
+ end
37
135
 
38
136
  def fade(options = {})
39
137
  end
40
138
  end
139
+
140
+ module GameObjectHelpers
141
+ #
142
+ # Fetch game objects of a certain type/class
143
+ #
144
+ def game_objects_of_class(klass)
145
+ @game_objects.select { |game_object| game_object.is_a? klass }
146
+ end
147
+ end
148
+
41
149
  end
data/lib/chingu/input.rb CHANGED
@@ -19,14 +19,14 @@ module Chingu
19
19
 
20
20
  KbBackspace => [:backspace],
21
21
  KbDelete => [:delete, :del],
22
- KbDown => [:down],
22
+ KbDown => [:down_arrow, :down],
23
23
  KbEnd => [:end],
24
24
  KbEnter => [:enter],
25
25
  KbEscape => [:escape, :esc],
26
26
 
27
27
  KbHome => [:home],
28
28
  KbInsert => [:insert, :ins],
29
- KbLeft => [:left],
29
+ KbLeft => [:left_arrow, :left],
30
30
  KbLeftAlt => [:left_alt, :lalt],
31
31
  KbLeftControl => [:left_control, :left_ctrl, :lctrl],
32
32
  KbLeftShift => [:left_shift, :lshift],
@@ -40,13 +40,13 @@ module Chingu
40
40
  KbPageUp => [:page_up],
41
41
  # KbPause => [:pause],
42
42
  KbReturn => [:return],
43
- KbRight => [:right],
43
+ KbRight => [:right_arrow, :right],
44
44
  KbRightAlt => [:right_alt, :ralt],
45
45
  KbRightControl => [:right_control, :right_ctrl, :rctrl],
46
46
  KbRightShift => [:right_shift, :rshift],
47
47
  KbSpace => [:" ", :space],
48
48
  KbTab => [:tabulator, :tab],
49
- KbUp => [:up],
49
+ KbUp => [:up_arrow, :up],
50
50
 
51
51
  MsLeft => [:left_mouse_button, :mouse_left],
52
52
  MsMiddle => [:middle_mouse_button, :mouse_middle],
@@ -29,7 +29,10 @@ module Chingu
29
29
  self.add_background(arg)
30
30
  end
31
31
 
32
- def update
32
+ #
33
+ # TODO: make use of time here!
34
+ #
35
+ def update(time)
33
36
  @backgrounds.each do |background|
34
37
  background.x = -@x / background.damping
35
38
  background.y = @y / background.damping
data/lib/chingu/text.rb CHANGED
@@ -13,7 +13,17 @@ module Chingu
13
13
  #
14
14
  class Text < Chingu::GameObject
15
15
  attr_accessor :text
16
- attr_reader :height, :font
16
+ attr_reader :height, :gosu_font
17
+
18
+ @@size = nil
19
+ @@font = nil
20
+ def self.font; @@font; end
21
+ def self.font=(value); @@font = value; end
22
+
23
+ def self.size; @@size; end
24
+ def self.size=(value); @@size = value; end
25
+ def self.height; @@size; end
26
+ def self.height=(value); @@size = value; end
17
27
 
18
28
  #
19
29
  # Takes the standard GameObject-hash-arguments but also:
@@ -24,21 +34,21 @@ module Chingu
24
34
  def initialize(options)
25
35
  super(options)
26
36
  @text = options[:text] || "-No text specified-"
27
- @font_name = options[:font_name] || options[:font] || "verdana"
28
- @height = options[:height] || options[:size] || 15
37
+ @font = options[:font] || @@font || "verdana"
38
+ @height = options[:height] || options[:size] || @@size || default_font_name()
29
39
 
30
- @font = Gosu::Font.new($window, @font_name, @height)
40
+ @gosu_font = Gosu::Font.new($window, @font, @height)
31
41
  end
32
42
 
33
43
  def draw
34
- @font.draw_rot(@text, @x.to_i, @y.to_i, @zorder, @angle, @factor_x, @factor_y, @color, @mode)
44
+ @gosu_font.draw_rot(@text, @x.to_i, @y.to_i, @zorder, @angle, @factor_x, @factor_y, @color, @mode)
35
45
  end
36
46
 
37
47
  #
38
48
  # Returns the width, in pixels, the given text would occupy if drawn.
39
49
  #
40
50
  def width
41
- @font.text_width(@text, @factor_x)
51
+ @gosu_font.text_width(@text, @factor_x)
42
52
  end
43
53
 
44
54
  end
data/lib/chingu/window.rb CHANGED
@@ -1,11 +1,22 @@
1
1
  module Chingu
2
2
  class Window < Gosu::Window
3
- include Chingu::GameStateHelpers # adds push_gamestate(), pop_gamestate() and previous_gamestate()
4
- include Chingu::DrawHelpers # adds fill() etc..
3
+ # adds push_game_state, pop_game_state, current_game_state and previous_game_state
4
+ include Chingu::GameStateHelpers
5
5
 
6
- attr_reader :root, :update_list, :draw_list, :tick, :game_state_manager
7
- attr_accessor :key_receivers, :input
8
- attr_reader :game_objects
6
+ # adds fill() etc...
7
+ include Chingu::DrawHelpers
8
+
9
+ # adds game_objects_of_class etc ...
10
+ include Chingu::GameObjectHelpers
11
+
12
+ # Input dispatch helpers
13
+ include Chingu::InputDispatcher
14
+
15
+ # input= and input
16
+ include Chingu::InputClient
17
+
18
+ attr_reader :root, :game_state_manager, :game_objects, :milliseconds_since_last_tick
19
+
9
20
 
10
21
  #
11
22
  # See http://www.libgosu.org/rdoc/classes/Gosu/Window.html
@@ -26,136 +37,125 @@ module Chingu
26
37
  Gosu::Image.autoload_dirs = [".", File.join(@root, "gfx"), File.join(@root, "media")]
27
38
  Gosu::Sample.autoload_dirs = [".", File.join(@root, "sound"), File.join(@root, "media")]
28
39
  Gosu::Tile.autoload_dirs = [".", File.join(@root, "gfx"), File.join(@root, "media")]
40
+ Gosu::Song.autoload_dirs = [".", File.join(@root, "sfx"), File.join(@root, "media")]
29
41
 
30
- @game_objects = []
31
- @input = nil
32
-
33
- @ticks = 0
34
- @last_tick = Gosu::milliseconds
42
+ @game_objects = Set.new
43
+ @input_clients = Set.new # Set is like a unique Array with Hash lookupspeed
35
44
 
36
45
  @fps_counter = FPSCounter.new
37
46
  @game_state_manager = GameStateManager.new
38
47
 
39
- @update_list = []
40
- @draw_list = []
41
-
42
48
  self.input = { :escape => close }
43
49
  end
44
50
 
45
- def add_game_object(game_object)
46
- @game_objects.push(game_object) unless @game_objects.include?(game_object)
51
+ def add_game_object(object)
52
+ @game_objects << object
47
53
  end
48
-
49
- def update_tick
50
- @tick = Gosu::milliseconds - @last_tick
51
- @last_tick = Gosu::milliseconds
52
- @tick
53
- end
54
-
54
+ def remove_game_object(object)
55
+ @input_clients.delete(object)
56
+ end
57
+
58
+ #
59
+ # Frames per second
60
+ #
55
61
  def fps
56
62
  @fps_counter.fps
57
63
  end
58
-
59
- #
60
- # By default button_up sends the keyevent to the GameStateManager
61
- # .. Which then is responsible to send it to the right GameState(s)
62
- #
63
- def button_up(id)
64
- @game_state_manager.button_up(id)
65
- end
66
-
64
+ alias :framerate :fps
67
65
 
68
66
  #
69
- # By default button_down sends the keyevent to the GameStateManager
70
- # .. Which then is responsible to send it to the right GameState(s)
67
+ # Total amount of game iterations (ticks)
71
68
  #
72
- def button_down(id)
73
- @game_state_manager.button_down(id)
74
- end
69
+ def ticks
70
+ @fps_counter.ticks
71
+ end
75
72
 
76
-
77
73
  #
78
- # Standard GOSU main class update
74
+ # Chingus core-logic / loop. Gosu will call this each game-iteration.
79
75
  #
80
76
  def update
81
- @fps_counter.register_tick
82
- update_tick
83
-
84
77
  #
85
- # Process inputs for:
86
- # - Our main game window (self)
87
- # - .. and all gameobjects connected to it
88
- # - the active gamestate
89
- # - ... and all GameObjects connected to it
78
+ # Register a tick with our rather standard tick/framerate counter.
79
+ # Returns the amount of milliseconds since last tick. This number is used in all update()-calls.
80
+ # Without this self.fps would return an incorrect value.
81
+ # If you override this in your Chingu::Window class, make sure to call super.
90
82
  #
91
- [self, @game_state_manager.state].each do |object|
92
- next if object.nil?
93
-
94
- dispatch_input_for(object)
95
-
96
- object.game_objects.each do |game_object|
97
- dispatch_input_for(game_object)
98
- end
99
- end
100
-
101
- @game_objects.each { |object| object.update }
102
- @game_state_manager.update
83
+ @milliseconds_since_last_tick = @fps_counter.register_tick
84
+
85
+ #
86
+ # Dispatch inputmap for main window
87
+ #
88
+ dispatch_input_for(self)
89
+
90
+ #
91
+ # Dispatch input for all input-clients handled by to main window (game objects with input created in main win)
92
+ #
93
+ @input_clients.each { |game_object| dispatch_input_for(game_object) }
94
+
95
+
96
+ #
97
+ # Call update(milliseconds_since_last_tick) on all game objects belonging to the main window.
98
+ #
99
+ update_game_objects
100
+
101
+ #
102
+ # Call update(milliseconds_since_last_tick) on all game objects belonging to the current game state.
103
+ #
104
+ update_game_state_manager
103
105
  end
104
106
 
107
+ #
108
+ # Chingus main screen manupulation method.
109
+ # If you override this in your Chingu::Window class, make sure to call super.
110
+ # Gosu will call this each game-iteration just after #update
111
+ #
105
112
  def draw
113
+ #
114
+ # Draw all game objects associated with the main window.
115
+ #
106
116
  @game_objects.each { |object| object.draw }
117
+
118
+ #
119
+ # Let the game state manager call draw on the active game state (if any)
120
+ #
107
121
  @game_state_manager.draw
108
122
  end
109
-
110
- private
111
123
 
112
124
  #
113
- # Dispatches a input for any given object
125
+ # Call update() on all game objects in main game window.
114
126
  #
115
- def dispatch_input_for(object)
116
- return if object.nil? || object.input.nil?
117
-
118
- object.input.each do |symbol, action|
119
- if button_down?(Input::SYMBOL_TO_CONSTANT[symbol])
120
- #puts "#{object.to_s} :#{symbol.to_s} => #{action.to_s}"
121
- #puts "[#{action.class.to_s} - #{action.class.superclass.to_s}]"
122
- if action.is_a? Symbol
123
- object.send(action)
124
- elsif action.is_a? Proc
125
- action.call
126
- elsif action.is_a? Chingu::GameState
127
- push_gamestate(action)
128
- elsif action.superclass == Chingu::GameState
129
- push_gamestate(action)
130
- end
131
- end
132
- end
127
+ def update_game_objects
128
+ @game_objects.each { |object| object.update(@milliseconds_since_last_tick) }
133
129
  end
134
130
 
135
- end
136
- end
137
-
138
-
131
+ #
132
+ # Call update() on our game_state_manger
133
+ # -> call update on active state
134
+ # -> call update on all game objects in that state
135
+ #
136
+ def update_game_state_manager
137
+ @game_state_manager.update(@milliseconds_since_last_tick)
138
+ end
139
139
 
140
140
 
141
- # def button_down(id)
142
- # key_recievers.each do |key_reciever|
143
- # key_reciever.keymap.each do |key, action|
144
- # key_reciever.send(:before_keymap_dispatch) if key_reciever.respond_to? (:before_keymap_dispatch)
145
- # if Keymap::constant_to_symbol[id] == key
146
- # puts "#{key.to_s} => #{action.to_s}"
147
- # key_reciever.send(action)
148
- # end
149
- # end
150
- # end
151
- # end
152
-
153
- #def button_up(id)
154
- # key_recievers.each do |key_reciever|
155
- # key_reciever.release_keymap.each do |key, action|
156
- # if Keymap::Keys[id] == key
157
- # key_reciever.send(action)
158
- # end
159
- # end
160
- # end
161
- #end
141
+ #
142
+ # By default button_up sends the keyevent to the GameStateManager
143
+ # .. Which then is responsible to send it to the right GameState(s)
144
+ #
145
+ def button_up(id)
146
+ dispatch_button_up(id, self)
147
+ @input_clients.each { |object| dispatch_button_up(id, object) }
148
+ @game_state_manager.button_up(id)
149
+ end
150
+
151
+ #
152
+ # By default button_down sends the keyevent to the GameStateManager
153
+ # .. Which then is responsible to send it to the right GameState(s)
154
+ #
155
+ def button_down(id)
156
+ dispatch_button_down(id, self)
157
+ @input_clients.each { |object| dispatch_button_down(id, object) }
158
+ @game_state_manager.button_down(id)
159
+ end
160
+ end
161
+ end