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/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