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/bin/zyps CHANGED
@@ -20,13 +20,15 @@
20
20
 
21
21
  gems_loaded = false
22
22
  begin
23
- require 'optparse'
23
+ require 'logger'
24
+ require 'wx'
24
25
  require 'zyps'
25
26
  require 'zyps/actions'
26
27
  require 'zyps/conditions'
27
28
  require 'zyps/environmental_factors'
28
29
  require 'zyps/remote'
29
30
  require 'zyps/views/trails'
31
+ require 'zyps/views/canvas/wx'
30
32
  rescue LoadError
31
33
  if gems_loaded == false
32
34
  require 'rubygems'
@@ -41,304 +43,368 @@ end
41
43
  include Zyps
42
44
 
43
45
 
44
- DEFAULT_VIEW_WIDTH = 800
45
- DEFAULT_VIEW_HEIGHT = 600
46
- DEFAULT_MAX_SPEED = 200
47
- DEFAULT_MAX_POPULATION = 100
48
- DEFAULT_FPS = 60
46
+ LOG_HANDLE = STDOUT
47
+ LOG_LEVEL = Logger::WARN
49
48
 
50
49
 
51
- class Application
50
+ #The Zyps GUI.
51
+ class Application < Wx::App
52
52
 
53
- #Port to open service on.
54
- attr_accessor :uri
55
- #Maximum allowed number of objects.
56
- attr_accessor :max_population
57
- #View dimensions.
58
- attr_accessor :view_width, :view_height
59
-
60
- #Create app window, game environment, and view.
61
- #Set up default values.
62
- def initialize(
63
- uri = nil,
64
- view_width = DEFAULT_VIEW_WIDTH,
65
- view_height = DEFAULT_VIEW_HEIGHT,
66
- fps = DEFAULT_FPS,
67
- max_population = DEFAULT_MAX_POPULATION,
68
- max_speed = DEFAULT_MAX_SPEED,
69
- enclosure = true
70
- )
71
-
72
- @uri, @view_width, @view_height, @fps, @max_population, @max_speed, @enclosure =
73
- uri, view_width, view_height, fps, max_population, max_speed, enclosure
74
-
75
- #Create a window, and set GTK up to quit when it is closed.
76
- window = Gtk::Window.new
77
- window.resizable = false
78
- window.signal_connect("delete_event") {false}
79
- window.signal_connect("destroy") {Gtk.main_quit}
80
-
81
- #Create environment.
82
- @environment = Environment.new
83
-
84
- #Set up controls.
85
- #Also initializes @view.
86
- window.add(create_controls)
53
+
54
+ #Takes a hash with these keys and defaults:
55
+ # :width => 800
56
+ # :height => 600
57
+ # :max_speed => 200
58
+ # :max_population => 100
59
+ # :fps => 60
60
+ # :enclosure => true
61
+ # :uri => nil
62
+ def initialize(options = {})
87
63
 
88
- #Show all widgets.
89
- window.show_all
90
-
91
- #Point view at environment.
92
- @environment.add_observer(@view)
93
-
94
- end
64
+ super()
95
65
 
66
+ @log = Logger.new(LOG_HANDLE)
67
+ @log.level = LOG_LEVEL
68
+ @log.progname = self
69
+
70
+ @log.debug "Load options and merge with defaults."
71
+ @options = Configuration.load('options')
72
+ @options = {
73
+ :width => 800,
74
+ :height => 600,
75
+ :max_speed => 200,
76
+ :max_population => 50,
77
+ :fps => 30,
78
+ :enclosure => true,
79
+ :uri => nil,
80
+ }.merge(options)
81
+ @log.debug "options: #{@options.inspect}"
96
82
 
97
- def main
83
+ end
98
84
 
