zyps 0.4.1 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/zyps +32 -80
- data/lib/zyps/actions.rb +74 -2
- data/lib/zyps/environmental_factors.rb +16 -0
- data/lib/zyps.rb +90 -17
- data/test/test_zyps.rb +111 -8
- data/test/zyps/test_actions.rb +111 -46
- data/test/zyps/test_behaviors.rb +0 -0
- data/test/zyps/test_environmental_factors.rb +33 -0
- metadata +3 -134
- data/doc/classes/AccelerateAction.html +0 -182
- data/doc/classes/AccelerateAction.src/M000009.html +0 -19
- data/doc/classes/AccelerateAction.src/M000010.html +0 -18
- data/doc/classes/Action.html +0 -118
- data/doc/classes/AgeCondition.html +0 -181
- data/doc/classes/AgeCondition.src/M000039.html +0 -18
- data/doc/classes/AgeCondition.src/M000040.html +0 -18
- data/doc/classes/ApproachAction.html +0 -189
- data/doc/classes/ApproachAction.src/M000048.html +0 -19
- data/doc/classes/ApproachAction.src/M000049.html +0 -38
- data/doc/classes/Behavior.html +0 -202
- data/doc/classes/Behavior.src/M000001.html +0 -18
- data/doc/classes/Behavior.src/M000002.html +0 -19
- data/doc/classes/BlendAction.html +0 -181
- data/doc/classes/BlendAction.src/M000043.html +0 -18
- data/doc/classes/BlendAction.src/M000044.html +0 -18
- data/doc/classes/Clock.html +0 -165
- data/doc/classes/Clock.src/M000041.html +0 -18
- data/doc/classes/Clock.src/M000042.html +0 -21
- data/doc/classes/CollisionCondition.html +0 -148
- data/doc/classes/CollisionCondition.src/M000053.html +0 -18
- data/doc/classes/Color.html +0 -299
- data/doc/classes/Color.src/M000022.html +0 -18
- data/doc/classes/Color.src/M000023.html +0 -16
- data/doc/classes/Color.src/M000024.html +0 -16
- data/doc/classes/Color.src/M000025.html +0 -16
- data/doc/classes/Color.src/M000026.html +0 -18
- data/doc/classes/Color.src/M000027.html +0 -22
- data/doc/classes/Condition.html +0 -118
- data/doc/classes/Creature.html +0 -191
- data/doc/classes/Creature.src/M000011.html +0 -19
- data/doc/classes/Creature.src/M000012.html +0 -18
- data/doc/classes/DestroyAction.html +0 -181
- data/doc/classes/DestroyAction.src/M000030.html +0 -18
- data/doc/classes/DestroyAction.src/M000031.html +0 -18
- data/doc/classes/EatAction.html +0 -149
- data/doc/classes/EatAction.src/M000045.html +0 -21
- data/doc/classes/Enclosure.html +0 -206
- data/doc/classes/Enclosure.src/M000005.html +0 -18
- data/doc/classes/Enclosure.src/M000006.html +0 -31
- data/doc/classes/Environment.html +0 -203
- data/doc/classes/Environment.src/M000046.html +0 -19
- data/doc/classes/Environment.src/M000047.html +0 -45
- data/doc/classes/EnvironmentalFactor.html +0 -118
- data/doc/classes/FaceAction.html +0 -148
- data/doc/classes/FaceAction.src/M000052.html +0 -18
- data/doc/classes/FleeAction.html +0 -189
- data/doc/classes/FleeAction.src/M000054.html +0 -19
- data/doc/classes/FleeAction.src/M000055.html +0 -38
- data/doc/classes/GameObject.html +0 -282
- data/doc/classes/GameObject.src/M000032.html +0 -19
- data/doc/classes/GameObject.src/M000033.html +0 -16
- data/doc/classes/GameObject.src/M000034.html +0 -19
- data/doc/classes/GameObject.src/M000035.html +0 -16
- data/doc/classes/GameObject.src/M000036.html +0 -16
- data/doc/classes/Location.html +0 -167
- data/doc/classes/Location.src/M000021.html +0 -18
- data/doc/classes/ProximityCondition.html +0 -182
- data/doc/classes/ProximityCondition.src/M000037.html +0 -18
- data/doc/classes/ProximityCondition.src/M000038.html +0 -18
- data/doc/classes/TagAction.html +0 -181
- data/doc/classes/TagAction.src/M000028.html +0 -18
- data/doc/classes/TagAction.src/M000029.html +0 -18
- data/doc/classes/TagCondition.html +0 -181
- data/doc/classes/TagCondition.src/M000007.html +0 -18
- data/doc/classes/TagCondition.src/M000008.html +0 -18
- data/doc/classes/TrailsView.html +0 -209
- data/doc/classes/TrailsView.src/M000003.html +0 -40
- data/doc/classes/TrailsView.src/M000004.html +0 -78
- data/doc/classes/TurnAction.html +0 -182
- data/doc/classes/TurnAction.src/M000050.html +0 -19
- data/doc/classes/TurnAction.src/M000051.html +0 -18
- data/doc/classes/Utility.html +0 -264
- data/doc/classes/Utility.src/M000056.html +0 -26
- data/doc/classes/Utility.src/M000057.html +0 -22
- data/doc/classes/Utility.src/M000058.html +0 -18
- data/doc/classes/Utility.src/M000059.html +0 -21
- data/doc/classes/Utility.src/M000060.html +0 -25
- data/doc/classes/Utility.src/M000061.html +0 -21
- data/doc/classes/Utility.src/M000062.html +0 -21
- data/doc/classes/Vector.html +0 -281
- data/doc/classes/Vector.src/M000013.html +0 -19
- data/doc/classes/Vector.src/M000014.html +0 -16
- data/doc/classes/Vector.src/M000015.html +0 -21
- data/doc/classes/Vector.src/M000016.html +0 -16
- data/doc/classes/Vector.src/M000017.html +0 -18
- data/doc/classes/Vector.src/M000018.html +0 -16
- data/doc/classes/Vector.src/M000019.html +0 -18
- data/doc/classes/Vector.src/M000020.html +0 -25
- data/doc/created.rid +0 -1
- data/doc/files/COPYING_LESSER_txt.html +0 -311
- data/doc/files/COPYING_txt.html +0 -1003
- data/doc/files/README_txt.html +0 -214
- data/doc/files/lib/zyps/actions_rb.html +0 -134
- data/doc/files/lib/zyps/conditions_rb.html +0 -134
- data/doc/files/lib/zyps/environmental_factors_rb.html +0 -134
- data/doc/files/lib/zyps/views/trails_rb.html +0 -134
- data/doc/files/lib/zyps_rb.html +0 -134
- data/doc/fr_class_index.html +0 -53
- data/doc/fr_file_index.html +0 -34
- data/doc/fr_method_index.html +0 -88
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
data/bin/zyps
CHANGED
@@ -37,13 +37,6 @@ end
|
|
37
37
|
|
38
38
|
class Application
|
39
39
|
|
40
|
-
#Tags to randomly choose from.
|
41
|
-
attr_accessor :tag_pool
|
42
|
-
#Colors to randomly choose from.
|
43
|
-
attr_accessor :color_pool
|
44
|
-
#Seconds between creature creation.
|
45
|
-
attr_accessor :birth_rate
|
46
|
-
|
47
40
|
#Create app window, game environment, and view.
|
48
41
|
def initialize(width, height)
|
49
42
|
|
@@ -81,6 +74,9 @@ class Application
|
|
81
74
|
enclosure.top = @height
|
82
75
|
enclosure.right = @width
|
83
76
|
@environment.environmental_factors << enclosure
|
77
|
+
|
78
|
+
#Keep all objects under a certain speed.
|
79
|
+
@environment.environmental_factors << SpeedLimit.new(100)
|
84
80
|
|
85
81
|
#Create thread to update environment.
|
86
82
|
thread = Thread.new do
|
@@ -147,32 +143,24 @@ class Application
|
|
147
143
|
Utility.find_angle(@press_location, @release_location) #Move in direction of drag.
|
148
144
|
),
|
149
145
|
0,
|
150
|
-
1
|
151
|
-
@tags_box.text.split(/,\s*/)
|
146
|
+
1
|
152
147
|
)
|
153
148
|
behavior = Behavior.new
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
behavior.
|
160
|
-
|
161
|
-
behavior.
|
162
|
-
acceleration = @acceleration_slider.value
|
163
|
-
behavior.actions << AccelerateAction.new(acceleration) if acceleration != 0
|
164
|
-
turn = @turn_slider.value
|
165
|
-
behavior.actions << TurnAction.new(turn) if turn != 0
|
166
|
-
approach_angle = @approach_slider.value
|
167
|
-
behavior.actions << ApproachAction.new(Vector.new(1, creature.vector.pitch), approach_angle) if approach_angle > 0
|
168
|
-
flee_angle = @flee_slider.value
|
169
|
-
behavior.actions << FleeAction.new(Vector.new(5, creature.vector.pitch), flee_angle) if flee_angle > 0
|
170
|
-
behavior.actions << EatAction.new(@environment) if @eat_flag.active?
|
171
|
-
apply_tags = @tag_action_box.text
|
172
|
-
apply_tags.split(/,\s*/).each do |tag|
|
173
|
-
behavior.actions << TagAction.new(tag)
|
174
|
-
end
|
149
|
+
behavior.conditions << ProximityCondition.new(200)
|
150
|
+
behavior.actions << AccelerateAction.new(1) if @accelerate_flag.active?
|
151
|
+
behavior.actions << TurnAction.new(90) if @turn_flag.active?
|
152
|
+
behavior.actions << ApproachAction.new(creature.vector, 720) if @approach_flag.active?
|
153
|
+
behavior.actions << FleeAction.new(creature.vector) if @flee_flag.active?
|
154
|
+
behavior.actions << PushAction.new(1000) if @push_flag.active?
|
155
|
+
behavior.actions << PullAction.new(1000) if @pull_flag.active?
|
156
|
+
behavior.actions << BlendAction.new(creature.color) if @paint_flag.active?
|
175
157
|
creature.behaviors << behavior
|
158
|
+
if @eat_flag.active?
|
159
|
+
behavior = Behavior.new
|
160
|
+
behavior.actions << EatAction.new(@environment)
|
161
|
+
behavior.conditions << ProximityCondition.new(10)
|
162
|
+
creature.behaviors << behavior
|
163
|
+
end
|
176
164
|
@environment.objects << creature
|
177
165
|
end
|
178
166
|
|
@@ -195,64 +183,28 @@ class Application
|
|
195
183
|
attribute_controls.pack_start(@blue_slider, expand, fill, padding)
|
196
184
|
@red_slider.value, @green_slider.value, @blue_slider.value = 1, 1, 1
|
197
185
|
|
198
|
-
#Add a text box for the tags.
|
199
|
-
attribute_controls.pack_start(Gtk::Label.new("Tags"), expand, fill, padding)
|
200
|
-
@tags_box = Gtk::Entry.new
|
201
|
-
attribute_controls.pack_start(@tags_box, expand, fill, padding)
|
202
|
-
|
203
186
|
#Add the attribute controls to the panel.
|
204
187
|
control_panel.pack_start(attribute_controls, expand, fill, padding)
|
205
188
|
|
206
|
-
#Create a group for the conditions.
|
207
|
-
condition_controls = Gtk::VBox.new(homogeneous, spacing)
|
208
|
-
condition_controls.pack_start(Gtk::Label.new("Target"), expand, fill, padding)
|
209
|
-
#Add a text box for the tag condition.
|
210
|
-
condition_controls.pack_start(Gtk::Label.new("Tags"), expand, fill, padding)
|
211
|
-
@tag_condition_box = Gtk::Entry.new
|
212
|
-
condition_controls.pack_start(@tag_condition_box, expand, fill, padding)
|
213
|
-
#Add a slider for the age condition.
|
214
|
-
@age_condition_slider = Gtk::HScale.new(0, 300, 30)
|
215
|
-
condition_controls.pack_start(Gtk::Label.new("Age"), expand, fill, padding)
|
216
|
-
condition_controls.pack_start(@age_condition_slider, expand, fill, padding)
|
217
|
-
#Add a slider for the proximity condition.
|
218
|
-
@proximity_condition_slider = Gtk::HScale.new(1, 100, 10)
|
219
|
-
condition_controls.pack_start(Gtk::Label.new("Proximity"), expand, fill, padding)
|
220
|
-
condition_controls.pack_start(@proximity_condition_slider, expand, fill, padding)
|
221
|
-
#Add the condition controls to the panel.
|
222
|
-
control_panel.pack_start(condition_controls, expand, fill, padding)
|
223
|
-
|
224
189
|
#Create a group for the actions.
|
225
190
|
action_controls = Gtk::VBox.new(homogeneous, spacing)
|
226
191
|
action_controls.pack_start(Gtk::Label.new("Actions"), expand, fill, padding)
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
action_controls.pack_start(@
|
231
|
-
@
|
232
|
-
|
233
|
-
@
|
234
|
-
action_controls.pack_start(
|
235
|
-
|
236
|
-
@
|
237
|
-
|
238
|
-
|
239
|
-
action_controls.pack_start(Gtk::Label.new("Approach"), expand, fill, padding)
|
240
|
-
action_controls.pack_start(@approach_slider, expand, fill, padding)
|
241
|
-
@approach_slider.value = 0
|
242
|
-
#Add a slider for the flee action.
|
243
|
-
@flee_slider = Gtk::HScale.new(-180, 180, 20)
|
244
|
-
action_controls.pack_start(Gtk::Label.new("Flee"), expand, fill, padding)
|
245
|
-
action_controls.pack_start(@flee_slider, expand, fill, padding)
|
246
|
-
@flee_slider.value = 0
|
247
|
-
#Add a checkbox for the eat action.
|
192
|
+
@accelerate_flag = Gtk::CheckButton.new("Accelerate")
|
193
|
+
action_controls.pack_start(@accelerate_flag, expand, fill, padding)
|
194
|
+
@turn_flag = Gtk::CheckButton.new("Turn")
|
195
|
+
action_controls.pack_start(@turn_flag, expand, fill, padding)
|
196
|
+
@approach_flag = Gtk::CheckButton.new("Approach")
|
197
|
+
action_controls.pack_start(@approach_flag, expand, fill, padding)
|
198
|
+
@flee_flag = Gtk::CheckButton.new("Flee")
|
199
|
+
action_controls.pack_start(@flee_flag, expand, fill, padding)
|
200
|
+
@push_flag = Gtk::CheckButton.new("Push")
|
201
|
+
action_controls.pack_start(@push_flag, expand, fill, padding)
|
202
|
+
@pull_flag = Gtk::CheckButton.new("Pull")
|
203
|
+
action_controls.pack_start(@pull_flag, expand, fill, padding)
|
248
204
|
@eat_flag = Gtk::CheckButton.new("Eat")
|
249
205
|
action_controls.pack_start(@eat_flag, expand, fill, padding)
|
250
|
-
|
251
|
-
|
252
|
-
#Add a text box for the tag action.
|
253
|
-
action_controls.pack_start(Gtk::Label.new("Apply Tag"), expand, fill, padding)
|
254
|
-
@tag_action_box = Gtk::Entry.new
|
255
|
-
action_controls.pack_start(@tag_action_box, expand, fill, padding)
|
206
|
+
@paint_flag = Gtk::CheckButton.new("Paint")
|
207
|
+
action_controls.pack_start(@paint_flag, expand, fill, padding)
|
256
208
|
#Add the action controls to the panel.
|
257
209
|
control_panel.pack_start(action_controls, expand, fill, padding)
|
258
210
|
|
data/lib/zyps/actions.rb
CHANGED
@@ -35,6 +35,11 @@ class AccelerateAction < Action
|
|
35
35
|
self.rate = rate
|
36
36
|
@clock = Clock.new
|
37
37
|
end
|
38
|
+
#Begin tracking time between actions.
|
39
|
+
def start(actor, target)
|
40
|
+
super
|
41
|
+
@clock.reset_elapsed_time
|
42
|
+
end
|
38
43
|
#Increase or decrease speed according to elapsed time.
|
39
44
|
def do(actor, target)
|
40
45
|
actor.vector.speed += @clock.elapsed_time * rate
|
@@ -51,6 +56,11 @@ class TurnAction < Action
|
|
51
56
|
self.rate = rate
|
52
57
|
@clock = Clock.new
|
53
58
|
end
|
59
|
+
#Begin tracking time between actions.
|
60
|
+
def start(actor, target)
|
61
|
+
super
|
62
|
+
@clock.reset_elapsed_time
|
63
|
+
end
|
54
64
|
#Turn according to elapsed time.
|
55
65
|
def do(actor, target)
|
56
66
|
actor.vector.pitch += @clock.elapsed_time * rate
|
@@ -68,6 +78,11 @@ class ApproachAction < Action
|
|
68
78
|
self.heading, self.turn_rate = heading, turn_rate
|
69
79
|
@clock = Clock.new
|
70
80
|
end
|
81
|
+
#Begin tracking time between actions.
|
82
|
+
def start(actor, target)
|
83
|
+
super
|
84
|
+
@clock.reset_elapsed_time
|
85
|
+
end
|
71
86
|
#Accelerate toward the target, but limited by turn rate.
|
72
87
|
def do(actor, target)
|
73
88
|
#Find the difference between the current heading and the angle to the target.
|
@@ -105,6 +120,11 @@ class FleeAction < Action
|
|
105
120
|
self.heading, self.turn_rate = heading, turn_rate
|
106
121
|
@clock = Clock.new
|
107
122
|
end
|
123
|
+
#Begin tracking time between actions.
|
124
|
+
def start(actor, target)
|
125
|
+
super
|
126
|
+
@clock.reset_elapsed_time
|
127
|
+
end
|
108
128
|
#Accelerate away from the target, but limited by turn rate.
|
109
129
|
def do(actor, target)
|
110
130
|
#Find the difference between the current heading and the angle to the target.
|
@@ -142,6 +162,8 @@ class DestroyAction < Action
|
|
142
162
|
#Remove the target from the environment.
|
143
163
|
def do(actor, target)
|
144
164
|
@environment.objects.delete(target)
|
165
|
+
#Discontinue action.
|
166
|
+
return false
|
145
167
|
end
|
146
168
|
end
|
147
169
|
|
@@ -150,10 +172,10 @@ end
|
|
150
172
|
class EatAction < DestroyAction
|
151
173
|
#Remove the target from the environment, and increase actor's size by size of target.
|
152
174
|
def do(actor, target)
|
153
|
-
#Remove the target from the environment.
|
154
|
-
super
|
155
175
|
#Grow in size.
|
156
176
|
actor.size += target.size
|
177
|
+
#Remove the target from the environment.
|
178
|
+
super
|
157
179
|
end
|
158
180
|
end
|
159
181
|
|
@@ -184,3 +206,53 @@ class BlendAction < Action
|
|
184
206
|
target.color += @color
|
185
207
|
end
|
186
208
|
end
|
209
|
+
|
210
|
+
|
211
|
+
#Pushes target away.
|
212
|
+
class PushAction < Action
|
213
|
+
#Units/second to accelerate target by.
|
214
|
+
attr_accessor :force
|
215
|
+
def initialize(force = 1)
|
216
|
+
@force = force
|
217
|
+
@clock = Clock.new
|
218
|
+
end
|
219
|
+
#Begin tracking time between actions.
|
220
|
+
def start(actor, target)
|
221
|
+
super
|
222
|
+
@clock.reset_elapsed_time
|
223
|
+
end
|
224
|
+
#Accelerate away from the target, but limited by turn rate.
|
225
|
+
def do(actor, target)
|
226
|
+
#Angle to target is also angle of push force.
|
227
|
+
push_angle = Utility.find_angle(actor.location, target.location)
|
228
|
+
#Acceleration will be limited by elapsed time.
|
229
|
+
push_force = @force * @clock.elapsed_time
|
230
|
+
#Apply the force to the creature's movement vector.
|
231
|
+
target.vector += Vector.new(push_force, push_angle)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
#Pulls target toward actor.
|
237
|
+
class PullAction < Action
|
238
|
+
#Units/second to accelerate target by.
|
239
|
+
attr_accessor :force
|
240
|
+
def initialize(force = 1)
|
241
|
+
@force = force
|
242
|
+
@clock = Clock.new
|
243
|
+
end
|
244
|
+
#Begin tracking time between actions.
|
245
|
+
def start(actor, target)
|
246
|
+
super
|
247
|
+
@clock.reset_elapsed_time
|
248
|
+
end
|
249
|
+
#Accelerate away from the target, but limited by turn rate.
|
250
|
+
def do(actor, target)
|
251
|
+
#Angle from target to actor is also angle of pull force (opposite of that for push).
|
252
|
+
pull_angle = Utility.find_angle(target.location, actor.location)
|
253
|
+
#Acceleration will be limited by elapsed time.
|
254
|
+
pull_force = @force * @clock.elapsed_time
|
255
|
+
#Apply the force to the creature's movement vector.
|
256
|
+
target.vector += Vector.new(pull_force, pull_angle)
|
257
|
+
end
|
258
|
+
end
|
@@ -53,3 +53,19 @@ end
|
|
53
53
|
|
54
54
|
|
55
55
|
|
56
|
+
#Keeps all objects at/under the assigned speed.
|
57
|
+
class SpeedLimit < EnvironmentalFactor
|
58
|
+
|
59
|
+
#Maximum allowed speed in units.
|
60
|
+
attr_accessor :maximum
|
61
|
+
def initialize(units)
|
62
|
+
self.maximum = units
|
63
|
+
end
|
64
|
+
#If object is over the speed, reduce its speed.
|
65
|
+
def act(object)
|
66
|
+
object.vector.speed = Utility.constrain_value(object.vector.speed, @maximum)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
|
data/lib/zyps.rb
CHANGED
@@ -37,7 +37,7 @@ class Environment
|
|
37
37
|
#Allow everything in the environment to interact with each other.
|
38
38
|
#Objects are first moved according to their preexisting vectors and the amount of time since the last call.
|
39
39
|
#Then each EnvironmentalFactor is allowed to act on each object.
|
40
|
-
#Finally, each GameObject with an act() method is allowed to act on
|
40
|
+
#Finally, each GameObject with an act() method is allowed to act on the environment.
|
41
41
|
def interact
|
42
42
|
|
43
43
|
#Get time since last interaction.
|
@@ -51,12 +51,9 @@ class Environment
|
|
51
51
|
#Have all environmental factors interact with each object.
|
52
52
|
environmental_factors.each {|factor| factor.act(object)}
|
53
53
|
|
54
|
-
#Have all creatures interact with
|
54
|
+
#Have all creatures interact with the environment.
|
55
55
|
if object.respond_to?(:act)
|
56
|
-
|
57
|
-
next if target.equal?(object) #Ensure object does not act on itself.
|
58
|
-
object.act(target)
|
59
|
-
end
|
56
|
+
object.act(self)
|
60
57
|
end
|
61
58
|
|
62
59
|
end
|
@@ -126,9 +123,9 @@ class Creature < GameObject
|
|
126
123
|
self.behaviors = behaviors
|
127
124
|
end
|
128
125
|
|
129
|
-
#Call Behavior.perform on each of the
|
130
|
-
def act(
|
131
|
-
behaviors.each {|behavior| behavior.perform(self,
|
126
|
+
#Call Behavior.perform(self, environment) on each of the creature's assigned Behaviors.
|
127
|
+
def act(environment)
|
128
|
+
behaviors.each {|behavior| behavior.perform(self, environment)}
|
132
129
|
end
|
133
130
|
|
134
131
|
end
|
@@ -143,8 +140,32 @@ end
|
|
143
140
|
|
144
141
|
|
145
142
|
#An action that one Creature takes on another.
|
146
|
-
#Actions must implement a do(actor, target) instance method.
|
147
143
|
class Action
|
144
|
+
|
145
|
+
#Whether the action was previously started.
|
146
|
+
attr_reader :started
|
147
|
+
|
148
|
+
def initialize
|
149
|
+
@started = false
|
150
|
+
end
|
151
|
+
|
152
|
+
#Start the action.
|
153
|
+
#Overriding subclasses must either call "super" or set the @started attribute to true.
|
154
|
+
def start(actor, target)
|
155
|
+
@started = true
|
156
|
+
end
|
157
|
+
|
158
|
+
#Perform the action.
|
159
|
+
#Subclasses should override this.
|
160
|
+
def do(actor, target)
|
161
|
+
end
|
162
|
+
|
163
|
+
#Stop the action.
|
164
|
+
#Overriding subclasses must either call "super" or set the @started attribute to false.
|
165
|
+
def stop(actor, target)
|
166
|
+
@started = false
|
167
|
+
end
|
168
|
+
|
148
169
|
end
|
149
170
|
|
150
171
|
|
@@ -161,7 +182,7 @@ end
|
|
161
182
|
#Likewise, the subject can change its own attributes, it can approach or flee from the target, it can spawn new Creatures or GameObjects (like bullets), or anything else.
|
162
183
|
class Behavior
|
163
184
|
|
164
|
-
#A list of Condition objects, which are called with the object itself and its target. A condition can consider the tags on the target, the distance from the subject, or any other criteria. If any condition returns false, the behavior will not be carried out.
|
185
|
+
#A list of Condition objects, which are called with the object itself and its target. A condition can consider the tags on the target, the distance from the subject, or any other criteria. If any condition returns false, the behavior will not be carried out (or stopped if it has begun).
|
165
186
|
attr_accessor :conditions
|
166
187
|
#A list of Action objects, which are called with the object and its target when all conditions are met. An action can act on the subject or its target.
|
167
188
|
attr_accessor :actions
|
@@ -169,15 +190,63 @@ class Behavior
|
|
169
190
|
#Optionally takes an array of actions and one of conditions.
|
170
191
|
def initialize (actions = [], conditions = [])
|
171
192
|
self.actions, self.conditions = actions, conditions
|
193
|
+
#Tracks current target.
|
194
|
+
@active_target = nil
|
172
195
|
end
|
173
196
|
|
174
|
-
#
|
175
|
-
#
|
176
|
-
|
177
|
-
|
178
|
-
|
197
|
+
#Test all conditions against each object in the evironment.
|
198
|
+
#For the first object that meets all of them, mark it active (and operate on it first next time).
|
199
|
+
#Then call start() (if applicable) and perform() for all actions against the active target.
|
200
|
+
#If any action or condition returns false, stop all actions, and deselect the active target.
|
201
|
+
def perform(actor, environment)
|
202
|
+
|
203
|
+
begin
|
204
|
+
#Select a target.
|
205
|
+
target = select_target(actor, environment.objects)
|
206
|
+
#Do the actions on the target.
|
207
|
+
actions.each do |action|
|
208
|
+
action.start(actor, target) unless action.started
|
209
|
+
action.do(actor, target)
|
210
|
+
end
|
211
|
+
rescue
|
212
|
+
#If the behavior can no longer be performed, halt it.
|
213
|
+
stop(actor, target)
|
214
|
+
end
|
215
|
+
|
179
216
|
end
|
180
217
|
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
#Stop all actions and de-select the active target.
|
222
|
+
def stop(actor, target)
|
223
|
+
actions.each do |action|
|
224
|
+
action.stop(actor, target) if action.started
|
225
|
+
end
|
226
|
+
@active_target = nil
|
227
|
+
end
|
228
|
+
|
229
|
+
#Select a target that matches all conditions.
|
230
|
+
def select_target(actor, targets)
|
231
|
+
#If a target is already active, still present in the environment, and all conditions are true for it, simply re-select it.
|
232
|
+
if @active_target and targets.include?(@active_target) and conditions.all?{|condition| condition.test(actor, @active_target)}
|
233
|
+
return @active_target
|
234
|
+
end
|
235
|
+
#For each object in environment:
|
236
|
+
targets.each do |target|
|
237
|
+
#Don't let actor target itself.
|
238
|
+
next if target == actor
|
239
|
+
#If all conditions match (or there are no conditions), select the object.
|
240
|
+
if conditions.all?{|condition| condition.test(actor, target)}
|
241
|
+
@active_target = target
|
242
|
+
return target
|
243
|
+
end
|
244
|
+
end
|
245
|
+
#If there were no matches, throw an exception.
|
246
|
+
raise "No matching targets found."
|
247
|
+
end
|
248
|
+
|
249
|
+
|
181
250
|
end
|
182
251
|
|
183
252
|
|
@@ -292,7 +361,7 @@ end
|
|
292
361
|
class Clock
|
293
362
|
|
294
363
|
def initialize
|
295
|
-
|
364
|
+
reset_elapsed_time
|
296
365
|
end
|
297
366
|
|
298
367
|
#Returns the time in (fractional) seconds since this method was last called (or on the first call, time since the Clock was created).
|
@@ -303,6 +372,10 @@ class Clock
|
|
303
372
|
elapsed_time
|
304
373
|
end
|
305
374
|
|
375
|
+
def reset_elapsed_time
|
376
|
+
@last_check_time = Time.new.to_f
|
377
|
+
end
|
378
|
+
|
306
379
|
end
|
307
380
|
|
308
381
|
|
data/test/test_zyps.rb
CHANGED
@@ -125,6 +125,7 @@ class TestEnvironment < Test::Unit::TestCase
|
|
125
125
|
@environment = Environment.new
|
126
126
|
@environment.objects << Creature.new('1')
|
127
127
|
@environment.objects << Creature.new('2')
|
128
|
+
@environment.objects << Creature.new('3')
|
128
129
|
|
129
130
|
end
|
130
131
|
|
@@ -144,11 +145,13 @@ class TestEnvironment < Test::Unit::TestCase
|
|
144
145
|
|
145
146
|
def test_interactions
|
146
147
|
|
147
|
-
#Set up
|
148
|
-
behavior = Behavior.new
|
148
|
+
#Set up behaviors that will log interactions.
|
149
149
|
log = LogAction.new
|
150
|
-
|
151
|
-
|
150
|
+
@environment.objects.each do |creature|
|
151
|
+
behavior = Behavior.new
|
152
|
+
behavior.actions << log
|
153
|
+
creature.behaviors << behavior
|
154
|
+
end
|
152
155
|
|
153
156
|
#Have environment elements interact.
|
154
157
|
@environment.interact
|
@@ -211,10 +214,32 @@ class TestEnvironment < Test::Unit::TestCase
|
|
211
214
|
#Have environment elements interact.
|
212
215
|
@environment.interact
|
213
216
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
217
|
+
assert_equal(0, log.interactions.find_all{|i| i == "2 targeting 1"}.length, "Creature '1' should NOT have been acted on.")
|
218
|
+
assert_equal(1, log.interactions.find_all{|i| i == "1 targeting 2"}.length, "Creature '2' SHOULD have been acted on.")
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
#Test that creatures can switch targets when one is removed from the environment.
|
224
|
+
def test_object_removal
|
225
|
+
|
226
|
+
#Set up behaviors that will log interactions.
|
227
|
+
log = LogAction.new
|
228
|
+
@environment.objects.each do |creature|
|
229
|
+
behavior = Behavior.new
|
230
|
+
behavior.actions << log
|
231
|
+
creature.behaviors << behavior
|
232
|
+
end
|
233
|
+
|
234
|
+
#Have environment elements interact.
|
235
|
+
@environment.interact
|
236
|
+
#Remove the original target from the environment.
|
237
|
+
@environment.objects.delete_at(1)
|
238
|
+
#Interact again.
|
239
|
+
@environment.interact
|
240
|
+
|
241
|
+
#Look for expected interactions (each should only occur once).
|
242
|
+
assert_equal(1, log.interactions.find_all{|i| i == "1 targeting 3"}.length)
|
218
243
|
|
219
244
|
end
|
220
245
|
|
@@ -446,4 +471,82 @@ class TestUtility < Test::Unit::TestCase
|
|
446
471
|
))
|
447
472
|
end
|
448
473
|
|
474
|
+
end
|
475
|
+
|
476
|
+
#TODO: Rewrite to pass environment.
|
477
|
+
class TestBehavior < Test::Unit::TestCase
|
478
|
+
|
479
|
+
#Always true.
|
480
|
+
class TrueCondition < Condition
|
481
|
+
def test(actor, target)
|
482
|
+
true
|
483
|
+
end
|
484
|
+
end
|
485
|
+
#Always false.
|
486
|
+
class FalseCondition < Condition
|
487
|
+
def test(actor, target)
|
488
|
+
false
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
|
493
|
+
def setup
|
494
|
+
@actor = Creature.new('actor')
|
495
|
+
@target = Creature.new('target')
|
496
|
+
@other = Creature.new('other')
|
497
|
+
@environment = Environment.new
|
498
|
+
@environment.objects << @actor << @target << @other
|
499
|
+
end
|
500
|
+
|
501
|
+
|
502
|
+
#Tracks number of times its start, stop, and do methods are called.
|
503
|
+
class MockAction < Action
|
504
|
+
attr_accessor :start_count, :stop_count, :do_count
|
505
|
+
def initialize
|
506
|
+
@start_count, @stop_count, @do_count = 0, 0, 0
|
507
|
+
end
|
508
|
+
def start(actor, target)
|
509
|
+
super
|
510
|
+
@start_count += 1
|
511
|
+
end
|
512
|
+
def do(actor, target)
|
513
|
+
@do_count += 1
|
514
|
+
end
|
515
|
+
def stop(actor, target)
|
516
|
+
super
|
517
|
+
@stop_count += 1
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def test_actions
|
522
|
+
|
523
|
+
#Set up a behavior with a true condition and a mock action.
|
524
|
+
behavior = Behavior.new
|
525
|
+
behavior.conditions << TrueCondition.new
|
526
|
+
action = MockAction.new
|
527
|
+
behavior.actions << action
|
528
|
+
|
529
|
+
#Perform the behavior.
|
530
|
+
behavior.perform(@actor, @environment)
|
531
|
+
assert_equal(1, action.start_count, "start() should have been called on the mock action.")
|
532
|
+
assert_equal(1, action.do_count, "do() should have been called.")
|
533
|
+
assert_equal(0, action.stop_count, "stop() should NOT have been called.")
|
534
|
+
|
535
|
+
#Perform the behavior again.
|
536
|
+
behavior.perform(@actor, @environment)
|
537
|
+
assert_equal(1, action.start_count, "start() should NOT have been called.")
|
538
|
+
assert_equal(2, action.do_count, "do() should have been called.")
|
539
|
+
assert_equal(0, action.stop_count, "stop() should NOT have been called.")
|
540
|
+
|
541
|
+
#Add a false condition to the behavior.
|
542
|
+
behavior.conditions << FalseCondition.new
|
543
|
+
#Perform the behavior.
|
544
|
+
behavior.perform(@actor, @environment)
|
545
|
+
assert_equal(1, action.start_count, "start() should NOT have been called.")
|
546
|
+
assert_equal(2, action.do_count, "do() should NOT have been called, because conditions are no longer true.")
|
547
|
+
assert_equal(1, action.stop_count, "stop() SHOULD have been called.")
|
548
|
+
|
549
|
+
end
|
550
|
+
|
551
|
+
|
449
552
|
end
|