zyps 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zyps/actions.rb CHANGED
@@ -48,7 +48,13 @@ class TimedAction < Action
48
48
  end
49
49
 
50
50
  #Begin tracking time between actions.
51
- def start(actor, target)
51
+ def start(actor, targets)
52
+ super
53
+ @clock.reset_elapsed_time
54
+ end
55
+
56
+ #Halt tracking time between actions.
57
+ def stop(actor, targets)
52
58
  super
53
59
  @clock.reset_elapsed_time
54
60
  end
@@ -58,9 +64,9 @@ end
58
64
 
59
65
  #Head toward a target.
60
66
  class FaceAction < Action
61
- #Set the actor's heading to point directly at target.
62
- def do(actor, target)
63
- actor.vector.pitch = Utility.find_angle(actor.location, target.location)
67
+ #Set the actor's heading to point directly at first target.
68
+ def do(actor, targets)
69
+ actor.vector.pitch = Utility.find_angle(actor.location, targets[0].location)
64
70
  end
65
71
  end
66
72
 
@@ -71,111 +77,56 @@ class AccelerateAction < TimedAction
71
77
  #Can be negative to slow down or go in reverse.
72
78
  attr_accessor :rate
73
79
  #Increase or decrease speed according to elapsed time.
74
- def do(actor, target)
80
+ def do(actor, targets)
75
81
  actor.vector.speed += delta
76
82
  end
77
83
  end
78
84
 
79
85
 
80
- #Turn over time.
86
+ #Apply a thrust that turns actor.
81
87
  class TurnAction < TimedAction
82
- #Degrees per second to turn.
83
- #Positive turns clockwise, negative turns counter-clockwise.
84
- attr_accessor :rate
85
- #Turn according to elapsed time.
86
- def do(actor, target)
87
- actor.vector.pitch += delta
88
+ #Angle to turn at.
89
+ attr_accessor :angle
90
+ def initialize(rate, angle)
91
+ super
92
+ @angle = angle
93
+ end
94
+ def do(actor, targets)
95
+ actor.vector += Vector.new(
96
+ delta,
97
+ actor.vector.pitch + @angle
98
+ )
88
99
  end
89
100
  end
90
101
 
91
102
 
92
103
  #Approaches the target, but obeys law of inertia.
93
104
  class ApproachAction < TimedAction
94
- #Direction/speed actor is accelerating in.
95
- attr_accessor :heading
96
- #Degrees per second the direction of acceleration can change.
97
- attr_accessor :rate
98
- def initialize(rate = 360, heading = Vector.new)
99
- super
100
- self.heading = heading
101
- end
102
- #Make a deep copy.
103
- def copy
104
- copy = super
105
- copy.heading = @heading.copy
106
- copy
107
- end
108
- #Accelerate toward the target, but limited by turn rate.
109
- def do(actor, target)
110
- #Find the difference between the current heading and the angle to the target.
111
- turn_angle = Utility.find_angle(actor.location, target.location) - @heading.pitch
112
- #If the angle is the long way around from the current heading, change it to the smaller angle.
113
- if turn_angle > 180 then
114
- turn_angle -= 360.0
115
- elsif turn_angle < -180 then
116
- turn_angle += 360.0
117
- end
118
- #The creature can only turn as fast as the elapsed time, of course.
119
- maximum_turn = delta
120
- if turn_angle.abs > maximum_turn
121
- if turn_angle > 0
122
- turn_angle = maximum_turn
123
- else
124
- turn_angle = maximum_turn * -1
125
- end
126
- end
127
- #Turn the appropriate amount.
128
- @heading.pitch += turn_angle
129
- #Apply the heading to the creature's movement vector.
130
- actor.vector += @heading
105
+ #Accelerate toward the first target, but limited by rate.
106
+ def do(actor, targets)
107
+ #Apply thrust to the creature's movement vector, adjusted by elapsed time.
108
+ actor.vector += Vector.new(
109
+ delta,
110
+ Utility.find_angle(actor.location, targets[0].location)
111
+ )
131
112
  end
132
113
  end
133
114
 
134
115
 
135
116
  #Flees from the target, but obeys law of inertia.
136
117
  class FleeAction < TimedAction