99
- #Keep objects on screen.
100
- if @enclosure
101
- enclosure = Enclosure.new()
102
- enclosure.left = 0
103
- enclosure.bottom = 0
104
- enclosure.top = @view_height
105
- enclosure.right = @view_width
106
- @environment.environmental_factors << enclosure
107
- end
108
-
109
- #Keep all objects under a certain speed.
110
- @environment.environmental_factors << SpeedLimit.new(@max_speed) if @max_speed
111
-
112
- #Limit population.
113
- @environment.environmental_factors << PopulationLimit.new(@environment, @max_population) if @max_population
114
-
115
- #Set up a creature generator.
116
- @generator = CreatureGenerator.new(@environment)
117
85
 
118
- #Create thread to update environment.
119
- thread = Thread.new do
120
-
121
- begin
122
-
123
- drawing_clock = Clock.new
124
- time_per_frame = 1.0 / @fps
125
-
126
- loop do
127
-
128
- @environment.interact
129
-
130
- #Determine how much time is left in this frame.
131
- time_left_in_frame = (time_per_frame) - drawing_clock.elapsed_time
132
- #Sleep for the remaining time.
133
- if time_left_in_frame > 0
134
- sleep time_left_in_frame
135
- #Skip a frame if things are going too slow.
136
- else
137
- sleep time_per_frame
138
- end
139
-
140
- end
141
-
142
- rescue Exception => exception
143
- puts exception, exception.backtrace
144
- end
145
-
86
+ #Create GUI objects.
87
+ def on_init
88
+
89
+ @log.debug "Create the main window."
90
+ frame = Wx::Frame.new(
91
+ nil, #No parent.
92
+ :size => [@options[:width], @options[:height]],
93
+ :title => "Zyps"
94
+ )
95
+ @log.debug "All controls in frame should be resized when frame is."
96
+ frame.sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
97
+ @log.debug "Application should exit when frame is closed."
98
+ frame.evt_close {on_exit}
99
+
100
+ @log.debug "Create a view and canvas."
101
+ @view = TrailsView.new(
102
+ :width => @options[:width],
103
+ :height => @options[:height],
104
+ :canvas => WxCanvas.new(frame)
105
+ )
106
+ frame.sizer.add(@view.canvas.drawing_area, 1, Wx::GROW)
107
+ @log.debug "Set up mouse click/drag handlers for canvas."
108
+ @press_location = nil
109
+ @view.canvas.drawing_area.evt_left_down {|event| on_mouse_press(event)}
110
+ @view.canvas.drawing_area.evt_left_up {|event| on_mouse_release(event)}
111
+ @log.debug "Resize view and enclosure when canvas changes size."
112
+ @view.canvas.drawing_area.evt_size {|event| on_view_resize(event)}
113
+
114
+ @log.debug "Create an interface."
115
+ @controls = ControlPanel.new(frame)
116
+ @controls.clear_button.evt_button(@controls.clear_button.get_id) do |event|
117
+ @environment.objects = []
118
+ end
119
+ @controls.length_slider.evt_slider(@controls.length_slider.get_id) do |event|
120
+ @view.trail_length = @controls.length_slider.value
121
+ end
122
+ @controls.trails_flag.evt_checkbox(@controls.trails_flag.get_id) do |event|
123
+ @view.erase_flag = ! @controls.trails_flag.is_checked
146
124
  end
125
+ frame.sizer.add(@controls, 0, Wx::GROW)
147
126
 
127
+ @log.debug "Create an environment, and link it to the view."
128
+ @environment = Environment.new
129
+ @environment.add_observer(@view)
148
130
 
149
- #Start a network service.
150
- if @uri
151
- server = EnvironmentServer.new(@environment, @uri)
152
- server.start
153
- #Disable file system access.
154
- $SAFE = 2
131
+ @log.debug "Set up environmental factors."
132
+ if @options[:enclosure]
133
+ @enclosure = Enclosure.new(
134
+ :left => 0,
135
+ :bottom => 0,
136
+ :top => @view.canvas.drawing_area.size.height,
137
+ :right => @view.canvas.drawing_area.size.width
138
+ )
139
+ @environment.environmental_factors << @enclosure
155
140
  end
