tank_island 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Tank Island Game](https://dl.dropboxusercontent.com/u/176100/tank_island/screen1.png)
|
10
|
+
|
11
|
+
![Tank Island Game](https://dl.dropboxusercontent.com/u/176100/tank_island/screen2.png)
|
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
|