137
- #Direction/speed actor is accelerating in.
138
- attr_accessor :heading
139
- #Degrees per second the direction of acceleration can change.
140
- attr_accessor :rate
141
- def initialize(rate = 360, heading = Vector.new)
142
- super
143
- self.heading = heading
144
- end
145
- #Make a deep copy.
146
- def copy
147
- copy = super
148
- copy.heading = @heading.copy
149
- copy
150
- end
151
- #Accelerate away from the target, but limited by turn rate.
152
- def do(actor, target)
153
- #Find the difference between the current heading and the angle to the target.
154
- turn_angle = Utility.find_angle(actor.location, target.location) - @heading.pitch + 180
155
- #If the angle is the long way around from the current heading, change it to the smaller angle.
156
- if turn_angle > 180 then
157
- turn_angle -= 360.0
158
- elsif turn_angle < -180 then
159
- turn_angle += 360.0
160
- end
161
- #The creature can only turn as fast as the elapsed time, of course.
162
- maximum_turn = delta
163
- if turn_angle.abs > maximum_turn
164
- if turn_angle > 0
165
- turn_angle = maximum_turn
166
- else
167
- turn_angle = maximum_turn * -1
168
- end
169
- end
170
- #Turn the appropriate amount.
171
- @heading.pitch += turn_angle
172
- #Apply the heading to the creature's movement vector.
173
- actor.vector += @heading
118
+ #Accelerate away from the first target, but limited by turn rate.
119
+ def do(actor, targets)
120
+ #Apply thrust to the creature's movement vector, adjusted by elapsed time.
121
+ actor.vector += Vector.new(
122
+ delta,
123
+ Utility.find_angle(actor.location, targets[0].location) + 180
124
+ )
174
125
  end
175
126
  end
176
127
 
177
128
 
178
- #Destroy the target.
129
+ #Destroy the targets.
179
130
  class DestroyAction < Action
180
131
  #The environment to remove objects from.
181
132
  attr_accessor :environment
@@ -183,19 +134,23 @@ class DestroyAction < Action
183
134
  self.environment = environment
184
135
  end
185
136
  #Remove the target from the environment.
186
- def do(actor, target)
187
- @environment.objects.delete(target)
137
+ def do(actor, targets)
138
+ targets.each do |target|
139
+ @environment.objects.delete(target)
140
+ end
188
141
  end
189
142
  end
190
143
 
191
144
 
192
- #Destroy the target and grow in size.
145
+ #Destroy the targets and grow in size.
193
146
  class EatAction < DestroyAction
194
- #Remove the target from the environment, and increase actor's size by size of target.
195
- def do(actor, target)
147
+ #Remove the targets from the environment, and increase actor's size by size of targets.
148
+ def do(actor, targets)
196
149
  #Grow in size.
197
- actor.size += target.size
198
- #Remove the target from the environment.
150
+ targets.each do |target|
151
+ actor.size += target.size
152
+ end
153
+ #Remove the targets from the environment.
199
154
  super
200
155
  end
201
156
  end
@@ -203,44 +158,50 @@ end
203
158
 
204
159
  #Add a tag to the target.
205
160
  class TagAction < Action
206
- #Tag to apply to target.
161
+ #Tag to apply to targets.
207
162
  attr_accessor :tag
208
163
  def initialize(tag)
209
164
  self.tag = tag
210
165
  end
211
- #Apply the given tag to the target.
212
- def do(actor, target)
213
- target.tags << tag unless target.tags.include?(tag)
166
+ #Apply the given tag to the targets.
167
+ def do(actor, targets)
168
+ targets.each do |target|
169
+ target.tags << tag unless target.tags.include?(tag)
170
+ end
214
171
  end
215
172
  end
216
173
 
217
174
 
218
175
  #Blend the target's color with another color.
219
176
  class BlendAction < Action
220
- #Color to apply to target.
177
+ #Color to apply to targets.
221
178
  attr_accessor :color
222
179
  def initialize(color)
223
180
  self.color = color
224
181
  end
225
- #Blend the target's color with the assigned color.
226
- def do(actor, target)
227
- target.color += @color
182
+ #Blend the targets' color with the assigned color.
183
+ def do(actor, targets)
184
+ targets.each do |target|
185
+ target.color += @color
186
+ end
228
187
  end
229
188
  end
230
189
 
231
190
 
232
191
  #Pushes target away.
233
192
  class PushAction < TimedAction
234
- #Units/second to accelerate target by.
193
+ #Units/second to accelerate targets by.
235
194
  attr_accessor :rate
236
- #Accelerate the target away from the actor, but limited by elapsed time.
237
- def do(actor, target)
238
- #Angle to target is also angle of push force.
239
- push_angle = Utility.find_angle(actor.location, target.location)
195
+ #Push the targets away from the actor, with force limited by elapsed time.
196
+ def do(actor, targets)
240
197
  #Acceleration will be limited by elapsed time.
241
198
  push_force = delta