156
-
157
-
158
- #Activate the GUI.
159
- Gtk.main
141
+ @environment.environmental_factors << SpeedLimit.new(@options[:max_speed]) if @options[:max_speed]
142
+ @environment.environmental_factors << PopulationLimit.new(@options[:max_population]) if @options[:max_population]
143
+
144
+ @log.debug "Set up a creature generator."
145
+ @generator = CreatureGenerator.new(:environment => @environment)
146
+
147
+ @log.debug "Set up a timer to update the environment."
148
+ milliseconds_per_frame = (1.0 / @options[:fps] * 1000).to_int
149
+ @log.debug "Timer will fire every #{milliseconds_per_frame} milliseconds."
150
+ timer_id = Wx::ID_HIGHEST + 1
151
+ timer = Wx::Timer.new(self, timer_id)
152
+ evt_timer(timer_id) do
153
+ @environment.interact
154
+ #Keeps dead objects from accumulating and causing hiccups later.
155
+ GC.start
156
+ end
157
+ timer.start(milliseconds_per_frame)
160
158
 
159
+ @log.debug "Display GUI."
160
+ frame.show
161
+
161
162
  end
163
+
162
164
 
165
+ #Save config and shut down.
166
+ def on_exit
167
+ Configuration.save('options', @options)
168
+ exit
169
+ end
163
170
 
164
- #Create a view and controls.
165
- def create_controls(view_width = @view_width, view_height = @view_height, homogeneous = false, spacing = 0, expand = false, fill = false, padding = 0)
166
171
 
167
- #Create a container for the view and controls.
168
- interface = Gtk::HBox.new(homogeneous, spacing)
169
-
170
- #Add view to interface.
171
- @view = TrailsView.new(view_width, view_height)
172
- interface.pack_start(@view.canvas, expand, fill, padding)
173
-
174
- #When mouse button pressed, record location for use in release event handler.
175
- @view.canvas.add_events(Gdk::Event::BUTTON_PRESS_MASK)
176
- @view.canvas.signal_connect("button-press-event") do |canvas, event|
177
- @press_location = Location.new(event.x, event.y)
178
- end
179
-
180
- #Create a creature on button release.
181
- @view.canvas.add_events(Gdk::Event::BUTTON_RELEASE_MASK)
182
- @view.canvas.signal_connect("button-release-event") do |canvas, event|
172
+ #When mouse button pressed, record location for use in release event handler.
173
+ def on_mouse_press(event)
174
+ @log.debug "Mouse pressed at #{event.x}, #{event.y}."
175
+ @press_location = Location.new(event.x, event.y)
176
+ end
177
+
178
+
179
+ #Create a creature on button release.
180
+ def on_mouse_release(event)
181
+ @log.debug "Mouse released at #{event.x}, #{event.y}."
182
+ #Ensure the mouse was pressed within the canvas.
183
+ if @press_location
184
+ #Create creature at release location.
183
185
  @release_location = Location.new(event.x, event.y)
