rtanque 0.0.2
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/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rvmrc +34 -0
- data/.travis.yml +11 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/Gemfile.ci +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +120 -0
- data/Rakefile +1 -0
- data/bin/rtanque +108 -0
- data/lib/rtanque.rb +40 -0
- data/lib/rtanque/arena.rb +8 -0
- data/lib/rtanque/bot.rb +117 -0
- data/lib/rtanque/bot/brain.rb +50 -0
- data/lib/rtanque/bot/brain_helper.rb +23 -0
- data/lib/rtanque/bot/command.rb +23 -0
- data/lib/rtanque/bot/radar.rb +54 -0
- data/lib/rtanque/bot/sensors.rb +33 -0
- data/lib/rtanque/bot/turret.rb +14 -0
- data/lib/rtanque/configuration.rb +46 -0
- data/lib/rtanque/explosion.rb +23 -0
- data/lib/rtanque/gui.rb +24 -0
- data/lib/rtanque/gui/bot.rb +42 -0
- data/lib/rtanque/gui/draw_group.rb +30 -0
- data/lib/rtanque/gui/explosion.rb +25 -0
- data/lib/rtanque/gui/shell.rb +20 -0
- data/lib/rtanque/gui/window.rb +51 -0
- data/lib/rtanque/heading.rb +172 -0
- data/lib/rtanque/match.rb +67 -0
- data/lib/rtanque/match/tick_group.rb +50 -0
- data/lib/rtanque/movable.rb +51 -0
- data/lib/rtanque/normalized_attr.rb +69 -0
- data/lib/rtanque/point.rb +140 -0
- data/lib/rtanque/runner.rb +88 -0
- data/lib/rtanque/shell.rb +40 -0
- data/lib/rtanque/version.rb +3 -0
- data/resources/images/body.png +0 -0
- data/resources/images/bullet.png +0 -0
- data/resources/images/explosions/explosion2-1.png +0 -0
- data/resources/images/explosions/explosion2-10.png +0 -0
- data/resources/images/explosions/explosion2-11.png +0 -0
- data/resources/images/explosions/explosion2-12.png +0 -0
- data/resources/images/explosions/explosion2-13.png +0 -0
- data/resources/images/explosions/explosion2-14.png +0 -0
- data/resources/images/explosions/explosion2-15.png +0 -0
- data/resources/images/explosions/explosion2-16.png +0 -0
- data/resources/images/explosions/explosion2-17.png +0 -0
- data/resources/images/explosions/explosion2-18.png +0 -0
- data/resources/images/explosions/explosion2-19.png +0 -0
- data/resources/images/explosions/explosion2-2.png +0 -0
- data/resources/images/explosions/explosion2-20.png +0 -0
- data/resources/images/explosions/explosion2-21.png +0 -0
- data/resources/images/explosions/explosion2-22.png +0 -0
- data/resources/images/explosions/explosion2-23.png +0 -0
- data/resources/images/explosions/explosion2-24.png +0 -0
- data/resources/images/explosions/explosion2-25.png +0 -0
- data/resources/images/explosions/explosion2-26.png +0 -0
- data/resources/images/explosions/explosion2-27.png +0 -0
- data/resources/images/explosions/explosion2-28.png +0 -0
- data/resources/images/explosions/explosion2-29.png +0 -0
- data/resources/images/explosions/explosion2-3.png +0 -0
- data/resources/images/explosions/explosion2-30.png +0 -0
- data/resources/images/explosions/explosion2-31.png +0 -0
- data/resources/images/explosions/explosion2-32.png +0 -0
- data/resources/images/explosions/explosion2-33.png +0 -0
- data/resources/images/explosions/explosion2-34.png +0 -0
- data/resources/images/explosions/explosion2-35.png +0 -0
- data/resources/images/explosions/explosion2-36.png +0 -0
- data/resources/images/explosions/explosion2-37.png +0 -0
- data/resources/images/explosions/explosion2-38.png +0 -0
- data/resources/images/explosions/explosion2-39.png +0 -0
- data/resources/images/explosions/explosion2-4.png +0 -0
- data/resources/images/explosions/explosion2-40.png +0 -0
- data/resources/images/explosions/explosion2-41.png +0 -0
- data/resources/images/explosions/explosion2-42.png +0 -0
- data/resources/images/explosions/explosion2-43.png +0 -0
- data/resources/images/explosions/explosion2-44.png +0 -0
- data/resources/images/explosions/explosion2-45.png +0 -0
- data/resources/images/explosions/explosion2-46.png +0 -0
- data/resources/images/explosions/explosion2-47.png +0 -0
- data/resources/images/explosions/explosion2-48.png +0 -0
- data/resources/images/explosions/explosion2-49.png +0 -0
- data/resources/images/explosions/explosion2-5.png +0 -0
- data/resources/images/explosions/explosion2-50.png +0 -0
- data/resources/images/explosions/explosion2-51.png +0 -0
- data/resources/images/explosions/explosion2-52.png +0 -0
- data/resources/images/explosions/explosion2-53.png +0 -0
- data/resources/images/explosions/explosion2-54.png +0 -0
- data/resources/images/explosions/explosion2-55.png +0 -0
- data/resources/images/explosions/explosion2-56.png +0 -0
- data/resources/images/explosions/explosion2-57.png +0 -0
- data/resources/images/explosions/explosion2-58.png +0 -0
- data/resources/images/explosions/explosion2-59.png +0 -0
- data/resources/images/explosions/explosion2-6.png +0 -0
- data/resources/images/explosions/explosion2-60.png +0 -0
- data/resources/images/explosions/explosion2-61.png +0 -0
- data/resources/images/explosions/explosion2-62.png +0 -0
- data/resources/images/explosions/explosion2-63.png +0 -0
- data/resources/images/explosions/explosion2-64.png +0 -0
- data/resources/images/explosions/explosion2-65.png +0 -0
- data/resources/images/explosions/explosion2-66.png +0 -0
- data/resources/images/explosions/explosion2-67.png +0 -0
- data/resources/images/explosions/explosion2-68.png +0 -0
- data/resources/images/explosions/explosion2-69.png +0 -0
- data/resources/images/explosions/explosion2-7.png +0 -0
- data/resources/images/explosions/explosion2-70.png +0 -0
- data/resources/images/explosions/explosion2-71.png +0 -0
- data/resources/images/explosions/explosion2-8.png +0 -0
- data/resources/images/explosions/explosion2-9.png +0 -0
- data/resources/images/grass.png +0 -0
- data/resources/images/radar.png +0 -0
- data/resources/images/turret.png +0 -0
- data/rtanque.gemspec +33 -0
- data/sample_bots/camper.rb +79 -0
- data/sample_bots/keyboard.rb +50 -0
- data/sample_bots/seek_and_destroy.rb +51 -0
- data/screenshots/battle_1.png +0 -0
- data/screenshots/battle_2.png +0 -0
- data/spec/rtanque/bot_spec.rb +239 -0
- data/spec/rtanque/heading_spec.rb +279 -0
- data/spec/rtanque/match_spec.rb +36 -0
- data/spec/rtanque/normalized_attr_spec.rb +90 -0
- data/spec/rtanque/point_spec.rb +196 -0
- data/spec/rtanque/radar_spec.rb +87 -0
- data/spec/rtanque/shell_spec.rb +35 -0
- data/spec/rtanque_spec.rb +6 -0
- data/spec/spec_helper.rb +51 -0
- data/templates/bot.erb +11 -0
- metadata +310 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Add the working directory so that loading of bots works as expected
|
|
2
|
+
$LOAD_PATH << Dir.pwd
|
|
3
|
+
# Add the gem root dir so that sample_bots can be loaded
|
|
4
|
+
$LOAD_PATH << File.expand_path('../../../', __FILE__)
|
|
5
|
+
|
|
6
|
+
module RTanque
|
|
7
|
+
# Runner manages running an {RTanque::Match}
|
|
8
|
+
class Runner
|
|
9
|
+
LoadError = Class.new(::LoadError)
|
|
10
|
+
attr_reader :match
|
|
11
|
+
|
|
12
|
+
# @param [Integer] width
|
|
13
|
+
# @param [Integer] height
|
|
14
|
+
# @param [*match_args] args provided to {RTanque::Match#initialize}
|
|
15
|
+
def initialize(width, height, *match_args)
|
|
16
|
+
@match = RTanque::Match.new(RTanque::Arena.new(width, height), *match_args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Attempts to load given {RTanque::Bot::Brain} given its path
|
|
20
|
+
# @param [String] brain_path
|
|
21
|
+
# @raise [RTanque::Runner::LoadError] if brain could not be loaded
|
|
22
|
+
def add_brain_path(brain_path)
|
|
23
|
+
parsed_path = self.parse_brain_path(brain_path)
|
|
24
|
+
bots = parsed_path.multiplier.times.map { self.new_bots_from_brain_path(parsed_path.path) }.flatten
|
|
25
|
+
self.match.add_bots(bots)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Starts the match
|
|
29
|
+
# @param [Boolean] gui if false, runs headless match
|
|
30
|
+
def start(gui = true)
|
|
31
|
+
if gui
|
|
32
|
+
require 'rtanque/gui'
|
|
33
|
+
window = RTanque::Gui::Window.new(self.match)
|
|
34
|
+
trap(:INT) { window.close }
|
|
35
|
+
window.show
|
|
36
|
+
else
|
|
37
|
+
trap(:INT) { self.match.stop }
|
|
38
|
+
self.match.start
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
def new_bots_from_brain_path(brain_path)
|
|
45
|
+
self.fetch_brain_klasses(brain_path).map do |brain_klass|
|
|
46
|
+
RTanque::Bot.new_random_location(self.match.arena, brain_klass)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def fetch_brain_klasses(brain_path)
|
|
51
|
+
@load_brain_klass_cache ||= Hash.new do |cache, path|
|
|
52
|
+
cache[path] = self.get_diff_in_object_space(RTanque::Bot::Brain) {
|
|
53
|
+
begin
|
|
54
|
+
require(path)
|
|
55
|
+
rescue ::LoadError => e
|
|
56
|
+
raise LoadError, e.message
|
|
57
|
+
end
|
|
58
|
+
}
|
|
59
|
+
raise LoadError, "No class of type #{RTanque::Bot::Brain} found in #{path}" if cache[path].empty?
|
|
60
|
+
cache[path]
|
|
61
|
+
end
|
|
62
|
+
@load_brain_klass_cache[brain_path]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_diff_in_object_space(klass)
|
|
66
|
+
current_object_space = self.get_descendants_of_class(klass)
|
|
67
|
+
yield
|
|
68
|
+
self.get_descendants_of_class(klass) - current_object_space
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def get_descendants_of_class(klass)
|
|
72
|
+
::ObjectSpace.each_object(::Class).select {|k| k < klass }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
BRAIN_PATH_PARSER = /\A(.+?)\:[x|X](\d+)\z/
|
|
76
|
+
# @!visibility private
|
|
77
|
+
ParsedBrainPath = Struct.new(:path, :multiplier)
|
|
78
|
+
def parse_brain_path(brain_path)
|
|
79
|
+
path = brain_path.gsub('\.rb$', '')
|
|
80
|
+
multiplier = 1
|
|
81
|
+
brain_path.match(BRAIN_PATH_PARSER) { |m|
|
|
82
|
+
path = m[1]
|
|
83
|
+
multiplier = m[2].to_i
|
|
84
|
+
}
|
|
85
|
+
ParsedBrainPath.new(path, multiplier)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Shell
|
|
3
|
+
include Movable
|
|
4
|
+
RATIO = Configuration.shell.ratio
|
|
5
|
+
SHELL_SPEED_FACTOR = Configuration.shell.speed_factor
|
|
6
|
+
attr_reader :bot, :arena, :fire_power
|
|
7
|
+
|
|
8
|
+
def initialize(bot, position, heading, fire_power)
|
|
9
|
+
@bot = bot
|
|
10
|
+
@arena = bot.arena
|
|
11
|
+
@fire_power = fire_power
|
|
12
|
+
self.position = position
|
|
13
|
+
self.heading = heading
|
|
14
|
+
self.speed = (fire_power * SHELL_SPEED_FACTOR) # TODO: add bot's relative speed in this heading
|
|
15
|
+
@dead = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bound_to_arena
|
|
19
|
+
false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def dead?
|
|
23
|
+
@dead ||= self.position.outside_arena?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dead!
|
|
27
|
+
@dead = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def hits(bots, &on_hit)
|
|
31
|
+
bots.each do |hit_bot|
|
|
32
|
+
if hit_bot.position.within_radius?(self.position, Bot::RADIUS)
|
|
33
|
+
self.dead!
|
|
34
|
+
on_hit.call(self.bot, hit_bot) if on_hit
|
|
35
|
+
break
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/rtanque.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'rtanque/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = "rtanque"
|
|
8
|
+
gem.version = RTanque::VERSION
|
|
9
|
+
gem.authors = ["Adam Williams"]
|
|
10
|
+
gem.email = ["pwnfactory@gmail.com"]
|
|
11
|
+
gem.summary = %q{RTanque is a game for programmers. Players program the brain of a tank and then send their tank into battle against other tanks.}
|
|
12
|
+
gem.description = %q{RTanque is a game for programmers. Players program the brain of a tank and then send their tank+brain into battle with other tanks. All tanks are otherwise equal.
|
|
13
|
+
|
|
14
|
+
Rules of the game are simple: Last bot standing wins. Gameplay is also pretty simple. Each tank has a base, turret and radar, each of which rotate independently. The base moves the tank, the turret has a gun mounted to it which can fire at other tanks, and the radar detects other tanks in its field of vision.
|
|
15
|
+
|
|
16
|
+
Have fun competing against friends' tanks or the sample ones included. Maybe you'll start a small league at your local Ruby meetup.}
|
|
17
|
+
gem.homepage = "https://github.com/awilliams/RTanque"
|
|
18
|
+
gem.license = 'MIT'
|
|
19
|
+
|
|
20
|
+
gem.files = `git ls-files`.split($/)
|
|
21
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
23
|
+
gem.require_paths = ["lib"]
|
|
24
|
+
|
|
25
|
+
gem.add_dependency 'gosu', '~> 0.7.45'
|
|
26
|
+
gem.add_dependency 'configuration', '~> 1.3.2'
|
|
27
|
+
gem.add_dependency 'octokit', '~> 1.23.0'
|
|
28
|
+
gem.add_dependency 'thor', '~> 0.17.0'
|
|
29
|
+
|
|
30
|
+
gem.add_development_dependency 'pry'
|
|
31
|
+
gem.add_development_dependency 'rspec', '~> 2.13.0'
|
|
32
|
+
gem.add_development_dependency 'rspec-mocks', '~> 2.13.0'
|
|
33
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Camper: Sample Bot
|
|
2
|
+
#
|
|
3
|
+
# Enjoys sitting in the corners and firing powerful shots
|
|
4
|
+
class Camper < RTanque::Bot::Brain
|
|
5
|
+
NAME = 'Camper'
|
|
6
|
+
include RTanque::Bot::BrainHelper
|
|
7
|
+
|
|
8
|
+
CORNERS = [:NW, :NE, :SE, :SW]
|
|
9
|
+
TURRET_FIRE_RANGE = RTanque::Heading::ONE_DEGREE * 1.0
|
|
10
|
+
SWITCH_CORNER_TICK_RANGE = (600..1000)
|
|
11
|
+
|
|
12
|
+
def tick!
|
|
13
|
+
self.hide_in_corners
|
|
14
|
+
if (target = self.nearest_target)
|
|
15
|
+
self.fire_upon(target)
|
|
16
|
+
else
|
|
17
|
+
self.detect_targets
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fire_upon(target)
|
|
22
|
+
self.command.radar_heading = target.heading
|
|
23
|
+
self.command.turret_heading = target.heading
|
|
24
|
+
if self.sensors.turret_heading.delta(target.heading).abs < TURRET_FIRE_RANGE
|
|
25
|
+
self.command.fire(MAX_FIRE_POWER)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def nearest_target
|
|
30
|
+
self.sensors.radar.min { |a,b| a.distance <=> b.distance }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def detect_targets
|
|
34
|
+
self.command.radar_heading = self.sensors.radar_heading + MAX_RADAR_ROTATION
|
|
35
|
+
self.command.turret_heading = self.sensors.heading + RTanque::Heading::HALF_ANGLE
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def hide_in_corners
|
|
39
|
+
@corner_cycle ||= CORNERS.shuffle.cycle
|
|
40
|
+
self.at_tick_interval(self.camp_interval) {
|
|
41
|
+
self.corner = @corner_cycle.next
|
|
42
|
+
self.reset_camp_interval
|
|
43
|
+
}
|
|
44
|
+
self.corner ||= @corner_cycle.next
|
|
45
|
+
self.move_to_corner
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def move_to_corner
|
|
49
|
+
if self.corner
|
|
50
|
+
command.heading = self.sensors.position.heading(RTanque::Point.new(*self.corner, self.arena))
|
|
51
|
+
command.speed = MAX_BOT_SPEED
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def corner=(corner_name)
|
|
56
|
+
@corner = case corner_name
|
|
57
|
+
when :NE
|
|
58
|
+
[self.arena.width, self.arena.height]
|
|
59
|
+
when :SE
|
|
60
|
+
[self.arena.width, 0]
|
|
61
|
+
when :SW
|
|
62
|
+
[0, 0]
|
|
63
|
+
else
|
|
64
|
+
[0, self.arena.height]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def corner
|
|
69
|
+
@corner
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def camp_interval
|
|
73
|
+
@camp_interval ||= self.reset_camp_interval
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def reset_camp_interval
|
|
77
|
+
@camp_interval = rand(SWITCH_CORNER_TICK_RANGE.max - SWITCH_CORNER_TICK_RANGE.min) + SWITCH_CORNER_TICK_RANGE.min
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# Keyboard: Sample Bot
|
|
3
|
+
#
|
|
4
|
+
# Special bot controlled with the keyboard
|
|
5
|
+
#
|
|
6
|
+
# Controls:
|
|
7
|
+
# w drive forward
|
|
8
|
+
# s drive backwards
|
|
9
|
+
# a rotate left
|
|
10
|
+
# d rotate right
|
|
11
|
+
#
|
|
12
|
+
# ↑ fire powerful shot
|
|
13
|
+
# ↓ fire week shot
|
|
14
|
+
# ← rotate turret left
|
|
15
|
+
# → rotate turret right
|
|
16
|
+
class Keyboard < RTanque::Bot::Brain
|
|
17
|
+
include RTanque::Bot::BrainHelper
|
|
18
|
+
|
|
19
|
+
NAME = 'Keyboard'
|
|
20
|
+
|
|
21
|
+
def tick!
|
|
22
|
+
command.radar_heading = sensors.radar_heading + (RTanque::Heading::ONE_DEGREE * 30)
|
|
23
|
+
|
|
24
|
+
if sensors.button_down?(Gosu::KbLeft)
|
|
25
|
+
command.turret_heading = sensors.turret_heading - (RTanque::Heading::ONE_DEGREE * 10)
|
|
26
|
+
elsif sensors.button_down?(Gosu::KbRight)
|
|
27
|
+
command.turret_heading = sensors.turret_heading + (RTanque::Heading::ONE_DEGREE * 10)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if sensors.button_down?('a')
|
|
31
|
+
command.heading = sensors.heading - (RTanque::Heading::ONE_DEGREE * 10)
|
|
32
|
+
elsif sensors.button_down?('d')
|
|
33
|
+
command.heading = sensors.heading + (RTanque::Heading::ONE_DEGREE * 10)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if sensors.button_down?('w')
|
|
37
|
+
command.speed = 10
|
|
38
|
+
elsif sensors.button_down?('s')
|
|
39
|
+
command.speed = -10
|
|
40
|
+
else
|
|
41
|
+
command.speed = 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if sensors.button_down?(Gosu::KbUp)
|
|
45
|
+
command.fire_power = 5
|
|
46
|
+
elsif sensors.button_down?(Gosu::KbDown)
|
|
47
|
+
command.fire_power = 1
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|