242
- #Apply the force to the creature's movement vector.
243
- target.vector += Vector.new(push_force, push_angle)
199
+ targets.each do |target|
200
+ #Angle to target is also angle of push force.
201
+ push_angle = Utility.find_angle(actor.location, target.location)
202
+ #Apply the force to the creature's movement vector.
203
+ target.vector += Vector.new(push_force, push_angle)
204
+ end
244
205
  end
245
206
  end
246
207
 
@@ -249,14 +210,16 @@ end
249
210
  class PullAction < TimedAction
250
211
  #Units/second to accelerate target by.
251
212
  attr_accessor :rate
252
- #Accelerate away from the target, but limited by turn rate.
253
- def do(actor, target)
254
- #Angle from target to actor is also angle of pull force (opposite of that for push).
255
- pull_angle = Utility.find_angle(target.location, actor.location)
213
+ #Pull the targets toward the actor, with force limited by elapsed time.
214
+ def do(actor, targets)
256
215
  #Acceleration will be limited by elapsed time.
257
216
  pull_force = delta
258
- #Apply the force to the creature's movement vector.
259
- target.vector += Vector.new(pull_force, pull_angle)
217
+ targets.each do |target|
218
+ #Angle from target to actor is also angle of pull force (opposite of that for push).
219
+ pull_angle = Utility.find_angle(target.location, actor.location)
220
+ #Apply the force to the creature's movement vector.
221
+ target.vector += Vector.new(pull_force, pull_angle)
222
+ end
260
223
  end
261
224
  end
262
225
 
@@ -272,31 +235,33 @@ class BreedAction < Action
272
235
  @clock = Clock.new
273
236
  @time_since_last_action = 0
274
237
  end
275
- def do(actor, target)
276
- #Skip action if target is not a Creature.
277
- return unless target.is_a?(Creature)
278
- #Get time since last action, and skip if it hasn't been long enough.
279
- @time_since_last_action += @clock.elapsed_time
280
- return unless @time_since_last_action >= @delay
281
- #Create a child.
282
- child = Creature.new
283
- #Combine colors.
284
- child.color = actor.color + target.color
285
- #Combine behaviors EXCEPT those with BreedActions.
286
- behaviors = (actor.behaviors + target.behaviors).find_all do |behavior|
287
- ! behavior.actions.any?{|action| action.is_a?(BreedAction)}
238
+ def do(actor, targets)
239
+ targets.each do |target|
240
+ #Skip action if target is not a Creature.
241
+ next unless target.is_a?(Creature)
242
+ #Get time since last action, and skip if it hasn't been long enough.
243
+ @time_since_last_action += @clock.elapsed_time
244
+ return unless @time_since_last_action >= @delay
245
+ #Create a child.
246
+ child = Creature.new
247
+ #Combine colors.
248
+ child.color = actor.color + target.color
249
+ #Combine behaviors EXCEPT those with BreedActions.
250
+ behaviors = (actor.behaviors + target.behaviors).find_all do |behavior|
251
+ ! behavior.actions.any?{|action| action.is_a?(BreedAction)}
252
+ end
253
+ behaviors.each {|behavior| child.behaviors << behavior.copy}
254
+ #Location should equal actor's.
255
+ child.location = actor.location.copy
256
+ #Add parents' vectors to get child's vector.
257
+ child.vector = actor.vector + target.vector
258
+ #Child's size should be half the average size of the parents'.
259
+ child.size = ((actor.size + target.size) / 2) / 2
260
+ #Add child to environment.
261
+ @environment.objects << child
262
+ #Reset elapsed time.
263
+ @time_since_last_action = 0
288
264
  end
289
- behaviors.each {|behavior| child.behaviors << behavior.copy}
290
- #Location should equal actor's.
291
- child.location = actor.location.copy
292
- #Add parents' vectors to get child's vector.
293
- child.vector = actor.vector + target.vector
294
- #Child's size should be half the average size of the parents'.
295
- child.size = ((actor.size + target.size) / 2) / 2
296
- #Add child to environment.
297
- @environment.objects << child
298
- #Reset elapsed time.
299
- @time_since_last_action = 0
300
265
  end
301
266
  end
302
267
 
@@ -21,53 +21,77 @@ require 'zyps'
21
21
  module Zyps
22
22
 
23
23
 
24
- #Act only on objects with the correct tag.
24
+ #Select objects with the correct tag.
25
25
  class TagCondition < Condition
26
26
  #The tag to look for on the target.
27
27
  attr_accessor :tag
28
28
  def initialize(tag)
29
29
  self.tag = tag
30
30
  end
