zyps 0.6.3 → 0.7.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.
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