shattered_pack 0.3.3

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.
@@ -0,0 +1,3 @@
1
+ %w(runner base state mock_camera).each do |dependency|
2
+ require "shattered_controller/#{dependency}"
3
+ end
@@ -0,0 +1,107 @@
1
+ module ShatteredController
2
+ module Actor #:nodoc:
3
+ def self.append_features(base)
4
+ super
5
+ base.extend(ClassMethods)
6
+ base.send(:include, InstanceMethods)
7
+ end
8
+ module ClassMethods
9
+
10
+ # An actor is the MVC encapsulated into one object.
11
+ #
12
+ # Here is how it works: You specify a actor in a game state with some starting
13
+ # attributes:
14
+ # class BattleState
15
+ # actor :player
16
+ # actor :bullet, :target => :player
17
+ # end
18
+ #
19
+ # With a model BulletModel defined as:
20
+ # class BulletModel
21
+ # attr_writer :target
22
+ # end
23
+ #
24
+ # And our BulletModel will be created, with the target set to the player object.
25
+ #
26
+ # Setting values for the actors is done prior to initialization, so:
27
+ #
28
+ # class BulletModel
29
+ # attr_writer :target
30
+ # def initialize
31
+ # target.run_away_from(self)
32
+ # end
33
+ # end
34
+ #
35
+ # Works fine.
36
+ #
37
+ # Actors delegate to ShatteredController::Base objects, and controllers delegate to Model and View.
38
+ #
39
+ # See ShatteredController::Base for more detail.
40
+ def actor( name, options = {} )
41
+ before_init_call( :actor, name, options )
42
+ end
43
+
44
+ end
45
+ module InstanceMethods
46
+
47
+ # This returns all of the actors created by this controller.
48
+ def actors
49
+ return @actors || []
50
+ end
51
+
52
+ # Same as ShatteredController::Actor::ClassMethods#actor
53
+ #
54
+ # Returns a newly created actor
55
+ def actor(name=nil, options={})
56
+ raise Error, "actor needs a name" if name.nil?
57
+ actor = Actor.new(name,load_actor_classes(name))
58
+
59
+ attr_reader(name.to_sym, actor)
60
+ call_object_function_for_each_key( name.to_sym, options )
61
+
62
+ @actors ||= []
63
+ @actors << actor
64
+ return actor
65
+ end
66
+
67
+ private
68
+
69
+ def load_actor_classes(name)
70
+ actor_options={}
71
+ ['model', 'view', 'controller'].each do |type|
72
+ evaled_class = nil
73
+ begin
74
+ evaled_class = eval("#{name.to_s.camelize}#{type.camelize}")
75
+ rescue NameError
76
+ next
77
+ end
78
+ actor_options[type.to_sym] = evaled_class.new
79
+ end
80
+ raise Error, "No Model, View, or Controller defined for Actor #{name}." if actor_options.empty?
81
+ return actor_options
82
+ end
83
+
84
+ end
85
+
86
+ # An actor is a delegator to a controller game object.
87
+ class Actor #:nodoc:all
88
+ attr_reader :controller
89
+ def initialize( name, options = {} )
90
+ @actor_name = name
91
+ @controller = (options[:controller] || ShatteredController::Base.new)
92
+ @controller.model = options[:model]
93
+ @controller.view = options[:view]
94
+ @controller.view.model = options[:model] unless @controller.view.nil?
95
+
96
+ if options[:view].nil? && options[:model].nil? && @controller.nil? && !options[:testing]
97
+ raise NameError, "No model view or controller found for actor #{name}"
98
+ end
99
+ end
100
+
101
+ # Delegate to controller
102
+ def method_missing(name, *args, &block)
103
+ @controller.send(name, *args, &block)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,108 @@
1
+
2
+ $:.unshift(File.dirname(__FILE__)) unless
3
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
+
5
+ require 'keyboard_input/keyboard_input'
6
+ require 'actor/actor'
7
+
8
+ module ShatteredController #:nodoc:
9
+
10
+ # Controllers tie together the Model and View. They are the only objects that know
11
+ # about both the model and the view, and as such have the responsibility of events.
12
+ #
13
+ # By default, when an unknown method is invoked on a controller object, it is delegated
14
+ # to the model and the view. IE:
15
+ #
16
+ # class BulletController < ...
17
+ # def known_method
18
+ # puts "This will not be delegated to the " + model.inspect + " or the " + view.inspect
19
+ # end
20
+ # end
21
+ #
22
+ # class BattleState < ...
23
+ # actor :bullet
24
+ # def initialize
25
+ # bullet.known_method # This is not delegated
26
+ # bullet.unknown_method # This is delegated (and will throw an exception if unknown among the view/model)
27
+ # end
28
+ # end
29
+ #
30
+ # When obtaining a result from a delegated method, the result will be the a response in the following order
31
+ # - Model (first)
32
+ # - View (last)
33
+ #
34
+ # They are sorted so that you get the most useful response first.
35
+ class Base < ShatteredPack::Base
36
+ attr_accessor :model, :view
37
+
38
+ before_init_call :disable_event
39
+
40
+ def quit
41
+ end
42
+
43
+ # Controller delegates all unknown methods to the view and model.
44
+ def method_missing(method_name, *args, &block)
45
+ return if disabled?
46
+ raise_no_method_error = true
47
+ result = nil
48
+ [view, model].each do |delegate|
49
+ if delegate.class.method_defined?(method_name)
50
+ result = delegate.send(method_name, *args, &block)
51
+ raise_no_method_error = false
52
+ end
53
+ end
54
+
55
+ raise NoMethodError, "Model, View, and Controller have no method defined named '#{method_name}'" if raise_no_method_error
56
+ return result
57
+ end
58
+
59
+
60
+ def update_timer(time_elapsed)#:nodoc:
61
+ return if @timer == :unloaded
62
+ [self, model, view].each do |base|
63
+ next if base.nil?
64
+ base.time_elapsed = time_elapsed
65
+ base.timer.update(time_elapsed)
66
+ end
67
+ end
68
+
69
+ # This is called to the State every frame by the main game loop. All update events are now
70
+ # run through Timers.
71
+ def update_timers(time_elapsed) #:nodoc:
72
+ update_timer(time_elapsed)
73
+ actors.each { |actor| actor.update_timers(time_elapsed) }
74
+ end
75
+
76
+ # Unloading a controller will completely unload an object from the scene.
77
+ def unload!
78
+ super
79
+ end
80
+
81
+ # An object is disabled when it has been unloaded.
82
+ def disabled?
83
+ super
84
+ end
85
+
86
+ private
87
+
88
+ def disable_event
89
+ when_unloaded do
90
+ @disabled = true
91
+ @timer=:unloaded
92
+ @model.send(:unload!) if @model
93
+ @model = nil
94
+ @view.send(:unload!) if @view
95
+ @view = nil
96
+ end
97
+ end
98
+ end
99
+
100
+ class Error < StandardError #:nodoc:
101
+ end
102
+ end
103
+
104
+ ShatteredController::Base.class_eval do
105
+ include ShatteredController::KeyboardInput
106
+ include ShatteredController::Actor
107
+ end
108
+
@@ -0,0 +1,43 @@
1
+ module ShatteredController
2
+ # This is a wrapper around Ogre's input listener.
3
+ # It allows us to query the status of each device every frame.
4
+ class KeyConverter #:nodoc:
5
+
6
+ def initialize( ogre_input )
7
+ @ogre_input = ogre_input
8
+ end
9
+ def key_event?( type, key_symbol )
10
+ return @ogre_input.send(:"isKey#{type.to_s.capitalize}", self.class.convert(key_symbol))
11
+ end
12
+ def flush
13
+ @ogre_input.flush
14
+ end
15
+
16
+ def self.back_conversions
17
+ @@back_conversions ||= conversions.invert
18
+ end
19
+ def self.conversions
20
+ begin
21
+ return @@conversions
22
+ rescue NameError
23
+ end
24
+ @@conversions = {}
25
+ ShatteredOgre.constants.each do |constant|
26
+ kc_evaled = eval("ShatteredOgre::#{constant}")
27
+ @@conversions[kc_evaled] = symbolize(constant[3..-1].downcase) if constant.to_s =~ /^KC_/
28
+ end
29
+ return @@conversions
30
+ end
31
+ def self.convert( kc_character )
32
+ return self.conversions[kc_character] if kc_character.is_a? Fixnum
33
+ return self.back_conversions[kc_character]
34
+ end
35
+ private
36
+ def self.symbolize( name )
37
+ exceptions = { 'pgdown' => :page_down,
38
+ 'pgup' => :page_up }
39
+ return name.to_sym if exceptions[name] == nil
40
+ return exceptions[name]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,106 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'key_converter'
5
+
6
+ module ShatteredController
7
+ module KeyboardInput #:nodoc:
8
+ def self.append_features(base)
9
+ super
10
+ base.extend(ClassMethods)
11
+ base.send(:include, InstanceMethods)
12
+ end
13
+ module ClassMethods
14
+ # Key pressing can take any of the following forms:
15
+ # key :released => :up, :action => :clicked_up
16
+ # key :pressed => :left, :action => :left_pressed
17
+ # key :held => :down, :action => :move_down_fluidly
18
+ # where each of the actions correspond to an actor function:
19
+ #
20
+ # class ExampleView < ShatteredView::Base
21
+ # def clicked_up
22
+ # #called when up is released
23
+ # end
24
+ # def left_pressed
25
+ # #called when left is released
26
+ # end
27
+ # def move_down_fluidly(time_elapsed)
28
+ # #called when down is held
29
+ # end
30
+ # end
31
+ #
32
+ # Only key :pressed recieves the time_elapsed variable (as it is updated per frame)
33
+ def key(options = {})
34
+ before_init_call(:key, options)
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ def pre_initialize
40
+ # update our keyboard input
41
+ timer.every(:frame) do |time_elapsed|
42
+ update_input(time_elapsed, ShatteredController::Runner.environment[:input])
43
+ end
44
+ super
45
+ end
46
+
47
+ def reaction_types
48
+ @reaction_types ||= [:pressed, :released, :held]
49
+ end
50
+ # Given an input of:
51
+ # key :held=>:up, :action => :key_action
52
+ # This will return [:held, [:up]]
53
+ def reaction_from(options) #:nodoc:
54
+ # Define the method to send the actions when the keys are pressed/clicked/released.
55
+ reaction_types.each do |reaction_type|
56
+ if options.has_key?(reaction_type)
57
+ return [reaction_type, options[reaction_type]] if options[reaction_type].is_a? Array
58
+ return [reaction_type, [options[reaction_type]]]
59
+ end
60
+ end
61
+ raise Error, "Reaction to key input must be #{reaction_types.join(',')}. #{options.inspect}" if options[:reaction].nil?
62
+ end
63
+
64
+ # Given an input of:
65
+ # 1) key :pressed => :up, :action => {:jump => :up}
66
+ # 2) key :pressed => :down, :action => :fall
67
+ # returns
68
+ # 1) [:jump, [:up]]
69
+ # 2) [:fall, []]
70
+ def action_from(params)
71
+ action = params[:action]
72
+ return [action, []] unless action.is_a? Hash
73
+ return [action.keys[0], action.values[0]]
74
+ end
75
+ # See KeyboardInput::key
76
+ def key(actions={})
77
+ reaction, keys = reaction_from(actions)
78
+ action, params = action_from(actions)
79
+ keys.each { |key| key_actions << [reaction, key.to_sym, action, params ] }
80
+ end
81
+ # Update_key will send each of the events to the actor object for whatever key options
82
+ # are defined.
83
+ def key_action(reaction, action, time_elapsed, params) #:nodoc:
84
+ send(action, *params)
85
+ end
86
+ def key_actions #:nodoc:
87
+ @key_actions ||= []
88
+ end
89
+ # update_input takes the input object, and sends the actor every key event this frame.
90
+ def update_input(time_elapsed, input) #:nodoc:
91
+ key_actions.each do |reaction, key, action, params|
92
+ key_action( reaction, action, time_elapsed, params) if input.key_event?(reaction,key)
93
+ end
94
+ end
95
+
96
+ # Simulate a key press, release or hold event
97
+ def key_event(params = {})
98
+ lookup_reaction, keys = reaction_from(params)
99
+ lookup_key = keys[0]
100
+ key_actions.each do |reaction, key, action, params|
101
+ key_action(reaction, action, 0, params) if key == lookup_key && lookup_reaction == reaction
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module ShatteredController
3
+ class MockCamera #:nodoc:
4
+ attr_reader :position
5
+ def position=(x,y,z)
6
+ @position=[x,y,z]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module ShatteredController
3
+ #The class Runner is present in model, view, and controller, and signifies the
4
+ #main entry point into the program. This is called by script/runner.rb
5
+ class Runner #:nodoc:
6
+ def initialize( options = {} )
7
+ @@environment ||= options
8
+ @@environment[:input] = ShatteredController::KeyConverter.new(@@environment[:input])
9
+ end
10
+ #Every time this exits, a game dies.
11
+ def start_game
12
+ each_frame do |time_elapsed|
13
+ @@environment[:state].update_timers(time_elapsed)
14
+ @@environment[:input].flush
15
+ end
16
+ end
17
+ def each_frame
18
+ yield @@environment[:renderer].timeSinceLastFrame while @@environment[:renderer].nextFrame
19
+ end
20
+ def self.environment
21
+ begin
22
+ return @@environment
23
+ rescue NameError
24
+ return mock_environment
25
+ end
26
+ end
27
+ def self.mock_environment
28
+ retv = {}
29
+ retv[:camera] = MockCamera.new
30
+ return retv
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ module ShatteredController
2
+ def self.append_features(base)
3
+ super
4
+ base.extend(ClassMethods)
5
+ end
6
+ module ClassMethods
7
+ # In order to set the initial position of the camera, and any other attributes you would like,
8
+ # use the following format.
9
+ # class CameraTestState < ShatteredController::Base
10
+ # camera :position => v(10,0,0), :orientation => [0.5, 0.5, 0.5, 0.5]
11
+ # end
12
+ #
13
+ # If you pass a symbol as a value, the symbol will be evaluated.
14
+ #
15
+ # This allows for things such as:
16
+ #
17
+ # class CameraTestState < ShatteredController::Base
18
+ # camera :position => :camera_position
19
+ # def camera_position
20
+ # return [rand*10,0,0]
21
+ # end
22
+ # end
23
+ #
24
+ # The valid options are any public functions within camera that ends in =
25
+ # (such as position=, looking_at=) See ShatteredView::Camera for more.
26
+ def camera(options = {})
27
+ before_init_call( 'camera', options )
28
+ end
29
+
30
+ # This blurs the lines between view and controller.
31
+ # Valid types are [:box]
32
+ def sky(type, options = {})
33
+ before_init_set( "self", { :"sky" => [type, options[:material]] })
34
+ end
35
+ end
36
+ # State is the entry point for your shattered game.
37
+ #
38
+ # States define the actors, communications between the actors, and control the camera.
39
+ # You can choose your starting state in config/environment.rb
40
+ class State < ShatteredController::Base
41
+ before_init_call :activate_state
42
+ def activate_state #:nodoc:
43
+ ShatteredPack::Configuration.environment[:state] = self
44
+ end
45
+
46
+ # Returns the camera used in the state. See ShatteredController::ClassMethods#camera
47
+ def camera(*args)
48
+ if args.length == 0
49
+ return @camera ||= Runner.environment[:camera]
50
+ end
51
+ call_object_function_for_each_key(:camera, args[0])
52
+ end
53
+
54
+ def sky=(type, material) #:nodoc:
55
+ Runner.environment[:scene].send("setSky#{type.to_s.capitalize}",material)
56
+ end
57
+ end
58
+ end
59
+