zyps 0.7.5 → 0.7.6
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/COPYING.txt +674 -674
- data/README_windows.txt +3 -0
- data/bin/zyps +17 -12
- data/bin/zyps-175 +514 -0
- data/bin/zyps_demo +139 -72
- data/lib/zyps.rb +71 -3
- data/lib/zyps/actions.rb +98 -9
- data/lib/zyps/conditions.rb +22 -0
- data/test/test_zyps.rb +51 -0
- data/test/zyps/test_actions.rb +78 -17
- data/test/zyps/test_conditions.rb +12 -0
- metadata +58 -64
data/README_windows.txt
CHANGED
@@ -28,6 +28,9 @@ your efforts so long ago.
|
|
28
28
|
|
29
29
|
Alex Fenton and Mario Steele for wxRuby advice.
|
30
30
|
|
31
|
+
The authors of the Nullsoft Scriptable Install System
|
32
|
+
(NSIS), which is used for the Windows installer.
|
33
|
+
|
31
34
|
My lovely wife, Diana, for patience and usability testing.
|
32
35
|
|
33
36
|
|
data/bin/zyps
CHANGED
@@ -126,11 +126,14 @@ class Application < Wx::App
|
|
126
126
|
@log.debug "Create an interface."
|
127
127
|
@controls = ControlPanel.new(frame)
|
128
128
|
@controls.clear_button.evt_button(@controls.clear_button.get_id) do |event|
|
129
|
-
@environment.objects
|
129
|
+
@environment.objects.clear
|
130
130
|
end
|
131
131
|
@controls.length_slider.evt_slider(@controls.length_slider.get_id) do |event|
|
132
132
|
@view.trail_length = @controls.length_slider.value
|
133
133
|
end
|
134
|
+
@controls.speed_slider.evt_slider(@controls.speed_slider) do |event|
|
135
|
+
Clock.speed = @controls.speed_slider.value.to_f / 5.0
|
136
|
+
end
|
134
137
|
frame.sizer.add(@controls, 0, Wx::GROW)
|
135
138
|
|
136
139
|
@log.debug "Create an environment, and link it to the view."
|
@@ -525,6 +528,7 @@ class ControlPanel < Wx::Panel
|
|
525
528
|
attr_accessor :breed_flag
|
526
529
|
attr_accessor :eat_flag
|
527
530
|
attr_accessor :length_slider
|
531
|
+
attr_accessor :speed_slider
|
528
532
|
attr_accessor :clear_button
|
529
533
|
|
530
534
|
|
@@ -546,11 +550,14 @@ class ControlPanel < Wx::Panel
|
|
546
550
|
|
547
551
|
display_controls = add_panel(self, :proportion => 1)
|
548
552
|
add_label(display_controls, :label => "Length")
|
549
|
-
@length_slider = add_slider(display_controls, :min_value => 2, :max_value =>
|
553
|
+
@length_slider = add_slider(display_controls, :min_value => 2, :max_value => 20)
|
550
554
|
@length_slider.value = 5
|
551
555
|
|
552
556
|
environment_controls = add_panel(self, :proportion => 1)
|
553
557
|
add_label(environment_controls, :label => "Environment")
|
558
|
+
add_label(environment_controls, :label => "Speed")
|
559
|
+
@speed_slider = add_slider(environment_controls, :min_value => 0, :max_value => 15)
|
560
|
+
@speed_slider.value = 5
|
554
561
|
@clear_button = add_button(environment_controls, :label => "Clear")
|
555
562
|
|
556
563
|
end
|
@@ -720,15 +727,14 @@ class CreatureGenerator
|
|
720
727
|
)
|
721
728
|
|
722
729
|
#Set up actions and merge colors according to selected behaviors.
|
723
|
-
color = Color.new(0.5, 0.5, 0.5)
|
724
730
|
if options[:turn]
|
725
|
-
color.blue
|
731
|
+
color = color ? color + Color.blue : Color.blue
|
726
732
|
creature.behaviors << Behavior.new(
|
727
733
|
:actions => [TurnAction.new(@turn_rate, @turn_angle)]
|
728
734
|
)
|
729
735
|
end
|
730
736
|
if options[:approach]
|
731
|
-
color.red
|
737
|
+
color = color ? color + Color.red : Color.red
|
732
738
|
creature.behaviors << Behavior.new(
|
733
739
|
:actions => [ApproachAction.new(@approach_rate)],
|
734
740
|
:conditions => [ProximityCondition.new(options[:action_proximity])],
|
@@ -736,7 +742,7 @@ class CreatureGenerator
|
|
736
742
|
)
|
737
743
|
end
|
738
744
|
if options[:flee]
|
739
|
-
color
|
745
|
+
color = color ? color + Color.yellow : Color.yellow
|
740
746
|
creature.behaviors << Behavior.new(
|
741
747
|
:actions => [FleeAction.new(@flee_rate)],
|
742
748
|
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)],
|
@@ -744,7 +750,7 @@ class CreatureGenerator
|
|
744
750
|
)
|
745
751
|
end
|
746
752
|
if options[:push]
|
747
|
-
color
|
753
|
+
color = color ? color + Color.violet : Color.violet
|
748
754
|
creature.behaviors << Behavior.new(
|
749
755
|
:actions => [PushAction.new(@push_strength)],
|
750
756
|
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)],
|
@@ -752,7 +758,7 @@ class CreatureGenerator
|
|
752
758
|
)
|
753
759
|
end
|
754
760
|
if options[:pull]
|
755
|
-
color
|
761
|
+
color = color ? color + Color.indigo : Color.indigo
|
756
762
|
creature.behaviors << Behavior.new(
|
757
763
|
:actions => [PullAction.new(@pull_strength)],
|
758
764
|
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)],
|
@@ -760,8 +766,7 @@ class CreatureGenerator
|
|
760
766
|
)
|
761
767
|
end
|
762
768
|
if options[:breed]
|
763
|
-
color
|
764
|
-
color.blue -= 0.1
|
769
|
+
color = color ? color + Color.orange : Color.orange
|
765
770
|
creature.behaviors << Behavior.new(
|
766
771
|
:actions => [BreedAction.new(@environment, @breed_rate)],
|
767
772
|
:conditions => [CollisionCondition.new], #The default ProximityCondition won't do.
|
@@ -769,7 +774,7 @@ class CreatureGenerator
|
|
769
774
|
)
|
770
775
|
end
|
771
776
|
if options[:eat]
|
772
|
-
color.green
|
777
|
+
color = color ? color + Color.green : Color.green
|
773
778
|
creature.behaviors << Behavior.new(
|
774
779
|
:actions => [EatAction.new(@environment)],
|
775
780
|
:conditions => [
|
@@ -780,7 +785,7 @@ class CreatureGenerator
|
|
780
785
|
)
|
781
786
|
end
|
782
787
|
|
783
|
-
creature.color = color
|
788
|
+
creature.color = color || Color.white
|
784
789
|
|
785
790
|
@log.debug "Created creature: #{creature.to_s}"
|
786
791
|
|
data/bin/zyps-175
ADDED
@@ -0,0 +1,514 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
# Copyright 2007 Jay McGavren, jay@mcgavren.com.
|
4
|
+
#
|
5
|
+
# This file is part of Zyps.
|
6
|
+
#
|
7
|
+
# Zyps is free software; you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
# the Free Software Foundation; either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
|
21
|
+
gems_loaded = false
|
22
|
+
begin
|
23
|
+
require 'gtk2'
|
24
|
+
require 'optparse'
|
25
|
+
require 'zyps'
|
26
|
+
require 'zyps/actions'
|
27
|
+
require 'zyps/conditions'
|
28
|
+
require 'zyps/environmental_factors'
|
29
|
+
require 'zyps/remote'
|
30
|
+
require 'zyps/views/trails'
|
31
|
+
require 'zyps/views/canvas/gtk2'
|
32
|
+
rescue LoadError
|
33
|
+
if gems_loaded == false
|
34
|
+
require 'rubygems'
|
35
|
+
gems_loaded = true
|
36
|
+
retry
|
37
|
+
else
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
include Zyps
|
44
|
+
|
45
|
+
|
46
|
+
DEFAULT_VIEW_WIDTH = 800
|
47
|
+
DEFAULT_VIEW_HEIGHT = 600
|
48
|
+
DEFAULT_MAX_SPEED = 200
|
49
|
+
DEFAULT_MAX_POPULATION = 100
|
50
|
+
DEFAULT_FPS = 60
|
51
|
+
|
52
|
+
|
53
|
+
class Application
|
54
|
+
|
55
|
+
#Port to open service on.
|
56
|
+
attr_accessor :uri
|
57
|
+
#Maximum allowed number of objects.
|
58
|
+
attr_accessor :max_population
|
59
|
+
#View dimensions.
|
60
|
+
attr_accessor :view_width, :view_height
|
61
|
+
|
62
|
+
#Create app window, game environment, and view.
|
63
|
+
#Set up default values.
|
64
|
+
#Takes a hash with these keys and defaults:
|
65
|
+
# :uri => nil,
|
66
|
+
# :view_width => DEFAULT_VIEW_WIDTH,
|
67
|
+
# :view_height => DEFAULT_VIEW_HEIGHT,
|
68
|
+
# :fps => DEFAULT_FPS,
|
69
|
+
# :max_population => DEFAULT_MAX_POPULATION,
|
70
|
+
# :max_speed => DEFAULT_MAX_SPEED,
|
71
|
+
# :enclosure => true
|
72
|
+
def initialize(options = {})
|
73
|
+
|
74
|
+
options = {
|
75
|
+
:uri => nil,
|
76
|
+
:view_width => DEFAULT_VIEW_WIDTH,
|
77
|
+
:view_height => DEFAULT_VIEW_HEIGHT,
|
78
|
+
:fps => DEFAULT_FPS,
|
79
|
+
:max_population => DEFAULT_MAX_POPULATION,
|
80
|
+
:max_speed => DEFAULT_MAX_SPEED,
|
81
|
+
:enclosure => true
|
82
|
+
}.merge(options)
|
83
|
+
|
84
|
+
@uri, @view_width, @view_height, @fps, @max_population, @max_speed, @enclosure =
|
85
|
+
options[:uri], options[:view_width], options[:view_height], options[:fps], options[:max_population], options[:max_speed], options[:enclosure]
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def main
|
91
|
+
|
92
|
+
#Create a window, and set GTK up to quit when it is closed.
|
93
|
+
window = Gtk::Window.new
|
94
|
+
window.resizable = false
|
95
|
+
window.signal_connect("delete_event") {false}
|
96
|
+
window.signal_connect("destroy") {Gtk.main_quit}
|
97
|
+
|
98
|
+
#Create environment.
|
99
|
+
@environment = Environment.new
|
100
|
+
|
101
|
+
#Set up controls.
|
102
|
+
#Also initializes @view.
|
103
|
+
window.add(
|
104
|
+
create_controls(
|
105
|
+
:view_width => @view_width,
|
106
|
+
:view_height => @view_height,
|
107
|
+
:spacing => 2
|
108
|
+
)
|
109
|
+
)
|
110
|
+
|
111
|
+
#Show all widgets.
|
112
|
+
window.show_all
|
113
|
+
|
114
|
+
#Point view at environment.
|
115
|
+
@environment.add_observer(@view)
|
116
|
+
|
117
|
+
#Keep objects on screen.
|
118
|
+
if @enclosure
|
119
|
+
enclosure = Enclosure.new(
|
120
|
+
:left => 0,
|
121
|
+
:bottom => 0,
|
122
|
+
:top => @view_height,
|
123
|
+
:right => @view_width
|
124
|
+
)
|
125
|
+
@environment.environmental_factors << enclosure
|
126
|
+
end
|
127
|
+
|
128
|
+
#Keep all objects under a certain speed.
|
129
|
+
@environment.environmental_factors << SpeedLimit.new(@max_speed) if @max_speed
|
130
|
+
|
131
|
+
#Limit population.
|
132
|
+
@environment.environmental_factors << PopulationLimit.new(@max_population) if @max_population
|
133
|
+
|
134
|
+
#Set up a creature generator.
|
135
|
+
@generator = CreatureGenerator.new(@environment)
|
136
|
+
|
137
|
+
#Create thread to update environment.
|
138
|
+
thread = Thread.new do
|
139
|
+
|
140
|
+
begin
|
141
|
+
|
142
|
+
drawing_clock = Clock.new
|
143
|
+
time_per_frame = 1.0 / @fps
|
144
|
+
|
145
|
+
loop do
|
146
|
+
|
147
|
+
@environment.interact
|
148
|
+
|
149
|
+
#Determine how much time is left in this frame.
|
150
|
+
time_left_in_frame = (time_per_frame) - drawing_clock.elapsed_time
|
151
|
+
#Sleep for the remaining time.
|
152
|
+
if time_left_in_frame > 0
|
153
|
+
sleep time_left_in_frame
|
154
|
+
#Skip a frame if things are going too slow.
|
155
|
+
else
|
156
|
+
sleep time_per_frame
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
rescue Exception => exception
|
162
|
+
puts exception, exception.backtrace
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
#Start a network service.
|
169
|
+
if @uri
|
170
|
+
server = EnvironmentServer.new(@environment, @uri)
|
171
|
+
server.start
|
172
|
+
#Disable file system access.
|
173
|
+
$SAFE = 2
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
#Activate the GUI.
|
178
|
+
Gtk.main
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
#Create a view and controls.
|
184
|
+
#Takes a hash with these defaults:
|
185
|
+
# :view_width => @view_width
|
186
|
+
# :view_height => @view_height
|
187
|
+
# :homogeneous => false
|
188
|
+
# :spacing => 0
|
189
|
+
# :expand => false
|
190
|
+
# :fill => false
|
191
|
+
# :padding => 0
|
192
|
+
def create_controls(options = {})
|
193
|
+
|
194
|
+
options = {
|
195
|
+
:view_width => @view_width,
|
196
|
+
:view_height => @view_height,
|
197
|
+
:homogeneous => false,
|
198
|
+
:spacing => 0,
|
199
|
+
:expand => false,
|
200
|
+
:fill => false,
|
201
|
+
:padding => 0,
|
202
|
+
}.merge(options)
|
203
|
+
|
204
|
+
#Create a container for the view and controls.
|
205
|
+
interface = Gtk::HBox.new(options[:homogeneous], options[:spacing])
|
206
|
+
|
207
|
+
#Add view to interface.
|
208
|
+
@view = TrailsView.new(
|
209
|
+
:width => options[:view_width],
|
210
|
+
:height => options[:view_height],
|
211
|
+
:trail_length => 5
|
212
|
+
)
|
213
|
+
drawing_window = Gtk::DrawingArea.new
|
214
|
+
@view.canvas = GTK2Canvas.new(drawing_window)
|
215
|
+
interface.pack_start(drawing_window, options[:expand], options[:fill], options[:padding])
|
216
|
+
|
217
|
+
#When mouse button pressed, record location for use in release event handler.
|
218
|
+
drawing_window.add_events(Gdk::Event::BUTTON_PRESS_MASK)
|
219
|
+
drawing_window.signal_connect("button-press-event") do |canvas, event|
|
220
|
+
@press_location = Location.new(event.x, event.y)
|
221
|
+
end
|
222
|
+
|
223
|
+
#Create a creature on button release.
|
224
|
+
drawing_window.add_events(Gdk::Event::BUTTON_RELEASE_MASK)
|
225
|
+
drawing_window.signal_connect("button-release-event") do |canvas, event|
|
226
|
+
#Ensure the mouse was pressed within the canvas.
|
227
|
+
if @press_location
|
228
|
+
#Create creature at release location.
|
229
|
+
@release_location = Location.new(event.x, event.y)
|
230
|
+
@generator.create_creature(
|
231
|
+
:x => event.x,
|
232
|
+
:y => event.y,
|
233
|
+
:speed => Utility.find_distance(@press_location, @release_location) * 2, #Use distance dragged as speed.
|
234
|
+
:pitch => Utility.find_angle(@press_location, @release_location), #Move in direction of drag.
|
235
|
+
:turn => @turn_flag.active?,
|
236
|
+
:approach => @approach_flag.active?,
|
237
|
+
:flee => @flee_flag.active?,
|
238
|
+
:push => @push_flag.active?,
|
239
|
+
:pull => @pull_flag.active?,
|
240
|
+
:breed => @breed_flag.active?,
|
241
|
+
:eat => @eat_flag.active?
|
242
|
+
)
|
243
|
+
#Nullify press location, as it is no longer applicable.
|
244
|
+
@press_location = nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
#Create a VBox for all controls.
|
249
|
+
control_panel = Gtk::VBox.new(options[:homogeneous], options[:spacing])
|
250
|
+
|
251
|
+
#Create a group for the actions.
|
252
|
+
action_controls = Gtk::VBox.new(options[:homogeneous], options[:spacing])
|
253
|
+
action_controls.pack_start(Gtk::Label.new("Actions"), options[:expand], options[:fill], options[:padding])
|
254
|
+
@turn_flag = Gtk::CheckButton.new("Turn")
|
255
|
+
action_controls.pack_start(@turn_flag, options[:expand], options[:fill], options[:padding])
|
256
|
+
@approach_flag = Gtk::CheckButton.new("Chase")
|
257
|
+
action_controls.pack_start(@approach_flag, options[:expand], options[:fill], options[:padding])
|
258
|
+
@flee_flag = Gtk::CheckButton.new("Flee")
|
259
|
+
action_controls.pack_start(@flee_flag, options[:expand], options[:fill], options[:padding])
|
260
|
+
@push_flag = Gtk::CheckButton.new("Push")
|
261
|
+
action_controls.pack_start(@push_flag, options[:expand], options[:fill], options[:padding])
|
262
|
+
@pull_flag = Gtk::CheckButton.new("Pull")
|
263
|
+
action_controls.pack_start(@pull_flag, options[:expand], options[:fill], options[:padding])
|
264
|
+
@breed_flag = Gtk::CheckButton.new("Breed")
|
265
|
+
action_controls.pack_start(@breed_flag, options[:expand], options[:fill], options[:padding])
|
266
|
+
@eat_flag = Gtk::CheckButton.new("Eat")
|
267
|
+
action_controls.pack_start(@eat_flag, options[:expand], options[:fill], options[:padding])
|
268
|
+
#Add the action controls to the panel.
|
269
|
+
control_panel.pack_start(action_controls, options[:expand], options[:fill], options[:padding])
|
270
|
+
|
271
|
+
#Create a group for environment controls.
|
272
|
+
environment_controls = Gtk::VBox.new(options[:homogeneous], options[:spacing])
|
273
|
+
environment_controls.pack_start(Gtk::Label.new("Length"), options[:expand], options[:fill], options[:padding])
|
274
|
+
@trail_length_slider = Gtk::HScale.new(2, 100, 1)
|
275
|
+
@trail_length_slider.signal_connect("value-changed") {
|
276
|
+
@view.trail_length = @trail_length_slider.value
|
277
|
+
}
|
278
|
+
@trail_length_slider.value = 5
|
279
|
+
environment_controls.pack_start(@trail_length_slider, options[:expand], options[:fill], options[:padding])
|
280
|
+
@trails_flag = Gtk::CheckButton.new("Trails")
|
281
|
+
@trails_flag.signal_connect("toggled") {
|
282
|
+
@view.erase_flag = ! @trails_flag.active?
|
283
|
+
}
|
284
|
+
@trails_flag.active = false
|
285
|
+
environment_controls.pack_start(@trails_flag, options[:expand], options[:fill], options[:padding])
|
286
|
+
@clear_button = Gtk::Button.new("Clear")
|
287
|
+
@clear_button.signal_connect("clicked") {
|
288
|
+
@environment.objects = []
|
289
|
+
}
|
290
|
+
environment_controls.pack_start(@clear_button, options[:expand], options[:fill], options[:padding])
|
291
|
+
#Add the environment controls to the BOTTOM of the panel.
|
292
|
+
control_panel.pack_end(environment_controls, options[:expand], options[:fill], options[:padding])
|
293
|
+
|
294
|
+
#Add the control panel to the interface.
|
295
|
+
interface.pack_start(control_panel, options[:expand], options[:fill], options[:padding])
|
296
|
+
|
297
|
+
interface
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
#Set attributes according to command-line arguments.
|
303
|
+
def process_options(arguments)
|
304
|
+
|
305
|
+
#Set up option parser.
|
306
|
+
options = OptionParser.new
|
307
|
+
|
308
|
+
#Define valid options.
|
309
|
+
options.on("-h", "--help", TrueClass, "Display program help.") {
|
310
|
+
puts options.help
|
311
|
+
exit
|
312
|
+
}
|
313
|
+
options.on(
|
314
|
+
"-m",
|
315
|
+
"--max-population [number]",
|
316
|
+
Integer,
|
317
|
+
"The maximum number of allowed game objects. #{DEFAULT_MAX_POPULATION} by default."
|
318
|
+
) {|value| @max_population = value}
|
319
|
+
options.on(
|
320
|
+
"-s",
|
321
|
+
"--max-speed [number]",
|
322
|
+
Integer,
|
323
|
+
"The fastest an object can go. #{DEFAULT_MAX_SPEED ? DEFAULT_MAX_SPEED : 'No limit'} by default."
|
324
|
+
) {|value| @max_speed = value}
|
325
|
+
options.on(
|
326
|
+
"-n",
|
327
|
+
"--no-enclosure",
|
328
|
+
"Disables the barrier that normally keeps objects on the screen."
|
329
|
+
) {|value| @enclosure = false}
|
330
|
+
options.on(
|
331
|
+
"-u",
|
332
|
+
"--uri [uri]",
|
333
|
+
String,
|
334
|
+
"URI to serve the environment on via dRuby. If not specified, no server will be started."
|
335
|
+
) {|value| @uri = value}
|
336
|
+
options.on(
|
337
|
+
"-f",
|
338
|
+
"--fps [frames]",
|
339
|
+
Integer,
|
340
|
+
"Number of frames to draw per second. #{DEFAULT_FPS} by default."
|
341
|
+
) {|value| @fps = value}
|
342
|
+
options.on(
|
343
|
+
"--view-width [pixels]",
|
344
|
+
Integer,
|
345
|
+
"Window width. #{DEFAULT_VIEW_WIDTH} by default."
|
346
|
+
) {|value| @view_width = value}
|
347
|
+
options.on(
|
348
|
+
"--view-height [pixels]",
|
349
|
+
Integer,
|
350
|
+
"Window height. #{DEFAULT_VIEW_HEIGHT} by default."
|
351
|
+
) {|value| @view_height = value}
|
352
|
+
|
353
|
+
#Parse the options, printing usage if parsing fails.
|
354
|
+
options.parse(arguments) rescue puts "#{$!}\nType '#{$0} --help' for valid options."
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
|
363
|
+
class CreatureGenerator
|
364
|
+
|
365
|
+
#Environment creatures will be added to.
|
366
|
+
attr_accessor :environment
|
367
|
+
#Default required proximity for actions.
|
368
|
+
attr_accessor :default_proximity
|
369
|
+
#Rate of new TurnActions.
|
370
|
+
attr_accessor :turn_rate
|
371
|
+
#Acceleration rate of new ApproachActions.
|
372
|
+
attr_accessor :approach_rate
|
373
|
+
#Acceleration rate of new FleeActions.
|
374
|
+
attr_accessor :flee_rate
|
375
|
+
#Strength of new PullActions.
|
376
|
+
attr_accessor :pull_strength
|
377
|
+
#Strength of new PushActions.
|
378
|
+
attr_accessor :push_strength
|
379
|
+
|
380
|
+
def initialize(environment)
|
381
|
+
|
382
|
+
@environment = environment
|
383
|
+
|
384
|
+
#Set up defaults for attributes.
|
385
|
+
@default_proximity = 200
|
386
|
+
@approach_rate = 200
|
387
|
+
@flee_rate = @approach_rate
|
388
|
+
@push_strength = @approach_rate * 2
|
389
|
+
@pull_strength = @push_strength * 0.75
|
390
|
+
@turn_rate = @approach_rate * 1.1
|
391
|
+
@turn_angle = 90
|
392
|
+
@breed_rate = 10
|
393
|
+
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
#Create a creature and add it to the environment.
|
398
|
+
def create_creature(options = {})
|
399
|
+
|
400
|
+
options = {
|
401
|
+
:x => 0,
|
402
|
+
:y => 0,
|
403
|
+
:speed => 1,
|
404
|
+
:pitch => 0,
|
405
|
+
:action_proximity => @default_proximity,
|
406
|
+
:turn => false,
|
407
|
+
:approach => false,
|
408
|
+
:flee => false,
|
409
|
+
:push => false,
|
410
|
+
:pull => false,
|
411
|
+
:breed => false,
|
412
|
+
:eat => false,
|
413
|
+
}.merge(options)
|
414
|
+
|
415
|
+
#Create a creature.
|
416
|
+
creature = Creature.new(
|
417
|
+
:location => Location.new(options[:x], options[:y]),
|
418
|
+
:vector => Vector.new(options[:speed], options[:pitch]),
|
419
|
+
:size => 5
|
420
|
+
)
|
421
|
+
|
422
|
+
#Set up actions and merge colors according to selected behaviors.
|
423
|
+
color = Color.new(0.25, 0.25, 0.25)
|
424
|
+
if options[:turn]
|
425
|
+
color.blue += 1
|
426
|
+
creature.behaviors << Behavior.new(
|
427
|
+
:actions => [TurnAction.new(@turn_rate, @turn_angle)],
|
428
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 2)]
|
429
|
+
)
|
430
|
+
end
|
431
|
+
if options[:approach]
|
432
|
+
color.red += 1
|
433
|
+
creature.behaviors << create_behavior(
|
434
|
+
:actions => [ApproachAction.new(@approach_rate)],
|
435
|
+
:conditions => [ProximityCondition.new(options[:action_proximity])]
|
436
|
+
)
|
437
|
+
end
|
438
|
+
if options[:flee]
|
439
|
+
color.red += 0.5; color.green += 0.5 #Yellow.
|
440
|
+
creature.behaviors << create_behavior(
|
441
|
+
:actions => [FleeAction.new(@flee_rate)],
|
442
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)]
|
443
|
+
)
|
444
|
+
end
|
445
|
+
if options[:push]
|
446
|
+
color.red += 0.5; color.blue += 0.5 #Purple.
|
447
|
+
creature.behaviors << create_behavior(
|
448
|
+
:actions => [PushAction.new(@push_strength)],
|
449
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)]
|
450
|
+
)
|
451
|
+
end
|
452
|
+
if options[:pull]
|
453
|
+
color.blue += 0.75; color.green += 0.75 #Aqua.
|
454
|
+
creature.behaviors << create_behavior(
|
455
|
+
:actions => [PullAction.new(@pull_strength)],
|
456
|
+
:conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)]
|
457
|
+
)
|
458
|
+
end
|
459
|
+
if options[:breed]
|
460
|
+
color.green -= 0.1 #Make a bit redder.
|
461
|
+
color.blue -= 0.1
|
462
|
+
creature.behaviors << create_behavior(
|
463
|
+
:actions => [BreedAction.new(@environment, @breed_rate)],
|
464
|
+
:conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
|
465
|
+
)
|
466
|
+
end
|
467
|
+
if options[:eat]
|
468
|
+
color.green += 1
|
469
|
+
creature.behaviors << create_behavior(
|
470
|
+
:actions => [EatAction.new(@environment)],
|
471
|
+
:conditions => [
|
472
|
+
CollisionCondition.new, #The default ProximityCondition won't do.
|
473
|
+
StrengthCondition.new #The eater should be as strong or stronger than its dinner.
|
474
|
+
]
|
475
|
+
)
|
476
|
+
end
|
477
|
+
|
478
|
+
creature.color = color
|
479
|
+
|
480
|
+
@environment.objects << creature
|
481
|
+
|
482
|
+
end
|
483
|
+
|
484
|
+
|
485
|
+
def create_behavior(options = {})
|
486
|
+
|
487
|
+
options = {
|
488
|
+
:actions => [],
|
489
|
+
:conditions => [ProximityCondition.new(@default_proximity)],
|
490
|
+
}.merge(options)
|
491
|
+
|
492
|
+
behavior = Behavior.new
|
493
|
+
behavior.actions = options[:actions]
|
494
|
+
behavior.conditions = options[:conditions]
|
495
|
+
behavior
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
|
500
|
+
end
|
501
|
+
|
502
|
+
|
503
|
+
|
504
|
+
begin
|
505
|
+
#Create a server.
|
506
|
+
application = Application.new
|
507
|
+
#Parse the command line.
|
508
|
+
application.process_options(ARGV)
|
509
|
+
#Start the server.
|
510
|
+
application.main
|
511
|
+
rescue => exception
|
512
|
+
#Print error to STDERR and exit with an abnormal status.
|
513
|
+
abort "Error: " + exception.message + exception.backtrace.join("\n")
|
514
|
+
end
|