184
- @generator.create_creature(
186
+ @log.debug "Release location: #{@release_location.inspect}"
187
+ @environment.objects << @generator.create_creature(
185
188
  :x => event.x,
186
189
  :y => event.y,
187
190
  :speed => Utility.find_distance(@press_location, @release_location) * 2, #Use distance dragged as speed.
188
191
  :pitch => Utility.find_angle(@press_location, @release_location), #Move in direction of drag.
189
- :accelerate => @accelerate_flag.active?,
190
- :turn => @turn_flag.active?,
191
- :approach => @approach_flag.active?,
192
- :flee => @flee_flag.active?,
193
- :push => @push_flag.active?,
194
- :pull => @pull_flag.active?,
195
- :breed => @breed_flag.active?,
196
- :eat => @eat_flag.active?
192
+ :turn => @controls.turn_flag.is_checked,
193
+ :approach => @controls.approach_flag.is_checked,
194
+ :flee => @controls.flee_flag.is_checked,
195
+ :push => @controls.push_flag.is_checked,
196
+ :pull => @controls.pull_flag.is_checked,
197
+ :breed => @controls.breed_flag.is_checked,
198
+ :eat => @controls.eat_flag.is_checked
197
199
  )
200
+ #Nullify press location, as it is no longer applicable.
201
+ @press_location = nil
198
202
  end
199
-
200
- #Create a VBox for all controls.
201
- control_panel = Gtk::VBox.new(homogeneous, spacing)
202
-
203
- #Create a group for the actions.
204
- action_controls = Gtk::VBox.new(homogeneous, spacing)
205
- action_controls.pack_start(Gtk::Label.new("Actions"), expand, fill, padding)
206
- @accelerate_flag = Gtk::CheckButton.new("Accelerate")
207
- action_controls.pack_start(@accelerate_flag, expand, fill, padding)
208
- @turn_flag = Gtk::CheckButton.new("Turn")
209
- action_controls.pack_start(@turn_flag, expand, fill, padding)
210
- @approach_flag = Gtk::CheckButton.new("Approach")
211
- action_controls.pack_start(@approach_flag, expand, fill, padding)
212
- @flee_flag = Gtk::CheckButton.new("Flee")
213
- action_controls.pack_start(@flee_flag, expand, fill, padding)
214
- @push_flag = Gtk::CheckButton.new("Push")
215
- action_controls.pack_start(@push_flag, expand, fill, padding)
216
- @pull_flag = Gtk::CheckButton.new("Pull")
217
- action_controls.pack_start(@pull_flag, expand, fill, padding)
218
- @breed_flag = Gtk::CheckButton.new("Breed")
219
- action_controls.pack_start(@breed_flag, expand, fill, padding)
220
- @eat_flag = Gtk::CheckButton.new("Eat")
221
- action_controls.pack_start(@eat_flag, expand, fill, padding)
222
- #Add the action controls to the panel.
223
- control_panel.pack_start(action_controls, expand, fill, padding)
224
-
225
- #Create a group for environment controls.
226
- environment_controls = Gtk::VBox.new(homogeneous, spacing)
227
- environment_controls.pack_start(Gtk::Label.new("Environment"), expand, fill, padding)
228
- @clear_button = Gtk::Button.new("Clear")
229
- @clear_button.signal_connect("clicked") {
230
- @environment.objects = []
231
- }
232
- environment_controls.pack_start(@clear_button, expand, fill, padding)
233
- #Add the environment controls to the panel.
234
- control_panel.pack_start(environment_controls, expand, fill, padding)
235
-
236
- #Add the control panel to the interface.
237
- interface.pack_start(control_panel, expand, fill, padding)
238
-
239
- interface
240
-
241
203
  end
204
+
205
+
206
+ #Resize objects that are dependent on the view size.
207
+ def on_view_resize(event)
208
+ @view.height = @view.canvas.drawing_area.size.height
209
+ @view.width = @view.canvas.drawing_area.size.width
210
+ @enclosure.top = @view.canvas.drawing_area.size.height
211
+ @enclosure.right = @view.canvas.drawing_area.size.width
212
+ end
213
+
214
+
215
+ end
216
+
242
217
 
243
218
 
244
- #Set attributes according to command-line arguments.
245
- def process_options(arguments)
219
+ #TODO
220
+ module Configuration
221
+ def Configuration.load(name)
222
+ {}
223
+ end
224
+ def Configuration.save(name, options)
225
+ end
226
+ end
227
+
228
+
229
+
230
+ #Controls for manipulating the environment.
231
+ class ControlPanel < Wx::Panel
232
+
233
+
234
+ attr_accessor :turn_flag
235
+ attr_accessor :approach_flag
236
+ attr_accessor :flee_flag
237
+ attr_accessor :push_flag
238
+ attr_accessor :pull_flag
239
+ attr_accessor :breed_flag
240
+ attr_accessor :eat_flag
241
+ attr_accessor :length_slider
242
+ attr_accessor :trails_flag
243
+ attr_accessor :clear_button
244
+
245
+
246
+ def initialize(parent)
246
247
 
247
- #Set up option parser.
248
- options = OptionParser.new
249
-
250
- #Define valid options.
251
- options.on("-h", "--help", TrueClass, "Display program help.") {
252
- puts options.help
253
- exit
254
- }
255
- options.on(
256
- "-m",
257
- "--max-population [number]",
258
- Integer,
259
- "The maximum number of allowed game objects. #{DEFAULT_MAX_POPULATION} by default."
260
- ) {|value| @max_population = value}
261
- options.on(
262
- "-s",
263
- "--max-speed [number]",
264
- Integer,
265
- "The fastest an object can go. #{DEFAULT_MAX_SPEED ? DEFAULT_MAX_SPEED : 'No limit'} by default."
266
- ) {|value| @max_speed = value}
267
- options.on(
268
- "-n",
269
- "--no-enclosure",
270
- "Disables the barrier that normally keeps objects on the screen."
271
- ) {|value| @enclosure = false}
272
- options.on(
273
- "-u",
274
- "--uri [uri]",
275
- String,
276
- "dRuby URI to run the server on. If not defined, one will be selected and printed to STDOUT."
277
- ) {|value| @uri = value}
278
- options.on(
279
- "-f",
280
- "--fps [frames]",
281
- Integer,
282
- "Number of frames to draw per second. #{DEFAULT_FPS} by default."
283
- ) {|value| @fps = value}
284
- options.on(
285
- "--view-width [pixels]",
286
- Integer,
287
- "Window width. #{DEFAULT_VIEW_WIDTH} by default."
288
- ) {|value| @view_width = value}
289
- options.on(
290
- "--view-height [pixels]",
291
- Integer,
292
- "Window height. #{DEFAULT_VIEW_HEIGHT} by default."
293
- ) {|value| @view_height = value}
294
-
295
- #Parse the options, printing usage if parsing fails.
296
- options.parse(arguments) rescue puts "#{$!}\nType '#{$0} --help' for valid options."
248
+ super(parent)
249
+ self.sizer = Wx::BoxSizer.new(Wx::VERTICAL)
250
+
251
+ action_controls = add_panel(self)
252
+ add_label(action_controls, :label => "Actions")
253
+ @turn_flag = add_check_box(action_controls, :label => "Turn")
254
+ @approach_flag = add_check_box(action_controls, :label => "Chase")
255
+ @flee_flag = add_check_box(action_controls, :label => "Flee")
256
+ @push_flag = add_check_box(action_controls, :label => "Push")
257
+ @pull_flag = add_check_box(action_controls, :label => "Pull")
258
+ @breed_flag = add_check_box(action_controls, :label => "Breed")
259
+ @eat_flag = add_check_box(action_controls, :label => "Eat")
260
+
261
+ display_controls = add_panel(self)
262
+ add_label(display_controls, :label => "Length")
263
+ @length_slider = add_slider(display_controls, :min_value => 2, :max_value => 100)
264
+ @length_slider.value = 5
265
+ @trails_flag = add_check_box(display_controls, :label => "Trails")
266
+
267
+ environment_controls = add_panel(self)
268
+ add_label(environment_controls, :label => "Environment")
269
+ @clear_button = add_button(environment_controls, :label => "Clear")
297
270
 
298
271
  end
299
272
 
300
273
 
274
+ private
275
+
276
+ #Other methods will call this with their particular type of control to add.
277
+ #Adds control to parent's sizer if it has one.
278
+ #Returns the control.
279
+ def add_control(control)
280
+ begin
281
+ control.parent.sizer.add(control, 0, Wx::ALL, 3)
282
+ rescue RuntimeError
283
+ #Parent had no sizer; take no action.
284
+ #Can't find another way to test for presence of a sizer as of wxRuby 1.9.2.
285
+ end
286
+ #Return the object.
287
+ control
288
+ end
289
+
290
+ #Create a check box with the given label, and add it to the given container.
291
+ def add_check_box(parent, options = {})
292
+ add_control(Wx::CheckBox.new(parent, options))
293
+ end
294
+
295
+ #Create a slider control with the given options, and add it to the given container.
296
+ def add_slider(parent, options = {})
297
+ add_control(Wx::Slider.new(parent, options))
298
+ end
299
+
300
+ #Create a button with the given options, and add it to the given container.
301
+ def add_button(parent, options = {})
302
+ add_control(Wx::Button.new(parent, options))
303
+ end
304
+
305
+ #Create the given label, and add it to the given container.
306
+ def add_label(parent, options = {})
307
+ control = Wx::StaticText.new(parent, options)
308
+ control.parent.sizer.add(control, 0, Wx::ALIGN_CENTER_HORIZONTAL|Wx::ALL, 3)
309
+ control
310
+ end
311
+
312
+ #Create a panel, and add it to the given container.
313
+ #Takes a hash with the following keys and defaults, in addition to those taken by Wx::Panel.new():
314
+ # :sizer => Wx::BoxSizer.new(Wx::VERTICAL)
315
+ def add_panel(parent, options = {})
316
+ options = {
317
+ :sizer => Wx::BoxSizer.new(Wx::VERTICAL),
318
+ }.merge(options)
319
+ panel = Wx::Panel.new(parent, options)
320
+ panel.sizer = options[:sizer]
321
+ panel.parent.sizer.add(panel, 1, Wx::GROW|Wx::ALL, 3)
322
+ panel
323
+ end
324
+
325
+
301
326
  end
302
327
 
303
328
 
304
329
 
330
+ #Creates Creature objects.
305
331
  class CreatureGenerator
306
-
307
- #Environment creatures will be added to.
332
+
333
+
334
+ #Environment creatures will be placed in.
308
335
  attr_accessor :environment
336
+ #Default size of new creatures.
337
+ attr_accessor :default_size
309
338
  #Default required proximity for actions.
310
- attr_accessor :action_proximity
311
- #Speed of new AccelerateActions.
312
- attr_accessor :acceleration_rate
339
+ attr_accessor :default_proximity
313
340
  #Rate of new TurnActions.
314
341
  attr_accessor :turn_rate
315
- #Turn rate of new ApproachActions.
316
- attr_accessor :approach_turn_rate
317
- #Turn rate of new FleeActions.
318
- attr_accessor :flee_turn_rate
342
+ #Acceleration rate of new ApproachActions.
343
+ attr_accessor :approach_rate
344
+ #Acceleration rate of new FleeActions.
345
+ attr_accessor :flee_rate
319
346
  #Strength of new PullActions.
320
347
  attr_accessor :pull_strength
321
348
  #Strength of new PushActions.
322
349
  attr_accessor :push_strength
350
+
323
351
 
324
- def initialize(environment)
352
+ #Takes a hash with these keys and defaults:
353
+ # :environment => nil
354
+ # :default_size => 5
355
+ # :default_proximity => 200
356
+ # :approach_rate => 200
357
+ # :flee_rate => :approach_rate
358
+ # :push_strength => :approach_rate * 2
359
+ # :pull_strength => :push_strength * 0.75
360
+ # :turn_rate => :approach_rate * 1.1
361
+ # :turn_angle => 90
362
+ # :breed_rate => 10
363
+ def initialize(options = {})
325
364
 
326
- @environment = environment
327
-
328
- #Set up defaults for attributes.
329
- @action_proximity = 200
330
- @acceleration_rate = 1
331
- @turn_rate = 90
332
- @approach_turn_rate = 720
333
- @flee_turn_rate = @approach_turn_rate
334
- @pull_strength = 100
335
- @push_strength = @pull_strength
336
- @breed_rate = 10
365
+ #Set up logger.
366
+ @log = Logger.new(LOG_HANDLE)
367
+ @log.level = LOG_LEVEL
368
+ @log.progname = self
369
+
370
+ options = {
371
+ :default_size => 5,
372
+ :default_proximity => 200,
373
+ :approach_rate => 200,
374
+ :turn_angle => 90,
375
+ :breed_rate => 10,
376
+ }.merge(options)
377
+ @log.debug "options: #{@options.inspect}"
378
+
379
+ @environment = options[:environment]
380
+ @default_size = options[:default_size]
381
+ @default_proximity = options[:default_proximity]
382
+ @approach_rate = options[:approach_rate]
383
+ @turn_angle = options[:turn_angle]
384
+ @breed_rate = options[:breed_rate]
385
+ @flee_rate = options[:flee_rate] || @approach_rate * 2
386
+ @push_strength = options [:push_strength] || @approach_rate * 2
387
+ @pull_strength = options[:pull_strength] || @push_strength * 0.75
388
+ @turn_rate = options[:turn_rate] || @approach_rate * 1.1
337
389
 
338
390
  end
339
-
340
-
341
- #Create a creature and add it to the environment.
391
+
392
+
393
+ #Create a creature with the given attributes and behaviors.
394
+ #Takes a hash with these keys and defaults:
395
+ # :x => 0,
396
+ # :y => 0,
397
+ # :speed => 1,
398
+ # :pitch => 0,
399
+ # :size => @default_size,
400
+ # :action_proximity => @default_proximity,
401
+ # :turn => false,
402
+ # :approach => false,
403
+ # :flee => false,
404
+ # :push => false,
405
+ # :pull => false,
406
+ # :breed => false,
407
+ # :eat => false,
342
408
  def create_creature(options = {})
343
409
 
344
410
  options = {
@@ -346,8 +412,8 @@ class CreatureGenerator
346
412
  :y => 0,
347
413
  :speed => 1,
348
414
  :pitch => 0,
349
- :action_proximity => @action_proximity,
350
- :accelerate => false,
415
+ :size => @default_size,
416
+ :action_proximity => @default_proximity,
351
417
  :turn => false,
352
418
  :approach => false,
353
419
  :flee => false,
@@ -356,79 +422,76 @@ class CreatureGenerator
356
422
  :breed => false,
357
423
  :eat => false,
358
424
  }.merge(options)
425
+ @log.debug "options: #{@options.inspect}"
359
426
 
360
427
  #Create a creature.
361
428
  creature = Creature.new(
362
- '',
363
- Location.new(options[:x], options[:y]),
364
- Color.new,
365
- Vector.new(options[:speed], options[:pitch]),
366
- 0,
367
- 5
429
+ :location => Location.new(options[:x], options[:y]),
430
+ :vector => Vector.new(options[:speed], options[:pitch]),
431
+ :size => options[:size]
368
432
  )
369
433
 
370
434
  #Set up actions and merge colors according to selected behaviors.
371
435
  color = Color.new(0.25, 0.25, 0.25)
372
- if options[:accelerate]
373
- color.red += 0.25
374
- color.green += 0.25
375
- color.blue += 0.25
376
- creature.behaviors << create_behavior(:actions => [AccelerateAction.new(@acceleration_rate)])
377
- end
378
436
  if options[:turn]
379
437
  color.blue += 1
380
- creature.behaviors << create_behavior(:actions => [TurnAction.new(@turn_rate)])
438
+ creature.behaviors << Behavior.new(
439
+ :actions => [TurnAction.new(@turn_rate, @turn_angle)],
440
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 2)]
441
+ )
381
442
  end