31
- #True if the target has the assigned tag.
32
- def met?(actor, target)
33
- target.tags.include?(@tag)
31
+ #Returns an array of targets which have the assigned tag.
32
+ def select(actor, targets)
33
+ targets.find_all {|target| target.tags.include?(@tag)}
34
34
  end
35
35
  end
36
36
 
37
37
 
38
- #Act only on objects older than the given age.
38
+ #Select objects older than the given age.
39
39
  class AgeCondition < Condition
40
40
  #The minimum age in seconds.
41
41
  attr_accessor :age
42
42
  def initialize(age)
43
43
  self.age = age
44
44
  end
45
- #True if the target is older than the assigned age.
46
- def met?(actor, target)
47
- target.age > @age
45
+ #Returns an array of targets which are older than the assigned age.
46
+ def select(actor, targets)
47
+ targets.find_all {|target| target.age > @age}
48
48
  end
49
49
  end
50
50
 
51
51
 
52
- #Act only on objects closer than the given distance.
52
+ #Select objects that are closer than the given distance.
53
53
  class ProximityCondition < Condition
54
54
  #The maximum number of units away the target can be.
55
55
  attr_accessor :distance
56
56
  def initialize(distance)
57
57
  self.distance = distance
58
58
  end
59
- #True if the actor and target are equal to or closer than the given distance.
60
- def met?(actor, target)
61
- Utility.find_distance(actor.location, target.location) <= @distance
59
+ #Returns an array of targets that are at the given distance or closer.
60
+ def select(actor, targets)
61
+ targets.find_all {|target| Utility.find_distance(actor.location, target.location) <= @distance}
62
62
  end
63
63
  end
64
64
 
65
65
 
66
66
  #True only if collided with target.
67
67
  class CollisionCondition < Condition
68
- #True if the objects have collided.
69
- def met?(actor, target)
70
- Utility.collided?(actor, target)
68
+ #Returns an array of targets that have collided with the actor.
69
+ def select(actor, targets)
70
+ targets.find_all {|target| Utility.collided?(actor, target)}
71
+ end
72
+ end
73
+
74
+
75
+ #True if the actor's strength is equal to or greater than the target's.
76
+ class StrengthCondition < Condition
77
+ #Returns an array of targets that are weaker than the actor.
78
+ #For now, strength is based merely on size.
79
+ def select(actor, targets)
80
+ targets.find_all {|target| actor.size >= target.size}
81
+ end
82
+ end
83
+
84
+
85
+ #True if the actor and target are of the same Ruby class.
86
+ class ClassCondition < Condition
87
+ #The class of target to look for.
88
+ attr_accessor :target_class
89
+ def initialize(target_class)
90
+ self.target_class = target_class
91
+ end
92
+ #Returns an array of targets that are of the selected Ruby class.
93
+ def select(actor, targets)
94
+ targets.grep(target_class)
71
95
  end
72
96
  end
73
97
 
@@ -33,32 +33,44 @@ class Enclosure < EnvironmentalFactor
33
33
  #Y coordinate of bottom boundary.
34
34
  attr_accessor :bottom
35
35
 
36
- def initialize(left = 0, top = 0, right = 0, bottom = 0)
37
- self.left, self.top, self.right, self.bottom = left, top, right, bottom
36
+ #Takes a hash with these keys and defaults:
37
+ # :left => 0
38
+ # :top => 0
39
+ # :right => 0
40
+ # :bottom => 0
41
+ def initialize(options = {})
42
+ options = {
43
+ :left => 0,
44
+ :top => 0,
45
+ :right => 0,
46
+ :bottom => 0
47
+ }.merge(options)
48
+ self.left, self.top, self.right, self.bottom = options[:left], options[:top], options[:right], options[:bottom]
38
49
  end
39
50
 
40
51
  #If object is beyond a boundary, set its position equal to the boundary and reflect it.
41
- def act(object)
42
- if (object.location.x < @left) then
43
- object.location.x = @left
44
- object.vector.pitch = Utility.find_reflection_angle(90, object.vector.pitch)
45
- elsif (object.location.x > @right) then
46
- object.location.x = @right
47
- object.vector.pitch = Utility.find_reflection_angle(270, object.vector.pitch)
48
- end
49
- if (object.location.y > @top) then
50
- object.location.y = @top
51
- object.vector.pitch = Utility.find_reflection_angle(0, object.vector.pitch)
52
- elsif (object.location.y < @bottom) then
53
- object.location.y = @bottom
54
- object.vector.pitch = Utility.find_reflection_angle(180, object.vector.pitch)
52
+ def act(environment)
53
+ environment.objects.each do |object|
54
+ if (object.location.x < @left) then
55
+ object.location.x = @left
56
+ object.vector.pitch = Utility.find_reflection_angle(90, object.vector.pitch)
57
+ elsif (object.location.x > @right) then
58
+ object.location.x = @right
59
+ object.vector.pitch = Utility.find_reflection_angle(270, object.vector.pitch)
60
+ end
61
+ if (object.location.y > @top) then
62
+ object.location.y = @top
63
+ object.vector.pitch = Utility.find_reflection_angle(0, object.vector.pitch)
64
+ elsif (object.location.y < @bottom) then
65
+ object.location.y = @bottom
66
+ object.vector.pitch = Utility.find_reflection_angle(180, object.vector.pitch)
67
+ end
55
68
  end
