tank_island 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +58 -0
- data/Rakefile +2 -0
- data/bin/tank_island +30 -0
- data/lib/entities/box.rb +28 -0
- data/lib/entities/bullet.rb +26 -0
- data/lib/entities/camera.rb +113 -0
- data/lib/entities/components/ai/gun.rb +114 -0
- data/lib/entities/components/ai/tank_chasing_state.rb +30 -0
- data/lib/entities/components/ai/tank_fighting_state.rb +47 -0
- data/lib/entities/components/ai/tank_fleeing_state.rb +50 -0
- data/lib/entities/components/ai/tank_motion_fsm.rb +102 -0
- data/lib/entities/components/ai/tank_motion_state.rb +84 -0
- data/lib/entities/components/ai/tank_navigating_state.rb +34 -0
- data/lib/entities/components/ai/tank_roaming_state.rb +83 -0
- data/lib/entities/components/ai/tank_stuck_state.rb +45 -0
- data/lib/entities/components/ai/vision.rb +109 -0
- data/lib/entities/components/ai_input.rb +70 -0
- data/lib/entities/components/box_graphics.rb +39 -0
- data/lib/entities/components/bullet_graphics.rb +13 -0
- data/lib/entities/components/bullet_physics.rb +65 -0
- data/lib/entities/components/bullet_sounds.rb +15 -0
- data/lib/entities/components/component.rb +32 -0
- data/lib/entities/components/damage_graphics.rb +20 -0
- data/lib/entities/components/explosion_graphics.rb +43 -0
- data/lib/entities/components/explosion_sounds.rb +16 -0
- data/lib/entities/components/health.rb +87 -0
- data/lib/entities/components/player_input.rb +100 -0
- data/lib/entities/components/player_sounds.rb +16 -0
- data/lib/entities/components/powerup_graphics.rb +22 -0
- data/lib/entities/components/powerup_sounds.rb +15 -0
- data/lib/entities/components/tank_graphics.rb +46 -0
- data/lib/entities/components/tank_health.rb +32 -0
- data/lib/entities/components/tank_physics.rb +179 -0
- data/lib/entities/components/tank_sounds.rb +43 -0
- data/lib/entities/components/tree_graphics.rb +69 -0
- data/lib/entities/damage.rb +26 -0
- data/lib/entities/explosion.rb +34 -0
- data/lib/entities/game_object.rb +54 -0
- data/lib/entities/hud.rb +79 -0
- data/lib/entities/map.rb +183 -0
- data/lib/entities/object_pool.rb +59 -0
- data/lib/entities/powerups/fire_rate_powerup.rb +14 -0
- data/lib/entities/powerups/health_powerup.rb +12 -0
- data/lib/entities/powerups/powerup.rb +35 -0
- data/lib/entities/powerups/powerup_respawn_queue.rb +23 -0
- data/lib/entities/powerups/repair_powerup.rb +14 -0
- data/lib/entities/powerups/tank_speed_powerup.rb +14 -0
- data/lib/entities/radar.rb +62 -0
- data/lib/entities/score_display.rb +35 -0
- data/lib/entities/tank.rb +64 -0
- data/lib/entities/tree.rb +18 -0
- data/lib/game_states/demo_state.rb +49 -0
- data/lib/game_states/game_state.rb +27 -0
- data/lib/game_states/menu_state.rb +60 -0
- data/lib/game_states/pause_state.rb +61 -0
- data/lib/game_states/play_state.rb +119 -0
- data/lib/misc/axis_aligned_bounding_box.rb +33 -0
- data/lib/misc/game_window.rb +30 -0
- data/lib/misc/names.rb +13 -0
- data/lib/misc/quad_tree.rb +91 -0
- data/lib/misc/stats.rb +55 -0
- data/lib/misc/stereo_sample.rb +96 -0
- data/lib/misc/utils.rb +145 -0
- data/media/armalite_rifle.ttf +0 -0
- data/media/boxes_barrels.json +60 -0
- data/media/boxes_barrels.png +0 -0
- data/media/bullet.png +0 -0
- data/media/c_dot.png +0 -0
- data/media/country_field.png +0 -0
- data/media/crash.ogg +0 -0
- data/media/damage1.png +0 -0
- data/media/damage2.png +0 -0
- data/media/damage3.png +0 -0
- data/media/damage4.png +0 -0
- data/media/decor.json +516 -0
- data/media/decor.png +0 -0
- data/media/decor.psd +0 -0
- data/media/explosion.mp3 +0 -0
- data/media/explosion.png +0 -0
- data/media/fire.mp3 +0 -0
- data/media/ground.json +492 -0
- data/media/ground.png +0 -0
- data/media/ground_units.json +900 -0
- data/media/ground_units.png +0 -0
- data/media/menu_music.mp3 +0 -0
- data/media/metal_interaction2.wav +0 -0
- data/media/names.txt +279 -0
- data/media/pickups.json +68 -0
- data/media/pickups.png +0 -0
- data/media/powerup.mp3 +0 -0
- data/media/respawn.wav +0 -0
- data/media/tank_driving.mp3 +0 -0
- data/media/top_secret.ttf +0 -0
- data/media/trees.png +0 -0
- data/media/trees_packed.json +388 -0
- data/media/trees_packed.png +0 -0
- data/media/water.png +0 -0
- data/spec/misc/aabb_spec.rb +85 -0
- data/spec/misc/quad_tree_spec.rb +137 -0
- data/tank_island.gemspec +29 -0
- metadata +223 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d532027c65a76a3a675c7bddbd7ed6699bf74880
|
4
|
+
data.tar.gz: 90459a44a1e0e4f91796ac0188e54f75ac64277a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd1d1bd1afed378b9655e77d6b7ee5972d7c37aa0e904cecdbee7f2c35b93075c145b4ef37ef5241026f994da48e98149df7f925c9538fed83eb336c4df1d657
|
7
|
+
data.tar.gz: b85eed9ea5b0fd7e7c9ba709b502a3e1a489c92399bd0f5daea7f2c47202f5be8b09f71a537edc8bf8ee3b6d6f630390836cde81a5ac54df31869f631fe8811b
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.3
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tomas Varaneckas
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Tank Island
|
2
|
+
|
3
|
+
Tank Island is an open source 2D top down shooter game that was created with Ruby using
|
4
|
+
[Gosu](http://www.libgosu.org) game development library while writing
|
5
|
+
[this book](https://leanpub.com/developing-games-with-ruby/read).
|
6
|
+
|
7
|
+
## Screenshots
|
8
|
+
|
9
|
+

|
10
|
+
|
11
|
+

|
12
|
+
|
13
|
+
[Gameplay video on YouTube](https://www.youtube.com/watch?v=PpbnCUBrXXU)
|
14
|
+
|
15
|
+
## Book: Developing Games With Ruby
|
16
|
+
|
17
|
+
Complete process of building this game is described step by step in this free to read book:
|
18
|
+
|
19
|
+
[Developing Games With Ruby](https://leanpub.com/developing-games-with-ruby/read)
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
To install it, run
|
24
|
+
|
25
|
+
$ gem install tank_island
|
26
|
+
|
27
|
+
On Mac OSX you will have to `brew install imagemagick` to make it work.
|
28
|
+
|
29
|
+
## Starting the game
|
30
|
+
|
31
|
+
There are several ways to start the game.
|
32
|
+
|
33
|
+
### Running in 800x600 window mode
|
34
|
+
|
35
|
+
$ tank_island
|
36
|
+
|
37
|
+
### Running with custom resolution
|
38
|
+
|
39
|
+
$ w=1600 h=1200 tank_island
|
40
|
+
|
41
|
+
### Running full screen with custom resolution
|
42
|
+
|
43
|
+
$ fs=1 w=1200 h=800 tank_island
|
44
|
+
|
45
|
+
## Controls
|
46
|
+
|
47
|
+
### Gameplay
|
48
|
+
|
49
|
+
- `W` `A` `S` `D` moves your tank.
|
50
|
+
- Mouse `left click` shoots.
|
51
|
+
- `ESC` goes into menu and away from it.
|
52
|
+
|
53
|
+
### Debugging
|
54
|
+
|
55
|
+
- `R` respawns your tank.
|
56
|
+
- `T` spawns an enemy tank under mouse cursor.
|
57
|
+
- `F1` enters debug mode.
|
58
|
+
- `F2` toggles profiling
|
data/Rakefile
ADDED
data/bin/tank_island
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gosu'
|
4
|
+
require 'gosu_texture_packer'
|
5
|
+
require 'perlin_noise'
|
6
|
+
|
7
|
+
root_dir = File.expand_path(File.join(
|
8
|
+
File.dirname(File.dirname(__FILE__)), 'lib'))
|
9
|
+
require_pattern = File.join(root_dir, '**/*.rb')
|
10
|
+
@failed = []
|
11
|
+
|
12
|
+
# Dynamically require everything
|
13
|
+
Dir.glob(require_pattern).each do |f|
|
14
|
+
begin
|
15
|
+
require f
|
16
|
+
rescue
|
17
|
+
# May fail if parent class not required yet
|
18
|
+
@failed << f
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retry unresolved requires
|
23
|
+
@failed.each do |f|
|
24
|
+
require f.gsub("#{root_dir}/", '')
|
25
|
+
end
|
26
|
+
|
27
|
+
$debug = false
|
28
|
+
$window = GameWindow.new
|
29
|
+
GameState.switch(MenuState.instance)
|
30
|
+
$window.show
|
data/lib/entities/box.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Box < GameObject
|
2
|
+
attr_reader :health, :graphics, :angle
|
3
|
+
|
4
|
+
def initialize(object_pool, x, y)
|
5
|
+
super
|
6
|
+
@graphics = BoxGraphics.new(self)
|
7
|
+
@health = Health.new(self, object_pool, 10, true)
|
8
|
+
@angle = rand(-15..15)
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_collision(object)
|
12
|
+
return unless object.physics.speed > 1.0
|
13
|
+
move(*Utils.point_at_distance(@x, @y, object.direction, 2))
|
14
|
+
@box = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def box
|
18
|
+
return @box if @box
|
19
|
+
w = @graphics.width / 2
|
20
|
+
h = @graphics.height / 2
|
21
|
+
# Bounding box adjusted to trim shadows
|
22
|
+
@box = [x - w + 4, y - h + 8,
|
23
|
+
x + w , y - h + 8,
|
24
|
+
x + w , y + h,
|
25
|
+
x - w + 4, y + h]
|
26
|
+
@box = Utils.rotate(@angle, @x, @y, *@box)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Bullet < GameObject
|
2
|
+
attr_accessor :target_x, :target_y, :source, :speed, :fired_at
|
3
|
+
|
4
|
+
def initialize(object_pool, source_x, source_y, target_x, target_y)
|
5
|
+
super(object_pool, source_x, source_y)
|
6
|
+
@target_x, @target_y = target_x, target_y
|
7
|
+
BulletPhysics.new(self, object_pool)
|
8
|
+
BulletGraphics.new(self)
|
9
|
+
BulletSounds.play(self, object_pool.camera)
|
10
|
+
end
|
11
|
+
|
12
|
+
def box
|
13
|
+
[@x, @y]
|
14
|
+
end
|
15
|
+
|
16
|
+
def explode
|
17
|
+
Explosion.new(object_pool, @x, @y, @source)
|
18
|
+
mark_for_removal
|
19
|
+
end
|
20
|
+
|
21
|
+
def fire(source, speed)
|
22
|
+
@source = source
|
23
|
+
@speed = speed
|
24
|
+
@fired_at = Gosu.milliseconds
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class Camera
|
2
|
+
attr_accessor :x, :y, :zoom
|
3
|
+
attr_reader :target
|
4
|
+
|
5
|
+
def target=(target)
|
6
|
+
@target = target
|
7
|
+
@x, @y = target.x, target.y
|
8
|
+
@zoom = 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def desired_spot
|
12
|
+
if @target.physics.moving?
|
13
|
+
Utils.point_at_distance(
|
14
|
+
@target.x, @target.y,
|
15
|
+
@target.direction,
|
16
|
+
@target.physics.speed.ceil * 25)
|
17
|
+
else
|
18
|
+
[@target.x, @target.y]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def mouse_coords
|
23
|
+
x, y = target_delta_on_screen
|
24
|
+
mouse_x_on_map = @target.x +
|
25
|
+
(x + $window.mouse_x - ($window.width / 2)) / @zoom
|
26
|
+
mouse_y_on_map = @target.y +
|
27
|
+
(y + $window.mouse_y - ($window.height / 2)) / @zoom
|
28
|
+
[mouse_x_on_map, mouse_y_on_map].map(&:round)
|
29
|
+
end
|
30
|
+
|
31
|
+
def update
|
32
|
+
des_x, des_y = desired_spot
|
33
|
+
shift = Utils.adjust_speed(
|
34
|
+
@target.physics.speed).floor *
|
35
|
+
@target.speed_modifier + 1
|
36
|
+
if @x < des_x
|
37
|
+
if des_x - @x < shift
|
38
|
+
@x = des_x
|
39
|
+
else
|
40
|
+
@x += shift
|
41
|
+
end
|
42
|
+
elsif @x > des_x
|
43
|
+
if @x - des_x < shift
|
44
|
+
@x = des_x
|
45
|
+
else
|
46
|
+
@x -= shift
|
47
|
+
end
|
48
|
+
end
|
49
|
+
if @y < des_y
|
50
|
+
if des_y - @y < shift
|
51
|
+
@y = des_y
|
52
|
+
else
|
53
|
+
@y += shift
|
54
|
+
end
|
55
|
+
elsif @y > des_y
|
56
|
+
if @y - des_y < shift
|
57
|
+
@y = des_y
|
58
|
+
else
|
59
|
+
@y -= shift
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
zoom_delta = @zoom > 0 ? 0.01 : 1.0
|
64
|
+
zoom_delta = Utils.adjust_speed(zoom_delta)
|
65
|
+
if $window.button_down?(Gosu::KbUp)
|
66
|
+
@zoom -= zoom_delta unless @zoom < 0.7
|
67
|
+
elsif $window.button_down?(Gosu::KbDown)
|
68
|
+
@zoom += zoom_delta unless @zoom > 10
|
69
|
+
else
|
70
|
+
target_zoom = @target.physics.speed > 1.1 ? 0.75 : 1.0
|
71
|
+
if @zoom <= (target_zoom - 0.01)
|
72
|
+
@zoom += zoom_delta / 3
|
73
|
+
elsif @zoom > (target_zoom + 0.01)
|
74
|
+
@zoom -= zoom_delta / 3
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"FPS: #{Gosu.fps}. " <<
|
81
|
+
"#{@x}:#{@y} @ #{'%.2f' % @zoom}. " <<
|
82
|
+
'WASD to move, arrows to zoom.'
|
83
|
+
end
|
84
|
+
|
85
|
+
def target_delta_on_screen
|
86
|
+
[(@x - @target.x) * @zoom, (@y - @target.y) * @zoom]
|
87
|
+
end
|
88
|
+
|
89
|
+
def draw_crosshair
|
90
|
+
factor = 0.5
|
91
|
+
x = $window.mouse_x
|
92
|
+
y = $window.mouse_y
|
93
|
+
c = crosshair
|
94
|
+
c.draw(x - c.width * factor / 2,
|
95
|
+
y - c.height * factor / 2,
|
96
|
+
1000, factor, factor)
|
97
|
+
end
|
98
|
+
|
99
|
+
def viewport
|
100
|
+
x0 = @x - ($window.width / 2) / @zoom
|
101
|
+
x1 = @x + ($window.width / 2) / @zoom
|
102
|
+
y0 = @y - ($window.height / 2) / @zoom
|
103
|
+
y1 = @y + ($window.height / 2) / @zoom
|
104
|
+
[x0, x1, y0, y1]
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def crosshair
|
110
|
+
@crosshair ||= Gosu::Image.new(
|
111
|
+
$window, Utils.media_path('c_dot.png'), false)
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
class AiGun
|
2
|
+
DECISION_DELAY = 300
|
3
|
+
attr_reader :target, :desired_gun_angle
|
4
|
+
|
5
|
+
def initialize(object, vision)
|
6
|
+
@object = object
|
7
|
+
@vision = vision
|
8
|
+
@desired_gun_angle = rand(0..360)
|
9
|
+
@retarget_speed = rand(1..5)
|
10
|
+
@accuracy = rand(0..10)
|
11
|
+
@aggressiveness = rand(1..5)
|
12
|
+
end
|
13
|
+
|
14
|
+
def adjust_angle
|
15
|
+
adjust_desired_angle
|
16
|
+
adjust_gun_angle
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
if @vision.in_sight.any?
|
21
|
+
if @vision.closest_tank != @target
|
22
|
+
change_target(@vision.closest_tank)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
@target = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
if @target
|
29
|
+
if (0..30 - rand(0..@accuracy)).include?(
|
30
|
+
(@desired_gun_angle - @object.gun_angle).abs.round)
|
31
|
+
distance = distance_to_target
|
32
|
+
if distance - 50 <= BulletPhysics::MAX_DIST
|
33
|
+
target_x, target_y = Utils.point_at_distance(
|
34
|
+
@object.x, @object.y, @object.gun_angle,
|
35
|
+
distance + 10 - rand(0..@accuracy))
|
36
|
+
if can_make_new_decision? && @object.can_shoot? &&
|
37
|
+
should_shoot?
|
38
|
+
@object.shoot(target_x, target_y)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def draw(viewport)
|
46
|
+
if $debug
|
47
|
+
color = Gosu::Color::BLUE
|
48
|
+
x, y = @object.x, @object.y
|
49
|
+
t_x, t_y = Utils.point_at_distance(x, y, @desired_gun_angle,
|
50
|
+
BulletPhysics::MAX_DIST)
|
51
|
+
$window.draw_line(x, y, color, t_x, t_y, color, 1001)
|
52
|
+
color = Gosu::Color::RED
|
53
|
+
t_x, t_y = Utils.point_at_distance(x, y, @object.gun_angle,
|
54
|
+
BulletPhysics::MAX_DIST)
|
55
|
+
$window.draw_line(x, y, color, t_x, t_y, color, 1000)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def distance_to_target
|
60
|
+
Utils.distance_between(
|
61
|
+
@object.x, @object.y, @target.x, @target.y)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def should_shoot?
|
66
|
+
rand * @aggressiveness > 0.3
|
67
|
+
end
|
68
|
+
|
69
|
+
def can_make_new_decision?
|
70
|
+
now = Gosu.milliseconds
|
71
|
+
if now - (@last_decision ||= 0) > DECISION_DELAY
|
72
|
+
@last_decision = now
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def adjust_desired_angle
|
78
|
+
@desired_gun_angle = if @target
|
79
|
+
Utils.angle_between(
|
80
|
+
@object.x, @object.y, @target.x, @target.y)
|
81
|
+
else
|
82
|
+
@object.direction
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def change_target(new_target)
|
87
|
+
@target = new_target
|
88
|
+
adjust_desired_angle
|
89
|
+
end
|
90
|
+
|
91
|
+
def adjust_gun_angle
|
92
|
+
actual = @object.gun_angle
|
93
|
+
desired = @desired_gun_angle
|
94
|
+
if actual > desired
|
95
|
+
if actual - desired > 180 # 0 -> 360 fix
|
96
|
+
@object.gun_angle = (actual + @retarget_speed) % 360
|
97
|
+
if @object.gun_angle < desired
|
98
|
+
@object.gun_angle = desired # damp
|
99
|
+
end
|
100
|
+
else
|
101
|
+
@object.gun_angle = [actual - @retarget_speed, desired].max
|
102
|
+
end
|
103
|
+
elsif actual < desired
|
104
|
+
if desired - actual > 180 # 360 -> 0 fix
|
105
|
+
@object.gun_angle = (360 + actual - @retarget_speed) % 360
|
106
|
+
if @object.gun_angle > desired
|
107
|
+
@object.gun_angle = desired # damp
|
108
|
+
end
|
109
|
+
else
|
110
|
+
@object.gun_angle = [actual + @retarget_speed, desired].min
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class TankChasingState < TankMotionState
|
2
|
+
def initialize(object, vision, gun)
|
3
|
+
super(object, vision)
|
4
|
+
@object = object
|
5
|
+
@vision = vision
|
6
|
+
@gun = gun
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
change_direction if should_change_direction?
|
11
|
+
drive
|
12
|
+
end
|
13
|
+
|
14
|
+
def change_direction
|
15
|
+
@object.physics.change_direction(
|
16
|
+
@gun.desired_gun_angle -
|
17
|
+
@gun.desired_gun_angle % 45)
|
18
|
+
|
19
|
+
@changed_direction_at = Gosu.milliseconds
|
20
|
+
@will_keep_direction_for = turn_time
|
21
|
+
end
|
22
|
+
|
23
|
+
def drive_time
|
24
|
+
10000
|
25
|
+
end
|
26
|
+
|
27
|
+
def turn_time
|
28
|
+
rand(300..600)
|
29
|
+
end
|
30
|
+
end
|