382
443
  if options[:approach]
383
444
  color.red += 1
384
- creature.behaviors << create_behavior(:actions => [ApproachAction.new(@approach_turn_rate, creature.vector)])
445
+ creature.behaviors << Behavior.new(
446
+ :actions => [ApproachAction.new(@approach_rate)],
447
+ :conditions => [ProximityCondition.new(options[:action_proximity])]
448
+ )
385
449
  end
386
450
  if options[:flee]
387
451
  color.red += 0.5; color.green += 0.5 #Yellow.
388
- creature.behaviors << create_behavior(:actions => [FleeAction.new(@flee_turn_rate, creature.vector)])
452
+ creature.behaviors << Behavior.new(
453
+ :actions => [FleeAction.new(@flee_rate)],
454
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.5)]
455
+ )
389
456
  end
390
457
  if options[:push]
391
458
  color.red += 0.5; color.blue += 0.5 #Purple.
392
- creature.behaviors << create_behavior(:actions => [PushAction.new(@push_strength)])
459
+ creature.behaviors << Behavior.new(
460
+ :actions => [PushAction.new(@push_strength)],
461
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.25)]
462
+ )
393
463
  end
394
464
  if options[:pull]
395
465
  color.blue += 0.75; color.green += 0.75 #Aqua.
396
- creature.behaviors << create_behavior(:actions => [PullAction.new(@pull_strength)])
466
+ creature.behaviors << Behavior.new(
467
+ :actions => [PullAction.new(@pull_strength)],
468
+ :conditions => [ProximityCondition.new(options[:action_proximity] * 0.75)]
469
+ )
397
470
  end