56
69
  end
57
70
 
58
71
  end
59
72
 
60
73
 
61
-
62
74
  #Keeps all objects at/under the assigned speed.
63
75
  class SpeedLimit < EnvironmentalFactor
64
76
 
@@ -70,8 +82,10 @@ class SpeedLimit < EnvironmentalFactor
70
82
  end
71
83
 
72
84
  #If object is over the speed, reduce its speed.
73
- def act(object)
74
- object.vector.speed = Utility.constrain_value(object.vector.speed, @maximum)
85
+ def act(environment)
86
+ environment.objects.each do |object|
87
+ object.vector.speed = Utility.constrain_value(object.vector.speed, @maximum)
88
+ end
75
89
  end
76
90
 
77
91
  end
@@ -85,15 +99,16 @@ class Accelerator < EnvironmentalFactor
85
99
 
86
100
  def initialize(vector)
87
101
  self.vector = vector
88
- @clocks = {} #Don't use Hash.new <block>, as it can't transmit via DRb.
102
+ @clock = Clock.new
89
103
  end
90
104
 
91
105
  #Accelerate the target away from the actor, but limited by elapsed time.
92
- def act(object)
93
- #Track time since last action for each GameObject.
94
- @clocks[object] ||= Clock.new
95
- #Push on object.
96
- object.vector += Vector.new(@vector.speed * @clocks[object].elapsed_time, @vector.pitch)
106
+ def act(environment)
107
+ elapsed_time = @clock.elapsed_time
108
+ environment.objects.each do |object|
109
+ #Push on object.
110
+ object.vector += Vector.new(@vector.speed * elapsed_time, @vector.pitch)
111
+ end
97
112
  end
98
113
 
99
114
  end
@@ -124,25 +139,26 @@ class Friction < EnvironmentalFactor
124
139
 
125
140
  def initialize(force)
126
141
  self.force = force
127
- #Track time since last action for each GameObject.
128
- @clocks = {} #Don't use Hash.new <block>, as it can't transmit via DRb.
142
+ #Track time since last action.
143
+ @clock = Clock.new
129
144
  end
130
145
 
131
146
  #Reduce the target's speed at the given rate.
132
- def act(object)
133
- #Track time since last action for each GameObject.
134
- @clocks[object] ||= Clock.new
135
- #Slow object.
136
- acceleration = @force * @clocks[object].elapsed_time
137
- speed = object.vector.speed
138
- if speed > 0
139
- speed -= acceleration
140
- speed = 0 if speed < 0
141
- elsif speed < 0
142
- speed += acceleration
143
- speed = 0 if speed > 0
147
+ def act(environment)
148
+ elapsed_time = @clock.elapsed_time
149
+ environment.objects.each do |object|
150
+ #Slow object.
151
+ acceleration = @force * elapsed_time
152
+ speed = object.vector.speed
153
+ if speed > 0
154
+ speed -= acceleration
155
+ speed = 0 if speed < 0
156
+ elsif speed < 0
157
+ speed += acceleration
158
+ speed = 0 if speed > 0
159
+ end
160
+ object.vector.speed = speed
144
161
  end
145
- object.vector.speed = speed
146
162
  end
147
163
 
148
164
  end
@@ -150,21 +166,17 @@ end
150
166
 
151
167
  class PopulationLimit < EnvironmentalFactor
152
168
 
153
- #Environment to remove objects from.
154
- attr_accessor :environment
155
169
  #Maximum allowed population.
156
170
  attr_accessor :count
157
171
 
158
- def initialize(environment, count)
159
- self.environment = environment
172
+ def initialize(count)
160
173
  self.count = count
161
174
  end
162
175
 
163
176
  #Remove target if there are too many objects in environment.
164
- def act(object)
165
- if environment.objects.length > @count
166
- environment.objects.delete(object)
167
- end
177
+ def act(environment)
178
+ excess = environment.objects.length - @count
179
+ environment.objects.slice!(0, excess) if excess > 0
168
180
  end
169
181
 
170
182
  end