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.
- data/lib/shattered_controller.rb +3 -0
- data/lib/shattered_controller/actor/actor.rb +107 -0
- data/lib/shattered_controller/base.rb +108 -0
- data/lib/shattered_controller/keyboard_input/key_converter.rb +43 -0
- data/lib/shattered_controller/keyboard_input/keyboard_input.rb +106 -0
- data/lib/shattered_controller/mock_camera.rb +9 -0
- data/lib/shattered_controller/runner.rb +33 -0
- data/lib/shattered_controller/state.rb +59 -0
- data/lib/shattered_model.rb +3 -0
- data/lib/shattered_model/base.rb +24 -0
- data/lib/shattered_model/fuzzy_logic.rb +188 -0
- data/lib/shattered_model/linear_interpolator.rb +29 -0
- data/lib/shattered_pack.rb +11 -0
- data/lib/shattered_pack/base.rb +127 -0
- data/lib/shattered_pack/pre_initialize/pre_initialize.rb +105 -0
- data/lib/shattered_pack/runner.rb +11 -0
- data/lib/shattered_pack/timer/timed_event.rb +77 -0
- data/lib/shattered_pack/timer/timer.rb +75 -0
- data/lib/shattered_view.rb +7 -0
- data/lib/shattered_view/base.rb +151 -0
- data/lib/shattered_view/camera.rb +7 -0
- data/lib/shattered_view/extensions.rb +10 -0
- data/lib/shattered_view/light.rb +28 -0
- data/lib/shattered_view/mesh/animation.rb +20 -0
- data/lib/shattered_view/mesh/mesh.rb +135 -0
- data/lib/shattered_view/node.rb +113 -0
- data/lib/shattered_view/resources.rb +47 -0
- data/lib/shattered_view/rmaterial.rb +43 -0
- data/lib/shattered_view/runner.rb +48 -0
- data/lib/shattered_view/utilities.rb +7 -0
- data/lib/shattered_view/vector.rb +242 -0
- metadata +75 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module ShatteredModel #:nodoc:
|
2
|
+
def self.append_features(base)
|
3
|
+
super
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
module ClassMethods #:nodoc:
|
7
|
+
def fuzzy_logic( file )
|
8
|
+
before_init_set("self", {:fuzzy_logic => [file]})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
# Models encompass all of the game play logic and the game specific data.
|
12
|
+
#
|
13
|
+
# Models are useful for unit tests and for making game rules.
|
14
|
+
# They are where all logic (AI/collisions/etc) go.
|
15
|
+
class Base < ShatteredPack::Base
|
16
|
+
def fuzzy_logic=(file) #:nodoc:
|
17
|
+
@fuzzy_logic = FuzzyLogic.new
|
18
|
+
@fuzzy_logic.parse_fuzzy_file(File.dirname(__FILE__)+"/#{file}")
|
19
|
+
end
|
20
|
+
def update_fuzzy_logic #:nodoc:
|
21
|
+
@fuzzy_logic.update(self)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
# This provides basic fuzzy logic. That is, it will tell you what properties any given
|
3
|
+
# attribute contains based upon previous definition.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# player_logic.add_classification( :hp, :dieing, :hp, :max => 5, :min => 45 )
|
7
|
+
# player_logic.add_classification( :hp, :healthy, :max => 80, :min => 65 )
|
8
|
+
# player_logic.hp(15) => [[:dieing, 0.75], [:healthy, 0.0]]
|
9
|
+
class FuzzyLogic #:nodoc:
|
10
|
+
attr_reader :consequences
|
11
|
+
def initialize
|
12
|
+
@attributes = {}
|
13
|
+
@consequences = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_consequence( consequence, classifications={} )
|
17
|
+
@consequences[classifications] ||= []
|
18
|
+
@consequences[classifications] += [consequence]
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_classification( attribute, classification, options = {} )
|
22
|
+
define_attribute(attribute) if(@attributes[attribute].nil?)
|
23
|
+
define_interpolator(attribute,classification,options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def consequences_of(options={})
|
27
|
+
# transform the passed in attributes to probability sets
|
28
|
+
valid_sequences = []
|
29
|
+
options.each_pair do |attribute, value|
|
30
|
+
valid_sequences << {attribute => classifications_of(attribute,value)}
|
31
|
+
end
|
32
|
+
# combine the probability sets
|
33
|
+
valid_combinations = compile_consequence_combinations(valid_sequences)
|
34
|
+
# realize the consequences of the combined probability sets
|
35
|
+
return consequences_of_combinations(valid_combinations)
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_fuzzy_file( file_name )
|
39
|
+
file_name += ".yaml" if not(file_name =~ /\.yaml$/)
|
40
|
+
raise ArgumentError, "Cannot find #{file_name}" if not(File.file? file_name)
|
41
|
+
begin
|
42
|
+
fuzzy_file = YAML::load(File.open(file_name))
|
43
|
+
rescue ArgumentError => bang
|
44
|
+
raise ArgumentError, "Error when loading #{file_name} : #{bang.message}"
|
45
|
+
end
|
46
|
+
parse_attributes(fuzzy_file)
|
47
|
+
add_consequences_from_parsed(parse_consequences(fuzzy_file['consequences']))
|
48
|
+
end
|
49
|
+
|
50
|
+
# The algorithm always performs an action on update.
|
51
|
+
def update(object)
|
52
|
+
status = {}
|
53
|
+
@attributes.each_pair do |attribute, classifications|
|
54
|
+
status.merge!({attribute => object.send(attribute)})
|
55
|
+
end
|
56
|
+
consequences = consequences_of(status)
|
57
|
+
total_probability = 0
|
58
|
+
consequences.each_pair do |actions, probability|
|
59
|
+
total_probability+=probability
|
60
|
+
end
|
61
|
+
action_at_probability = rand*total_probability
|
62
|
+
total_probability = 0
|
63
|
+
consequences.each_pair do |actions, probability|
|
64
|
+
total_probability += probability
|
65
|
+
if( action_at_probability < total_probability )
|
66
|
+
actions.each do |action|
|
67
|
+
object.send(action)
|
68
|
+
end
|
69
|
+
break
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# This function recursively moves through each valid combination of probabilities.
|
77
|
+
# It sums the probabilities by multiplication.
|
78
|
+
def compile_consequence_combinations(valid_sequences, compiled={}, probability=1.0)
|
79
|
+
retv = []
|
80
|
+
attribute = valid_sequences[0].keys[0]
|
81
|
+
classifications = valid_sequences[0][attribute]
|
82
|
+
classifications.each do |classification, chance|
|
83
|
+
next if chance == 0
|
84
|
+
nprobability=probability*chance
|
85
|
+
compiled[attribute]=classification
|
86
|
+
if(valid_sequences.length == 1)
|
87
|
+
retv << [compiled.dup, nprobability]
|
88
|
+
else
|
89
|
+
retv += compile_consequence_combinations(valid_sequences[1..-1],compiled,nprobability)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return retv
|
93
|
+
end
|
94
|
+
|
95
|
+
def consequences_of_combinations(combinations)
|
96
|
+
consequences = {}
|
97
|
+
combinations.each do |combination, probability|
|
98
|
+
# where a = { {1 => 2}, 3 } and b = {1,2}, a[b] != 3
|
99
|
+
# in order to circumvent this (error?) we use the fact that
|
100
|
+
# a.keys[0] == b
|
101
|
+
@consequences.keys.each do |index|
|
102
|
+
next if index != combination
|
103
|
+
consequences[@consequences[index]] ||= 0
|
104
|
+
consequences[@consequences[index]]+=probability
|
105
|
+
end
|
106
|
+
end
|
107
|
+
return consequences
|
108
|
+
end
|
109
|
+
|
110
|
+
def parse_attributes(yaml)
|
111
|
+
yaml['attributes'].each_pair do |attribute, classifications|
|
112
|
+
classifications.each_pair do |classification, options|
|
113
|
+
options.keys.each do |key|
|
114
|
+
options[key.to_sym]=options.delete key
|
115
|
+
end
|
116
|
+
add_classification( attribute.to_sym, classification.to_sym, options )
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_consequences(consequences,result={})
|
122
|
+
compilation = []
|
123
|
+
consequences.each_pair do |consequence, action|
|
124
|
+
nresult = result.dup
|
125
|
+
nresult.merge!(attribute_for(consequence.to_sym) => consequence.to_sym)
|
126
|
+
if action.is_a? String
|
127
|
+
compilation += [nresult => action.to_sym]
|
128
|
+
else
|
129
|
+
compilation+=parse_consequences(action,nresult)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return compilation
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_consequences_from_parsed(compilation)
|
136
|
+
compilation.each do |item|
|
137
|
+
item.each_pair do |index, action|
|
138
|
+
add_consequence(action, index)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def attribute_for(unknown_classification)
|
144
|
+
@attributes.each_pair do |attribute, classifications|
|
145
|
+
classifications.keys.each do |classification|
|
146
|
+
return attribute if classification == unknown_classification
|
147
|
+
end
|
148
|
+
end
|
149
|
+
return nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def define_attribute(attribute)
|
153
|
+
@attributes[attribute] = {}
|
154
|
+
eval <<-EOF
|
155
|
+
class << self
|
156
|
+
define_method(:#{attribute}) do |at|
|
157
|
+
classifications_of(:#{attribute},at)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
EOF
|
161
|
+
end
|
162
|
+
|
163
|
+
def define_interpolator( attribute, classification,options )
|
164
|
+
interpolator = LinearInterpolator.new
|
165
|
+
@attributes[attribute][classification] = interpolator
|
166
|
+
|
167
|
+
add_point(interpolator, options[:max],1)
|
168
|
+
add_point(interpolator, options[:min],0)
|
169
|
+
end
|
170
|
+
|
171
|
+
def classifications_of( attribute, at )
|
172
|
+
retv = []
|
173
|
+
@attributes[attribute].each_pair do |classification,interpolator|
|
174
|
+
retv << [ classification, interpolator.value_at(at) ]
|
175
|
+
end
|
176
|
+
return retv.sort_by { |classification| (1.0-classification[1]) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def add_point( interpolator, points, value )
|
180
|
+
if(points.is_a? Array)
|
181
|
+
points.each do |x|
|
182
|
+
interpolator.add_point(x,value)
|
183
|
+
end
|
184
|
+
else
|
185
|
+
interpolator.add_point(points,value)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# This provides basic linear interpolation between any number of arbitrarily defined points.
|
2
|
+
# It is used for fuzzy logic. Although, fuzzy logic based on a statistically normal curve
|
3
|
+
# provides best results, linear interpolation provides a "gud 'nuf" approach.
|
4
|
+
class LinearInterpolator #:nodoc:
|
5
|
+
def add_point(x,y)
|
6
|
+
@points ||= []
|
7
|
+
@points << [x,y]
|
8
|
+
@points = @points.sort_by { |x| x[0] }
|
9
|
+
end
|
10
|
+
def value_at(x)
|
11
|
+
return 0 if @points.nil?
|
12
|
+
return @points[0][1] if x < @points[0][0]
|
13
|
+
return @points[-1][1] if x > @points[-1][0]
|
14
|
+
|
15
|
+
after,before=nil,nil
|
16
|
+
@points.each_with_index do |point,i|
|
17
|
+
if(point[0] >= x)
|
18
|
+
after = point
|
19
|
+
before = @points[i-1]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
m=(before[1]-after[1])/(before[0]-after[0]).to_f
|
24
|
+
x-=before[0]
|
25
|
+
b=before[1]
|
26
|
+
|
27
|
+
return m*x+b
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
%w( base runner ).each do |component|
|
4
|
+
require "shattered_pack/#{component}"
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'shattered_support'
|
8
|
+
require 'shattered_ogre'
|
9
|
+
require 'shattered_model'
|
10
|
+
require 'shattered_view'
|
11
|
+
require 'shattered_controller'
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
require 'timer/timer'
|
6
|
+
require 'pre_initialize/pre_initialize'
|
7
|
+
|
8
|
+
module ShatteredPack #:nodoc:
|
9
|
+
|
10
|
+
class Base
|
11
|
+
attr_accessor :time_elapsed
|
12
|
+
alias_method :per_second, :time_elapsed
|
13
|
+
|
14
|
+
# This is overwritten to allow for a pre_initialize before initialize
|
15
|
+
def self.new(*options) #:nodoc
|
16
|
+
new_base = allocate
|
17
|
+
new_base.pre_initialize
|
18
|
+
new_base.send(:initialize, *options)
|
19
|
+
return new_base
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retrieve the current state
|
23
|
+
def state
|
24
|
+
Configuration.environment[:state]
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO - is this called anymore?
|
28
|
+
def update_event(time_elapsed) #:nodoc:
|
29
|
+
actors.each do |actor|
|
30
|
+
actor.update_actors(time_elapsed)
|
31
|
+
actor.update(time_elapsed)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# remove_from_scene? should just be defined in model.
|
36
|
+
# Refactor? TODO
|
37
|
+
def remove_from_scene? #:nodoc:
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO outdated?
|
42
|
+
def update_actors(time_elapsed) #:nodoc:
|
43
|
+
update_event time_elapsed
|
44
|
+
remove_dead_actors
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO outdated?
|
48
|
+
def remove_dead_actors #:nodoc:
|
49
|
+
actors.each do |actor|
|
50
|
+
remove_from_scene actor if actor.remove_from_scene?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# TODO outdated?
|
55
|
+
def remove_from_scene(actor)
|
56
|
+
actors.delete actor
|
57
|
+
actor.unload!
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
public
|
62
|
+
|
63
|
+
# attr helpers. These are instance level attr_* functions.
|
64
|
+
def attr_reader(*args)
|
65
|
+
name, value = args
|
66
|
+
attr_define(:reader, name, value)
|
67
|
+
end
|
68
|
+
|
69
|
+
# attr helpers. These are instance level attr_* functions.
|
70
|
+
# attr_writer accepts a value as the second argument
|
71
|
+
def attr_writer(*args)
|
72
|
+
name, value = args
|
73
|
+
attr_define(:writer, name, value)
|
74
|
+
end
|
75
|
+
|
76
|
+
# attr helpers. These are instance level attr_* functions.
|
77
|
+
# attr_accessor accepts a value as the second argument
|
78
|
+
def attr_accessor(*args)
|
79
|
+
name, value = args
|
80
|
+
attr_define(:accessor, name, value)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Define a block to execute when an object is unloaded.
|
84
|
+
def when_unloaded(&block)
|
85
|
+
@unloaded_events ||= []
|
86
|
+
@unloaded_events << block
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def attr_define(accessor_level, name, value)
|
92
|
+
self.class.send("attr_#{accessor_level}".to_sym, "#{name}".to_sym)
|
93
|
+
instance_variable_set("@#{name}".to_sym, value) if !value.nil?
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def disabled?
|
99
|
+
@unloaded_events == nil
|
100
|
+
end
|
101
|
+
|
102
|
+
def unload!
|
103
|
+
return if disabled?
|
104
|
+
@unloaded_events.each do |event|
|
105
|
+
event.call
|
106
|
+
end
|
107
|
+
@unloaded_events = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
class Error < StandardError # :nodoc:
|
113
|
+
end
|
114
|
+
|
115
|
+
class RetossError < StandardError # :nodoc:
|
116
|
+
attr_accessor :message
|
117
|
+
def initialize(error, message)
|
118
|
+
self.message = message
|
119
|
+
set_backtrace error.backtrace
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
ShatteredPack::Base.class_eval do
|
125
|
+
include ShatteredPack::Timer
|
126
|
+
include ShatteredPack::PreInitialize
|
127
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module ShatteredPack
|
2
|
+
module PreInitialize #:nodoc:all
|
3
|
+
def self.append_features(base)
|
4
|
+
super
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
BEFORE_INIT_CALL_VALUES = :before_init_call_values
|
10
|
+
BEFORE_INIT_SET_VALUES = :before_init_set_values
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# In the pre_initialization phase (see #before_init_call), send an object a bunch of sets.
|
15
|
+
#
|
16
|
+
# This is used in actor, camera, mesh, and virtually everywhere to support the format of:
|
17
|
+
# mesh "ruby", :position => v(1,0,0)
|
18
|
+
#
|
19
|
+
# becomes
|
20
|
+
# before_init_set( :ruby, {:position => v(1,0,0) } )
|
21
|
+
# becomes
|
22
|
+
# ruby.position=v(1,0,0)
|
23
|
+
# when the obect is initialized.
|
24
|
+
def before_init_set(variable, options={})
|
25
|
+
self.write_inheritable_array(BEFORE_INIT_SET_VALUES, [[ variable, options ]] )
|
26
|
+
end
|
27
|
+
|
28
|
+
# All shattered objects have a pre_initialization phase. Use this to specify that
|
29
|
+
# a function should be called when the object is created.
|
30
|
+
#
|
31
|
+
# _Example_:
|
32
|
+
# class BulletModel < ShatteredModel::Base
|
33
|
+
# before_init_call(:calculate_trajectory, v(0,0,0), v(0,0,1))
|
34
|
+
# def calculate_trajectory
|
35
|
+
# #called before initialize is
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
def before_init_call(function, *arguments)
|
40
|
+
self.write_inheritable_array(BEFORE_INIT_CALL_VALUES, [[function, [*arguments]]])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
module InstanceMethods
|
44
|
+
# This function is called after an object is allocated, but before it's initialized.
|
45
|
+
#
|
46
|
+
# See ShatteredPack::ClassMethods#before_init_call for usage.
|
47
|
+
def pre_initialize #:nodoc:
|
48
|
+
pre_initialize_set
|
49
|
+
pre_initialize_call
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used in pre_initialize
|
53
|
+
def each_init_value(name) #:nodoc:
|
54
|
+
startup_attributes = self.class.read_inheritable_attribute( name ) || []
|
55
|
+
startup_attributes.each do |actor, options|
|
56
|
+
next if options.nil?
|
57
|
+
yield actor, options
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Used in pre_initialize for before_init_set
|
62
|
+
def pre_initialize_set #:nodoc:
|
63
|
+
each_init_value(BEFORE_INIT_SET_VALUES) do |variable, options|
|
64
|
+
call_object_function_for_each_key( variable, options )
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Used in pre_initialize for before_init_call
|
69
|
+
def pre_initialize_call #:nodoc:
|
70
|
+
each_init_value(BEFORE_INIT_CALL_VALUES) do |function, args|
|
71
|
+
call_object_function( :self, function, args )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def call_object_function_for_each_key( actor, options )
|
76
|
+
options.each_pair do |action, params|
|
77
|
+
call_object_function actor, "#{action}=", params
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def call_object_function(actor, action, params)
|
82
|
+
begin
|
83
|
+
if params.is_a? Symbol #|| (params.length == 1 && params[0].is_a?(Symbol)) this will allow substitution in before_init_call. This may not be intended behavior.
|
84
|
+
params = eval(params.to_s)
|
85
|
+
end
|
86
|
+
rescue NameError
|
87
|
+
puts "It is not advisable to pass #{params.inspect} to #{action.inspect} for #{actor.inspect}."
|
88
|
+
puts " It will try to be evaluated. Use a string instead."
|
89
|
+
end
|
90
|
+
begin
|
91
|
+
sendee = eval(actor.to_s)
|
92
|
+
if(params.is_a? Array)
|
93
|
+
sendee.send( action.to_sym, *params )
|
94
|
+
else
|
95
|
+
sendee.send( action.to_sym, params )
|
96
|
+
end
|
97
|
+
rescue NoMethodError, ArgumentError => bang
|
98
|
+
message="Error upon #{actor.to_s}.send(#{action.to_sym.inspect}, #{params.inspect}):\n\r #{bang.class}: \n\r\t #{bang.message}\n\r #{bang.backtrace[0]}\n\r"
|
99
|
+
bang = ShatteredPack::RetossError.new(bang,message)
|
100
|
+
raise bang
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|