398
471
  if options[:breed]
399
472
  color.green -= 0.1 #Make a bit redder.
400
473
  color.blue -= 0.1
401
- creature.behaviors << create_behavior(
474
+ creature.behaviors << Behavior.new(
402
475
  :actions => [BreedAction.new(@environment, @breed_rate)],
403
476
  :conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
404
477
  )
405
478
  end
406
479
  if options[:eat]
407
480
  color.green += 1
408
- creature.behaviors << create_behavior(
481
+ creature.behaviors << Behavior.new(
409
482
  :actions => [EatAction.new(@environment)],
410
- :conditions => [CollisionCondition.new] #The default ProximityCondition won't do.
483
+ :conditions => [
484
+ CollisionCondition.new, #The default ProximityCondition won't do.
485
+ StrengthCondition.new #The eater should be as strong or stronger than its dinner.
486
+ ]
411
487
  )
412
488
  end
413
489
 
414
490
  creature.color = color
415
491
 
416
- @environment.objects << creature
492
+ @log.debug "Created creature: #{creature.inspect}"
417
493
 
418
- end
419
-
420
-
421
- def create_behavior(options = {})
422
-
423
- options = {
424
- :actions => [],
425
- :conditions => [ProximityCondition.new(@action_proximity)],
426
- }.merge(options)
427
-
428
- behavior = Behavior.new
429
- behavior.actions = options[:actions]
430
- behavior.conditions = options[:conditions]
431
- behavior
494
+ creature
432
495
 
433
496
  end
434
497
 
@@ -436,15 +499,11 @@ class CreatureGenerator
436
499
  end
437
500
 
438
501
 
439
-
440
502
  begin
441
- #Create a server.
442
- application = Application.new
443
- #Parse the command line.
444
- application.process_options(ARGV)
445
- #Start the server.
446
- application.main
447
- rescue => exception
503
+ Application.new.main_loop
504
+ rescue SystemExit
505
+ #No action.
506
+ rescue Exception => exception
448
507
  #Print error to STDERR and exit with an abnormal status.
449
- abort "Error: " + exception.message + exception.backtrace.join("\n")
508
+ abort "Error: " + exception.message
450
509
  end